fix: resolve Gemini agent 400 error on tool call responses (#5054)

* add gtc__ prefix to tool call names in Gemini agent message formatting

* resolve Gemini agent 400 error on tool call responses

* add comments explaining geminis thought signatures

---------

Co-authored-by: Timothy Carambat <rambat1010@gmail.com>
This commit is contained in:
Marcello Fitton 2026-02-26 13:42:02 -08:00 committed by GitHub
parent ac0b1d401d
commit b13dd820cc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -83,6 +83,10 @@ class GeminiProvider extends Provider {
* Format the messages to the Gemini API Responses format.
* - Gemini has some loosely documented format for tool calls and it can change at any time.
* - We need to map the function call to the correct id and Gemini will throw an error if it does not.
* - Gemini requires a `thought_signature` (via `extra_content.google.thought_signature`) on function call
* parts in multi-turn tool conversations. This is an encrypted token Gemini attaches to every tool call
* it makes, and it must be passed back when sending tool results or Gemini rejects the request with a 400.
* See: https://ai.google.dev/gemini-api/docs/thought-signatures
* @param {any[]} messages - The messages to format.
* @returns {OpenAI.OpenAI.Responses.ResponseInput[]} The formatted messages.
*/
@ -101,17 +105,25 @@ class GeminiProvider extends Provider {
return;
}
const prefixedName = this.prefixToolCall(
message.originalFunctionCall.name,
"add"
);
formattedMessages.push(
{
role: "assistant",
content: "",
tool_calls: [
{
type: "function",
...(message.originalFunctionCall.extra_content
? { extra_content: message.originalFunctionCall.extra_content }
: {}),
function: {
arguments: JSON.stringify(
message.originalFunctionCall.arguments
),
name: message.originalFunctionCall.name,
name: prefixedName,
},
id: message.originalFunctionCall.id,
},
@ -120,6 +132,7 @@ class GeminiProvider extends Provider {
{
role: "tool",
tool_call_id: message.originalFunctionCall.id,
name: prefixedName,
content: message.content,
}
);
@ -188,6 +201,8 @@ class GeminiProvider extends Provider {
name: this.prefixToolCall(toolCall.function.name, "strip"),
call_id: toolCall.id,
arguments: toolCall.function.arguments,
// Preserve Gemini's thought_signature so it can be passed back in #formatMessages
extra_content: toolCall.extra_content ?? null,
};
eventHandler?.("reportStreamEvent", {
type: "toolCallInvocation",
@ -208,6 +223,7 @@ class GeminiProvider extends Provider {
id: completion.functionCall.call_id,
name: completion.functionCall.name,
arguments: completion.functionCall.arguments,
extra_content: completion.functionCall.extra_content,
},
cost: this.getCost(),
};
@ -265,6 +281,8 @@ class GeminiProvider extends Provider {
name: this.prefixToolCall(toolCall.function.name, "strip"),
arguments: functionArgs,
id: toolCall.id,
// Preserve Gemini's thought_signature so it can be passed back in #formatMessages
extra_content: toolCall.extra_content ?? null,
},
cost,
};