feat(advisor) 3/4: wire activation into the query layer - #178
Merged
ericleepi314 merged 1 commit intoMay 19, 2026
Merged
Conversation
Stacked on #PR2. With helpers + provider preservation in place, this
is the actual server-side parity wiring — same beta header, same
schema, same instructions, same cache-preserving order as TS
typescript/src/services/api/claude.ts.
- `src/state/app_state.py`: adds `advisor_model: str | None = None`
field + `_on_advisor_model_change` handler. The handler persists
to `settings.advisor_model` via the shared default ConfigManager
AND invalidates the settings cache, so mid-session toggles via the
reactive store reach the next API call without a process restart.
Registered in `_FIELD_HANDLERS` to satisfy the coverage contract.
- `src/query/query.py`: `_call_model_sync` now:
1. Computes `advisor_active` via a single try/except wrapper:
`is_advisor_enabled(provider)` (first-party Anthropic AND env
not disabled) AND `get_settings().advisor_model` non-empty AND
`model_supports_advisor(provider.model)` AND
`is_valid_advisor_model(candidate)`. Any exception → inactive.
2. When inactive, strips historical advisor blocks via
`strip_advisor_blocks` (the API 400s on advisor blocks without
the beta header).
3. When active, appends the advisor schema AFTER the cached tool
list (preserves the `cache_control` marker position), sets
`betas=[ADVISOR_BETA_HEADER]`, and appends
`ADVISOR_TOOL_INSTRUCTIONS` to the system prompt's tail. The
append order mirrors TS claude.ts:1395, 1411-1421 — toggling
/advisor doesn't bust the cache prefix.
4. After the response, preserves `raw_content_blocks` (from PR2)
into `assistant_blocks` so the pair survives history replay.
A stale `advisor_model` setting under a non-supporting base model is
silently ignored — never sent to the API. Mirrors the TS "skipping
advisor — base model X does not support advisor" branch.
## Test plan
- [x] `tests/test_advisor_request_wiring.py` (12 tests): full
activation matrix on a first-party Anthropic provider, plus
defensive activation (synthetic exception in the gate → turn
proceeds without advisor). Verifies the beta header attaches,
schema appends AFTER regular tools, instructions append to
both string and block-list system prompts, advisor blocks
survive in history when active. Negative cases: env disabled,
custom base_url, unsupported base model, invalid advisor
model, settings unset — none send the schema/header.
- [x] `tests/integration/test_advisor_smoke.py` (3 tests): mocked
Anthropic stream produces text + server_tool_use(advisor) +
advisor_tool_result + text — verify pair preserved across
turns. Interrupt scenario: orphan use stripped on next-turn
replay even when beta IS active (orphan strip is unconditional
because the API rejects orphans regardless of header).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
7 tasks
ericleepi314
added a commit
that referenced
this pull request
Jul 7, 2026
feat(advisor) 3/4: wire activation into the query layer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
3/4 in the
/advisorstack. Stacked on PR 2.Summary
With helpers + provider preservation in place, this is the actual server-side parity wiring — same beta header, same schema, same instructions, same cache-preserving order as
typescript/src/services/api/claude.ts.src/state/app_state.py— addsadvisor_model: str | None = None+_on_advisor_model_changehandler. The handler persists tosettings.advisor_modelvia the shared defaultConfigManagerAND invalidates the settings cache, so mid-session toggles via the reactive store reach the next API call without a process restart. Registered in_FIELD_HANDLERSto satisfy the coverage contract.src/query/query.py—_call_model_syncnow:advisor_activevia a single try/except wrapper: first-party Anthropic + env not disabled +settings.advisor_modelset +model_supports_advisor(provider.model)+is_valid_advisor_model(candidate). Any exception → inactive (caught + logged).strip_advisor_blocks(API 400s on advisor blocks without the beta header).cache_controlmarker position), setsbetas=[ADVISOR_BETA_HEADER], and appendsADVISOR_TOOL_INSTRUCTIONSto the system prompt's tail. The append order mirrors TSclaude.ts:1395, 1411-1421— toggling/advisordoesn't bust the cache prefix.raw_content_blocks(from PR 2) intoassistant_blocksso the pair survives history replay.A stale
advisor_modelsetting under a non-supporting base model is silently ignored — never sent to the API. Mirrors the TS "skipping advisor — base model X does not support advisor" branch.Stack
Test plan
tests/test_advisor_request_wiring.py— 12 tests: full activation matrix on first-party Anthropic + defensive activation (synthetic exception in the gate → turn proceeds without advisor). Verifies the beta header attaches, schema appends AFTER regular tools, instructions append to both string and block-list system prompts, advisor blocks survive in history when active. Negative: env disabled, custom base_url, unsupported base model, invalid advisor model, settings unset.tests/integration/test_advisor_smoke.py— 3 tests: mocked Anthropic stream with the full advisor pair preserved across turns. Interrupt scenario: orphan use stripped on next-turn replay even when beta IS active.🤖 Generated with Claude Code