From ad9e861ae701cc25e96e7e7daacbabc1e70806aa Mon Sep 17 00:00:00 2001 From: ryan-crabbe-berri Date: Tue, 13 Oct 2026 19:01:59 -0700 Subject: [PATCH] fix(anthropic): treat adaptive thinking as enabled when mapping response_format is_thinking_enabled only recognised thinking.type=enabled, so a request that paired thinking.type=adaptive with response_format took the synthetic JSON tool path with a forced tool_choice, which Anthropic rejects alongside thinking. The same check also gates the Bedrock Converse and Databricks forced tool_choice downgrades, so they now see adaptive thinking too. --- litellm/llms/base_llm/chat/transformation.py | 6 ++++- ...ations_anthropic_claude3_transformation.py | 25 +++++++++++++++++++ tests/unit/test_thinking_enabled.py | 1 + 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/litellm/llms/base_llm/chat/transformation.py b/litellm/llms/base_llm/chat/transformation.py index 8275829765b..c410ba8c55d 100644 --- a/litellm/llms/base_llm/chat/transformation.py +++ b/litellm/llms/base_llm/chat/transformation.py @@ -124,7 +124,11 @@ class BaseConfig(ABC): def is_thinking_enabled(self, non_default_params: dict) -> bool: thinking: Final = non_default_params.get("thinking") thinking_type: Final = thinking.get("type") if isinstance(thinking, dict) else None - return thinking is True or thinking_type == "enabled" or non_default_params.get("reasoning_effort") is not None + return ( + thinking is True + or thinking_type in ("enabled", "adaptive") + or non_default_params.get("reasoning_effort") is not None + ) def is_max_tokens_in_request(self, non_default_params: dict) -> bool: """ diff --git a/tests/unit/llms/bedrock/chat/invoke_transformations/test_bedrock_chat_invoke_transformations_anthropic_claude3_transformation.py b/tests/unit/llms/bedrock/chat/invoke_transformations/test_bedrock_chat_invoke_transformations_anthropic_claude3_transformation.py index 859b57c7cf3..e64f472a5d3 100644 --- a/tests/unit/llms/bedrock/chat/invoke_transformations/test_bedrock_chat_invoke_transformations_anthropic_claude3_transformation.py +++ b/tests/unit/llms/bedrock/chat/invoke_transformations/test_bedrock_chat_invoke_transformations_anthropic_claude3_transformation.py @@ -760,6 +760,31 @@ def test_bedrock_chat_invoke_tool_based_response_format_still_upgrades_legacy_th assert result["output_config"] == {"effort": "high"} +def test_bedrock_chat_invoke_adaptive_thinking_with_tool_based_response_format_avoids_forced_tool_choice( + local_model_cost_map, +): + result = AmazonAnthropicClaudeConfig().map_openai_params( + non_default_params={ + "response_format": { + "type": "json_schema", + "json_schema": { + "name": "test_schema", + "schema": {"type": "object", "properties": {"result": {"type": "string"}}}, + }, + }, + "thinking": {"type": "adaptive"}, + "max_tokens": 8192, + }, + optional_params={}, + model="us.anthropic.claude-opus-5", + drop_params=False, + ) + + assert "tools" in result + assert "tool_choice" not in result + assert result["thinking"] == {"type": "adaptive"} + + def test_bedrock_chat_invoke_response_format_stub_still_upgrades_legacy_thinking(local_model_cost_map): """Regression: the tool-based ``response_format`` path swaps in a Claude 3 stub model before the shared Anthropic mapping, which hid the adaptive-only model diff --git a/tests/unit/test_thinking_enabled.py b/tests/unit/test_thinking_enabled.py index 744b258e617..60f0ec42483 100644 --- a/tests/unit/test_thinking_enabled.py +++ b/tests/unit/test_thinking_enabled.py @@ -50,6 +50,7 @@ class TestIsThinkingEnabled: ({"thinking": None}, False), # thinking={'type': 'enabled'} returns True ({"thinking": {"type": "enabled"}}, True), + ({"thinking": {"type": "adaptive"}}, True), # thinking key missing returns False ({}, False), # thinking={} returns False