synaptic_condenser/
llm_summarizing.rs1use std::sync::Arc;
2
3use crate::Condenser;
4use async_trait::async_trait;
5use synaptic_core::{ChatModel, ChatRequest, Message, SynapticError};
6
7pub struct LlmSummarizingCondenser {
9 model: Arc<dyn ChatModel>,
10 max_tokens: usize,
11 keep_recent: usize,
12}
13
14impl LlmSummarizingCondenser {
15 pub fn new(model: Arc<dyn ChatModel>, max_tokens: usize, keep_recent: usize) -> Self {
16 Self {
17 model,
18 max_tokens,
19 keep_recent,
20 }
21 }
22}
23
24#[async_trait]
25impl Condenser for LlmSummarizingCondenser {
26 async fn condense(&self, messages: Vec<Message>) -> Result<Vec<Message>, SynapticError> {
27 let estimated_tokens: usize = messages.iter().map(|m| m.content().len() / 4 + 4).sum();
29 if estimated_tokens <= self.max_tokens {
30 return Ok(messages);
31 }
32
33 let (system_msg, rest) = if !messages.is_empty() && messages[0].is_system() {
35 (Some(messages[0].clone()), &messages[1..])
36 } else {
37 (None, messages.as_slice())
38 };
39
40 if rest.len() <= self.keep_recent {
41 return Ok(messages);
42 }
43
44 let split = rest.len() - self.keep_recent;
45 let to_summarize = &rest[..split];
46 let to_keep = &rest[split..];
47
48 let mut summary_text = String::new();
50 for msg in to_summarize {
51 summary_text.push_str(&format!("{}: {}\n", msg.role(), msg.content()));
52 }
53
54 let prompt = format!(
55 "Summarize the following conversation concisely, preserving key information:\n\n{}",
56 summary_text
57 );
58
59 let request = ChatRequest::new(vec![Message::human(prompt)]);
60 let response = self.model.chat(request).await?;
61 let summary = response.message.content().to_string();
62
63 let mut result = Vec::new();
65 if let Some(sys) = system_msg {
66 result.push(sys);
67 }
68 result.push(Message::system(format!(
69 "[Conversation Summary]\n{}",
70 summary
71 )));
72 result.extend_from_slice(to_keep);
73
74 Ok(result)
75 }
76}