From 8dad86dc5f026e68e99543fd977438f09bd226bd Mon Sep 17 00:00:00 2001 From: Yingjie He Date: Thu, 6 Feb 2025 10:34:57 -0800 Subject: [PATCH] fix: handle empty arguments in tool call (#1111) Co-authored-by: Salman Mohammed --- crates/goose/src/providers/formats/openai.rs | 25 +++++++++++++++++++- crates/goose/tests/truncate_agent.rs | 10 +++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/crates/goose/src/providers/formats/openai.rs b/crates/goose/src/providers/formats/openai.rs index 2c69a614..528c3f23 100644 --- a/crates/goose/src/providers/formats/openai.rs +++ b/crates/goose/src/providers/formats/openai.rs @@ -183,10 +183,14 @@ pub fn response_to_message(response: Value) -> anyhow::Result { .as_str() .unwrap_or_default() .to_string(); - let arguments = tool_call["function"]["arguments"] + let mut arguments = tool_call["function"]["arguments"] .as_str() .unwrap_or_default() .to_string(); + // If arguments is empty, we will have invalid json parsing error later. + if arguments.is_empty() { + arguments = "{}".to_string(); + } if !is_valid_function_name(&function_name) { let error = ToolError::NotFound(format!( @@ -581,4 +585,23 @@ mod tests { Ok(()) } + + #[test] + fn test_response_to_message_empty_argument() -> anyhow::Result<()> { + let mut response: Value = serde_json::from_str(OPENAI_TOOL_USE_RESPONSE)?; + response["choices"][0]["message"]["tool_calls"][0]["function"]["arguments"] = + serde_json::Value::String("".to_string()); + + let message = response_to_message(response)?; + + if let MessageContent::ToolRequest(request) = &message.content[0] { + let tool_call = request.tool_call.as_ref().unwrap(); + assert_eq!(tool_call.name, "example_fn"); + assert_eq!(tool_call.arguments, json!({})); + } else { + panic!("Expected ToolRequest content"); + } + + Ok(()) + } } diff --git a/crates/goose/tests/truncate_agent.rs b/crates/goose/tests/truncate_agent.rs index f8375086..4c8be6e9 100644 --- a/crates/goose/tests/truncate_agent.rs +++ b/crates/goose/tests/truncate_agent.rs @@ -13,7 +13,7 @@ use goose::providers::{ }; use goose::providers::{google::GoogleProvider, groq::GroqProvider}; -#[derive(Debug)] +#[derive(Debug, PartialEq)] enum ProviderType { Azure, OpenAi, @@ -136,6 +136,14 @@ async fn run_truncate_test( println!("Responses: {responses:?}\n"); assert_eq!(responses.len(), 1); + + // Ollama and OpenRouter truncate by default even when the context window is exceeded + // We don't have control over the truncation behavior in these providers + if provider_type == ProviderType::Ollama || provider_type == ProviderType::OpenRouter { + println!("WARNING: Skipping test for {:?} because it truncates by default when the context window is exceeded", provider_type); + return Ok(()); + } + assert_eq!(responses[0].content.len(), 1); let response_text = responses[0].content[0].as_text().unwrap();