type MinimaxBaseResp = { status_code?: number; status_msg?: string; }; function coerceApiHost(params: { apiHost?: string; modelBaseUrl?: string; env?: NodeJS.ProcessEnv; }): string { const env = params.env ?? process.env; const raw = params.apiHost?.trim() || env.MINIMAX_API_HOST?.trim() || params.modelBaseUrl?.trim() || "https://api.minimax.io"; try { const url = new URL(raw); return url.origin; } catch {} try { const url = new URL(`https://${raw}`); return url.origin; } catch { return "https://api.minimax.io"; } } function isRecord(value: unknown): value is Record { return Boolean(value && typeof value === "object" && !Array.isArray(value)); } function pickString(rec: Record, key: string): string { const v = rec[key]; return typeof v === "string" ? v : ""; } export async function minimaxUnderstandImage(params: { apiKey: string; prompt: string; imageDataUrl: string; apiHost?: string; modelBaseUrl?: string; }): Promise { const apiKey = params.apiKey.trim(); 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 host = coerceApiHost({ apiHost: params.apiHost, modelBaseUrl: params.modelBaseUrl, }); const url = new URL("/v1/coding_plan/vlm", host).toString(); const res = await fetch(url, { method: "POST", headers: { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json", "MM-API-Source": "Clawdbot", }, body: JSON.stringify({ prompt, image_url: imageDataUrl, }), }); const traceId = res.headers.get("Trace-Id") ?? ""; if (!res.ok) { const body = await res.text().catch(() => ""); const trace = traceId ? ` Trace-Id: ${traceId}` : ""; throw new Error( `MiniMax VLM request failed (${res.status} ${res.statusText}).${trace}${ body ? ` Body: ${body.slice(0, 400)}` : "" }`, ); } const json = (await res.json().catch(() => null)) as unknown; 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 as MinimaxBaseResp) : {}; const code = typeof baseResp.status_code === "number" ? baseResp.status_code : -1; if (code !== 0) { const msg = (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; }