Skip to content

fix(llm): distinguish duplicate tool call IDs within a response - #1615

Open
samirhvbr wants to merge 1 commit into
usestrix:mainfrom
samirhvbr:fix/same-turn-tool-call-ids-20261028
Open

samirhvbr wants to merge 1 commit into
usestrix:mainfrom
samirhvbr:fix/same-turn-tool-call-ids-20261028

Conversation

@samirhvbr

Copy link
Copy Markdown

When a provider returns two distinct tool calls with the same nonblank call_id in one response, the turn guard keeps the collision. The SDK then collapses results in the next request's history, and the per-turn limiter treats the distinct calls as one decision. Recycling a historical ID into two new calls produces the same problem.

Associate the assigned ID with the response item's identity, using its output position on Chat Completions routes where the SDK uses a shared placeholder item ID. Keep assignments stable through added/done/completed events, preserve the first valid ID, and give subsequent collisions distinct IDs. This extends the existing normalization without changing tool execution or provider routing.

Closes #1611.

Validation on macOS/Apple Silicon, Python 3.12.14, against upstream 98e3df14:

  • Four loopback-provider regressions fail before the fix, losing one of two calls or one of three calls in final history.
  • Nine additional identity/event/limiter cases fail against the original rewriter and pass with the change, including real, placeholder and absent item IDs, historical reuse and repeated sightings.
  • pytest -q tests/test_tool_call_ids.py tests/test_tool_call_limits.py tests/test_tool_call_ids_providers.py: 454 passed, 3 expected failures. This exercises 18 provider-route model configurations in streamed and non-streamed modes. The existing expected failures concern SDK conversion of null IDs before the guard runs; dependency deprecation/unclosed-session warnings remain.
  • Repository-wide Ruff format/lint, Mypy and Bandit pass; git diff --check passes.
  • make check-all still stops at Pyright: 936 errors versus 940 on unchanged upstream. A comparison of diagnostic file/rule/message signatures, normalized for checkout paths and line shifts, finds no new diagnostics.
  • The full repository suite on unchanged upstream was interrupted after stalling, with 19 failures in existing budget/runner mocks; this PR does not claim a completed or green full repository suite.

All provider responses are synthetic, served on loopback through the actual SDK and LiteLLM adapters. No live provider, LLM account, Docker scan or external target was exercised.

@greptile-apps

greptile-apps Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no actionable issues were found.

Summary

TurnCallIdRewriter now assigns IDs by item identity, using output position when the SDK supplies a shared placeholder.

  • Tool calls with repeated IDs stay distinct in a response.

Reviews (1) · Last reviewed commit: "fix(llm): distinguish duplicate tool cal..." · Reviewed by Greptile

This branch has not been deployed

No deployments
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.

[BUG] Duplicate tool-call IDs within one response silently lose history

1 participant