fix(anthropic): honor disabled thinking on Opus 5

Send an explicit disabled thinking mode for default-on Opus and Sonnet 5 models while preserving unset provider defaults and minimum-SDK compatibility.
This commit is contained in:
chengyongru 2026-08-04 13:04:42 +08:00
parent e971f6bb8f
commit 356eeeb48c
2 changed files with 29 additions and 3 deletions

View File

@ -41,6 +41,10 @@ _ADAPTIVE_ONLY_MIN_VERSIONS = {
"fable": (5, 0),
"mythos": (5, 0),
}
_THINKING_DISABLE_MIN_VERSIONS = {
"opus": (5, 0),
"sonnet": (5, 0),
}
_SAMPLING_DEPRECATED_MODELS = {"claude-mythos-preview"}
@ -606,7 +610,14 @@ class AnthropicProvider(LLMProvider):
if system:
kwargs["system"] = system
if reasoning_effort_lower == "adaptive":
if reasoning_effort_lower == "none" and _model_version_at_least(
model_name, _THINKING_DISABLE_MIN_VERSIONS
):
# These models think by default, so omission would not honor an
# explicit request to disable thinking. Use extra_body for SDKs
# that predate the typed thinking parameter.
kwargs["extra_body"] = {"thinking": {"type": "disabled"}}
elif reasoning_effort_lower == "adaptive":
# Adaptive thinking: model decides when and how much to think
# Also auto-enables interleaved thinking between tool calls.
kwargs["thinking"] = {"type": "adaptive"}

View File

@ -142,9 +142,10 @@ def test_sonnet_5_high_uses_adaptive_effort() -> None:
def test_sonnet_5_omits_temperature_none() -> None:
kw = _build(_make_provider("anthropic/claude-sonnet-5"), None)
kw = _build(_make_provider("anthropic/claude-sonnet-5"), "none")
assert "temperature" not in kw
assert "thinking" not in kw
assert kw["extra_body"] == {"thinking": {"type": "disabled"}}
def test_mythos_preview_omits_temperature_but_keeps_manual_budget() -> None:
@ -154,12 +155,26 @@ def test_mythos_preview_omits_temperature_but_keeps_manual_budget() -> None:
assert "extra_body" not in kw
@pytest.mark.parametrize("reasoning_effort", [None, "adaptive", "low", "medium", "high", "xhigh", "max"])
@pytest.mark.parametrize(
"reasoning_effort", [None, "none", "adaptive", "low", "medium", "high", "xhigh", "max"]
)
def test_opus_5_omits_temperature(reasoning_effort: str | None) -> None:
kw = _build(_make_provider("claude-opus-5"), reasoning_effort)
assert "temperature" not in kw
def test_opus_5_none_disables_default_thinking() -> None:
kw = _build(_make_provider("claude-opus-5"), "none")
assert kw["extra_body"] == {"thinking": {"type": "disabled"}}
assert "thinking" not in kw
def test_opus_5_unset_preserves_provider_default() -> None:
kw = _build(_make_provider("claude-opus-5"), None)
assert "thinking" not in kw
assert "extra_body" not in kw
@pytest.mark.parametrize("reasoning_effort", ["low", "medium", "high", "xhigh", "max"])
def test_opus_5_uses_adaptive_thinking_with_effort(reasoning_effort: str) -> None:
kw = _build(_make_provider("claude-opus-5"), reasoning_effort, max_tokens=4096)