refactor(anthropic): drive adaptive-thinking gate via supports_adaptive_thinking flag

Three of greptile's open comments on #27074 (P2 converse:512, P1
databricks:361, and the underlying capability-flag policy rule) flagged
the same pattern: _is_claude_4_6_model(...) or _is_claude_4_7_model(...)
used inline as a runtime 'is this an adaptive-thinking model?' check.
That requires a code release each time a new adaptive Claude lands.

Consolidate the inline gating to AnthropicModelInfo._is_adaptive_thinking_model,
and switch the helper itself to read a new supports_adaptive_thinking
flag from `model_prices_and_context_window.json` via `_supports_factory`,
falling back to the family pattern only when the model-map entry doesn't
carry the flag (preserves OpenRouter / Vercel / Bedrock-prefixed variants
that route through the same code path with non-canonical ids).

Adds `supports_adaptive_thinking: true` to the four 4.6/4.7 anthropic
entries (opus-4-6 + dated, opus-4-7 + dated, sonnet-4-6). Bedrock-prefixed
and Vertex-prefixed entries don't need the flag because both fall back
through the family pattern (the helper short-circuits early on True from
either path) and the bedrock/vertex Claude IDs all match the existing
opus-4-{6,7} / sonnet-4-{6,7} pattern.

Affected call sites:

- `bedrock/chat/converse_transformation.py:_handle_reasoning_effort_parameter`
- `anthropic/chat/transformation.py:_map_reasoning_effort`
- `anthropic/chat/transformation.py:map_openai_params` (output_config branch)
- `databricks/chat/transformation.py:map_openai_params` (output_config branch)

The remaining `_is_claude_4_6_model` / `_is_claude_4_7_model` references
in `AnthropicConfig._validate_effort_for_model` and
`AnthropicConfig.get_supported_openai_params` are intentionally retained:
they're per-model gating fallbacks for variants whose model-map entries
don't yet carry the `supports_max_reasoning_effort` /
`supports_reasoning` flag. Those are documented in-place.

Tests: 537 anthropic/bedrock/databricks/vertex/messages tests pass.

