From 5c5f836eca35368bf68f51e018d93c300b5efaf7 Mon Sep 17 00:00:00 2001 From: hobokenchicken Date: Thu, 5 Mar 2026 17:45:55 +0000 Subject: [PATCH] fix(gemini): override finish_reason to 'tool_calls' when tools are present Gemini often reports 'STOP' even when tool calls are generated. To remain OpenAI-compatible and ensure clients execute tools and continue, we must report 'tool_calls' as the finish_reason when tools are present. --- src/providers/gemini.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/providers/gemini.rs b/src/providers/gemini.rs index 44ad7231..be094c96 100644 --- a/src/providers/gemini.rs +++ b/src/providers/gemini.rs @@ -927,12 +927,18 @@ impl super::Provider for GeminiProvider { let tool_calls = if deltas.is_empty() { None } else { Some(deltas) }; // Determine finish_reason - let finish_reason = candidate.finish_reason.as_ref().map(|fr| { - match fr.as_str() { - "STOP" => "stop".to_string(), - _ => fr.to_lowercase(), - } - }); + // STRATEGY: If we have tool calls, the finish_reason MUST be "tool_calls" + // to comply with OpenAI-style expectations and ensure the client continues. + let finish_reason = if tool_calls.is_some() { + Some("tool_calls".to_string()) + } else { + candidate.finish_reason.as_ref().map(|fr| { + match fr.as_str() { + "STOP" => "stop".to_string(), + _ => fr.to_lowercase(), + } + }) + }; // Avoid emitting completely empty chunks unless they carry usage. if !content.is_empty() || reasoning_content.is_some() || tool_calls.is_some() || stream_usage.is_some() {