use crate::model::{Message, Model, Request, Role};
pub(crate) const COMPACT_PROMPT: &str = include_str!("../../prompts/compact.md");
impl<M: Model> super::Agent<M> {
pub async fn compact(&self, history: &[Message]) -> Option<String> {
let model_name = self
.config
.model
.clone()
.unwrap_or_else(|| self.model.active_model());
let prompt = COMPACT_PROMPT.to_owned();
let mut messages = Vec::with_capacity(2 + history.len());
messages.push(Message::system(&prompt));
if !self.config.system_prompt.is_empty() {
messages.push(Message::user(format!(
"Agent system prompt (preserve identity/profile info):\n{}",
self.config.system_prompt
)));
}
let max_len = self.config.compact_tool_max_len;
messages.extend(history.iter().cloned().map(|mut m| {
if m.role == Role::Tool && m.content.len() > max_len {
m.content.truncate(m.content.floor_char_boundary(max_len));
m.content.push_str("... [truncated]");
}
m
}));
let request = Request::new(model_name).with_messages(messages);
match self.model.send(&request).await {
Ok(response) => response.content().cloned(),
Err(e) => {
tracing::warn!("compaction LLM call failed: {e}");
None
}
}
}
pub(crate) fn estimate_tokens(history: &[Message]) -> usize {
let total_chars: usize = history
.iter()
.map(|m| {
let mut chars = m.content.len() + m.reasoning_content.len();
for tc in &m.tool_calls {
chars += tc.function.arguments.len();
}
chars
})
.sum();
total_chars / 4
}
}