UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

293 lines (292 loc) 14.3 kB
import { l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js"; import { r as defaultRuntime } from "./runtime-CF2WjnNZ.js"; import { n as getRuntimeConfig } from "./io.runtime-B9iJRs3w.js"; import { u as resolveAgentDir } from "./agent-scope-config-DcbEhP0R.js"; import { a as resolveAgentModelPrimaryValue } from "./model-input-BuGMCNOz.js"; import "./agent-scope-DbtJyKUL.js"; import "./config-Cs0XXL3x.js"; import { n as detectMime } from "./mime-CVpcq9ju.js"; import { c as getImageMetadata } from "./image-ops-DU1SIRgh.js"; import "./media-services-By-i0Drw.js"; import { n as listRuntimeImageGenerationProviders, t as generateImage } from "./runtime-Dyvgun2q.js"; import { t as runWithImageModelFallback } from "./model-fallback-image-BVf3iJBv.js"; import { u as getModelsCommandSecretTargetIds } from "./command-secret-targets-D0VrT_k_.js"; import { n as runCommandWithRuntime } from "./cli-utils-D1DAWB8d.js"; import { o as prepareImageDescriptionInput, r as describePreparedImageWithModel, t as describeImageFile } from "./runtime-D2rkI188.js"; import { a as parseOptionalTimeoutMs, c as providerSummaryText, d as resolveCapabilityProviderAgentId, f as resolveLocalCapabilityRuntimeConfig, i as parseOptionalPositiveInteger, l as requireProviderModelOverride, m as resolveSelectedProviderFromModelRef, n as formatEnvelopeForText, s as providerHasGenericConfig, t as emitJsonOrText, u as resolveCapabilityAgentOption } from "./shared-LJFJI8g1.js"; import { t as isMissingMediaUnderstandingProvider } from "./media-understanding-result-CCD7dk7R.js"; import { t as collectOption } from "./helpers-B-LqXQ3Z.js"; import { n as writeOutputAsset, t as readInputFiles } from "./media-output-BwM2cftT.js"; import path from "node:path"; //#region src/cli/capability-cli/image.ts const IMAGE_OUTPUT_FORMATS = [ "png", "jpeg", "webp" ]; const IMAGE_BACKGROUNDS = [ "transparent", "opaque", "auto" ]; async function runImageGenerate(params) { requireProviderModelOverride(params.model); const cfg = await resolveLocalCapabilityRuntimeConfig({ commandName: `infer ${params.capability}`, targetIds: getModelsCommandSecretTargetIds() }); const agentId = resolveCapabilityProviderAgentId(cfg, params.agent, `infer ${params.capability}`); const agentDir = resolveAgentDir(cfg, agentId); const inputImages = params.file && params.file.length > 0 ? await Promise.all((await readInputFiles(params.file)).map(async (entry) => ({ buffer: entry.buffer, fileName: path.basename(entry.path), mimeType: await detectMime({ buffer: entry.buffer, filePath: entry.path }) ?? "image/png" }))) : void 0; const result = await generateImage({ cfg, agentDir, prompt: params.prompt, modelOverride: params.model, count: params.count, size: params.size, aspectRatio: params.aspectRatio, resolution: params.resolution, quality: params.quality, outputFormat: params.outputFormat, background: params.background, providerOptions: params.openaiBackground || params.openaiModeration ? { openai: { ...params.openaiBackground ? { background: params.openaiBackground } : {}, ...params.openaiModeration ? { moderation: params.openaiModeration } : {} } } : void 0, timeoutMs: params.timeoutMs, inputImages }); const outputs = await Promise.all(result.images.map(async (image, index) => { const written = await writeOutputAsset({ buffer: image.buffer, mimeType: image.mimeType, originalFilename: image.fileName, outputPath: params.output, outputIndex: index, outputCount: result.images.length, subdir: "generated" }); const metadata = await getImageMetadata(image.buffer).catch(() => void 0); return { ...written, width: metadata?.width, height: metadata?.height, revisedPrompt: image.revisedPrompt }; })); return { ok: true, capability: params.capability, transport: "local", provider: result.provider, model: result.model, attempts: result.attempts, outputs, ignoredOverrides: result.ignoredOverrides }; } async function runImageDescribe(params) { const cfg = await resolveLocalCapabilityRuntimeConfig({ commandName: `infer ${params.capability}`, targetIds: getModelsCommandSecretTargetIds() }); const agentId = resolveCapabilityProviderAgentId(cfg, params.agent, `infer ${params.capability}`); const agentDir = resolveAgentDir(cfg, agentId); const activeModel = requireProviderModelOverride(params.model); const prompt = normalizeOptionalString(params.prompt); const outputs = await Promise.all(params.files.map(async (filePath) => { const resolvedPath = resolveImageDescribeInput(filePath); const isRemoteUrl = /^https?:\/\//i.test(resolvedPath); const preparedImage = activeModel ? await prepareImageDescriptionInput({ filePath: resolvedPath, ...isRemoteUrl ? { mediaUrl: resolvedPath } : {}, cfg, timeoutMs: params.timeoutMs }) : void 0; const result = activeModel && preparedImage ? await runWithImageModelFallback({ cfg, modelOverride: `${activeModel.provider}/${activeModel.model}`, run: async (provider, model) => { const described = await describePreparedImageWithModel({ image: preparedImage, cfg, agentDir, provider, model, prompt: prompt ?? "Describe the image.", timeoutMs: params.timeoutMs }); if (!described.text?.trim()) throw new Error(`No description returned for image: ${resolvedPath}`); return described; } }) : { result: await describeImageFile({ filePath: resolvedPath, ...isRemoteUrl ? { mediaUrl: resolvedPath } : {}, cfg, agentDir, prompt, timeoutMs: params.timeoutMs }), provider: void 0, model: void 0, attempts: [] }; if (!result.result.text) { if (isMissingMediaUnderstandingProvider(result.result)) throw new Error("No image understanding provider is configured or ready. Configure an image-capable tools.media.models entry or agents.defaults.imageModel.primary, or pass --model <provider/model> after configuring that provider's auth/API key."); throw new Error(`No description returned for image: ${resolvedPath}`); } return { path: resolvedPath, text: result.result.text, provider: result.provider ?? result.result.provider, model: result.result.model ?? result.model, attempts: result.attempts, kind: "image.description" }; })); return { ok: true, capability: params.capability, transport: "local", provider: outputs[0]?.provider, model: outputs[0]?.model, attempts: outputs.flatMap((output) => output.attempts), outputs: outputs.map(({ attempts: _attempts, ...output }) => output) }; } function normalizeImageOutputFormat(raw) { const normalized = normalizeLowercaseStringOrEmpty(raw); if (!normalized) return; if (IMAGE_OUTPUT_FORMATS.includes(normalized)) return normalized; throw new Error("--output-format must be one of png, jpeg, or webp"); } function normalizeImageBackground(raw, label = "--background") { const normalized = normalizeLowercaseStringOrEmpty(raw); if (!normalized) return; if (IMAGE_BACKGROUNDS.includes(normalized)) return normalized; throw new Error(`${label} must be one of transparent, opaque, or auto`); } function normalizeImageQuality(raw) { const normalized = normalizeLowercaseStringOrEmpty(raw); if (!normalized) return; if (normalized === "low" || normalized === "medium" || normalized === "high" || normalized === "auto") return normalized; throw new Error("--quality must be one of low, medium, high, or auto"); } function normalizeOpenAIModeration(raw) { const normalized = normalizeLowercaseStringOrEmpty(raw); if (!normalized) return; if (normalized === "low" || normalized === "auto") return normalized; throw new Error("--openai-moderation must be one of low or auto"); } function resolveImageDescribeInput(filePath) { const trimmed = filePath.trim(); return /^https?:\/\//i.test(trimmed) ? trimmed : path.resolve(filePath); } function addImageGenerationOptions(command) { return command.option("--model <provider/model>", "Model override").option("--count <n>", "Number of images").option("--size <size>", "Size hint like 1024x1024").option("--aspect-ratio <ratio>", "Aspect ratio hint like 16:9").option("--resolution <value>", "Resolution hint: 1K, 2K, or 4K").option("--output-format <format>", "Output format hint: png, jpeg, or webp").option("--background <value>", "Background hint: transparent, opaque, or auto").option("--openai-background <value>", "OpenAI background hint: transparent, opaque, or auto").option("--openai-moderation <value>", "OpenAI moderation hint: low or auto").option("--quality <value>", "Quality hint: low, medium, high, or auto").option("--timeout-ms <ms>", "Provider request timeout in milliseconds").option("--output <path>", "Output path").option("--agent <id>", "Agent whose saved provider auth is used (default: agents.defaults.systemAgent.agentId, then the sole agent)").option("--json", "Output JSON", false); } function readStringOption(opts, key) { const value = opts[key]; return typeof value === "string" ? value : void 0; } function resolveImageGenerationOptions(opts, command) { return { agent: resolveCapabilityAgentOption(command, opts.agent), model: opts.model, count: parseOptionalPositiveInteger(opts.count, "--count"), size: opts.size, aspectRatio: opts.aspectRatio, resolution: opts.resolution, outputFormat: normalizeImageOutputFormat(readStringOption(opts, "outputFormat")), background: normalizeImageBackground(readStringOption(opts, "background")), openaiBackground: normalizeImageBackground(opts.openaiBackground, "--openai-background"), openaiModeration: normalizeOpenAIModeration(readStringOption(opts, "openaiModeration")), quality: normalizeImageQuality(readStringOption(opts, "quality")), timeoutMs: parseOptionalTimeoutMs(opts.timeoutMs), output: opts.output }; } function registerImageCapabilityCommands(capability) { const image = capability.command("image").description("Image generation and description").option("--agent <id>", "Agent whose model and auth state should be used"); addImageGenerationOptions(image.command("generate").description("Generate images").requiredOption("--prompt <text>", "Prompt text")).action(async (opts, command) => { await runCommandWithRuntime(defaultRuntime, async () => { const result = await runImageGenerate({ capability: "image.generate", prompt: String(opts.prompt), ...resolveImageGenerationOptions(opts, command) }); emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText); }); }); addImageGenerationOptions(image.command("edit").description("Edit images with one or more input files").requiredOption("--file <path>", "Input file", collectOption).requiredOption("--prompt <text>", "Prompt text")).action(async (opts, command) => { await runCommandWithRuntime(defaultRuntime, async () => { const files = Array.isArray(opts.file) ? opts.file : [String(opts.file)]; const result = await runImageGenerate({ capability: "image.edit", prompt: String(opts.prompt), file: files, ...resolveImageGenerationOptions(opts, command) }); emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText); }); }); image.command("describe").description("Describe one image file").requiredOption("--file <path>", "Image file").option("--prompt <text>", "Prompt hint").option("--model <provider/model>", "Model override").option("--timeout-ms <ms>", "Provider request timeout in milliseconds").option("--agent <id>", "Agent whose saved provider auth is used (default: agents.defaults.systemAgent.agentId, then the sole agent)").option("--json", "Output JSON", false).action(async (opts, command) => { await runCommandWithRuntime(defaultRuntime, async () => { const result = await runImageDescribe({ capability: "image.describe", files: [String(opts.file)], model: opts.model, prompt: opts.prompt, timeoutMs: parseOptionalTimeoutMs(opts.timeoutMs), agent: resolveCapabilityAgentOption(command, opts.agent) }); emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText); }); }); image.command("describe-many").description("Describe multiple image files").requiredOption("--file <path>", "Image file", collectOption).option("--prompt <text>", "Prompt hint").option("--model <provider/model>", "Model override").option("--timeout-ms <ms>", "Provider request timeout in milliseconds").option("--agent <id>", "Agent whose saved provider auth is used (default: agents.defaults.systemAgent.agentId, then the sole agent)").option("--json", "Output JSON", false).action(async (opts, command) => { await runCommandWithRuntime(defaultRuntime, async () => { const result = await runImageDescribe({ capability: "image.describe-many", files: opts.file, model: opts.model, prompt: opts.prompt, timeoutMs: parseOptionalTimeoutMs(opts.timeoutMs), agent: resolveCapabilityAgentOption(command, opts.agent) }); emitJsonOrText(defaultRuntime, Boolean(opts.json), result, formatEnvelopeForText); }); }); image.command("providers").description("List image generation providers").option("--agent <id>", "Agent whose provider state should be inspected").option("--json", "Output JSON", false).action(async (opts, command) => { await runCommandWithRuntime(defaultRuntime, async () => { const cfg = getRuntimeConfig(); const agentId = resolveCapabilityProviderAgentId(cfg, resolveCapabilityAgentOption(command, opts.agent)); const selectedProvider = resolveSelectedProviderFromModelRef(resolveAgentModelPrimaryValue(cfg.agents?.defaults?.mediaModels?.image)); const result = listRuntimeImageGenerationProviders({ config: cfg }).map((provider) => ({ available: true, configured: selectedProvider === provider.id || providerHasGenericConfig({ cfg, providerId: provider.id, agentId }), selected: selectedProvider === provider.id, id: provider.id, label: provider.label, defaultModel: provider.defaultModel, models: provider.models ?? [], capabilities: provider.capabilities })); emitJsonOrText(defaultRuntime, Boolean(opts.json), result, providerSummaryText); }); }); } //#endregion export { registerImageCapabilityCommands };