From aaa5ce6280704bf075fd61225b8d2b5db54a6a37 Mon Sep 17 00:00:00 2001 From: VACInc <3279061+VACInc@users.noreply.github.com> Date: Mon, 1 Jun 2026 12:34:13 -0400 Subject: [PATCH] fix(openai): preserve catalog spark base urls --- src/agents/model-catalog.test.ts | 59 +++++++++++++++++++++++++++----- src/agents/model-catalog.ts | 4 ++- 2 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/agents/model-catalog.test.ts b/src/agents/model-catalog.test.ts index bbbd745ad982..121e4bce4cca 100644 --- a/src/agents/model-catalog.test.ts +++ b/src/agents/model-catalog.test.ts @@ -25,21 +25,37 @@ let readCachedAgentModelCatalogMock: ReturnType; let writeCachedAgentModelCatalogMock: ReturnType; vi.mock("./model-suppression.runtime.js", () => ({ - shouldSuppressBuiltInModel: (params: { provider?: string; id?: string }) => - isSuppressedModel(params.provider, params.id), - buildShouldSuppressBuiltInModel: () => (params: { provider?: string; id?: string }) => - isSuppressedModel(params.provider, params.id), + shouldSuppressBuiltInModel: (params: { provider?: string; id?: string; baseUrl?: string }) => + isSuppressedModel(params.provider, params.id, params.baseUrl), + buildShouldSuppressBuiltInModel: + () => (params: { provider?: string; id?: string; baseUrl?: string }) => + isSuppressedModel(params.provider, params.id, params.baseUrl), })); -function isSuppressedModel(provider?: string, id?: string): boolean { +function isDirectOpenAiBaseUrl(baseUrl?: string): boolean { + const trimmed = baseUrl?.trim(); + if (!trimmed) { + return true; + } + try { + return new URL(trimmed).hostname.toLowerCase().replace(/\.+$/, "") === "api.openai.com"; + } catch { + return false; + } +} + +function isSuppressedModel(provider?: string, id?: string, baseUrl?: string): boolean { const modelId = id?.trim().toLowerCase(); if (!modelId) { return false; } - return ( - (provider === "openai" || provider === "azure-openai-responses" || provider === "openai") && - modelId === "gpt-5.3-codex-spark" - ); + if (modelId !== "gpt-5.3-codex-spark") { + return false; + } + if (provider === "azure-openai-responses") { + return true; + } + return provider === "openai" && isDirectOpenAiBaseUrl(baseUrl); } function mockCatalogImportFailThenRecover() { @@ -1310,6 +1326,31 @@ describe("loadModelCatalog", () => { expectNoCatalogEntry(result, "openai", "gpt-5.3-codex-spark"); }); + it("keeps custom endpoint gpt-5.3-codex-spark rows in the catalog", async () => { + mockAgentDiscoveryModels([ + { + id: "gpt-5.3-codex-spark", + provider: "openai", + baseUrl: "https://api.openai.com/v1", + name: "GPT-5.3 Codex Spark", + contextWindow: 128000, + input: ["text"], + }, + { + id: "gpt-5.3-codex-spark", + provider: "openai", + baseUrl: "https://proxy.example.com/v1", + name: "GPT-5.3 Codex Spark Proxy", + contextWindow: 128000, + input: ["text"], + }, + ]); + + const result = await loadModelCatalog({ config: {} as OpenClawConfig }); + const entry = requireCatalogEntry(result, "openai", "gpt-5.3-codex-spark"); + expect(entry.name).toBe("GPT-5.3 Codex Spark Proxy"); + }); + it("keeps available openai 5.1/5.2/5.3 built-ins in the catalog", async () => { mockAgentDiscoveryModels([ { diff --git a/src/agents/model-catalog.ts b/src/agents/model-catalog.ts index b34791f3e852..9f77a8ecfe25 100644 --- a/src/agents/model-catalog.ts +++ b/src/agents/model-catalog.ts @@ -72,6 +72,7 @@ type DiscoveredModel = { input?: ModelInputType[]; params?: ModelCatalogEntry["params"]; compat?: ModelCatalogEntry["compat"]; + baseUrl?: string; }; type AgentDiscoveryModule = typeof import("./agent-model-discovery.js"); @@ -674,7 +675,8 @@ export async function loadModelCatalog(params?: { const id = normalizeConfiguredProviderCatalogModelId(provider, rawId, { manifestPlugins: getManifestPlugins(), }); - if (shouldSuppressBuiltInModel({ provider, id })) { + const baseUrl = normalizeOptionalString(entry?.baseUrl); + if (shouldSuppressBuiltInModel({ provider, id, baseUrl })) { continue; } const name = normalizeOptionalString(entry?.name ?? id) || id;