Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions crates/switchyard-translation/src/codecs/anthropic/buffered.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ use crate::llm::{
};
use crate::policy::{DeterministicIdPolicy, TranslationPolicy};
use crate::util::{
capture_request_preservation, capture_response_preservation, desanitize_anthropic_tool_use_id,
embed_preservation, exact_preserved_request, exact_preserved_response,
sanitize_anthropic_tool_use_id,
AnthropicToolIdRewriter, attach_seen_tool_ids, capture_request_preservation,
capture_response_preservation, desanitize_anthropic_tool_use_id, embed_preservation,
exact_preserved_request, exact_preserved_response, sanitize_anthropic_tool_use_id,
};
use crate::util::{
json_string, push_lossy, reject_responses_builtin_tool_item, stable_id, string_value,
Expand Down Expand Up @@ -178,6 +178,7 @@ impl FormatCodec for AnthropicMessagesCodec {
.extensions
.fields
.insert(ANTHROPIC_REQUEST_KEY.to_string(), Value::Bool(true));
attach_seen_tool_ids(&mut request.extensions.fields, &request.messages);

Ok(DecodedRequest {
request,
Expand Down Expand Up @@ -311,7 +312,12 @@ impl FormatCodec for AnthropicMessagesCodec {
output_config.insert("format".to_string(), format);
}

let body = embed_preservation(Value::Object(body), &request.preservation, policy);
// Anthropic rejects repeated tool IDs, so a conversation that already
// carries a repeat (for example from a backend that reuses IDs) must
// reach the provider with one distinct ID per call and its result.
let mut body = Value::Object(body);
AnthropicToolIdRewriter::default().rewrite_body(&mut body);
let body = embed_preservation(body, &request.preservation, policy);
Ok(EncodedRequest { body, diagnostics })
}

Expand Down
5 changes: 5 additions & 0 deletions crates/switchyard-translation/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::error::{Result, TranslationError};
use crate::format::FormatId;
use crate::llm::{AggLlmResponse, LlmRequest, ProviderExtensions};
use crate::policy::TranslationPolicy;
use crate::util::{AnthropicToolIdRewriter, seen_tool_ids};

/// Encoded translation result with any diagnostics emitted along the way.
#[derive(Debug)]
Expand Down Expand Up @@ -221,6 +222,10 @@ impl TranslationEngine {
&mut output.body,
&crate::codex_custom_tools::custom_tool_names(request_extensions),
);
// A single response knows nothing about earlier turns, so the IDs the
// conversation already used seed the rewrite of this turn's calls.
let mut tool_ids = AnthropicToolIdRewriter::new(seen_tool_ids(request_extensions));
tool_ids.rewrite_body(&mut output.body);
Ok(output)
}

Expand Down
6 changes: 6 additions & 0 deletions crates/switchyard-translation/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,10 @@ pub fn encode_stream_with_extensions(
let origins = crate::codex_namespaces::qualified_tool_origins(request_extensions);
let custom_tools = crate::codex_custom_tools::custom_tool_names(request_extensions);
let mut custom_state = crate::codex_custom_tools::CustomToolCallStreamState::default();
// One response knows nothing about earlier turns, so the IDs the conversation
// already used seed the rewrite of tool IDs across all of this stream's events.
let mut tool_ids =
crate::util::AnthropicToolIdRewriter::new(crate::util::seen_tool_ids(request_extensions));
let target_format: FormatId = target.into();
// The target is always a built-in wire format, so this lookup cannot fail; a
// failure returns as an `Err` rather than a panic.
Expand All @@ -153,6 +157,7 @@ pub fn encode_stream_with_extensions(
for value in &mut encoded {
stamp_streamed_response_model(value, target, served_model_for_events.as_deref());
crate::codex_namespaces::restore_qualified_tool_names(value, &origins);
tool_ids.rewrite_body(value);
}
// Argument deltas for a freeform tool cannot be expressed on the wire; the
// rewritten completed item carries the input instead.
Expand Down Expand Up @@ -187,6 +192,7 @@ pub fn encode_stream_with_extensions(
served_model_for_events.as_deref(),
);
crate::codex_namespaces::restore_qualified_tool_names(&mut value, &origins);
tool_ids.rewrite_body(&mut value);
if crate::codex_custom_tools::restore_custom_tool_calls_in_event(
&mut value,
&custom_tools,
Expand Down
4 changes: 2 additions & 2 deletions crates/switchyard-translation/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,6 @@ pub use llm::*;
pub use policy::*;
pub use stream::*;
pub use util::{
PRESERVATION_METADATA_KEY, normalize_anthropic_tool_use_ids, prepare_request_for_target,
sanitize_anthropic_tool_use_id,
AnthropicToolIdRewriter, PRESERVATION_METADATA_KEY, SEEN_TOOL_IDS_KEY,
prepare_request_for_target, sanitize_anthropic_tool_use_id,
};
Loading
Loading