Skip to content

fix(gemini): Implement Gemini 3 thought_signature handling for stateful reasoning#307

Open
Andyi955 wants to merge 1 commit intosipeed:mainfrom
Andyi955:main
Open

fix(gemini): Implement Gemini 3 thought_signature handling for stateful reasoning#307
Andyi955 wants to merge 1 commit intosipeed:mainfrom
Andyi955:main

Conversation

@Andyi955
Copy link

Description

This PR addresses a critical compatibility issue with Gemini 3 models (specifically gemini-3-flash-preview and gemini-3-pro-preview) where tool calls would fail with a 400 INVALID_ARGUMENT error due to missing thought_signature.

According to Google's Stateful Reasoning documentation, Gemini 3 generates encrypted thought_signature tokens during reasoning steps involving tools. These signatures must be persisted and returned in the subsequent conversation turn's extra_content field to maintain the reasoning chain. Failure to include them causes the API to reject the request.

Changes

  • pkg/providers/types.go:
    • Added ExtraContent and GoogleExtra structs to support the extra_content JSON field.
    • Added internal ThoughtSignature fields to ToolCall and FunctionCall to track this data internally.
  • pkg/providers/http_provider.go:
    • Updated parseResponse to extract the thought_signature from the extra_content.google field in the API response.
    • Ensured the ExtraContent is correctly populated in the ToolCall object.
  • pkg/agent/loop.go:
    • Updated the main agent loop to persist ExtraContent when constructing the assistant's message history. This ensures the signature is included in the context window for the next turn.

Verification

  • Test Environment: Verified locally using gemini-3-flash-preview.
  • Scenario: Executed multi-step tool calls (e.g., list_dir).
  • Result:
    • Before fix: API returned 400 Function call is missing a thought_signature.
    • After fix: Tool calls execute successfully, and the model maintains context across multiple turns without error.

Related Issues

Fixes 400 error with Gemini 3 models.

Comment on lines +634 to +636
ExtraContent: extraContent,
// We also set internal ThoughtSignature, but ExtraContent is what matters for serialization
ThoughtSignature: tc.ThoughtSignature,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ThoughtSignture should be included in ExtraContent. Why are we pulling it out to its own field?

Have you tested this with non Google LLMs?

Comment on lines +170 to +174
// Extract thought_signature from Gemini/Google-specific extra content
thoughtSignature := ""
if tc.ExtraContent != nil && tc.ExtraContent.Google != nil {
thoughtSignature = tc.ExtraContent.Google.ThoughtSignature
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like extra lines of code when tc.ExtraContent can just be passed to ToolCall struct

ID: tc.ID,
Name: name,
Arguments: arguments,
ThoughtSignature: thoughtSignature, // Populating internal field for convenience
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whats this for? Seems to be the wrong structure of a tool call, Thought Signature is an ExtraContent field based by google.

Since this is the HTTP Provider other services other than google will use it.

Comment on lines +201 to +207
if thoughtSignature != "" {
toolCall.ExtraContent = &ExtraContent{
Google: &GoogleExtra{
ThoughtSignature: thoughtSignature,
},
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra assignment when line 194 set tool call fields

Function *FunctionCall `json:"function,omitempty"`
Name string `json:"name,omitempty"`
Arguments map[string]interface{} `json:"arguments,omitempty"`
ThoughtSignature string `json:"-"` // Internal use only
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove

Name string `json:"name,omitempty"`
Arguments map[string]interface{} `json:"arguments,omitempty"`
ThoughtSignature string `json:"-"` // Internal use only
ExtraContent *ExtraContent `json:"extra_content,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this a pointer?

WORKDIR /src

# Cache dependencies
# Cache dependencies for faster subsequent builds
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI fluff

@Leeaandrob
Copy link
Collaborator

@Zepan Implements thought_signature handling for Gemini 3 stateful reasoning — required for Gemini's thinking/reasoning mode to work correctly.

Recommendation: Merge. +67/-21, needed for Gemini 3 compatibility. Without this, Gemini 3's reasoning feature doesn't work in multi-turn conversations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants