diff --git a/src/router.ts b/src/router.ts index fb093d986..05c30cf94 100644 --- a/src/router.ts +++ b/src/router.ts @@ -306,8 +306,13 @@ export class NoEnabledOpenAiProviderError extends Error { } } +// Codex uses a small number of control-plane model ids that are not part of the public GPT/o +// naming families. Keep this exact: a broad `codex-*` rule could capture a third-party model. +const CODEX_INTERNAL_OPENAI_MODELS = new Set(["codex-auto-review"]); + function isBareOpenAiFamilyModel(modelId: string): boolean { - return !modelId.includes("/") && /^(?:gpt-|o1-|o3-|o4-)/.test(modelId); + return !modelId.includes("/") + && (/^(?:gpt-|o1-|o3-|o4-)/.test(modelId) || CODEX_INTERNAL_OPENAI_MODELS.has(modelId)); } function routeResult(providerName: string, provider: OcxProviderConfig, modelId: string): RouteResult { diff --git a/tests/router.test.ts b/tests/router.test.ts index ac6fe6d7e..14a263b67 100644 --- a/tests/router.test.ts +++ b/tests/router.test.ts @@ -139,12 +139,42 @@ describe("routeModel registry effort defaults", () => { }, }; expect(routeModel(base, "gpt-5.5")).toMatchObject({ providerName: "openai", codexAccountMode: "pool" }); + expect(routeModel(base, "codex-auto-review")).toMatchObject({ + providerName: "openai", + modelId: "codex-auto-review", + codexAccountMode: "pool", + }); + expect(routeModel(base, "codex-third-party-model")).toMatchObject({ + providerName: "openai-apikey", + modelId: "codex-third-party-model", + }); + const withDeepSeekDefault: OcxConfig = { + ...base, + defaultProvider: "deepseek", + providers: { + ...base.providers, + deepseek: { + adapter: "openai-chat", + baseUrl: "https://api.deepseek.com/v1", + defaultModel: "deepseek-chat", + }, + }, + }; + expect(routeModel(withDeepSeekDefault, "codex-auto-review")).toMatchObject({ + providerName: "openai", + modelId: "codex-auto-review", + }); + expect(routeModel(withDeepSeekDefault, "codex-third-party-model")).toMatchObject({ + providerName: "deepseek", + modelId: "codex-third-party-model", + }); expect(routeModel({ ...base, providers: { ...base.providers, openai: { ...forward, codexAccountMode: "direct" } } }, "gpt-5.5")) .toMatchObject({ providerName: "openai", codexAccountMode: "direct" }); expect(() => routeModel({ ...base, providers: { ...base.providers, openai: { ...forward, disabled: true } } }, "gpt-5.5")) .toThrow(/requires the canonical openai provider/); const unavailable = { ...base, providers: { "openai-proxy": base.providers["openai-proxy"] } }; expect(() => routeModel(unavailable, "gpt-5.5")).toThrow(/ocx provider add openai/); + expect(() => routeModel(unavailable, "codex-auto-review")).toThrow(NoEnabledOpenAiProviderError); }); test("rejects legacy chatgpt namespaces even when configured", () => {