"""Tests for Anthropic effort-parameter gating (#831). Haiku 4.5 (and current Haiku versions) reject the ``effort`` parameter with a 400. Opus 4.5+ and Sonnet 4.5+ accept it. The gate uses a forward-compat regex so future ``claude-{opus,sonnet}-X-Y`` releases inherit support automatically. """ import pytest from tradingagents.llm_clients import anthropic_client as mod def _capture_kwargs(monkeypatch): captured: dict = {} monkeypatch.setattr( mod, "NormalizedChatAnthropic", lambda **kwargs: captured.setdefault("kwargs", kwargs), ) return captured @pytest.mark.unit class TestEffortGate: @pytest.mark.parametrize( "model", ["claude-haiku-4-5", "claude-haiku-5-0", "claude-haiku-4-7-preview"], ) def test_haiku_does_not_receive_effort(self, monkeypatch, model): captured = _capture_kwargs(monkeypatch) mod.AnthropicClient(model=model, effort="medium", api_key="x").get_llm() assert "effort" not in captured["kwargs"] @pytest.mark.parametrize( "model", [ "claude-opus-4-5", "claude-opus-4-6", "claude-opus-4-7", "claude-sonnet-4-5", "claude-sonnet-4-6", ], ) def test_current_opus_and_sonnet_receive_effort(self, monkeypatch, model): captured = _capture_kwargs(monkeypatch) mod.AnthropicClient(model=model, effort="high", api_key="x").get_llm() assert captured["kwargs"]["effort"] == "high" @pytest.mark.parametrize( "model", ["claude-opus-5-0", "claude-opus-4-8", "claude-sonnet-5-0"], ) def test_future_opus_sonnet_inherit_effort_via_pattern(self, monkeypatch, model): """Forward-compat: new Opus/Sonnet versions don't need a code change.""" captured = _capture_kwargs(monkeypatch) mod.AnthropicClient(model=model, effort="low", api_key="x").get_llm() assert captured["kwargs"]["effort"] == "low" def test_mythos_preview_receives_effort(self, monkeypatch): captured = _capture_kwargs(monkeypatch) mod.AnthropicClient( model="claude-mythos-preview", effort="medium", api_key="x" ).get_llm() assert captured["kwargs"]["effort"] == "medium" def test_unknown_anthropic_model_does_not_receive_effort(self, monkeypatch): """Default is conservative — unknown models don't get effort to avoid 400s.""" captured = _capture_kwargs(monkeypatch) mod.AnthropicClient( model="claude-experimental-x", effort="medium", api_key="x" ).get_llm() assert "effort" not in captured["kwargs"] def test_other_kwargs_still_forwarded_when_effort_skipped(self, monkeypatch): """Skipping effort must not break other passthrough kwargs.""" captured = _capture_kwargs(monkeypatch) mod.AnthropicClient( model="claude-haiku-4-5", effort="medium", api_key="placeholder", max_tokens=1024, timeout=30, ).get_llm() assert captured["kwargs"]["api_key"] == "placeholder" assert captured["kwargs"]["max_tokens"] == 1024 assert captured["kwargs"]["timeout"] == 30 assert "effort" not in captured["kwargs"]