openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
166 lines (165 loc) • 6.96 kB
JavaScript
import { P as resolvePositiveTimerTimeoutMs } from "./number-coercion-CLj0HTDM.js";
import { t as decodeTextPrefix } from "./src-vebZIeLe.js";
import { c as isRecord } from "./record-coerce-DItp3I4t.js";
import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.js";
import "./utils-P__uGsPB.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { t as createSubsystemLogger } from "./subsystem-Dy2tqXOS.js";
import "./http-body-D3IMwTJJ.js";
import { n as readResponseTextPrefix } from "./http-response-body-CwT_cCNz.js";
import { a as createProviderErrorTextRedactor, m as readProviderJsonResponse } from "./provider-http-errors-U-nhuk_f.js";
import { n as normalizeSecretInput } from "./normalize-secret-input-Df_qhWv_.js";
import { r as resolveProviderTransportSsrFPolicy } from "./provider-transport-fetch-DGSx4u4m.js";
import { l as postJsonRequest, m as resolveProviderHttpRequestConfigWithOriginTrust } from "./shared-DCDpYrmy.js";
//#region src/infra/http-error-body.ts
const errorBodyLog = createSubsystemLogger("http-error-body");
async function readResponseBodySnippet(response, limits) {
const normalize = (text, truncated) => truncateUtf16Safe(limits.redact?.(text, { truncated }) ?? text, limits.maxChars);
try {
const body = response.body;
if (!body || typeof body.getReader !== "function") {
const text = await response.text();
const encoded = new TextEncoder().encode(text);
if (encoded.byteLength > limits.maxBytes) return normalize(decodeTextPrefix(encoded.subarray(0, limits.maxBytes), { truncated: true }), true);
return normalize(text, false);
}
const prefix = await readResponseTextPrefix(response, limits.maxBytes);
return normalize(prefix.text, prefix.truncated);
} catch (err) {
errorBodyLog.warn(`Failed to read response body snippet: ${formatErrorMessage(err)}`);
return "";
}
}
//#endregion
//#region src/agents/minimax-vlm.ts
/**
* Adapts MiniMax VLM image-understanding requests for agent image inputs.
*/
const MINIMAX_VLM_ERROR_BODY_MAX_BYTES = 8192;
const MINIMAX_VLM_ERROR_BODY_MAX_CHARS = 400;
const DEFAULT_MINIMAX_VLM_TIMEOUT_MS = 6e4;
function isMinimaxVlmProvider(provider) {
const normalized = provider.trim().toLowerCase();
return normalized === "minimax" || normalized === "minimax-cn" || normalized === "minimax-portal" || normalized === "minimax-portal-cn";
}
function isMinimaxVlmModel(provider, modelId) {
return isMinimaxVlmProvider(provider) && modelId.trim() === "MiniMax-VL-01";
}
function isMinimaxCnProvider(provider) {
const normalized = provider?.trim().toLowerCase();
return normalized === "minimax-cn" || normalized === "minimax-portal-cn";
}
function resolveDefaultApiHost(provider) {
return isMinimaxCnProvider(provider) ? "https://api.minimaxi.com" : "https://api.minimax.io";
}
function coerceApiHost(params) {
const env = params.env ?? process.env;
const defaultHost = resolveDefaultApiHost(params.provider);
const raw = params.apiHost?.trim() || env.MINIMAX_API_HOST?.trim() || params.modelBaseUrl?.trim() || defaultHost;
try {
return new URL(raw).origin;
} catch {}
if (/^[a-z][a-z\d+.-]*:\/\//i.test(raw)) return defaultHost;
try {
return new URL(`https://${raw}`).origin;
} catch {
return defaultHost;
}
}
function pickString(rec, key) {
const v = rec[key];
return typeof v === "string" ? v : "";
}
async function minimaxUnderstandImage(params) {
const apiKey = normalizeSecretInput(params.apiKey);
if (!apiKey) throw new Error("MiniMax VLM: apiKey required");
const prompt = params.prompt.trim();
if (!prompt) throw new Error("MiniMax VLM: prompt required");
const imageDataUrl = params.imageDataUrl.trim();
if (!imageDataUrl) throw new Error("MiniMax VLM: imageDataUrl required");
if (!/^data:image\/(png|jpeg|webp);base64,/i.test(imageDataUrl)) throw new Error("MiniMax VLM: imageDataUrl must be a base64 data:image/(png|jpeg|webp) URL");
const configuredHost = coerceApiHost({
apiHost: params.apiHost,
modelBaseUrl: params.modelBaseUrl,
provider: params.provider
});
const { baseUrl, allowPrivateNetwork, headers, dispatcherPolicy, trustConfiguredBaseUrlOrigin } = resolveProviderHttpRequestConfigWithOriginTrust({
baseUrl: configuredHost,
defaultBaseUrl: resolveDefaultApiHost(params.provider),
allowPrivateNetwork: params.allowPrivateNetwork,
defaultHeaders: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
"MM-API-Source": "OpenClaw"
},
request: params.request,
provider: params.provider ?? "minimax",
capability: "image",
transport: "media-understanding"
});
const url = new URL("/v1/coding_plan/vlm", baseUrl).toString();
const timeoutMs = resolvePositiveTimerTimeoutMs(params.timeoutMs, DEFAULT_MINIMAX_VLM_TIMEOUT_MS);
const ssrfPolicy = resolveProviderTransportSsrFPolicy({
baseUrl,
url,
allowPrivateNetwork,
trustConfiguredBaseUrlOrigin
});
const guarded = await postJsonRequest({
url,
headers,
body: {
prompt,
image_url: imageDataUrl
},
timeoutMs,
...params.signal ? { signal: params.signal } : {},
fetchFn: fetch,
allowPrivateNetwork,
ssrfPolicy,
dispatcherPolicy,
auditContext: "minimax-vlm"
});
const res = guarded.response;
const redactErrorText = createProviderErrorTextRedactor({
headers,
request: params.request,
defaultAuthHeader: "Authorization",
defaultAuthPrefix: "Bearer "
});
try {
const traceId = redactErrorText(res.headers.get("Trace-Id") ?? "");
if (!res.ok) {
const body = await readResponseBodySnippet(res, {
maxBytes: MINIMAX_VLM_ERROR_BODY_MAX_BYTES,
maxChars: MINIMAX_VLM_ERROR_BODY_MAX_CHARS,
redact: redactErrorText
});
const trace = traceId ? ` Trace-Id: ${traceId}` : "";
throw new Error(`MiniMax VLM request failed (${res.status} ${redactErrorText(res.statusText)}).${trace}${body ? ` Body: ${body}` : ""}`);
}
const responseLabel = traceId ? `MiniMax VLM response [Trace-Id=${traceId}]` : "MiniMax VLM response";
const json = await readProviderJsonResponse(res, responseLabel);
if (!isRecord(json)) {
const trace = traceId ? ` Trace-Id: ${traceId}` : "";
throw new Error(`MiniMax VLM response was not JSON.${trace}`);
}
const baseResp = isRecord(json.base_resp) ? json.base_resp : {};
const code = typeof baseResp.status_code === "number" ? baseResp.status_code : -1;
if (code !== 0) {
const msg = redactErrorText((baseResp.status_msg ?? "").trim());
const trace = traceId ? ` Trace-Id: ${traceId}` : "";
throw new Error(`MiniMax VLM API error (${code})${msg ? `: ${msg}` : ""}.${trace}`);
}
const content = pickString(json, "content").trim();
if (!content) {
const trace = traceId ? ` Trace-Id: ${traceId}` : "";
throw new Error(`MiniMax VLM returned no content.${trace}`);
}
return content;
} finally {
await guarded.release();
}
}
//#endregion
export { readResponseBodySnippet as i, isMinimaxVlmProvider as n, minimaxUnderstandImage as r, isMinimaxVlmModel as t };