perf(agent): per-request session effort override skips the runtime rebuild / 思考强度切换走每请求覆盖免重建 - #9866
Open
Linearl wants to merge 1 commit into
Open
perf(agent): per-request session effort override skips the runtime rebuild / 思考强度切换走每请求覆盖免重建#9866Linearl wants to merge 1 commit into
Linearl wants to merge 1 commit into
Conversation
…build Switching the reasoning effort currently pays for the full desktop build+swap rebuild (session snapshot, lease re-arm, provider re-init, history carry) even though providers already resolve effort per request: provider.Request.EffortOverride exists and the openai adapter resolves it against the endpoint's effort vocabulary on every call. Wire that existing channel to the UI switch. Agent.SetSessionEffortOverride stores a session-scoped override (atomic.Value, following the responseLanguage precedent) and accepts a level only when the running provider lists it in its per-request vocabulary (new optional probe PerRequestEfforts). Sampling requests merge it behind the governor's engaged override so a running guard cannot be outbid by a session-level depth bump. The desktop effort switch tries the fast path first and falls back to the rebuild for providers or sessions that decline it — recovery-forked sessions decline on purpose so their reanchor semantics (forked file sealed, fresh branch anchored) stay on the rebuild path. Frontends whose provider lacks per-request depth see no behavior change; the fast path degrades to the exact rebuild flow they use today.
There was a problem hiding this comment.
🟡 Changes recommended
The new fast path misses state persistence and there are a couple of correctness/safety gaps (recovery-meta error handling and slice exposure) that should be addressed before approval.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
This PR introduces a session-scoped reasoning-effort override that can be applied per request (via provider.Request.EffortOverride) so effort switches can skip the expensive runtime rebuild when the active provider supports request-scoped effort vocabularies.
Changes:
- Add
Agent.SetSessionEffortOverrideand request-time selection logic (effortOverrideForRequest) so the governor override still takes priority. - Expose provider capability via
PerRequestEfforts()(OpenAI adapter) and plumb an override setter throughcontrol.Controller. - Add a desktop fast path in
SetEffortForTabthat attempts the no-rebuild override before falling back to the existing build+swap path; add agent tests for vocab gating and governor priority.
File summaries
| File | Description |
|---|---|
| internal/provider/openai/effort.go | Exposes OpenAI endpoint request-scoped effort vocabulary via PerRequestEfforts(). |
| internal/control/controller.go | Adds Controller.SetSessionEffortOverride to forward session overrides to the executor agent. |
| internal/agent/sampling_request.go | Switches sampling requests to use effortOverrideForRequest() instead of governor-only overrides. |
| internal/agent/effort_override.go | Implements session-scoped override storage, provider vocab gating, recovery-branch rejection, and governor priority. |
| internal/agent/effort_override_test.go | Adds tests for vocab gating, non-varying provider rejection, and governor priority. |
| internal/agent/agent.go | Adds the sessionEffort field to the agent runtime state. |
| desktop/app.go | Adds a per-request fast path for effort switching (no rebuild) with fallback to existing rebuild flow. |
Review details
- Files reviewed: 7/7 changed files
- Comments generated: 3
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+9880
to
+9883
| a.mu.Lock() | ||
| tab.effort = &effort | ||
| a.mu.Unlock() | ||
| return nil |
Comment on lines
+44
to
+48
| if path := strings.TrimSpace(a.sess.path); path != "" { | ||
| if meta, ok, err := LoadBranchMeta(path); err == nil && ok && meta.Recovered { | ||
| return false | ||
| } | ||
| } |
Comment on lines
+114
to
+119
| // PerRequestEfforts reports the depth levels a request-scoped EffortOverride | ||
| // may take on this endpoint (see requestEffort). Nil or empty means the | ||
| // endpoint cannot vary depth per request, so frontends that fix effort at | ||
| // boot must keep using the configured depth (or rebuild the runtime) instead | ||
| // of writing a per-request override that would silently degrade on the wire. | ||
| func (c *client) PerRequestEfforts() []string { return c.requestEfforts } |
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.
Summary
Agent.SetSessionEffortOverridestores a session-scoped override and every sampling request carries it via the existingprovider.Request.EffortOverridechannel, so the switch costs milliseconds instead of a full build+swap (session snapshot, lease re-arm, provider re-init, history carry) — the multi-second stall users see in the effort menu.effortOverrideForRequest), so a running guard cannot be outbid by a session-level depth bump.PerRequestEffortsprobe / empty vocabulary) decline the override and the desktop switch falls back to today's exact rebuild path; recovery-forked sessions also decline on purpose, so their reanchor semantics (forked file sealed, fresh branch anchored) stay on the rebuild path.Issues-Fixes
None.
Verification
go build ./...(root) andgo build ./...(desktop module) — clean.go test ./internal/agent -run "SessionEffortOverride|EffortOverrideForRequest|Governor|Sampling" -count=1— ok. New tests: vocabulary gate (listed level accepted, out-of-vocabulary rejected, clear always succeeds), non-varying provider rejection, governor-beats-session priority.go test ./internal/provider/openai -run Effort -count=1— ok.go test ./internal/control -run "Session|Controller" -count=1— ok (controller forwards to the agent; nil executor declines).go test . -run Effort -count=1(desktop) — ok, includingTestSetEffortForTabReanchorsDepthCapRecoveryBranch: a recovery-forked session declines the fast path and keeps today's rebuild + reanchor behavior.Documentation-impact
None — no config keys, CLI flags, or docs surface changes. The effort switch behaves the same from the user's perspective (faster).
Cache-impact
None — the prompt bytes never change:
EffortOverrideonly selects the reasoning-depth fields on the wire (reasoning_effort/ thinking), and the openai adapter already resolved per-request overrides this way (seerequestEffort,session_title/vision_summaryper-requestEffortOverride: "low"). Distinct requests after a switch were already cache-compatible in those paths.Cache-guard
Not applicable — no system prompt, tool schema, or message-history changes; the override travels on the request object only.
System-prompt-review
None — no system prompt changes; effort affects reasoning-depth wire fields only.
Notes