use std::collections::{HashMap, VecDeque};
use serde_json::Value;
const RECENT_TOOL_OUTPUTS: usize = 2;
const DELTA_WINDOW: usize = 16;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct DedupStats {
pub chunks_seen: usize,
pub exact_deduped: usize,
pub delta_deduped: usize,
pub tokens_saved: usize,
}
struct Canonical {
id: String,
content: String,
}
pub fn dedup_tool_outputs(messages: &mut [Value]) -> DedupStats {
let chunk_count: usize = messages.iter().map(tool_output_count).sum();
let mut stats = DedupStats {
chunks_seen: chunk_count,
..DedupStats::default()
};
let mut by_hash: HashMap<blake3::Hash, String> = HashMap::new();
let mut window: VecDeque<Canonical> = VecDeque::with_capacity(DELTA_WINDOW);
let mut chunk_index = 0;
let mut protection: Option<bool> = None;
for message in messages.iter_mut() {
visit_tool_outputs(message, &mut |id, content| {
let is_recent = chunk_index + RECENT_TOOL_OUTPUTS >= chunk_count;
chunk_index += 1;
let hash = blake3::hash(content.as_bytes());
if !is_recent {
let replacement = by_hash
.get(&hash)
.map(String::as_str)
.map(exact_stub)
.filter(|stub| estimate_tokens(stub) < estimate_tokens(content));
if let Some(stub) = replacement {
stats.exact_deduped += 1;
stats.tokens_saved += estimate_tokens(content) - estimate_tokens(&stub);
*content = stub;
return;
}
if let Some(stub) = best_delta_stub(&window, content) {
if *protection.get_or_insert_with(super::compress::protected) {
super::compress::note_kept_whole(
content,
super::compress::DEDUP_WITHOUT_COPY,
);
} else {
stats.delta_deduped += 1;
stats.tokens_saved += estimate_tokens(content) - estimate_tokens(&stub);
*content = stub;
return;
}
}
}
if let Some(id) = id {
by_hash.entry(hash).or_insert_with(|| id.to_owned());
if window.len() == DELTA_WINDOW {
window.pop_front();
}
window.push_back(Canonical {
id: id.to_owned(),
content: content.clone(),
});
}
});
}
stats
}
fn exact_stub(canonical_id: &str) -> String {
format!("[lean-ctx: identical to tool result {canonical_id} above]")
}
fn best_delta_stub(window: &VecDeque<Canonical>, content: &str) -> Option<String> {
let budget = estimate_tokens(content) / 2;
window
.iter()
.filter_map(|canonical| delta_stub(canonical, content))
.filter(|stub| estimate_tokens(stub) <= budget)
.min_by_key(String::len)
}
fn delta_stub(canonical: &Canonical, content: &str) -> Option<String> {
if canonical.content.contains('\r')
|| content.contains('\r')
|| canonical.content.ends_with('\n') != content.ends_with('\n')
{
return None;
}
let old: Vec<&str> = canonical.content.lines().collect();
let new: Vec<&str> = content.lines().collect();
let prefix = old
.iter()
.zip(&new)
.take_while(|(old_line, new_line)| old_line == new_line)
.count();
let max_suffix = old.len().min(new.len()) - prefix;
let suffix = old
.iter()
.rev()
.zip(new.iter().rev())
.take(max_suffix)
.take_while(|(old_line, new_line)| old_line == new_line)
.count();
let removed = &old[prefix..old.len() - suffix];
let inserted = &new[prefix..new.len() - suffix];
let id = &canonical.id;
let change = match (removed.is_empty(), inserted.is_empty()) {
(true, true) => return None,
(true, false) if prefix == 0 => "these lines are inserted at the start".to_owned(),
(true, false) => format!("these lines are inserted after line {prefix}"),
(false, true) => {
return Some(format!(
"[lean-ctx: tool result {id} above, except {} removed]",
line_span(prefix, removed.len(), "is", "are")
));
}
(false, false) => format!(
"{} replaced by",
line_span(prefix, removed.len(), "is", "are")
),
};
Some(format!(
"[lean-ctx: tool result {id} above, except {change}:\n{}\n]",
inserted.join("\n")
))
}
fn line_span(prefix: usize, count: usize, singular: &str, plural: &str) -> String {
let first = prefix + 1;
if count == 1 {
format!("line {first} {singular}")
} else {
format!("lines {first}–{} {plural}", prefix + count)
}
}
fn estimate_tokens(content: &str) -> usize {
content.len().div_ceil(4)
}
fn tool_output_count(message: &Value) -> usize {
if is_tool_message(message) {
return usize::from(message.get("content").is_some_and(Value::is_string));
}
message
.get("content")
.and_then(Value::as_array)
.map_or(0, |blocks| {
blocks
.iter()
.filter(|block| {
block.get("tool_use_id").is_some()
&& block.get("content").is_some_and(Value::is_string)
})
.count()
})
}
fn is_tool_message(message: &Value) -> bool {
message.get("role").and_then(Value::as_str) == Some("tool")
|| message.get("tool_use_id").is_some()
}
fn visit_tool_outputs(message: &mut Value, visit: &mut impl FnMut(Option<&str>, &mut String)) {
if is_tool_message(message) {
let id = ["tool_call_id", "tool_use_id"]
.iter()
.find_map(|key| message.get(*key).and_then(Value::as_str))
.map(str::to_owned);
if let Some(Value::String(content)) = message.get_mut("content") {
visit(id.as_deref(), content);
}
return;
}
let Some(blocks) = message.get_mut("content").and_then(Value::as_array_mut) else {
return;
};
for block in blocks {
let Some(id) = block
.get("tool_use_id")
.and_then(Value::as_str)
.map(str::to_owned)
else {
continue;
};
if let Some(Value::String(content)) = block.get_mut("content") {
visit(Some(&id), content);
}
}
}
#[cfg(test)]
mod tests {
use super::dedup_tool_outputs;
use serde_json::{Value, json};
fn tool(id: &str, content: &str) -> Value {
json!({"role": "tool", "tool_call_id": id, "content": content})
}
fn content(message: &Value) -> &str {
message["content"].as_str().unwrap()
}
fn ls_output(date: &str) -> String {
let mut lines: Vec<String> = (0..30)
.map(|index| {
format!("-rw-r--r-- 1 dev staff {index:>6} Oct 1 09:00 src/file_{index}.rs")
})
.collect();
lines[12] = format!("-rwxr-xr-x 1 dev staff 52428800 {date} target/release/lean-ctx");
lines.join("\n")
}
#[test]
fn near_duplicate_keeps_the_changed_lines_verbatim() {
let _data = lean_ctx::core::data_dir::isolated_data_dir();
let _community = lean_ctx::core::policy::runtime::TestPolicyOverride::set(None);
let before = ls_output("Oct 1 09:00");
let after = ls_output("Oct 2 18:53");
let mut messages = vec![
tool("call_1", &before),
tool("call_2", &after),
tool("call_3", "recent one"),
tool("call_4", "recent two"),
];
let stats = dedup_tool_outputs(&mut messages);
assert_eq!(content(&messages[0]), before);
assert_eq!(
content(&messages[1]),
"[lean-ctx: tool result call_1 above, except line 13 is replaced by:\n\
-rwxr-xr-x 1 dev staff 52428800 Oct 2 18:53 target/release/lean-ctx\n]"
);
assert_eq!(stats.delta_deduped, 1);
}
#[test]
fn resent_history_never_references_content_absent_from_the_request() {
let unique = "unique tool output ".repeat(40);
let request = vec![
tool("call_1", &unique),
tool("call_2", "recent one"),
tool("call_3", "recent two"),
];
for _ in 0..2 {
let mut resent = request.clone();
assert_eq!(dedup_tool_outputs(&mut resent).tokens_saved, 0);
assert_eq!(resent, request);
}
}
#[test]
fn exact_duplicate_references_the_earlier_result_by_id() {
let repeated = "cargo test: 812 passed\n".repeat(20);
let mut messages = vec![
json!({"role": "user", "content": [
{"type": "tool_result", "tool_use_id": "toolu_a", "content": repeated}
]}),
json!({"role": "user", "content": [
{"type": "tool_result", "tool_use_id": "toolu_b", "content": repeated}
]}),
tool("call_3", "recent one"),
tool("call_4", "recent two"),
];
let stats = dedup_tool_outputs(&mut messages);
assert_eq!(messages[0]["content"][0]["content"], json!(repeated));
assert_eq!(
messages[1]["content"][0]["content"],
json!("[lean-ctx: identical to tool result toolu_a above]")
);
assert_eq!(stats.exact_deduped, 1);
}
#[test]
fn line_ending_differences_are_never_described_as_a_delta() {
let lf = ls_output("Oct 1 09:00");
for changed in [
ls_output("Oct 2 18:53").replace('\n', "\r\n"),
format!("{}\n", ls_output("Oct 2 18:53")),
] {
let mut messages = vec![
tool("call_1", &lf),
tool("call_2", &changed),
tool("call_3", "recent one"),
tool("call_4", "recent two"),
];
assert_eq!(dedup_tool_outputs(&mut messages).delta_deduped, 0);
assert_eq!(content(&messages[1]), changed);
}
}
#[test]
fn outputs_without_an_id_are_never_referenced() {
let repeated = "same output ".repeat(40);
let mut messages = vec![
json!({"role": "tool", "content": repeated}),
tool("call_2", &repeated),
tool("call_3", "recent one"),
tool("call_4", "recent two"),
];
let original = messages.clone();
assert_eq!(dedup_tool_outputs(&mut messages).tokens_saved, 0);
assert_eq!(messages, original);
}
}