diff --git a/crates/language_models/src/provider/copilot_chat.rs b/crates/language_models/src/provider/copilot_chat.rs index fe11458282..d200248269 100644 --- a/crates/language_models/src/provider/copilot_chat.rs +++ b/crates/language_models/src/provider/copilot_chat.rs @@ -368,25 +368,34 @@ pub fn map_to_language_model_completion_events( } Some("tool_calls") => { events.extend(state.tool_calls_by_index.drain().map( - |(_, tool_call)| match serde_json::Value::from_str( - &tool_call.arguments, - ) { - Ok(input) => Ok(LanguageModelCompletionEvent::ToolUse( - LanguageModelToolUse { - id: tool_call.id.clone().into(), - name: tool_call.name.as_str().into(), - is_input_complete: true, - input, - raw_input: tool_call.arguments.clone(), - }, - )), - Err(error) => { - Err(LanguageModelCompletionError::BadInputJson { - id: tool_call.id.into(), - tool_name: tool_call.name.as_str().into(), - raw_input: tool_call.arguments.into(), - json_parse_error: error.to_string(), - }) + |(_, tool_call)| { + // The model can output an empty string + // to indicate the absence of arguments. + // When that happens, create an empty + // object instead. + let arguments = if tool_call.arguments.is_empty() { + Ok(serde_json::Value::Object(Default::default())) + } else { + serde_json::Value::from_str(&tool_call.arguments) + }; + match arguments { + Ok(input) => Ok(LanguageModelCompletionEvent::ToolUse( + LanguageModelToolUse { + id: tool_call.id.clone().into(), + name: tool_call.name.as_str().into(), + is_input_complete: true, + input, + raw_input: tool_call.arguments.clone(), + }, + )), + Err(error) => { + Err(LanguageModelCompletionError::BadInputJson { + id: tool_call.id.into(), + tool_name: tool_call.name.as_str().into(), + raw_input: tool_call.arguments.into(), + json_parse_error: error.to_string(), + }) + } } }, ));