diff --git a/apps/web/src/components/chat/TraitsPicker.test.ts b/apps/web/src/components/chat/TraitsPicker.test.ts index 0cb199d64ba..b457f515ff7 100644 --- a/apps/web/src/components/chat/TraitsPicker.test.ts +++ b/apps/web/src/components/chat/TraitsPicker.test.ts @@ -35,39 +35,48 @@ const CONTEXT_WINDOW = selectDescriptor( const CODEX = ProviderDriverKind.make("codex"); -function display(descriptors: ReadonlyArray, fastModeEnabled: boolean) { +function display(descriptors: ReadonlyArray) { return buildTraitsTriggerDisplay({ provider: CODEX, descriptors, primarySelectDescriptorId: "reasoningEffort", ultrathinkPromptControlled: false, - fastModeEnabled, }); } describe("buildTraitsTriggerDisplay", () => { it("omits fast mode from the label entirely when it is off", () => { - expect(display([EFFORT, fastModeDescriptor(false), CONTEXT_WINDOW], false)).toEqual({ + expect(display([EFFORT, fastModeDescriptor(false), CONTEXT_WINDOW])).toEqual({ label: "High · 1M", showFastModeIcon: false, }); }); it("shows the bolt instead of a text label when fast mode is on", () => { - expect(display([EFFORT, fastModeDescriptor(true), CONTEXT_WINDOW], true)).toEqual({ + expect(display([EFFORT, fastModeDescriptor(true), CONTEXT_WINDOW])).toEqual({ label: "High · 1M", showFastModeIcon: true, }); }); - it("omits Codex's default Standard tier beside reasoning", () => { + it("renders Codex's Standard and Fast service tiers as fast mode", () => { const serviceTier = selectDescriptor( "serviceTier", - [{ id: "default", label: "Standard", isDefault: true }], + [ + { id: "default", label: "Standard", isDefault: true }, + { id: "priority", label: "Fast" }, + ], "default", ); - expect(display([EFFORT, serviceTier], false).label).toBe("High"); + expect(display([EFFORT, serviceTier])).toEqual({ + label: "High", + showFastModeIcon: false, + }); + expect(display([EFFORT, { ...serviceTier, currentValue: "priority" }])).toEqual({ + label: "High", + showFastModeIcon: true, + }); }); it("keeps non-fastMode booleans as text labels", () => { @@ -77,18 +86,18 @@ describe("buildTraitsTriggerDisplay", () => { type: "boolean", currentValue: true, }; - expect(display([EFFORT, thinking], false)).toEqual({ + expect(display([EFFORT, thinking])).toEqual({ label: "High · Thinking On", showFastModeIcon: false, }); }); it("falls back to a text label when fast mode is the only trait", () => { - expect(display([fastModeDescriptor(true)], true)).toEqual({ + expect(display([fastModeDescriptor(true)])).toEqual({ label: "Fast", showFastModeIcon: false, }); - expect(display([fastModeDescriptor(false)], false)).toEqual({ + expect(display([fastModeDescriptor(false)])).toEqual({ label: "Normal", showFastModeIcon: false, }); @@ -107,7 +116,7 @@ describe("buildTraitsTriggerDisplay", () => { { id: "high", label: "High" }, ], }; - expect(display([unresolved], false)).toEqual({ label: "", showFastModeIcon: false }); + expect(display([unresolved])).toEqual({ label: "", showFastModeIcon: false }); }); it("still renders the prompt-controlled ultrathink label alongside the bolt", () => { @@ -117,7 +126,6 @@ describe("buildTraitsTriggerDisplay", () => { descriptors: [EFFORT, fastModeDescriptor(true)], primarySelectDescriptorId: "reasoningEffort", ultrathinkPromptControlled: true, - fastModeEnabled: true, }), ).toEqual({ label: "Ultrathink", showFastModeIcon: true }); }); diff --git a/apps/web/src/components/chat/TraitsPicker.tsx b/apps/web/src/components/chat/TraitsPicker.tsx index f95f3a4c08a..a9f910ec064 100644 --- a/apps/web/src/components/chat/TraitsPicker.tsx +++ b/apps/web/src/components/chat/TraitsPicker.tsx @@ -134,8 +134,6 @@ function getSelectedTraits( : getDescriptorStringValue(primarySelectDescriptor)) ?? null; const thinkingEnabled = typeof thinkingDescriptor?.currentValue === "boolean" ? thinkingDescriptor.currentValue : null; - const fastModeEnabled = - typeof fastModeDescriptor?.currentValue === "boolean" ? fastModeDescriptor.currentValue : false; const contextWindow = getDescriptorStringValue(contextWindowDescriptor); const selectedAgent = getDescriptorStringValue(agentDescriptor); const selectedAgentLabel = agentDescriptor @@ -154,7 +152,6 @@ function getSelectedTraits( thinkingDescriptor, effort, thinkingEnabled, - fastModeEnabled, contextWindow, ultrathinkPromptControlled, ultrathinkInBodyText, @@ -392,24 +389,26 @@ export function buildTraitsTriggerDisplay(input: { descriptors: ReadonlyArray; primarySelectDescriptorId: string | null; ultrathinkPromptControlled: boolean; - fastModeEnabled: boolean; }): { label: string; showFastModeIcon: boolean } { let hasFastMode = false; + let fastModeEnabled = false; const labels: Array = []; for (const descriptor of input.descriptors) { if (descriptor.id === "fastMode" && descriptor.type === "boolean") { hasFastMode = true; + fastModeEnabled = descriptor.currentValue === true; continue; } if ( input.provider === "codex" && descriptor.id === "serviceTier" && - descriptor.type === "select" && - input.descriptors.some(({ id }) => id === "reasoningEffort") + descriptor.type === "select" ) { const currentValue = getProviderOptionCurrentValue(descriptor); - const currentOption = descriptor.options.find((option) => option.id === currentValue); - if (currentOption?.id === "default" && currentOption.isDefault === true) { + const fastTier = descriptor.options.find(({ label }) => label === "Fast"); + if (fastTier && (currentValue === "default" || currentValue === fastTier.id)) { + hasFastMode = true; + fastModeEnabled = currentValue === fastTier.id; continue; } } @@ -428,9 +427,9 @@ export function buildTraitsTriggerDisplay(input: { // off an empty label list alone would also catch descriptors that resolved to // no label at all, printing a bogus "Normal" for a model without fast mode. if (labels.length === 0 && hasFastMode) { - return { label: input.fastModeEnabled ? "Fast" : "Normal", showFastModeIcon: false }; + return { label: fastModeEnabled ? "Fast" : "Normal", showFastModeIcon: false }; } - return { label: labels.join(" · "), showFastModeIcon: input.fastModeEnabled }; + return { label: labels.join(" · "), showFastModeIcon: fastModeEnabled }; } export const TraitsPicker = memo(function TraitsPicker({ @@ -447,7 +446,7 @@ export const TraitsPicker = memo(function TraitsPicker({ ...persistence }: TraitsMenuContentProps & TraitsPersistence) { const [isMenuOpen, setIsMenuOpen] = useState(false); - const { descriptors, primarySelectDescriptor, ultrathinkPromptControlled, fastModeEnabled } = + const { descriptors, primarySelectDescriptor, ultrathinkPromptControlled } = getTraitsSectionVisibility({ provider, models, @@ -474,7 +473,6 @@ export const TraitsPicker = memo(function TraitsPicker({ descriptors, primarySelectDescriptorId: primarySelectDescriptor?.id ?? null, ultrathinkPromptControlled, - fastModeEnabled, }); const fastModeIcon = showFastModeIcon ? ( <>