feat: add reasoning effort slider to the chat model selector - #26977
Conversation
cd73e8f to
ed3b43c
Compare
e26d472 to
94e5bb9
Compare
ed3b43c to
1934e5b
Compare
94e5bb9 to
8d6506c
Compare
1934e5b to
26325c8
Compare
8d6506c to
c4f372b
Compare
26325c8 to
3685d3c
Compare
c4f372b to
28bf556
Compare
3685d3c to
c1f9fc4
Compare
28bf556 to
56104c4
Compare
c1f9fc4 to
2f40dd5
Compare
56104c4 to
51acd5d
Compare
2f40dd5 to
35efe67
Compare
51acd5d to
474e71e
Compare
35efe67 to
5286cc9
Compare
474e71e to
514790b
Compare
5286cc9 to
cc24930
Compare
514790b to
c5b2fa9
Compare
cc24930 to
fd9e20c
Compare
c5b2fa9 to
847f0e7
Compare
fd9e20c to
226f860
Compare
847f0e7 to
515eeef
Compare
226f860 to
1f8421d
Compare
515eeef to
b6365c7
Compare
1f8421d to
d6a7d4c
Compare
|
Chat: Review posted | View chat Review historydeep-review v0.9.0 | Round 4 | Last posted: Round 4, 13 findings (1 P1, 2 P2, 3 P3, 1 P4, 3 Nit, 3 Note), APPROVE. Review Finding inventoryFindings
Contested and acknowledgedCRF-6 (P3, reasoningEffort.ts:2) - "Xhigh" display label
CRF-10 (Note, AgentCreateForm.stories.tsx:332) - No default effort story
Round logRound 1Netero-only. 1 P2, 1 P3. Reviewed against 7b4f4ee..fd8c08a. Round 2Netero-only. CRF-1, CRF-2 addressed. 1 P2 new (CRF-3). Reviewed against 7b4f4ee..76f68fe. Round 3Panel (19 reviewers). CRF-3 addressed. 1 P1, 2 P3, 1 P4, 2 Nit, 3 Note new. Reviewed against 63d94a3..3c16e1e. Round 4Panel (8 reviewers). CRF-4/5/7/8/9/11/12 addressed. CRF-6 contested, panel closed (5/5 accept product decision). CRF-10 acknowledged. 1 Nit new (CRF-13). Reviewed against 63d94a3..1277de5. About deep-reviewCRF = Coder Review Finding (P0-P4, Nit, Note)
|
There was a problem hiding this comment.
The frontend wiring and Storybook coverage are well structured. The slider component reuse, keyboard navigation stories, and pickReasoningEffort unit tests show care. Two mechanical findings from the first pass below.
This is a first-pass review only: these are mechanical findings from Netero. The full review panel has not yet reviewed this PR and will review after these findings are addressed.
1 P2, 1 P3.
coderd/x/chatd/chatd.go:2951
P3 [CRF-2] appendMessageFields no longer appends to params.ReasoningEffort, so the ReasoningEffort slice stays at length 0 while every other field slice is length 1. PostgreSQL 10+ pads mismatched UNNEST lengths with NULLs, so the insert succeeds and reasoning_effort is NULL, which is functionally correct. But the explicit insertion path at line 2830 sets ReasoningEffort: []string{""} to keep arrays aligned. The inconsistency is fragile: if any validation or middleware starts asserting array length parity, this path breaks.
Add params.ReasoningEffort = append(params.ReasoningEffort, "") to keep the arrays aligned. (Netero)
🤖
🤖 This review was automatically generated with Coder Agents.
| CheckAPIKeysAllowListNotEmpty CheckConstraint = "api_keys_allow_list_not_empty" // api_keys | ||
| CheckBoundaryLogsSequenceNumberCheck CheckConstraint = "boundary_logs_sequence_number_check" // boundary_logs | ||
| CheckChatMessagesReasoningEffortCheck CheckConstraint = "chat_messages_reasoning_effort_check" // chat_messages | ||
| CheckChatModelConfigsAIProviderRequiredWhenActive CheckConstraint = "chat_model_configs_ai_provider_required_when_active" // chat_model_configs |
There was a problem hiding this comment.
P2 [CRF-1] The up migration (000541_chat_reasoning_effort.up.sql) still creates three CHECK constraints (chats_last_reasoning_effort_check, chat_messages_reasoning_effort_check, chat_queued_messages_reasoning_effort_check), but this PR removes them from dump.sql, check_constraint.go, and the down migration's DROP CONSTRAINT lines. The generated schema and the migration are now inconsistent: running make gen regenerates dump.sql with the constraints, producing a diff. This is the likely cause of the CI gen failure.
Remove the three ALTER TABLE ... ADD CONSTRAINT blocks from the up migration, then re-run make gen. (Netero)
🤖
There was a problem hiding this comment.
Resolved in the latest push.
CRF-1 is fixed by restoring generated CHECK constraint consistency with PR26975, not by dropping the up migration constraints. make gen restored coderd/database/dump.sql and coderd/database/check_constraint.go, and the down migration keeps the explicit DROP CONSTRAINT IF EXISTS statements.
This reply was generated with Coder Agents.
fd8c08a to
76f68fe
Compare
DanielleMaywood
left a comment
There was a problem hiding this comment.
Resolved in the latest push.
CRF-2 is fixed by appending "" to ReasoningEffort in appendMessageFields, matching the explicit insertion path and keeping the UNNEST arrays aligned.
This reply was generated with Coder Agents.
|
/coder-agents-review |
There was a problem hiding this comment.
CRF-1 and CRF-2 verified fixed in 76f68fe. The constraint consistency is restored and the UNNEST arrays are aligned. One new finding from this pass.
This is a first-pass review only: these are mechanical findings from Netero. The full review panel has not yet reviewed this PR and will review after these findings are addressed.
1 P2.
coderd/x/chatd/chatd_test.go:11663
P2 [CRF-3] Five backend test functions were deleted with no replacement, but the code paths they covered still exist:
TestEditMessageReasoningEffort: edit preserves effort by default, overrides when explicitly setTestPromoteQueuedPreservesReasoningEffort: promote carries effort from queued row to chatTestSendMessageQueuesReasoningEffort: queuing preserves reasoning effortTestPatchChatMessage/ReasoningEffort: HTTP-level edit effort preservation/overrideTestPatchChatMessage/RejectsInvalidReasoningEffort: invalid effort rejection on edit
EditMessage at chatd.go:1679-1691 still accepts ReasoningEffort and chatstate.EditMessageInput.ReasoningEffortOverride still threads it through. No remaining test covers edit+effort, promote+effort, or queue+effort after this PR. The frontend stories cover the UI slider but not the backend round-trip. (Netero)
🤖
🤖 This review was automatically generated with Coder Agents.
7b4f4ee to
d34a520
Compare
ead7707 to
0b08c0a
Compare
|
Resolved in the latest push. Backend reasoning-effort coverage is restored and shrunk:
This reply was generated with Coder Agents. |
d34a520 to
b8c5ac8
Compare
0b08c0a to
6455ba5
Compare
b8c5ac8 to
63d94a3
Compare
6455ba5 to
3c16e1e
Compare
|
/coder-agents-review |
1 similar comment
|
/coder-agents-review |
Documentation CheckUpdates Needed
Automated review via Coder Agents |
There was a problem hiding this comment.
CRF-1, CRF-2, CRF-3 all verified fixed. The slider component is well-structured, the prop threading mirrors the established model selection pattern, and test density at 63.8% is solid with genuine assertions throughout.
One P1 that is the likely root cause of the Go CI failures, plus two P3s, one P4, two Nits, and three Notes.
1 P1, 2 P3, 1 P4, 2 Nit, 3 Note.
"The comment paraphrases six lines of self-documenting code. Delete." — Gon, on the narrating comment pattern
site/src/pages/AgentsPage/utils/reasoningEffort.ts:2
P3 [CRF-6] formatReasoningEffort("xhigh") produces "Xhigh", which reads like an internal enum value. Every other effort level renders as a real English word (None, Minimal, Low, Medium, High, Max). A simple display map with a fallback to the capitalize path fixes it:
const effortLabels: Record<string, string> = { xhigh: "Extra high" };
export const formatReasoningEffort = (value: string): string =>
effortLabels[value] ?? value.charAt(0).toUpperCase() + value.slice(1);The test and the EffortRow story badge assertion would need updating. If the Figma spec genuinely says "Xhigh", push back on the spec. (Leorio P3, Luffy P3)
🤖
site/src/pages/AgentsPage/utils/reasoningEffort.ts:5
Nit [CRF-8] Doc comment says "the highest effort" but the code returns efforts[efforts.length - 1], which is only "highest" if the array is sorted ascending. The server sends them sorted today, but the comment documents a semantic guarantee the function doesn't enforce. Either note the precondition ("efforts must be sorted ascending") or describe the mechanical behavior ("the last selectable effort"). (Leorio)
🤖
🤖 This review was automatically generated with Coder Agents.
| }, | ||
| ] as const; | ||
|
|
||
| export const SubmitsReasoningEffort: Story = { |
There was a problem hiding this comment.
Note [CRF-10] No story tests submitting without moving the slider (the model default should flow through). The gap is narrow: pickReasoningEffort unit tests cover the fallback from "" to default, and the existing model-only submission stories don't carry reasoning efforts. Worth noting for completeness. (Bisky)
🤖
There was a problem hiding this comment.
Not changing this: default fallback is already covered by focused pickReasoningEffort unit tests, and submission wiring is covered by the form story, so another story would duplicate non-blocking coverage. This reply was generated by Coder Agents.
There was a problem hiding this comment.
Acknowledged. Existing coverage is sufficient.
🤖
3c16e1e to
1277de5
Compare
|
CRF-6: retaining |
|
/coder-agents-review |
There was a problem hiding this comment.
All prior findings verified fixed. CRF-6 ("Xhigh" label) panel vote: 5/5 accept the product decision defense — the cosmetic inconsistency is bounded by slider position context and does not affect functionality.
The iteration from R3 to R4 was thorough: the isEditReasoningEffortDirtyRef fix for CRF-12 went beyond the Note's assessment and improved the design (truthiness would break on default-value selection since "" is falsy). The InfoIcon keyboard accessibility fix added proper <button> wrapping with story coverage. Test density climbed to 67.8%.
One residual Nit below.
"I tried to build a case against this change and couldn't." — Pariston
site/src/pages/AgentsPage/utils/reasoningEffort.test.ts:21
Nit [CRF-13] Test description says "falls back to the highest server-provided effort" but CRF-8 fixed the pickReasoningEffort doc comment to say "the last selectable effort" (since the function returns efforts[efforts.length - 1] without enforcing sort order). The assertion is correct, but the description makes an ordering claim the function doesn't enforce. Consider: "falls back to the last selectable effort when value and default are out of range". (Leorio)
🤖
🤖 This review was automatically generated with Coder Agents.
54bf64c
into
dm/reasoning-effort-2-per-turn
Adds an "Effort" slider to the chat model selector dropdown, matching the Figma design: label with an info tooltip, a discrete slider, and a value badge (e.g. "Xhigh"), pinned below the model list.
Behavior
last_reasoning_effortwhen valid, else the model's default; re-clamps when switching models.reasoning_efforton chat creation, message send, and message edit (on edit only when the user changed the slider, so historical turns keep their original effort). The backend re-clamps regardless.Part of the reasoning effort stack (base: #26975).
🤖 Generated by Coder Agents on behalf of @DanielleMaywood