| 493 | |
| 494 | |
| 495 | async def _call_model_sync( |
| 496 | *, |
| 497 | provider: BaseProvider, |
| 498 | messages: list[Message], |
| 499 | system_prompt: str, |
| 500 | tools: Tools, |
| 501 | max_output_tokens_override: int | None = None, |
| 502 | abort_signal: Any = None, |
| 503 | on_text_chunk: Callable[[str], None] | None = None, |
| 504 | on_thinking_chunk: Callable[[str], None] | None = None, |
| 505 | extended_thinking: bool | None = None, |
| 506 | thinking_effort: str = "medium", |
| 507 | sdk_max_retries: int | None = None, |
| 508 | ) -> tuple[list[AssistantMessage], list[ToolUseBlock]]: |
| 509 | from ..types.messages import normalize_messages_for_api |
| 510 | from ..utils.advisor import ( |
| 511 | ADVISOR_BETA_HEADER, |
| 512 | ADVISOR_MODE_CLIENT_SIDE, |
| 513 | ADVISOR_MODE_INACTIVE, |
| 514 | ADVISOR_MODE_SERVER_SIDE, |
| 515 | ADVISOR_TOOL_INSTRUCTIONS, |
| 516 | build_advisor_tool_schema, |
| 517 | build_client_advisor_tool_schema, |
| 518 | decide_advisor_mode, |
| 519 | strip_advisor_blocks, |
| 520 | ) |
| 521 | |
| 522 | # Advisor activation decision. Three outcomes: |
| 523 | # |
| 524 | # * SERVER_SIDE: 1P Anthropic provider + opus-4-6/sonnet-4-6 main |
| 525 | # loop + valid server-side advisor target. Carries the beta |
| 526 | # header and the ``advisor_20260301`` schema; the API runs the |
| 527 | # reviewer model server-side. Optimal path — single roundtrip, |
| 528 | # cache-friendly. |
| 529 | # * CLIENT_SIDE: any provider, any tool-calling main loop, any |
| 530 | # advisor model that routes to a known provider. Registers |
| 531 | # ``advisor`` as a regular client-side tool; the dispatcher |
| 532 | # (``src/tool_system/tools/advisor.py``) makes a separate API |
| 533 | # call to the configured advisor model. Two roundtrips but |
| 534 | # provider-agnostic. |
| 535 | # * INACTIVE: no advisor on this request (env-disabled, no |
| 536 | # advisor_model set, or no path can be resolved). |
| 537 | # |
| 538 | # The full decision table lives in ``decide_advisor_mode``. Any |
| 539 | # exception during the predicate degrades to INACTIVE rather than |
| 540 | # killing the turn (critic M1 from the original advisor PR). |
| 541 | main_loop_model = getattr(provider, "model", "") or "" |
| 542 | advisor_mode = ADVISOR_MODE_INACTIVE |
| 543 | advisor_model_normalized: str | None = None |
| 544 | try: |
| 545 | from ..settings.settings import get_settings |
| 546 | settings = get_settings() |
| 547 | configured = (getattr(settings, "advisor_model", "") or "").strip() |
| 548 | configured_provider = (getattr(settings, "advisor_provider", "") or "").strip() |
| 549 | force_client = bool(getattr(settings, "advisor_client_mode", False)) |
| 550 | # Master switch (default False): the advisor stays inactive unless the |
| 551 | # user opted in via `advisor_enabled` in ~/.clawcodex/config.json. |
| 552 | advisor_enabled = bool(getattr(settings, "advisor_enabled", False)) |