Strip the audience for compacting (#6646)

Co-authored-by: Douwe Osinga <douwe@squareup.com>
This commit is contained in:
Douwe Osinga
2026-01-22 16:13:35 -05:00
committed by GitHub
parent 86b38c6a4f
commit c57c2562a1
4 changed files with 84 additions and 15 deletions
+5 -2
View File
@@ -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,
@@ -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(
+8 -7
View File
@@ -222,7 +222,7 @@ 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()
@@ -230,7 +230,7 @@ fn filter_tool_responses<'a>(messages: &[&'a Message], remove_percent: u32) -> V
}
if remove_percent == 0 {
return messages.to_vec();
return messages.iter().collect();
}
let tool_indices: Vec<usize> = messages
@@ -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);
@@ -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()
}
@@ -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
@@ -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 {
@@ -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)
+71
View File
@@ -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 {
@@ -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 {
-6
View File
@@ -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?)
}