import { VisionInterface } from "../../interfaces/Vision"; import { ImageParams, ImageResult, VideoParams, VideoResult } from "../../types/vision/vision"; import { VisionResultStream } from "../../utils/stream"; import { VisionBase } from "./base"; import { ImageGenerateParams } from "openai/resources"; import { RequestObj } from "@volcengine/openapi/lib/base/types"; import { Signer } from "@volcengine/openapi"; interface ErrorResponse { response?: { data?: { ResponseMetadata?: { Error?: { Message?: string; }; }; message?: string; code?: number; }; }; message?: string; } interface DoubaoImageRequestBody { req_key: string; prompt: string; model_version?: string; req_schedule_conf?: string; seed?: number; scale?: number; ddim_steps?: number; width?: number; height?: number; use_pre_llm?: boolean; use_sr?: boolean; return_url?: boolean; logo_info?: { add_logo?: boolean; position?: number; language?: number; opacity?: number; logo_text_content?: string; }; } interface DoubaoImageResponse { code: number; message: string; data: { algorithm_base_resp: { status_code: number; status_message: string; }; binary_data_base64?: string[]; image_urls: string[]; request_id: string; }; } interface DoubaoErrorResponse { ResponseMetadata: { RequestId: string; Action: string; Version: string; Error: { CodeN: number; Code: string; Message: string; }; }; } export class VisionDoubaoService extends VisionBase implements VisionInterface { private accessKeyId: string; private secretKey: string; private readonly service = "cv"; private readonly region = "cn-north-1" private readonly action = "CVProcess"; private readonly version = "2022-08-31"; private readonly API_ENDPOINT = "https://visual.volcengineapi.com"; private readonly API_PATH = "/"; private readonly API_QUERY = "Action=CVProcess&Version=2022-08-31"; constructor(secretId: string, secretKey: string) { super(); if (!secretId || !secretKey) { throw new Error("secretId and secretKey are required"); } this.accessKeyId = secretId; this.secretKey = secretKey; } private getHeaders(body: string): Record { // const signer = new Signer(this.accessKeyId, this.secretKey, this.service, this.region); // const queryParams = { // Version: this.version, // Action: this.action, // }; // const headers = signer.getHeaders(queryParams, 'POST', this.API_PATH, body); // console.log("headers", headers); // return headers as any // return generateHeaders( // { // accessKeyId: this.accessKeyId, // secretKey: this.secretKey, // region: "cn-north-1", // service: "cv" // }, // "POST", // this.API_PATH, // this.API_QUERY, // body // ); // 请求数据 const openApiRequestData: RequestObj = { region: 'cn-north-1', method: 'POST', // [可选] http request url query params: { Version: this.version, Action: this.action, }, // http request headers headers: { 'X-Date': new Date().toISOString().replace(/\.\d+Z$/, 'Z'), }, // [可选] http request body body: body, } const signer = new Signer(openApiRequestData, this.service); // 签名 signer.addAuthorization({ accessKeyId: this.accessKeyId, secretKey: this.secretKey }); // 打印签名后的 headers console.log(openApiRequestData.headers); return openApiRequestData.headers; } async image(params: ImageGenerateParams & ImageParams): Promise> { const body: DoubaoImageRequestBody = { req_key: params.req_key || "high_aes_general_v21_L", model_version: params.model || params.model_version || "general_v2.1_L", prompt: params.prompt || "", width: params.width || 512, height: params.height || 512, seed: params.seed, scale: params.scale, ddim_steps: params.steps, use_pre_llm: true, use_sr: true, return_url: true }; const stream = new VisionResultStream(); const timer = setInterval(() => { const progress: ImageResult = { status: "processing", created_at: Date.now() }; stream._transform(progress, 'utf8', () => { }); }, 1000); fetch( `${this.API_ENDPOINT}${this.API_PATH}?${this.API_QUERY}`, { method: 'POST', headers: this.getHeaders(JSON.stringify(body)), body: JSON.stringify(body) }).then(async (response) => { clearInterval(timer) if (!response.ok || response.status !== 200) { stream.write({ status: "failed", created_at: Date.now(), message: `${response.status} ${response.statusText}` } as ImageResult) stream.end() return } const json = await response.json(); // console.log(json) if (json.code !== 10000) { stream.write({ status: "failed", created_at: Date.now(), message: json.message } as ImageResult) stream.end() return } stream.write({ status: "completed", created_at: Date.now(), data: json.data.image_urls.map((item: any) => { return { format: "url", url: item } }) } as ImageResult) stream.end() }) // try { // // const { data: json } = response; // // if (json.code !== 0) { // // let errorMessage = json.message; // // switch (json.code) { // // case 50411: // // errorMessage = "Input image risk check failed"; // // break; // // case 50511: // // errorMessage = "Output image risk check failed"; // // break; // // case 50412: // // errorMessage = "Input text risk check failed"; // // break; // // case 50512: // // errorMessage = "Output text risk check failed"; // // break; // // case 50413: // // errorMessage = "Input text blocked by NER/IP/Blocklist"; // // break; // // } // // this.streamFail(stream, `API Error: ${errorMessage}`); // // return stream; // // } // // const result: ImageResult = { // // status: "completed", // // created_at: Date.now(), // // data: json.data.image_urls.map((url) => { // // return { // // format: "url", // // url // // } // // }) // // }; // // stream._transform(result, 'utf8', () => { }); // stream.end(); // } catch (error) { // clearInterval(timer); // const err = error as ErrorResponse; // if (err.response?.data) { // const errorData = err.response.data; // const errorMessage = errorData.ResponseMetadata?.Error?.Message || // errorData.message || // 'Unknown API error'; // this.streamFail(stream, `API Error: ${errorMessage}`); // } else { // const errorMessage = err.message || String(error); // this.streamFail(stream, errorMessage); // } // } return stream; } async video(params: VideoParams): Promise> { return Promise.reject("Video generation is not supported by Doubao API"); } }