Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 23 additions & 5 deletions crates/goose/src/agents/agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1218,11 +1218,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);
Expand Down
73 changes: 46 additions & 27 deletions crates/goose/src/context_mgmt/auto_compact.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<AutoCompactResult> {
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.
Expand Down Expand Up @@ -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)]
Expand Down
Loading