import type { VisualTaskQueryResponse, VisualTaskSubmitResponse } from "@/lib/types"; import { signVolcengineRequest } from "@/lib/volcengine/signature"; export type VisualClientConfig = { accessKeyId: string; secretAccessKey: string; region: string; service: string; endpoint: string; }; export function getVisualClientConfig(): VisualClientConfig | null { const accessKeyId = process.env.VOLCENGINE_ACCESS_KEY_ID; const secretAccessKey = process.env.VOLCENGINE_SECRET_ACCESS_KEY; if (!accessKeyId || !secretAccessKey) return null; return { accessKeyId, secretAccessKey, region: process.env.VOLCENGINE_REGION || "cn-north-1", service: process.env.VOLCENGINE_SERVICE || "cv", endpoint: process.env.VOLCENGINE_VISUAL_ENDPOINT || "https://visual.volcengineapi.com" }; } export async function submitVisualTask( payload: Record, config = getVisualClientConfig() ): Promise { if (!config) throw new Error("Volcengine Visual credentials are not configured."); const protocol = visualProtocolForPayload(payload); return callVisualApi(protocol.submitAction, protocol.version, payload, config); } export async function queryVisualTask( payload: Record, config = getVisualClientConfig() ): Promise { if (!config) throw new Error("Volcengine Visual credentials are not configured."); const protocol = visualProtocolForPayload(payload); return callVisualApi(protocol.queryAction, protocol.version, payload, config); } type VisualAction = | "CVSync2AsyncSubmitTask" | "CVSync2AsyncGetResult" | "JimengSeedream46CVToBSubmitTask" | "JimengSeedream46CVToBGetResult"; function visualProtocolForPayload(payload: Record): { submitAction: VisualAction; queryAction: VisualAction; version: string; } { if (String(payload.req_key || "").trim() === "jimeng_seedream46_cvtob") { return { submitAction: "JimengSeedream46CVToBSubmitTask", queryAction: "JimengSeedream46CVToBGetResult", version: "2024-06-06" }; } return { submitAction: "CVSync2AsyncSubmitTask", queryAction: "CVSync2AsyncGetResult", version: "2022-08-31" }; } async function callVisualApi( action: VisualAction, version: string, payload: Record, config: VisualClientConfig ): Promise { const body = JSON.stringify(payload); const signed = signVolcengineRequest({ method: "POST", endpoint: config.endpoint, query: { Action: action, Version: version }, body, accessKeyId: config.accessKeyId, secretAccessKey: config.secretAccessKey, region: config.region, service: config.service }); const response = await fetch(signed.url, { method: "POST", headers: signed.headers, body }); const text = await response.text(); let json: unknown; try { json = JSON.parse(text); } catch { throw new Error(`Volcengine Visual returned non-JSON response: ${response.status} ${text.slice(0, 240)}`); } if (!response.ok) { const message = typeof json === "object" && json && "message" in json ? String(json.message) : text; throw new Error(`Volcengine Visual HTTP ${response.status}: ${message}`); } return json as T; }