fix(codex): remove ghost models, add gpt-daybreak/reserve, route gpt-5.x/6.x bare slugs to codex (#4418)
This commit is contained in:
1 parent
60e890d7f1
commit
8f9ff44f27
3 files changed
+153
-8
No files matched your search
@@ -63,12 +63,11 @@ export default {
|
||||
{ id: "gpt-5.6-luna-review", name: "GPT 5.6 Luna Review", upstreamModelId: "gpt-5.6-luna", quotaFamily: "review" },
|
||||
{ id: "gpt-5.5", name: "GPT 5.5" },
|
||||
{ id: "gpt-5.5-review", name: "GPT 5.5 Review", upstreamModelId: "gpt-5.5", quotaFamily: "review" },
|
||||
{ id: "gpt-5.4", name: "GPT 5.4" },
|
||||
{ id: "gpt-5.4-review", name: "GPT 5.4 Review", upstreamModelId: "gpt-5.4", quotaFamily: "review" },
|
||||
{ id: "gpt-5.4-mini", name: "GPT 5.4 Mini" },
|
||||
{ id: "gpt-5.4-mini-review", name: "GPT 5.4 Mini Review", upstreamModelId: "gpt-5.4-mini", quotaFamily: "review" },
|
||||
{ id: "gpt-5.3-codex-spark", name: "GPT 5.3 Codex Spark" },
|
||||
{ id: "gpt-5.3-codex-spark-review", name: "GPT 5.3 Codex Spark Review", upstreamModelId: "gpt-5.3-codex-spark", quotaFamily: "review" },
|
||||
// gpt-5.4 / gpt-5.4-mini / gpt-5.3-codex-spark removed: absent from backend-api/codex/models
|
||||
// for ChatGPT Plus/Pro accounts and return HTTP 400 "model is not supported" (#4202).
|
||||
// gpt-daybreak-blue-latest and gpt-reserve added: confirmed live via backend-api/codex/models (#4202).
|
||||
{ id: "gpt-daybreak-blue-latest", name: "GPT Daybreak Blue" },
|
||||
{ id: "gpt-reserve", name: "GPT Reserve" },
|
||||
// Codex CLI's auto-review virtual model. Unlike the "-review" variants above it is not derived
|
||||
// from a base model, so it is forwarded verbatim instead of having "-review" stripped (#1398).
|
||||
{ id: "codex-auto-review", name: "Codex Auto Review", upstreamModelId: "codex-auto-review", quotaFamily: "review" },
|
||||
@@ -81,7 +80,7 @@ export default {
|
||||
{ id: "gpt-5.6-terra-image", name: "GPT 5.6 Terra Image", capabilities: ["text2img","edit"], params: ["size","quality","background","image_detail","output_format"], kind: "image" },
|
||||
{ id: "gpt-5.6-luna-image", name: "GPT 5.6 Luna Image", capabilities: ["text2img","edit"], params: ["size","quality","background","image_detail","output_format"], kind: "image" },
|
||||
{ id: "gpt-5.5-image", name: "GPT 5.5 Image", capabilities: ["text2img","edit"], params: ["size","quality","background","image_detail","output_format"], kind: "image" },
|
||||
{ id: "gpt-5.4-image", name: "GPT 5.4 Image", capabilities: ["text2img","edit"], params: ["size","quality","background","image_detail","output_format"], kind: "image" },
|
||||
// gpt-5.4-image removed alongside gpt-5.4 (both are dead on the backend) (#4202).
|
||||
{ id: "gpt-5.3-image", name: "GPT 5.3 Image", capabilities: ["text2img","edit"], params: ["size","quality","background","image_detail","output_format"], kind: "image" },
|
||||
],
|
||||
serviceKinds: ["llm","image"],
|
||||
|
||||
@@ -124,8 +124,19 @@ export async function getModelInfoCore(modelStr, aliasesOrGetter) {
|
||||
|
||||
// Config-driven prefix → provider inference (first match wins, fallback "openai").
|
||||
const MODEL_PREFIX_PROVIDERS = [
|
||||
// Codex CLI sends this bare virtual model for auto-review — keep it on OAuth Codex (#1398).
|
||||
// Codex CLI sends this bare virtual model for auto-review - keep it on OAuth Codex (#1398).
|
||||
[/^codex-auto-review$/, "codex"],
|
||||
// Codex-only GPT model slugs: present in backend-api/codex/models but not on the
|
||||
// OpenAI API. Without these rules a bare model id (e.g. "gpt-5.6-terra" from the
|
||||
// Codex CLI /model picker) resolves to provider "openai", which 404s for users that
|
||||
// only have a Codex OAuth account and no OpenAI API key (#4405).
|
||||
// Ranges covered: gpt-5.x, gpt-6.x, gpt-daybreak-*, gpt-reserve* — all are
|
||||
// Codex-backend models. Plain "gpt-4*" / "gpt-3.5*" / "gpt-4o*" fall through to
|
||||
// the generic gpt-* → openai rule below.
|
||||
[/^gpt-[56]\./, "codex"],
|
||||
[/^gpt-6-/, "codex"],
|
||||
[/^gpt-daybreak-/, "codex"],
|
||||
[/^gpt-reserve/, "codex"],
|
||||
[/^claude-/, "anthropic"],
|
||||
[/^gemini-/, "gemini"],
|
||||
[/^gpt-/, "openai"],
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
/**
|
||||
* Tests for #4202 and #4405
|
||||
*
|
||||
* #4202 — Codex registry has ghost models (always HTTP 400) and is missing
|
||||
* gpt-daybreak-blue-latest / gpt-reserve.
|
||||
* Fix: remove gpt-5.4/mini/spark entries + gpt-5.4-image; add gpt-daybreak-blue-latest and gpt-reserve.
|
||||
*
|
||||
* #4405 — Bare Codex model slugs (e.g. gpt-5.6-terra from the CLI /model picker)
|
||||
* routed to provider "openai" instead of "codex", causing 404 for users without
|
||||
* an OpenAI API key connection.
|
||||
* Fix: add codex-specific gpt-5.x / gpt-6.x / gpt-daybreak-* / gpt-reserve* rules
|
||||
* to MODEL_PREFIX_PROVIDERS before the generic gpt-* → openai rule.
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from "vitest";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
// ── #4202 Registry checks ────────────────────────────────────────────────────
|
||||
|
||||
const codexSrc = fs.readFileSync(
|
||||
path.resolve("../open-sse/providers/registry/codex.js"),
|
||||
"utf-8"
|
||||
);
|
||||
|
||||
describe("Codex registry — ghost models removed (#4202)", () => {
|
||||
it("gpt-5.4 is removed", () => {
|
||||
// Match only a standalone entry, not inside gpt-5.4-mini or gpt-5.45 etc.
|
||||
expect(codexSrc).not.toMatch(/id:\s*"gpt-5\.4"/);
|
||||
});
|
||||
|
||||
it("gpt-5.4-mini is removed", () => {
|
||||
expect(codexSrc).not.toMatch(/id:\s*"gpt-5\.4-mini"/);
|
||||
});
|
||||
|
||||
it("gpt-5.3-codex-spark is removed", () => {
|
||||
expect(codexSrc).not.toMatch(/id:\s*"gpt-5\.3-codex-spark"/);
|
||||
});
|
||||
|
||||
it("gpt-5.4-image is removed", () => {
|
||||
expect(codexSrc).not.toMatch(/id:\s*"gpt-5\.4-image"/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Codex registry — new models added (#4202)", () => {
|
||||
it("gpt-daybreak-blue-latest is present", () => {
|
||||
expect(codexSrc).toContain('"gpt-daybreak-blue-latest"');
|
||||
});
|
||||
|
||||
it("gpt-reserve is present", () => {
|
||||
expect(codexSrc).toContain('"gpt-reserve"');
|
||||
});
|
||||
});
|
||||
|
||||
describe("Codex registry — still-live models kept (#4202 regression guard)", () => {
|
||||
it("gpt-5.5 still present", () => {
|
||||
expect(codexSrc).toContain('"gpt-5.5"');
|
||||
});
|
||||
|
||||
it("gpt-5.6-terra still present", () => {
|
||||
expect(codexSrc).toContain('"gpt-5.6-terra"');
|
||||
});
|
||||
|
||||
it("gpt-6-astra still present", () => {
|
||||
expect(codexSrc).toContain('"gpt-6-astra"');
|
||||
});
|
||||
});
|
||||
|
||||
// ── #4405 Model prefix routing ───────────────────────────────────────────────
|
||||
// Replicate MODEL_PREFIX_PROVIDERS logic from open-sse/services/model.js
|
||||
|
||||
const MODEL_PREFIX_PROVIDERS = [
|
||||
[/^codex-auto-review$/, "codex"],
|
||||
[/^gpt-[56]\./, "codex"],
|
||||
[/^gpt-6-/, "codex"],
|
||||
[/^gpt-daybreak-/, "codex"],
|
||||
[/^gpt-reserve/, "codex"],
|
||||
[/^claude-/, "anthropic"],
|
||||
[/^gemini-/, "gemini"],
|
||||
[/^gpt-/, "openai"],
|
||||
[/^o[134]/, "openai"],
|
||||
[/^deepseek-/, "openrouter"],
|
||||
];
|
||||
|
||||
function inferProvider(modelName) {
|
||||
if (!modelName) return "openai";
|
||||
const m = modelName.toLowerCase();
|
||||
return MODEL_PREFIX_PROVIDERS.find(([re]) => re.test(m))?.[1] || "openai";
|
||||
}
|
||||
|
||||
describe("inferProviderFromModelName — Codex gpt-* models route to codex (#4405)", () => {
|
||||
it("gpt-5.6-terra → codex", () => {
|
||||
expect(inferProvider("gpt-5.6-terra")).toBe("codex");
|
||||
});
|
||||
|
||||
it("gpt-5.6-sol → codex", () => {
|
||||
expect(inferProvider("gpt-5.6-sol")).toBe("codex");
|
||||
});
|
||||
|
||||
it("gpt-5.5 → codex", () => {
|
||||
expect(inferProvider("gpt-5.5")).toBe("codex");
|
||||
});
|
||||
|
||||
it("gpt-6-astra → codex", () => {
|
||||
expect(inferProvider("gpt-6-astra")).toBe("codex");
|
||||
});
|
||||
|
||||
it("gpt-daybreak-blue-latest → codex", () => {
|
||||
expect(inferProvider("gpt-daybreak-blue-latest")).toBe("codex");
|
||||
});
|
||||
|
||||
it("gpt-reserve → codex", () => {
|
||||
expect(inferProvider("gpt-reserve")).toBe("codex");
|
||||
});
|
||||
|
||||
it("gpt-4o → openai (standard model not affected)", () => {
|
||||
expect(inferProvider("gpt-4o")).toBe("openai");
|
||||
});
|
||||
|
||||
it("gpt-4-turbo → openai (standard model not affected)", () => {
|
||||
expect(inferProvider("gpt-4-turbo")).toBe("openai");
|
||||
});
|
||||
|
||||
it("gpt-3.5-turbo → openai (standard model not affected)", () => {
|
||||
expect(inferProvider("gpt-3.5-turbo")).toBe("openai");
|
||||
});
|
||||
|
||||
it("claude-opus-5 → anthropic (regression guard)", () => {
|
||||
expect(inferProvider("claude-opus-5")).toBe("anthropic");
|
||||
});
|
||||
|
||||
it("codex-auto-review → codex (regression guard)", () => {
|
||||
expect(inferProvider("codex-auto-review")).toBe("codex");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user