| import pytest |
|
|
| from free_claude_code.application.reasoning import ( |
| client_reasoning_policy, |
| resolve_reasoning_policy, |
| ) |
| from free_claude_code.config.reasoning import ReasoningPreference |
| from free_claude_code.core.anthropic.models import MessagesRequest |
| from free_claude_code.core.reasoning import ( |
| ReasoningControl, |
| ReasoningEffort, |
| ReasoningPolicy, |
| ) |
|
|
|
|
| def _request(**overrides) -> MessagesRequest: |
| payload = { |
| "model": "provider/model", |
| "messages": [{"role": "user", "content": "hello"}], |
| } |
| payload.update(overrides) |
| return MessagesRequest.model_validate(payload) |
|
|
|
|
| def test_client_without_reasoning_control_uses_provider_default() -> None: |
| assert client_reasoning_policy(_request()) == ReasoningPolicy.provider_default() |
|
|
|
|
| def test_client_reasoning_preserves_effort_and_exact_budget() -> None: |
| policy = client_reasoning_policy( |
| _request( |
| thinking={"type": "enabled", "budget_tokens": 4096}, |
| output_config={"effort": "xhigh"}, |
| ) |
| ) |
|
|
| assert policy == ReasoningPolicy.on( |
| effort=ReasoningEffort.XHIGH, |
| budget_tokens=4096, |
| ) |
|
|
|
|
| def test_named_effort_preserves_intent_without_exact_client_budget() -> None: |
| policy = client_reasoning_policy(_request(output_config={"effort": "high"})) |
|
|
| assert policy == ReasoningPolicy( |
| control=ReasoningControl.DEFAULT, |
| effort=ReasoningEffort.HIGH, |
| ) |
| assert policy.budget_tokens is None |
| assert policy.requests_reasoning is True |
|
|
|
|
| def test_invalid_budget_does_not_implicitly_enable_reasoning() -> None: |
| policy = client_reasoning_policy(_request(thinking={"budget_tokens": 0})) |
|
|
| assert policy == ReasoningPolicy.provider_default() |
|
|
|
|
| @pytest.mark.parametrize( |
| "messages_request", |
| [ |
| _request(thinking={"type": "disabled"}), |
| _request(output_config={"effort": "none"}), |
| ], |
| ) |
| def test_client_disable_is_explicit(messages_request: MessagesRequest) -> None: |
| policy = client_reasoning_policy(messages_request) |
|
|
| assert policy.control is ReasoningControl.OFF |
| assert policy.output_enabled is False |
| assert policy.requests_reasoning is False |
|
|
|
|
| def test_disabled_thinking_preserves_independent_effort_intent() -> None: |
| policy = client_reasoning_policy( |
| _request( |
| thinking={"type": "disabled"}, |
| output_config={"effort": "medium"}, |
| ) |
| ) |
|
|
| assert policy == ReasoningPolicy( |
| control=ReasoningControl.OFF, |
| effort=ReasoningEffort.MEDIUM, |
| ) |
| assert policy.requests_reasoning is False |
|
|
|
|
| def test_fixed_route_effort_overrides_client_disable() -> None: |
| policy = resolve_reasoning_policy( |
| _request(thinking={"type": "disabled"}), |
| ReasoningPreference.MAX, |
| ) |
|
|
| assert policy == ReasoningPolicy.on(effort=ReasoningEffort.MAX) |
|
|
|
|
| def test_fixed_off_overrides_client_enable() -> None: |
| policy = resolve_reasoning_policy( |
| _request(thinking={"type": "enabled", "budget_tokens": 1024}), |
| ReasoningPreference.OFF, |
| ) |
|
|
| assert policy == ReasoningPolicy.off() |
|
|
|
|
| def test_client_preference_preserves_client_policy() -> None: |
| request = _request(output_config={"effort": "low"}) |
|
|
| assert resolve_reasoning_policy( |
| request, ReasoningPreference.CLIENT |
| ) == client_reasoning_policy(request) |
|
|
|
|
| def test_unresolved_inherit_is_rejected() -> None: |
| with pytest.raises(ValueError, match="must be resolved"): |
| resolve_reasoning_policy(_request(), ReasoningPreference.INHERIT) |
|
|
|
|
| @pytest.mark.parametrize("budget", [0, -1, True]) |
| def test_reasoning_budget_requires_a_positive_integer(budget: int) -> None: |
| with pytest.raises(ValueError, match="positive integer"): |
| ReasoningPolicy.on(budget_tokens=budget) |
|
|
|
|
| def test_reasoning_budget_requires_explicit_on_control() -> None: |
| with pytest.raises(ValueError, match="control to be on"): |
| ReasoningPolicy(budget_tokens=100) |
|
|
|
|
| @pytest.mark.parametrize( |
| ("effort", "expected"), |
| ( |
| (ReasoningEffort.MINIMAL, 512), |
| (ReasoningEffort.LOW, 512), |
| (ReasoningEffort.MEDIUM, 1_024), |
| (ReasoningEffort.HIGH, 2_048), |
| (ReasoningEffort.XHIGH, 4_096), |
| (ReasoningEffort.MAX, 8_192), |
| ), |
| ) |
| def test_reasoning_effort_has_one_fcc_numeric_budget( |
| effort: ReasoningEffort, expected: int |
| ) -> None: |
| assert ReasoningPolicy.on(effort=effort).numeric_budget_tokens == expected |
|
|
|
|
| def test_exact_reasoning_budget_takes_precedence_over_effort_mapping() -> None: |
| policy = ReasoningPolicy.on( |
| effort=ReasoningEffort.XHIGH, |
| budget_tokens=777, |
| ) |
|
|
| assert policy.numeric_budget_tokens == 777 |
|
|
|
|
| @pytest.mark.parametrize( |
| "policy", |
| ( |
| ReasoningPolicy.provider_default(), |
| ReasoningPolicy.off(), |
| ReasoningPolicy.on(), |
| ), |
| ) |
| def test_reasoning_without_numeric_intensity_has_no_budget( |
| policy: ReasoningPolicy, |
| ) -> None: |
| assert policy.numeric_budget_tokens is None |
|
|