Repository navigation
feat(agent): xhigh and max reasoning-effort rungs where the model's provider takes them - #1123
Handsome-wzw wants to merge 16 commits into
Conversation
…session.set_effort) One conversation can now ask for more or less thinking than the configured default without moving that default. The pick rides the session's metadata beside `model` (`raven.agent.session_effort`), so it outlives the gateway and a resumed conversation runs at what it was left at; the turn entrance hands it to `_run_agent_loop`, which ranks it over the mode's effort and the configured `agents.defaults.reasoningEffort`, under a hook's rollback override. Raven's own calls only: sub-agents keep the configured default. Surfaces: `session.set_effort` (report, set or clear; every answer names what the default is on this install and the words), `session.info` carries `reasoning_effort` for the status bar, the WebUI's model picker gains an effort row under the tier row, and the TUI gains `/effort`. The settings page drew an unset `agents.defaults.reasoningEffort` as Low; the unset state is now its own rung, Provider default, and the key accepts null so that rung clears it. The using-raven guide documents both levels. Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
…g_effort # Conflicts: # CHANGELOG.md
… left A switch asks for the conversation being opened before the page's current session moves over to it, and the effort read built its request from "the current session": opening B from A asked for A's effort and painted it on B -- and a pick of that very rung on B was taken as already in force. The read now names the conversation and the switch it belongs to, the way the permission-mode read does, and the source drops an answer whose switch has been overtaken rather than painting a view the reader has left. Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
…rovider takes them The words a session may pick now come from the provider of the session's model rather than from one global tuple: LLMProvider.reasoning_efforts answers the four every wire takes, the Anthropic Messages adapter answers its _ADAPTIVE_EFFORTS table (xhigh and max over high), and the lazy, per-model, resolving and rotor wrappers forward it the way they forward reasoning_wire_keys. session.set_effort answers options from the session's own binding and refuses a word the provider does not take, naming the model and the words it takes. The console checker for agents.defaults.reasoningEffort stays at the four on purpose: that key reaches every model call on every vendor and every sub-agent. The record may carry any rung this raven knows (REASONING_EFFORT_RUNGS, the retry descent's ladder read upward), because the model can move after the pick: the turn asks its provider again (session_effort.fit) and runs a pick the new wire does not take at the highest rung it does take, logged, rather than at a request the wire would refuse; a mode's effort goes out as named. Surfaces: the picker row names the two rungs (en + zh), wraps whole words when seven do not fit one line, re-reads its words after a model switch and draws a pick the words no longer carry after them, ticked; the TUI's /effort help and usage list the six words; the offline library offers six for its Claude; the using-raven guide (en + zh), CHANGELOG and CONTEXT.md say the above; openrpc.json describes effort and options as the model's words, and both generated contracts are regenerated. Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
# Conflicts: # CHANGELOG.md
…nto feat/effort_xhigh_max # Conflicts: # ui-web/src/features/model/source.test.ts
|
Verification on the pushed head (merged with
|
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
Reviewed github/main...HEAD at 35489d530c28, including the repository rules and canonical Session Reasoning Effort term, the full diff, provider and loop callers, RPC/schema/generated-client boundaries, WebUI/TUI state flows, relevant history, backward compatibility with older providers/gateways, and the test changes. I specifically tried to refute the model-switch fitting behavior, provider-wrapper routing, draft staging, stale async reads, persisted session precedence, and the claim that tests were not weakened; I found no concrete failure.
Verification:
uv run pytest tests/test_session_effort.py tests/test_provider_protocol.py tests/test_rpc_session.py tests/test_agent_loop_empty_recovery.py tests/test_agent_loop_usage_sink.py tests/test_rpc_session_init_bundle.py tests/test_rpc_settings.py -q: 595 passed.uv run pytest tests/test_rpc_schema_match.py tests/test_l4_entrances.py tests/test_source_language_check.py -q: 559 passed.- WebUI focused Vitest run: 221 passed; TUI focused Vitest run: 12 passed.
- WebUI generated RPC check and type check passed. TUI generated RPC/i18n checks and type check passed.
- Source-language, large-file, diff-whitespace, and generated-file cleanliness checks passed.
|
Windows compatibility review — PR #1123 @ Scope: full base Result: no Windows-specific issue.
Verification (native Windows 11, CPython 3.12.13, isolated worktree Limitations: TUI tests not run (harness unavailable in this checkout); no live model provider was called, so the |
|
Merge conflict: @Handsome-wzw this PR no longer merges cleanly into Measured just now with After a rebase onto current main I re-check what moved before the grade comes back. |
|
Blocking: resolve the CHANGELOG.md conflict against current GitHub main before merge. Confirmed independently at head |
# Conflicts: # CHANGELOG.md
…g_effort # Conflicts: # CHANGELOG.md
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
Reviewed the delta from the previously reviewed head 35489d530c28 to d2e73da75f01. The new commit merges current GitHub main; the only hand-resolved conflict was CHANGELOG.md, and the resolution preserves both effort entries and the upstream entries. The PR diff against its new merge base remains the expected 55-file effort change, git merge-tree --write-tree HEAD github/main succeeds, and no effort implementation or test was weakened by the refresh. I rechecked the repository/domain rules, merge history and resolution, overlapping runtime/RPC/provider/frontend callers, generated contracts, backward-compatible surfaces, architecture checks, and test changes.
Verification on this head:
- Focused Python runtime/RPC/provider/schema/layer/source-language suite: 1,169 passed.
- Focused WebUI suite: 221 passed; generated RPC check and type check passed.
- Focused TUI suite: 12 passed; generated RPC/i18n checks and type check passed.
- PR diff whitespace and merge-tree checks passed.
|
Windows compatibility re-review — PR #1123 @ Scope: the head moved from the previously reviewed Result: no Windows-specific issue; the previous conclusion holds. Verification (native Windows 11, isolated worktree The provider additions remain capability metadata only ( Limitations: TUI tests not run; no live model provider was called. |
There was a problem hiding this comment.
Blocking: carried from #1113. This PR targets main and contains #1113's earlier head 516c7e7d (merged in through 35489d5), so merging it as it stands merges #1113's open defect too: the Provider default rung clears agents.defaults.reasoningEffort, but a running gateway keeps sending the effort it booted with while session.info / session.set_effort report the clear (thread on #1113, anchored to raven/agent/loop/turn_path.py; the judge reproduced it there).
This PR's own commit a23029c0 reads fine on its own:
LLMProvider.reasoning_effortsdefaults to the four words;anthropic_messagesanswers its_ADAPTIVE_EFFORTSkeys (low to max).- The lazy, per-model, resolving and rotor wrappers forward it the way they forward
reasoning_wire_keys. _effort_wordsfalls back to the four when the provider cannot answer.session_effort.fitruns a pick the new wire does not take at the highest rung it does take, and logs it.effective_modelis bound before the call atturn_path.py:1003.- The console key stays at the four on purpose.
This clears once #1113's fix lands and this branch is rebased onto it, so its diff against main carries the fixed code. Read only; local tests not run.
There was a problem hiding this comment.
Blocking: merge the #1113 clear fix and resolve the current CHANGELOG.md conflict before merge.
Confirmed on this head: gen_overrides still omits reasoning_effort when both the session pick/mode and live configured default are null, so the provider can reuse its frozen boot-time word while the RPC surfaces report the provider default. The current #1113 branch fixes this in 5057b49f by resolving the effort once and always sending None explicitly; that fix is absent here. The affected existing suite passes (uv run pytest tests/test_agent_loop_usage_sink.py tests/test_rpc_session.py tests/test_rpc_settings.py -q: 407 passed), consistent with the transition lacking coverage on this head. I am confirming the facts, not re-grading or resolving the blocker opened here.
There was a problem hiding this comment.
Merged rather than rebased (branches here merge, never rebase): 754ff3160 brings in #1113's head 01f245518, which holds the clear fix 5057b49f0, so this PR's diff against main now carries the fixed turn -- session_reasoning_effort ranks pick > mode's word > configured default and the call sends it every time, None included. The pick fitting from a23029c0 sits in front of it: turn_effort = self.session_reasoning_effort(session_key, _fit_reasoning_effort(...)) in raven/agent/loop/turn_path.py. Head c325201e2; the verification comment on the PR has the local counts.
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
Confirmed on c325201e2d6a: #1113 fix 5057b49f0 is now in the ancestry, the conflict resolution fits the session pick before session_reasoning_effort ranks it, and gen_overrides always carries the resulting value, including explicit None. The new clear-transition coverage passes. Current GitHub main is also in the ancestry and git merge-tree --write-tree HEAD github/main succeeds. My prior blocker is withdrawn; I am leaving resolution of this thread to its opener.
…xt turn Picking Provider default clears agents.defaults.reasoningEffort, and session.info / session.set_effort reported the clear, but a gateway that booted while the key held a word kept calling at that word: the turn left reasoning_effort out of the call when the live reader answered None, and the provider filled the missing argument from its own copy of the key, frozen at construction (ResolvingProvider). The display and the wire disagreed in exactly the way the per-conversation effort set out to end. The turn now sends the effort in force on every call, None included: an explicit None reaches the provider as None (its own default, nothing on the wire) instead of the boot copy, so a clear lands on the next turn the way a word change already did. The exhaustion wrap-up and the watch-work judgement send it the same way. The ranking has one home, session_effort.in_force (pick > mode's word > configured default, None one answer with one meaning), which the turn runs at through session_reasoning_effort on the loop and the RPC reports from, the way session_tier is shared; the two used to spell it out apart and to read an absent word differently. Tests: a real provider booted at high with the live key cleared records None on the next turn (low as the control), the three cases that pinned "the key is left out" now pin "the key is sent as None", and in_force has its own unit tests. CONTEXT and the CHANGELOG entry say the rule. Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
|
Merge conflict: @Handsome-wzw this PR no longer merges cleanly into Measured just now with After a rebase onto current main I re-check what moved before the grade comes back. |
|
Blocking: merge the #1113 clear fix and resolve the current CHANGELOG.md conflict before merge. The second condition also reproduces independently: after fetching GitHub |
…nto feat/effort_xhigh_max # Conflicts: # CONTEXT.md # raven/agent/loop/turn_path.py # raven/rpc/methods/session.py
|
Merged on top of the previous head
Local checks on
|
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
Reviewed the delta from d2e73da75f01 to c325201e2d6a, including the repository and domain rules, merge history and three hand-resolved overlaps, affected loop/RPC/provider callers, backward-compatible None semantics, architecture boundaries, and whether tests were weakened. The prior clear defect is fixed: provider-aware fitting feeds the shared in-force ranking, and every model call receives the resolved effort, including explicit None. Current GitHub main is in the ancestry and the merge-tree check is clean. I found no new concrete failure.
Verification on this head:
- Focused session-effort, loop, provider, RPC, and import-boundary suite: 533 passed.
- WebUI generated RPC check and type check passed.
- TUI generated RPC/i18n checks and type check passed.
- Source-language, large-file, diff-whitespace, and merge-tree checks passed.
|
Windows compatibility re-review — PR #1123 @ Scope: the head moved from the previously reviewed Result: no Windows-specific issue; the previous conclusion holds. Verification (native Windows 11, isolated worktree The provider additions remain capability metadata ( Limitations: TUI tests not run; no live model provider was called. |
…ords Inside a direct chat with a sub-agent instance, /effort sent the main conversation's session_key and then said "this conversation" in the instance's pane: the main record moved while the reader was told the instance's had. The pick is the main conversation's and an instance has none of its own to set from here, so the command is refused there, naming the instance and the way back, the way /instance does. The reset words are /mode's RESET_WORDS now rather than a second copy of the same three. Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
…omotion's ticket A draft's effort pick was staged before the session.set_effort read that answers the default and the words, and left staged when that read failed: the toast said the pick was not made and the old rung was repainted, but the first send's applyStagedEffort ran the turn at it. The pick is still staged first, so a send during the round trip carries it, and a failed read puts back the last pick whose read landed -- not merely the value staged before the failing call, which is another in-flight pick when two went out and both failed. The re-read after the promotion wrote the staged effort took the view's ticket after the awaited write, so a reader who opened another conversation while the write was in flight got the minted session's rung painted over that conversation's own read. The promotion's ticket now rides into applyStagedEffort the way it rides into applyStagedModel, and the source drops the late answer. The tier row's tooltip (gui.tier.scope) said reasoning depth follows the tier, one row above a row titled with the same words; the shipped tiers leave the effort alone, so the tooltip now names sub-agents only. The TUI catalogue is regenerated from it. Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
…arry the pick on a fork The exhaustion wrap-up and the watch-work judgement forward the effort in force, explicit None included, but nothing failed when either call site went back to the policy's word or the wrap-up helper left the key out on None. Two tests now drive each side call to a provider booted at high: the configured default where the policy names none, the session's own pick, and a cleared key that must arrive as None rather than be filled from the boot copy. The watch-work case is pinned at the provider, through chat_stream, with the spawn roster naming an on-call agent so the look earns the judgement. The agent's raven_config door kept its own copy of the effort words and refused null for agents.defaults.reasoningEffort while the settings page accepted it. The setting reads REASONING_EFFORTS and is nullable, so the words have one owner and the agent can clear the key the way the page does. A fork carried the parent's model but dropped its effort pick, so a forked conversation ran at the default until resumed; FORK_CARRIES names what a fork carries, with the pick beside the model. session_effort.write's docstring promised a "pending" report that session.set_effort never makes; it now says what the answer is for. CONTEXT.md, the using-raven guide and the CHANGELOG entry carry the three user-visible facts. Co-authored-by: Claude (claude-fable-5-1) <noreply@anthropic.com>
…nto feat/effort_xhigh_max # Conflicts: # CONTEXT.md # docs-site/docs/using-raven.md # docs-site/docs/using-raven.zh.md # ui-tui/src/app/slash/commands/session.ts # ui-web/src/features/model/source.test.ts
|
Merged #1113's current head Five conflicts, none in Python; each was the two branches adding a sentence or a fake at the same place, kept together:
Local checks on
LivXue's thread on #1113 about the words being the model's is left as it is; nothing here answers it. |
gloryfromca
left a comment
There was a problem hiding this comment.
No blockers; this can merge as far as I am concerned.
Reviewed the delta from c325201, including all five conflict resolutions and the three newly merged #1113 fixes. I checked the repository rules and domain terms; the resulting diff and affected callers; the merge history and current main; backward compatibility; test changes for weakening; and the provider/session/UI architecture boundaries. The resolutions preserve the provider-specific xhigh/max behavior while correctly integrating draft recovery, promotion-ticket guarding, side-call effort pinning, config clearing, fork inheritance, and the TUI direct-chat guard.
Verification on this head: 953 affected Python tests passed; 133 focused Web tests passed; 12 focused TUI tests passed; Web and TUI type checks passed; generated Web RPC plus TUI RPC/i18n checks passed; git diff --check passed; current GitHub main is in the branch ancestry and git merge-tree --write-tree HEAD github/main succeeds. I found no new defect, compatibility break, weakened test, or unresolved conflict introduced by this merge.
The only open review thread was opened by another reviewer; its defect is fixed and my prior reply already withdrew my supporting objection, so I did not resolve someone else's thread.
|
Windows compatibility re-review — PR #1123 @ Scope: the head moved from the previously reviewed Result: no Windows-specific issue; the previous conclusion holds. The delta is browser/TUI state and config-catalogue logic, with no path, subprocess, encoding, or platform primitive. Verification (native Windows 11, isolated worktree Limitations: TUI tests not run; no live model provider was called. |
Summary
Two more reasoning-effort rungs,
xhighandmax, for a conversation whosemodel is served by the Anthropic Messages adapter -- the follow-up #1113 left
out on purpose.
Base: cut from
feat/session_reasoning_effort(#1113), which is not mergedyet, so this PR shows #1113's commits beside its own until #1113 lands. The
change of this PR is the one
feat(agent)commit; the merges after it bringin
origin/mainand #1113's later head (its merge with main and its fix thatreads the opened conversation's effort), so the diff against main is #1113
plus this.
Design, settled from the code:
model, not from one global tuple. New
LLMProvider.reasoning_efforts(model)(
raven/providers/base.py) answers the four every wire takes(
REASONING_EFFORTS);AnthropicMessagesProvideranswers the keys of its_ADAPTIVE_EFFORTStable, which is six (xhigh,maxoverhigh), oneanswer per wire rather than per model (a model that refuses a rung says so in
a 400 and
rewrite_on_400already turns the label into a budget there); theOpenAI-shaped adapters and litellm keep the four.
LazyProvider,PerModelProvider,ResolvingProviderandEndpointRotorProviderforward itthe way they forward
reasoning_wire_keys, pinned by name in a test.session.set_effortanswersoptionsfrom the provider of the session'sown binding (a draft gets the default model's), and refuses a word the
provider does not take with a sentence naming the model and the words it
takes. With no loop or a provider that cannot answer, the four stand.
agents.defaults.reasoningEffortstays at the four,on purpose: that key reaches every model call on every vendor and every
sub-agent (
RAVEN_PARENT_REASONING_EFFORT), so a rung only one wire takeswould be refused everywhere else with no per-session way out. Written on
REASONING_EFFORTSand at the checker. The settings page therefore keepsits four rungs.
REASONING_EFFORT_RUNGS(
config/schema.py) is the retry descent'sREASONING_EFFORT_LADDERreadupward, so a pick and a retry never disagree about which rung is higher, and
session_effort.ofreadsxhigh/maxback after a restart./model, the picker), so the turn asksits own provider again:
session_effort.fitruns a pick the new wire doesnot take at the highest rung it does take (
maxon a Claude, then a switchto a four-word model, runs at
high), logged, rather than at a request thewire would refuse or at the default the person moved away from. The pick
only: a mode's
reasoningEffortis the catalogue's word and goes out asnamed, as before. A provider that cannot answer leaves the pick as named,
and nothing on that path fails a turn.
lower_reasoning_effortalready descended frommax/xhigh; the tests nowsay so explicitly, on the label-only wire and on the Anthropic one.
Surfaces: the picker row names the two rungs (
gui.effort.xhigh/.max,en + zh) and, after a model switch, re-reads the row so the words are the new
model's; a pick the words no longer carry is drawn after them, ticked, so it
can be moved off rather than a row with no rung pressed. The TUI's
/efforthelp and usage list the six words and say where the top two apply. The offline
page library's conversation runs on a Claude, so its fixture offers six. The
using-raven guide (en + zh), CHANGELOG (Unreleased, its own bullet) and
CONTEXT.md's Session Reasoning Effort entry say the above;
openrpc.jsondescribes
effortandoptionsas the model's words, and both generatedcontracts were regenerated.
Type
Verification
pytest tests/test_session_effort.py tests/test_rpc_session.py tests/test_agent_loop_usage_sink.py tests/test_agent_loop_empty_recovery.py tests/test_provider_protocol.py tests/test_rpc_settings.py tests/test_rpc_console.py tests/test_import_cycle_budget.py tests/test_rpc_schema_match.py tests/test_rpc_registration.py tests/test_rpc_session_init_bundle.py-- passfull
pytest tests-- see the first comment for the count and the baseline failuresscripts/coverage_gate.py diff --base-ref origin/main-- see the first commentruff check/ruff format --check/ty checkon the touched modules,lint-imports-- cleanui-web:
npm run gen(contract regenerated),vitest run(all gates),type-check,lint-- cleanui-tui:
npm run gen:rpc,npm run gen:i18n,vitest run,type-check,lint-- cleanmkdocs build --strict-- cleanThe picker row was checked on the built page's offline library (
?stub=1) in light, dark and zh; the screenshots were shown to the owner before this PR was openedRelevant tests pass locally
Relevant lint / type checks pass locally
User-facing docs or screenshots are updated when needed
Risk
xhigh/maxis read as no pick by a build without this changeRelated Issues
Follows #1113.