UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

276 lines (275 loc) 11.8 kB
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js"; import { i as readResponseWithLimit } from "./http-response-body-CwT_cCNz.js"; import { n as assertOkOrThrowHttpError } from "./provider-http-errors-U-nhuk_f.js"; import { r as extensionForMime } from "./mime-CVpcq9ju.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 { t as executeProviderOperationWithRetry } from "./operation-retry-v4wKh_39.js"; import { h as resolveProviderOperationTimeoutMs, l as postJsonRequest, p as resolveProviderHttpRequestConfig, r as createProviderOperationDeadline, s as fetchWithTimeoutGuarded } from "./shared-DCDpYrmy.js"; import "./response-limit-runtime-BV0RL9tn.js"; import "./string-coerce-runtime-GQa0ehRA.js"; import "./media-mime-BNY9NrbL.js"; import "./media-generation-runtime-Dd81xPqM.js"; import { a as resolveApiKeyForProvider } from "./provider-auth-runtime-BqyR7mkf.js"; import "./provider-http-k9RMI7iG.js"; import { t as downloadGeneratedMusicAsset } from "./music-generation-CAe8vdz7.js"; import { a as resolveMinimaxMediaBaseUrl, i as resolveMinimaxGuardedRequestOptions, n as assertMinimaxBaseResp, r as normalizeMinimaxHexAudio, t as DEFAULT_MINIMAX_MEDIA_BASE_URL } from "./media-provider-runtime-BNxo_XGD.js"; //#region extensions/minimax/music-generation-provider.ts const DEFAULT_MINIMAX_MUSIC_MODEL = "music-2.6"; const DEFAULT_TIMEOUT_MS = 12e4; const DEFAULT_OPERATION_TIMEOUT_MS = 3e5; const STREAM_ENVELOPE_MAX_BYTES_MULTIPLIER = 5; const STREAM_ENVELOPE_OVERHEAD_BYTES = 65536; function decodeHexAudioWithLimit(data, maxBytes) { const trimmed = normalizeMinimaxHexAudio(data, "MiniMax music generation"); if (trimmed.length / 2 > maxBytes) throw createGeneratedMusicTooLargeError(maxBytes); return Buffer.from(trimmed, "hex"); } function decodePossibleText(data) { const trimmed = data.trim(); if (!trimmed) return ""; if (/^[0-9a-f]+$/iu.test(trimmed) && trimmed.length % 2 === 0) return Buffer.from(trimmed, "hex").toString("utf8").trim(); return trimmed; } function isLikelyRemoteUrl(value) { const trimmed = normalizeOptionalString(value); return Boolean(trimmed && /^https?:\/\//iu.test(trimmed)); } async function downloadTrackFromUrl(params) { return await downloadGeneratedMusicAsset({ candidate: { url: params.url }, timeoutMs: params.timeoutMs ?? DEFAULT_TIMEOUT_MS, fetchFn: params.fetchFn, provider: "MiniMax", requestFailedMessage: "MiniMax generated music download failed", maxBytes: params.maxBytes, validateBinaryResponse: true, includeSourceUrl: false, fetchResponse: async ({ timeoutMs }) => { const result = await executeProviderOperationWithRetry({ provider: "minimax", stage: "download", operation: async () => { const guardedResult = await fetchWithTimeoutGuarded(params.url, { method: "GET" }, timeoutMs(), params.fetchFn, resolveMinimaxGuardedRequestOptions(params.policy)); try { await assertOkOrThrowHttpError(guardedResult.response, "MiniMax generated music download failed"); } catch (error) { await guardedResult.release(); throw error; } return guardedResult; } }); return { ...result, mimeType: normalizeOptionalString(result.response.headers.get("content-type")) ?? "audio/mpeg" }; } }); } function resolveBodyReadTimeoutMs(deadline) { return resolveProviderOperationTimeoutMs({ deadline, defaultTimeoutMs: deadline.timeoutMs ?? DEFAULT_OPERATION_TIMEOUT_MS }); } function createGeneratedMusicTooLargeError(maxBytes) { return /* @__PURE__ */ new Error(`MiniMax generated music download exceeds ${maxBytes} bytes`); } function createMinimaxMusicTimeoutError(deadline) { const timeoutLabel = typeof deadline.timeoutMs === "number" ? ` after ${deadline.timeoutMs}ms` : ""; return /* @__PURE__ */ new Error(`${deadline.label} timed out${timeoutLabel}`); } function resolveStreamEnvelopeMaxBytes(maxBytes) { return Math.max(STREAM_ENVELOPE_OVERHEAD_BYTES, maxBytes * STREAM_ENVELOPE_MAX_BYTES_MULTIPLIER + STREAM_ENVELOPE_OVERHEAD_BYTES); } async function readResponseBufferWithDeadline(response, deadline, maxBytes) { return await readResponseWithLimit(response, maxBytes, { timeoutMs: () => resolveBodyReadTimeoutMs(deadline), onTimeout: () => createMinimaxMusicTimeoutError(deadline), onOverflow: ({ maxBytes: limit }) => createGeneratedMusicTooLargeError(limit) }); } async function readStreamingTrack(response, deadline, maxBytes) { const contentType = normalizeOptionalString(response.headers.get("content-type")) ?? ""; if (contentType.toLowerCase().startsWith("audio/")) { const ext = extensionForMime(contentType)?.replace(/^\./u, "") || "mp3"; return { buffer: await readResponseBufferWithDeadline(response, deadline, maxBytes), mimeType: contentType, fileName: `track-1.${ext}` }; } const chunks = []; let decodedBytes = 0; let completed = false; const text = new TextDecoder().decode(await readResponseBufferWithDeadline(response, deadline, resolveStreamEnvelopeMaxBytes(maxBytes))); for (const rawLine of text.split(/\r?\n/u)) { const line = rawLine.trim(); if (!line.startsWith("data:")) continue; const json = line.slice(5).trim(); if (!json || json === "[DONE]") continue; const frame = JSON.parse(json); assertMinimaxBaseResp(frame.base_resp, "MiniMax music generation failed"); if (String(frame.data?.status ?? "") === "2") { completed = true; if (chunks.length > 0) continue; } const audio = normalizeOptionalString(frame.data?.audio); if (audio) { const chunk = decodeHexAudioWithLimit(audio, maxBytes - decodedBytes); chunks.push(chunk); decodedBytes += chunk.byteLength; } } if (!completed) throw new Error("MiniMax music generation stream ended without completion"); const buffer = Buffer.concat(chunks); if (buffer.byteLength === 0) throw new Error("MiniMax music generation response missing audio output"); return { buffer, mimeType: "audio/mpeg", fileName: "track-1.mp3" }; } function resolveMinimaxMusicModel(model) { const trimmed = normalizeOptionalString(model); if (!trimmed) return DEFAULT_MINIMAX_MUSIC_MODEL; return trimmed; } function buildMinimaxMusicProvider(providerId) { return { id: providerId, label: "MiniMax", defaultModel: DEFAULT_MINIMAX_MUSIC_MODEL, models: [ DEFAULT_MINIMAX_MUSIC_MODEL, "music-2.6-free", "music-cover", "music-cover-free" ], isConfigured: (ctx) => isProviderApiKeyConfigured({ provider: providerId, ...ctx }), capabilities: { generate: { maxTracks: 1, supportsLyrics: true, supportsInstrumental: true, supportsFormat: true, supportedFormats: ["mp3"] }, edit: { enabled: false } }, async generateMusic(req) { if ((req.inputImages?.length ?? 0) > 0) throw new Error("MiniMax music generation does not support image reference inputs."); if (req.instrumental === true && normalizeOptionalString(req.lyrics)) throw new Error("MiniMax music generation cannot use lyrics when instrumental=true."); if (req.format && req.format !== "mp3") throw new Error("MiniMax music generation currently supports mp3 output only."); const auth = await resolveApiKeyForProvider({ provider: providerId, cfg: req.cfg, agentDir: req.agentDir, store: req.authStore }); if (!auth.apiKey) throw new Error("MiniMax API key missing"); const fetchFn = fetch; const operationTimeoutMs = req.timeoutMs ?? DEFAULT_OPERATION_TIMEOUT_MS; const deadline = createProviderOperationDeadline({ timeoutMs: operationTimeoutMs, label: "MiniMax music generation" }); const { baseUrl, allowPrivateNetwork, headers, dispatcherPolicy } = resolveProviderHttpRequestConfig({ baseUrl: resolveMinimaxMediaBaseUrl(req.cfg, providerId), defaultBaseUrl: DEFAULT_MINIMAX_MEDIA_BASE_URL, defaultHeaders: { Authorization: `Bearer ${auth.apiKey}` }, provider: providerId, capability: "audio", transport: "http", request: sanitizeConfiguredModelProviderRequest(req.cfg.models?.providers?.[providerId]?.request) }); const requestPolicy = { allowPrivateNetwork, dispatcherPolicy }; const jsonHeaders = new Headers(headers); jsonHeaders.set("Content-Type", "application/json"); const model = resolveMinimaxMusicModel(req.model); const requestedLyrics = normalizeOptionalString(req.lyrics); const body = { model, prompt: req.prompt.trim(), ...req.instrumental === true ? { is_instrumental: true } : {}, ...requestedLyrics ? { lyrics: requestedLyrics } : req.instrumental === true ? {} : { lyrics_optimizer: true }, stream: true, output_format: "hex", audio_setting: { sample_rate: 44100, bitrate: 256e3, format: "mp3" } }; const { response: res, release } = await postJsonRequest({ url: `${baseUrl}/v1/music_generation`, headers: jsonHeaders, body, timeoutMs: resolveProviderOperationTimeoutMs({ deadline, defaultTimeoutMs: operationTimeoutMs }), fetchFn, pinDns: false, allowPrivateNetwork, dispatcherPolicy }); try { await assertOkOrThrowHttpError(res, "MiniMax music generation failed"); const lowerContentType = (normalizeOptionalString(res.headers.get("content-type")) ?? "").toLowerCase(); const maxGeneratedMusicBytes = resolveGeneratedMediaMaxBytes(req.cfg, "audio"); const payload = lowerContentType.includes("text/event-stream") || lowerContentType.startsWith("audio/") ? null : JSON.parse(new TextDecoder().decode(await readResponseBufferWithDeadline(res.clone(), deadline, resolveStreamEnvelopeMaxBytes(maxGeneratedMusicBytes)))); if (payload) assertMinimaxBaseResp(payload.base_resp, "MiniMax music generation failed"); const audioCandidate = normalizeOptionalString(payload?.audio) ?? normalizeOptionalString(payload?.data?.audio); const audioUrl = normalizeOptionalString(payload?.audio_url) || normalizeOptionalString(payload?.data?.audio_url) || (isLikelyRemoteUrl(audioCandidate) ? audioCandidate : void 0); const inlineAudio = isLikelyRemoteUrl(audioCandidate) ? void 0 : audioCandidate; const responseLyrics = decodePossibleText(payload?.lyrics ?? payload?.data?.lyrics ?? ""); return { tracks: [audioUrl ? await downloadTrackFromUrl({ url: audioUrl, timeoutMs: resolveProviderOperationTimeoutMs({ deadline, defaultTimeoutMs: req.timeoutMs ?? DEFAULT_TIMEOUT_MS }), fetchFn, maxBytes: maxGeneratedMusicBytes, policy: requestPolicy }) : inlineAudio ? (() => { return { buffer: decodeHexAudioWithLimit(inlineAudio, maxGeneratedMusicBytes), mimeType: "audio/mpeg", fileName: "track-1.mp3" }; })() : await readStreamingTrack(res, deadline, maxGeneratedMusicBytes)], ...responseLyrics ? { lyrics: [responseLyrics] } : {}, model, metadata: { ...normalizeOptionalString(payload?.task_id) ? { taskId: normalizeOptionalString(payload?.task_id) } : {}, ...audioUrl ? { audioUrl } : {}, instrumental: req.instrumental === true, ...requestedLyrics ? { requestedLyrics: true } : {} } }; } finally { await release(); } } }; } function buildMinimaxMusicGenerationProvider() { return buildMinimaxMusicProvider("minimax"); } function buildMinimaxPortalMusicGenerationProvider() { return buildMinimaxMusicProvider("minimax-portal"); } //#endregion export { buildMinimaxPortalMusicGenerationProvider as n, buildMinimaxMusicGenerationProvider as t };