authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-07-30 02:15:48-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-07-30 02:15:48-07:00
log9146ed2f49a094aa8afee7d3a66987e2a82aaaf6
tree1998debaf5e0b1e6fd9a96d702bbba344419ece1
parentf0f16e4f6e8e923dcb17fc1c25d4fb1e442f80a5
signature Signed by PGP key B5690EEEBB952194

fix(web): show Codex fast mode as a bolt (#4947)


2 files changed, 30 insertions(+), 24 deletions(-)

apps/web/src/components/chat/TraitsPicker.test.ts+20-12
......@@ -35,39 +35,48 @@ const CONTEXT_WINDOW = selectDescriptor(
3535
3636const CODEX = ProviderDriverKind.make("codex");
3737
38function display(descriptors: ReadonlyArray<ProviderOptionDescriptor>, fastModeEnabled: boolean) {
38function display(descriptors: ReadonlyArray<ProviderOptionDescriptor>) {
3939 return buildTraitsTriggerDisplay({
4040 provider: CODEX,
4141 descriptors,
4242 primarySelectDescriptorId: "reasoningEffort",
4343 ultrathinkPromptControlled: false,
44 fastModeEnabled,
4544 });
4645}
4746
4847describe("buildTraitsTriggerDisplay", () => {
4948 it("omits fast mode from the label entirely when it is off", () => {
50 expect(display([EFFORT, fastModeDescriptor(false), CONTEXT_WINDOW], false)).toEqual({
49 expect(display([EFFORT, fastModeDescriptor(false), CONTEXT_WINDOW])).toEqual({
5150 label: "High · 1M",
5251 showFastModeIcon: false,
5352 });
5453 });
5554
5655 it("shows the bolt instead of a text label when fast mode is on", () => {
57 expect(display([EFFORT, fastModeDescriptor(true), CONTEXT_WINDOW], true)).toEqual({
56 expect(display([EFFORT, fastModeDescriptor(true), CONTEXT_WINDOW])).toEqual({
5857 label: "High · 1M",
5958 showFastModeIcon: true,
6059 });
6160 });
6261
63 it("omits Codex's default Standard tier beside reasoning", () => {
62 it("renders Codex's Standard and Fast service tiers as fast mode", () => {
6463 const serviceTier = selectDescriptor(
6564 "serviceTier",
66 [{ id: "default", label: "Standard", isDefault: true }],
65 [
66 { id: "default", label: "Standard", isDefault: true },
67 { id: "priority", label: "Fast" },
68 ],
6769 "default",
6870 );
6971
70 expect(display([EFFORT, serviceTier], false).label).toBe("High");
72 expect(display([EFFORT, serviceTier])).toEqual({
73 label: "High",
74 showFastModeIcon: false,
75 });
76 expect(display([EFFORT, { ...serviceTier, currentValue: "priority" }])).toEqual({
77 label: "High",
78 showFastModeIcon: true,
79 });
7180 });
7281
7382 it("keeps non-fastMode booleans as text labels", () => {
......@@ -77,18 +86,18 @@ describe("buildTraitsTriggerDisplay", () => {
7786 type: "boolean",
7887 currentValue: true,
7988 };
80 expect(display([EFFORT, thinking], false)).toEqual({
89 expect(display([EFFORT, thinking])).toEqual({
8190 label: "High · Thinking On",
8291 showFastModeIcon: false,
8392 });
8493 });
8594
8695 it("falls back to a text label when fast mode is the only trait", () => {
87 expect(display([fastModeDescriptor(true)], true)).toEqual({
96 expect(display([fastModeDescriptor(true)])).toEqual({
8897 label: "Fast",
8998 showFastModeIcon: false,
9099 });
91 expect(display([fastModeDescriptor(false)], false)).toEqual({
100 expect(display([fastModeDescriptor(false)])).toEqual({
92101 label: "Normal",
93102 showFastModeIcon: false,
94103 });
......@@ -107,7 +116,7 @@ describe("buildTraitsTriggerDisplay", () => {
107116 { id: "high", label: "High" },
108117 ],
109118 };
110 expect(display([unresolved], false)).toEqual({ label: "", showFastModeIcon: false });
119 expect(display([unresolved])).toEqual({ label: "", showFastModeIcon: false });
111120 });
112121
113122 it("still renders the prompt-controlled ultrathink label alongside the bolt", () => {
......@@ -117,7 +126,6 @@ describe("buildTraitsTriggerDisplay", () => {
117126 descriptors: [EFFORT, fastModeDescriptor(true)],
118127 primarySelectDescriptorId: "reasoningEffort",
119128 ultrathinkPromptControlled: true,
120 fastModeEnabled: true,
121129 }),
122130 ).toEqual({ label: "Ultrathink", showFastModeIcon: true });
123131 });
apps/web/src/components/chat/TraitsPicker.tsx+10-12
......@@ -134,8 +134,6 @@ function getSelectedTraits(
134134 : getDescriptorStringValue(primarySelectDescriptor)) ?? null;
135135 const thinkingEnabled =
136136 typeof thinkingDescriptor?.currentValue === "boolean" ? thinkingDescriptor.currentValue : null;
137 const fastModeEnabled =
138 typeof fastModeDescriptor?.currentValue === "boolean" ? fastModeDescriptor.currentValue : false;
139137 const contextWindow = getDescriptorStringValue(contextWindowDescriptor);
140138 const selectedAgent = getDescriptorStringValue(agentDescriptor);
141139 const selectedAgentLabel = agentDescriptor
......@@ -154,7 +152,6 @@ function getSelectedTraits(
154152 thinkingDescriptor,
155153 effort,
156154 thinkingEnabled,
157 fastModeEnabled,
158155 contextWindow,
159156 ultrathinkPromptControlled,
160157 ultrathinkInBodyText,
......@@ -392,24 +389,26 @@ export function buildTraitsTriggerDisplay(input: {
392389 descriptors: ReadonlyArray<ProviderOptionDescriptor>;
393390 primarySelectDescriptorId: string | null;
394391 ultrathinkPromptControlled: boolean;
395 fastModeEnabled: boolean;
396392}): { label: string; showFastModeIcon: boolean } {
397393 let hasFastMode = false;
394 let fastModeEnabled = false;
398395 const labels: Array<string> = [];
399396 for (const descriptor of input.descriptors) {
400397 if (descriptor.id === "fastMode" && descriptor.type === "boolean") {
401398 hasFastMode = true;
399 fastModeEnabled = descriptor.currentValue === true;
402400 continue;
403401 }
404402 if (
405403 input.provider === "codex" &&
406404 descriptor.id === "serviceTier" &&
407 descriptor.type === "select" &&
408 input.descriptors.some(({ id }) => id === "reasoningEffort")
405 descriptor.type === "select"
409406 ) {
410407 const currentValue = getProviderOptionCurrentValue(descriptor);
411 const currentOption = descriptor.options.find((option) => option.id === currentValue);
412 if (currentOption?.id === "default" && currentOption.isDefault === true) {
408 const fastTier = descriptor.options.find(({ label }) => label === "Fast");
409 if (fastTier && (currentValue === "default" || currentValue === fastTier.id)) {
410 hasFastMode = true;
411 fastModeEnabled = currentValue === fastTier.id;
413412 continue;
414413 }
415414 }
......@@ -428,9 +427,9 @@ export function buildTraitsTriggerDisplay(input: {
428427 // off an empty label list alone would also catch descriptors that resolved to
429428 // no label at all, printing a bogus "Normal" for a model without fast mode.
430429 if (labels.length === 0 && hasFastMode) {
431 return { label: input.fastModeEnabled ? "Fast" : "Normal", showFastModeIcon: false };
430 return { label: fastModeEnabled ? "Fast" : "Normal", showFastModeIcon: false };
432431 }
433 return { label: labels.join(" · "), showFastModeIcon: input.fastModeEnabled };
432 return { label: labels.join(" · "), showFastModeIcon: fastModeEnabled };
434433}
435434
436435export const TraitsPicker = memo(function TraitsPicker({
......@@ -447,7 +446,7 @@ export const TraitsPicker = memo(function TraitsPicker({
447446 ...persistence
448447}: TraitsMenuContentProps & TraitsPersistence) {
449448 const [isMenuOpen, setIsMenuOpen] = useState(false);
450 const { descriptors, primarySelectDescriptor, ultrathinkPromptControlled, fastModeEnabled } =
449 const { descriptors, primarySelectDescriptor, ultrathinkPromptControlled } =
451450 getTraitsSectionVisibility({
452451 provider,
453452 models,
......@@ -474,7 +473,6 @@ export const TraitsPicker = memo(function TraitsPicker({
474473 descriptors,
475474 primarySelectDescriptorId: primarySelectDescriptor?.id ?? null,
476475 ultrathinkPromptControlled,
477 fastModeEnabled,
478476 });
479477 const fastModeIcon = showFastModeIcon ? (
480478 <>