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.
- package/dist/__tests__/llm.real.test.js +13 -11
- package/dist/cli/commands/image.js +146 -43
- package/dist/config.d.ts +3 -3
- package/dist/config.js +2 -2
- package/dist/image.d.ts +93 -0
- package/dist/image.js +227 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +9 -1
- package/package.json +1 -1
- package/skills/ai-world-sdk/docs/image-generation.md +324 -9
|
@@ -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("
|
|
218
|
+
describe("generateKunpoImage(kunpo 图像)", () => {
|
|
218
219
|
const promptArg = "A simple red circle on a white background";
|
|
219
|
-
test("kunpo —
|
|
220
|
-
const modelId = process.env.KUNPO_IMAGE_MODEL || "
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
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("
|
|
298
|
-
const
|
|
299
|
-
|
|
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("
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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 或
|
|
155
|
+
if (!['doubao', 'gemini', 'openai', 'kunpo'].includes(providerName)) {
|
|
156
|
+
const err = new Error('缺少必需参数: --provider 必须是 doubao、gemini、openai 或 kunpo');
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
-
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
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:
|
|
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.
|
|
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.
|
|
39
|
-
readonly sdkVersion = "1.5.
|
|
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.
|
|
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.
|
|
18
|
+
exports.SDK_SIGNATURE = "AI_WORLD_SDK_V:1.5.9";
|
|
19
19
|
/**
|
|
20
20
|
* 版本兼容性错误
|
|
21
21
|
*/
|
package/dist/image.d.ts
ADDED
|
@@ -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,13 +1,331 @@
|
|
|
1
1
|
# 图像生成
|
|
2
2
|
|
|
3
|
-
##
|
|
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');
|
|
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',
|
|
34
|
-
quality: 'hd',
|
|
35
|
-
n: 1,
|
|
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()` |
|
|
405
|
+
| `model` | `generate()` | 模型名称 |
|
|
91
406
|
| `image` | `generate()` / `chat()` | 输入图像(base64 或 URL) |
|
|
92
407
|
| `aspect_ratio` | `generate()` | 宽高比 |
|