| author | |
| committer | |
| log | dedcd99a9d16240327ce763b885b326aff607bdb |
| tree | 2c3a9a46a7be27672ac7762034cac825d5d70d2f |
| parent | d9c1732b2791f762bce06e4898cdb6679ce24961 |
| signature | Signed by PGP key B5690EEEBB952194 |
2 files changed, 49 insertions(+), 21 deletions(-)
apps/web/src/components/chat/TraitsPicker.test.ts+37-12| ... | ... | @@ -16,6 +16,22 @@ function fastModeDescriptor( |
| 16 | 16 | return { id: "fastMode", label: "Fast Mode", type: "boolean", currentValue }; |
| 17 | 17 | } |
| 18 | 18 | |
| 19 | function serviceTierDescriptor( | |
| 20 | currentValue: "default" | "priority" | "flex", | |
| 21 | ): Extract<ProviderOptionDescriptor, { type: "select" }> { | |
| 22 | return { | |
| 23 | id: "serviceTier", | |
| 24 | label: "Service Tier", | |
| 25 | type: "select", | |
| 26 | options: [ | |
| 27 | { id: "default", label: "Standard", isDefault: true }, | |
| 28 | { id: "priority", label: "Fast" }, | |
| 29 | { id: "flex", label: "Flex" }, | |
| 30 | ], | |
| 31 | currentValue, | |
| 32 | }; | |
| 33 | } | |
| 34 | ||
| 19 | 35 | const EFFORT = selectDescriptor( |
| 20 | 36 | "reasoningEffort", |
| 21 | 37 | [ |
| ... | ... | @@ -59,26 +75,35 @@ describe("buildTraitsTriggerDisplay", () => { |
| 59 | 75 | }); |
| 60 | 76 | }); |
| 61 | 77 | |
| 62 | it("renders Codex's Standard and Fast service tiers as fast mode", () => { | |
| 63 | const serviceTier = selectDescriptor( | |
| 64 | "serviceTier", | |
| 65 | [ | |
| 66 | { id: "default", label: "Standard", isDefault: true }, | |
| 67 | { id: "priority", label: "Fast" }, | |
| 68 | ], | |
| 69 | "default", | |
| 70 | ); | |
| 71 | ||
| 72 | expect(display([EFFORT, serviceTier])).toEqual({ | |
| 78 | it("treats Codex standard and fast service tiers as fast mode states", () => { | |
| 79 | expect(display([EFFORT, serviceTierDescriptor("default")])).toEqual({ | |
| 73 | 80 | label: "High", |
| 74 | 81 | showFastModeIcon: false, |
| 75 | 82 | }); |
| 76 | expect(display([EFFORT, { ...serviceTier, currentValue: "priority" }])).toEqual({ | |
| 83 | expect(display([EFFORT, serviceTierDescriptor("priority")])).toEqual({ | |
| 77 | 84 | label: "High", |
| 78 | 85 | showFastModeIcon: true, |
| 79 | 86 | }); |
| 80 | 87 | }); |
| 81 | 88 | |
| 89 | it("keeps other Codex service tiers in the label", () => { | |
| 90 | expect(display([EFFORT, serviceTierDescriptor("flex")])).toEqual({ | |
| 91 | label: "High · Flex", | |
| 92 | showFastModeIcon: false, | |
| 93 | }); | |
| 94 | }); | |
| 95 | ||
| 96 | it("keeps the Codex service tier readable when it is the only trait", () => { | |
| 97 | expect(display([serviceTierDescriptor("default")])).toEqual({ | |
| 98 | label: "Standard", | |
| 99 | showFastModeIcon: false, | |
| 100 | }); | |
| 101 | expect(display([serviceTierDescriptor("priority")])).toEqual({ | |
| 102 | label: "Fast", | |
| 103 | showFastModeIcon: false, | |
| 104 | }); | |
| 105 | }); | |
| 106 | ||
| 82 | 107 | it("keeps non-fastMode booleans as text labels", () => { |
| 83 | 108 | const thinking: Extract<ProviderOptionDescriptor, { type: "boolean" }> = { |
| 84 | 109 | id: "thinking", |
apps/web/src/components/chat/TraitsPicker.tsx+12-9| ... | ... | @@ -396,10 +396,11 @@ export const TraitsMenuContent = memo(function TraitsMenuContentImpl({ |
| 396 | 396 | |
| 397 | 397 | /** |
| 398 | 398 | * Build the traits trigger's text label plus whether the fast-mode bolt should |
| 399 | * render. Fast mode is a lightning bolt when on and nothing at all when off — | |
| 400 | * "Normal" is the near-universal case and isn't worth the horizontal space. The | |
| 401 | * one exception is when fast mode is the only trait, where a bare bolt (or bare | |
| 402 | * chevron) would leave the trigger unreadable. | |
| 399 | * render. Claude and Cursor expose fast mode as a boolean, while Codex exposes | |
| 400 | * it through the Standard/Fast service tiers. In either form, fast mode is a | |
| 401 | * lightning bolt when on and nothing at all when off. The one exception is when | |
| 402 | * fast mode is the only trait, where a bare bolt (or bare chevron) would leave | |
| 403 | * the trigger unreadable. | |
| 403 | 404 | */ |
| 404 | 405 | export function buildTraitsTriggerDisplay(input: { |
| 405 | 406 | provider: ProviderDriverKind; |
| ... | ... | @@ -407,13 +408,13 @@ export function buildTraitsTriggerDisplay(input: { |
| 407 | 408 | primarySelectDescriptorId: string | null; |
| 408 | 409 | ultrathinkPromptControlled: boolean; |
| 409 | 410 | }): { label: string; showFastModeIcon: boolean } { |
| 410 | let hasFastMode = false; | |
| 411 | let fastModeFallbackLabel: string | null = null; | |
| 411 | 412 | let fastModeEnabled = false; |
| 412 | 413 | const labels: Array<string> = []; |
| 413 | 414 | for (const descriptor of input.descriptors) { |
| 414 | 415 | if (descriptor.id === "fastMode" && descriptor.type === "boolean") { |
| 415 | hasFastMode = true; | |
| 416 | 416 | fastModeEnabled = descriptor.currentValue === true; |
| 417 | fastModeFallbackLabel = fastModeEnabled ? "Fast" : "Normal"; | |
| 417 | 418 | continue; |
| 418 | 419 | } |
| 419 | 420 | if ( |
| ... | ... | @@ -424,8 +425,10 @@ export function buildTraitsTriggerDisplay(input: { |
| 424 | 425 | const currentValue = getProviderOptionCurrentValue(descriptor); |
| 425 | 426 | const fastTier = descriptor.options.find(({ label }) => label === "Fast"); |
| 426 | 427 | if (fastTier && (currentValue === "default" || currentValue === fastTier.id)) { |
| 427 | hasFastMode = true; | |
| 428 | 428 | fastModeEnabled = currentValue === fastTier.id; |
| 429 | fastModeFallbackLabel = | |
| 430 | descriptor.options.find(({ id }) => id === currentValue)?.label ?? | |
| 431 | (fastModeEnabled ? "Fast" : "Normal"); | |
| 429 | 432 | continue; |
| 430 | 433 | } |
| 431 | 434 | } |
| ... | ... | @@ -443,8 +446,8 @@ export function buildTraitsTriggerDisplay(input: { |
| 443 | 446 | // Only fall back to text when fast mode is genuinely the sole trait. Keying |
| 444 | 447 | // off an empty label list alone would also catch descriptors that resolved to |
| 445 | 448 | // no label at all, printing a bogus "Normal" for a model without fast mode. |
| 446 | if (labels.length === 0 && hasFastMode) { | |
| 447 | return { label: fastModeEnabled ? "Fast" : "Normal", showFastModeIcon: false }; | |
| 449 | if (labels.length === 0 && fastModeFallbackLabel !== null) { | |
| 450 | return { label: fastModeFallbackLabel, showFastModeIcon: false }; | |
| 448 | 451 | } |
| 449 | 452 | return { label: labels.join(" · "), showFastModeIcon: fastModeEnabled }; |
| 450 | 453 | } |