Compact messages by summarizing old conversation turns. Returns `Some(new_messages)` if compaction was performed, or `None` if the message count is below the compaction threshold.
(
session_id: &str,
messages: &[Message],
llm_client: &Arc<dyn LlmClient>,
)
| 38 | /// Returns `Some(new_messages)` if compaction was performed, or `None` if |
| 39 | /// the message count is below the compaction threshold. |
| 40 | pub(crate) async fn compact_messages( |
| 41 | session_id: &str, |
| 42 | messages: &[Message], |
| 43 | llm_client: &Arc<dyn LlmClient>, |
| 44 | ) -> Result<Option<Vec<Message>>> { |
| 45 | if messages.len() <= MIN_MESSAGES_FOR_COMPACTION { |
| 46 | tracing::debug!( |
| 47 | "Session {} has {} messages, no compaction needed (threshold: {})", |
| 48 | session_id, |
| 49 | messages.len(), |
| 50 | MIN_MESSAGES_FOR_COMPACTION |
| 51 | ); |
| 52 | return Ok(None); |
| 53 | } |
| 54 | |
| 55 | tracing::info!( |
| 56 | "Compacting session {} with {} messages", |
| 57 | session_id, |
| 58 | messages.len() |
| 59 | ); |
| 60 | |
| 61 | let total = messages.len(); |
| 62 | let summarize_start = KEEP_INITIAL_MESSAGES; |
| 63 | let summarize_end = total.saturating_sub(KEEP_RECENT_MESSAGES); |
| 64 | |
| 65 | // If there's nothing to summarize, keep initial + recent messages |
| 66 | if summarize_end <= summarize_start { |
| 67 | tracing::debug!( |
| 68 | "Not enough messages to summarize, keeping initial {} + last {}", |
| 69 | KEEP_INITIAL_MESSAGES, |
| 70 | KEEP_RECENT_MESSAGES |
| 71 | ); |
| 72 | let mut result = messages[..KEEP_INITIAL_MESSAGES.min(total)].to_vec(); |
| 73 | let recent_start = total |
| 74 | .saturating_sub(KEEP_RECENT_MESSAGES) |
| 75 | .max(KEEP_INITIAL_MESSAGES); |
| 76 | result.extend_from_slice(&messages[recent_start..]); |
| 77 | return Ok(Some(result)); |
| 78 | } |
| 79 | |
| 80 | let initial_messages = messages[..summarize_start].to_vec(); |
| 81 | let messages_to_summarize = &messages[summarize_start..summarize_end]; |
| 82 | let recent_messages = messages[summarize_end..].to_vec(); |
| 83 | |
| 84 | tracing::debug!( |
| 85 | "Compaction split: {} initial, {} to summarize, {} recent", |
| 86 | initial_messages.len(), |
| 87 | messages_to_summarize.len(), |
| 88 | recent_messages.len() |
| 89 | ); |
| 90 | |
| 91 | // Build summarization prompt |
| 92 | let conversation_text = messages_to_summarize |
| 93 | .iter() |
| 94 | .map(|msg| { |
| 95 | let role = &msg.role; |
| 96 | let text = msg.text(); |
| 97 | format!("{}: {}", role, text) |