🤖 feat: add per-model minimum thinking level#3427
Conversation
Add a per-model "Minimum Thinking level" setting configured on the Models settings page. It defaults to Medium for reasoning-capable models, hiding off/low so the thinking slider toggle is more efficient. Users can override the floor per model (including back down to off/low). - policy.ts: getDefaultMinimumThinkingLevel / resolveMinimumThinkingLevel / getAvailableThinkingLevels; enforceThinkingPolicy gains an optional minimum. - config: minThinkingLevelByModel map (schema, Config load/save, IPC getConfig output + updateMinThinkingLevels mutation). - frontend: useMinThinkingLevels hook floors the ThinkingSlider, keybind cycle, and command-palette thinking picker. - backend: agentSession send path floors the effective thinking level so all clients honor the per-model minimum. - Models page: per-model Min Thinking dropdown (ModelRow + ModelsSection).
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bbc1b182e5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Address Codex P1: the shared fallback thinking policy is used by non-reasoning models (gpt-4o, claude-3.5), so defaulting them to a medium floor would send unsupported reasoning params. Split the policy table into explicit rules vs the shared default; only explicitly-recognized reasoning models default to medium and expose the Models-page floor selector — others keep the legacy off behavior. Also simplify the Models-page picker per review: drop the "Default (…)" option and show the effective floor as a plain level, and hide the selector entirely for non-reasoning / unrecognized / single-level models.
|
@codex Addressed the P1 in Root cause was real: the shared fallback policy is used by non-reasoning models, so defaulting them to a Fix: split |
|
@codex review |
This comment has been minimized.
This comment has been minimized.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7ac57fb174
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- useMinThinkingLevels now reads an optional API context (new useOptionalAPI), so ThinkingProvider no longer crashes when mounted outside an APIProvider (e.g. the isolated useDraftWorkspaceSettings test harness); it degrades to default floors when no api is available. - settings.spec.ts: scope the add-form combobox assertion, since the model tables now render per-model "Min Thinking" comboboxes.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: c545e8b686
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex Re: P2 "Detect thinking support through mapped models" (
So detecting capability via Fully honoring |
This comment has been minimized.
This comment has been minimized.
Mobile clients previously listed raw capability levels and sent enforceThinkingPolicy(model, level) without the floor, so a default-floored model (e.g. gpt-5.5 → medium) let mobile users pick Off/Low that the backend silently clamped up — the UI lied. - New mobile useMinThinkingLevels hook fetches minThinkingLevelByModel from api.config.getConfig (react-query, best-effort; falls back to default floor). - WorkspaceScreen threads the resolved floor through all enforceThinkingPolicy calls and passes it to RunSettingsSheet. - RunSettingsSheet lists getAvailableThinkingLevels(model, floor) so the picker hides levels below the floor, matching desktop + backend.
|
@codex Fixed the mobile floor sync in
|
This comment has been minimized.
This comment has been minimized.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2f3d259435
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
If updateMinThinkingLevels fails, re-fetch config so the UI reverts to the backend's actual floor instead of showing an override the send path never applied. The optimistic update itself mirrors the existing routeOverrides pattern; the onConfigChanged subscription reconciles on success.
|
@codex Addressed in
|
This comment has been minimized.
This comment has been minimized.
|
@codex review |
|
Codex Review: Didn't find any major issues. Keep it up! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Summary
Adds a per-model Minimum Thinking level setting, configured on the Models settings page. It defaults to Medium for reasoning-capable models, hiding
off/lowso the thinking slider toggle cycles only through levels people actually use. Users can override the floor per model (including lowering it back tooff/low).Background
Most users never want
off/lowthinking. Showing those levels on the thinking slider makes cycling less efficient and surfaces choices that are rarely desirable. A per-model minimum lets us default to a sensible floor (Medium) while keeping full control for power users.Implementation
The minimum is a floor applied on top of each model's existing capability policy — capability detection (
getThinkingPolicyForModel) is unchanged.common/utils/thinking/policy.tsgetDefaultMinimumThinkingLevel(model)→mediumfor reasoning-capable models, elseoff.resolveMinimumThinkingLevel(model, override?)→override ?? default.getAvailableThinkingLevels(model, minimum?)→ capability filtered to levels ≥ floor (by canonical ordering), with a non-empty invariant (locks to the highest level if the floor exceeds the model's max).enforceThinkingPolicy(model, requested, minimum?)gains an optional floor; omitting it preserves the legacy capability-only behavior (so advisor/compaction/tasks/ACP are unaffected).minThinkingLevelByModel, keyed by canonical model id): schema (appConfigOnDisk,ProjectsConfig),Configload/save normalization (self-healing — invalid entries are dropped on load), and IPC (config.getConfigoutput + newconfig.updateMinThinkingLevelsmutation).useMinThinkingLevels(mirrorsuseRouting) floors the ThinkingSlider, the keybind cycle (ThinkingContext), and the command-palette "Set Thinking Effort" picker.agentSessionsend path applies the floor to the effective thinking level, so all clients (desktop, mobile, ACP) honor the per-model minimum even though the persisted level stays untouched.ModelRow+ModelsSection) listing the model's full capability plus aDefault (…)option that resolves the built-in floor (e.g.Default (medium), orDefault (high)for gemini-3).The floor is enforced at the provider boundary (backend) and mirrored in the UI, so a stored/below-floor
offdisplays and runs asmediumwithout mutating the saved value — set a model's minimum tooffto restore the old behavior.Validation
policy.test.tscases: default floor, ordering-based clamp (gemini-3["low","high"]+ medium →["high"]), non-empty invariant, below-floor clamp-up, and legacy no-floor compatibility.ThinkingContext.test.tsxto reflect floored cycling (off→ flooredmedium→high).make typecheckandmake static-checkpass; full unit suite green for touched areas (remaining unrelated failures are pre-existing parallel-run flakes that pass in isolation).Risks
offwill think atmediumuntil a user lowers that model's minimum. This is the intended default. Capability detection and the advisor/tasks/compaction/ACP thinking paths are untouched (they callenforceThinkingPolicywithout a floor).Generated with
mux• Model:anthropic:claude-opus-4-8• Thinking:xhigh• Cost:$n/a