Co-authored-by: Mateo Wang <mateo-berri@users.noreply.github.com>
This commit is contained in:
Cursor Agent
2026-05-04 18:58:22 +00:00
parent 5d124892d2
commit 98ced0ae43
5 changed files with 48 additions and 22 deletions
@@ -920,9 +920,7 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
"""
if reasoning_effort is None or reasoning_effort == "none":
return None
if AnthropicConfig._is_claude_4_6_model(
model
) or AnthropicConfig._is_claude_4_7_model(model):
if AnthropicConfig._is_adaptive_thinking_model(model):
return AnthropicThinkingParam(
type="adaptive",
)
@@ -1262,11 +1260,12 @@ class AnthropicConfig(AnthropicModelInfo, BaseConfig):
optional_params.pop("output_config", None)
else:
optional_params["thinking"] = mapped_thinking
# For Claude 4.6+ models, effort is controlled via output_config,
# not thinking budget_tokens. Map reasoning_effort to output_config.
if AnthropicConfig._is_claude_4_6_model(
model
) or AnthropicConfig._is_claude_4_7_model(model):
# For Claude 4.6+ adaptive-thinking models, effort is
# controlled via ``output_config``, not
# ``thinking.budget_tokens``. Driven by
# ``supports_adaptive_thinking`` in the model map so
# adding a new adaptive Claude is a model-map-only change.
if AnthropicConfig._is_adaptive_thinking_model(model):
# ``_map_reasoning_effort`` returns ``type=adaptive``
# for any string on adaptive models without checking
# the value, so reject unmapped efforts here (matching
+20 -1
View File
@@ -273,7 +273,26 @@ class AnthropicModelInfo(BaseLLMModelInfo):
@staticmethod
def _is_adaptive_thinking_model(model: str) -> bool:
"""Claude 4.6+ models use adaptive thinking with output_config effort."""
"""Claude 4.6+ models use adaptive thinking with ``output_config.effort``.
Driven by the ``supports_adaptive_thinking`` flag in
``model_prices_and_context_window.json`` so that adding a new
adaptive-thinking model is a pure model-map change. Falls back to
the family-pattern check for OpenRouter / Vercel / Bedrock /
provider-prefixed variants whose model-map entries don't (yet)
carry the flag.
"""
from litellm.utils import _supports_factory
try:
if _supports_factory(
model=model,
custom_llm_provider=None,
key="supports_adaptive_thinking",
):
return True
except Exception:
pass
return AnthropicModelInfo._is_claude_4_6_model(
model
) or AnthropicModelInfo._is_claude_4_7_model(model)
@@ -478,14 +478,15 @@ class AmazonConverseConfig(BaseConfig):
optional_params.pop("output_config", None)
else:
optional_params["thinking"] = mapped_thinking
# Adaptive-thinking models (Claude 4.6 / 4.7) take the tier
# via output_config.effort. Mirror the mapping used by
# AnthropicConfig.map_openai_params and apply the same
# validation rules so unmapped/garbage efforts surface as a
# 400 instead of being silently flattened on the wire.
if AnthropicConfig._is_claude_4_6_model(
model
) or AnthropicConfig._is_claude_4_7_model(model):
# Adaptive-thinking models (Claude 4.6 / 4.7+) take the
# tier via ``output_config.effort``. Mirror the mapping
# used by ``AnthropicConfig.map_openai_params`` and apply
# the same validation rules so unmapped/garbage efforts
# surface as a 400 instead of being silently flattened on
# the wire. Driven by ``supports_adaptive_thinking`` in
# ``model_prices_and_context_window.json`` so a future
# adaptive Claude release lands as a model-map change.
if AnthropicConfig._is_adaptive_thinking_model(model):
# Use ``.get()`` without a fallback so unmapped efforts
# (e.g. ``"disabled"``) surface as a clean 400 here
# rather than leaking the raw garbage string through to
@@ -350,15 +350,17 @@ class DatabricksConfig(DatabricksBase, OpenAILikeChatConfig, AnthropicConfig):
optional_params.pop("output_config", None)
else:
optional_params["thinking"] = mapped_thinking
# For Claude 4.6/4.7 adaptive models, ``_map_reasoning_effort``
# For Claude 4.6+ adaptive models, ``_map_reasoning_effort``
# returns ``type=adaptive`` for ANY non-None / non-"none"
# string without validating the value, so reject unmapped
# efforts here and set ``output_config.effort`` (matching the
# Anthropic native / Bedrock Converse / Bedrock Invoke /
# /v1/messages paths).
if AnthropicConfig._is_claude_4_6_model(
model
) or AnthropicConfig._is_claude_4_7_model(model):
# /v1/messages paths). Driven by ``supports_adaptive_thinking``
# in the model map so future adaptive Claudes land via a
# model-map update rather than a code release — the
# Anthropic-native and Bedrock routes already use the same
# helper, so all three paths stay in lock-step.
if AnthropicConfig._is_adaptive_thinking_model(model):
# ``reasoning_effort_value`` comes from ``non_default_params``
# so its static type is ``Any | None``. Narrow to ``str`` for
# the mapping lookup; non-strings fall through to the
+5
View File
@@ -9228,6 +9228,7 @@
"search_context_size_low": 0.01,
"search_context_size_medium": 0.01
},
"supports_adaptive_thinking": true,
"supports_assistant_prefill": true,
"supports_computer_use": true,
"supports_function_calling": true,
@@ -9421,6 +9422,7 @@
"search_context_size_low": 0.01,
"search_context_size_medium": 0.01
},
"supports_adaptive_thinking": true,
"supports_assistant_prefill": false,
"supports_computer_use": true,
"supports_function_calling": true,
@@ -9454,6 +9456,7 @@
"search_context_size_low": 0.01,
"search_context_size_medium": 0.01
},
"supports_adaptive_thinking": true,
"supports_assistant_prefill": false,
"supports_computer_use": true,
"supports_function_calling": true,
@@ -9487,6 +9490,7 @@
"search_context_size_low": 0.01,
"search_context_size_medium": 0.01
},
"supports_adaptive_thinking": true,
"supports_assistant_prefill": false,
"supports_computer_use": true,
"supports_function_calling": true,
@@ -9521,6 +9525,7 @@
"search_context_size_low": 0.01,
"search_context_size_medium": 0.01
},
"supports_adaptive_thinking": true,
"supports_assistant_prefill": false,
"supports_computer_use": true,
"supports_function_calling": true,