🌐 US-Proxy
class="logged-out env-production page-responsive" style="word-wrap: break-word;" >
Skip to content

feat: add configurable reasoning effort to Coder agents - #26974

Merged
DanielleMaywood merged 4 commits into
mainfrom
dm/reasoning-effort-1-model-config
Jul 9, 2026
Merged

feat: add configurable reasoning effort to Coder agents#26974
DanielleMaywood merged 4 commits into
mainfrom
dm/reasoning-effort-1-model-config

Conversation

@DanielleMaywood

@DanielleMaywood DanielleMaywood commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Add configurable reasoning effort throughout Coder agents. Administrators can define a default and maximum effort for each model, users can select an effort per chat turn, and deployment or personal model overrides can request a specific effort.

Problem

Reasoning effort was stored as a fixed value in provider-specific model options. Offering the same model at different effort levels required duplicate model configs, chat turns could not select their own effort, and model overrides could not specify effort independently.

Changes

  • Define the canonical effort scale none < minimal < low < medium < high < xhigh < max and validate each model's top-level {default, max} configuration.
  • Add per-turn effort to chat create, send, edit, and queue flows. Chats and messages persist explicit selections, while an unset value falls back to the model default at generation time.
  • Resolve requested effort against the model maximum and translate it to provider-native options across normal generation, title generation, advisor, and subagent paths.
  • Add default and maximum effort fields to the model administration UI, plus a keyboard-accessible effort slider to chat creation and conversation flows.
  • Allow deployment and personal model overrides to select an effort, including title and subagent overrides.
  • Update generated API types, API documentation, database models, audit metadata, and chatd architecture documentation.

Migration note: Migration 000542_chat_reasoning_effort adds a database enum and nullable effort columns to chats, messages, and queued messages. It copies recognized legacy provider-specific values into the new top-level model configuration as both default and max, while retaining the legacy keys for reversibility. Invalid legacy values are not copied, and existing chat rows remain unset so runtime fallback uses the configured model default.

This PR consolidates the changes previously reviewed in #26975, #26976, #26977, and #27061.

🤖 Generated by Coder Agents on behalf of @DanielleMaywood

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown

Docs preview

📖 View docs preview for docs/admin/security/audit-logs.md

@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review

coder-agents-review Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Chat: Review posted | View chat
Requested: 2026-07-08 07:58 UTC by @DanielleMaywood
Spend: $102.97 / $100.00

Review history
  • R1 (2026-07-07), 1 Note, 1 P2, 2 P3, COMMENT. Review
  • R2 (2026-07-07): 19 reviewers, 3 Nit, 2 Note, 2 P2, 7 P3, 1 P4, COMMENT. Review
  • R3 (2026-07-08): 9 reviewers, 5 Nit, 3 Note, 3 P2, 8 P3, 1 P4, COMMENT. Review
  • R4 (2026-07-08): 4 reviewers, 6 Nit, 3 Note, 3 P2, 8 P3, 1 P4, APPROVE. Review

deep-review v0.9.0 | Round 4 | 07f4c9f..f677efc

Last posted: Round 4, 21 findings (3 P2, 8 P3, 1 P4, 6 Nit, 3 Note), APPROVE. Review

Finding inventory

Findings

