From 5b41f18c3dea56708efc154b03fa6f98a0bf66f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E9=97=BB=E6=B6=B50668001344?= Date: Tue, 16 Jun 2026 20:51:59 +0800 Subject: [PATCH] fix(gemini): set both camelCase and snake_case thought_signature in tool call request body MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Gemini HTTP API provider only set thoughtSignature (camelCase) when building tool call request bodies. Gemini 2.5 models accept camelCase, but Gemini 3.5 Flash Agentic reasoning requires thought_signature (snake_case) — matching the format it returns in API responses. When only the camelCase variant was sent, Gemini 3.5 Flash rejected the request with a 400 Bad Request for missing thought_signature. The fix sets both ThoughtSignature and ThoughtSignatureSnake in the geminiPart struct, matching the pattern already used in antigravity_provider.go. This ensures compatibility with both Gemini 2.x (camelCase) and Gemini 3.x Agentic (snake_case) models. Closes #3111 --- pkg/providers/httpapi/gemini_provider.go | 1 + pkg/providers/httpapi/gemini_provider_test.go | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pkg/providers/httpapi/gemini_provider.go b/pkg/providers/httpapi/gemini_provider.go index 9d0b44e1..338a5659 100644 --- a/pkg/providers/httpapi/gemini_provider.go +++ b/pkg/providers/httpapi/gemini_provider.go @@ -285,6 +285,7 @@ func (p *GeminiProvider) buildRequestBody( } if thoughtSignature != "" { part.ThoughtSignature = thoughtSignature + part.ThoughtSignatureSnake = thoughtSignature } content.Parts = append(content.Parts, part) } diff --git a/pkg/providers/httpapi/gemini_provider_test.go b/pkg/providers/httpapi/gemini_provider_test.go index 05080b81..bb4bea6c 100644 --- a/pkg/providers/httpapi/gemini_provider_test.go +++ b/pkg/providers/httpapi/gemini_provider_test.go @@ -453,7 +453,7 @@ func TestGeminiProvider_ChatStreamReturnsErrorOnInvalidDataFrame(t *testing.T) { } } -func TestGeminiProvider_BuildRequestBody_UsesCamelCaseThoughtSignatureOnly(t *testing.T) { +func TestGeminiProvider_BuildRequestBody_SetsBothThoughtSignatureFormats(t *testing.T) { provider := NewGeminiProvider("test-key", "https://example.com/v1beta", "", "", 0, nil, nil) body := provider.buildRequestBody( @@ -484,8 +484,8 @@ func TestGeminiProvider_BuildRequestBody_UsesCamelCaseThoughtSignatureOnly(t *te if !strings.Contains(jsonBody, `"thoughtSignature":"sig-1"`) { t.Fatalf("request body = %s, expected camelCase thoughtSignature", jsonBody) } - if strings.Contains(jsonBody, `"thought_signature"`) { - t.Fatalf("request body = %s, unexpected snake_case thought_signature", jsonBody) + if !strings.Contains(jsonBody, `"thought_signature":"sig-1"`) { + t.Fatalf("request body = %s, expected snake_case thought_signature for Gemini 3.5 Flash Agentic compatibility", jsonBody) } }