Compare commits

...

1 Commits

Author SHA1 Message Date
Friel
b813aea26e Drop duplicate contiguous user messages during compaction 2026-04-23 05:17:18 +00:00
2 changed files with 83 additions and 5 deletions

View File

@@ -393,9 +393,10 @@ pub fn content_items_to_text(content: &[ContentItem]) -> Option<String> {
}
pub(crate) fn collect_user_messages(items: &[ResponseItem]) -> Vec<String> {
items
.iter()
.filter_map(|item| match crate::event_mapping::parse_turn_item(item) {
let mut messages = Vec::new();
let mut previous_message: Option<String> = Some(String::new());
for item in items {
let message = match crate::event_mapping::parse_turn_item(item) {
Some(TurnItem::UserMessage(user)) => {
if is_summary_message(&user.message()) {
None
@@ -404,8 +405,21 @@ pub(crate) fn collect_user_messages(items: &[ResponseItem]) -> Vec<String> {
}
}
_ => None,
})
.collect()
};
let Some(message) = message else {
previous_message = None;
continue;
};
if message.is_empty() {
continue;
}
if previous_message.as_deref() == Some(message.as_str()) {
continue;
}
previous_message = Some(message.clone());
messages.push(message);
}
messages
}
pub(crate) fn is_summary_message(message: &str) -> bool {

View File

@@ -125,6 +125,70 @@ do things
assert_eq!(vec!["real user message".to_string()], collected);
}
#[test]
fn collect_user_messages_drops_contiguous_duplicates_and_empty_messages() {
let items = vec![
ResponseItem::Message {
id: None,
role: "user".to_string(),
content: vec![ContentItem::InputText {
text: String::new(),
}],
end_turn: None,
phase: None,
},
ResponseItem::Message {
id: None,
role: "user".to_string(),
content: vec![ContentItem::InputText {
text: "repeat".to_string(),
}],
end_turn: None,
phase: None,
},
ResponseItem::Message {
id: None,
role: "user".to_string(),
content: vec![ContentItem::InputText {
text: "repeat".to_string(),
}],
end_turn: None,
phase: None,
},
ResponseItem::Message {
id: None,
role: "assistant".to_string(),
content: vec![ContentItem::OutputText {
text: "keeps the next user message non-contiguous".to_string(),
}],
end_turn: None,
phase: None,
},
ResponseItem::Message {
id: None,
role: "user".to_string(),
content: vec![ContentItem::InputText {
text: "repeat".to_string(),
}],
end_turn: None,
phase: None,
},
ResponseItem::Message {
id: None,
role: "user".to_string(),
content: vec![ContentItem::InputText {
text: String::new(),
}],
end_turn: None,
phase: None,
},
];
let collected = collect_user_messages(&items);
assert_eq!(vec!["repeat".to_string(), "repeat".to_string()], collected);
}
#[test]
fn build_token_limited_compacted_history_truncates_overlong_user_messages() {
// Use a small truncation limit so the test remains fast while still validating