Skip to content

fix(providers): tool-use translation correctness (3 audit bugs)#332

Merged
Destynova2 merged 1 commit intomainfrom
fix/tool-use-translation-correctness
Apr 28, 2026
Merged

fix(providers): tool-use translation correctness (3 audit bugs)#332
Destynova2 merged 1 commit intomainfrom
fix/tool-use-translation-correctness

Conversation

@Destynova2
Copy link
Copy Markdown
Contributor

Summary

Fixes three tool-use translation correctness bugs from the audit. All map to silent failure paths in the canonical -> upstream -> client round trip.

  • Bug chore: release v0.7.0 #1 (tool_use serialization) -- extract_tool_calls in src/providers/openai/transform.rs replaced serde_json::to_string(input).unwrap_or_default() with proper error propagation via a new TransformError::ToolInputSerialization { tool_name, source }. A From<TransformError> for ProviderError bridge keeps the existing callers compatible (surfaces as ProviderError::SerializationError).
  • Bug chore: release v0.7.1 #2 (tool ID round trip) -- New OriginalToolIdMap in src/providers/anthropic_sanitize.rs. Populated by sanitize_tool_use_ids before the upstream Anthropic call; applied by restore_original_tool_ids on the response so clients (e.g. Claude Code) can map response IDs back to their tool definitions even when the original ID violated Anthropic's ^[a-zA-Z0-9_-]{1,64}$. Scoped to the single provider call -- no global state. Streaming SSE rewriting is left as a TODO; the non-streaming path is the common case.
  • Bug chore: release v0.7.1 #3 (system ordering) -- transform_request now drops any residual role:\"system\" entries from the canonical messages array after hoisting the dedicated system field. Prevents duplicate system messages when a client sends [user, system, assistant] and the request is routed to OpenAI. Includes a debug_assert! guard against future regressions.

Tests added

  • transform_returns_error_when_tool_input_unserializable
  • transform_propagates_tool_input_error_to_provider_error
  • transform_strips_system_from_messages_after_hoisting
  • transform_strips_system_when_no_hoisted_system_field
  • tool_use_id_round_trip_preserves_original
  • restore_is_noop_when_map_is_empty
  • restore_leaves_unmapped_ids_untouched

Test plan

  • cargo check --tests
  • cargo fmt -- --check on changed files
  • cargo clippy --tests --all-targets -- -D warnings
  • cargo nextest run --lib (1113 tests pass)
  • CI green

Surfaces silent translation failures and preserves client-supplied tool IDs
across the canonical->OpenAI / Anthropic upstream round trip.

* Bug #1 (tool_use serialization): replace `serde_json::to_string(...).unwrap_or_default()`
  in `extract_tool_calls` with proper error propagation. Adds `TransformError`
  enum (`ToolInputSerialization { tool_name, source }`) and a `From<TransformError>
  for ProviderError` bridge so existing callers continue to surface the failure
  as `ProviderError::SerializationError`.

* Bug #2 (tool ID round trip): introduces `OriginalToolIdMap` populated by
  `sanitize_tool_use_ids` before the upstream Anthropic call, and applied by
  `restore_original_tool_ids` on the response so downstream clients (e.g.
  Claude Code) can match response IDs back to their tool definitions even
  when the original ID violated Anthropic's `^[a-zA-Z0-9_-]{1,64}$` pattern.
  Map is scoped to the single provider call (no global state).

* Bug #3 (system ordering): `transform_request` now drops residual
  `role:"system"` entries from `request.messages` after hoisting the
  canonical `system` field, preventing duplicate system messages when a
  client sends `[user, system, assistant]` to the Anthropic-style endpoint
  and the request is routed to OpenAI.

Adds unit tests:
- `transform_returns_error_when_tool_input_unserializable`
- `transform_propagates_tool_input_error_to_provider_error`
- `transform_strips_system_from_messages_after_hoisting`
- `transform_strips_system_when_no_hoisted_system_field`
- `tool_use_id_round_trip_preserves_original`
- `restore_is_noop_when_map_is_empty`
- `restore_leaves_unmapped_ids_untouched`

Streaming round trip for tool IDs is left as a follow-up TODO; the common
non-streaming path is fixed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Destynova2 Destynova2 enabled auto-merge April 28, 2026 21:28
@Destynova2 Destynova2 merged commit d667d2f into main Apr 28, 2026
43 checks passed
@Destynova2 Destynova2 deleted the fix/tool-use-translation-correctness branch April 28, 2026 21:46
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.

1 participant