Skip to content

fix: propagate reasoningSignature on Gemini tool use#1703

Merged
afarntrog merged 4 commits intostrands-agents:mainfrom
afarntrog:gemini_thought_sig
Feb 16, 2026
Merged

fix: propagate reasoningSignature on Gemini tool use#1703
afarntrog merged 4 commits intostrands-agents:mainfrom
afarntrog:gemini_thought_sig

Conversation

@afarntrog
Copy link
Contributor

Description

  • Add reasoningSignature field to ToolUse TypedDict and streaming handlers
  • Pass reasoning signatures through content block start/stop in event loop
  • Fix Gemini model to use base64 encode/decode for thought_signature instead of raw UTF-8, ensuring correct binary data handling
  • Thread reasoning signatures on function_call parts for Gemini

Related Issues

Fixes #1622

Documentation PR

Type of Change

Bug fix

Testing

How have you tested the change? Verify that the changes do not break functionality or introduce warnings in consuming repositories: agents-docs, agents-tools, agents-cli

  • I ran hatch run prepare

Checklist

  • I have read the CONTRIBUTING document
  • I have added any necessary tests that prove my fix is effective or my feature works
  • I have updated the documentation accordingly
  • I have added an appropriate example to the documentation to outline the feature, or no new docs are needed
  • My changes generate no new warnings
  • Any dependent changes have been merged and published

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

- Add reasoningSignature field to ToolUse TypedDict and streaming handlers
- Pass reasoning signatures through content block start/stop in event loop
- Fix Gemini model to use base64 encode/decode for thought_signature
  instead of raw UTF-8, ensuring correct binary data handling
- Thread reasoning signatures on function_call parts for Gemini
@afarntrog afarntrog self-assigned this Feb 16, 2026
@codecov
Copy link

codecov bot commented Feb 16, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions
Copy link

Review Summary

Assessment: Comment

This PR correctly propagates the reasoningSignature through the streaming handlers and fixes the base64 encoding/decoding for Gemini's thought_signature field. The type definitions and Gemini model changes look good.

Review Categories
  • Testing: The streaming.py changes (lines 189-190 and 291-292) have no test coverage. Test cases should be added to test_streaming.py for both handle_content_block_start and handle_content_block_stop with reasoningSignature.
  • Verification: The expected value in test_stream_request_with_tool_use should be verified against the actual behavior since base64.b64decode() produces bytes.

The implementation approach is clean and follows existing patterns in the codebase.

@github-actions
Copy link

Review Summary

Assessment: Approve

The PR now looks ready to merge. All previous review feedback has been addressed with comprehensive test coverage.

Changes Verified
  • streaming.py: Tests added for handle_content_block_start and handle_content_block_stop with reasoningSignature
  • gemini.py: Tests cover both sending (request) and receiving (response) tool use with thought_signature
  • Coverage: Improved from 57% to 85.71%
  • Type definitions: Properly documented with NotRequired[str]

Nice work on the clean implementation and thorough test coverage! 🎉

@afarntrog
Copy link
Contributor Author

My local run test:

#!/usr/bin/env python3
"""Manual test: Gemini thought signatures with parallel function calls.

Requires: GOOGLE_API_KEY env var set.
Uses gemini-2.5-flash which supports thinking + thought signatures on tool calls.
"""

import json
from strands import Agent, tool
from strands.models.gemini import GeminiModel


@tool
def get_weather(city: str) -> str:
    """Get the current weather for a city."""
    weather_data = {
        "new york": "72°F, sunny",
        "london": "58°F, cloudy",
        "tokyo": "80°F, humid",
    }
    return weather_data.get(city.lower(), f"No data for {city}")


@tool
def get_time(city: str) -> str:
    """Get the current time for a city."""
    time_data = {
        "new york": "2:00 PM EST",
        "london": "7:00 PM GMT",
        "tokyo": "3:00 AM JST",
    }
    return time_data.get(city.lower(), f"No data for {city}")


