Load journal from memory graph, not flat file
Replace flat-file journal parser with direct store query for EpisodicSession nodes. Filter journal entries to only those older than the oldest conversation message (plus one overlap entry to avoid gaps). Falls back to 20 recent entries when no conversation exists yet. Fixes: poc-agent context window showing 0 journal entries. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
c814ed1345
commit
e4285ba75f
2 changed files with 71 additions and 2 deletions
|
|
@ -125,4 +125,26 @@ impl ConversationLog {
|
||||||
pub fn path(&self) -> &Path {
|
pub fn path(&self) -> &Path {
|
||||||
&self.path
|
&self.path
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the timestamp of the oldest message in the log.
|
||||||
|
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(msg) = serde_json::from_str::<Message>(&line) {
|
||||||
|
if let Some(ts) = &msg.timestamp {
|
||||||
|
if let Ok(dt) = chrono::DateTime::parse_from_rfc3339(ts) {
|
||||||
|
return Some(dt.to_utc());
|
||||||
|
}
|
||||||
|
// Try other formats
|
||||||
|
if let Ok(dt) = chrono::NaiveDateTime::parse_from_str(ts, "%Y-%m-%dT%H:%M:%S") {
|
||||||
|
return Some(dt.and_utc());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -785,8 +785,55 @@ impl Agent {
|
||||||
/// Uses the same budget logic as compaction but with empty conversation.
|
/// Uses the same budget logic as compaction but with empty conversation.
|
||||||
/// Only parses the tail of the journal file (last 64KB) for speed.
|
/// Only parses the tail of the journal file (last 64KB) for speed.
|
||||||
fn load_startup_journal(&mut self) {
|
fn load_startup_journal(&mut self) {
|
||||||
let journal_path = journal::default_journal_path();
|
let store = match crate::store::Store::load() {
|
||||||
let entries = journal::parse_journal_tail(&journal_path, 64 * 1024);
|
Ok(s) => s,
|
||||||
|
Err(_) => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Find oldest message timestamp in conversation log
|
||||||
|
let oldest_msg_ts = self.conversation_log.as_ref()
|
||||||
|
.and_then(|log| log.oldest_timestamp());
|
||||||
|
|
||||||
|
// Get journal entries from the memory graph
|
||||||
|
let mut journal_nodes: Vec<_> = store.nodes.values()
|
||||||
|
.filter(|n| n.node_type == crate::store::NodeType::EpisodicSession)
|
||||||
|
.collect();
|
||||||
|
journal_nodes.sort_by_key(|n| n.created_at);
|
||||||
|
|
||||||
|
// Filter: entries older than oldest conversation message,
|
||||||
|
// plus the first one that overlaps (no gap)
|
||||||
|
let entries: Vec<journal::JournalEntry> = if let Some(cutoff) = oldest_msg_ts {
|
||||||
|
let mut result = Vec::new();
|
||||||
|
let mut included_overlap = false;
|
||||||
|
for node in &journal_nodes {
|
||||||
|
let ts = chrono::DateTime::from_timestamp(node.created_at, 0)
|
||||||
|
.unwrap_or_default();
|
||||||
|
if ts < cutoff || !included_overlap {
|
||||||
|
result.push(journal::JournalEntry {
|
||||||
|
timestamp: ts,
|
||||||
|
content: node.content.clone(),
|
||||||
|
});
|
||||||
|
if ts >= cutoff {
|
||||||
|
included_overlap = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
} else {
|
||||||
|
// No conversation yet — include recent entries for orientation
|
||||||
|
let n = 20;
|
||||||
|
let skip = journal_nodes.len().saturating_sub(n);
|
||||||
|
journal_nodes.iter().skip(skip).map(|node| {
|
||||||
|
journal::JournalEntry {
|
||||||
|
timestamp: chrono::DateTime::from_timestamp(node.created_at, 0)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
content: node.content.clone(),
|
||||||
|
}
|
||||||
|
}).collect()
|
||||||
|
};
|
||||||
|
|
||||||
if entries.is_empty() {
|
if entries.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue