From 5f6d3fa96bd125626bcca004f5fad2291c6ef36a Mon Sep 17 00:00:00 2001 From: Alex Fournier Date: Wed, 2 Sep 2026 09:35:21 -0700 Subject: [PATCH] fix(translation): preserve OpenAI tool strictness for Anthropic Signed-off-by: Alex Fournier --- .../src/codecs/anthropic/buffered.rs | 8 +++- .../tests/request_translation.rs | 38 +++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/crates/switchyard-translation/src/codecs/anthropic/buffered.rs b/crates/switchyard-translation/src/codecs/anthropic/buffered.rs index 723744021..87b907fb5 100644 --- a/crates/switchyard-translation/src/codecs/anthropic/buffered.rs +++ b/crates/switchyard-translation/src/codecs/anthropic/buffered.rs @@ -1044,11 +1044,15 @@ fn encode_anthropic_tools(tools: &[ToolDefinition]) -> Value { tools .iter() .map(|tool| { - json!({ + let mut item = json!({ "name": tool.name, "description": tool.description.clone().unwrap_or_default(), "input_schema": tool.parameters, - }) + }); + if let Some(strict) = tool.strict { + item["strict"] = Value::Bool(strict); + } + item }) .collect(), ) diff --git a/crates/switchyard-translation/tests/request_translation.rs b/crates/switchyard-translation/tests/request_translation.rs index 22b710b05..7338a8d16 100644 --- a/crates/switchyard-translation/tests/request_translation.rs +++ b/crates/switchyard-translation/tests/request_translation.rs @@ -1853,6 +1853,44 @@ fn openai_schema_constraints_are_removed_from_anthropic_output_format() -> TestR Ok(()) } +// Verifies OpenAI tool strictness survives translation to Anthropic. +#[test] +fn openai_tool_strictness_is_preserved_for_anthropic() -> TestResult { + let engine = TranslationEngine::default(); + let body = json!({ + "model": "gpt-5", + "messages": [{"role": "user", "content": "Use a tool."}], + "tools": [ + { + "type": "function", + "function": {"name": "strict", "parameters": {}, "strict": true} + }, + { + "type": "function", + "function": {"name": "non_strict", "parameters": {}, "strict": false} + }, + { + "type": "function", + "function": {"name": "unspecified", "parameters": {}} + } + ] + }); + + let output = engine + .translate_request( + WireFormat::OpenAiChat, + WireFormat::AnthropicMessages, + &body, + &TranslationPolicy::default(), + )? + .body; + + assert_eq!(output["tools"][0]["strict"], true); + assert_eq!(output["tools"][1]["strict"], false); + assert!(output["tools"][2].get("strict").is_none()); + Ok(()) +} + // Verifies Anthropic-bound OpenAI requests get the required max_tokens fallback. #[test] fn openai_request_to_anthropic_adds_required_default_max_tokens() -> TestResult {