| """Resolve client reasoning input and FCC configuration exactly once.""" |
|
|
| from collections.abc import Mapping |
| from typing import Any |
|
|
| from free_claude_code.config.reasoning import ReasoningPreference |
| from free_claude_code.core.anthropic.models import MessagesRequest, ThinkingConfig |
| from free_claude_code.core.reasoning import ( |
| ReasoningControl, |
| ReasoningEffort, |
| ReasoningPolicy, |
| ) |
|
|
|
|
| def resolve_reasoning_policy( |
| request: MessagesRequest, |
| preference: ReasoningPreference, |
| ) -> ReasoningPolicy: |
| """Apply one resolved configuration preference to the client request.""" |
|
|
| if preference is ReasoningPreference.INHERIT: |
| raise ValueError("Reasoning preference must be resolved before application.") |
| if preference is ReasoningPreference.OFF: |
| return ReasoningPolicy.off() |
| if preference is not ReasoningPreference.CLIENT: |
| return ReasoningPolicy.on(effort=ReasoningEffort(preference.value)) |
| return client_reasoning_policy(request) |
|
|
|
|
| def client_reasoning_policy(request: MessagesRequest) -> ReasoningPolicy: |
| """Return the lossless reasoning intent expressed by one client request.""" |
|
|
| budget_tokens = _positive_budget(request.thinking) |
| thinking_control = _thinking_control( |
| request.thinking, |
| budget_tokens=budget_tokens, |
| ) |
| effort, effort_disables = _output_effort(request.output_config) |
|
|
| if effort_disables: |
| return ReasoningPolicy.off() |
| if thinking_control is ReasoningControl.OFF: |
| return ReasoningPolicy( |
| control=ReasoningControl.OFF, |
| effort=effort, |
| ) |
| if thinking_control is ReasoningControl.ON or budget_tokens is not None: |
| return ReasoningPolicy.on( |
| effort=effort, |
| budget_tokens=budget_tokens, |
| ) |
| return ReasoningPolicy( |
| control=ReasoningControl.DEFAULT, |
| effort=effort, |
| ) |
|
|
|
|
| def _thinking_control( |
| thinking: ThinkingConfig | None, |
| *, |
| budget_tokens: int | None, |
| ) -> ReasoningControl: |
| if thinking is None: |
| return ReasoningControl.DEFAULT |
| if thinking.type == "disabled" or ( |
| "enabled" in thinking.model_fields_set and thinking.enabled is False |
| ): |
| return ReasoningControl.OFF |
| if ( |
| thinking.type in {"adaptive", "enabled"} |
| or ("enabled" in thinking.model_fields_set and thinking.enabled is True) |
| or budget_tokens is not None |
| ): |
| return ReasoningControl.ON |
| return ReasoningControl.DEFAULT |
|
|
|
|
| def _output_effort(value: Any) -> tuple[ReasoningEffort | None, bool]: |
| if not isinstance(value, Mapping): |
| return None, False |
| raw = value.get("effort") |
| if not isinstance(raw, str): |
| return None, False |
| normalized = raw.strip().lower() |
| if normalized == "none": |
| return None, True |
| try: |
| return ReasoningEffort(normalized), False |
| except ValueError: |
| return None, False |
|
|
|
|
| def _positive_budget(thinking: ThinkingConfig | None) -> int | None: |
| if thinking is None: |
| return None |
| value = thinking.budget_tokens |
| if isinstance(value, int) and not isinstance(value, bool) and value > 0: |
| return value |
| return None |
|
|