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
7 changes: 5 additions & 2 deletions crates/goose-server/src/routes/session.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::routes::errors::ErrorResponse;
use crate::routes::recipe_utils::{apply_recipe_to_agent, build_recipe_with_parameter_values};
use crate::state::AppState;
use axum::extract::State;
use axum::extract::{DefaultBodyLimit, State};
use axum::routing::post;
use axum::{
extract::Path,
Expand Down Expand Up @@ -493,7 +493,10 @@ pub fn routes(state: Arc<AppState>) -> Router {
.route("/sessions/{session_id}", get(get_session))
.route("/sessions/{session_id}", delete(delete_session))
.route("/sessions/{session_id}/export", get(export_session))
.route("/sessions/import", post(import_session))
.route(
"/sessions/import",
post(import_session).layer(DefaultBodyLimit::max(25 * 1024 * 1024)),
)
.route("/sessions/insights", get(get_session_insights))
.route("/sessions/{session_id}/name", put(update_session_name))
.route(
Expand Down
15 changes: 8 additions & 7 deletions crates/goose/src/context_mgmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,15 +222,15 @@ pub async fn check_if_compaction_needed(
Ok(needs_compaction)
}

fn filter_tool_responses<'a>(messages: &[&'a Message], remove_percent: u32) -> Vec<&'a Message> {
fn filter_tool_responses(messages: &[Message], remove_percent: u32) -> Vec<&Message> {
fn has_tool_response(msg: &Message) -> bool {
msg.content
.iter()
.any(|c| matches!(c, MessageContent::ToolResponse(_)))
}

if remove_percent == 0 {
return messages.to_vec();
return messages.iter().collect();
}

let tool_indices: Vec<usize> = messages
Expand All @@ -241,7 +241,7 @@ fn filter_tool_responses<'a>(messages: &[&'a Message], remove_percent: u32) -> V
.collect();

if tool_indices.is_empty() {
return messages.to_vec();
return messages.iter().collect();
}

let num_to_remove = ((tool_indices.len() * remove_percent as usize) / 100).max(1);
Expand All @@ -268,7 +268,7 @@ fn filter_tool_responses<'a>(messages: &[&'a Message], remove_percent: u32) -> V
.iter()
.enumerate()
.filter(|(i, _)| !indices_to_remove.contains(i))
.map(|(_, msg)| *msg)
.map(|(_, msg)| msg)
.collect()
}

Expand All @@ -277,9 +277,10 @@ async fn do_compact(
session_id: &str,
messages: &[Message],
) -> Result<(Message, ProviderUsage), anyhow::Error> {
let agent_visible_messages: Vec<&Message> = messages
let agent_visible_messages: Vec<Message> = messages
.iter()
.filter(|msg| msg.is_agent_visible())
.map(|msg| msg.agent_visible_content())
.collect();

// Try progressively removing more tool response messages from the middle to reduce context length
Expand Down Expand Up @@ -350,7 +351,7 @@ fn format_message_for_compacting(msg: &Message) -> String {
format!(
"tool_request({}): {}",
call.name,
serde_json::to_string_pretty(&call.arguments)
serde_json::to_string(&call.arguments)
.unwrap_or_else(|_| "<<invalid json>>".to_string())
)
} else {
Expand Down Expand Up @@ -397,7 +398,7 @@ fn format_message_for_compacting(msg: &Message) -> String {
"frontend_tool_request: [error]".to_string()
}
}
MessageContent::Thinking(thinking) => format!("thinking: {}", thinking.thinking),
MessageContent::Thinking(_) => "thinking".to_string(),
MessageContent::RedactedThinking(_) => "redacted_thinking".to_string(),
MessageContent::SystemNotification(notification) => {
format!("system_notification: {}", notification.msg)
Expand Down
71 changes: 71 additions & 0 deletions crates/goose/src/conversation/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,64 @@ impl MessageContent {
)
}

pub fn filter_for_audience(&self, audience: Role) -> Option<MessageContent> {
match self {
MessageContent::Text(text) => {
if text
.audience()
.map(|roles| roles.contains(&audience))
.unwrap_or(true)
{
Some(self.clone())
} else {
None
}
}
MessageContent::Image(img) => {
if img
.audience()
.map(|roles| roles.contains(&audience))
.unwrap_or(true)
{
Some(self.clone())
} else {
None
}
}
MessageContent::ToolResponse(res) => {
let Ok(result) = &res.tool_result else {
return Some(self.clone());
};

let filtered_content: Vec<Content> = result
.content
.iter()
.filter(|c| {
c.audience()
.map(|roles| roles.contains(&audience))
.unwrap_or(true)
})
.cloned()
.collect();

if filtered_content.is_empty() {
return None;
}

Some(MessageContent::ToolResponse(ToolResponse {
id: res.id.clone(),
tool_result: Ok(CallToolResult {
content: filtered_content,
..result.clone()
}),
metadata: res.metadata.clone(),
}))
}
MessageContent::Thinking(_) | MessageContent::RedactedThinking(_) => None,
_ => Some(self.clone()),
}
}

pub fn image<S: Into<String>, T: Into<String>>(data: S, mime_type: T) -> Self {
MessageContent::Image(
RawImageContent {
Expand Down Expand Up @@ -621,6 +679,19 @@ impl Message {
format!("{:?}", self)
}

pub fn agent_visible_content(&self) -> Message {
let filtered_content = self
.content
.iter()
.filter_map(|c| c.filter_for_audience(Role::Assistant))
.collect();

Message {
content: filtered_content,
..self.clone()
}
}

/// Create a new user message with the current timestamp
pub fn user() -> Self {
Message {
Expand Down
6 changes: 0 additions & 6 deletions crates/goose/src/providers/api_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,12 +354,6 @@ impl<'a> ApiRequestBuilder<'a> {
}

pub async fn response_post(self, payload: &Value) -> Result<Response> {
// Log the JSON payload being sent to the LLM
tracing::debug!(
"LLM_REQUEST: {}",
serde_json::to_string(payload).unwrap_or_else(|_| "{}".to_string())
);

let request = self.send_request(|url, client| client.post(url)).await?;
Ok(request.json(payload).send().await?)
}
Expand Down
Loading