Skip to content
Open
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
16 changes: 12 additions & 4 deletions crates/goose/src/agents/large_response_handler.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
use crate::config::Config;
use chrono::Utc;
use rmcp::model::{CallToolResult, Content, ErrorData};
use std::fs::File;
use std::io::Write;

const LARGE_TEXT_THRESHOLD: usize = 200_000;
const DEFAULT_LARGE_TEXT_THRESHOLD: usize = 200_000;

fn large_text_threshold() -> usize {
Config::global()
.get_param::<usize>("GOOSE_MAX_TOOL_RESPONSE_SIZE")
.unwrap_or(DEFAULT_LARGE_TEXT_THRESHOLD)
Comment on lines +9 to +12
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Cache configured response-size limit

Avoid reading GOOSE_MAX_TOOL_RESPONSE_SIZE from Config on every tool result. process_tool_response runs for each tool call, and large_text_threshold() now calls Config::global().get_param(...), which falls through to config-file loading/parsing when the env var is unset. That introduces synchronous filesystem/config-deserialization work on a hot path and can add noticeable latency in tool-heavy sessions compared to the previous constant-time check.

Useful? React with 👍 / 👎.

}

/// Process tool response and handle large text content
pub fn process_tool_response(
response: Result<CallToolResult, ErrorData>,
) -> Result<CallToolResult, ErrorData> {
let threshold = large_text_threshold();
match response {
Ok(mut result) => {
let mut processed_contents = Vec::new();
Expand All @@ -17,7 +25,7 @@ pub fn process_tool_response(
match content.as_text() {
Some(text_content) => {
// Check if text exceeds threshold
if text_content.text.chars().count() > LARGE_TEXT_THRESHOLD {
if text_content.text.chars().count() > threshold {
// Write to temp file
match write_large_text_to_file(&text_content.text) {
Ok(file_path) => {
Expand Down Expand Up @@ -107,7 +115,7 @@ mod tests {
#[test]
fn test_large_text_response_redirected_to_file() {
// Create a text larger than the threshold
let large_text = "a".repeat(LARGE_TEXT_THRESHOLD + 1000);
let large_text = "a".repeat(DEFAULT_LARGE_TEXT_THRESHOLD + 1000);
let content = Content::text(large_text.clone());

let response = Ok(CallToolResult::success(vec![content]));
Expand Down Expand Up @@ -166,7 +174,7 @@ mod tests {
fn test_mixed_content_handled_correctly() {
// Create a response with mixed content types
let small_text = Content::text("Small text");
let large_text = Content::text("a".repeat(LARGE_TEXT_THRESHOLD + 1000));
let large_text = Content::text("a".repeat(DEFAULT_LARGE_TEXT_THRESHOLD + 1000));
let image = Content::image("image_data".to_string(), "image/jpeg".to_string());

let response = Ok(CallToolResult::success(vec![small_text, large_text, image]));
Expand Down
Loading