openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
259 lines (258 loc) • 11 kB
JavaScript
import { l as normalizeOptionalString } from "./string-coerce-1VuiLaaR.mjs";
import { i as readResponseWithLimit } from "./http-response-body-umfRGZ-E.mjs";
import { n as assertOkOrThrowHttpError } from "./provider-http-errors-DmewLLH7.mjs";
import { p as sanitizeConfiguredModelProviderRequest } from "./provider-request-config-By9R9pyt.mjs";
import { r as extensionForMime } from "./mime-CGDYrRzL.mjs";
import "./string-coerce-runtime-50qKeWeL.mjs";
import { r as resolveGeneratedMediaMaxBytes } from "./configured-max-bytes-94qDtzvY.mjs";
import { _ as resolveProviderOperationTimeoutMs, d as postJsonRequest, h as resolveProviderHttpRequestConfig, i as createProviderOperationTimeoutError, r as createProviderOperationDeadline } from "./shared-DcnGQdHV.mjs";
import "./response-limit-runtime-CpfmMF5T.mjs";
import { t as isProviderApiKeyConfigured } from "./provider-auth-availability-GiSm5QUa.mjs";
import "./media-mime-DLFR6fqC.mjs";
import "./media-generation-runtime-DrYRW4VG.mjs";
import { a as resolveApiKeyForProvider } from "./provider-auth-runtime-JvyP44zh.mjs";
import "./provider-http-CuRGJD0L.mjs";
import { t as downloadGeneratedMusicAsset } from "./music-generation-D-5knzUP.mjs";
import "./provider-auth-C0Mnu3Sx.mjs";
import { a as resolveMinimaxMediaBaseUrl, i as normalizeMinimaxHexAudio, n as assertMinimaxBaseResp, r as fetchMinimaxResponse, t as DEFAULT_MINIMAX_MEDIA_BASE_URL } from "./media-provider-runtime-3IuGITmK.mjs";
//#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 fetchMinimaxResponse({
stage: "download",
url: params.url,
init: { method: "GET" },
timeoutMs,
fetchFn: params.fetchFn,
requestFailedMessage: "MiniMax generated music download failed",
policy: params.policy
});
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 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: () => createProviderOperationTimeoutError(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 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 = normalizeOptionalString(req.model) ?? DEFAULT_MINIMAX_MUSIC_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 ? {
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 };