def main():
    model = GeminiModel(
        model_id="gemini-2.5-flash",
        params={"thinking_config": {"thinking_budget": 1024}},
    )

    agent = Agent(model=model, tools=[get_weather, get_time])

    # This prompt should trigger parallel function calls
    result = agent("What's the weather and time in New York and London right now?")

    print("\n=== RESULT ===")
    print(result)

    # Inspect the message history for thought signatures
    print("\n=== MESSAGE HISTORY INSPECTION ===")
    for i, msg in enumerate(agent.messages):
        role = msg.get("role", "unknown")
        print(f"\n--- Message {i} ({role}) ---")
        for j, block in enumerate(msg.get("content", [])):
            if "toolUse" in block:
                tu = block["toolUse"]
                sig = tu.get("reasoningSignature")
                print(f"  toolUse: {tu['name']}({json.dumps(tu['input'])})")
                print(f"    reasoningSignature: {'YES (' + sig[:20] + '...)' if sig else 'None'}")
            elif "reasoningContent" in block:
                rc = block["reasoningContent"]
                if "reasoningText" in rc:
                    rt = rc["reasoningText"]
                    sig = rt.get("signature")
                    print(f"  reasoningContent: {rt['text'][:80]}...")
                    print(f"    signature: {'YES (' + sig[:20] + '...)' if sig else 'None'}")
            elif "toolResult" in block:
                tr = block["toolResult"]
                print(f"  toolResult: {tr['toolUseId']} -> {tr['content']}")
            elif "text" in block:
                print(f"  text: {block['text'][:100]}...")


if __name__ == "__main__":
    main()

Output:


Tool #1: get_weather

Tool #2: get_time

Tool #3: get_weather

Tool #4: get_time
The weather in New York is 72°F and sunny, and the time is 2:00 PM EST. In London, the weather is 58°F and cloudy, and the time is 7:00 PM GMT.
=== RESULT ===
The weather in New York is 72°F and sunny, and the time is 2:00 PM EST. In London, the weather is 58°F and cloudy, and the time is 7:00 PM GMT.


=== MESSAGE HISTORY INSPECTION ===

--- Message 0 (user) ---
  text: What's the weather and time in New York and London right now?...

--- Message 1 (assistant) ---
  toolUse: get_weather({"city": "New York"})
    reasoningSignature: YES (CmQBvj72+011nRgTlKX8...)
  toolUse: get_time({"city": "New York"})
    reasoningSignature: None
  toolUse: get_weather({"city": "London"})
    reasoningSignature: None
  toolUse: get_time({"city": "London"})
    reasoningSignature: None

--- Message 2 (user) ---
  toolResult: tooluse_IhfhURJy54-Vi45STr9smA -> [{'text': '72°F, sunny'}]
  toolResult: tooluse_w9oz59ru6cr0NToXyKriyQ -> [{'text': '2:00 PM EST'}]
  toolResult: tooluse_XwXGxoN6eck3T171vRsbRg -> [{'text': '58°F, cloudy'}]
  toolResult: tooluse_Y4RvgigaOCNocY79DxG9mA -> [{'text': '7:00 PM GMT'}]

--- Message 3 (assistant) ---
  text: The weather in New York is 72°F and sunny, and the time is 2:00 PM EST. In London, the weather is 58...

@github-actions github-actions bot added size/m and removed size/m labels Feb 16, 2026
@github-actions
Copy link

Review Summary

Assessment: Approve

Excellent work! The PR is ready to merge.

Final Status
  • Coverage: 100% - All modified lines now covered by tests
  • Manual Testing: Verified working with Gemini 2.5-flash and parallel function calls
  • Implementation: Clean, follows existing patterns

Thanks for the thorough manual test output showing reasoningSignature working end-to-end! 🚀

@afarntrog afarntrog merged commit 2281a20 into strands-agents:main Feb 16, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments