UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

211 lines (210 loc) 7.96 kB
import { w as parseStrictPositiveInteger } from "../../number-coercion-CLj0HTDM.js"; import { c as isRecord } from "../../record-coerce-DItp3I4t.js"; import { l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "../../string-coerce-CIXf7egm.js"; import { m as readProviderJsonResponse, n as assertOkOrThrowHttpError } from "../../provider-http-errors-U-nhuk_f.js"; import { p as sanitizeConfiguredModelProviderRequest } from "../../provider-request-config-DIOYidiO.js"; import { t as isProviderApiKeyConfigured } from "../../provider-auth-availability-BUrlw84-.js"; import { n as resolveGeneratedMediaMaxBytes } from "../../configured-max-bytes-CTSUbFDK.js"; import "../../provider-auth-BeZ7NZUU.js"; import { l as postJsonRequest } from "../../shared-DCDpYrmy.js"; import "../../number-runtime-Cy4drVnh.js"; import "../../string-coerce-runtime-GQa0ehRA.js"; import "../../media-generation-runtime-Dd81xPqM.js"; import { a as resolveApiKeyForProvider } from "../../provider-auth-runtime-BqyR7mkf.js"; import "../../provider-http-k9RMI7iG.js"; import { l as resolveInlineImageJsonResponseMaxBytes, n as generatedImageAssetFromBase64 } from "../../image-generation-B61kkjvL.js"; import { n as normalizeGoogleModelId } from "../../model-id-CAmKILzd.js"; import { t as resolveGoogleGenerativeAiHttpRequestConfig } from "../../api-BIkqybAZ.js"; import { n as toStandardGoogleProviderBase64 } from "../../base64-L17PENqC.js"; //#region extensions/google/image-generation-provider.ts const DEFAULT_GOOGLE_IMAGE_MODEL = "gemini-3.1-flash-image"; const DEFAULT_IMAGE_TIMEOUT_MS = 18e4; const DEFAULT_OUTPUT_MIME = "image/png"; const GOOGLE_MAX_IMAGE_RESULTS = 4; const GOOGLE_SUPPORTED_SIZES = [ "1024x1024", "1024x1536", "1536x1024", "1024x1792", "1792x1024" ]; const GOOGLE_SUPPORTED_ASPECT_RATIOS = [ "1:1", "2:3", "3:2", "3:4", "4:3", "4:5", "5:4", "9:16", "16:9", "21:9" ]; const GOOGLE_IMAGE_MALFORMED_RESPONSE = "Google image generation response malformed"; function normalizeGoogleImageModel(model) { const trimmed = model?.trim(); return normalizeGoogleModelId(trimmed || DEFAULT_GOOGLE_IMAGE_MODEL); } function mapSizeToImageConfig(size) { const trimmed = size?.trim(); if (!trimmed) return; const normalized = normalizeLowercaseStringOrEmpty(trimmed); const aspectRatio = (/* @__PURE__ */ new Map([ ["1024x1024", "1:1"], ["1024x1536", "2:3"], ["1536x1024", "3:2"], ["1024x1792", "9:16"], ["1792x1024", "16:9"] ])).get(normalized); const [widthRaw, heightRaw] = normalized.split("x"); const width = parseStrictPositiveInteger(widthRaw); const height = parseStrictPositiveInteger(heightRaw); if (width === void 0 || height === void 0) return; const longestEdge = Math.max(width, height); const imageSize = longestEdge >= 3072 ? "4K" : longestEdge >= 1536 ? "2K" : void 0; if (!aspectRatio && !imageSize) return; return { ...aspectRatio ? { aspectRatio } : {}, ...imageSize ? { imageSize } : {} }; } function googleResponseParts(payload) { if (!isRecord(payload)) throw new Error(GOOGLE_IMAGE_MALFORMED_RESPONSE); const candidates = payload.candidates; if (candidates === void 0 || candidates === null) return []; if (!Array.isArray(candidates)) throw new Error(GOOGLE_IMAGE_MALFORMED_RESPONSE); const parts = []; for (const candidate of candidates) { if (!isRecord(candidate)) throw new Error(GOOGLE_IMAGE_MALFORMED_RESPONSE); const content = candidate.content; if (content === void 0 || content === null) continue; if (!isRecord(content)) throw new Error(GOOGLE_IMAGE_MALFORMED_RESPONSE); const candidateParts = content.parts; if (candidateParts === void 0 || candidateParts === null) continue; if (!Array.isArray(candidateParts)) throw new Error(GOOGLE_IMAGE_MALFORMED_RESPONSE); parts.push(...candidateParts); } return parts; } function googleInlineDataFromPart(part) { if (!isRecord(part)) throw new Error(GOOGLE_IMAGE_MALFORMED_RESPONSE); const inline = part.inlineData ?? part.inline_data; if (inline === void 0 || inline === null) return; if (!isRecord(inline)) throw new Error(GOOGLE_IMAGE_MALFORMED_RESPONSE); return inline; } function buildGoogleImageGenerationProvider() { return { id: "google", label: "Google", defaultModel: DEFAULT_GOOGLE_IMAGE_MODEL, models: [DEFAULT_GOOGLE_IMAGE_MODEL, "gemini-3-pro-image"], isConfigured: (ctx) => isProviderApiKeyConfigured({ provider: "google", ...ctx }), capabilities: { generate: { maxCount: GOOGLE_MAX_IMAGE_RESULTS, supportsSize: true, supportsAspectRatio: true, supportsResolution: true }, edit: { enabled: true, maxCount: GOOGLE_MAX_IMAGE_RESULTS, maxInputImages: 5, supportsSize: true, supportsAspectRatio: true, supportsResolution: true }, geometry: { sizes: [...GOOGLE_SUPPORTED_SIZES], aspectRatios: [...GOOGLE_SUPPORTED_ASPECT_RATIOS], resolutions: [ "1K", "2K", "4K" ] } }, async generateImage(req) { const auth = await resolveApiKeyForProvider({ provider: "google", cfg: req.cfg, agentDir: req.agentDir, store: req.authStore }); if (!auth.apiKey) throw new Error("Google API key missing"); const model = normalizeGoogleImageModel(req.model); const { baseUrl, allowPrivateNetwork, headers, dispatcherPolicy } = resolveGoogleGenerativeAiHttpRequestConfig({ apiKey: auth.apiKey, baseUrl: req.cfg?.models?.providers?.google?.baseUrl, request: sanitizeConfiguredModelProviderRequest(req.cfg?.models?.providers?.google?.request), capability: "image", transport: "http" }); const imageConfig = mapSizeToImageConfig(req.size); const inputParts = (req.inputImages ?? []).map((image) => ({ inlineData: { mimeType: image.mimeType, data: image.buffer.toString("base64") } })); const resolvedImageConfig = { ...imageConfig, ...req.aspectRatio?.trim() ? { aspectRatio: req.aspectRatio.trim() } : {}, ...req.resolution ? { imageSize: req.resolution } : {} }; const { response: res, release } = await postJsonRequest({ url: `${baseUrl}/models/${model}:generateContent`, headers, body: { contents: [{ role: "user", parts: [...inputParts, { text: req.prompt }] }], generationConfig: { responseModalities: ["TEXT", "IMAGE"], ...Object.keys(resolvedImageConfig).length > 0 ? { imageConfig: resolvedImageConfig } : {} } }, timeoutMs: req.timeoutMs ?? DEFAULT_IMAGE_TIMEOUT_MS, fetchFn: fetch, pinDns: false, allowPrivateNetwork, ssrfPolicy: req.ssrfPolicy, dispatcherPolicy }); try { await assertOkOrThrowHttpError(res, "Google image generation failed"); const payload = await readProviderJsonResponse(res, "google.image-generation", { maxBytes: resolveInlineImageJsonResponseMaxBytes(GOOGLE_MAX_IMAGE_RESULTS, resolveGeneratedMediaMaxBytes(req.cfg, "image")) }); let imageIndex = 0; const images = []; for (const part of googleResponseParts(payload)) { const inline = googleInlineDataFromPart(part); if (!inline) continue; const data = normalizeOptionalString(inline.data); if (!data) throw new Error(GOOGLE_IMAGE_MALFORMED_RESPONSE); const standardData = toStandardGoogleProviderBase64(data); if (!standardData) throw new Error(GOOGLE_IMAGE_MALFORMED_RESPONSE); const image = generatedImageAssetFromBase64({ base64: standardData, index: imageIndex, mimeType: normalizeOptionalString(inline.mimeType) ?? normalizeOptionalString(inline.mime_type) ?? DEFAULT_OUTPUT_MIME }); if (!image) throw new Error(GOOGLE_IMAGE_MALFORMED_RESPONSE); imageIndex += 1; images.push(image); } if (images.length === 0) throw new Error("Google image generation response missing image data"); return { images, model }; } finally { await release(); } } }; } //#endregion export { buildGoogleImageGenerationProvider };