ai-world-sdk 1.5.8 → 1.5.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -45,6 +45,7 @@ var __importStar = (this && this.__importStar) || (function () {
45
45
  Object.defineProperty(exports, "__esModule", { value: true });
46
46
  const dotenv = __importStar(require("dotenv"));
47
47
  const llm_1 = require("../llm");
48
+ const image_1 = require("../image");
48
49
  const config_1 = require("../config");
49
50
  const ai_1 = require("ai");
50
51
  // 加载环境变量
@@ -214,17 +215,17 @@ describe("llm.ts — 真实集成测试(文本 + 图像)", () => {
214
215
  // ============================================================
215
216
  // createProvider + generateImage(kunpo 图像生成)
216
217
  // ============================================================
217
- describe("createProvider + generateImage(kunpo 图像)", () => {
218
+ describe("generateKunpoImage(kunpo 图像)", () => {
218
219
  const promptArg = "A simple red circle on a white background";
219
- test("kunpo — openai/gpt-5.4-image-2", async () => {
220
- const modelId = process.env.KUNPO_IMAGE_MODEL || "openai/gpt-5.4-image-2";
221
- const provider = (0, llm_1.createProvider)("kunpo", "openai", PLUGIN_ID);
222
- const imageModel = provider.imageModel(modelId);
223
- const result = await (0, ai_1.generateImage)({
224
- model: imageModel,
220
+ test("kunpo — Image-GI2(异步)", async () => {
221
+ const modelId = process.env.KUNPO_IMAGE_MODEL || "Image-GI2";
222
+ const result = await (0, image_1.generateKunpoImage)({
223
+ pluginId: PLUGIN_ID,
224
+ model: modelId,
225
225
  prompt: promptArg,
226
- n: 1,
227
226
  size: "1024x1024",
227
+ quality: "high",
228
+ async: true,
228
229
  });
229
230
  expect(result.images).toBeDefined();
230
231
  expect(result.images.length).toBeGreaterThan(0);
@@ -294,9 +295,10 @@ describe("llm.ts — 真实集成测试(文本 + 图像)", () => {
294
295
  const provider = (0, llm_1.createProvider)("api2img", "openai", "test");
295
296
  expect(typeof provider.languageModel).toBe("function");
296
297
  });
297
- test("kunpo provider 实例有 imageModel 方法", () => {
298
- const provider = (0, llm_1.createProvider)("kunpo", "openai", "test");
299
- expect(typeof provider.imageModel).toBe("function");
298
+ test("getImageProxyConfig 返回 /api/llm/image 代理地址", () => {
299
+ const { getImageProxyConfig } = require("../image");
300
+ const cfg = getImageProxyConfig("test");
301
+ expect(cfg.baseUrl).toContain("/api/llm/image");
300
302
  });
301
303
  });
302
304
  });
@@ -36,9 +36,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.registerImageCommands = registerImageCommands;
37
37
  const fs = __importStar(require("fs"));
38
38
  const path = __importStar(require("path"));
39
- const ai_1 = require("ai");
40
39
  const index_1 = require("../../index");
41
- const config_1 = require("../config");
40
+ const config_1 = require("../../config");
41
+ const config_2 = require("../config");
42
42
  const output_1 = require("../output");
43
43
  const utils_1 = require("../utils");
44
44
  function collectOpts(cmd) {
@@ -63,22 +63,86 @@ function stripB64ForLog(obj) {
63
63
  }
64
64
  return copy;
65
65
  }
66
+ async function downloadUrlToFile(url, outputPath) {
67
+ const res = await fetch(url);
68
+ if (!res.ok) {
69
+ throw new Error(`下载图像失败: ${res.status}`);
70
+ }
71
+ const abs = path.resolve(outputPath);
72
+ const dir = path.dirname(abs);
73
+ if (!fs.existsSync(dir))
74
+ fs.mkdirSync(dir, { recursive: true });
75
+ fs.writeFileSync(abs, Buffer.from(await res.arrayBuffer()));
76
+ }
77
+ /** OpenAI 图像:经 /api/llm/openai 代理(HTTP,非 AI SDK) */
78
+ async function generateOpenAIImageViaProxy(pluginId, model, prompt, size) {
79
+ const serverUrl = config_1.sdkConfig.getServerUrl() || '';
80
+ const token = config_1.sdkConfig.getToken() || '';
81
+ const body = {
82
+ model,
83
+ prompt,
84
+ n: 1,
85
+ response_format: 'b64_json',
86
+ };
87
+ if (size)
88
+ body.size = size;
89
+ const res = await fetch(`${serverUrl}/api/llm/openai/images/generations`, {
90
+ method: 'POST',
91
+ headers: {
92
+ 'Content-Type': 'application/json',
93
+ 'X-Plugin-Id': pluginId,
94
+ 'X-Provider': 'api2img',
95
+ Authorization: `Bearer ${token}`,
96
+ },
97
+ body: JSON.stringify(body),
98
+ });
99
+ if (!res.ok) {
100
+ const text = await res.text().catch(() => '');
101
+ throw new Error(`OpenAI 图像生成失败: ${res.status} ${text}`);
102
+ }
103
+ const json = (await res.json());
104
+ const first = json.data?.[0];
105
+ if (!first) {
106
+ throw new Error('OpenAI 图像生成响应无数据');
107
+ }
108
+ if (first.b64_json) {
109
+ const buf = Buffer.from(first.b64_json, 'base64');
110
+ return { uint8Array: new Uint8Array(buf), mediaType: 'image/png' };
111
+ }
112
+ if (first.url) {
113
+ const imgRes = await fetch(first.url);
114
+ if (!imgRes.ok) {
115
+ throw new Error(`下载图像失败: ${imgRes.status}`);
116
+ }
117
+ const buf = Buffer.from(await imgRes.arrayBuffer());
118
+ return {
119
+ uint8Array: new Uint8Array(buf),
120
+ mediaType: imgRes.headers.get('content-type') || 'image/png',
121
+ url: first.url,
122
+ };
123
+ }
124
+ throw new Error('OpenAI 图像生成响应无 url 或 b64_json');
125
+ }
66
126
  function registerImageCommands(program) {
67
- const image = program.command('image').description('图像生成(doubao / gemini / openai)');
127
+ const image = program
128
+ .command('image')
129
+ .description('图像生成(doubao / gemini / openai / kunpo)');
68
130
  image
69
131
  .command('generate')
70
- .requiredOption('--provider <name>', 'doubao, gemini, or openai')
132
+ .requiredOption('--provider <name>', 'doubao, gemini, openai, or kunpo')
71
133
  .requiredOption('--prompt <text>', 'Prompt text')
72
134
  .option('--model <id>', 'Model id')
73
- .option('--size <value>', 'Size (provider-specific)')
135
+ .option('--size <value>', 'Size e.g. 1024x1024, 1536x1024')
136
+ .option('--quality <value>', 'KUNPO quality: low, standard, medium, high, hd')
137
+ .option('--sync', 'KUNPO: use sync /images/generations (not recommended)')
74
138
  .option('--output <file>', 'Write first image to file')
75
- .option('--plugin-id <id>', 'Plugin id (required for openai)')
139
+ .option('--plugin-id <id>', 'Plugin id (required for openai / kunpo)')
76
140
  .action(async (options, cmd) => {
77
141
  const commandName = 'image generate';
78
142
  try {
79
143
  void (0, utils_1.getFormat)(program);
80
144
  const o = collectOpts(cmd);
81
- const auth = (0, config_1.resolveAuth)({
145
+ const auth = (0, config_2.resolveAuth)({
82
146
  baseUrl: o.baseUrl,
83
147
  token: o.token,
84
148
  pluginId: options.pluginId ??
@@ -88,8 +152,8 @@ function registerImageCommands(program) {
88
152
  (0, utils_1.requireOption)(options.prompt, 'prompt');
89
153
  (0, utils_1.requireOption)(options.provider, 'provider');
90
154
  const providerName = options.provider.toLowerCase();
91
- if (!['doubao', 'gemini', 'openai'].includes(providerName)) {
92
- const err = new Error('缺少必需参数: --provider 必须是 doubao、gemini 或 openai');
155
+ if (!['doubao', 'gemini', 'openai', 'kunpo'].includes(providerName)) {
156
+ const err = new Error('缺少必需参数: --provider 必须是 doubao、gemini、openaikunpo');
93
157
  err.code = 'VALIDATION_ERROR';
94
158
  throw err;
95
159
  }
@@ -106,15 +170,7 @@ function registerImageCommands(program) {
106
170
  (0, utils_1.writeOutputFile)(options.output, Buffer.from(first.b64_json, 'base64'));
107
171
  }
108
172
  else if (first?.url) {
109
- const abs = path.resolve(options.output);
110
- const dir = path.dirname(abs);
111
- if (!fs.existsSync(dir))
112
- fs.mkdirSync(dir, { recursive: true });
113
- const res = await fetch(first.url);
114
- if (!res.ok) {
115
- throw new Error(`下载图像失败: ${res.status}`);
116
- }
117
- fs.writeFileSync(abs, Buffer.from(await res.arrayBuffer()));
173
+ await downloadUrlToFile(first.url, options.output);
118
174
  }
119
175
  }
120
176
  (0, output_1.outputJSON)(options.output ? stripB64ForLog({ ...result, savedTo: options.output }) : result);
@@ -136,15 +192,7 @@ function registerImageCommands(program) {
136
192
  (0, utils_1.writeOutputFile)(options.output, Buffer.from(first.b64_json, 'base64'));
137
193
  }
138
194
  else if (first?.url) {
139
- const abs = path.resolve(options.output);
140
- const dir = path.dirname(abs);
141
- if (!fs.existsSync(dir))
142
- fs.mkdirSync(dir, { recursive: true });
143
- const res = await fetch(first.url);
144
- if (!res.ok) {
145
- throw new Error(`下载图像失败: ${res.status}`);
146
- }
147
- fs.writeFileSync(abs, Buffer.from(await res.arrayBuffer()));
195
+ await downloadUrlToFile(first.url, options.output);
148
196
  }
149
197
  }
150
198
  (0, output_1.outputJSON)(options.output ? stripB64ForLog({ ...result, savedTo: options.output }) : result);
@@ -155,28 +203,83 @@ function registerImageCommands(program) {
155
203
  auth.pluginId;
156
204
  (0, utils_1.requireOption)(pluginId, 'plugin-id');
157
205
  (0, utils_1.initSDK)({ ...auth, pluginId });
158
- const provider = (0, index_1.createProvider)('api2img', 'openai', pluginId);
159
- const modelId = options.model || 'dall-e-3';
160
- const genOpts = {
161
- model: provider.imageModel(modelId),
162
- prompt: options.prompt,
163
- n: 1,
164
- };
165
- if (options.size) {
166
- genOpts.size = options.size;
206
+ if (providerName === 'kunpo') {
207
+ const modelId = options.model || 'Image-GI2';
208
+ const useSync = Boolean(options.sync);
209
+ if (useSync) {
210
+ const body = {
211
+ model: modelId,
212
+ prompt: options.prompt,
213
+ response_format: 'b64_json',
214
+ n: 1,
215
+ };
216
+ if (options.size && /^\d+x\d+$|auto/.test(options.size)) {
217
+ body.size = options.size;
218
+ }
219
+ if (options.quality) {
220
+ body.quality = options.quality;
221
+ }
222
+ const resp = await (0, index_1.generateImageSync)(pluginId, body);
223
+ const first = resp.data?.[0];
224
+ if (options.output && first?.b64_json) {
225
+ (0, utils_1.writeOutputFile)(options.output, Buffer.from(first.b64_json, 'base64'));
226
+ }
227
+ else if (options.output && first?.url) {
228
+ await downloadUrlToFile(first.url, options.output);
229
+ }
230
+ (0, output_1.outputJSON)({
231
+ provider: 'kunpo',
232
+ model: modelId,
233
+ mode: 'sync',
234
+ proxy: (0, index_1.getImageProxyConfig)(pluginId).baseUrl,
235
+ savedTo: options.output || undefined,
236
+ data: resp.data?.map((d) => ({
237
+ ...d,
238
+ b64_json: d.b64_json ? '[omitted]' : undefined,
239
+ })),
240
+ });
241
+ return;
242
+ }
243
+ const kunpoOpts = {
244
+ pluginId: pluginId,
245
+ model: modelId,
246
+ prompt: options.prompt,
247
+ async: true,
248
+ };
249
+ if (options.size && /^\d+x\d+$|auto/.test(options.size)) {
250
+ kunpoOpts.size = options.size;
251
+ }
252
+ if (options.quality) {
253
+ kunpoOpts.quality = options.quality;
254
+ }
255
+ const imgResult = await (0, index_1.generateKunpoImage)(kunpoOpts);
256
+ const first = imgResult.images[0];
257
+ if (options.output && first) {
258
+ (0, utils_1.writeOutputFile)(options.output, Buffer.from(first.uint8Array));
259
+ }
260
+ (0, output_1.outputJSON)({
261
+ provider: 'kunpo',
262
+ model: modelId,
263
+ mode: 'async',
264
+ taskId: imgResult.taskId,
265
+ resultUrl: imgResult.resultUrl,
266
+ mediaType: first?.mediaType,
267
+ savedTo: options.output || undefined,
268
+ });
269
+ return;
167
270
  }
168
- const imgResult = await (0, ai_1.generateImage)(genOpts);
169
- const first = imgResult.images[0];
170
- if (options.output && first) {
171
- (0, utils_1.writeOutputFile)(options.output, Buffer.from(first.uint8Array));
271
+ // openai:/api/llm/openai 代理 HTTP
272
+ const modelId = options.model || 'dall-e-3';
273
+ const img = await generateOpenAIImageViaProxy(pluginId, modelId, options.prompt, options.size);
274
+ if (options.output) {
275
+ (0, utils_1.writeOutputFile)(options.output, Buffer.from(img.uint8Array));
172
276
  }
173
277
  (0, output_1.outputJSON)({
174
278
  provider: 'openai',
175
279
  model: modelId,
176
- mediaType: first?.mediaType,
280
+ mediaType: img.mediaType,
281
+ url: img.url,
177
282
  savedTo: options.output || undefined,
178
- usage: imgResult.usage,
179
- warnings: imgResult.warnings,
180
283
  });
181
284
  }
182
285
  catch (err) {
package/dist/config.d.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * 注意: {VERSION} 占位符会在构建时被替换为实际版本号
11
11
  */
12
- export declare const SDK_SIGNATURE = "AI_WORLD_SDK_V:1.5.8";
12
+ export declare const SDK_SIGNATURE = "AI_WORLD_SDK_V:1.5.9";
13
13
  /**
14
14
  * 版本兼容性错误
15
15
  */
@@ -35,8 +35,8 @@ declare class SDKConfig {
35
35
  private _authCheckPromise;
36
36
  private _currentUser;
37
37
  private _cliMode;
38
- readonly sdkSignature = "AI_WORLD_SDK_V:1.5.8";
39
- readonly sdkVersion = "1.5.8";
38
+ readonly sdkSignature = "AI_WORLD_SDK_V:1.5.9";
39
+ readonly sdkVersion = "1.5.9";
40
40
  constructor();
41
41
  /**
42
42
  * Set global base URL
package/dist/config.js CHANGED
@@ -7,7 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.sdkConfig = exports.VersionCompatibilityError = exports.SDK_SIGNATURE = void 0;
8
8
  // SDK 版本号(构建时自动从 package.json 更新)
9
9
  // 此版本号会在运行 npm run build 时自动从 package.json 读取并更新
10
- const SDK_VERSION = "1.5.8";
10
+ const SDK_VERSION = "1.5.9";
11
11
  /**
12
12
  * SDK 特征码 - 用于在构建后的 JS 文件中识别 SDK 版本
13
13
  * 格式: AI_WORLD_SDK_V:版本号
@@ -15,7 +15,7 @@ const SDK_VERSION = "1.5.8";
15
15
  *
16
16
  * 注意: {VERSION} 占位符会在构建时被替换为实际版本号
17
17
  */
18
- exports.SDK_SIGNATURE = "AI_WORLD_SDK_V:1.5.8";
18
+ exports.SDK_SIGNATURE = "AI_WORLD_SDK_V:1.5.9";
19
19
  /**
20
20
  * 版本兼容性错误
21
21
  */
@@ -0,0 +1,93 @@
1
+ /** 腾讯 AIART 支持的模型 */
2
+ export type KunpoTencentImageModel = "Image-GI" | "Image-GI2" | "Image-GPT2";
3
+ /** OpenAI Images 兼容的 quality(GI 映射分辨率,GPT 映射质量档位) */
4
+ export type KunpoImageQuality = "low" | "standard" | "medium" | "high" | "hd";
5
+ export type KunpoInputFidelity = "high" | "low";
6
+ export type KunpoResponseFormat = "url" | "b64_json";
7
+ export interface KunpoImageGenerationOptions {
8
+ pluginId: string;
9
+ model: KunpoTencentImageModel | string;
10
+ prompt: string;
11
+ /** 默认 true:POST /images/tasks + 轮询 */
12
+ async?: boolean;
13
+ /** 图片数量,默认 1 */
14
+ n?: number;
15
+ /** 图片宽高,默认 auto:自动根据模型和质量生成 */
16
+ size?: `${number}x${number}` | "auto";
17
+ quality?: KunpoImageQuality;
18
+ responseFormat?: KunpoResponseFormat;
19
+ /** 图生图参考图 URL 数组 */
20
+ images?: string[];
21
+ /** 图生图 */
22
+ image?: string;
23
+ background?: string;
24
+ outputFormat?: "png";
25
+ inputFidelity?: KunpoInputFidelity;
26
+ seed?: number;
27
+ maxRetries?: number;
28
+ abortSignal?: AbortSignal;
29
+ headers?: Record<string, string>;
30
+ /** 轮询间隔时间,默认 30000ms */
31
+ pollIntervalMs?: number;
32
+ /** 轮询超时时间,默认 15 * 60 * 1000ms */
33
+ pollTimeoutMs?: number;
34
+ }
35
+ export interface GeneratedImageFile {
36
+ base64: string;
37
+ uint8Array: Uint8Array;
38
+ mediaType: string;
39
+ url?: string;
40
+ }
41
+ export interface KunpoImageGenerationResult {
42
+ image: GeneratedImageFile;
43
+ images: GeneratedImageFile[];
44
+ taskId?: string;
45
+ resultUrl?: string;
46
+ }
47
+ interface KunpoTaskSubmitResponse {
48
+ id?: string;
49
+ task_id?: string;
50
+ model?: string;
51
+ status?: string;
52
+ created_at?: number;
53
+ }
54
+ interface KunpoTaskQueryResponse {
55
+ code?: string;
56
+ data?: {
57
+ task_id?: string;
58
+ status?: string;
59
+ result_url?: string;
60
+ fail_reason?: string;
61
+ };
62
+ }
63
+ interface OpenAIImageGenerationResponse {
64
+ created?: number;
65
+ data?: Array<{
66
+ url?: string;
67
+ b64_json?: string;
68
+ revised_prompt?: string;
69
+ }>;
70
+ }
71
+ /** 后端图像专用代理 /api/llm/image */
72
+ export declare function getImageProxyConfig(pluginId: string, headers?: Record<string, string>): {
73
+ baseUrl: string;
74
+ headers: Record<string, string>;
75
+ };
76
+ export declare function bytesFromUrl(url: string, signal?: AbortSignal): Promise<GeneratedImageFile>;
77
+ /** POST /api/llm/image/images/tasks */
78
+ export declare function submitImageTask(pluginId: string, body: Record<string, unknown>, signal?: AbortSignal, headers?: Record<string, string>): Promise<KunpoTaskSubmitResponse>;
79
+ /** GET /api/llm/image/images/tasks/:taskId */
80
+ export declare function getImageTask(pluginId: string, taskId: string, signal?: AbortSignal, headers?: Record<string, string>): Promise<KunpoTaskQueryResponse>;
81
+ /** POST /api/llm/image/images/generations(同步,不推荐生产使用) */
82
+ export declare function generateImageSync(pluginId: string, body: Record<string, unknown>, signal?: AbortSignal, headers?: Record<string, string>): Promise<OpenAIImageGenerationResponse>;
83
+ export declare function pollImageTask(pluginId: string, taskId: string, options?: {
84
+ pollIntervalMs?: number;
85
+ pollTimeoutMs?: number;
86
+ abortSignal?: AbortSignal;
87
+ headers?: Record<string, string>;
88
+ }): Promise<string>;
89
+ /**
90
+ * 通过后端 /api/llm/image 代理生成图像(默认异步)
91
+ */
92
+ export declare function generateKunpoImage(options: KunpoImageGenerationOptions): Promise<KunpoImageGenerationResult>;
93
+ export {};
package/dist/image.js ADDED
@@ -0,0 +1,227 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getImageProxyConfig = getImageProxyConfig;
4
+ exports.bytesFromUrl = bytesFromUrl;
5
+ exports.submitImageTask = submitImageTask;
6
+ exports.getImageTask = getImageTask;
7
+ exports.generateImageSync = generateImageSync;
8
+ exports.pollImageTask = pollImageTask;
9
+ exports.generateKunpoImage = generateKunpoImage;
10
+ const config_1 = require("./config");
11
+ const TERMINAL_TASK_STATUSES = new Set(["SUCCESS", "FAILURE"]);
12
+ /** 后端图像专用代理 /api/llm/image */
13
+ function getImageProxyConfig(pluginId, headers) {
14
+ const serverUrl = config_1.sdkConfig.getServerUrl() || "";
15
+ const token = config_1.sdkConfig.getToken() || "proxy";
16
+ return {
17
+ baseUrl: `${serverUrl}/api/llm/image`,
18
+ headers: {
19
+ "Content-Type": "application/json",
20
+ "X-Plugin-Id": pluginId,
21
+ "X-Provider": "kunpo",
22
+ Authorization: `Bearer ${token}`,
23
+ ...headers,
24
+ },
25
+ };
26
+ }
27
+ function buildSyncBody(options) {
28
+ const body = {
29
+ model: options.model,
30
+ prompt: options.prompt,
31
+ response_format: options.responseFormat ?? "b64_json",
32
+ };
33
+ if (options.n != null)
34
+ body.n = options.n;
35
+ if (options.size)
36
+ body.size = options.size;
37
+ if (options.quality)
38
+ body.quality = options.quality;
39
+ if (options.images?.length)
40
+ body.images = options.images;
41
+ if (options.image)
42
+ body.image = options.image;
43
+ if (options.background)
44
+ body.background = options.background;
45
+ if (options.outputFormat)
46
+ body.output_format = options.outputFormat;
47
+ if (options.inputFidelity)
48
+ body.input_fidelity = options.inputFidelity;
49
+ if (options.seed != null)
50
+ body.seed = options.seed;
51
+ return body;
52
+ }
53
+ function buildAsyncSubmitBody(options) {
54
+ const body = {
55
+ model: options.model,
56
+ prompt: options.prompt,
57
+ };
58
+ if (options.size)
59
+ body.size = options.size;
60
+ if (options.images?.length)
61
+ body.images = options.images;
62
+ if (options.image)
63
+ body.image = options.image;
64
+ if (options.outputFormat)
65
+ body.output_format = options.outputFormat;
66
+ if (options.inputFidelity)
67
+ body.input_fidelity = options.inputFidelity;
68
+ if (options.quality) {
69
+ body.metadata = { quality: options.quality };
70
+ }
71
+ return body;
72
+ }
73
+ async function sleep(ms, signal) {
74
+ if (signal?.aborted) {
75
+ throw new DOMException("Aborted", "AbortError");
76
+ }
77
+ await new Promise((resolve, reject) => {
78
+ const timer = setTimeout(resolve, ms);
79
+ if (signal) {
80
+ const onAbort = () => {
81
+ clearTimeout(timer);
82
+ reject(new DOMException("Aborted", "AbortError"));
83
+ };
84
+ signal.addEventListener("abort", onAbort, { once: true });
85
+ }
86
+ });
87
+ }
88
+ async function bytesFromUrl(url, signal) {
89
+ const res = await fetch(url, { signal });
90
+ if (!res.ok) {
91
+ throw new Error(`下载生成图片失败: ${res.status} ${res.statusText}`);
92
+ }
93
+ const buffer = await res.arrayBuffer();
94
+ const uint8Array = new Uint8Array(buffer);
95
+ const mediaType = res.headers.get("content-type") || "image/png";
96
+ let binary = "";
97
+ for (let i = 0; i < uint8Array.length; i++) {
98
+ binary += String.fromCharCode(uint8Array[i]);
99
+ }
100
+ const base64 = typeof btoa !== "undefined"
101
+ ? btoa(binary)
102
+ : Buffer.from(uint8Array).toString("base64");
103
+ return { base64, uint8Array, mediaType, url };
104
+ }
105
+ function fileFromB64(b64, mediaType = "image/png") {
106
+ const uint8Array = typeof Buffer !== "undefined"
107
+ ? new Uint8Array(Buffer.from(b64, "base64"))
108
+ : Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
109
+ return { base64: b64, uint8Array, mediaType };
110
+ }
111
+ /** POST /api/llm/image/images/tasks */
112
+ async function submitImageTask(pluginId, body, signal, headers) {
113
+ const config = getImageProxyConfig(pluginId, headers);
114
+ const res = await fetch(`${config.baseUrl}/images/tasks`, {
115
+ method: "POST",
116
+ headers: config.headers,
117
+ body: JSON.stringify(body),
118
+ signal,
119
+ });
120
+ if (!res.ok) {
121
+ const text = await res.text().catch(() => "");
122
+ throw new Error(`提交图像任务失败: ${res.status} ${text}`);
123
+ }
124
+ return (await res.json());
125
+ }
126
+ /** GET /api/llm/image/images/tasks/:taskId */
127
+ async function getImageTask(pluginId, taskId, signal, headers) {
128
+ const config = getImageProxyConfig(pluginId, headers);
129
+ const res = await fetch(`${config.baseUrl}/images/tasks/${taskId}`, {
130
+ method: "GET",
131
+ headers: config.headers,
132
+ signal,
133
+ });
134
+ if (!res.ok) {
135
+ const text = await res.text().catch(() => "");
136
+ throw new Error(`查询图像任务失败: ${res.status} ${text}`);
137
+ }
138
+ return (await res.json());
139
+ }
140
+ /** POST /api/llm/image/images/generations(同步,不推荐生产使用) */
141
+ async function generateImageSync(pluginId, body, signal, headers) {
142
+ const config = getImageProxyConfig(pluginId, headers);
143
+ const res = await fetch(`${config.baseUrl}/images/generations`, {
144
+ method: "POST",
145
+ headers: config.headers,
146
+ body: JSON.stringify(body),
147
+ signal,
148
+ });
149
+ if (!res.ok) {
150
+ const text = await res.text().catch(() => "");
151
+ throw new Error(`同步图像生成失败: ${res.status} ${text}`);
152
+ }
153
+ return (await res.json());
154
+ }
155
+ async function pollImageTask(pluginId, taskId, options) {
156
+ const interval = options?.pollIntervalMs ?? 30000;
157
+ const timeout = options?.pollTimeoutMs ?? 15 * 60 * 1000;
158
+ const started = Date.now();
159
+ while (Date.now() - started < timeout) {
160
+ const json = await getImageTask(pluginId, taskId, options?.abortSignal, options?.headers);
161
+ const data = json.data;
162
+ const status = data?.status?.toUpperCase();
163
+ if (status && TERMINAL_TASK_STATUSES.has(status)) {
164
+ if (status === "SUCCESS" && data?.result_url) {
165
+ return data.result_url;
166
+ }
167
+ throw new Error(data?.fail_reason || `图像生成任务失败: ${status}`);
168
+ }
169
+ await sleep(interval, options?.abortSignal);
170
+ }
171
+ throw new Error(`图像生成任务超时(${timeout}ms): ${taskId}`);
172
+ }
173
+ function parseGenerationResponse(resp) {
174
+ const files = [];
175
+ for (const item of resp.data ?? []) {
176
+ if (item.b64_json) {
177
+ files.push(fileFromB64(item.b64_json));
178
+ }
179
+ else if (item.url) {
180
+ // 同步 URL 需调用方自行下载;generateKunpoImage 内会处理
181
+ files.push({
182
+ base64: "",
183
+ uint8Array: new Uint8Array(),
184
+ mediaType: "image/png",
185
+ url: item.url,
186
+ });
187
+ }
188
+ }
189
+ if (!files.length) {
190
+ throw new Error("图像生成响应中无图片数据");
191
+ }
192
+ return files;
193
+ }
194
+ /**
195
+ * 通过后端 /api/llm/image 代理生成图像(默认异步)
196
+ */
197
+ async function generateKunpoImage(options) {
198
+ const useAsync = options.async !== false;
199
+ if (useAsync) {
200
+ const body = buildAsyncSubmitBody(options);
201
+ const submitted = await submitImageTask(options.pluginId, body, options.abortSignal, options.headers);
202
+ const taskId = submitted.task_id || submitted.id;
203
+ if (!taskId) {
204
+ throw new Error("提交图像任务成功但未返回 task_id");
205
+ }
206
+ const resultUrl = await pollImageTask(options.pluginId, taskId, {
207
+ pollIntervalMs: options.pollIntervalMs,
208
+ pollTimeoutMs: options.pollTimeoutMs,
209
+ abortSignal: options.abortSignal,
210
+ headers: options.headers,
211
+ });
212
+ const file = await bytesFromUrl(resultUrl, options.abortSignal);
213
+ return {
214
+ image: file,
215
+ images: [file],
216
+ taskId,
217
+ resultUrl,
218
+ };
219
+ }
220
+ const resp = await generateImageSync(options.pluginId, buildSyncBody(options), options.abortSignal, options.headers);
221
+ let files = parseGenerationResponse(resp);
222
+ if (files[0]?.url && !files[0].base64) {
223
+ const downloaded = await bytesFromUrl(files[0].url, options.abortSignal);
224
+ files = [downloaded];
225
+ }
226
+ return { image: files[0], images: files };
227
+ }
package/dist/index.d.ts CHANGED
@@ -45,3 +45,4 @@ export { sdkConfig, VersionCompatibilityError, SDK_SIGNATURE, type Authenticated
45
45
  */
46
46
  export declare function createChatModel(model: string, config: LangchainClientConfig & BaseChatModelParams): BaseChatModel;
47
47
  export * from "./llm";
48
+ export { generateKunpoImage, generateImageSync, submitImageTask, getImageTask, pollImageTask, getImageProxyConfig, bytesFromUrl, type KunpoTencentImageModel, type KunpoImageQuality, type KunpoInputFidelity, type KunpoResponseFormat, type KunpoImageGenerationOptions, type KunpoImageGenerationResult, type GeneratedImageFile, } from "./image";
package/dist/index.js CHANGED
@@ -20,7 +20,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
20
20
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
21
21
  };
22
22
  Object.defineProperty(exports, "__esModule", { value: true });
23
- exports.SDK_SIGNATURE = exports.VersionCompatibilityError = exports.sdkConfig = exports.ChromeExtensionClient = exports.SharedResourceClient = exports.AIConfigClient = exports.DashboardClient = exports.StatsClient = exports.PluginManagementClient = exports.AdminClient = exports.getCurrentUserInfo = exports.AuthClient = exports.AgentSkillClient = exports.VersionedResourceClient = exports.ResourceClient = exports.MinioStorageClient = exports.DownloadClient = exports.OpenAIVideoGenerationClient = exports.VideoUnderstandingClient = exports.VideoGenerationClient = exports.GeminiImageGenerationClient = exports.DoubaoImageGenerationClient = exports.ChatAnthropic = exports.ChatGoogleGenerativeAI = exports.ChatOpenAI = exports.BaseChatModel = exports.AIMessageChunk = exports.SystemMessage = exports.AIMessage = exports.HumanMessage = void 0;
23
+ exports.bytesFromUrl = exports.getImageProxyConfig = exports.pollImageTask = exports.getImageTask = exports.submitImageTask = exports.generateImageSync = exports.generateKunpoImage = exports.SDK_SIGNATURE = exports.VersionCompatibilityError = exports.sdkConfig = exports.ChromeExtensionClient = exports.SharedResourceClient = exports.AIConfigClient = exports.DashboardClient = exports.StatsClient = exports.PluginManagementClient = exports.AdminClient = exports.getCurrentUserInfo = exports.AuthClient = exports.AgentSkillClient = exports.VersionedResourceClient = exports.ResourceClient = exports.MinioStorageClient = exports.DownloadClient = exports.OpenAIVideoGenerationClient = exports.VideoUnderstandingClient = exports.VideoGenerationClient = exports.GeminiImageGenerationClient = exports.DoubaoImageGenerationClient = exports.ChatAnthropic = exports.ChatGoogleGenerativeAI = exports.ChatOpenAI = exports.BaseChatModel = exports.AIMessageChunk = exports.SystemMessage = exports.AIMessage = exports.HumanMessage = void 0;
24
24
  exports.createChatModel = createChatModel;
25
25
  const openai_1 = require("./chat_models/openai");
26
26
  const google_1 = require("./chat_models/google");
@@ -121,3 +121,11 @@ function createChatModel(model, config) {
121
121
  });
122
122
  }
123
123
  __exportStar(require("./llm"), exports);
124
+ var image_1 = require("./image");
125
+ Object.defineProperty(exports, "generateKunpoImage", { enumerable: true, get: function () { return image_1.generateKunpoImage; } });
126
+ Object.defineProperty(exports, "generateImageSync", { enumerable: true, get: function () { return image_1.generateImageSync; } });
127
+ Object.defineProperty(exports, "submitImageTask", { enumerable: true, get: function () { return image_1.submitImageTask; } });
128
+ Object.defineProperty(exports, "getImageTask", { enumerable: true, get: function () { return image_1.getImageTask; } });
129
+ Object.defineProperty(exports, "pollImageTask", { enumerable: true, get: function () { return image_1.pollImageTask; } });
130
+ Object.defineProperty(exports, "getImageProxyConfig", { enumerable: true, get: function () { return image_1.getImageProxyConfig; } });
131
+ Object.defineProperty(exports, "bytesFromUrl", { enumerable: true, get: function () { return image_1.bytesFromUrl; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-world-sdk",
3
- "version": "1.5.8",
3
+ "version": "1.5.9",
4
4
  "description": "TypeScript SDK for AI World Platform - Chat Models, Image Generation, and Video Generation",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,13 +1,331 @@
1
1
  # 图像生成
2
2
 
3
- ## AI SDK 图像生成(DALL-E / GPT Image)
3
+ ## KUNPO 腾讯 AIART · `generateKunpoImage`
4
+
5
+ 通过 `generateKunpoImage` 调用腾讯 AIART(OpenAI Images API 兼容格式),请求经平台后端 **`/api/llm/image`** 代理转发至 KUNPO 网关的 `/v1/images/*`。
6
+
7
+ 接口语义与 [OpenAI Image API](https://developers.openai.com/api/docs/guides/image-generation?api=image) 的 **Generations** 端点相近(`model`、`prompt`、`size`、`quality`、参考图等),但模型名与映射规则以 KUNPO/腾讯 AIART 为准。
8
+
9
+ ### 调用路径
10
+
11
+ | SDK `async` | 代理路径 | 说明 |
12
+ |-------------|----------|------|
13
+ | `true`(**默认**) | `POST /images/tasks` → 轮询 `GET /images/tasks/:id` | 生产推荐;GI 约 30–60s,`Image-GPT2` 可达数分钟 |
14
+ | `false` | `POST /images/generations` | 网关内最长等待约 15 分钟;易受 CDN/反代超时,GPT 易 504 |
15
+
16
+ SDK 在异步模式下自动轮询直至 `SUCCESS` 或 `FAILURE`,并下载 `result_url` 填入 `images[0]`。
17
+
18
+ ### 支持的模型
19
+
20
+ | `model` | 类型 | 说明 |
21
+ |---------|------|------|
22
+ | `Image-GI` | GI | 腾讯 AIART 文生图 |
23
+ | `Image-GI2` | GI | 腾讯 AIART 文生图 v2(**CLI 默认**) |
24
+ | `Image-GPT2` | GPT | 腾讯 GPT Image 2(对齐 OpenAI GPT Image 类参数) |
25
+
26
+ > **务必使用 `Image-GPT2`**,不要写 `gpt-image-2` / `gpt-image-1` 等 OpenAI 官方模型 ID,否则会路由到其他渠道,参数规则不同。
27
+
28
+ ### 快速开始
29
+
30
+ ```typescript
31
+ import { generateKunpoImage } from 'ai-world-sdk';
32
+
33
+ const { images, taskId, resultUrl } = await generateKunpoImage({
34
+ pluginId: 'my-plugin',
35
+ model: 'Image-GI2',
36
+ prompt: '赛博朋克风格的武汉长江大桥夜景,霓虹灯光',
37
+ size: '1536x1024',
38
+ quality: 'high',
39
+ // async: true 为默认值
40
+ });
41
+
42
+ // images[0].base64 / images[0].uint8Array / images[0].mediaType
43
+ // 异步时另有 taskId、resultUrl(永久 CDN 地址)
44
+ ```
45
+
46
+ ### 返回值 `KunpoImageGenerationResult`
47
+
48
+ | 字段 | 说明 |
49
+ |------|------|
50
+ | `image` | 第一张图(与 `images[0]` 相同) |
51
+ | `images` | 图片数组(异步目前为 1 张) |
52
+ | `taskId` | 异步任务 ID(仅 `async: true`) |
53
+ | `resultUrl` | 生成图永久 CDN URL(仅 `async: true`) |
54
+
55
+ ---
56
+
57
+ ## `generateKunpoImage` 参数完整说明
58
+
59
+ ### 必填参数
60
+
61
+ | 参数 | 类型 | 映射 API 字段 | 说明 |
62
+ |------|------|---------------|------|
63
+ | `pluginId` | `string` | 请求头 `X-Plugin-Id` | 插件 ID,用于鉴权与统计 |
64
+ | `model` | `string` | `model` | `Image-GI` / `Image-GI2` / `Image-GPT2` |
65
+ | `prompt` | `string` | `prompt` | 图像描述,建议具体、可视觉化(参考 [OpenAI 提示词实践](https://developers.openai.com/api/docs/guides/image-generation?api=image)) |
66
+
67
+ ### 调用模式
68
+
69
+ | 参数 | 类型 | 默认 | 说明 |
70
+ |------|------|------|------|
71
+ | `async` | `boolean` | `true` | `true`:提交任务并轮询;`false`:同步 `generations` |
72
+ | `pollIntervalMs` | `number` | `30000` | 异步轮询间隔(毫秒) |
73
+ | `pollTimeoutMs` | `number` | `900000`(15 分钟) | 异步轮询超时 |
74
+ | `abortSignal` | `AbortSignal` | — | 取消请求或轮询 |
75
+ | `headers` | `Record<string, string>` | — | 额外 HTTP 头 |
76
+
77
+ ### 输出与数量
78
+
79
+ | 参数 | 类型 | 默认 | 同步 API | 异步 API | 说明 |
80
+ |------|------|------|----------|----------|------|
81
+ | `n` | `number` | `1` | `n` | — | **仅同步**有效;异步每次任务 1 张 |
82
+ | `responseFormat` | `'url' \| 'b64_json'` | `'b64_json'`(同步) | `response_format` | — | 同步默认 SDK 请求 `b64_json` 便于直接拿字节;异步结果始终经 `result_url` 下载 |
83
+
84
+ ### `size` — 尺寸(OpenAI 兼容字段名)
85
+
86
+ 与 [OpenAI Image API 的 size](https://developers.openai.com/api/docs/guides/image-generation?api=image#size-and-quality-options) 使用相同字符串格式(如 `1024x1024`),但 **GI 与 GPT 映射逻辑不同**:
87
+
88
+ #### GI / GI2(`Image-GI`、`Image-GI2`)
89
+
90
+ `size` 映射为**宽高比**,非精确像素:
91
+
92
+ | `size` 取值 | 实际宽高比 |
93
+ |-------------|------------|
94
+ | `1024x1024` 或省略 | `1:1` |
95
+ | `1792x1024`、`1536x1024`、`1280x720` | `16:9` |
96
+ | `1024x1792`、`1024x1536`、`720x1280` | `9:16` |
97
+ | **其它任意值** | **回落 `1:1`** |
98
+
99
+ 常用横图:`1536x1024`;竖图:`1024x1536`。
100
+
101
+ #### GPT(`Image-GPT2`)
102
+
103
+ | `size` 取值 | 效果 |
104
+ |-------------|------|
105
+ | 省略 | 默认 `1024x1024` |
106
+ | `1024x1024`、`1536x1024`、`1024x1536`、`auto` | 保持原值 |
107
+ | **其它** | **回落 `1024x1024`** |
108
+
109
+ OpenAI 文档中 `gpt-image-2` 还支持更多分辨率(如 `2048x2048`);经 KUNPO 网关时以腾讯上游支持为准,非法值会被回落。
110
+
111
+ SDK 类型:`size?: \`${number}x${number}\` | 'auto'`。
112
+
113
+ ### `quality` — 质量
114
+
115
+ OpenAI 侧为渲染档位(`low` / `medium` / `high` 等);经 KUNPO 后 **GI 映射分辨率、GPT 映射质量档位**:
116
+
117
+ #### GI / GI2
118
+
119
+ | `quality` | 上游分辨率 |
120
+ |-----------|------------|
121
+ | `hd`、`high` | `4K` |
122
+ | `low`、`standard` | `1K` |
123
+ | 省略、`medium` 及其它 | **`2K`(默认)** |
124
+
125
+ #### Image-GPT2
126
+
127
+ | `quality` | 效果 |
128
+ |-----------|------|
129
+ | `hd`、`high` | `high` |
130
+ | `low` | `low` |
131
+ | 省略及其它 | **`medium`(默认)** |
132
+
133
+ #### 异步 vs 同步传参(重要)
134
+
135
+ | 模式 | SDK 写法 | 实际请求体 |
136
+ |------|----------|------------|
137
+ | **异步**(`async: true`) | `quality: 'high'` | `metadata: { quality: 'high' }`(SDK 自动转换) |
138
+ | **同步**(`async: false`) | `quality: 'high'` | 顶层 `quality: 'high'` |
139
+
140
+ > 异步接口顶层 `quality` **不生效**;务必通过 SDK 的 `quality` 字段,不要手写错误结构。
141
+
142
+ 类型:`KunpoImageQuality = 'low' | 'standard' | 'medium' | 'high' | 'hd'`。
143
+
144
+ ### 图生图(参考图)
145
+
146
+ | 参数 | 类型 | API 字段 | 说明 |
147
+ |------|------|----------|------|
148
+ | `images` | `string[]` | `images` | 多张参考图 URL;GI 映射 `Url`,GPT 映射 `ImageUrl` |
149
+ | `image` | `string` | `image` | 单张参考图:**HTTPS URL** 或 **Base64**(网关会自动上传 Base64 到 COS) |
150
+
151
+ **限制(上游)**:
152
+
153
+ - 单张参考图 ≤ **10 MiB**
154
+ - 格式:PNG、JPEG、WebP、GIF
155
+ - GI/GI2:`images` 或 `image` 均可
156
+ - **Image-GPT2 图生图推荐 `image` 单图 URL**
157
+
158
+ 与 OpenAI [Create a new image using image references](https://developers.openai.com/api/docs/guides/image-generation?api=image#create-a-new-image-using-image-references) 类似,用参考图约束风格/主体。
159
+
160
+ ### 仅 `Image-GPT2` 支持的参数
161
+
162
+ | SDK 参数 | API 字段 | 说明 |
163
+ |----------|----------|------|
164
+ | `background` | `background` | 背景描述(如 `white background`);OpenAI 还支持 `transparent`/`auto`,腾讯上游以网关为准 |
165
+ | `outputFormat` | `output_format` | 仅 **`png`** 有效,其它值仍返回 png |
166
+ | `inputFidelity` | `input_fidelity` | 仅 **`high`** 或 **`low`**,其它值返回 400;控制编辑/参考图保真度(概念同 OpenAI [`input_fidelity`](https://developers.openai.com/api/docs/guides/image-generation?api=image#image-input-fidelity)) |
167
+
168
+ > OpenAI `gpt-image-2` 不建议传 `input_fidelity`(固定高保真);`Image-GPT2` 经 KUNPO 时仍可传 `high`/`low`。
169
+
170
+ ### 其它
171
+
172
+ | 参数 | 同步 | 异步 | 说明 |
173
+ |------|------|------|------|
174
+ | `seed` | ✓ | — | 随机种子(若上游支持) |
175
+ | `maxRetries` | ✓ | — | 同步路径重试次数 |
176
+
177
+ **当前不支持**:`mask`(蒙版)、水印等字段。
178
+
179
+ ---
180
+
181
+ ## 按场景的参数组合
182
+
183
+ ### 文生图(GI,横屏海报)
184
+
185
+ ```typescript
186
+ await generateKunpoImage({
187
+ pluginId: 'my-plugin',
188
+ model: 'Image-GI2',
189
+ prompt: '水墨山水画,远处白云缭绕的山峰',
190
+ size: '1536x1024', // → 16:9
191
+ quality: 'high', // → 4K
192
+ });
193
+ ```
194
+
195
+ ### 文生图(GPT,竖版)
196
+
197
+ ```typescript
198
+ await generateKunpoImage({
199
+ pluginId: 'my-plugin',
200
+ model: 'Image-GPT2',
201
+ prompt: '扁平插画,一杯咖啡,白色背景',
202
+ size: '1024x1536',
203
+ quality: 'high',
204
+ background: 'white background',
205
+ outputFormat: 'png',
206
+ });
207
+ ```
208
+
209
+ ### 图生图(URL 参考)
210
+
211
+ ```typescript
212
+ await generateKunpoImage({
213
+ pluginId: 'my-plugin',
214
+ model: 'Image-GI',
215
+ prompt: '保持构图,改为水彩风格',
216
+ images: ['https://example.com/ref.png'],
217
+ });
218
+ ```
219
+
220
+ ### 同步调试(不推荐生产)
221
+
222
+ ```typescript
223
+ const { images } = await generateKunpoImage({
224
+ pluginId: 'my-plugin',
225
+ model: 'Image-GI2',
226
+ prompt: '红色圆形',
227
+ async: false,
228
+ n: 1,
229
+ responseFormat: 'b64_json',
230
+ });
231
+ ```
232
+
233
+ ---
234
+
235
+ ## 底层 HTTP API(高级)
236
+
237
+ 无需 `generateKunpoImage` 时可直接调用:
238
+
239
+ ```typescript
240
+ import {
241
+ submitImageTask,
242
+ getImageTask,
243
+ pollImageTask,
244
+ generateImageSync,
245
+ getImageProxyConfig,
246
+ } from 'ai-world-sdk';
247
+
248
+ // 提交异步任务
249
+ const { task_id } = await submitImageTask('my-plugin', {
250
+ model: 'Image-GPT2',
251
+ prompt: '...',
252
+ size: '1024x1024',
253
+ metadata: { quality: 'high' },
254
+ });
255
+
256
+ // 轮询
257
+ const url = await pollImageTask('my-plugin', task_id!);
258
+
259
+ // 或同步
260
+ const resp = await generateImageSync('my-plugin', {
261
+ model: 'Image-GI2',
262
+ prompt: '...',
263
+ quality: 'high',
264
+ response_format: 'b64_json',
265
+ });
266
+ ```
267
+
268
+ 代理基址:`getImageProxyConfig(pluginId).baseUrl` → `{serverUrl}/api/llm/image`。
269
+
270
+ ---
271
+
272
+ ## CLI
273
+
274
+ ```bash
275
+ # KUNPO 异步(默认)
276
+ ai-world image generate --provider kunpo \
277
+ --prompt "赛博朋克大桥" \
278
+ --model Image-GI2 \
279
+ --size 1536x1024 \
280
+ --quality high \
281
+ --plugin-id my-plugin \
282
+ --output out.png
283
+
284
+ # KUNPO 同步(易超时)
285
+ ai-world image generate --provider kunpo --sync ...
286
+ ```
287
+
288
+ ---
289
+
290
+ ## 常见错误
291
+
292
+ | 现象 | 原因 | 处理 |
293
+ |------|------|------|
294
+ | `unsupported model` | `model` 名称错误 | 使用 `Image-GI` / `Image-GI2` / `Image-GPT2` |
295
+ | 异步 `quality` 无效 | 手写顶层 `quality` 而非 `metadata` | 使用 SDK `quality` 字段 |
296
+ | 同步 504 | GPT 耗时长 + CDN 超时 | 改 `async: true` |
297
+ | GI 同步 `terminated` | 连接超时 | 改异步 |
298
+ | `invalid input_fidelity` | 非 `high`/`low` | 仅传 `high` 或 `low` |
299
+ | `UrlIllegal` | 参考图 Base64/体积/格式错误 | ≤10MiB,PNG/JPEG/WebP/GIF |
300
+ | `size` 不符合预期 | GI 将未知 size 回落 1:1 | 使用文档列出的 size 值 |
301
+
302
+ ---
303
+
304
+ ## 与 OpenAI Image API 的对照
305
+
306
+ | 概念 | OpenAI([文档](https://developers.openai.com/api/docs/guides/image-generation?api=image)) | KUNPO `generateKunpoImage` |
307
+ |------|-------------------------------------------------------------------------------------------|---------------------------|
308
+ | 端点 | `/v1/images/generations` | 经 `/api/llm/image/images/generations` |
309
+ | 模型 | `gpt-image-2` 等 | `Image-GPT2`(勿混用 ID) |
310
+ | `prompt` | 必填 | 必填 |
311
+ | `size` | 像素或 `auto` | 同字段名;GI/GPT 映射见上 |
312
+ | `quality` | `low`/`medium`/`high`/`auto` | 同枚举;GI 另映射分辨率 |
313
+ | `n` | 单次多张 | 仅同步有效 |
314
+ | 参考图 | `image` / 多图 | `image` / `images` |
315
+ | 长耗时 | 官方建议 Image API 单次生成 | **务必** `async: true` |
316
+
317
+ 平台内 **DALL·E / api2img** 等仍走 `createProvider` + Vercel AI SDK `generateImage`,见下文。
318
+
319
+ ---
320
+
321
+ ## AI SDK 图像生成(DALL-E / GPT Image · api2img)
4
322
 
5
323
  ```typescript
6
324
  import { createProvider } from 'ai-world-sdk';
7
325
  import { generateImage } from 'ai';
8
326
 
9
327
  const provider = createProvider('api2img', 'openai', 'my-plugin');
10
- const imageModel = (provider as any).image('dall-e-3'); // as any: ProviderV3 类型不含 .image()
328
+ const imageModel = (provider as any).image('dall-e-3');
11
329
 
12
330
  const { images } = await generateImage({
13
331
  model: imageModel,
@@ -15,7 +333,6 @@ const { images } = await generateImage({
15
333
  size: '1024x1024',
16
334
  n: 1,
17
335
  });
18
- // images[0].base64, images[0].mediaType
19
336
  ```
20
337
 
21
338
  ## 豆包(Doubao)图像生成
@@ -30,9 +347,9 @@ const client = new DoubaoImageGenerationClient({});
30
347
  const result = await client.generate({
31
348
  prompt: '一只可爱的小猫',
32
349
  model: 'doubao-seedream-4-5-251128',
33
- size: '2K', // '1K' | '2K' | '4K' | '2048x2048' | '2560x1440' 等
34
- quality: 'hd', // 'standard' | 'hd'
35
- n: 1, // 1-10
350
+ size: '2K',
351
+ quality: 'hd',
352
+ n: 1,
36
353
  });
37
354
  console.log(result.data[0]?.url);
38
355
  ```
@@ -63,12 +380,10 @@ await client.generate({
63
380
  ```typescript
64
381
  import { GeminiImageGenerationClient } from 'ai-world-sdk';
65
382
 
66
- // provider 在构造函数中指定
67
383
  const client = new GeminiImageGenerationClient({ provider: 'gemini' });
68
384
  const result = await client.generate({
69
385
  prompt: '一只可爱的小猫',
70
386
  });
71
- // result.images — 生成的图像列表
72
387
  ```
73
388
 
74
389
  ### Gemini 图像对话(chat)
@@ -87,6 +402,6 @@ const chatResult = await client.chat({
87
402
  |------|------|------|
88
403
  | `provider` | 构造函数 | 图像生成 Provider(如 `'gemini'`) |
89
404
  | `prompt` | `generate()` | 文本提示词 |
90
- | `model` | `generate()` | 模型名称(默认: `gemini-2.0-flash-exp-image-generation`,chat 默认: `gemini-3-pro-image-preview`) |
405
+ | `model` | `generate()` | 模型名称 |
91
406
  | `image` | `generate()` / `chat()` | 输入图像(base64 或 URL) |
92
407
  | `aspect_ratio` | `generate()` | 宽高比 |