From 2d99433093cf032c434a118a9468520601e95188 Mon Sep 17 00:00:00 2001 From: hcl Date: Thu, 18 Jun 2026 20:22:45 +0800 Subject: [PATCH] fix(anthropic): don't leak tool 'type' into OpenAI function parameters schema (#30618) In the messages->chat/completions bridge, translate_anthropic_tools_to_openai merged every non-mapped tool key into the function parameters dict. The Anthropic tool 'type' (e.g. 'custom') thus overwrote parameters.type ('object' -> 'custom'), and providers reject it ('custom' is not a valid JSON-Schema type). Exclude 'type' from the passthrough. Fixes #30557. --- .../adapters/transformation.py | 12 ++++++++++- ...al_pass_through_adapters_transformation.py | 20 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py b/litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py index bf425637b56..75a8acdfcc3 100644 --- a/litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py +++ b/litellm/llms/anthropic/experimental_pass_through/adapters/transformation.py @@ -859,7 +859,17 @@ class LiteLLMAnthropicMessagesAdapter: """ new_tools: List[ChatCompletionToolParam] = [] tool_name_mapping: Dict[str, str] = {} - mapped_tool_params = ["name", "input_schema", "description", "cache_control"] + # "type" is the Anthropic tool type (e.g. "custom"); it must not be + # merged into the OpenAI function `parameters` schema below, or it + # overwrites the real parameters.type ("object") and the provider + # rejects the request. See #30557. + mapped_tool_params = [ + "name", + "input_schema", + "description", + "cache_control", + "type", + ] for idx, tool in enumerate(tools): # Check if this is an Anthropic-native tool that should be kept as-is diff --git a/tests/test_litellm/llms/anthropic/experimental_pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py b/tests/test_litellm/llms/anthropic/experimental_pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py index 76aa3a9c6aa..0300b6f3f51 100644 --- a/tests/test_litellm/llms/anthropic/experimental_pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py +++ b/tests/test_litellm/llms/anthropic/experimental_pass_through/adapters/test_anthropic_experimental_pass_through_adapters_transformation.py @@ -2747,3 +2747,23 @@ def test_translate_openai_response_to_anthropic_with_polyfill_both_compaction_an cm = result.get("context_management") assert cm is not None assert cm["applied_edits"][0]["type"] == "compact_20260112" + + +def test_translate_anthropic_tools_to_openai_preserves_parameters_type(): + """Regression for #30557: the Anthropic tool `type` ("custom") must not be + merged into the OpenAI function `parameters`, overwriting parameters.type.""" + adapter = LiteLLMAnthropicMessagesAdapter() + tools = [ + { + "type": "custom", + "name": "get_weather", + "description": "Get weather", + "input_schema": {"type": "object", "properties": {}}, + } + ] + + new_tools, _ = adapter.translate_anthropic_tools_to_openai(tools=tools) + + params = new_tools[0]["function"]["parameters"] + assert params["type"] == "object" + assert new_tools[0]["type"] == "function"