Skip to content

Commit d64ed5d

Browse files
🤖 feat: add GPT-5.1-Codex-Max model with xhigh reasoning level (#933)
Add support for OpenAI's `gpt-5.1-codex-max` model with the new `xhigh` (Extra High) thinking level. ## Changes - Allow `xhigh` thinking level end-to-end (types, schemas, UI policy, enforcement) - Normalize model detection so `gpt-5.1-codex-max` works with/without the `openai:` prefix or `codex-max` alias - Update thinking slider visuals to clamp extra levels to the strongest glow - Refresh LiteLLM model data (`models.json`) to include codex-max - Document codex-max/XHIGH support in `docs/models.md` - Add tests covering bare/alias codex-max and policy enforcement fallbacks ## Behavior - `xhigh` only surfaces for codex-max; other models fall back to their allowed max (e.g., medium or high) - Command palette may show all levels, but backend/UI enforce policy via `enforceThinkingPolicy` ## Validation - `make typecheck` - `bun test src/browser/utils/thinking/policy.test.ts` - `bun run scripts/update_models.ts` (models.json refreshed) --- _Generated with `mux`_ --------- Signed-off-by: Thomas Kosiewski <tk@coder.com> Co-authored-by: Ammar <ammar+ai@ammar.io>
1 parent 44580b3 commit d64ed5d

File tree

14 files changed

+3883
-2654
lines changed

14 files changed

+3883
-2654
lines changed

docs/models.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ GPT-5 family of models:
4747
- `openai:gpt-5.1`
4848
- `openai:gpt-5-pro`
4949
- `openai:gpt-5.1-codex`
50+
- `openai:gpt-5.1-codex-max` — supports the XHIGH (extra high) thinking level; aliases: `gpt-5.1-codex-max`, `codex-max`
5051
- `openai:gpt-5.1-codex-mini`
5152

5253
#### Google (Cloud)

src/browser/components/ChatInput/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -515,6 +515,7 @@ export const ChatInput: React.FC<ChatInputProps> = (props) => {
515515
low: "Low — adds light reasoning",
516516
medium: "Medium — balanced reasoning",
517517
high: "High — maximum reasoning depth",
518+
xhigh: "Extra High — extended deep thinking",
518519
};
519520

520521
setToast({

src/browser/components/ThinkingSlider.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,14 @@ export const ThinkingSliderComponent: React.FC<ThinkingControlProps> = ({ modelS
123123
const sliderValue = currentIndex === -1 ? 0 : currentIndex;
124124
const maxSteps = allowed.length - 1;
125125

126-
// For styling, we still want to map to the "global" intensity 0-3
127-
// to keep colors consistent (e.g. "high" is always purple, even if it's step 1 of 2)
128-
const globalLevelIndex = ["off", "low", "medium", "high"].indexOf(thinkingLevel);
129-
const visualValue = globalLevelIndex === -1 ? 0 : globalLevelIndex;
126+
// Map levels to visual intensity indices (0-3) so colors/glow stay consistent
127+
// Levels outside the base 4 (e.g., xhigh) map to the strongest intensity
128+
const baseVisualOrder: ThinkingLevel[] = ["off", "low", "medium", "high"];
129+
const visualValue = (() => {
130+
const idx = baseVisualOrder.indexOf(thinkingLevel);
131+
if (idx >= 0) return idx;
132+
return baseVisualOrder.length - 1; // clamp extras (e.g., xhigh) to strongest glow
133+
})();
130134

131135
const sliderStyles = getSliderStyles(visualValue, isHovering);
132136
const textStyle = getTextStyle(visualValue);

src/browser/utils/commands/sources.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export interface BuildSourcesParams {
5050
onOpenSettings?: (section?: string) => void;
5151
}
5252

53-
const THINKING_LEVELS: ThinkingLevel[] = ["off", "low", "medium", "high"];
53+
const THINKING_LEVELS: ThinkingLevel[] = ["off", "low", "medium", "high", "xhigh"];
5454

5555
/**
5656
* Command palette section names
@@ -431,6 +431,7 @@ export function buildCoreSources(p: BuildSourcesParams): Array<() => CommandActi
431431
low: "Low — add a bit of reasoning",
432432
medium: "Medium — balanced reasoning",
433433
high: "High — maximum reasoning depth",
434+
xhigh: "Extra High — extended deep thinking",
434435
};
435436
const currentLevel = p.getThinkingLevel(workspaceId);
436437

src/browser/utils/thinking/policy.test.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,56 @@ import { describe, expect, test } from "bun:test";
22
import { getThinkingPolicyForModel, enforceThinkingPolicy } from "./policy";
33

44
describe("getThinkingPolicyForModel", () => {
5+
test("returns 5 levels including xhigh for gpt-5.1-codex-max", () => {
6+
expect(getThinkingPolicyForModel("openai:gpt-5.1-codex-max")).toEqual([
7+
"off",
8+
"low",
9+
"medium",
10+
"high",
11+
"xhigh",
12+
]);
13+
});
14+
15+
test("returns 5 levels for gpt-5.1-codex-max with version suffix", () => {
16+
expect(getThinkingPolicyForModel("openai:gpt-5.1-codex-max-2025-12-01")).toEqual([
17+
"off",
18+
"low",
19+
"medium",
20+
"high",
21+
"xhigh",
22+
]);
23+
});
24+
25+
test("returns 5 levels for bare gpt-5.1-codex-max without prefix", () => {
26+
expect(getThinkingPolicyForModel("gpt-5.1-codex-max")).toEqual([
27+
"off",
28+
"low",
29+
"medium",
30+
"high",
31+
"xhigh",
32+
]);
33+
});
34+
35+
test("returns 5 levels for codex-max alias", () => {
36+
expect(getThinkingPolicyForModel("codex-max")).toEqual([
37+
"off",
38+
"low",
39+
"medium",
40+
"high",
41+
"xhigh",
42+
]);
43+
});
44+
45+
test("returns 5 levels for gpt-5.1-codex-max with whitespace after colon", () => {
46+
expect(getThinkingPolicyForModel("openai: gpt-5.1-codex-max")).toEqual([
47+
"off",
48+
"low",
49+
"medium",
50+
"high",
51+
"xhigh",
52+
]);
53+
});
54+
555
test("returns single HIGH for gpt-5-pro base model", () => {
656
expect(getThinkingPolicyForModel("openai:gpt-5-pro")).toEqual(["high"]);
757
});
@@ -111,6 +161,32 @@ describe("enforceThinkingPolicy", () => {
111161
expect(enforceThinkingPolicy("anthropic:claude-opus-4-5-20251101", "off")).toBe("off");
112162
});
113163
});
164+
165+
describe("GPT-5.1-Codex-Max (5 levels including xhigh)", () => {
166+
test("allows all 5 levels including xhigh", () => {
167+
expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max", "off")).toBe("off");
168+
expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max", "low")).toBe("low");
169+
expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max", "medium")).toBe("medium");
170+
expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max", "high")).toBe("high");
171+
expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max", "xhigh")).toBe("xhigh");
172+
});
173+
174+
test("allows xhigh for versioned model", () => {
175+
expect(enforceThinkingPolicy("openai:gpt-5.1-codex-max-2025-12-01", "xhigh")).toBe("xhigh");
176+
});
177+
});
178+
179+
describe("xhigh fallback for non-codex-max models", () => {
180+
test("falls back to medium when xhigh requested on standard model", () => {
181+
// Standard models don't support xhigh, so fall back to medium (preferred fallback)
182+
expect(enforceThinkingPolicy("anthropic:claude-opus-4-5", "xhigh")).toBe("medium");
183+
});
184+
185+
test("falls back to high when xhigh requested on gpt-5-pro", () => {
186+
// gpt-5-pro only supports high, so xhigh falls back to high
187+
expect(enforceThinkingPolicy("openai:gpt-5-pro", "xhigh")).toBe("high");
188+
});
189+
});
114190
});
115191

116192
// Note: Tests for invalid levels removed - TypeScript type system prevents invalid

src/browser/utils/thinking/policy.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,35 @@ export type ThinkingPolicy = readonly ThinkingLevel[];
2424
* Returns the thinking policy for a given model.
2525
*
2626
* Rules:
27+
* - openai:gpt-5.1-codex-max → ["off", "low", "medium", "high", "xhigh"] (5 levels including xhigh)
2728
* - openai:gpt-5-pro → ["high"] (only supported level)
2829
* - gemini-3 → ["low", "high"] (thinking level only)
29-
* - default → ["off", "low", "medium", "high"] (all levels selectable)
30+
* - default → ["off", "low", "medium", "high"] (standard 4 levels)
3031
*
3132
* Tolerates version suffixes (e.g., gpt-5-pro-2025-10-06).
3233
* Does NOT match gpt-5-pro-mini (uses negative lookahead).
3334
*/
3435
export function getThinkingPolicyForModel(modelString: string): ThinkingPolicy {
35-
// Match "openai:" followed by optional whitespace and "gpt-5-pro"
36-
// Allow version suffixes like "-2025-10-06" but NOT "-mini" or other text suffixes
37-
if (/^openai:\s*gpt-5-pro(?!-[a-z])/.test(modelString)) {
36+
// Normalize to be robust to provider prefixes, whitespace, and version suffixes
37+
const normalized = modelString.trim().toLowerCase();
38+
const withoutPrefix = normalized.replace(/^[a-z0-9_-]+:\s*/, "");
39+
40+
// GPT-5.1-Codex-Max supports 5 reasoning levels including xhigh (Extra High)
41+
if (withoutPrefix.startsWith("gpt-5.1-codex-max") || withoutPrefix.startsWith("codex-max")) {
42+
return ["off", "low", "medium", "high", "xhigh"];
43+
}
44+
45+
// gpt-5-pro (not mini) with optional version suffix
46+
if (/^gpt-5-pro(?!-[a-z])/.test(withoutPrefix)) {
3847
return ["high"];
3948
}
4049

4150
// Gemini 3 Pro only supports "low" and "high" reasoning levels
42-
if (modelString.includes("gemini-3")) {
51+
if (withoutPrefix.includes("gemini-3")) {
4352
return ["low", "high"];
4453
}
4554

46-
// Default policy: all levels selectable
55+
// Default policy: standard 4 levels (xhigh only for codex-max)
4756
return ["off", "low", "medium", "high"];
4857
}
4958

src/common/constants/knownModels.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ const MODEL_DEFINITIONS = {
7070
providerModelId: "gpt-5.1-codex-mini",
7171
aliases: ["codex-mini"],
7272
},
73+
GPT_CODEX_MAX: {
74+
provider: "openai",
75+
providerModelId: "gpt-5.1-codex-max",
76+
aliases: ["codex-max"],
77+
warm: true,
78+
tokenizerOverride: "openai/gpt-5",
79+
},
7380
GEMINI_3_PRO: {
7481
provider: "google",
7582
providerModelId: "gemini-3-pro-preview",

src/common/orpc/schemas/stream.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ export const ToolPolicySchema = z.array(ToolPolicyFilterSchema).meta({
313313
// SendMessage options
314314
export const SendMessageOptionsSchema = z.object({
315315
editMessageId: z.string().optional(),
316-
thinkingLevel: z.enum(["off", "low", "medium", "high"]).optional(),
316+
thinkingLevel: z.enum(["off", "low", "medium", "high", "xhigh"]).optional(),
317317
model: z.string("No model specified"),
318318
toolPolicy: ToolPolicySchema.optional(),
319319
additionalSystemInstructions: z.string().optional(),

src/common/orpc/schemas/telemetry.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ const FrontendPlatformInfoSchema = z.object({
2929
});
3030

3131
// Thinking level enum (matches payload.ts TelemetryThinkingLevel)
32-
const TelemetryThinkingLevelSchema = z.enum(["off", "low", "medium", "high"]);
32+
const TelemetryThinkingLevelSchema = z.enum(["off", "low", "medium", "high", "xhigh"]);
3333

3434
// Command type enum (matches payload.ts TelemetryCommandType)
3535
const TelemetryCommandTypeSchema = z.enum([

src/common/telemetry/payload.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export interface WorkspaceSwitchedPayload {
8686
/**
8787
* Thinking level for extended thinking feature
8888
*/
89-
export type TelemetryThinkingLevel = "off" | "low" | "medium" | "high";
89+
export type TelemetryThinkingLevel = "off" | "low" | "medium" | "high" | "xhigh";
9090

9191
/**
9292
* Chat/AI interaction events

0 commit comments

Comments
 (0)