diff --git a/crates/goose/src/agents/agent.rs b/crates/goose/src/agents/agent.rs index 57421320e1..bb5c37747c 100644 --- a/crates/goose/src/agents/agent.rs +++ b/crates/goose/src/agents/agent.rs @@ -1235,11 +1235,29 @@ impl Agent { messages_to_add.push(final_message_tool_resp); } } - Err(ProviderError::ContextLengthExceeded(_)) => { - yield AgentEvent::Message(Message::assistant().with_context_length_exceeded( - "The context length of the model has been exceeded. Please start a new session and try again.", - )); - break; + Err(ProviderError::ContextLengthExceeded(error_msg)) => { + info!("Context length exceeded, attempting compaction"); + + match auto_compact::perform_compaction(self, messages.messages()).await { + Ok(compact_result) => { + messages = compact_result.messages; + + yield AgentEvent::Message( + Message::assistant().with_summarization_requested( + "Context limit reached. Conversation has been automatically compacted to continue." + ) + ); + yield AgentEvent::HistoryReplaced(messages.messages().to_vec()); + + continue; + } + Err(_) => { + yield AgentEvent::Message(Message::assistant().with_context_length_exceeded( + format!("Context length exceeded and cannot summarize: {}. Unable to continue.", error_msg) + )); + break; + } + } } Err(e) => { error!("Error: {}", e); diff --git a/crates/goose/src/context_mgmt/auto_compact.rs b/crates/goose/src/context_mgmt/auto_compact.rs index 0783a8257d..826dab8561 100644 --- a/crates/goose/src/context_mgmt/auto_compact.rs +++ b/crates/goose/src/context_mgmt/auto_compact.rs @@ -120,6 +120,50 @@ pub async fn check_compaction_needed( }) } +/// Perform compaction on messages without checking thresholds +/// +/// This function directly performs compaction on the provided messages. +/// If the most recent message is a user message, it will be preserved by removing it +/// before compaction and adding it back afterwards. +/// +/// # Arguments +/// * `agent` - The agent to use for context management +/// * `messages` - The current message history +/// +/// # Returns +/// * `AutoCompactResult` containing the compacted messages and metadata +pub async fn perform_compaction(agent: &Agent, messages: &[Message]) -> Result { + info!("Performing message compaction"); + + // Check if the most recent message is a user message + let (messages_to_compact, preserved_user_message) = if let Some(last_message) = messages.last() + { + if matches!(last_message.role, rmcp::model::Role::User) { + // Remove the last user message before compaction + (&messages[..messages.len() - 1], Some(last_message.clone())) + } else { + (messages, None) + } + } else { + (messages, None) + }; + + // Perform the compaction on messages excluding the preserved user message + let (mut compacted_messages, _, summarization_usage) = + agent.summarize_context(messages_to_compact).await?; + + // Add back the preserved user message if it exists + if let Some(user_message) = preserved_user_message { + compacted_messages.push(user_message); + } + + Ok(AutoCompactResult { + compacted: true, + messages: compacted_messages, + summarization_usage, + }) +} + /// Check if messages need compaction and compact them if necessary /// /// This is a convenience wrapper function that combines checking and compaction. @@ -163,33 +207,8 @@ pub async fn check_and_compact_messages( check_result.usage_ratio * 100.0 ); - // Check if the most recent message is a user message - let (messages_to_compact, preserved_user_message) = if let Some(last_message) = messages.last() - { - if matches!(last_message.role, rmcp::model::Role::User) { - // Remove the last user message before auto-compaction - (&messages[..messages.len() - 1], Some(last_message.clone())) - } else { - (messages, None) - } - } else { - (messages, None) - }; - - // Perform the compaction on messages excluding the preserved user message - let (mut compacted_messages, _, summarization_usage) = - agent.summarize_context(messages_to_compact).await?; - - // Add back the preserved user message if it exists - if let Some(user_message) = preserved_user_message { - compacted_messages.push(user_message); - } - - Ok(AutoCompactResult { - compacted: true, - messages: compacted_messages, - summarization_usage, - }) + // Use perform_compaction to do the actual work + perform_compaction(agent, messages).await } #[cfg(test)]