Skip to content

Commit fd3eda8

Browse files
committed
feat: preserve thoughtSignature for Gemini 3 Pro function calling
- Add thoughtSignature field to ToolUse TypedDict as optional field - Add thoughtSignature to ContentBlockStartToolUse TypedDict - Preserve thoughtSignature during streaming event processing - Fixes compatibility with Gemini 3 Pro thinking mode This change enables proper multi-turn function calling with Gemini 3 Pro, which requires thought_signature to be passed back in subsequent requests. Resolves: Gemini 3 Pro 400 error for missing thought_signature See: https://ai.google.dev/gemini-api/docs/thought-signatures
1 parent 93997f0 commit fd3eda8

File tree

3 files changed

+17
-2
lines changed

3 files changed

+17
-2
lines changed

src/strands/event_loop/streaming.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,9 @@ def handle_content_block_start(event: ContentBlockStartEvent) -> dict[str, Any]:
185185
current_tool_use["toolUseId"] = tool_use_data["toolUseId"]
186186
current_tool_use["name"] = tool_use_data["name"]
187187
current_tool_use["input"] = ""
188+
# Preserve thoughtSignature if present (required for Gemini 3 Pro)
189+
if "thoughtSignature" in tool_use_data:
190+
current_tool_use["thoughtSignature"] = tool_use_data["thoughtSignature"]
188191

189192
return current_tool_use
190193

@@ -285,6 +288,11 @@ def handle_content_block_stop(state: dict[str, Any]) -> dict[str, Any]:
285288
name=tool_use_name,
286289
input=current_tool_use["input"],
287290
)
291+
292+
# Preserve thoughtSignature if present (required for Gemini 3 Pro)
293+
if "thoughtSignature" in current_tool_use:
294+
tool_use["thoughtSignature"] = current_tool_use["thoughtSignature"]
295+
288296
content.append({"toolUse": tool_use})
289297
state["current_tool_use"] = {}
290298

src/strands/types/content.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,16 +123,18 @@ class DeltaContent(TypedDict, total=False):
123123
toolUse: Dict[Literal["input"], str]
124124

125125

126-
class ContentBlockStartToolUse(TypedDict):
126+
class ContentBlockStartToolUse(TypedDict, total=False):
127127
"""The start of a tool use block.
128128
129129
Attributes:
130130
name: The name of the tool that the model is requesting to use.
131131
toolUseId: The ID for the tool request.
132+
thoughtSignature: Optional encrypted token from Gemini for multi-turn reasoning.
132133
"""
133134

134135
name: str
135136
toolUseId: str
137+
thoughtSignature: NotRequired[str]
136138

137139

138140
class ContentBlockStart(TypedDict, total=False):

src/strands/types/tools.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,24 @@ class Tool(TypedDict):
5252
toolSpec: ToolSpec
5353

5454

55-
class ToolUse(TypedDict):
55+
class ToolUse(TypedDict, total=False):
5656
"""A request from the model to use a specific tool with the provided input.
5757
5858
Attributes:
5959
input: The input parameters for the tool.
6060
Can be any JSON-serializable type.
6161
name: The name of the tool to invoke.
6262
toolUseId: A unique identifier for this specific tool use request.
63+
thoughtSignature: Optional encrypted token from Gemini that preserves
64+
the model's internal reasoning process for multi-turn conversations.
65+
Required for Gemini 3 Pro when using function calling.
66+
See: https://ai.google.dev/gemini-api/docs/thought-signatures
6367
"""
6468

6569
input: Any
6670
name: str
6771
toolUseId: str
72+
thoughtSignature: NotRequired[str]
6873

6974

7075
class ToolResultContent(TypedDict, total=False):

0 commit comments

Comments
 (0)