From efc80ba2e3d30e1a427798743c7b73fedbc52064 Mon Sep 17 00:00:00 2001 From: Amirsalar Sojoudi Date: Fri, 18 Sep 2026 17:03:57 +0700 Subject: [PATCH] fix(codex): route bare codex-auto-review to the Codex provider (#4135) --- open-sse/providers/registry/codex.js | 3 ++ open-sse/services/model.js | 2 + tests/unit/codex-auto-review-routing.test.js | 44 ++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100644 tests/unit/codex-auto-review-routing.test.js diff --git a/open-sse/providers/registry/codex.js b/open-sse/providers/registry/codex.js index 9eaafe74..710cbc27 100644 --- a/open-sse/providers/registry/codex.js +++ b/open-sse/providers/registry/codex.js @@ -65,6 +65,9 @@ export default { { 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" }, + // 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" }, { id: "gpt-image-2.5", name: "GPT Image 2.5", capabilities: ["text2img","edit","multiImage"], params: ["size","quality","background","image_detail","output_format"], kind: "image" }, { id: "gpt-image-2.5-flare", name: "GPT Image 2.5 Flare", capabilities: ["text2img","edit","multiImage"], params: ["size","quality","background","image_detail","output_format"], kind: "image" }, { id: "gpt-image-2.5-sunburst", name: "GPT Image 2.5 Sunburst", capabilities: ["text2img","edit","multiImage"], params: ["size","quality","background","image_detail","output_format"], kind: "image" }, diff --git a/open-sse/services/model.js b/open-sse/services/model.js index 5b88809c..15c50195 100644 --- a/open-sse/services/model.js +++ b/open-sse/services/model.js @@ -124,6 +124,8 @@ 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-auto-review$/, "codex"], [/^claude-/, "anthropic"], [/^gemini-/, "gemini"], [/^gpt-/, "openai"], diff --git a/tests/unit/codex-auto-review-routing.test.js b/tests/unit/codex-auto-review-routing.test.js new file mode 100644 index 00000000..e96b0fef --- /dev/null +++ b/tests/unit/codex-auto-review-routing.test.js @@ -0,0 +1,44 @@ +import { describe, expect, it } from "vitest"; + +import { + getDefaultModel, + getModelQuotaFamily, + getModelUpstreamId, + getProviderModels, +} from "../../open-sse/config/providerModels.js"; +import { getModelInfoCore } from "../../open-sse/services/model.js"; + +// Codex CLI's auto-review sends the bare model id "codex-auto-review". Before #1398 it fell +// through prefix inference to the "openai" default and failed with +// "No active credentials for provider: openai". +describe("codex auto-review routing (#1398)", () => { + it("routes the bare Codex auto-review model to the OAuth Codex provider", async () => { + await expect(getModelInfoCore("codex-auto-review", {})).resolves.toEqual({ + provider: "codex", + model: "codex-auto-review", + }); + }); + + it("exposes Codex auto-review as a review-quota Codex model", () => { + const autoReview = getProviderModels("cx").find( + (model) => model.id === "codex-auto-review", + ); + + expect(autoReview).toBeTruthy(); + expect(autoReview.name).toBe("Codex Auto Review"); + expect(getModelQuotaFamily("cx", "codex-auto-review")).toBe("review"); + }); + + // getModelUpstreamId strips CODEX_REVIEW_SUFFIX from unregistered "cx" ids, which would send + // "codex-auto" upstream. This model is not a derived review variant, so it must go out verbatim. + it("forwards the id upstream without stripping the -review suffix", () => { + expect(getModelUpstreamId("cx", "codex-auto-review")).toBe( + "codex-auto-review", + ); + }); + + // Registering it must not push it to the front of the cx list — getDefaultModel takes models[0]. + it("does not become the default Codex model", () => { + expect(getDefaultModel("cx")).not.toBe("codex-auto-review"); + }); +});