Files
NianAIGC/lib/volcengine/visual-client.ts
2026-08-19 17:58:22 +08:00

106 lines
3.3 KiB
TypeScript

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<string, unknown>,
config = getVisualClientConfig()
): Promise<VisualTaskSubmitResponse> {
if (!config) throw new Error("Volcengine Visual credentials are not configured.");
const protocol = visualProtocolForPayload(payload);
return callVisualApi<VisualTaskSubmitResponse>(protocol.submitAction, protocol.version, payload, config);
}
export async function queryVisualTask(
payload: Record<string, unknown>,
config = getVisualClientConfig()
): Promise<VisualTaskQueryResponse> {
if (!config) throw new Error("Volcengine Visual credentials are not configured.");
const protocol = visualProtocolForPayload(payload);
return callVisualApi<VisualTaskQueryResponse>(protocol.queryAction, protocol.version, payload, config);
}
type VisualAction =
| "CVSync2AsyncSubmitTask"
| "CVSync2AsyncGetResult"
| "JimengSeedream46CVToBSubmitTask"
| "JimengSeedream46CVToBGetResult";
function visualProtocolForPayload(payload: Record<string, unknown>): {
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<T>(
action: VisualAction,
version: string,
payload: Record<string, unknown>,
config: VisualClientConfig
): Promise<T> {
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;
}