fix: remove skip_thought_signature_validator bypass for Gemini 3.1+ models

Gemini 3.1+ models reject the synthetic "skip_thought_signature_validator"
string that was injected as a fallback when no real thought signature was
available. This caused 400 errors ("Thought signature is not valid") after
tool calls.

Instead of falling back to the bypass string, omit the thoughtSignature
field entirely when no real signature exists. This is safe because:
- Real signatures from the model are still preserved and round-tripped
- Function calls without signatures are accepted by the API
- The bypass was only needed for Gemini 3 cross-model history, which
  works fine without it

Closes #12050
This commit is contained in:
Roo Code 2026-04-03 13:15:58 +00:00
parent 137d3f4fd8
commit dc07aaa1f5
2 changed files with 40 additions and 5 deletions

View file

@ -107,7 +107,7 @@ describe("convertAnthropicMessageToGemini", () => {
expect(() => convertAnthropicMessageToGemini(anthropicMessage)).toThrow("Unsupported image source type")
})
it("should convert a message with tool use", () => {
it("should convert a message with tool use (no thought signature in history)", () => {
const anthropicMessage: Anthropic.Messages.MessageParam = {
role: "assistant",
content: [
@ -133,7 +133,40 @@ describe("convertAnthropicMessageToGemini", () => {
name: "calculator",
args: { operation: "add", numbers: [2, 3] },
},
thoughtSignature: "skip_thought_signature_validator",
},
],
},
])
})
it("should attach thoughtSignature to functionCall when a real signature exists", () => {
const anthropicMessage: Anthropic.Messages.MessageParam = {
role: "assistant",
content: [
{ type: "thoughtSignature", thoughtSignature: "real-sig-abc" } as any,
{ type: "text", text: "Let me calculate that for you." },
{
type: "tool_use",
id: "calc-123",
name: "calculator",
input: { operation: "add", numbers: [2, 3] },
},
],
}
const result = convertAnthropicMessageToGemini(anthropicMessage)
expect(result).toEqual([
{
role: "model",
parts: [
{ text: "Let me calculate that for you." },
{
functionCall: {
name: "calculator",
args: { operation: "add", numbers: [2, 3] },
},
thoughtSignature: "real-sig-abc",
},
],
},

View file

@ -41,11 +41,13 @@ export function convertAnthropicContentToGemini(
// Determine the signature to attach to function calls.
// If we're in a mode that expects signatures (includeThoughtSignatures is true):
// 1. Use the actual signature if we found one in the history/content.
// 2. Fallback to "skip_thought_signature_validator" if missing (e.g. cross-model history).
// - Use the actual signature if we found one in the history/content.
// - If no real signature exists, omit it (undefined) so the API accepts the request.
// Gemini 3.1+ models reject the synthetic "skip_thought_signature_validator" bypass
// that worked with earlier Gemini 3 models.
let functionCallSignature: string | undefined
if (includeThoughtSignatures) {
functionCallSignature = activeThoughtSignature || "skip_thought_signature_validator"
functionCallSignature = activeThoughtSignature
}
if (typeof content === "string") {