Skip to content

Commit 2228e77

Browse files
committed
fix: harden gpt-5.4 matching and close review gaps
- make gpt-5.4/pro family matching boundary-aware in runtime + prompt family detection - clarify docs for none->low coercion across codex/pro families - add regression tests for gpt-5.40/gpt-5.4pro non-matches and gpt-5.4-pro-none coverage
1 parent 44b8a2c commit 2228e77

File tree

6 files changed

+21
-13
lines changed

6 files changed

+21
-13
lines changed

docs/configuration.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ controls how much thinking the model does.
4848
the shipped config templates include 21 presets and do not add optional IDs by default. add `gpt-5.4-pro` and/or `gpt-5.3-codex-spark` manually only for entitled workspaces.
4949

5050
what they mean:
51-
- `none` - no reasoning phase (base models only, auto-converts to `low` for codex)
51+
- `none` - no reasoning phase (base models only; auto-converts to `low` for codex/pro families, including `gpt-5-codex` and `gpt-5.4-pro`)
5252
- `low` - light reasoning, fastest
5353
- `medium` - balanced (default)
5454
- `high` - deep reasoning

lib/prompts/codex.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export function getModelFamily(normalizedModel: string): ModelFamily {
120120
) {
121121
return "codex";
122122
}
123-
if (normalizedModel.includes("gpt-5.4") || normalizedModel.includes("gpt 5.4")) {
123+
if (/\bgpt(?:-| )5\.4(?:\b|[- ])/i.test(normalizedModel)) {
124124
return "gpt-5.4";
125125
}
126126
if (normalizedModel.includes("gpt-5.2")) {

lib/request/request-transformer.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,12 @@ export function normalizeModel(model: string | undefined): string {
8080
}
8181

8282
// 4. GPT-5.4 Pro (optional/manual model)
83-
if (
84-
normalized.includes("gpt-5.4-pro") ||
85-
normalized.includes("gpt 5.4 pro")
86-
) {
83+
if (/\bgpt(?:-| )5\.4(?:-| )pro(?:\b|[- ])/.test(normalized)) {
8784
return "gpt-5.4-pro";
8885
}
8986

9087
// 5. GPT-5.4 (general purpose)
91-
if (normalized.includes("gpt-5.4") || normalized.includes("gpt 5.4")) {
88+
if (/\bgpt(?:-| )5\.4(?:\b|[- ])/.test(normalized)) {
9289
return "gpt-5.4";
9390
}
9491

test/codex-prompts.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,10 @@ describe("Codex Prompts Module", () => {
8484
expect(getModelFamily("gpt 5.4 pro")).toBe("gpt-5.4");
8585
});
8686

87+
it("should not classify gpt-5.40 style names as gpt-5.4 family", () => {
88+
expect(getModelFamily("gpt-5.40")).toBe("gpt-5.1");
89+
});
90+
8791
it("should detect gpt-5.3-codex-spark", () => {
8892
expect(getModelFamily("gpt-5.3-codex-spark")).toBe("gpt-5-codex");
8993
});

test/gpt54-models.test.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ describe("GPT-5.4 Model Support", () => {
6363

6464
it("should normalize all gpt-5.4-pro reasoning effort variants", () => {
6565
const variants = [
66+
"gpt-5.4-pro-none",
6667
"gpt-5.4-pro-low",
6768
"gpt-5.4-pro-medium",
6869
"gpt-5.4-pro-high",
@@ -93,6 +94,7 @@ describe("GPT-5.4 Model Support", () => {
9394

9495
it("should handle gpt-5.4-pro in MODEL_MAP", () => {
9596
expect(MODEL_MAP["gpt-5.4-pro"]).toBe("gpt-5.4-pro");
97+
expect(MODEL_MAP["gpt-5.4-pro-none"]).toBe("gpt-5.4-pro");
9698
expect(MODEL_MAP["gpt-5.4-pro-low"]).toBe("gpt-5.4-pro");
9799
expect(MODEL_MAP["gpt-5.4-pro-medium"]).toBe("gpt-5.4-pro");
98100
expect(MODEL_MAP["gpt-5.4-pro-high"]).toBe("gpt-5.4-pro");
@@ -203,10 +205,10 @@ describe("GPT-5.4 Model Support", () => {
203205
expect(normalizeModel("gpt_5_4")).toBe("gpt-5.1");
204206
});
205207

206-
it("should match gpt-5.4x patterns as gpt-5.4", () => {
207-
// gpt-5.40 and gpt-5.44 contain "gpt-5.4" so they match gpt-5.4 pattern
208-
expect(normalizeModel("gpt-5.40")).toBe("gpt-5.4");
209-
expect(normalizeModel("gpt-5.44")).toBe("gpt-5.4");
208+
it("should not match gpt-5.4x patterns as gpt-5.4", () => {
209+
// Boundary-aware matching prevents accidental family routing from nearby version strings.
210+
expect(normalizeModel("gpt-5.40")).toBe("gpt-5.1");
211+
expect(normalizeModel("gpt-5.44")).toBe("gpt-5.1");
210212
});
211213

212214
it("should handle empty/undefined model names defaulting to gpt-5.1", () => {
@@ -283,7 +285,7 @@ describe("GPT-5.4 Model Support", () => {
283285
const gpt54ProVariants = Object.keys(MODEL_MAP).filter((key) =>
284286
key.startsWith("gpt-5.4-pro")
285287
);
286-
expect(gpt54ProVariants.length).toBeGreaterThanOrEqual(5); // base + 4 effort levels (no 'none')
288+
expect(gpt54ProVariants.length).toBeGreaterThanOrEqual(6); // base + none/low/medium/high/xhigh
287289
});
288290

289291
it("should ensure all gpt-5.4 variants map to correct normalized name", () => {
@@ -297,4 +299,4 @@ describe("GPT-5.4 Model Support", () => {
297299
}
298300
});
299301
});
300-
});
302+
});

test/request-transformer.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,11 @@ describe('Request Transformer Module', () => {
156156
expect(normalizeModel('GPT-5.3-CODEX-SPARK')).toBe('gpt-5-codex');
157157
});
158158

159+
it('should not misclassify unrelated gpt-5.4x model strings', async () => {
160+
expect(normalizeModel('gpt-5.40')).toBe('gpt-5.1');
161+
expect(normalizeModel('gpt-5.4pro')).toBe('gpt-5.1');
162+
});
163+
159164
it('should handle mixed case', async () => {
160165
expect(normalizeModel('Gpt-5-Codex-Low')).toBe('gpt-5-codex');
161166
expect(normalizeModel('GpT-5-MeDiUm')).toBe('gpt-5.1');

0 commit comments

Comments
 (0)