fix(models): hide unsupported codex mini route

This commit is contained in:
Peter Steinberger
2026-04-28 06:42:45 +01:00
parent 23818600bb
commit dc6031197b
7 changed files with 34 additions and 92 deletions

View File

@@ -13,11 +13,9 @@ Docs: https://docs.openclaw.ai
- Cron/Telegram: preserve explicit `:topic:` delivery targets over stale session-derived thread IDs when isolated cron announces to Telegram forum topics. Carries forward #59069; refs #49704 and #43808. Thanks @roytong9.
- CLI/onboarding: infer image input for common custom-provider vision model IDs, ask only for unknown models, and keep `--custom-image-input`/`--custom-text-input` overrides so vision-capable proxies do not get saved as text-only configs. Fixes #51869. Thanks @Antsoldier1974.
- Models/OpenAI Codex: stop listing or resolving unsupported `openai-codex/gpt-5.4-mini` rows through Codex OAuth, keep stale discovery rows suppressed with a clear API-key-route hint, and leave direct `openai/gpt-5.4-mini` available. Fixes #73242. Thanks @0xCyda.
- Memory/Dreaming: retry Dream Diary once with the session default when a configured dreaming model is unavailable, while leaving subagent trust and allowlist errors visible instead of silently masking configuration problems. Refs #67409 and #69209. Thanks @Ghiggins18 and @everySympathy.
- Feishu/inbound files: recover CJK filenames from plain `Content-Disposition: filename=` download headers when Feishu exposes UTF-8 bytes through Latin-1 header decoding, while leaving valid Latin-1 and JSON-derived names unchanged. (#48578, #50435, #59431) Thanks @alex-xuweilong, @lishuaigit, and @DoChaoing.
### Fixes
- Channels/Telegram: normalize accidental full `/bot<TOKEN>` Telegram `apiRoot` values at runtime and teach `openclaw doctor --fix` to remove the suffix, so startup control calls no longer 404 when direct Bot API curl commands work. Fixes #55387. Thanks @brendanmatthewjones-cmyk, @techfindubai-ux, and @Sivlerback-Chris.
## 2026.4.27

View File

@@ -217,6 +217,12 @@ Choose your preferred auth method and follow the setup steps.
It does not select or auto-enable the bundled Codex app-server harness.
</Note>
<Warning>
`openai-codex/gpt-5.4-mini` is not a supported Codex OAuth route. Use
`openai/gpt-5.4-mini` with an OpenAI API key, or use
`openai-codex/gpt-5.5` with Codex OAuth.
</Warning>
### Config example
```json5

View File

@@ -439,7 +439,7 @@ describe("openai codex provider", () => {
});
});
it("resolves gpt-5.4-mini from codex templates with codex-sized limits", () => {
it("does not resolve gpt-5.4-mini through the Codex OAuth route", () => {
const provider = buildOpenAICodexProviderPlugin();
const model = provider.resolveDynamicModel?.({
@@ -454,13 +454,7 @@ describe("openai codex provider", () => {
) as never,
} as never);
expect(model).toMatchObject({
id: "gpt-5.4-mini",
contextWindow: 272_000,
maxTokens: 128_000,
cost: { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0 },
});
expect(model).not.toHaveProperty("contextTokens");
expect(model).toBeUndefined();
});
it("augments catalog with gpt-5.5-pro and gpt-5.4 native metadata", () => {
@@ -509,11 +503,9 @@ describe("openai codex provider", () => {
cost: { input: 30, output: 180, cacheRead: 0, cacheWrite: 0 },
}),
);
expect(entries).toContainEqual(
expect(entries).not.toContainEqual(
expect.objectContaining({
id: "gpt-5.4-mini",
contextWindow: 272_000,
cost: { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0 },
}),
);
});

View File

@@ -53,14 +53,12 @@ const OPENAI_CODEX_GPT_55_PRO_MODEL_ID = "gpt-5.5-pro";
const OPENAI_CODEX_GPT_54_MODEL_ID = "gpt-5.4";
const OPENAI_CODEX_GPT_54_LEGACY_MODEL_ID = "gpt-5.4-codex";
const OPENAI_CODEX_GPT_54_PRO_MODEL_ID = "gpt-5.4-pro";
const OPENAI_CODEX_GPT_54_MINI_MODEL_ID = "gpt-5.4-mini";
const OPENAI_CODEX_GPT_55_CODEX_CONTEXT_TOKENS = 400_000;
const OPENAI_CODEX_GPT_55_DEFAULT_RUNTIME_CONTEXT_TOKENS = 272_000;
const OPENAI_CODEX_GPT_55_PRO_NATIVE_CONTEXT_TOKENS = 1_000_000;
const OPENAI_CODEX_GPT_55_PRO_DEFAULT_CONTEXT_TOKENS = 272_000;
const OPENAI_CODEX_GPT_54_NATIVE_CONTEXT_TOKENS = 1_050_000;
const OPENAI_CODEX_GPT_54_DEFAULT_CONTEXT_TOKENS = 272_000;
const OPENAI_CODEX_GPT_54_MINI_CONTEXT_TOKENS = 272_000;
const OPENAI_CODEX_GPT_54_MAX_TOKENS = 128_000;
const OPENAI_CODEX_GPT_55_PRO_COST = {
input: 30,
@@ -80,12 +78,6 @@ const OPENAI_CODEX_GPT_54_PRO_COST = {
cacheRead: 0,
cacheWrite: 0,
} as const;
const OPENAI_CODEX_GPT_54_MINI_COST = {
input: 0.75,
output: 4.5,
cacheRead: 0.075,
cacheWrite: 0,
} as const;
const OPENAI_CODEX_GPT_54_TEMPLATE_MODEL_IDS = ["gpt-5.3-codex", "gpt-5.2-codex"] as const;
/** Legacy codex rows first; fall back to catalog `gpt-5.4` when the API omits 5.3/5.2. */
const OPENAI_CODEX_GPT_54_CATALOG_SYNTH_TEMPLATE_MODEL_IDS = [
@@ -97,11 +89,6 @@ const OPENAI_CODEX_GPT_55_PRO_TEMPLATE_MODEL_IDS = [
OPENAI_CODEX_GPT_54_PRO_MODEL_ID,
...OPENAI_CODEX_GPT_54_TEMPLATE_MODEL_IDS,
] as const;
const OPENAI_CODEX_GPT_54_MINI_TEMPLATE_MODEL_IDS = [
OPENAI_CODEX_GPT_54_MODEL_ID,
"gpt-5.1-codex-mini",
...OPENAI_CODEX_GPT_54_TEMPLATE_MODEL_IDS,
] as const;
const OPENAI_CODEX_GPT_53_MODEL_ID = "gpt-5.3-codex";
const OPENAI_CODEX_TEMPLATE_MODEL_IDS = ["gpt-5.2-codex"] as const;
const OPENAI_CODEX_XHIGH_MODEL_IDS = [
@@ -109,7 +96,6 @@ const OPENAI_CODEX_XHIGH_MODEL_IDS = [
OPENAI_CODEX_GPT_55_PRO_MODEL_ID,
OPENAI_CODEX_GPT_54_MODEL_ID,
OPENAI_CODEX_GPT_54_PRO_MODEL_ID,
OPENAI_CODEX_GPT_54_MINI_MODEL_ID,
OPENAI_CODEX_GPT_53_MODEL_ID,
"gpt-5.2-codex",
"gpt-5.1-codex",
@@ -119,7 +105,6 @@ const OPENAI_CODEX_MODERN_MODEL_IDS = [
OPENAI_CODEX_GPT_55_PRO_MODEL_ID,
OPENAI_CODEX_GPT_54_MODEL_ID,
OPENAI_CODEX_GPT_54_PRO_MODEL_ID,
OPENAI_CODEX_GPT_54_MINI_MODEL_ID,
"gpt-5.2",
"gpt-5.2-codex",
OPENAI_CODEX_GPT_53_MODEL_ID,
@@ -242,13 +227,6 @@ function resolveCodexForwardCompatModel(ctx: ProviderResolveDynamicModelContext)
maxTokens: OPENAI_CODEX_GPT_54_MAX_TOKENS,
cost: OPENAI_CODEX_GPT_54_PRO_COST,
};
} else if (lower === OPENAI_CODEX_GPT_54_MINI_MODEL_ID) {
templateIds = OPENAI_CODEX_GPT_54_MINI_TEMPLATE_MODEL_IDS;
patch = {
contextWindow: OPENAI_CODEX_GPT_54_MINI_CONTEXT_TOKENS,
maxTokens: OPENAI_CODEX_GPT_54_MAX_TOKENS,
cost: OPENAI_CODEX_GPT_54_MINI_COST,
};
} else if (lower === OPENAI_CODEX_GPT_53_MODEL_ID) {
templateIds = OPENAI_CODEX_TEMPLATE_MODEL_IDS;
} else {
@@ -552,11 +530,6 @@ export function buildOpenAICodexProviderPlugin(): ProviderPlugin {
providerId: PROVIDER_ID,
templateIds: OPENAI_CODEX_GPT_55_PRO_TEMPLATE_MODEL_IDS,
});
const gpt54MiniTemplate = findCatalogTemplate({
entries: ctx.entries,
providerId: PROVIDER_ID,
templateIds: OPENAI_CODEX_GPT_54_MINI_TEMPLATE_MODEL_IDS,
});
return [
buildOpenAISyntheticCatalogEntry(gpt55ProTemplate, {
id: OPENAI_CODEX_GPT_55_PRO_MODEL_ID,
@@ -582,13 +555,6 @@ export function buildOpenAICodexProviderPlugin(): ProviderPlugin {
contextTokens: OPENAI_CODEX_GPT_54_DEFAULT_CONTEXT_TOKENS,
cost: OPENAI_CODEX_GPT_54_PRO_COST,
}),
buildOpenAISyntheticCatalogEntry(gpt54MiniTemplate, {
id: OPENAI_CODEX_GPT_54_MINI_MODEL_ID,
reasoning: true,
input: ["text", "image"],
contextWindow: OPENAI_CODEX_GPT_54_MINI_CONTEXT_TOKENS,
cost: OPENAI_CODEX_GPT_54_MINI_COST,
}),
].filter((entry): entry is NonNullable<typeof entry> => entry !== undefined);
},
};

View File

@@ -128,6 +128,11 @@
"provider": "openai-codex",
"model": "gpt-5.3-codex-spark",
"reason": "gpt-5.3-codex-spark is no longer exposed by the OpenAI or Codex catalogs. Use openai/gpt-5.5."
},
{
"provider": "openai-codex",
"model": "gpt-5.4-mini",
"reason": "gpt-5.4-mini is not supported by the OpenAI Codex OAuth route. Use openai/gpt-5.4-mini with an OpenAI API key or openai-codex/gpt-5.5 with Codex OAuth."
}
]
},

View File

@@ -269,20 +269,9 @@ function buildDynamicModel(
? findTemplate(params, "openai-codex", ["gpt-5.4", "gpt-5.4-pro", "gpt-5.3-codex"])
: lower === "gpt-5.4" || isLegacyGpt54Alias || lower === "gpt-5.4-pro"
? findTemplate(params, "openai-codex", ["gpt-5.4", "gpt-5.3-codex", "gpt-5.2-codex"])
: lower === "gpt-5.4-mini"
? findTemplate(params, "openai-codex", [
"gpt-5.4",
"gpt-5.1-codex-mini",
"gpt-5.3-codex",
"gpt-5.2-codex",
])
: lower === "gpt-5.3-codex-spark"
? findTemplate(params, "openai-codex", [
"gpt-5.4",
"gpt-5.3-codex",
"gpt-5.2-codex",
])
: findTemplate(params, "openai-codex", ["gpt-5.4"]);
: lower === "gpt-5.3-codex-spark"
? findTemplate(params, "openai-codex", ["gpt-5.4", "gpt-5.3-codex", "gpt-5.2-codex"])
: findTemplate(params, "openai-codex", ["gpt-5.4"]);
const fallback = {
provider: "openai-codex",
api: "openai-codex-responses",
@@ -341,21 +330,6 @@ function buildDynamicModel(
fallback,
);
}
if (lower === "gpt-5.4-mini") {
return cloneTemplate(
template,
modelId,
{
provider: "openai-codex",
api: "openai-codex-responses",
baseUrl: OPENAI_CODEX_BASE_URL,
cost: { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0 },
contextWindow: 272_000,
maxTokens: 128_000,
},
fallback,
);
}
if (lower === "gpt-5.3-codex-spark") {
return cloneTemplate(
template,

View File

@@ -4,11 +4,15 @@ import { createProviderRuntimeTestMock } from "./model.provider-runtime.test-sup
vi.mock("../model-suppression.js", () => ({
shouldSuppressBuiltInModel: ({ provider, id }: { provider?: string; id?: string }) =>
(provider === "openai" ||
((provider === "openai" ||
provider === "azure-openai-responses" ||
provider === "openai-codex") &&
id?.trim().toLowerCase() === "gpt-5.3-codex-spark",
id?.trim().toLowerCase() === "gpt-5.3-codex-spark") ||
(provider === "openai-codex" && id?.trim().toLowerCase() === "gpt-5.4-mini"),
buildSuppressedBuiltInModelError: ({ provider, id }: { provider?: string; id?: string }) => {
if (provider === "openai-codex" && id?.trim().toLowerCase() === "gpt-5.4-mini") {
return "Unknown model: openai-codex/gpt-5.4-mini. gpt-5.4-mini is not supported by the OpenAI Codex OAuth route. Use openai/gpt-5.4-mini with an OpenAI API key or openai-codex/gpt-5.5 with Codex OAuth.";
}
if (
(provider !== "openai" &&
provider !== "azure-openai-responses" &&
@@ -1363,13 +1367,15 @@ describe("resolveModel", () => {
});
});
it("builds an openai-codex fallback for gpt-5.4-mini", () => {
it("does not build an openai-codex fallback for unsupported gpt-5.4-mini", () => {
mockOpenAICodexTemplateModel(discoverModels);
const result = resolveModelForTest("openai-codex", "gpt-5.4-mini", "/tmp/agent");
expect(result.error).toBeUndefined();
expect(result.model).toMatchObject(buildOpenAICodexForwardCompatExpectation("gpt-5.4-mini"));
expect(result.model).toBeUndefined();
expect(result.error).toBe(
"Unknown model: openai-codex/gpt-5.4-mini. gpt-5.4-mini is not supported by the OpenAI Codex OAuth route. Use openai/gpt-5.4-mini with an OpenAI API key or openai-codex/gpt-5.5 with Codex OAuth.",
);
});
it("does not build an openai-codex fallback for removed gpt-5.3-codex-spark", () => {
@@ -1763,7 +1769,7 @@ describe("resolveModel", () => {
});
});
it("keeps exact discovered metadata for other openai-codex models", () => {
it("rejects stale discovered openai-codex gpt-5.4-mini rows", () => {
mockDiscoveredModel(discoverModels, {
provider: "openai-codex",
modelId: "gpt-5.4-mini",
@@ -1777,15 +1783,10 @@ describe("resolveModel", () => {
const result = resolveModelForTest("openai-codex", "gpt-5.4-mini", "/tmp/agent");
expect(result.error).toBeUndefined();
expect(result.model).toMatchObject({
provider: "openai-codex",
id: "gpt-5.4-mini",
api: "openai-codex-responses",
baseUrl: "https://chatgpt.com/backend-api",
contextWindow: 64_000,
input: ["text"],
});
expect(result.model).toBeUndefined();
expect(result.error).toBe(
"Unknown model: openai-codex/gpt-5.4-mini. gpt-5.4-mini is not supported by the OpenAI Codex OAuth route. Use openai/gpt-5.4-mini with an OpenAI API key or openai-codex/gpt-5.5 with Codex OAuth.",
);
});
it("rejects stale direct openai gpt-5.3-codex-spark discovery rows", () => {