diff --git a/mock/common.ts b/mock/common.ts new file mode 100644 index 0000000..0842776 --- /dev/null +++ b/mock/common.ts @@ -0,0 +1,40 @@ +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.createImage({ + model: 'wan2.2-t2i-flash', + input: { + prompt: '一个可爱的小的熊猫,卡通风格,抱着竹子。', + }, + }); + console.log('create image response:', res); + // 2. for-of loop get status +}; + +const id = '857ac37e-d470-4008-a98b-afe8ad054f81'; +const loopGetStatus = async (taskId: string) => { + const qwen = new QwenText2Image(process.env.BAILIAN_API_KEY || ''); + let status: string; + do { + await qwen.getTaskStatus(taskId); + const taskResponse = qwen.taskResponse; + console.log('task status:', taskResponse); + status = taskResponse?.output?.task_status; + if (status === 'SUCCEEDED') { + break; + } + await new Promise((resolve) => setTimeout(resolve, 1000)); + } while (status !== 'SUCCEEDED' && status !== 'FAILED'); + + console.log(util.inspect(qwen.taskResponse, { depth: null, colors: true })); +}; + +// main() +loopGetStatus(id); diff --git a/package.json b/package.json index 09d554d..bc6d288 100644 --- a/package.json +++ b/package.json @@ -13,5 +13,8 @@ "type": "module", "devDependencies": { "@types/node": "^24.4.0" + }, + "dependencies": { + "dotenv": "^17.2.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 491ef8e..ff7e2af 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,6 +7,10 @@ settings: importers: .: + dependencies: + dotenv: + specifier: ^17.2.2 + version: 17.2.2 devDependencies: '@types/node': specifier: ^24.4.0 @@ -17,6 +21,10 @@ packages: '@types/node@24.4.0': resolution: {integrity: sha512-gUuVEAK4/u6F9wRLznPUU4WGUacSEBDPoC2TrBkw3GAnOLHBL45QdfHOXp1kJ4ypBGLxTOB+t7NJLpKoC3gznQ==} + dotenv@17.2.2: + resolution: {integrity: sha512-Sf2LSQP+bOlhKWWyhFsn0UsfdK/kCWRv1iuA2gXAwt3dyNabr6QSj00I2V10pidqz69soatm9ZwZvpQMTIOd5Q==} + engines: {node: '>=12'} + undici-types@7.11.0: resolution: {integrity: sha512-kt1ZriHTi7MU+Z/r9DOdAI3ONdaR3M3csEaRc6ewa4f4dTvX4cQCbJ4NkEn0ohE4hHtq85+PhPSTY+pO/1PwgA==} @@ -26,4 +34,6 @@ snapshots: dependencies: undici-types: 7.11.0 + dotenv@17.2.2: {} + undici-types@7.11.0: {} diff --git a/src/dashscope/dashscope.ts b/src/dashscope/dashscope.ts new file mode 100644 index 0000000..f75d0d3 --- /dev/null +++ b/src/dashscope/dashscope.ts @@ -0,0 +1,114 @@ +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}'; + apiKey: string; + taskResponse: TaskResponse; + constructor(apiKey: string) { + this.apiKey = apiKey; + } + async createImage(data: CreateImageRequest): Promise { + const res = await fetch(this.text2imageApi, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-DashScope-Async': 'enable', + Authorization: `Bearer ${this.apiKey}`, + }, + body: JSON.stringify(data), + }); + return await res.json(); + } + /** + * // PENDING:任务排队中 + // RUNNING:任务处理中 + // SUCCEEDED:任务执行成功 + // FAILED:任务执行失败 + // CANCELED:任务取消成功 + // UNKNOWN:任务不存在或状态未知 + * @param taskId + * @returns + */ + async getTaskStatus(taskId: string): Promise { + const url = this.tasksApi.replace('{task_id}', taskId); + const res = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${this.apiKey}`, + }, + }); + this.taskResponse = await res.json(); + return this.taskResponse; + } +} + +type TaskStatus = 'PENDING' | 'SUCCESS' | 'FAILURE' | 'CANCELED' | 'UNKNOWN'; +type CreateImageResponse = { + output: { + task_status: TaskStatus; + task_id: string; + }; + request_id: string; + code?: string; + message?: string; +}; + +type CreateImageRequest = { + model: ImageModel; + input: { + prompt: string; + negative_prompt?: string; + }; + parameters?: { + size?: string; // 图片尺寸,支持 "256x256", "512x512", "1024x1024" + n: number; // 生成图片数量,默认为4,1-4之间 + seed?: number; // 随机种子,seed参数取值范围是[0, 2147483647]。 + prompt_extend?: boolean; // 是否启用提示词增强,默认为true 是否开启prompt智能改写。开启后会使用大模型对输入prompt进行智能改写,仅对正向提示词有效。对于较短的输入prompt生成效果提升明显,但会增加 + watermark?: boolean; // 是否添加水印,默认为false 是否在生成的图片上添加水印, "AI 生成" 字样 + }; +}; + +type TaskResponse = { + output: { + task_status: TaskStatus; + task_id: string; + submit_time: string; + scheduled_time: string; + end_time: string; + results: { + orig_prompt: string; + actual_prompt: string; + url: string; + code: string; + message: string; + [key: string]: any; + }[]; + task_metrics: { + TOTAL: number; + SUCCEEDED: number; + FAILED: number; + }; + [key: string]: any; + }; + request_id: string; + usage: { + image_count: number; + [key: string]: any; + }; + code?: string; + 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/dashscope/index.ts b/src/dashscope/index.ts new file mode 100644 index 0000000..144e408 --- /dev/null +++ b/src/dashscope/index.ts @@ -0,0 +1 @@ +export { QwenText2Image } from './dashscope.ts'; diff --git a/src/dashscope/readme.md b/src/dashscope/readme.md new file mode 100644 index 0000000..4a60865 --- /dev/null +++ b/src/dashscope/readme.md @@ -0,0 +1,17 @@ +https://help.aliyun.com/zh/model-studio/text-to-image-v2-api-reference?spm=a2c4g.11186623.help-menu-2400256.d_2_2_3.3b3256e5iOwcyt#f281932a43b03 + + +链接仅在 24 小时内有效。 +``` +# OSS域名列表 +dashscope-result-bj.oss-cn-beijing.aliyuncs.com +dashscope-result-hz.oss-cn-hangzhou.aliyuncs.com +dashscope-result-sh.oss-cn-shanghai.aliyuncs.com +dashscope-result-wlcb.oss-cn-wulanchabu.aliyuncs.com +dashscope-result-zjk.oss-cn-zhangjiakou.aliyuncs.com +dashscope-result-sz.oss-cn-shenzhen.aliyuncs.com +dashscope-result-hy.oss-cn-heyuan.aliyuncs.com +dashscope-result-cd.oss-cn-chengdu.aliyuncs.com +dashscope-result-gz.oss-cn-guangzhou.aliyuncs.com +dashscope-result-wlcb-acdr-1.oss-cn-wulanchabu-acdr-1.aliyuncs.com +``` \ No newline at end of file