Journal: walk backwards with token budget, not load-all
Iterate journal entries backwards from the conversation cutoff, accumulating within ~10K token budget (~8% of context window). Stops when budget is full, keeps at least one entry. Much more efficient than loading all entries and trimming. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
e4285ba75f
commit
7776d87d53
1 changed files with 29 additions and 29 deletions
|
|
@ -800,51 +800,51 @@ impl Agent {
|
||||||
.collect();
|
.collect();
|
||||||
journal_nodes.sort_by_key(|n| n.created_at);
|
journal_nodes.sort_by_key(|n| n.created_at);
|
||||||
|
|
||||||
// Filter: entries older than oldest conversation message,
|
// Find the cutoff index — entries older than conversation, plus one overlap
|
||||||
// plus the first one that overlaps (no gap)
|
let cutoff_idx = if let Some(cutoff) = oldest_msg_ts {
|
||||||
let entries: Vec<journal::JournalEntry> = if let Some(cutoff) = oldest_msg_ts {
|
let mut idx = journal_nodes.len();
|
||||||
let mut result = Vec::new();
|
for (i, node) in journal_nodes.iter().enumerate() {
|
||||||
let mut included_overlap = false;
|
|
||||||
for node in &journal_nodes {
|
|
||||||
let ts = chrono::DateTime::from_timestamp(node.created_at, 0)
|
let ts = chrono::DateTime::from_timestamp(node.created_at, 0)
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
if ts < cutoff || !included_overlap {
|
if ts >= cutoff {
|
||||||
result.push(journal::JournalEntry {
|
idx = i + 1; // include this overlapping entry
|
||||||
timestamp: ts,
|
|
||||||
content: node.content.clone(),
|
|
||||||
});
|
|
||||||
if ts >= cutoff {
|
|
||||||
included_overlap = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result
|
idx
|
||||||
} else {
|
} else {
|
||||||
// No conversation yet — include recent entries for orientation
|
journal_nodes.len()
|
||||||
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()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Walk backwards from cutoff, accumulating entries within token budget
|
||||||
|
let count = |s: &str| self.tokenizer.encode_with_special_tokens(s).len();
|
||||||
|
let journal_budget_tokens = 10_000; // ~8% of 128K context
|
||||||
|
let mut entries = Vec::new();
|
||||||
|
let mut total_tokens = 0;
|
||||||
|
|
||||||
|
for node in journal_nodes[..cutoff_idx].iter().rev() {
|
||||||
|
let tokens = count(&node.content);
|
||||||
|
if total_tokens + tokens > journal_budget_tokens && !entries.is_empty() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
entries.push(journal::JournalEntry {
|
||||||
|
timestamp: chrono::DateTime::from_timestamp(node.created_at, 0)
|
||||||
|
.unwrap_or_default(),
|
||||||
|
content: node.content.clone(),
|
||||||
|
});
|
||||||
|
total_tokens += tokens;
|
||||||
|
}
|
||||||
|
entries.reverse(); // chronological order
|
||||||
|
|
||||||
if entries.is_empty() {
|
if entries.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let count = |s: &str| self.tokenizer.encode_with_special_tokens(s).len();
|
|
||||||
let context_message = self.context.render_context_message();
|
let context_message = self.context.render_context_message();
|
||||||
|
|
||||||
let plan = crate::agent::context::plan_context(
|
let plan = crate::agent::context::plan_context(
|
||||||
&self.context.system_prompt,
|
&self.context.system_prompt,
|
||||||
&context_message,
|
&context_message,
|
||||||
&[], // no conversation yet
|
&[],
|
||||||
&entries,
|
&entries,
|
||||||
&self.client.model,
|
&self.client.model,
|
||||||
&count,
|
&count,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue