diff --git a/extensions/openai/image-generation-provider.test.ts b/extensions/openai/image-generation-provider.test.ts index 6d31cdcdcfe..39ac33fded3 100644 --- a/extensions/openai/image-generation-provider.test.ts +++ b/extensions/openai/image-generation-provider.test.ts @@ -2,12 +2,14 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import { buildOpenAIImageGenerationProvider } from "./image-generation-provider.js"; const { + isProviderApiKeyConfiguredMock, resolveApiKeyForProviderMock, postJsonRequestMock, postMultipartRequestMock, assertOkOrThrowHttpErrorMock, resolveProviderHttpRequestConfigMock, } = vi.hoisted(() => ({ + isProviderApiKeyConfiguredMock: vi.fn(() => false), resolveApiKeyForProviderMock: vi.fn( async (_params?: { provider?: string; @@ -26,6 +28,10 @@ const { })), })); +vi.mock("openclaw/plugin-sdk/provider-auth", () => ({ + isProviderApiKeyConfigured: isProviderApiKeyConfiguredMock, +})); + vi.mock("openclaw/plugin-sdk/provider-auth-runtime", () => ({ resolveApiKeyForProvider: resolveApiKeyForProviderMock, })); @@ -90,6 +96,8 @@ function mockCodexAuthOnly() { describe("openai image generation provider", () => { afterEach(() => { + isProviderApiKeyConfiguredMock.mockReset(); + isProviderApiKeyConfiguredMock.mockReturnValue(false); resolveApiKeyForProviderMock.mockReset(); resolveApiKeyForProviderMock.mockResolvedValue({ apiKey: "openai-key" }); postJsonRequestMock.mockReset(); @@ -109,6 +117,36 @@ describe("openai image generation provider", () => { ); }); + it("reports configured when either OpenAI API key auth or Codex OAuth auth is available", () => { + const provider = buildOpenAIImageGenerationProvider(); + + isProviderApiKeyConfiguredMock.mockImplementation( + (params: { provider: string }) => params.provider === "openai", + ); + expect(provider.isConfigured?.({ agentDir: "/tmp/agent" })).toBe(true); + expect(isProviderApiKeyConfiguredMock).toHaveBeenCalledWith({ + provider: "openai", + agentDir: "/tmp/agent", + }); + + isProviderApiKeyConfiguredMock.mockClear(); + isProviderApiKeyConfiguredMock.mockImplementation( + (params: { provider: string }) => params.provider === "openai-codex", + ); + expect(provider.isConfigured?.({ agentDir: "/tmp/agent" })).toBe(true); + expect(isProviderApiKeyConfiguredMock).toHaveBeenCalledWith({ + provider: "openai", + agentDir: "/tmp/agent", + }); + expect(isProviderApiKeyConfiguredMock).toHaveBeenCalledWith({ + provider: "openai-codex", + agentDir: "/tmp/agent", + }); + + isProviderApiKeyConfiguredMock.mockReturnValue(false); + expect(provider.isConfigured?.({ agentDir: "/tmp/agent" })).toBe(false); + }); + it("does not auto-allow local baseUrl overrides for image requests", async () => { mockGeneratedPngResponse(); diff --git a/src/agents/tools/image-generate-tool.test.ts b/src/agents/tools/image-generate-tool.test.ts index 451623c1764..0e03b29ab78 100644 --- a/src/agents/tools/image-generate-tool.test.ts +++ b/src/agents/tools/image-generate-tool.test.ts @@ -266,6 +266,49 @@ describe("createImageGenerateTool", () => { expect(createImageGenerateTool({ config: {} })).not.toBeNull(); }); + it("infers the canonical OpenAI image model from provider readiness without explicit config", () => { + const isConfigured = vi.fn(({ agentDir }: { agentDir?: string }) => agentDir === "/tmp/agent"); + vi.spyOn(imageGenerationRuntime, "listRuntimeImageGenerationProviders").mockReturnValue([ + { + id: "openai", + defaultModel: "gpt-image-2", + models: ["gpt-image-2"], + isConfigured, + capabilities: { + generate: { + maxCount: 4, + supportsSize: true, + }, + edit: { + enabled: true, + maxInputImages: 5, + supportsSize: true, + }, + geometry: { + sizes: ["1024x1024", "1536x1024", "1024x1536"], + }, + }, + generateImage: vi.fn(async () => { + throw new Error("not used"); + }), + }, + ]); + + expect( + resolveImageGenerationModelConfigForTool({ + cfg: {}, + agentDir: "/tmp/agent", + }), + ).toEqual({ + primary: "openai/gpt-image-2", + }); + expect(createImageGenerateTool({ config: {}, agentDir: "/tmp/agent" })).not.toBeNull(); + expect(isConfigured).toHaveBeenCalledWith({ + cfg: {}, + agentDir: "/tmp/agent", + }); + }); + it("prefers the primary model provider when multiple image providers have auth", () => { stubImageGenerationProviders(); vi.stubEnv("OPENAI_API_KEY", "openai-test");