fix(anthropic): preserve SDK and dated model compatibility

This commit is contained in:
chengyongru 2026-08-04 10:50:26 +08:00
parent 39b2294ecf
commit 5e0ef36cf2
2 changed files with 28 additions and 15 deletions

View File

@ -32,7 +32,8 @@ def _gen_tool_id() -> str:
_VALID_TOOL_ID = re.compile(r"^[a-zA-Z0-9_-]+$")
_CLAUDE_MODEL_VERSION = re.compile(
r"claude-(?P<family>[a-z]+)-(?P<major>\d+)(?:-(?P<minor>\d+))?"
r"claude-(?P<family>[a-z]+)-(?P<major>\d+)"
r"(?:-(?P<minor>\d{1,2})(?=-|$))?"
)
_ADAPTIVE_ONLY_MIN_VERSIONS = {
"opus": (4, 7),
@ -617,8 +618,13 @@ class AnthropicProvider(LLMProvider):
elif thinking_enabled and adaptive_only:
# Newer Claude models removed manual token budgets. Their effort
# control is independent from the adaptive thinking mode.
kwargs["thinking"] = {"type": "adaptive"}
kwargs["output_config"] = {"effort": reasoning_effort_lower}
# These fields were added as first-class SDK arguments after our
# minimum anthropic version; extra_body preserves wire compatibility
# across the declared dependency range.
kwargs["extra_body"] = {
"thinking": {"type": "adaptive"},
"output_config": {"effort": reasoning_effort_lower},
}
elif thinking_enabled:
budget_map = {"low": 1024, "medium": 4096, "high": max(8192, max_tokens)}
budget = budget_map.get(reasoning_effort_lower, 4096)

View File

@ -82,8 +82,8 @@ def test_opus_4_7_omits_temperature_adaptive() -> None:
def test_opus_4_7_high_uses_adaptive_effort() -> None:
kw = _build(_make_provider("claude-opus-4-7"), "high", max_tokens=4096)
assert "temperature" not in kw
assert kw["thinking"] == {"type": "adaptive"}
assert kw["output_config"] == {"effort": "high"}
assert kw["extra_body"]["thinking"] == {"type": "adaptive"}
assert kw["extra_body"]["output_config"] == {"effort": "high"}
assert kw["max_tokens"] == 4096
@ -102,8 +102,8 @@ def test_opus_4_8_omits_temperature_adaptive() -> None:
def test_opus_4_8_high_uses_adaptive_effort() -> None:
kw = _build(_make_provider("claude-opus-4-8"), "high", max_tokens=4096)
assert "temperature" not in kw
assert kw["thinking"] == {"type": "adaptive"}
assert kw["output_config"] == {"effort": "high"}
assert kw["extra_body"]["thinking"] == {"type": "adaptive"}
assert kw["extra_body"]["output_config"] == {"effort": "high"}
def test_opus_4_8_omits_temperature_none() -> None:
@ -119,8 +119,8 @@ def test_fable_omits_temperature_adaptive() -> None:
def test_fable_high_uses_adaptive_effort() -> None:
kw = _build(_make_provider("claude-fable-5"), "high", max_tokens=4096)
assert "temperature" not in kw
assert kw["thinking"] == {"type": "adaptive"}
assert kw["output_config"] == {"effort": "high"}
assert kw["extra_body"]["thinking"] == {"type": "adaptive"}
assert kw["extra_body"]["output_config"] == {"effort": "high"}
def test_fable_omits_temperature_none() -> None:
@ -137,8 +137,8 @@ def test_sonnet_5_omits_temperature_adaptive() -> None:
def test_sonnet_5_high_uses_adaptive_effort() -> None:
kw = _build(_make_provider("claude-sonnet-5"), "high", max_tokens=4096)
assert "temperature" not in kw
assert kw["thinking"] == {"type": "adaptive"}
assert kw["output_config"] == {"effort": "high"}
assert kw["extra_body"]["thinking"] == {"type": "adaptive"}
assert kw["extra_body"]["output_config"] == {"effort": "high"}
def test_sonnet_5_omits_temperature_none() -> None:
@ -156,16 +156,23 @@ def test_opus_5_omits_temperature(reasoning_effort: str | None) -> None:
@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)
assert kw["thinking"] == {"type": "adaptive"}
assert kw["output_config"] == {"effort": reasoning_effort}
assert kw["extra_body"]["thinking"] == {"type": "adaptive"}
assert kw["extra_body"]["output_config"] == {"effort": reasoning_effort}
assert kw["max_tokens"] == 4096
def test_dated_opus_5_model_uses_family_capabilities() -> None:
kw = _build(_make_provider("claude-opus-5-20260724"), "medium")
assert "temperature" not in kw
assert kw["thinking"] == {"type": "adaptive"}
assert kw["output_config"] == {"effort": "medium"}
assert kw["extra_body"]["thinking"] == {"type": "adaptive"}
assert kw["extra_body"]["output_config"] == {"effort": "medium"}
def test_dated_opus_4_model_does_not_treat_date_as_minor_version() -> None:
kw = _build(_make_provider("claude-opus-4-20250514"), "high")
assert kw["temperature"] == 1.0
assert kw["thinking"] == {"type": "enabled", "budget_tokens": 8192}
assert "extra_body" not in kw
def test_ordinary_model_sends_temperature() -> None: