fix(translation): preserve tool IDs across Anthropic - #397
fix(translation): preserve tool IDs across Anthropic#397ting-hong-shieh wants to merge 1 commit into
Conversation
Signed-off-by: Ting-Hong Shieh <32212900+ting-hong-shieh@users.noreply.github.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (7)
WalkthroughThe change replaces lossy tool ID sanitization with reversible URL-safe Base64 encoding. OpenAI Chat and Responses encoders restore original IDs before serialization. Request and streaming tests cover round trips, reserved prefixes, malformed values, Unicode, and empty IDs. ChangesTool ID round-trip
Estimated code review effort: 3 (Moderate) | ~20 minutes Mergeability Score: ⚪ Minimal · up to This change preserves tool-call IDs across translation boundaries and adds regression coverage; no actionable merge-blocking risk remains beyond normal checks and review. Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
|
@ting-hong-shieh Thanks for putting this up. Can you always do the before and after the change snapshot of the results. It helps me to do better and fast reviews. |
|
Thanks, @ayushag-nv. I ran the same in-process buffered translation at the PR base ( Input upstream tool-call ID: Before ( {
"anthropic_tool_use_id": "functions_list_skills_0",
"replayed_openai_tool_call_id": "functions_list_skills_0",
"replayed_openai_tool_result_id": "functions_list_skills_0",
"upstream_emitted_id": "functions.list_skills:0"
}The unsupported characters are replaced with underscores, and both replayed OpenAI IDs differ from the ID emitted by the upstream model. After ( {
"anthropic_tool_use_id": "sy64_ZnVuY3Rpb25zLmxpc3Rfc2tpbGxzOjA",
"replayed_openai_tool_call_id": "functions.list_skills:0",
"replayed_openai_tool_result_id": "functions.list_skills:0",
"upstream_emitted_id": "functions.list_skills:0"
}The Anthropic-facing ID stays within the accepted character set, while the OpenAI tool call and tool result both recover the exact upstream ID on replay. |
What
sy64_-prefixed, unpadded Base64URL encoding.[A-Za-z0-9_-]IDs unchanged, escape the reserved prefix, and pass malformed encoded values through unchanged.Why
Some OpenAI-compatible models emit tool-call IDs containing characters that Anthropic does not accept. Kimi K2, for example, can emit IDs such as
functions.list_skills:0and expects the same ID on the next turn.The previous sanitizer replaced unsupported characters with underscores. That mapping could not be reversed, so Switchyard replayed a different ID to the upstream model and could break multi-turn tool calling. The new encoding keeps Anthropic-facing IDs within the accepted character set while allowing the OpenAI-bound request path to recover the original bytes.
Closes #178
How tested
cargo fmt --all --checkPYO3_NO_PYTHON=1 cargo clippy --workspace --all-targets -- -D warningscargo test -p switchyard-translationPYO3_NO_PYTHON=1 cargo test --workspace --exclude switchyard-pyPYO3_NO_PYTHON=1 cargo check -p switchyard-pyChecklist
--helpare unchanged because this does not add a configuration or command surface.Notes for reviewers
sy64_is reserved for encoded IDs. An upstream ID that already starts with this prefix is encoded again before it reaches an Anthropic client, which keeps decoding unambiguous. Invalid prefixed input is left unchanged.Summary by CodeRabbit