diff --git a/nanobot/providers/anthropic_provider.py b/nanobot/providers/anthropic_provider.py index 8c43ce562..6e24b4ba4 100644 --- a/nanobot/providers/anthropic_provider.py +++ b/nanobot/providers/anthropic_provider.py @@ -614,9 +614,8 @@ class AnthropicProvider(LLMProvider): 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"}} + # explicit request to disable thinking. + kwargs["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. @@ -626,13 +625,8 @@ 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. - # 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}, - } + kwargs["thinking"] = {"type": "adaptive"} + kwargs["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) diff --git a/pyproject.toml b/pyproject.toml index d2be96b23..a4056afdf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -24,7 +24,7 @@ license-files = [ dependencies = [ "typer>=0.20.0,<1.0.0", - "anthropic>=0.45.0,<1.0.0", + "anthropic>=0.100.0,<1.0.0", "pydantic>=2.12.0,<3.0.0", "pydantic-settings>=2.12.0,<3.0.0", # Feishu's lark-oapi currently requires websockets<16; core supports 15 and 16. diff --git a/tests/providers/test_anthropic_thinking.py b/tests/providers/test_anthropic_thinking.py index 2e962175b..de5e9ffbe 100644 --- a/tests/providers/test_anthropic_thinking.py +++ b/tests/providers/test_anthropic_thinking.py @@ -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["extra_body"]["thinking"] == {"type": "adaptive"} - assert kw["extra_body"]["output_config"] == {"effort": "high"} + assert kw["thinking"] == {"type": "adaptive"} + assert kw["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["extra_body"]["thinking"] == {"type": "adaptive"} - assert kw["extra_body"]["output_config"] == {"effort": "high"} + assert kw["thinking"] == {"type": "adaptive"} + assert kw["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["extra_body"]["thinking"] == {"type": "adaptive"} - assert kw["extra_body"]["output_config"] == {"effort": "high"} + assert kw["thinking"] == {"type": "adaptive"} + assert kw["output_config"] == {"effort": "high"} def test_fable_omits_temperature_none() -> None: @@ -137,22 +137,22 @@ 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["extra_body"]["thinking"] == {"type": "adaptive"} - assert kw["extra_body"]["output_config"] == {"effort": "high"} + assert kw["thinking"] == {"type": "adaptive"} + assert kw["output_config"] == {"effort": "high"} def test_sonnet_5_omits_temperature_none() -> 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"}} + assert kw["thinking"] == {"type": "disabled"} + assert "output_config" not in kw def test_mythos_preview_omits_temperature_but_keeps_manual_budget() -> None: kw = _build(_make_provider("claude-mythos-preview"), "high", max_tokens=4096) assert "temperature" not in kw assert kw["thinking"] == {"type": "enabled", "budget_tokens": 8192} - assert "extra_body" not in kw + assert "output_config" not in kw @pytest.mark.parametrize( @@ -165,36 +165,36 @@ def test_opus_5_omits_temperature(reasoning_effort: str | None) -> None: 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 + assert kw["thinking"] == {"type": "disabled"} + assert "output_config" 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 + assert "output_config" 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) - assert kw["extra_body"]["thinking"] == {"type": "adaptive"} - assert kw["extra_body"]["output_config"] == {"effort": reasoning_effort} + assert kw["thinking"] == {"type": "adaptive"} + assert kw["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["extra_body"]["thinking"] == {"type": "adaptive"} - assert kw["extra_body"]["output_config"] == {"effort": "medium"} + assert kw["thinking"] == {"type": "adaptive"} + assert kw["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 + assert "output_config" not in kw def test_ordinary_model_sends_temperature() -> None: