use super::*;
pub(crate) fn bound_history_for_summarization(
history: &[Message],
instructions: &str,
budget: Option<usize>,
) -> Vec<Message> {
let Some(budget) = budget.filter(|value| *value > 0) else {
return history.to_vec();
};
let instruction_tokens = Message::user(instructions.to_string()).estimate_tokens();
let history_budget = budget.saturating_sub(instruction_tokens).max(4);
bound_history_to_token_budget(history, history_budget)
}
pub(crate) fn bound_history_for_native_compaction(
history: &[Message],
instructions: &str,
budget: Option<usize>,
) -> Vec<Message> {
let Some(budget) = budget.filter(|value| *value > 0) else {
return history.to_vec();
};
let instruction_tokens = Message::user(instructions.to_string()).estimate_tokens();
let history_budget = budget.saturating_sub(instruction_tokens).max(4);
let total_tokens = history.iter().map(Message::estimate_tokens).sum::<usize>();
if total_tokens <= history_budget {
return history.to_vec();
}
let Some(marker_index) = history.iter().rposition(is_provider_compaction_message) else {
return bound_history_to_token_budget(history, history_budget);
};
let marker = history[marker_index].clone();
let marker_tokens = marker.estimate_tokens();
let mut bounded = vec![marker];
if marker_tokens < history_budget {
bounded.extend(bound_history_to_token_budget(&history[marker_index + 1..], history_budget - marker_tokens));
}
bounded
}
fn bound_history_to_token_budget(history: &[Message], history_budget: usize) -> Vec<Message> {
let total_tokens = history.iter().map(Message::estimate_tokens).sum::<usize>();
if total_tokens <= history_budget {
return history.to_vec();
}
let group_starts: Vec<usize> = history
.iter()
.enumerate()
.filter_map(|(index, message)| (message.role == MessageRole::User).then_some(index))
.collect();
let last_group_index = group_starts.len().saturating_sub(1);
let mut selected_start = history.len();
let mut selected_end = history.len();
let mut selected_tokens = 0usize;
for (position, &start) in group_starts.iter().enumerate().rev() {
let natural_end = group_starts.get(position + 1).copied().unwrap_or(history.len());
let end = if position == last_group_index {
start.saturating_add(complete_protocol_group_prefix(&history[start..natural_end]))
} else {
natural_end
};
if start >= end {
continue;
}
let group_tokens = history[start..end].iter().map(Message::estimate_tokens).sum::<usize>();
if selected_tokens.saturating_add(group_tokens) > history_budget {
break;
}
if selected_start == history.len() {
selected_end = end;
}
selected_tokens += group_tokens;
selected_start = start;
}
if selected_start >= selected_end {
return bounded_protocol_group(history, history_budget);
}
history[selected_start..selected_end].to_vec()
}
fn is_provider_compaction_message(message: &Message) -> bool {
message.role == MessageRole::Assistant
&& message
.reasoning_details
.as_ref()
.is_some_and(|details| details.iter().any(is_compaction_detail))
}