From 0f54ca20aa502976aa57c315ffa03a150d7b9117 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 22 Mar 2026 17:55:48 -0700 Subject: [PATCH] refactor(image-generation): move provider builders into plugins --- .../fal/image-generation-provider.ts | 8 +++++--- extensions/fal/index.ts | 2 +- .../google/image-generation-provider.ts | 12 +++++++----- extensions/google/index.ts | 2 +- .../openai/image-generation-provider.ts | 9 +++++---- extensions/openai/index.ts | 2 +- package.json | 4 ++++ scripts/lib/plugin-sdk-entrypoints.json | 1 + src/image-generation/providers/fal.test.ts | 2 +- src/image-generation/providers/google.test.ts | 2 +- src/image-generation/providers/openai.test.ts | 2 +- src/plugin-sdk/image-generation-core.ts | 16 ++++++++++++++++ src/plugin-sdk/image-generation.ts | 6 +++--- 13 files changed, 47 insertions(+), 21 deletions(-) rename src/image-generation/providers/fal.ts => extensions/fal/image-generation-provider.ts (97%) rename src/image-generation/providers/google.ts => extensions/google/image-generation-provider.ts (94%) rename src/image-generation/providers/openai.ts => extensions/openai/image-generation-provider.ts (91%) create mode 100644 src/plugin-sdk/image-generation-core.ts diff --git a/src/image-generation/providers/fal.ts b/extensions/fal/image-generation-provider.ts similarity index 97% rename from src/image-generation/providers/fal.ts rename to extensions/fal/image-generation-provider.ts index 8d0cd8ceaaf..64675951c7a 100644 --- a/src/image-generation/providers/fal.ts +++ b/extensions/fal/image-generation-provider.ts @@ -1,6 +1,8 @@ -import { resolveApiKeyForProvider } from "../../agents/model-auth.js"; -import type { ImageGenerationProviderPlugin } from "../../plugins/types.js"; -import type { GeneratedImageAsset } from "../types.js"; +import type { + GeneratedImageAsset, + ImageGenerationProviderPlugin, +} from "openclaw/plugin-sdk/image-generation-core"; +import { resolveApiKeyForProvider } from "openclaw/plugin-sdk/image-generation-core"; const DEFAULT_FAL_BASE_URL = "https://fal.run"; const DEFAULT_FAL_IMAGE_MODEL = "fal-ai/flux/dev"; diff --git a/extensions/fal/index.ts b/extensions/fal/index.ts index b26468879d3..435c8dd0678 100644 --- a/extensions/fal/index.ts +++ b/extensions/fal/index.ts @@ -1,6 +1,6 @@ -import { buildFalImageGenerationProvider } from "openclaw/plugin-sdk/image-generation"; import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry"; import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth"; +import { buildFalImageGenerationProvider } from "./image-generation-provider.js"; import { applyFalConfig, FAL_DEFAULT_IMAGE_MODEL_REF } from "./onboard.js"; const PROVIDER_ID = "fal"; diff --git a/src/image-generation/providers/google.ts b/extensions/google/image-generation-provider.ts similarity index 94% rename from src/image-generation/providers/google.ts rename to extensions/google/image-generation-provider.ts index 24c725fa666..21a41c841bf 100644 --- a/src/image-generation/providers/google.ts +++ b/extensions/google/image-generation-provider.ts @@ -1,12 +1,14 @@ -import { resolveApiKeyForProvider } from "../../agents/model-auth.js"; -import { normalizeGoogleModelId } from "../../agents/model-id-normalization.js"; -import { parseGeminiAuth } from "../../infra/gemini-auth.js"; +import type { ImageGenerationProviderPlugin } from "openclaw/plugin-sdk/image-generation-core"; +import { + normalizeGoogleModelId, + parseGeminiAuth, + resolveApiKeyForProvider, +} from "openclaw/plugin-sdk/image-generation-core"; import { assertOkOrThrowHttpError, normalizeBaseUrl, postJsonRequest, -} from "../../media-understanding/providers/shared.js"; -import type { ImageGenerationProviderPlugin } from "../../plugins/types.js"; +} from "openclaw/plugin-sdk/media-understanding"; const DEFAULT_GOOGLE_IMAGE_BASE_URL = "https://generativelanguage.googleapis.com/v1beta"; const DEFAULT_GOOGLE_IMAGE_MODEL = "gemini-3.1-flash-image-preview"; diff --git a/extensions/google/index.ts b/extensions/google/index.ts index 17a597344eb..eb578942a9f 100644 --- a/extensions/google/index.ts +++ b/extensions/google/index.ts @@ -1,4 +1,3 @@ -import { buildGoogleImageGenerationProvider } from "openclaw/plugin-sdk/image-generation"; import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry"; import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth"; import { @@ -7,6 +6,7 @@ import { } from "openclaw/plugin-sdk/provider-models"; import { createGoogleThinkingPayloadWrapper } from "openclaw/plugin-sdk/provider-stream"; import { registerGoogleGeminiCliProvider } from "./gemini-cli-provider.js"; +import { buildGoogleImageGenerationProvider } from "./image-generation-provider.js"; import { googleMediaUnderstandingProvider } from "./media-understanding-provider.js"; import { isModernGoogleModel, resolveGoogle31ForwardCompatModel } from "./provider-models.js"; import { createGeminiWebSearchProvider } from "./src/gemini-web-search-provider.js"; diff --git a/src/image-generation/providers/openai.ts b/extensions/openai/image-generation-provider.ts similarity index 91% rename from src/image-generation/providers/openai.ts rename to extensions/openai/image-generation-provider.ts index c6066971fda..17bd2972d47 100644 --- a/src/image-generation/providers/openai.ts +++ b/extensions/openai/image-generation-provider.ts @@ -1,12 +1,13 @@ -import { resolveApiKeyForProvider } from "../../agents/model-auth.js"; -import type { ImageGenerationProviderPlugin } from "../../plugins/types.js"; -import { OPENAI_DEFAULT_IMAGE_MODEL as DEFAULT_OPENAI_IMAGE_MODEL } from "../../providers/openai-defaults.js"; +import type { ImageGenerationProviderPlugin } from "openclaw/plugin-sdk/image-generation-core"; +import { + OPENAI_DEFAULT_IMAGE_MODEL as DEFAULT_OPENAI_IMAGE_MODEL, + resolveApiKeyForProvider, +} from "openclaw/plugin-sdk/image-generation-core"; const DEFAULT_OPENAI_IMAGE_BASE_URL = "https://api.openai.com/v1"; const DEFAULT_OUTPUT_MIME = "image/png"; const DEFAULT_SIZE = "1024x1024"; const OPENAI_SUPPORTED_SIZES = ["1024x1024", "1024x1536", "1536x1024"] as const; -// Only advertise ratios we can satisfy exactly with OpenAI's supported size presets. const OPENAI_SUPPORTED_ASPECT_RATIOS = ["1:1", "2:3", "3:2"] as const; type OpenAIImageApiResponse = { diff --git a/extensions/openai/index.ts b/extensions/openai/index.ts index 1ea449d6205..302ce168536 100644 --- a/extensions/openai/index.ts +++ b/extensions/openai/index.ts @@ -1,6 +1,6 @@ -import { buildOpenAIImageGenerationProvider } from "openclaw/plugin-sdk/image-generation"; import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry"; import { openaiMediaUnderstandingProvider } from "./media-understanding-provider.js"; +import { buildOpenAIImageGenerationProvider } from "./image-generation-provider.js"; import { buildOpenAICodexProviderPlugin } from "./openai-codex-provider.js"; import { buildOpenAIProvider } from "./openai-provider.js"; import { buildOpenAISpeechProvider } from "./speech-provider.js"; diff --git a/package.json b/package.json index 0edc59dc646..ca4fa310518 100644 --- a/package.json +++ b/package.json @@ -329,6 +329,10 @@ "types": "./dist/plugin-sdk/image-generation.d.ts", "default": "./dist/plugin-sdk/image-generation.js" }, + "./plugin-sdk/image-generation-core": { + "types": "./dist/plugin-sdk/image-generation-core.d.ts", + "default": "./dist/plugin-sdk/image-generation-core.js" + }, "./plugin-sdk/imessage": { "types": "./dist/plugin-sdk/imessage.d.ts", "default": "./dist/plugin-sdk/imessage.js" diff --git a/scripts/lib/plugin-sdk-entrypoints.json b/scripts/lib/plugin-sdk-entrypoints.json index 4ba14b4a973..94cb8b47308 100644 --- a/scripts/lib/plugin-sdk-entrypoints.json +++ b/scripts/lib/plugin-sdk-entrypoints.json @@ -72,6 +72,7 @@ "directory-runtime", "googlechat", "image-generation", + "image-generation-core", "imessage", "imessage-core", "irc", diff --git a/src/image-generation/providers/fal.test.ts b/src/image-generation/providers/fal.test.ts index 82c809354f6..e60420a0343 100644 --- a/src/image-generation/providers/fal.test.ts +++ b/src/image-generation/providers/fal.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import * as modelAuth from "../../agents/model-auth.js"; -import { buildFalImageGenerationProvider } from "./fal.js"; +import { buildFalImageGenerationProvider } from "../../../extensions/fal/image-generation-provider.js"; function expectFalJsonPost( fetchMock: ReturnType, diff --git a/src/image-generation/providers/google.test.ts b/src/image-generation/providers/google.test.ts index 5c64481edae..7a6b35b1316 100644 --- a/src/image-generation/providers/google.test.ts +++ b/src/image-generation/providers/google.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import * as modelAuth from "../../agents/model-auth.js"; -import { buildGoogleImageGenerationProvider } from "./google.js"; +import { buildGoogleImageGenerationProvider } from "../../../extensions/google/image-generation-provider.js"; describe("Google image-generation provider", () => { afterEach(() => { diff --git a/src/image-generation/providers/openai.test.ts b/src/image-generation/providers/openai.test.ts index 76e2ead729b..67d49ce3e30 100644 --- a/src/image-generation/providers/openai.test.ts +++ b/src/image-generation/providers/openai.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import * as modelAuth from "../../agents/model-auth.js"; -import { buildOpenAIImageGenerationProvider } from "./openai.js"; +import { buildOpenAIImageGenerationProvider } from "../../../extensions/openai/image-generation-provider.js"; describe("OpenAI image-generation provider", () => { afterEach(() => { diff --git a/src/plugin-sdk/image-generation-core.ts b/src/plugin-sdk/image-generation-core.ts new file mode 100644 index 00000000000..757ed580db0 --- /dev/null +++ b/src/plugin-sdk/image-generation-core.ts @@ -0,0 +1,16 @@ +// Shared image-generation implementation helpers for bundled and third-party plugins. + +export type { ImageGenerationProviderPlugin } from "../plugins/types.js"; +export type { + GeneratedImageAsset, + ImageGenerationProvider, + ImageGenerationResolution, + ImageGenerationRequest, + ImageGenerationResult, + ImageGenerationSourceImage, +} from "../image-generation/types.js"; + +export { resolveApiKeyForProvider } from "../agents/model-auth.js"; +export { normalizeGoogleModelId } from "../agents/model-id-normalization.js"; +export { parseGeminiAuth } from "../infra/gemini-auth.js"; +export { OPENAI_DEFAULT_IMAGE_MODEL } from "../providers/openai-defaults.js"; diff --git a/src/plugin-sdk/image-generation.ts b/src/plugin-sdk/image-generation.ts index c999bbbaabe..1dc0c04b403 100644 --- a/src/plugin-sdk/image-generation.ts +++ b/src/plugin-sdk/image-generation.ts @@ -9,6 +9,6 @@ export type { ImageGenerationSourceImage, } from "../image-generation/types.js"; -export { buildFalImageGenerationProvider } from "../image-generation/providers/fal.js"; -export { buildGoogleImageGenerationProvider } from "../image-generation/providers/google.js"; -export { buildOpenAIImageGenerationProvider } from "../image-generation/providers/openai.js"; +export { buildFalImageGenerationProvider } from "../../extensions/fal/image-generation-provider.js"; +export { buildGoogleImageGenerationProvider } from "../../extensions/google/image-generation-provider.js"; +export { buildOpenAIImageGenerationProvider } from "../../extensions/openai/image-generation-provider.js";