consciousness/src/mind/log.rs

84 lines
2.6 KiB
Rust
Raw Normal View History

2026-04-05 01:48:11 -04:00
use anyhow::{Context, Result};
use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
use crate::agent::context::AstNode;
2026-04-05 01:48:11 -04:00
pub struct ConversationLog {
path: PathBuf,
}
impl ConversationLog {
pub fn new(path: PathBuf) -> Result<Self> {
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)
.with_context(|| format!("creating log dir {}", parent.display()))?;
}
Ok(Self { path })
}
pub fn append_node(&self, node: &AstNode) -> Result<()> {
2026-04-05 01:48:11 -04:00
let mut file = OpenOptions::new()
.create(true)
.append(true)
.open(&self.path)
.with_context(|| format!("opening log {}", self.path.display()))?;
let line = serde_json::to_string(node)
.context("serializing node for log")?;
2026-04-05 01:48:11 -04:00
writeln!(file, "{}", line)
.context("writing to conversation log")?;
2026-04-06 23:04:08 -04:00
file.sync_all()
.context("syncing conversation log")?;
2026-04-05 01:48:11 -04:00
Ok(())
}
pub fn read_nodes(&self, max_bytes: u64) -> Result<Vec<AstNode>> {
2026-04-05 01:48:11 -04:00
if !self.path.exists() {
return Ok(Vec::new());
}
let file = File::open(&self.path)
.with_context(|| format!("opening log {}", self.path.display()))?;
let file_len = file.metadata()?.len();
let mut reader = BufReader::new(file);
if file_len > max_bytes {
reader.seek(SeekFrom::Start(file_len - max_bytes))?;
let mut discard = String::new();
reader.read_line(&mut discard)?;
}
let mut nodes = Vec::new();
2026-04-05 01:48:11 -04:00
for line in reader.lines() {
let line = line.context("reading log tail")?;
let line = line.trim();
if line.is_empty() { continue; }
if let Ok(node) = serde_json::from_str::<AstNode>(line) {
nodes.push(node);
2026-04-05 01:48:11 -04:00
}
}
Ok(nodes)
2026-04-05 01:48:11 -04:00
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn oldest_timestamp(&self) -> Option<chrono::DateTime<chrono::Utc>> {
let file = File::open(&self.path).ok()?;
let reader = BufReader::new(file);
for line in reader.lines().flatten() {
let line = line.trim().to_string();
if line.is_empty() { continue; }
if let Ok(node) = serde_json::from_str::<AstNode>(&line) {
if let Some(leaf) = node.leaf() {
if let Some(ts) = leaf.timestamp() {
return Some(ts);
2026-04-05 01:48:11 -04:00
}
}
}
}
None
}
}