diff --git a/mock/common.ts b/mock/common.ts index 0842776..beb3cc0 100644 --- a/mock/common.ts +++ b/mock/common.ts @@ -37,4 +37,4 @@ const loopGetStatus = async (taskId: string) => { }; // main() -loopGetStatus(id); +// loopGetStatus(id); diff --git a/mock/qwen/01.ts b/mock/qwen/01.ts new file mode 100644 index 0000000..135ff04 --- /dev/null +++ b/mock/qwen/01.ts @@ -0,0 +1,41 @@ +import dotenv from 'dotenv'; +import util from 'node:util'; + +dotenv.config({ path: '.env' }); + +import { QwenText2Image } from '../../src/dashscope/dashscope.ts'; + + +const main = async () => { + // 1. create image + const qwen = new QwenText2Image(process.env.BAILIAN_API_KEY || ''); + const res = await qwen.queryImage({ + model: 'wan2.2-t2i-flash', + input: { + prompt: '一个可爱的小的熊猫,卡通风格,抱着竹子。周边有很多彩色的气球。', + }, + }); + console.log('create image response:', util.inspect(res, { depth: null, colors: true })); +} + +main() + +// create image response: { +// request_id: '6b8d3e4d-0b1a-4a01-bcc9-826336e4267a', +// output: { +// task_id: '3318eb1d-cd5d-4408-a9bb-6cddd7967a43', +// task_status: 'SUCCEEDED', +// submit_time: '2025-09-15 19:37:22.177', +// scheduled_time: '2025-09-15 19:37:22.228', +// end_time: '2025-09-15 19:37:28.802', +// results: [ +// { +// orig_prompt: '一个可爱的小的熊猫,卡通风格,抱着竹子。周边有很多彩色的气球。', +// actual_prompt: '卡通风格的可爱小熊猫,圆润造型,毛茸茸的白色身体与黑色斑纹分明,抱着翠绿竹子,周围漂浮着五彩缤纷的圆形气球,背景为浅蓝色天空与几缕白云,画面明亮温馨,适合儿童插画,采用柔和线条与饱满色彩,仰视视角增强萌态。', +// url: 'https://dashscope-result-wlcb-acdr-1.oss-cn-wulanchabu-acdr-1.aliyuncs.com/1d/2d/20250915/5020e443/3318eb1d-cd5d-4408-a9bb-6cddd7967a432353322002.png?Expires=1758022648&OSSAccessKeyId=LTAI5tKPD3TMqf2Lna1fASuh&Signature=8EkJ7ccUsKlZHfU6y3Iiog3fjSQ%3D' +// } +// ], +// task_metrics: { TOTAL: 1, SUCCEEDED: 1, FAILED: 0 } +// }, +// usage: { image_count: 1 } +// } \ No newline at end of file diff --git a/mock/volces.ts b/mock/volces.ts new file mode 100644 index 0000000..7ea403e --- /dev/null +++ b/mock/volces.ts @@ -0,0 +1,24 @@ +import dotenv from 'dotenv'; +import util from 'node:util'; + +dotenv.config({ path: '.env' }); + +import { VolcesImage } from '../src/volces/image.ts'; + +const main = async () => { + const volces = new VolcesImage(process.env.VOLECS_API_KEY || ''); + const resp = await volces.generateImage({ + model: 'doubao-seedream-4-0-250828', + prompt: '一个可爱的小的熊猫,卡通风格,抱着竹子。旁边有一个卡通的奶牛的毛绒玩具。', + sequential_image_generation: 'auto', + sequential_image_generation_options: { max_images: 3 }, + response_format: 'url', + size: '2K', + stream: false, + watermark: false + }); + console.log('response:', resp); + // console.log(util.inspect(resp, { depth: null, colors: true })); +}; + +main() \ No newline at end of file diff --git a/src/dashscope/dashscope.ts b/src/dashscope/dashscope.ts index f75d0d3..644c2b1 100644 --- a/src/dashscope/dashscope.ts +++ b/src/dashscope/dashscope.ts @@ -1,3 +1,17 @@ +export const models = ['wan2.2-t2i-flash', 'wan2.2-t2i-plus', 'wanx2.1-t2i-turbo', 'wanx2.1-t2i-plus', 'wanx2.0-t2i-turbo'] as const; + +export type ImageModel = (typeof models)[number]; + +export const sizes = [ + '1664x928', // 16:9 + '1472x1140', // 4:3 + '1328x1328', // 1:1 + '1140x1472', // 3:4 + '928x1664', // 9:16 +] as const; + +export type ImageSize = (typeof sizes)[number]; + export class QwenText2Image { text2imageApi = 'https://dashscope.aliyuncs.com/api/v1/services/aigc/text2image/image-synthesis'; tasksApi = 'https://dashscope.aliyuncs.com/api/v1/tasks/{task_id}'; @@ -7,6 +21,10 @@ export class QwenText2Image { this.apiKey = apiKey; } async createImage(data: CreateImageRequest): Promise { + const parameters = data.parameters || {} as CreateImageRequest['parameters']; + if (parameters!.n === undefined) { + parameters!.n = 1; // 默认生成1张图片 + } const res = await fetch(this.text2imageApi, { method: 'POST', headers: { @@ -14,7 +32,7 @@ export class QwenText2Image { 'X-DashScope-Async': 'enable', Authorization: `Bearer ${this.apiKey}`, }, - body: JSON.stringify(data), + body: JSON.stringify({ ...data, parameters }), }); return await res.json(); } @@ -40,6 +58,21 @@ export class QwenText2Image { this.taskResponse = await res.json(); return this.taskResponse; } + async queryImage(data: CreateImageRequest): Promise { + const image = await this.createImage(data); + const taskId = image.output.task_id; + let status: string; + do { + await this.getTaskStatus(taskId); + status = this.taskResponse?.output?.task_status; + if (status === 'SUCCEEDED') { + break; + } + await new Promise((resolve) => setTimeout(resolve, 1000)); + } while (status !== 'SUCCEEDED' && status !== 'FAILED'); + + return this.taskResponse; + } } type TaskStatus = 'PENDING' | 'SUCCESS' | 'FAILURE' | 'CANCELED' | 'UNKNOWN'; @@ -99,16 +132,3 @@ type TaskResponse = { message?: string; }; -export const models = ['wan2.2-t2i-flash', 'wan2.2-t2i-plus', 'wanx2.1-t2i-turbo', 'wanx2.1-t2i-plus', 'wanx2.0-t2i-turbo'] as const; - -export type ImageModel = (typeof models)[number]; - -export const sizes = [ - '1664x928', // 16:9 - '1472x1140', // 4:3 - '1328x1328', // 1:1 - '1140x1472', // 3:4 - '928x1664', // 9:16 -] as const; - -export type ImageSize = (typeof sizes)[number]; diff --git a/src/volces/image.ts b/src/volces/image.ts new file mode 100644 index 0000000..4a4d838 --- /dev/null +++ b/src/volces/image.ts @@ -0,0 +1,45 @@ +export class VolcesImage { + apiUrl = 'https://ark.cn-beijing.volces.com/api/v3/images/generations'; + apiKey: string; + + constructor(apiKey: string) { + this.apiKey = apiKey; + } + + async generateImage(data: VolcesImageRequest): Promise { + const res = await fetch(this.apiUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.apiKey}`, + }, + body: JSON.stringify(data), + }); + return await res.json(); + } +} + +export type VolcesImageRequest = { + model: string; + prompt: string; + image?: string[]; // 输入图片数组 + sequential_image_generation?: 'auto' | 'manual'; + sequential_image_generation_options?: { + max_images?: number; + }; + response_format?: 'url' | 'base64'; + size?: string; // 如 "2K" + stream?: boolean; + watermark?: boolean; +}; + +export type VolcesImageResponse = { + id: string; + object: string; + created: number; + urls?: string[]; // 当 response_format 为 url + images?: string[]; // 当 response_format 为 base64 + status: string; + message?: string; + [key: string]: any; +}; \ No newline at end of file