Skip to content

fix(translation): rewrite colliding anthropic tool-call ids (#502) - #868

Open
jsvisa wants to merge 2 commits into
NVIDIA-NeMo:mainfrom
jsvisa:fix/issue-502-unique-tool-call-ids
Open

jsvisa wants to merge 2 commits into
NVIDIA-NeMo:mainfrom
jsvisa:fix/issue-502-unique-tool-call-ids

Conversation

@jsvisa

@jsvisa jsvisa commented Sep 29, 2026 •

Copy link
Copy Markdown

A backend that reuses OpenAI tool-call ids across responses (e.g. Grok emitting call_0, call_1, call_0) made Switchyard translate that conversation to Anthropic with duplicate tool_use ids. Anthropic returns 400 "tool_use ids must be unique", and clients pair results to calls by id, so agent loops never terminated.

Translation now treats id uniqueness as an Anthropic-facing invariant: first-seen ids pass through unchanged; a repeat mints sydup2_call_0-style ids (distinct from the sy64_ scheme, original readable after stripping the prefix). The Anthropic request decoder records the ids a conversation already used in request extensions, and both buffered and streaming response encoders seed from that — no new cross-request state. Every tool_result is rewritten to match its own call.

Verified: new buffered/streaming/extension tests fail before the fix and pass after; full translation suite (231 tests), cargo fmt --all --check and cargo clippy --workspace --all-targets -- -D warnings clean.

Fixes #502

Summary by CodeRabbit

  • Bug Fixes
    • Tool-call IDs are now kept unique when translating Anthropic requests, responses, and streamed events, including when IDs repeat or were used earlier in the conversation.
    • Tool results continue to match the corresponding tool calls after IDs are rewritten. First-seen IDs remain unchanged.

@jsvisa
jsvisa requested a review from a team as a code owner September 29, 2026 11:31
@eric-liu-nvidia

Copy link
Copy Markdown
Contributor

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: NVIDIA-NeMo/Switchyard/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 6d8ac926-133e-4a94-8be5-eb0c41498601

📥 Commits

Reviewing files that changed from the base of the PR and between a601a9a and 0222825.

📒 Files selected for processing (8)
  • crates/switchyard-translation/src/codecs/anthropic/buffered.rs
  • crates/switchyard-translation/src/engine.rs
  • crates/switchyard-translation/src/helpers.rs
  • crates/switchyard-translation/src/lib.rs
  • crates/switchyard-translation/src/util.rs
  • crates/switchyard-translation/tests/request_translation.rs
  • crates/switchyard-translation/tests/response_translation.rs
  • crates/switchyard-translation/tests/stream_translation.rs

Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 7 remain after this review.


Walkthrough

Anthropic translation now records tool IDs from decoded request history and uses a stateful rewriter for request bodies, buffered responses, and streamed events. The rewriter preserves first-use IDs and assigns distinct IDs to repeated calls while pairing results with the latest assigned call ID.

Changes

Anthropic tool ID uniqueness

Layer / File(s) Summary
Seen-ID metadata and rewriter
crates/switchyard-translation/src/util.rs, crates/switchyard-translation/src/lib.rs
Adds a stateful tool ID rewriter and provider-extension helpers. Replaces the prior normalization helper and adds tests for repeated calls, paired results, and reserved IDs.
Request history and body rewriting
crates/switchyard-translation/src/codecs/anthropic/buffered.rs, crates/switchyard-translation/tests/request_translation.rs
Request decoding records tool IDs in extensions. Request encoding rewrites IDs in the generated body. A regression test covers repeated IDs and paired results.
Buffered and streamed response rewriting
crates/switchyard-translation/src/engine.rs, crates/switchyard-translation/src/helpers.rs, crates/switchyard-translation/tests/response_translation.rs, crates/switchyard-translation/tests/stream_translation.rs
Buffered and streaming response encoding seed the rewriter from request extensions and rewrite encoded IDs, including terminal stream events. Tests cover repeated IDs and IDs from request history.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Severity of issue fixed: Medium

Merge Risk: ⚪ Minimal · up to 02228

The change rewrites repeated Anthropic tool-call IDs so results pair with the correct calls. No merge-blocking risk was identified in the supplied context.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning Issue [#502] requires unique Anthropic-facing IDs for repeated OpenAI tool-call IDs, correct tool_result pairing, and buffered and streaming coverage. AnthropicToolIdRewriter seeds IDs from reques… Add an automated multi-turn regression test for repeated upstream tool IDs. Verify that each translated tool_result matches its call and that the workflow emits a terminal assistant response after the final successful result.
Docstring Coverage ⚠️ Warning Docstring coverage is 72.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 25 functions across 8 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: rewriting colliding Anthropic tool-call IDs.
Out of Scope Changes check ✅ Passed The changed production files and tests support issue [#502]. They implement per-request ID tracking, repeated-ID rewriting, result pairing, and buffered or streaming translation coverage. No unrelated…
Full details: Linked Issues check

Explanation

Issue [#502] requires unique Anthropic-facing IDs for repeated OpenAI tool-call IDs, correct tool_result pairing, and buffered and streaming coverage. AnthropicToolIdRewriter seeds IDs from request history and rewrites response events, and the added tests cover repeated IDs and pairing in buffered, request, and streaming translation. The change does not add the requested multi-turn workflow test that verifies termination after the final successful tool result.

  • Fix all pre-merge checks with AI
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


A rabbit checks each tool-call name,
The first stays whole, repeats change.
Results find the latest call,
Stream and buffer guard them all.
Then hops along, IDs in line.

Comment @coderabbitai help to get the list of available commands.

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] Reused OpenAI tool-call IDs break multi-turn Anthropic tool workflows

2 participants