openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
214 lines (213 loc) • 10.6 kB
JavaScript
import { l as normalizeOptionalString } from "./string-coerce-1VuiLaaR.mjs";
import { n as detectMime } from "./mime-C3j3dfQb.mjs";
import { t as createEnumOptionParser } from "./enum-option-Dnt8s0vT.mjs";
import { a as providerSummaryText, i as formatEnvelopeForText, n as runCapabilityCommand, t as registerLocalProvidersCommand } from "./providers-command-B5dG8bOG.mjs";
import { t as isMissingMediaUnderstandingProvider } from "./media-understanding-result-CCD7dk7R.mjs";
import { t as collectOption } from "./helpers-CZZt1vds.mjs";
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"
];
const IMAGE_QUALITIES = [
"low",
"medium",
"high",
"xhigh",
"max",
"auto"
];
const IMAGE_MODERATIONS = ["low", "auto"];
const parseImageOption = createEnumOptionParser();
function registerImageCapabilityCommands(capabilityCommand) {
const imageCommand = capabilityCommand.command("image").description("Image generation and description").option("--agent <id>", "Agent whose model and auth state should be used");
for (const [commandName, description] of [["generate", "Generate images"], ["edit", "Edit images with one or more input files"]]) {
const generate = imageCommand.command(commandName).description(description);
if (commandName === "edit") generate.requiredOption("--file <path>", "Input file", collectOption);
generate.requiredOption("--prompt <text>", "Prompt text").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, xhigh, max, 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).action((opts, command) => runCapabilityCommand(opts.json, formatEnvelopeForText, async () => {
const capability = `image.${commandName}`;
const prompt = String(opts.prompt);
const file = commandName === "edit" ? Array.isArray(opts.file) ? opts.file : [String(opts.file)] : void 0;
const { resolveCapabilityAgentOption, parseOptionalPositiveInteger, parseOptionalTimeoutMs, requireProviderModelOverride, resolveLocalCapabilityAgent } = await import("./shared-CFOpEpOD.mjs");
const agent = resolveCapabilityAgentOption(command, opts.agent);
const model = opts.model;
const count = parseOptionalPositiveInteger(opts.count, "--count");
const size = opts.size;
const aspectRatio = opts.aspectRatio;
const resolution = opts.resolution;
const outputFormat = parseImageOption(opts.outputFormat, IMAGE_OUTPUT_FORMATS, "--output-format");
const background = parseImageOption(opts.background, IMAGE_BACKGROUNDS, "--background");
const openaiBackground = parseImageOption(opts.openaiBackground, IMAGE_BACKGROUNDS, "--openai-background");
const openaiModeration = parseImageOption(opts.openaiModeration, IMAGE_MODERATIONS, "--openai-moderation");
const quality = parseImageOption(opts.quality, IMAGE_QUALITIES, "--quality");
const timeoutMs = parseOptionalTimeoutMs(opts.timeoutMs);
const output = opts.output;
const { getModelsCommandSecretTargetIds } = await import("./command-secret-targets-xFD7VvKI.mjs");
const { generateImage } = await import("./runtime-DWL9vGew.mjs");
const { getImageMetadata } = await import("./media-services-ChUjSIoP.mjs");
const { readInputFiles, writeOutputAsset } = await import("./media-output-BuKNgNdI.mjs");
requireProviderModelOverride(model);
const { cfg, agentDir } = await resolveLocalCapabilityAgent({
commandName: `infer ${capability}`,
targetIds: getModelsCommandSecretTargetIds(),
agent
});
const inputImages = file && file.length > 0 ? await Promise.all((await readInputFiles(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,
modelOverride: model,
count,
size,
aspectRatio,
resolution,
quality,
outputFormat,
background,
providerOptions: openaiBackground || openaiModeration ? { openai: {
...openaiBackground ? { background: openaiBackground } : {},
...openaiModeration ? { moderation: openaiModeration } : {}
} } : void 0,
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: 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,
transport: "local",
provider: result.provider,
model: result.model,
attempts: result.attempts,
outputs,
ignoredOverrides: result.ignoredOverrides
};
}));
}
for (const [commandName, description] of [["describe", "Describe one image file"], ["describe-many", "Describe multiple image files"]]) {
const describe = imageCommand.command(commandName).description(description);
const multiple = commandName === "describe-many";
if (multiple) describe.requiredOption("--file <path>", "Image file", collectOption);
else describe.requiredOption("--file <path>", "Image file");
describe.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((opts, command) => runCapabilityCommand(opts.json, formatEnvelopeForText, async () => {
const { parseOptionalTimeoutMs, resolveCapabilityAgentOption, requireProviderModelOverride, resolveLocalCapabilityAgent } = await import("./shared-CFOpEpOD.mjs");
const capability = `image.${commandName}`;
const files = multiple ? opts.file : [String(opts.file)];
const requestedModel = opts.model;
const promptInput = opts.prompt;
const timeoutMs = parseOptionalTimeoutMs(opts.timeoutMs);
const agent = resolveCapabilityAgentOption(command, opts.agent);
const { getModelsCommandSecretTargetIds } = await import("./command-secret-targets-xFD7VvKI.mjs");
const { runWithImageModelFallback } = await import("./model-fallback-image-n2hoJGy7.mjs");
const { describeImageFile, describePreparedImageWithModel, prepareImageDescriptionInput } = await import("./runtime-gL2yE7df.mjs");
const { cfg, agentId, agentDir } = await resolveLocalCapabilityAgent({
commandName: `infer ${capability}`,
targetIds: getModelsCommandSecretTargetIds(),
agent
});
const activeModel = requireProviderModelOverride(requestedModel);
const prompt = normalizeOptionalString(promptInput);
const outputs = await Promise.all(files.map(async (filePath) => {
const trimmed = filePath.trim();
const resolvedPath = /^https?:\/\//i.test(trimmed) ? trimmed : path.resolve(filePath);
const isRemoteUrl = /^https?:\/\//i.test(resolvedPath);
const preparedImage = activeModel ? await prepareImageDescriptionInput({
filePath: resolvedPath,
...isRemoteUrl ? { mediaUrl: resolvedPath } : {},
cfg,
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,
agentId,
agentDir,
provider,
model,
prompt: prompt ?? "Describe the image.",
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,
agentId,
agentDir,
prompt,
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,
transport: "local",
provider: outputs[0]?.provider,
model: outputs[0]?.model,
attempts: outputs.flatMap((output) => output.attempts),
outputs: outputs.map(({ attempts: _attempts, ...output }) => output)
};
}));
}
registerLocalProvidersCommand(imageCommand, "List image generation providers", async (cfg, agentId) => {
const { listGenerationProviders } = await import("./media-providers-BKGgWF5p.mjs");
return listGenerationProviders("image", cfg, agentId);
}, providerSummaryText);
}
//#endregion
export { registerImageCapabilityCommands };