From 007f714dca431fc6000b170a05efb6aecafbcbee Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 12 Jul 2026 18:27:57 +0200 Subject: [PATCH] fix(openai): preserve image routing before catalog load (#105518) * fix(openai): handle image providers without model catalogs * test(openai): use exported config contract * test(openai): model provider overlays before catalog load --- .../openai/image-generation-provider.test.ts | 30 +++++++++++++++++++ .../openai/image-generation-provider.ts | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/extensions/openai/image-generation-provider.test.ts b/extensions/openai/image-generation-provider.test.ts index 38010046ea0a..6cfc5a2ae669 100644 --- a/extensions/openai/image-generation-provider.test.ts +++ b/extensions/openai/image-generation-provider.test.ts @@ -1,4 +1,5 @@ // Openai tests cover image generation provider plugin behavior. +import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; import { afterEach, describe, expect, it, vi } from "vitest"; import { buildOpenAIImageGenerationProvider } from "./image-generation-provider.js"; @@ -728,6 +729,35 @@ describe("openai image generation provider", () => { expect(result.metadata).toBeUndefined(); }); + it("falls back to the provider baseUrl when the model catalog is omitted", async () => { + mockGeneratedPngResponse(); + + const provider = buildOpenAIImageGenerationProvider(); + // Plugin-scoped runtime snapshots can carry a built-in provider overlay + // before its model catalog is present. + const cfg = { + models: { + providers: { + openai: { + baseUrl: "https://openai-compatible.example.com/v1", + }, + }, + }, + } as unknown as OpenClawConfig; + const result = await provider.generateImage({ + provider: "openai", + model: "gpt-image-2", + prompt: "Create an image through a provider overlay", + cfg, + }); + + expect(httpConfigCall().baseUrl).toBe("https://openai-compatible.example.com/v1"); + expect(jsonRequestCall().url).toBe( + "https://openai-compatible.example.com/v1/images/generations", + ); + expect(result.images).toHaveLength(1); + }); + it("forwards output and OpenAI-only options on direct generations", async () => { mockGeneratedPngResponse(); diff --git a/extensions/openai/image-generation-provider.ts b/extensions/openai/image-generation-provider.ts index 64f9866f0d0a..d974bcf3a390 100644 --- a/extensions/openai/image-generation-provider.ts +++ b/extensions/openai/image-generation-provider.ts @@ -268,7 +268,7 @@ function resolveNativeOpenAIImageSizesForModel(model: string): readonly string[] function resolveConfiguredOpenAIImageBaseUrl(cfg: OpenClawConfig | undefined, model: string) { const modelId = model.trim().replace(/^openai\//u, ""); const modelBaseUrl = cfg?.models?.providers?.openai?.models - .find((candidate) => candidate.id.trim().replace(/^openai\//u, "") === modelId) + ?.find((candidate) => candidate.id.trim().replace(/^openai\//u, "") === modelId) ?.baseUrl?.trim(); return modelBaseUrl || resolveConfiguredOpenAIBaseUrl(cfg); }