use sha2::{Digest, Sha256};
use std::collections::HashMap;
use super::ExperimentalStats;
use crate::provider::{ContentPart, Message};
pub const KEEP_LAST_MESSAGES: usize = 8;
pub const MIN_COLLAPSE_BYTES: usize = 64;
pub fn collapse_duplicate_calls(messages: &mut [Message]) -> ExperimentalStats {
let mut stats = ExperimentalStats::default();
let total = messages.len();
if total <= KEEP_LAST_MESSAGES {
return stats;
}
let eligible = total - KEEP_LAST_MESSAGES;
let mut seen: HashMap<[u8; 32], String> = HashMap::new();
for msg in messages[..eligible].iter_mut() {
for part in msg.content.iter_mut() {
let ContentPart::ToolCall {
id,
name,
arguments,
..
} = part
else {
continue;
};
if arguments.len() < MIN_COLLAPSE_BYTES {
continue;
}
if arguments.starts_with("[REPEAT") {
continue;
}
let mut h = Sha256::new();
h.update(name.as_bytes());
h.update(b"\0");
h.update(arguments.as_bytes());
let key: [u8; 32] = h.finalize().into();
match seen.get(&key) {
Some(first_id) => {
let marker =
format!("[REPEAT of call_id={first_id}, {} bytes]", arguments.len());
let saved = arguments.len().saturating_sub(marker.len());
*arguments = marker;
stats.dedup_hits += 1;
stats.total_bytes_saved += saved;
}
None => {
seen.insert(key, id.clone());
}
}
}
}
stats
}
#[cfg(test)]
mod tests {
use super::*;
use crate::provider::Role;
fn call(id: &str, name: &str, args: &str) -> Message {
Message {
role: Role::Assistant,
content: vec![ContentPart::ToolCall {
id: id.into(),
name: name.into(),
arguments: args.into(),
thought_signature: None,
}],
}
}
#[test]
fn different_names_not_merged() {
let args = "a".repeat(200);
let mut msgs = vec![
call("1", "read_file", &args),
call("2", "write_file", &args),
];
for _ in 0..KEEP_LAST_MESSAGES + 1 {
msgs.push(Message {
role: Role::User,
content: vec![ContentPart::Text { text: "q".into() }],
});
}
let stats = collapse_duplicate_calls(&mut msgs);
assert_eq!(stats.dedup_hits, 0);
}
#[test]
fn small_args_preserved() {
let mut msgs = vec![call("1", "noop", "{}"), call("2", "noop", "{}")];
for _ in 0..KEEP_LAST_MESSAGES + 1 {
msgs.push(Message {
role: Role::User,
content: vec![ContentPart::Text { text: "q".into() }],
});
}
let stats = collapse_duplicate_calls(&mut msgs);
assert_eq!(stats.dedup_hits, 0);
}
#[test]
fn recent_calls_preserved() {
let args = "z".repeat(200);
let mut msgs = vec![call("1", "f", &args)];
for _ in 0..KEEP_LAST_MESSAGES - 1 {
msgs.push(Message {
role: Role::User,
content: vec![ContentPart::Text { text: "q".into() }],
});
}
msgs.push(call("2", "f", &args));
let stats = collapse_duplicate_calls(&mut msgs);
assert_eq!(stats.dedup_hits, 0);
}
}