# Sev Status Location Summary Round Reviewer Posted
CRF-1 P2 Author fixed (d39150f) codersdk/chats.go:1411 Doc comment claims mirroring behavior the code rejects R1 Netero Yes
CRF-2 P3 Author fixed (d39150f) coderd/x/chatd/chatprovider/reasoningeffort.go:83 SelectableReasoningEfforts returns sub-slice aliasing package-level var R1 Netero Yes
CRF-3 P3 Author fixed (d39150f) coderd/database/queries/chats.sql:1700 UpdateChatStatusPreserveUpdatedAt has no production caller R1 Netero, Law Yes
CRF-4 Note Author accepted R2 (intentional scaffolding for downstream PR #26977) site/src/pages/AgentsPage/utils/reasoningEffort.ts:2 formatReasoningEffort and pickReasoningEffort exported but unused (follow-up PR) R1 Netero, Law Yes
CRF-5 P2 Author contested; panel closed R3 (4/5 accept) coderd/exp_chats.go:7577 Validation accepts global-scale effort values that specific providers reject at runtime R2 Mafuuu P2, Knov P2, Hisoka P3, Razor P3, Pariston Note, Ryosuke Note, Chopper Note Yes
CRF-6 P3 Author fixed (355f182) coderd/exp_chats.go:7577 ReasoningEffortLessOrEqual(x,x) used as membership check obscures intent R2 Gon, Leorio, Ryosuke, Meruem, Zoro, Knov, Hisoka, Mafuuu, Chopper, Knuckle, Kite Yes
CRF-7 P3 Author fixed (355f182) coderd/x/chatd/chatprovider/reasoningeffort.go:88 ApplyReasoningEffort has 3 of 5 provider branches at 0% test coverage R2 Bisky, Chopper Yes
CRF-8 P4 Author contested; panel closed R3 (5/5 accept) coderd/x/chatd/chatprovider/reasoningeffort.go:100 ApplyReasoningEffort silently drops effort for Google provider R2 Hisoka, Chopper, Razor Yes
CRF-9 P3 Author fixed (355f182) coderd/x/chatd/generation_preparer.go:535 Call-site narration duplicates ResolveReasoningEffort doc comment R2 Gon Yes
CRF-10 P3 Author fixed (355f182) coderd/database/migrations/000540_chat_reasoning_effort.up.sql:68 Migration comment asserts application runtime behavior outside its scope R2 Gon Yes
CRF-11 Nit Author fixed (355f182) site/src/pages/AgentsPage/components/ChatModelAdminPanel/modelConfigFormLogic.ts:453 Comment over-explains self-documenting validation R2 Gon Yes
CRF-12 P3 Author contested; panel closed R3 (3/5 accept) site/src/pages/AgentsPage/utils/reasoningEffort.ts:2 formatReasoningEffort("xhigh") produces "Xhigh", meaningless to users R2 Leorio Yes
CRF-13 Nit Author fixed (355f182) coderd/exp_chats.go:7584 xerrors.Errorf used without format args; should be xerrors.New R2 Meruem, Zoro Yes
CRF-14 Note Author fixed (355f182) coderd/x/chatd/chatprovider/reasoningeffort.go:21 Effort scale defined twice in Go (reasoningEffortOrder and enum struct tags) R2 Robin Yes
CRF-15 Nit Author fixed (355f182) site/src/pages/AgentsPage/utils/reasoningEffort.ts:5 pickReasoningEffort missing doc comment for its three-step fallback chain R2 Leorio Yes
CRF-16 P2 Author fixed (f677efc) coderd/x/chatd/chatd.go:401 Advisor model setup drops reasoning effort (ApplyReasoningEffort not called) R3 Knov Yes
CRF-17 P3 Author fixed (f677efc) coderd/x/chatd/chatprovider/reasoningeffort_test.go:90 OpenAI "Preserves" test missing; mutation branches in applyOpenAIReasoningEffort untested R3 Bisky, Hisoka Yes
CRF-18 Nit Author fixed (f677efc) coderd/database/migrations/testdata/fixtures/000535_chat_model_config_legacy_reasoning_effort.up.sql:2 Fixture comment references migration 000540 but actual is 000541 R3 Meruem, Kite Yes
CRF-19 Nit Author fixed (f677efc) codersdk/chats.go:1265 ReasoningEfforts field has no doc comment R3 Leorio Yes
CRF-20 Note Author fixed (f677efc) codersdk/chats.go:1421 ChatModelReasoningEffortValues is an exported mutable var slice R3 Meruem Yes

Contested and acknowledged

CRF-4 (Note, site/src/pages/AgentsPage/utils/reasoningEffort.ts:2) - unused exports

  • Finding: formatReasoningEffort and pickReasoningEffort are exported but have no callers outside tests in this PR.
  • Author defense: The helpers are consumed by downstream PR feat: add reasoning effort slider to the chat model selector #26977 (chat slider). They are intentionally landed here as part of the PR stack.
  • Author accepted (R2): The finding was a Note with the PR stack context already noted as intentional. Author confirmed the specific downstream consumer. No action needed.

CRF-5 (P2, coderd/exp_chats.go:7577) - global-scale validation gap

  • Finding: The old per-provider validators enforced disjoint supported sets. The new validation only checks the global scale. An admin can store values a specific provider doesn't support, causing runtime generation failures.
  • Author defense: Provider-specific validation removal is intentional. Provider/model effort support is admin-asserted and provider-verified at runtime.
  • Panel closure (R3, 4/5): Knov traced the design tradeoff: per-provider validation sets require manual maintenance and can drift as providers evolve. Runtime validation is the authoritative source. The consequence is bounded to admin-configured models and produces an actionable runtime error. Meruem, Hisoka, and Pariston concurred. Mafuuu dissented, noting the validation error message promises all global-scale values are valid for the specific model.

CRF-8 (P4, coderd/x/chatd/chatprovider/reasoningeffort.go:100) - Google provider missing

  • Finding: ApplyReasoningEffort has no case for Google. Configured reasoning effort is silently discarded.
  • Author defense: Google had no prior effort mapping, so this is not a regression. Mapping Gemini thinking settings is out of scope for this PR.
  • Panel closure (R3, 5/5): All reviewers confirmed the old code also had no Google case. Google/Gemini uses ThinkingConfig.ThinkingBudget, a different mechanism from the reasoning effort scale.

CRF-12 (P3, site/src/pages/AgentsPage/utils/reasoningEffort.ts:2) - "Xhigh" label

  • Finding: formatReasoningEffort("xhigh") produces "Xhigh", which is meaningless to users. Should be "Extra High" or similar.
  • Author defense: xhigh remains the canonical enum/UI raw label for now. No schema/value rename in this PR.
  • Panel closure (R3, 3/5): Meruem, Pariston, and Kite accepted the deferral to the downstream slider PR (feat: add reasoning effort slider to the chat model selector #26977). Mafuuu and Leorio dissented, noting the label is meaningless and the fix is trivial.

Law analysis

Effective LOC: 1254 additions. Head SHA: 15d4c72. Verdict: Don't split. Enforcement: Advisory.

Round log

Round 1

Netero + Law. 1 P2, 2 P3, 1 Note. Reviewed against 07f4c9f..15d4c72. Law verdict: Don't split (Advisory). Netero P2 gates panel.

Round 2

Churn guard: PROCEED. CRF-1, CRF-2, CRF-3 fixed in d39150f. CRF-4 acknowledged. Panel: 19 reviewers. Netero: no new findings. 1 P2, 5 P3, 1 P4, 3 Nit, 1 Note new. Reviewed against 07f4c9f..d39150f.

Round 3

Churn guard: PROCEED. CRF-6, CRF-7, CRF-9, CRF-10, CRF-11, CRF-13, CRF-14, CRF-15 fixed in 355f182. CRF-5, CRF-8, CRF-12 contested. Panel: 9 reviewers (Bisky, Hisoka, Mafu-san, Mafuuu, Pariston, Leorio, Meruem, Knov, Kite). CRF-5 closed (4/5 accept), CRF-8 closed (5/5), CRF-12 closed (3/5). 1 P2, 1 P3, 2 Nit, 1 Note new. Reviewed against 07f4c9f..3d36549.

Round 4

Churn guard: PROCEED. CRF-16, CRF-17, CRF-18, CRF-19, CRF-20 fixed in f677efc. PR description updated. Panel: 4 reviewers (Bisky, Mafuuu, Pariston, Meruem). Netero: no findings. 1 Nit new (CRF-21). Reviewed against 07f4c9f..f677efc.
| CRF-21 | Nit | Open | coderd/x/chatd/chatprovider/reasoningeffort_test.go:207 | OpenRouter missing "Creates" test case; Reasoning==nil initialization branch untested | R4 | Bisky | Yes |

About deep-review

CRF = Coder Review Finding (P0-P4, Nit, Note)

Reviewer Focus
Bisky tests
Chopper ops/errors
Churn-guard change verification
Ging language modernization
Gon naming
Hisoka edge cases
Killua perf
Kite change integrity
Knov contracts
Knuckle SQL
Komugi flake/determinism
Kurapika security
Law decomposition
Leorio docs
Luffy product
Mafu-san process
Mafuuu contracts
Melody dispatch/pairing
Meruem structural
Nami frontend
Netero mechanical checks
Pariston premise testing
Pen-botter product gaps
Razor verification
Robin duplication
Ryosuke Go arch
Takumi concurrency
Zoro shape

🤖 Managed by Coder Agents.

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First-pass review (Netero). This is a mechanical-and-coverage scan; the full review panel has not yet reviewed this PR.

The reasoning effort refactor is well structured: clean separation of the new resolution logic into its own module, thorough migration with fixtures, and good test coverage (~50% density). The concern decomposition tracks a single feature across the stack with tight coupling, so Law says no split.

Severity count: 1 P2, 2 P3, 1 Note.

The P2 is a documentation/implementation mismatch worth fixing before the panel reviews. The panel will review after these findings are addressed.

"The comment makes a factual claim about behavior that does not exist. Either implement mirroring or fix the comment." (Netero)

🤖 This review was automatically generated with Coder Agents.

Comment thread codersdk/chats.go Outdated
Comment thread coderd/x/chatd/chatprovider/reasoningeffort.go Outdated
Comment thread coderd/database/queries/chats.sql Outdated
Comment thread site/src/pages/AgentsPage/utils/reasoningEffort.ts
@DanielleMaywood
DanielleMaywood force-pushed the dm/reasoning-effort-1-model-config branch 2 times, most recently from 228a7db to d39150f Compare July 7, 2026 14:11
@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Panel review (round 2, 19 reviewers). R1 findings (CRF-1 through CRF-3) all verified fixed in d39150f. CRF-4 acknowledged as follow-up scaffolding.

The reasoning effort refactor is well-structured. The resolution pipeline (validate at API boundary, resolve with clamping at generation time, provider-specific injection at the last mile) is cleanly layered with single responsibilities. The migration is solid: nullable columns avoid rewrites, lower(trim(...)) normalization with NULLIF and IN-clause whitelisting handles edge cases, and the test fixture seeds real-world variations. Test density is 53.4%, covering validation, resolution, and the full generation chain.

Severity count: 1 P2, 5 P3, 1 P4, 3 Nit, 1 Note.

The P2 is a widened perimeter: the old per-provider validation was removed, so admins can now save global-scale effort values that individual providers reject at runtime, breaking all chats using that model. Seven reviewers flagged this independently.

"The old code had per-provider normalization... This PR removes that layer without adding a replacement." (Mafuuu)

"The config API advertises all seven global-scale values, the resolution accepts them, and the provider API rejects them. The gap between 'the API accepts it' and 'the runtime uses it' is the finding." (Razor)

🤖 This review was automatically generated with Coder Agents.

Comment thread coderd/exp_chats.go Outdated
Comment thread coderd/exp_chats.go Outdated
Comment thread coderd/x/chatd/chatprovider/reasoningeffort.go
Comment thread coderd/x/chatd/chatprovider/reasoningeffort.go
Comment thread coderd/x/chatd/generation_preparer.go Outdated
Comment thread site/src/pages/AgentsPage/utils/reasoningEffort.ts
Comment thread coderd/exp_chats.go Outdated
Comment thread coderd/x/chatd/chatprovider/reasoningeffort.go Outdated
Comment thread site/src/pages/AgentsPage/utils/reasoningEffort.ts
@DanielleMaywood
DanielleMaywood force-pushed the dm/reasoning-effort-1-model-config branch from d39150f to 355f182 Compare July 7, 2026 23:26

Copy link
Copy Markdown
Contributor Author

Fixed CRF-6, CRF-7, CRF-9, CRF-10, CRF-11, CRF-13, CRF-14, and CRF-15 in 355f182.

Intentionally not changed:

  • CRF-5: provider-specific validation removal is intentional. Provider/model effort support is admin-asserted and provider-verified.
  • CRF-8: Google had no prior effort mapping, so this is not a regression. Mapping Gemini thinking settings is out of scope.
  • CRF-12: xhigh remains the canonical enum/UI raw label for now. No schema/value rename in this PR.

Coder Agents generated.

@DanielleMaywood
DanielleMaywood force-pushed the dm/reasoning-effort-1-model-config branch from 355f182 to 3d36549 Compare July 7, 2026 23:44

Copy link
Copy Markdown
Contributor Author

Follow-up: also renumbered the migration to avoid the current origin/main 000540 migration collision. Latest fixed commit is 3d36549.

Coder Agents generated.

@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Panel review (round 3, 9 reviewers). R2 fixes (CRF-6, 7, 9, 10, 11, 13, 14, 15) all verified correct and complete.

Contested findings resolved by panel vote:

  • CRF-5 (P2, validation gap): closed 4/5. The design trades upfront strictness for flexibility. Runtime validation is the authoritative source, and per-provider sets would require manual maintenance that could drift.
  • CRF-8 (P4, Google missing): closed 5/5. Not a regression; Google uses a different mechanism.
  • CRF-12 (P3, "Xhigh" label): closed 3/5. Deferred to the downstream slider PR.

Severity count (new): 1 P2, 1 P3, 2 Nit, 1 Note.

The P2 is a missed call site: the advisor model setup builds provider options but never applies reasoning effort, unlike the generation and title-generation paths.

Note: the PR description still contains two inaccurate claims: (1) "values must be in the provider's runtime-supported set" (validation checks global scale only, as CRF-5 confirmed), and (2) "a single value mirrors into the other" (validation requires both, as CRF-1 established). Please update the description to match the actual behavior.

"Two of the three call sites were updated; this one was missed." (Knov)


coderd/x/chatd/chatd.go:401

P2 [CRF-16] Advisor model setup drops reasoning effort after the refactoring.

ProviderOptionsFromChatModelConfig builds provider options for the advisor model here, but ApplyReasoningEffort is never called. The generation path (generation_preparer.go:534-537) and title generation path (quickgen.go:359-360 via titleGenerationProviderOptions) were both updated to call ApplyReasoningEffort after ProviderOptionsFromChatModelConfig. This call site was missed.

The advisorCallConfig carries the new ReasoningEffort field (from the fallback config or override config's migrated options). The effort is present in the config but never injected into provider options, so the advisor model operates without its configured reasoning effort.

Fix:

providerOptions := chatprovider.ProviderOptionsFromChatModelConfig(
    advisorModel,
    advisorCallConfig.ProviderOptions,
)
providerOptions = chatprovider.ApplyReasoningEffort(
    advisorModel,
    providerOptions,
    chatprovider.ResolveReasoningEffort(advisorCallConfig.ReasoningEffort),
)

(Knov)

🤖

🤖 This review was automatically generated with Coder Agents.

require.Equal(t, fantasyopenai.ReasoningEffortHigh, *providerOptions.ReasoningEffort)
})

tests := []struct {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 [CRF-17] TestApplyReasoningEffort has no "Preserves" test for OpenAI, leaving the two mutation branches in applyOpenAIReasoningEffort untested.

The OpenAI apply function has a three-way type switch: *ResponsesProviderOptions (mutate existing), *ProviderOptions (mutate existing), and default (create new). Only the default branch is tested via CreatesOpenAIResponsesEntry. Every other provider has a "Preserves" test proving apply doesn't clobber existing fields. OpenAI has none. Coverage: applyOpenAIReasoningEffort at 66.7%.

In production, ApplyReasoningEffort runs after ProviderOptionsFromChatModelConfig, which builds initial options from admin config. When an admin configures both reasoning effort and another OpenAI setting, the options map already contains an OpenAI entry. That path hits lines 118-121 (mutation branches), which are untested.

Fix: add PreservesOpenAIResponsesEntry and PreservesOpenAILegacyEntry subtests, analogous to the existing preserve tests for other providers. (Bisky P3, Hisoka P3)

🤖

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed CRF-17 in f677efc by adding OpenAI responses and legacy preserve coverage.

Coder Agents generated.

@@ -0,0 +1,105 @@
-- Chat model configs carrying legacy per-provider reasoning effort
-- values inside options. Inserted at 000535 so the 000540 data

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit [CRF-18] Fixture comment references migration 000540 but the actual migration is 000541.

Line 2 says "the 000540 data migration" but the migration file is 000541_chat_reasoning_effort.up.sql. Stale from the renumber in this round. (Meruem, Kite)

🤖

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed CRF-18 in f677efc by correcting the fixture comment to reference migration 000541.

Coder Agents generated.

Comment thread codersdk/chats.go Outdated
ContextLimit int64 `json:"context_limit"`
CompressionThreshold int32 `json:"compression_threshold"`
ModelConfig *ChatModelCallConfig `json:"model_config,omitempty"`
ReasoningEfforts []string `json:"reasoning_efforts,omitempty"`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit [CRF-19] ReasoningEfforts field has no doc comment.

Every other field on ChatModelConfig is self-evident from its name, but ReasoningEfforts []string is ambiguous. Is it the configured effort pair? The selectable values for UI dropdowns? The raw enum constants?

The field is computed by chatprovider.SelectableReasoningEfforts and lists the global effort values from "none" through the configured max. A one-line comment makes that clear. (Leorio)

🤖

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed CRF-19 in f677efc by documenting that ReasoningEfforts contains selectable values through the configured max.

Coder Agents generated.

Comment thread codersdk/chats.go Outdated
)

// ChatModelReasoningEffortValues is the global reasoning effort scale.
var ChatModelReasoningEffortValues = []string{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note [CRF-20] ChatModelReasoningEffortValues is an exported mutable var slice.

Any consumer can mutate the global scale (e.g., codersdk.ChatModelReasoningEffortValues[0] = "foo"), which would corrupt reasoningEffortRank, IsValidReasoningEffort, and SelectableReasoningEfforts across the entire process. Internal consumers are defensive (slices.Index is read-only, SelectableReasoningEfforts clones), and Go convention treats exported vars as read-only. Practical risk is low. Worth converting to a function or private var in a future cleanup. (Meruem)

🤖

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed CRF-20 in f677efc by making the global scale private and exposing ChatModelReasoningEffortValues() as a cloned slice.

Coder Agents generated.

@DanielleMaywood
DanielleMaywood force-pushed the dm/reasoning-effort-1-model-config branch from 3d36549 to f677efc Compare July 8, 2026 07:53

DanielleMaywood commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Fixed CRF-16, CRF-17, CRF-18, CRF-19, and CRF-20 in f677efc. The PR body stack and validation wording are corrected.

Coder Agents generated.

Copy link
Copy Markdown
Contributor Author

Fixed CRF-16 in f677efc by applying the advisor model config reasoning effort when building advisor provider options.

Coder Agents generated.

@DanielleMaywood

Copy link
Copy Markdown
Contributor Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All findings resolved across 4 rounds (21 findings total: 16 fixed, 3 closed by panel vote, 1 acknowledged, 1 minor nit below).

R4 fixes verified: CRF-16 (advisor model now applies reasoning effort, with dedicated test), CRF-17 (OpenAI preserve tests added for both Responses and legacy API shapes), CRF-18 (fixture comment corrected), CRF-19 (doc comment added), CRF-20 (exported mutable var converted to function returning clone). PR description updated to match actual behavior.

Test density: 57.8%. Netero clean across all 8 categories. The pipeline (validate at API boundary, resolve with clamping, provider-specific injection) is cleanly layered. Migration handles edge cases well. The TestChatModelReasoningEffortConfigEnumTags test mechanically prevents enum drift.

🤖 This review was automatically generated with Coder Agents.

Comment thread coderd/x/chatd/chatprovider/reasoningeffort_test.go
@DanielleMaywood
DanielleMaywood force-pushed the dm/reasoning-effort-1-model-config branch 3 times, most recently from c485ab1 to a1bbe9e Compare July 8, 2026 10:14
@DanielleMaywood
DanielleMaywood marked this pull request as ready for review July 8, 2026 10:18
@DanielleMaywood
DanielleMaywood requested a review from mafredri July 8, 2026 10:18
@DanielleMaywood
DanielleMaywood requested a review from johnstcn July 8, 2026 10:18
@coder-tasks

coder-tasks Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Documentation Check

Updates Needed

  • docs/ai-coder/agents/models.md - Provider-specific options tables are stale. This PR removes per-provider reasoning effort fields (Reasoning Effort from OpenAI, Effort from Anthropic, Reasoning Effort from OpenRouter, Reasoning Effort from Vercel) and moves reasoning effort to a top-level default/max config. The following sections need updating:
    • Remove "Reasoning Effort" row from the OpenAI table (line 200)
    • Remove "Effort" row from the Anthropic table (line 194)
    • Remove "Reasoning Effort" row from the OpenRouter table (line 216)
    • Remove "Reasoning Effort" row from the Vercel AI Gateway table (line 223)
    • Add "Default Reasoning Effort" and "Max Reasoning Effort" rows to the General options table (around line 167), describing the default/max pair and the global scale (none, minimal, low, medium, high, xhigh, max)
    • Update the Azure/Bedrock note (line 226) which currently says "reasoning effort" is a provider-specific option shared with OpenAI/Anthropic

Automated review via Coder Agents

Comment thread coderd/database/migrations/000542_chat_reasoning_effort.up.sql
Comment thread coderd/x/chatd/chatprovider/reasoningeffort.go Outdated
Comment thread coderd/x/chatd/chatprovider/reasoningeffort_test.go Outdated
Comment thread codersdk/chats.go

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: need to also update the coderd provider

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coder Agents generated.

This needs a separate follow-up PR in coder/terraform-provider-coderd after the SDK bump. That provider lives in a separate repo, and model_config is currently opaque JSON there, so I left this PR scoped to coder/coder.

Comment thread codersdk/chats.go
Comment on lines +1414 to +1420
ChatModelReasoningEffortNone = "none"
ChatModelReasoningEffortMinimal = "minimal"
ChatModelReasoningEffortLow = "low"
ChatModelReasoningEffortMedium = "medium"
ChatModelReasoningEffortHigh = "high"
ChatModelReasoningEffortXHigh = "xhigh"
ChatModelReasoningEffortMax = "max"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: should these be numeric with a String() method?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly wise, yeah. Will change

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coder Agents generated.

I left this unchanged. A numeric enum with string JSON would require custom handling across JSON/OpenAPI/TypeScript generation and modeloptionsgen, which is out of scope for this stack. The current SDK pattern here is string-backed enums.

@DanielleMaywood
DanielleMaywood force-pushed the dm/reasoning-effort-1-model-config branch from a1bbe9e to a1ccef1 Compare July 8, 2026 12:10

@mafredri mafredri left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writing critique of this PR's title, description, and code comments, produced with the writing-critique method: each unit checked against clear-writing guides, every claim verified against the diff.

Ten findings. Inline suggestions are attached for the three code-comment findings (CF1-CF3) and can be applied directly. The title and description findings (F1-F7) are below, because they are not part of the diff and cannot carry inline suggestions.

Three findings need a maintainer fact: F2, F7, and CF1.


Title and description

F1 - title contradicts the body on the old state. The title calls the old state "per-provider"; para 1 calls it a "fixed per-model setting stored at provider-specific paths." The value was one-per-model-config keyed by provider, not shared per provider, so "per-provider" is wider than the evidence. Proposed title:

feat: replace fixed reasoning effort with a per-model default and max

F2 - Validation bullet asserts a behavior not in this diff (needs a fact). "Updates fail when the config's provider has been soft-deleted" has no supporting change anywhere in the diff; the only validation added is validateChatModelReasoningEffortConfig. If it is not introduced here, drop the sentence:

-- **Validation**: `default` and `max` must both be present when reasoning effort is configured, each value must be in the global scale `none < minimal < low < medium < high < xhigh < max`, and `default <= max`. Updates fail when the config's provider has been soft-deleted.
+- **Validation**: `default` and `max` must both be present when reasoning effort is configured, each value must be in the global scale `none < minimal < low < medium < high < xhigh < max`, and `default <= max`.

If it is introduced or changed here, keep it but cite where.

F3 - "site" bullet contradicts itself and carries jargon. "compile fallout only" says nothing behavioral changed, then the same sentence reports new default <= max validation. It also uses the coined "compile fallout" / "known-model catalog" and UI jargon "selects".

-- **site**: compile fallout only; the admin form schema loses the old per-provider effort selects, known-model catalog defaults populate the new pair, and form logic validates `default <= max`. New admin UI placement and the chat slider land in follow-ups.
+- **site**: mostly mechanical changes forced by the removed fields. The admin form no longer shows the old per-provider effort dropdowns, and default values for recognized models fill the new `default` and `max` fields. The one behavior change is form validation that rejects a `default` greater than `max`. New admin UI placement and the chat slider land in follow-ups.

F4 - Stack line hides a branching tree behind linear arrows. Verified: #26975 and #26976 both branch off this PR; #26977 off #26975; #27061 off #26977. The -> / (on X) notation cannot express that.

-Stack: this PR → per-turn API (#26975) and models page UI (#26976, on this PR); chat slider (#26977, on #26975) → model override reasoning effort (#27061, on #26977).
+Stack (each PR is based on the one named):
+
+- this PR (on `main`)
+- #26975 per-turn API (on this PR)
+- #26976 models page UI (on this PR)
+- #26977 chat slider (on #26975)
+- #27061 model override reasoning effort (on #26977)

F5 + F7 - "behavior-preserving" is undefined-term jargon and omits its scope (F7 needs a fact). Para 2 relies on "global scale" before it is defined (it is defined only in the Validation bullet), and asserts "behavior-preserving" without qualification. The migration filters with AND legacy.effort IN ('none', ..., 'max') and so drops out-of-scale legacy values; the testdata's GPT-5.1 (Invalid Legacy Effort) = "extreme" becomes no effort, where the old path passed it straight through. Combined fix:

-This PR is behavior-preserving: generation resolves the configured default (clamped to max on the global scale), which matches the old fixed value after migration. Per-turn user selection lands in the follow-up PR.
+This PR is behavior-preserving for efforts that were already a recognized value: generation resolves the configured default, clamped to the configured max, which matches the old fixed value after migration. A stored value the new schema does not recognize (for example a manually set `"extreme"`) is dropped by the migration rather than carried over, so that config resolves to no effort. Per-turn user selection lands in the follow-up PR.

If out-of-scale rows cannot exist because the old write path enforced each provider's enum, the second sentence can shrink to a one-clause scope note.

F6 - "fantasy provider options" is an undefined library name. The chatd bullet says the value is injected into the "fantasy provider options"; the ARCHITECTURE.md section this same PR adds calls them the "provider-native options". Use the term the PR's own docs use.

-- **chatd**: new `chatprovider.ResolveReasoningEffort` clamps the configured default to the configured max on the global scale before `ApplyReasoningEffort` injects it into the fantasy provider options (azure routes via openai, bedrock via anthropic).
+- **chatd**: new `chatprovider.ResolveReasoningEffort` clamps the configured default to the configured max before `ApplyReasoningEffort` injects it into the provider-native options (azure routes via openai, bedrock via anthropic).

The three code-comment findings are attached inline as applicable suggestions.

🤖 This review was automatically generated with Coder Agents.

Comment thread codersdk/chats.go Outdated
CacheWritePricePerMillionTokens *decimal.Decimal `json:"cache_write_price_per_million_tokens,omitempty" description:"Cache write or cache creation token price in USD per 1M tokens"`
}

// ChatModelReasoningEffort values.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CF2 (minor): this header names a type ChatModelReasoningEffort that does not exist (the members are untyped string constants) and otherwise restates the names. State the non-obvious fact instead: the declaration order is the ranking used to clamp and compare.

Suggested change
// ChatModelReasoningEffort values.
// Reasoning effort levels, ordered low to high. The declaration order is the
// ranking used to clamp and compare efforts. Keep in sync with
// chatModelReasoningEffortValues below.

🤖

@DanielleMaywood DanielleMaywood Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8f4025c. I reworded the constants comment to describe the ordering/ranking used for clamping and comparison.

Coder Agents generated.

Comment on lines +116 to +117
// The catalog carries a single editorial effort value. Write both
// reasoning_effort bounds because the API requires default and max.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CF3 (minor): "editorial" is undefined here; the intended meaning is the single curated value the catalog ships.

Suggested change
// The catalog carries a single editorial effort value. Write both
// reasoning_effort bounds because the API requires default and max.
// The catalog carries a single curated effort value. Write it to both
// reasoning_effort bounds because the API requires default and max.

🤖

@DanielleMaywood DanielleMaywood Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 8f4025c. I changed editorial to curated in the known-model defaults comment.

Coder Agents generated.

@DanielleMaywood DanielleMaywood changed the title feat: replace fixed per-provider reasoning effort with per-model default and max feat: replace fixed reasoning effort with per-model default and max Jul 8, 2026
@DanielleMaywood
DanielleMaywood force-pushed the dm/reasoning-effort-1-model-config branch from a1ccef1 to 8f4025c Compare July 8, 2026 14:06

DanielleMaywood commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

@mafredri addressed the writing critique in PR #26974.

  • F1-F7: PR title/body updated.
  • CF1: migration comments reworded.
  • CF2: constants comment fixed.
  • CF3: editorial changed to curated.

Coder Agents generated.

@DanielleMaywood DanielleMaywood changed the title feat: replace fixed reasoning effort with per-model default and max feat: add configurable reasoning effort to Coder agents Jul 9, 2026
@DanielleMaywood
DanielleMaywood merged commit d66e4d7 into main Jul 9, 2026
36 of 37 checks passed
@DanielleMaywood
DanielleMaywood deleted the dm/reasoning-effort-1-model-config branch July 9, 2026 22:35
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 9, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants