openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
204 lines (203 loc) • 8.9 kB
JavaScript
import { c as normalizeOptionalString, s as normalizeOptionalLowercaseString } from "./string-coerce-mnp54Vah.js";
import { n as readResponseWithLimit } from "./read-response-with-limit-MDCSJrlg.js";
import { r as fetchWithTimeout } from "./fetch-timeout-CDaw0X8V.js";
import { r as extensionForMime } from "./mime-C8mVE2Bw.js";
import { r as assertOkOrThrowHttpError } from "./provider-http-errors-DqaqQLLZ.js";
import { h as waitProviderOperationPollInterval, m as resolveProviderOperationTimeoutMs, n as createProviderOperationDeadline, p as resolveProviderHttpRequestConfig } from "./shared-IuCmx8oC.js";
import "./string-coerce-runtime-CEGJWkQ_.js";
import "./media-mime-v9DSctG7.js";
import "./response-limit-runtime-B0ZHF0eR.js";
import { a as resolveApiKeyForProvider } from "./provider-auth-runtime-D2xYnWtb.js";
import "./provider-http-BXCBCi4E.js";
//#region extensions/vydra/shared.ts
const DEFAULT_VYDRA_BASE_URL = "https://www.vydra.ai/api/v1";
const DEFAULT_VYDRA_IMAGE_MODEL = "grok-imagine";
const DEFAULT_VYDRA_VIDEO_MODEL = "veo3";
const DEFAULT_VYDRA_SPEECH_MODEL = "elevenlabs/tts";
const DEFAULT_VYDRA_VOICE_ID = "21m00Tcm4TlvDq8ikWAM";
const DEFAULT_HTTP_TIMEOUT_MS = 12e4;
const DEFAULT_GENERATED_IMAGE_MAX_BYTES = 6 * 1024 * 1024;
const DEFAULT_GENERATED_AUDIO_MAX_BYTES = 16 * 1024 * 1024;
const DEFAULT_GENERATED_VIDEO_MAX_BYTES = 16 * 1024 * 1024;
const POLL_INTERVAL_MS = 2500;
const MAX_POLL_ATTEMPTS = 120;
function asObject(value) {
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
}
function addUrlValue(value, urls) {
if (typeof value === "string") {
const trimmed = value.trim();
if (/^https?:\/\//iu.test(trimmed)) urls.add(trimmed);
return;
}
if (Array.isArray(value)) for (const entry of value) addUrlValue(entry, urls);
}
const trimToUndefined = normalizeOptionalString;
function normalizeVydraBaseUrl(value) {
const fallback = DEFAULT_VYDRA_BASE_URL;
const trimmed = trimToUndefined(value);
if (!trimmed) return fallback;
try {
const url = new URL(trimmed);
if (url.hostname === "vydra.ai") url.hostname = "www.vydra.ai";
const pathname = url.pathname.replace(/\/+$/u, "");
if (!pathname) url.pathname = "/api/v1";
else url.pathname = pathname;
return url.toString().replace(/\/$/u, "");
} catch {
return fallback;
}
}
function resolveVydraBaseUrlFromConfig(cfg) {
return normalizeVydraBaseUrl(trimToUndefined(asObject(asObject(asObject(asObject(cfg)?.models)?.providers)?.vydra)?.baseUrl));
}
async function resolveVydraRequestContext(params) {
const auth = await resolveApiKeyForProvider({
provider: "vydra",
cfg: params.cfg,
agentDir: params.agentDir,
store: params.authStore
});
if (!auth.apiKey) throw new Error("Vydra API key missing");
const fetchFn = fetch;
const { baseUrl, allowPrivateNetwork, headers, dispatcherPolicy } = resolveProviderHttpRequestConfig({
baseUrl: resolveVydraBaseUrlFromConfig(params.cfg),
defaultBaseUrl: DEFAULT_VYDRA_BASE_URL,
allowPrivateNetwork: false,
defaultHeaders: {
Authorization: `Bearer ${auth.apiKey}`,
"Content-Type": "application/json"
},
provider: "vydra",
capability: params.capability,
transport: "http"
});
return {
fetchFn,
baseUrl,
allowPrivateNetwork,
headers,
dispatcherPolicy
};
}
function resolveVydraResponseJobId(payload) {
const object = asObject(payload);
return trimToUndefined(object?.jobId) ?? trimToUndefined(object?.id);
}
function resolveVydraResponseStatus(payload) {
return normalizeOptionalLowercaseString(trimToUndefined(asObject(payload)?.status));
}
function resolveVydraErrorMessage(payload) {
const object = asObject(payload);
const error = object?.error;
if (typeof error === "string" && error.trim()) return error.trim();
const errorObject = asObject(error);
return trimToUndefined(errorObject?.message) ?? trimToUndefined(errorObject?.detail) ?? trimToUndefined(object?.message);
}
function extractVydraResultUrls(payload, kind) {
const urls = /* @__PURE__ */ new Set();
const preferredKeys = kind === "audio" ? ["audioUrl", "audioUrls"] : kind === "image" ? ["imageUrl", "imageUrls"] : ["videoUrl", "videoUrls"];
const sharedKeys = [
"resultUrl",
"resultUrls",
"outputUrl",
"outputUrls",
"url",
"urls"
];
const recurseKeys = [
"output",
"outputs",
"result",
"results",
"data",
"asset",
"assets"
];
const visit = (value, depth = 0) => {
if (depth > 5) return;
if (Array.isArray(value)) {
for (const entry of value) visit(entry, depth + 1);
return;
}
const object = asObject(value);
if (!object) return;
for (const key of [...preferredKeys, ...sharedKeys]) addUrlValue(object[key], urls);
for (const key of recurseKeys) if (key in object) visit(object[key], depth + 1);
};
visit(payload);
return [...urls];
}
function resolveVydraFileExtension(kind, mimeType) {
return extensionForMime(mimeType)?.slice(1) ?? (kind === "image" ? "png" : kind === "audio" ? "mp3" : "mp4");
}
function resolveVydraHttpTimeoutMs(timeoutMs) {
const resolved = typeof timeoutMs === "function" ? timeoutMs() : timeoutMs;
if (typeof resolved !== "number" || !Number.isFinite(resolved) || resolved <= 0) return DEFAULT_HTTP_TIMEOUT_MS;
return resolved;
}
function resolveVydraGeneratedMediaMaxBytes(params) {
const configured = params.cfg.agents?.defaults?.mediaMaxMb;
if (typeof configured === "number" && Number.isFinite(configured) && configured > 0) return Math.floor(configured * 1024 * 1024);
if (params.kind === "image") return DEFAULT_GENERATED_IMAGE_MAX_BYTES;
if (params.kind === "audio") return DEFAULT_GENERATED_AUDIO_MAX_BYTES;
return DEFAULT_GENERATED_VIDEO_MAX_BYTES;
}
async function downloadVydraAsset(params) {
const timeoutMs = resolveVydraHttpTimeoutMs(params.timeoutMs);
const response = await fetchWithTimeout(params.url, { method: "GET" }, timeoutMs, params.fetchFn);
await assertOkOrThrowHttpError(response, `Vydra ${params.kind} download failed`);
const mimeType = response.headers.get("content-type")?.trim() || (params.kind === "image" ? "image/png" : params.kind === "audio" ? "audio/mpeg" : "video/mp4");
const buffer = await readResponseWithLimit(response, params.maxBytes, {
chunkTimeoutMs: timeoutMs,
onOverflow: ({ maxBytes }) => /* @__PURE__ */ new Error(`Vydra ${params.kind} download exceeds ${maxBytes} bytes`),
onIdleTimeout: ({ chunkTimeoutMs }) => /* @__PURE__ */ new Error(`Vydra ${params.kind} download stalled after ${chunkTimeoutMs}ms`)
});
const extension = resolveVydraFileExtension(params.kind, mimeType);
return {
buffer,
mimeType,
fileName: `${params.kind === "image" ? "image" : params.kind === "audio" ? "audio" : "video"}-1.${extension}`
};
}
async function waitForVydraJob(params) {
const deadline = params.deadline ?? createProviderOperationDeadline({
timeoutMs: params.timeoutMs,
label: `Vydra job ${params.jobId}`
});
for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt += 1) {
const response = await fetchWithTimeout(`${params.baseUrl}/jobs/${params.jobId}`, {
method: "GET",
headers: params.headers
}, resolveProviderOperationTimeoutMs({
deadline,
defaultTimeoutMs: DEFAULT_HTTP_TIMEOUT_MS
}), params.fetchFn);
await assertOkOrThrowHttpError(response, "Vydra job status request failed");
const payload = await response.json();
const status = resolveVydraResponseStatus(payload);
if (status === "completed" || extractVydraResultUrls(payload, params.kind).length > 0) return payload;
if (status === "failed" || status === "error" || status === "cancelled") throw new Error(resolveVydraErrorMessage(payload) ?? `Vydra job ${params.jobId} failed`);
await waitProviderOperationPollInterval({
deadline,
pollIntervalMs: POLL_INTERVAL_MS
});
}
throw new Error(`Vydra job ${params.jobId} did not finish in time`);
}
async function resolveCompletedVydraPayload(params) {
if (resolveVydraResponseStatus(params.submitted) === "completed" || extractVydraResultUrls(params.submitted, params.kind).length > 0) return params.submitted;
const jobId = resolveVydraResponseJobId(params.submitted);
if (!jobId) throw new Error(resolveVydraErrorMessage(params.submitted) ?? params.missingJobIdMessage);
return waitForVydraJob({
baseUrl: params.baseUrl,
jobId,
headers: params.headers,
timeoutMs: params.timeoutMs,
...params.deadline ? { deadline: params.deadline } : {},
fetchFn: params.fetchFn,
kind: params.kind
});
}
//#endregion
export { DEFAULT_VYDRA_VOICE_ID as a, normalizeVydraBaseUrl as c, resolveVydraRequestContext as d, resolveVydraResponseJobId as f, DEFAULT_VYDRA_VIDEO_MODEL as i, resolveCompletedVydraPayload as l, trimToUndefined as m, DEFAULT_VYDRA_IMAGE_MODEL as n, downloadVydraAsset as o, resolveVydraResponseStatus as p, DEFAULT_VYDRA_SPEECH_MODEL as r, extractVydraResultUrls as s, DEFAULT_VYDRA_BASE_URL as t, resolveVydraGeneratedMediaMaxBytes as u };