use super::ExperimentalStats;
use crate::provider::{ContentPart, Message, Role};
pub const KEEP_LAST_MESSAGES: usize = 4;
pub fn prune_thinking(messages: &mut Vec<Message>) -> ExperimentalStats {
let mut stats = ExperimentalStats::default();
let total = messages.len();
if total <= KEEP_LAST_MESSAGES {
return stats;
}
let eligible = total - KEEP_LAST_MESSAGES;
for msg in messages[..eligible].iter_mut() {
if msg.role != Role::Assistant {
continue;
}
let before: usize = msg.content.iter().map(thinking_bytes).sum();
msg.content
.retain(|p| !matches!(p, ContentPart::Thinking { .. }));
let after: usize = msg.content.iter().map(thinking_bytes).sum();
let saved = before.saturating_sub(after);
if saved > 0 {
stats.total_bytes_saved += saved;
stats.snippet_hits += 1;
}
}
let mut write = 0;
for read in 0..messages.len() {
let drop = read < eligible
&& messages[read].role == Role::Assistant
&& messages[read].content.is_empty();
if !drop {
if write != read {
messages.swap(write, read);
}
write += 1;
}
}
messages.truncate(write);
stats
}
fn thinking_bytes(p: &ContentPart) -> usize {
match p {
ContentPart::Thinking { text } => text.len(),
_ => 0,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn recent_thinking_preserved() {
let mut msgs = vec![Message {
role: Role::Assistant,
content: vec![ContentPart::Thinking {
text: "x".repeat(5000),
}],
}];
let stats = prune_thinking(&mut msgs);
assert_eq!(stats.total_bytes_saved, 0);
}
#[test]
fn empty_assistant_dropped() {
let mut msgs = vec![Message {
role: Role::Assistant,
content: vec![ContentPart::Thinking {
text: "x".repeat(1000),
}],
}];
for i in 0..KEEP_LAST_MESSAGES + 1 {
msgs.push(Message {
role: Role::User,
content: vec![ContentPart::Text {
text: format!("q{i}"),
}],
});
}
let before = msgs.len();
prune_thinking(&mut msgs);
assert_eq!(msgs.len(), before - 1);
}
#[test]
fn user_thinking_untouched() {
let mut msgs = vec![Message {
role: Role::User,
content: vec![ContentPart::Thinking {
text: "user-thought".repeat(100),
}],
}];
for i in 0..KEEP_LAST_MESSAGES + 1 {
msgs.push(Message {
role: Role::Assistant,
content: vec![ContentPart::Text {
text: format!("r{i}"),
}],
});
}
let stats = prune_thinking(&mut msgs);
assert_eq!(stats.total_bytes_saved, 0);
}
}