UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

263 lines (262 loc) 9.75 kB
import "./fs-safe-aqmM_n6V.js"; import { i as readLocalFileSafely } from "./secure-temp-dir-XAWcZnE2.js"; import { c as kindFromMime, l as mimeTypeFromFilePath } from "./mime-C8mVE2Bw.js"; import { n as normalizeMediaProviderId } from "./provider-id-DSbuCFIb.js"; import { t as describeImageWithModel } from "./image-runtime-CmS6zMcE.js"; import { d as getMediaUnderstandingProvider, n as DEFAULT_MAX_BYTES, u as buildMediaUnderstandingRegistry } from "./defaults.constants-A49pIMjc.js"; import { i as resolveMediaRuntimeTimeoutMs } from "./resolve-BmY6318x.js"; import { c as normalizeImageDescriptionInput, i as normalizeDecisionReason, n as findDecisionReason } from "./runner.entries-B23jIAju.js"; import { a as runCapability, o as createMediaAttachmentCache, s as normalizeMediaAttachments, t as buildProviderRegistry } from "./runner-CswjdYkd.js"; import path from "node:path"; //#region src/media-understanding/runtime.ts const KIND_BY_CAPABILITY = { audio: "audio.transcription", image: "image.description", video: "video.description" }; function resolveDecisionFailureReason(decision) { return normalizeDecisionReason(findDecisionReason(decision, "failed")); } function buildFileContext(params) { const scopeFields = { ...params.scopeContext?.sessionKey ? { SessionKey: params.scopeContext.sessionKey } : {}, ...params.scopeContext?.channel ? { Provider: params.scopeContext.channel, Surface: params.scopeContext.channel } : {}, ...params.scopeContext?.chatType ? { ChatType: params.scopeContext.chatType } : {} }; const remoteRef = params.mediaUrl ?? (isRemoteMediaReference(params.filePath) ? params.filePath.trim() : void 0); const extensionMime = remoteRef ? mimeTypeFromFilePath(remoteRef) : void 0; const extensionKind = kindFromMime(extensionMime); const mediaType = params.mime ?? (remoteRef && params.capability && extensionKind === params.capability ? `${params.capability}/*` : extensionMime) ?? (remoteRef && params.capability ? `${params.capability}/*` : void 0); if (remoteRef) return { MediaUrl: remoteRef, MediaType: mediaType, ...scopeFields }; return { MediaPath: params.filePath, MediaType: mediaType, ...scopeFields }; } function isRemoteMediaReference(value) { return /^https?:\/\//i.test(value.trim()); } function concreteMime(mime) { const normalized = mime?.trim(); if (!normalized || normalized.endsWith("/*")) return; return normalized; } function resolveFileLocalRoots(filePath) { return isRemoteMediaReference(filePath) ? void 0 : [path.dirname(filePath)]; } function basenameFromMediaReference(value) { if (isRemoteMediaReference(value)) try { const url = new URL(value); return path.basename(url.pathname) || "image"; } catch {} return path.basename(value); } function hasStructuredImageInput(input) { return input.some((entry) => entry.type === "image"); } /** Runs media understanding for one local file or remote URL and returns the first matching output. */ async function runMediaUnderstandingFile(params) { const requestPrompt = params.prompt?.trim(); const requestTimeoutSeconds = typeof params.timeoutMs === "number" && Number.isFinite(params.timeoutMs) && params.timeoutMs > 0 ? Math.ceil(params.timeoutMs / 1e3) : void 0; const cfg = requestPrompt || requestTimeoutSeconds !== void 0 ? { ...params.cfg, tools: { ...params.cfg.tools, media: { ...params.cfg.tools?.media, [params.capability]: { ...params.cfg.tools?.media?.[params.capability], ...requestPrompt ? { prompt: requestPrompt, _requestPromptOverride: requestPrompt } : {}, ...requestTimeoutSeconds !== void 0 ? { timeoutSeconds: requestTimeoutSeconds } : {} } } } } : params.cfg; const ctx = buildFileContext({ ...params, capability: params.capability, scopeContext: params.scopeContext }); const attachments = normalizeMediaAttachments(ctx); if (attachments.length === 0) return { text: void 0, decision: { capability: params.capability, outcome: "no-attachment", attachments: [] } }; const config = cfg.tools?.media?.[params.capability]; if (config?.enabled === false) return { text: void 0, provider: void 0, model: void 0, output: void 0, decision: { capability: params.capability, outcome: "disabled", attachments: [] } }; const providerRegistry = buildProviderRegistry(void 0, cfg); const cache = createMediaAttachmentCache(attachments, { localPathRoots: params.mediaUrl ? void 0 : resolveFileLocalRoots(params.filePath), ssrfPolicy: cfg.tools?.web?.fetch?.ssrfPolicy }); try { const result = await runCapability({ capability: params.capability, cfg, ctx, attachments: cache, media: attachments, agentDir: params.agentDir, ...params.workspaceDir ? { workspaceDir: params.workspaceDir } : {}, providerRegistry, config, activeModel: params.activeModel }); if (result.outputs.length === 0 && result.decision.outcome === "failed") throw new Error(resolveDecisionFailureReason(result.decision) ?? `${params.capability} understanding failed`); const output = result.outputs.find((entry) => entry.kind === KIND_BY_CAPABILITY[params.capability]); const fileResult = { text: output?.text?.trim() || void 0, provider: output?.provider, model: output?.model, output }; if (result.decision) fileResult.decision = result.decision; return fileResult; } finally { await cache.cleanup(); } } /** Describes one image file or URL through the configured image-understanding pipeline. */ async function describeImageFile(params) { return await runMediaUnderstandingFile({ ...params, capability: "image" }); } /** Describes one image with an explicit provider/model, bypassing configured media model selection. */ async function describeImageFileWithModel(params) { const timeoutMs = resolveMediaRuntimeTimeoutMs(params.timeoutMs); const provider = buildProviderRegistry(void 0, params.cfg).get(normalizeMediaProviderId(params.provider)); const image = await readImageDescriptionInput({ filePath: params.filePath, mediaUrl: params.mediaUrl, mime: params.mime, cfg: params.cfg, timeoutMs }); const normalizedImage = await normalizeImageDescriptionInput({ buffer: image.buffer, fileName: image.fileName, mime: image.mime, maxBytes: DEFAULT_MAX_BYTES.image }); return await (provider?.describeImage ?? describeImageWithModel)({ buffer: normalizedImage.buffer, fileName: image.fileName, mime: normalizedImage.mime, provider: params.provider, model: params.model, prompt: params.prompt, maxTokens: params.maxTokens, timeoutMs, cfg: params.cfg, agentDir: params.agentDir ?? "", ...params.workspaceDir ? { workspaceDir: params.workspaceDir } : {} }); } async function readImageDescriptionInput(params) { const remoteRef = params.mediaUrl ?? (isRemoteMediaReference(params.filePath) ? params.filePath.trim() : void 0); if (!remoteRef) return { buffer: (await readLocalFileSafely({ filePath: params.filePath })).buffer, fileName: basenameFromMediaReference(params.filePath), mime: params.mime }; const cache = createMediaAttachmentCache(normalizeMediaAttachments(buildFileContext({ ...params, capability: "image" })), { ssrfPolicy: params.cfg.tools?.web?.fetch?.ssrfPolicy }); try { const media = await cache.getBuffer({ attachmentIndex: 0, maxBytes: DEFAULT_MAX_BYTES.image, timeoutMs: params.timeoutMs }); return { buffer: media.buffer, fileName: media.fileName || basenameFromMediaReference(remoteRef), mime: concreteMime(params.mime) ?? media.mime }; } finally { await cache.cleanup(); } } /** Runs provider-backed structured extraction for multimodal text/image input. */ async function extractStructuredWithModel(params) { const timeoutMs = resolveMediaRuntimeTimeoutMs(params.timeoutMs); if (!hasStructuredImageInput(params.input)) throw new Error("Structured extraction requires at least one image input."); const provider = getMediaUnderstandingProvider(params.provider, buildMediaUnderstandingRegistry(void 0, params.cfg)); if (!provider?.extractStructured) throw new Error(`Provider does not support structured extraction: ${params.provider}`); return await provider.extractStructured({ input: params.input, instructions: params.instructions, schemaName: params.schemaName, jsonSchema: params.jsonSchema, jsonMode: params.jsonMode, provider: params.provider, model: params.model, profile: params.profile, preferredProfile: params.preferredProfile, authStore: params.authStore, timeoutMs, cfg: params.cfg, agentDir: params.agentDir ?? "" }); } /** Describes one video file or URL through the configured video-understanding pipeline. */ async function describeVideoFile(params) { return await runMediaUnderstandingFile({ ...params, capability: "video" }); } /** Transcribes one audio file or URL through the configured audio-understanding pipeline. */ async function transcribeAudioFile(params) { const cfg = params.language || params.prompt ? { ...params.cfg, tools: { ...params.cfg.tools, media: { ...params.cfg.tools?.media, audio: { ...params.cfg.tools?.media?.audio, ...params.language ? { _requestLanguageOverride: params.language } : {}, ...params.prompt ? { _requestPromptOverride: params.prompt } : {}, ...params.language ? { language: params.language } : {}, ...params.prompt ? { prompt: params.prompt } : {} } } } } : params.cfg; return await runMediaUnderstandingFile({ ...params, cfg, capability: "audio" }); } //#endregion export { runMediaUnderstandingFile as a, extractStructuredWithModel as i, describeImageFileWithModel as n, transcribeAudioFile as o, describeVideoFile as r, describeImageFile as t };