| author | |
| committer | |
| log | aeddb5759425de2f9fd589843a2392280d6c89ef |
| tree | 06bfd311491d5e28460a957e78f4e5d55ea8fdb3 |
| parent | f2253269ddf80446425b06629ed5dd8809a2bc1e |
- Replace global reasoning constants with provider-scoped effort options/defaults
- Update ChatView to use Codex-specific reasoning effort types and default lookup
- Expand model contract tests for provider-specific reasoning effort behavior3 files changed, 63 insertions(+), 26 deletions(-)
apps/web/src/components/ChatView.tsx+13-11| ... | @@ -2,17 +2,18 @@ import { | ... | @@ -2,17 +2,18 @@ import { |
| 2 | type ApprovalRequestId, | 2 | type ApprovalRequestId, |
| 3 | CURSOR_REASONING_OPTIONS, | 3 | CURSOR_REASONING_OPTIONS, |
| 4 | DEFAULT_MODEL, | 4 | DEFAULT_MODEL, |
| 5 | DEFAULT_REASONING, | ||
| 6 | EDITORS, | 5 | EDITORS, |
| 7 | type EditorId, | 6 | type EditorId, |
| 8 | type KeybindingCommand, | 7 | type KeybindingCommand, |
| 8 | type CodexReasoningEffort, | ||
| 9 | type CursorReasoningOption, | 9 | type CursorReasoningOption, |
| 10 | type MessageId, | 10 | type MessageId, |
| 11 | getDefaultModel, | 11 | getDefaultModel, |
| 12 | getDefaultReasoningEffort, | ||
| 12 | getCursorModelCapabilities, | 13 | getCursorModelCapabilities, |
| 13 | getCursorModelFamilyOptions, | 14 | getCursorModelFamilyOptions, |
| 14 | getModelOptions, | 15 | getModelOptions, |
| 15 | getReasoningOptions, | 16 | getReasoningEffortOptions, |
| 16 | type ProjectId, | 17 | type ProjectId, |
| 17 | type ProjectEntry, | 18 | type ProjectEntry, |
| 18 | type ProjectScript, | 19 | type ProjectScript, |
| ... | @@ -20,7 +21,6 @@ import { | ... | @@ -20,7 +21,6 @@ import { |
| 20 | parseCursorModelSelection, | 21 | parseCursorModelSelection, |
| 21 | PROVIDER_SEND_TURN_MAX_ATTACHMENTS, | 22 | PROVIDER_SEND_TURN_MAX_ATTACHMENTS, |
| 22 | PROVIDER_SEND_TURN_MAX_IMAGE_BYTES, | 23 | PROVIDER_SEND_TURN_MAX_IMAGE_BYTES, |
| 23 | type ReasoningEffort, | ||
| 24 | type ResolvedKeybindingsConfig, | 24 | type ResolvedKeybindingsConfig, |
| 25 | type ProviderApprovalDecision, | 25 | type ProviderApprovalDecision, |
| 26 | type ServerProviderStatus, | 26 | type ServerProviderStatus, |
| ... | @@ -683,8 +683,8 @@ export default function ChatView({ threadId }: ChatViewProps) { | ... | @@ -683,8 +683,8 @@ export default function ChatView({ threadId }: ChatViewProps) { |
| 683 | DEFAULT_MODEL, | 683 | DEFAULT_MODEL, |
| 684 | ); | 684 | ); |
| 685 | const selectedModel = resolveModelSlugForProvider(selectedProvider, composerDraft.model ?? baseThreadModel); | 685 | const selectedModel = resolveModelSlugForProvider(selectedProvider, composerDraft.model ?? baseThreadModel); |
| 686 | const selectedEffort = composerDraft.effort ?? DEFAULT_REASONING; | 686 | const selectedEffort = composerDraft.effort ?? getDefaultReasoningEffort(selectedProvider); |
| 687 | const reasoningOptions = getReasoningOptions(selectedProvider); | 687 | const reasoningOptions = getReasoningEffortOptions(selectedProvider); |
| 688 | const supportsReasoningEffort = reasoningOptions.length > 0; | 688 | const supportsReasoningEffort = reasoningOptions.length > 0; |
| 689 | const selectedCursorModel = useMemo( | 689 | const selectedCursorModel = useMemo( |
| 690 | () => (selectedProvider === "cursor" ? parseCursorModelSelection(selectedModel) : null), | 690 | () => (selectedProvider === "cursor" ? parseCursorModelSelection(selectedModel) : null), |
| ... | @@ -2386,8 +2386,8 @@ export default function ChatView({ threadId }: ChatViewProps) { | ... | @@ -2386,8 +2386,8 @@ export default function ChatView({ threadId }: ChatViewProps) { |
| 2386 | [onProviderModelSelect, selectedModel, selectedProvider], | 2386 | [onProviderModelSelect, selectedModel, selectedProvider], |
| 2387 | ); | 2387 | ); |
| 2388 | const onEffortSelect = useCallback( | 2388 | const onEffortSelect = useCallback( |
| 2389 | (effort: ReasoningEffort) => { | 2389 | (effort: CodexReasoningEffort) => { |
| 2390 | setComposerDraftEffort(threadId, effort); | 2390 | setSelectedEffort(effort); |
| 2391 | scheduleComposerFocus(); | 2391 | scheduleComposerFocus(); |
| 2392 | }, | 2392 | }, |
| 2393 | [scheduleComposerFocus, setComposerDraftEffort, threadId], | 2393 | [scheduleComposerFocus, setComposerDraftEffort, threadId], |
| ... | @@ -4132,10 +4132,12 @@ const ProviderModelPicker = memo(function ProviderModelPicker(props: { | ... | @@ -4132,10 +4132,12 @@ const ProviderModelPicker = memo(function ProviderModelPicker(props: { |
| 4132 | }); | 4132 | }); |
| 4133 | 4133 | ||
| 4134 | const ReasoningEffortPicker = memo(function ReasoningEffortPicker(props: { | 4134 | const ReasoningEffortPicker = memo(function ReasoningEffortPicker(props: { |
| 4135 | effort: ReasoningEffort; | 4135 | effort: CodexReasoningEffort; |
| 4136 | options: ReadonlyArray<ReasoningEffort>; | 4136 | options: ReadonlyArray<CodexReasoningEffort>; |
| 4137 | onEffortChange: (effort: ReasoningEffort) => void; | 4137 | onEffortChange: (effort: CodexReasoningEffort) => void; |
| 4138 | }) { | 4138 | }) { |
| 4139 | const defaultReasoningEffort = getDefaultReasoningEffort("codex"); | ||
| 4140 | |||
| 4139 | return ( | 4141 | return ( |
| 4140 | <Select | 4142 | <Select |
| 4141 | value={props.effort} | 4143 | value={props.effort} |
| ... | @@ -4148,7 +4150,7 @@ const ReasoningEffortPicker = memo(function ReasoningEffortPicker(props: { | ... | @@ -4148,7 +4150,7 @@ const ReasoningEffortPicker = memo(function ReasoningEffortPicker(props: { |
| 4148 | {props.options.map((effort) => ( | 4150 | {props.options.map((effort) => ( |
| 4149 | <SelectItem key={effort} value={effort}> | 4151 | <SelectItem key={effort} value={effort}> |
| 4150 | {effort} | 4152 | {effort} |
| 4151 | {effort === DEFAULT_REASONING ? " (default)" : ""} | 4153 | {effort === defaultReasoningEffort ? " (default)" : ""} |
| 4152 | </SelectItem> | 4154 | </SelectItem> |
| 4153 | ))} | 4155 | ))} |
| 4154 | </SelectPopup> | 4156 | </SelectPopup> |
packages/contracts/src/model.test.ts+27-5| ... | @@ -7,12 +7,14 @@ import { | ... | @@ -7,12 +7,14 @@ import { |
| 7 | DEFAULT_MODEL_BY_PROVIDER, | 7 | DEFAULT_MODEL_BY_PROVIDER, |
| 8 | MODEL_OPTIONS, | 8 | MODEL_OPTIONS, |
| 9 | MODEL_OPTIONS_BY_PROVIDER, | 9 | MODEL_OPTIONS_BY_PROVIDER, |
| 10 | REASONING_EFFORT_OPTIONS_BY_PROVIDER, | ||
| 11 | DEFAULT_REASONING_EFFORT_BY_PROVIDER, | ||
| 10 | getDefaultModel, | 12 | getDefaultModel, |
| 13 | getDefaultReasoningEffort, | ||
| 11 | getCursorModelFamilyOptions, | 14 | getCursorModelFamilyOptions, |
| 12 | getModelOptions, | 15 | getModelOptions, |
| 13 | getReasoningOptions, | 16 | getReasoningEffortOptions, |
| 14 | normalizeModelSlug, | 17 | normalizeModelSlug, |
| 15 | REASONING_OPTIONS, | ||
| 16 | parseCursorModelSelection, | 18 | parseCursorModelSelection, |
| 17 | resolveCursorModelFromSelection, | 19 | resolveCursorModelFromSelection, |
| 18 | resolveModelSlug, | 20 | resolveModelSlug, |
| ... | @@ -148,12 +150,32 @@ describe("cursor model selection", () => { | ... | @@ -148,12 +150,32 @@ describe("cursor model selection", () => { |
| 148 | }); | 150 | }); |
| 149 | }); | 151 | }); |
| 150 | 152 | ||
| 151 | describe("getReasoningOptions", () => { | 153 | describe("getReasoningEffortOptions", () => { |
| 152 | it("returns codex reasoning options for codex", () => { | 154 | it("returns codex reasoning options for codex", () => { |
| 153 | expect(getReasoningOptions("codex")).toEqual(REASONING_OPTIONS); | 155 | expect(getReasoningEffortOptions("codex")).toEqual( |
| 156 | REASONING_EFFORT_OPTIONS_BY_PROVIDER.codex, | ||
| 157 | ); | ||
| 154 | }); | 158 | }); |
| 155 | 159 | ||
| 156 | it("returns no reasoning options for claudeCode", () => { | 160 | it("returns no reasoning options for claudeCode", () => { |
| 157 | expect(getReasoningOptions("claudeCode")).toEqual([]); | 161 | expect(getReasoningEffortOptions("claudeCode")).toEqual([]); |
| 162 | }); | ||
| 163 | |||
| 164 | it("returns no reasoning options for cursor", () => { | ||
| 165 | expect(getReasoningEffortOptions("cursor")).toEqual([]); | ||
| 166 | }); | ||
| 167 | }); | ||
| 168 | |||
| 169 | describe("getDefaultReasoningEffort", () => { | ||
| 170 | it("returns provider-scoped defaults", () => { | ||
| 171 | expect(getDefaultReasoningEffort("codex")).toBe( | ||
| 172 | DEFAULT_REASONING_EFFORT_BY_PROVIDER.codex, | ||
| 173 | ); | ||
| 174 | expect(getDefaultReasoningEffort("claudeCode")).toBe( | ||
| 175 | DEFAULT_REASONING_EFFORT_BY_PROVIDER.claudeCode, | ||
| 176 | ); | ||
| 177 | expect(getDefaultReasoningEffort("cursor")).toBe( | ||
| 178 | DEFAULT_REASONING_EFFORT_BY_PROVIDER.cursor, | ||
| 179 | ); | ||
| 158 | }); | 180 | }); |
| 159 | }); | 181 | }); |
packages/contracts/src/model.ts+23-10| ... | @@ -491,18 +491,31 @@ export function resolveModelSlugForProvider( | ... | @@ -491,18 +491,31 @@ export function resolveModelSlugForProvider( |
| 491 | return resolveModelSlug(model, provider); | 491 | return resolveModelSlug(model, provider); |
| 492 | } | 492 | } |
| 493 | 493 | ||
| 494 | export const REASONING_OPTIONS_BY_PROVIDER = { | 494 | const CODEX_REASONING_EFFORT_OPTIONS = ["xhigh", "high", "medium", "low"] as const; |
| 495 | codex: ["xhigh", "high", "medium", "low"], | 495 | export type CodexReasoningEffort = (typeof CODEX_REASONING_EFFORT_OPTIONS)[number]; |
| 496 | |||
| 497 | export const REASONING_EFFORT_OPTIONS_BY_PROVIDER = { | ||
| 498 | codex: CODEX_REASONING_EFFORT_OPTIONS, | ||
| 496 | claudeCode: [], | 499 | claudeCode: [], |
| 497 | } as const satisfies Record<ProviderKind, readonly string[]>; | 500 | cursor: [], |
| 501 | } as const satisfies Record<ProviderKind, readonly CodexReasoningEffort[]>; | ||
| 498 | 502 | ||
| 499 | // Backward compatibility for existing Codex-only call sites. | 503 | export const DEFAULT_REASONING_EFFORT_BY_PROVIDER = { |
| 500 | export const REASONING_OPTIONS = REASONING_OPTIONS_BY_PROVIDER.codex; | 504 | codex: "high", |
| 501 | export type ReasoningEffort = (typeof REASONING_OPTIONS_BY_PROVIDER.codex)[number]; | 505 | claudeCode: null, |
| 502 | export const DEFAULT_REASONING: ReasoningEffort = "high"; | 506 | cursor: null, |
| 507 | } as const satisfies Record<ProviderKind, CodexReasoningEffort | null>; | ||
| 508 | |||
| 509 | export function getReasoningEffortOptions( | ||
| 510 | provider: ProviderKind = "codex", | ||
| 511 | ): ReadonlyArray<CodexReasoningEffort> { | ||
| 512 | return REASONING_EFFORT_OPTIONS_BY_PROVIDER[provider]; | ||
| 513 | } | ||
| 503 | 514 | ||
| 504 | export function getReasoningOptions( | 515 | export function getDefaultReasoningEffort(provider: "codex"): CodexReasoningEffort; |
| 516 | export function getDefaultReasoningEffort(provider: ProviderKind): CodexReasoningEffort | null; | ||
| 517 | export function getDefaultReasoningEffort( | ||
| 505 | provider: ProviderKind = "codex", | 518 | provider: ProviderKind = "codex", |
| 506 | ): ReadonlyArray<ReasoningEffort> { | 519 | ): CodexReasoningEffort | null { |
| 507 | return REASONING_OPTIONS_BY_PROVIDER[provider] as ReadonlyArray<ReasoningEffort>; | 520 | return DEFAULT_REASONING_EFFORT_BY_PROVIDER[provider]; |
| 508 | } | 521 | } |