crabtalk_core/agent/
compact.rs1use crate::model::{Message, Model, Request, Role};
4
5pub(crate) const COMPACT_PROMPT: &str = include_str!("../../prompts/compact.md");
6
7impl<M: Model> super::Agent<M> {
8 pub async fn compact(&self, history: &[Message]) -> Option<String> {
14 let model_name = self
15 .config
16 .model
17 .clone()
18 .unwrap_or_else(|| self.model.active_model());
19
20 let prompt = COMPACT_PROMPT.to_owned();
21
22 let mut messages = Vec::with_capacity(2 + history.len());
23 messages.push(Message::system(&prompt));
24 if !self.config.system_prompt.is_empty() {
27 messages.push(Message::user(format!(
28 "Agent system prompt (preserve identity/profile info):\n{}",
29 self.config.system_prompt
30 )));
31 }
32 let max_len = self.config.compact_tool_max_len;
33 messages.extend(history.iter().cloned().map(|mut m| {
34 if m.role == Role::Tool && m.content.len() > max_len {
35 m.content.truncate(m.content.floor_char_boundary(max_len));
36 m.content.push_str("... [truncated]");
37 }
38 m
39 }));
40
41 let request = Request::new(model_name).with_messages(messages);
42 match self.model.send(&request).await {
43 Ok(response) => response.content().cloned(),
44 Err(e) => {
45 tracing::warn!("compaction LLM call failed: {e}");
46 None
47 }
48 }
49 }
50
51 pub(crate) fn estimate_tokens(history: &[Message]) -> usize {
56 let total_chars: usize = history
57 .iter()
58 .map(|m| {
59 let mut chars = m.content.len() + m.reasoning_content.len();
60 for tc in &m.tool_calls {
61 chars += tc.function.arguments.len();
62 }
63 chars
64 })
65 .sum();
66 total_chars / 4
67 }
68}