Skip to content

fix(google): scope Gemini tool-call id→name resolution per turn (#45)#54

Draft
TYRMars wants to merge 1 commit into
mainfrom
claude/vibrant-dijkstra-HdTVa
Draft

fix(google): scope Gemini tool-call id→name resolution per turn (#45)#54
TYRMars wants to merge 1 commit into
mainfrom
claude/vibrant-dijkstra-HdTVa

Conversation

@TYRMars
Copy link
Copy Markdown
Owner

@TYRMars TYRMars commented May 30, 2026

Summary

Fixes #45 — Gemini's per-turn gem_<index> tool-call ids collide across turns, corrupting multi-turn tool-result pairing.

GoogleProvider synthesises tool-call ids as gem_<index> where the index restarts at 0 every turn. GoogleRequest::from_request built a conversation-wide, last-writer-wins id_to_name map, so a later turn's gem_0 shadowed an earlier turn's gem_0. Since Gemini matches functionResponsefunctionCall by name (no ids on the wire), turn 1's tool reply was sent with the wrong function name → cryptic Gemini 400s or wrong-tool attribution on any conversation that uses tools across more than one turn.

Fix

Resolve each tool reply against the most recent assistant turn's tool-calls instead of a global map. The id→name scope is rebuilt (cleared + repopulated) at every assistant message in from_request's contents pass.

Ids are unique within a turn, so this is collision-proof. Chosen over a minting-side change (making ids globally unique) because:

  • It also repairs already-persisted conversations whose stored ids already collide — a minting change only affects newly-minted ids.
  • No change to the on-the-wire id format or the harness's id-keyed routing (which only needs intra-turn uniqueness).

Tests

  • New regression test convert_tool_results_resolve_name_per_turn_despite_colliding_ids builds a two-turn conversation where both turns emit gem_0 for different tools (fs.read then code.grep) and asserts each functionResponse resolves to the correct name.
  • All 15 google:: tests pass; cargo clippy -p harness-llm --all-targets -- -D warnings clean.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Bztt9AbeP1uxDN5oh4QJMR


Generated by Claude Code

Gemini synthesises tool-call ids as `gem_<index>` where the index
restarts at 0 every turn, so ids collide across turns of a multi-turn
conversation. `GoogleRequest::from_request` built a conversation-wide,
last-writer-wins `id_to_name` map, so a later turn's `gem_0` shadowed
an earlier turn's `gem_0`. Because Gemini matches `functionResponse`
to `functionCall` by name (no ids on the wire), turn 1's tool reply
was sent with the wrong function name, causing cryptic 400s or
wrong-tool attribution on any conversation using tools across more
than one turn.

Resolve each tool reply against the most recent assistant turn's
tool-calls instead. Ids are unique within a turn, so this is
collision-proof, and unlike a minting-side change it also repairs
already-persisted conversations whose stored ids already collide.

Fixes #45
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.

Gemini provider: per-turn 'gem_<index>' tool-call ids collide across turns, corrupting multi-turn tool-result pairing

2 participants