From 5c0612a07f1fe973d8845dfe155fc9532ec4f667 Mon Sep 17 00:00:00 2001 From: badgerbees <93577481+badgerbees@users.noreply.github.com> Date: Mon, 16 Mar 2026 22:53:21 +0700 Subject: [PATCH] fix(providers): prevent Gemini thought_signature from leaking to other providers and breaking validation (#230) * fix: prevent gemini thought_signature from leaking to other providers * test: refine gemini model detection for robust provider multiplexing --- internal/providers/openai.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/internal/providers/openai.go b/internal/providers/openai.go index f5d72d4a..fdb182b2 100644 --- a/internal/providers/openai.go +++ b/internal/providers/openai.go @@ -227,7 +227,14 @@ func (p *OpenAIProvider) buildRequestBody(model string, req ChatRequest, stream // don't return it (e.g. gemini-3-flash) will cause HTTP 400 if sent as-is. // Tool results are folded into plain user messages to preserve context. inputMessages := req.Messages - if strings.Contains(strings.ToLower(p.name), "gemini") { + + // Compute provider capability once: does this endpoint support Google's thought_signature? + // We check name, apiBase, and the model string (which covers OpenRouter/LiteLLM routing to Gemini). + supportsThoughtSignature := strings.Contains(strings.ToLower(p.name), "gemini") || + strings.Contains(strings.ToLower(p.apiBase), "generativelanguage") || + strings.Contains(strings.ToLower(model), "gemini") + + if supportsThoughtSignature { inputMessages = collapseToolCallsWithoutSig(inputMessages) } @@ -280,7 +287,11 @@ func (p *OpenAIProvider) buildRequestBody(model string, req ChatRequest, stream "arguments": string(argsJSON), } if sig := tc.Metadata["thought_signature"]; sig != "" { - fn["thought_signature"] = sig + // Only send thought_signature to providers that support it (Google/Gemini). + // Non-Google providers will reject the unknown field with 422 Unprocessable Entity. + if supportsThoughtSignature { + fn["thought_signature"] = sig + } } toolCalls[i] = map[string]any{ "id": tc.ID,