Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/strands/models/gemini.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,16 @@ def _format_request_content_part(self, content: ContentBlock) -> genai.types.Par
)

if "toolUse" in content:
# Use skip_thought_signature_validator to bypass Gemini 3 Pro's strict validation
# This is a temporary workaround until we implement proper thought signature preservation
# See: https://ai.google.dev/gemini-api/docs/thought-signatures
Copy link
Member

Choose a reason for hiding this comment

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

Are we creating our own issue to track the permanent fix.

return genai.types.Part(
function_call=genai.types.FunctionCall(
args=content["toolUse"]["input"],
id=content["toolUse"]["toolUseId"],
name=content["toolUse"]["name"],
),
thought_signature=b"skip_thought_signature_validator",
)

raise TypeError(f"content_type=<{next(iter(content))}> | unsupported type")
Expand Down
31 changes: 31 additions & 0 deletions tests/strands/models/test_gemini.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,8 @@ async def test_stream_request_with_tool_use(gemini_client, model, model_id):
"id": "c1",
"name": "calculator",
},
# Skip validator is used as a temporary workaround for Gemini 3 Pro
"thought_signature": "c2tpcF90aG91Z2h0X3NpZ25hdHVyZV92YWxpZGF0b3I=",
},
],
"role": "model",
Expand All @@ -299,6 +301,35 @@ async def test_stream_request_with_tool_use(gemini_client, model, model_id):
gemini_client.aio.models.generate_content_stream.assert_called_with(**exp_request)


@pytest.mark.asyncio
async def test_stream_request_with_tool_use_includes_thought_signature_skip(gemini_client, model, model_id):
Copy link
Member

Choose a reason for hiding this comment

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

Nit: Look like test_stream_request_with_tool_use already captures this use case and so looks like we don't need an additional test here.

"""Test that toolUse includes skip_thought_signature_validator for Gemini 3 Pro compatibility."""
messages = [
{
"role": "assistant",
"content": [
{
"toolUse": {
"toolUseId": "tool1",
"name": "get_weather",
"input": {"city": "Seattle"},
},
},
],
},
]
await anext(model.stream(messages))

call_args = gemini_client.aio.models.generate_content_stream.call_args
contents = call_args.kwargs["contents"]

# Verify the thought_signature is set to skip validator
part = contents[0]["parts"][0]
assert "thought_signature" in part
# Base64 encoded "skip_thought_signature_validator"
assert part["thought_signature"] == "c2tpcF90aG91Z2h0X3NpZ25hdHVyZV92YWxpZGF0b3I="


@pytest.mark.asyncio
async def test_stream_request_with_tool_results(gemini_client, model, model_id):
messages = [
Expand Down
25 changes: 25 additions & 0 deletions tests_integ/models/test_model_gemini.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,3 +175,28 @@ def test_agent_structured_output_image_input(assistant_agent, yellow_img, yellow
tru_color = assistant_agent.structured_output(type(yellow_color), content)
exp_color = yellow_color
assert tru_color == exp_color


@pytest.mark.parametrize("model_id", ["gemini-2.5-flash", "gemini-3-pro-preview"])
def test_agent_multiturn_tool_use(model_id, tools, system_prompt):
"""Test multi-turn conversation with tool use.

Gemini 3 Pro requires thought_signature in multi-turn function calling.
Without skip_thought_signature_validator, this fails with:
"Function call is missing a thought_signature in functionCall parts"

Validates fix for issue #1199.
"""
model = GeminiModel(
client_args={"api_key": os.getenv("GOOGLE_API_KEY")},
model_id=model_id,
params={"temperature": 0.15},
)
agent = Agent(model=model, tools=tools, system_prompt=system_prompt)

result1 = agent("What is the current time in New York?")
assert "12:00" in result1.message["content"][0]["text"].lower()

# Second turn with tool use history - this is where Gemini 3 Pro would fail
result2 = agent("And what is the weather there?")
assert "sunny" in result2.message["content"][0]["text"].lower()
Loading