koishi-plugin-aka-ai-generator 0.0.2 → 0.0.5
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/lib/index.d.ts +1 -0
- package/lib/index.js +199 -29
- package/lib/providers/index.d.ts +1 -0
- package/lib/providers/types.d.ts +1 -0
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -195,7 +195,9 @@ async function downloadImageAsBase642(ctx, url, timeout, logger) {
|
|
|
195
195
|
mimeType = "image/gif";
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
|
-
logger
|
|
198
|
+
if (logger) {
|
|
199
|
+
logger.debug("图片下载并转换为Base64", { url, mimeType, size: base64.length });
|
|
200
|
+
}
|
|
199
201
|
return { data: base64, mimeType };
|
|
200
202
|
} catch (error) {
|
|
201
203
|
logger.error("下载图片失败", { url, error });
|
|
@@ -203,14 +205,23 @@ async function downloadImageAsBase642(ctx, url, timeout, logger) {
|
|
|
203
205
|
}
|
|
204
206
|
}
|
|
205
207
|
__name(downloadImageAsBase642, "downloadImageAsBase64");
|
|
206
|
-
function parseGptGodResponse(response) {
|
|
208
|
+
function parseGptGodResponse(response, logger) {
|
|
207
209
|
try {
|
|
208
210
|
const images = [];
|
|
209
211
|
if (Array.isArray(response.images)) {
|
|
212
|
+
if (logger) {
|
|
213
|
+
logger.debug("从 response.images 数组提取图片", { count: response.images.length });
|
|
214
|
+
}
|
|
210
215
|
return response.images;
|
|
211
216
|
}
|
|
212
|
-
if (response.image && typeof response.image === "string"
|
|
213
|
-
|
|
217
|
+
if (response.image && typeof response.image === "string") {
|
|
218
|
+
if (response.image.startsWith("data:") || response.image.startsWith("http")) {
|
|
219
|
+
if (logger) {
|
|
220
|
+
logger.debug("从 response.image 提取图片");
|
|
221
|
+
}
|
|
222
|
+
images.push(response.image);
|
|
223
|
+
return images;
|
|
224
|
+
}
|
|
214
225
|
}
|
|
215
226
|
if (response?.choices?.length > 0) {
|
|
216
227
|
const firstChoice = response.choices[0];
|
|
@@ -219,33 +230,123 @@ function parseGptGodResponse(response) {
|
|
|
219
230
|
if (typeof messageContent === "string") {
|
|
220
231
|
contentText = messageContent;
|
|
221
232
|
} else if (Array.isArray(messageContent)) {
|
|
222
|
-
|
|
233
|
+
for (const part of messageContent) {
|
|
234
|
+
if (part?.type === "image_url" && part?.image_url?.url) {
|
|
235
|
+
if (logger) {
|
|
236
|
+
logger.debug("从 content 数组的 image_url 提取图片");
|
|
237
|
+
}
|
|
238
|
+
images.push(part.image_url.url);
|
|
239
|
+
} else if (part?.type === "text" && part?.text) {
|
|
240
|
+
contentText += part.text + "\n";
|
|
241
|
+
} else if (part?.text) {
|
|
242
|
+
contentText += part.text + "\n";
|
|
243
|
+
}
|
|
244
|
+
}
|
|
223
245
|
} else if (messageContent?.text) {
|
|
224
246
|
contentText = messageContent.text;
|
|
225
247
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
while ((match = mdImageRegex.exec(contentText)) !== null) {
|
|
229
|
-
images.push(match[1]);
|
|
248
|
+
if (images.length > 0) {
|
|
249
|
+
return images;
|
|
230
250
|
}
|
|
231
|
-
if (
|
|
232
|
-
const
|
|
233
|
-
let
|
|
234
|
-
while ((
|
|
235
|
-
images.push(
|
|
251
|
+
if (contentText) {
|
|
252
|
+
const mdImageRegex = /!\[.*?\]\((https?:\/\/[^\)]+)\)/g;
|
|
253
|
+
let match;
|
|
254
|
+
while ((match = mdImageRegex.exec(contentText)) !== null) {
|
|
255
|
+
images.push(match[1]);
|
|
256
|
+
}
|
|
257
|
+
if (images.length === 0) {
|
|
258
|
+
const urlRegex = /(https?:\/\/[^\s"')<>]+\.(?:png|jpg|jpeg|webp|gif|bmp))/gi;
|
|
259
|
+
let urlMatch;
|
|
260
|
+
while ((urlMatch = urlRegex.exec(contentText)) !== null) {
|
|
261
|
+
images.push(urlMatch[1]);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (images.length === 0 && contentText.trim().startsWith("http")) {
|
|
265
|
+
const trimmedUrl = contentText.trim().split(/\s/)[0];
|
|
266
|
+
if (trimmedUrl.match(/^https?:\/\//)) {
|
|
267
|
+
images.push(trimmedUrl);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const dataUrlRegex = /(data:image\/[^;]+;base64,[^\s"')<>]+)/gi;
|
|
271
|
+
let dataUrlMatch;
|
|
272
|
+
while ((dataUrlMatch = dataUrlRegex.exec(contentText)) !== null) {
|
|
273
|
+
images.push(dataUrlMatch[1]);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
if (images.length === 0 && firstChoice.message) {
|
|
277
|
+
if (firstChoice.message.image_url) {
|
|
278
|
+
if (logger) {
|
|
279
|
+
logger.debug("从 message.image_url 提取图片");
|
|
280
|
+
}
|
|
281
|
+
images.push(firstChoice.message.image_url);
|
|
282
|
+
}
|
|
283
|
+
if (Array.isArray(firstChoice.message.images)) {
|
|
284
|
+
if (logger) {
|
|
285
|
+
logger.debug("从 message.images 数组提取图片", { count: firstChoice.message.images.length });
|
|
286
|
+
}
|
|
287
|
+
return firstChoice.message.images;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
if (images.length === 0) {
|
|
292
|
+
if (response.data) {
|
|
293
|
+
if (Array.isArray(response.data)) {
|
|
294
|
+
const dataImages = response.data.filter(
|
|
295
|
+
(item) => item?.url || item?.image_url || typeof item === "string" && (item.startsWith("http") || item.startsWith("data:"))
|
|
296
|
+
).map((item) => item?.url || item?.image_url || item);
|
|
297
|
+
if (dataImages.length > 0) {
|
|
298
|
+
if (logger) {
|
|
299
|
+
logger.debug("从 response.data 数组提取图片", { count: dataImages.length });
|
|
300
|
+
}
|
|
301
|
+
return dataImages;
|
|
302
|
+
}
|
|
303
|
+
} else if (response.data.url || response.data.image_url) {
|
|
304
|
+
if (logger) {
|
|
305
|
+
logger.debug("从 response.data 提取图片");
|
|
306
|
+
}
|
|
307
|
+
images.push(response.data.url || response.data.image_url);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (response.result) {
|
|
311
|
+
if (Array.isArray(response.result)) {
|
|
312
|
+
const resultImages = response.result.filter(
|
|
313
|
+
(item) => item?.url || item?.image_url || typeof item === "string" && (item.startsWith("http") || item.startsWith("data:"))
|
|
314
|
+
).map((item) => item?.url || item?.image_url || item);
|
|
315
|
+
if (resultImages.length > 0) {
|
|
316
|
+
if (logger) {
|
|
317
|
+
logger.debug("从 response.result 数组提取图片", { count: resultImages.length });
|
|
318
|
+
}
|
|
319
|
+
return resultImages;
|
|
320
|
+
}
|
|
321
|
+
} else if (typeof response.result === "string" && (response.result.startsWith("http") || response.result.startsWith("data:"))) {
|
|
322
|
+
if (logger) {
|
|
323
|
+
logger.debug("从 response.result 提取图片");
|
|
324
|
+
}
|
|
325
|
+
images.push(response.result);
|
|
236
326
|
}
|
|
237
327
|
}
|
|
238
|
-
|
|
239
|
-
|
|
328
|
+
}
|
|
329
|
+
if (images.length > 0) {
|
|
330
|
+
if (logger) {
|
|
331
|
+
logger.debug("成功提取图片", { count: images.length });
|
|
240
332
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
333
|
+
} else {
|
|
334
|
+
if (logger) {
|
|
335
|
+
logger.warn("未能从响应中提取图片", {
|
|
336
|
+
responseStructure: {
|
|
337
|
+
hasChoices: !!response?.choices,
|
|
338
|
+
hasImages: !!response?.images,
|
|
339
|
+
hasImage: !!response?.image,
|
|
340
|
+
hasData: !!response?.data,
|
|
341
|
+
hasResult: !!response?.result,
|
|
342
|
+
keys: Object.keys(response || {})
|
|
343
|
+
}
|
|
344
|
+
});
|
|
245
345
|
}
|
|
246
346
|
}
|
|
247
347
|
return images;
|
|
248
348
|
} catch (error) {
|
|
349
|
+
logger?.error("解析响应时出错", { error });
|
|
249
350
|
return [];
|
|
250
351
|
}
|
|
251
352
|
}
|
|
@@ -265,7 +366,9 @@ var GptGodProvider = class {
|
|
|
265
366
|
if (!this.config.apiKey) {
|
|
266
367
|
throw new Error("GPTGod 配置不完整,请检查 API Key");
|
|
267
368
|
}
|
|
268
|
-
|
|
369
|
+
if (this.config.logLevel === "debug") {
|
|
370
|
+
logger.debug("调用 GPTGod 图像编辑 API", { prompt, imageCount: urls.length, numImages });
|
|
371
|
+
}
|
|
269
372
|
const contentParts = [
|
|
270
373
|
{
|
|
271
374
|
type: "text",
|
|
@@ -312,9 +415,62 @@ var GptGodProvider = class {
|
|
|
312
415
|
}
|
|
313
416
|
);
|
|
314
417
|
logger.success("GPTGod 图像编辑 API 调用成功");
|
|
315
|
-
|
|
418
|
+
if (response?.choices?.length > 0) {
|
|
419
|
+
const firstChoice = response.choices[0];
|
|
420
|
+
const messageContent = firstChoice.message?.content;
|
|
421
|
+
let errorMessage = "";
|
|
422
|
+
if (typeof messageContent === "string") {
|
|
423
|
+
errorMessage = messageContent;
|
|
424
|
+
} else if (Array.isArray(messageContent)) {
|
|
425
|
+
const textParts = messageContent.filter((part) => part?.type === "text" && part?.text).map((part) => part.text).join(" ");
|
|
426
|
+
errorMessage = textParts;
|
|
427
|
+
} else if (messageContent?.text) {
|
|
428
|
+
errorMessage = messageContent.text;
|
|
429
|
+
}
|
|
430
|
+
if (errorMessage && (errorMessage.includes("PROHIBITED_CONTENT") || errorMessage.includes("blocked by Google Gemini") || errorMessage.includes("prohibited under official usage policies") || errorMessage.toLowerCase().includes("content is prohibited"))) {
|
|
431
|
+
logger.error("内容被 Google Gemini 政策拦截", {
|
|
432
|
+
errorMessage: errorMessage.substring(0, 200),
|
|
433
|
+
finishReason: firstChoice.finish_reason
|
|
434
|
+
});
|
|
435
|
+
throw new Error("内容被安全策略拦截");
|
|
436
|
+
}
|
|
437
|
+
if (errorMessage && (errorMessage.toLowerCase().includes("error") || errorMessage.toLowerCase().includes("failed") || errorMessage.toLowerCase().includes("blocked")) && !errorMessage.match(/https?:\/\//)) {
|
|
438
|
+
logger.error("API 返回错误消息", {
|
|
439
|
+
errorMessage: errorMessage.substring(0, 200),
|
|
440
|
+
finishReason: firstChoice.finish_reason
|
|
441
|
+
});
|
|
442
|
+
const shortError = errorMessage.length > 50 ? errorMessage.substring(0, 50) + "..." : errorMessage;
|
|
443
|
+
throw new Error(`处理失败:${shortError}`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (this.config.logLevel === "debug") {
|
|
447
|
+
logger.debug("GPTGod API 响应结构", {
|
|
448
|
+
hasChoices: !!response?.choices,
|
|
449
|
+
choicesLength: response?.choices?.length,
|
|
450
|
+
hasImages: !!response?.images,
|
|
451
|
+
hasImage: !!response?.image,
|
|
452
|
+
responseKeys: Object.keys(response || {}),
|
|
453
|
+
firstChoiceContent: response?.choices?.[0]?.message?.content ? typeof response.choices[0].message.content === "string" ? response.choices[0].message.content.substring(0, 200) : JSON.stringify(response.choices[0].message.content).substring(0, 200) : "none"
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
const images = parseGptGodResponse(response, this.config.logLevel === "debug" ? logger : null);
|
|
316
457
|
if (images.length < numImages) {
|
|
317
|
-
|
|
458
|
+
const warnData = {
|
|
459
|
+
requested: numImages,
|
|
460
|
+
received: images.length
|
|
461
|
+
};
|
|
462
|
+
if (this.config.logLevel === "debug") {
|
|
463
|
+
warnData.responsePreview = JSON.stringify(response).substring(0, 500);
|
|
464
|
+
}
|
|
465
|
+
logger.warn("生成的图片数量不足", warnData);
|
|
466
|
+
if (images.length === 0 && response?.choices?.[0]?.message?.content) {
|
|
467
|
+
const content = response.choices[0].message.content;
|
|
468
|
+
const contentText = typeof content === "string" ? content : Array.isArray(content) ? content.map((p) => p?.text || "").join(" ") : "";
|
|
469
|
+
if (contentText && !contentText.match(/https?:\/\//)) {
|
|
470
|
+
const shortError = contentText.length > 50 ? contentText.substring(0, 50) + "..." : contentText;
|
|
471
|
+
throw new Error(`生成失败:${shortError}`);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
318
474
|
}
|
|
319
475
|
return images;
|
|
320
476
|
} catch (error) {
|
|
@@ -337,6 +493,7 @@ function createImageProvider(config) {
|
|
|
337
493
|
apiKey: config.yunwuApiKey,
|
|
338
494
|
modelId: config.yunwuModelId,
|
|
339
495
|
apiTimeout: config.apiTimeout,
|
|
496
|
+
logLevel: config.logLevel,
|
|
340
497
|
logger: config.logger,
|
|
341
498
|
ctx: config.ctx
|
|
342
499
|
});
|
|
@@ -345,6 +502,7 @@ function createImageProvider(config) {
|
|
|
345
502
|
apiKey: config.gptgodApiKey,
|
|
346
503
|
modelId: config.gptgodModelId,
|
|
347
504
|
apiTimeout: config.apiTimeout,
|
|
505
|
+
logLevel: config.logLevel,
|
|
348
506
|
logger: config.logger,
|
|
349
507
|
ctx: config.ctx
|
|
350
508
|
});
|
|
@@ -384,7 +542,12 @@ var Config = import_koishi.Schema.intersect([
|
|
|
384
542
|
rateLimitWindow: import_koishi.Schema.number().default(300).min(60).max(3600).description("限流时间窗口(秒)"),
|
|
385
543
|
rateLimitMax: import_koishi.Schema.number().default(3).min(1).max(20).description("限流窗口内最大调用次数"),
|
|
386
544
|
// 管理员设置
|
|
387
|
-
adminUsers: import_koishi.Schema.array(import_koishi.Schema.string()).default([]).description("管理员用户ID列表(不受每日使用限制)")
|
|
545
|
+
adminUsers: import_koishi.Schema.array(import_koishi.Schema.string()).default([]).description("管理员用户ID列表(不受每日使用限制)"),
|
|
546
|
+
// 日志级别设置
|
|
547
|
+
logLevel: import_koishi.Schema.union([
|
|
548
|
+
import_koishi.Schema.const("info").description("普通信息"),
|
|
549
|
+
import_koishi.Schema.const("debug").description("完整的debug信息")
|
|
550
|
+
]).default("info").description("日志输出详细程度")
|
|
388
551
|
}),
|
|
389
552
|
// 自定义风格命令配置
|
|
390
553
|
import_koishi.Schema.object({
|
|
@@ -432,6 +595,7 @@ function apply(ctx, config) {
|
|
|
432
595
|
gptgodApiKey: config.gptgodApiKey,
|
|
433
596
|
gptgodModelId: config.gptgodModelId,
|
|
434
597
|
apiTimeout: config.apiTimeout,
|
|
598
|
+
logLevel: config.logLevel,
|
|
435
599
|
logger,
|
|
436
600
|
ctx
|
|
437
601
|
});
|
|
@@ -688,7 +852,9 @@ function apply(ctx, config) {
|
|
|
688
852
|
if (img) {
|
|
689
853
|
url = img.attrs?.src || null;
|
|
690
854
|
if (url) {
|
|
691
|
-
|
|
855
|
+
if (config.logLevel === "debug") {
|
|
856
|
+
logger.debug("从命令参数获取图片", { url });
|
|
857
|
+
}
|
|
692
858
|
return url;
|
|
693
859
|
}
|
|
694
860
|
}
|
|
@@ -701,7 +867,9 @@ function apply(ctx, config) {
|
|
|
701
867
|
return null;
|
|
702
868
|
}
|
|
703
869
|
url = images2[0].attrs.src;
|
|
704
|
-
|
|
870
|
+
if (config.logLevel === "debug") {
|
|
871
|
+
logger.debug("从引用消息获取图片", { url });
|
|
872
|
+
}
|
|
705
873
|
return url;
|
|
706
874
|
}
|
|
707
875
|
}
|
|
@@ -722,7 +890,9 @@ function apply(ctx, config) {
|
|
|
722
890
|
return null;
|
|
723
891
|
}
|
|
724
892
|
url = images[0].attrs.src;
|
|
725
|
-
|
|
893
|
+
if (config.logLevel === "debug") {
|
|
894
|
+
logger.debug("从用户输入获取图片", { url });
|
|
895
|
+
}
|
|
726
896
|
return url;
|
|
727
897
|
}
|
|
728
898
|
__name(getImageUrl, "getImageUrl");
|
|
@@ -816,7 +986,7 @@ function apply(ctx, config) {
|
|
|
816
986
|
if (activeTasks.has(userId)) {
|
|
817
987
|
return "您有一个图像处理任务正在进行中,请等待完成";
|
|
818
988
|
}
|
|
819
|
-
await session.send('
|
|
989
|
+
await session.send('图片+描述\n\n多张图片使用"合成图像"指令');
|
|
820
990
|
const collectedImages = [];
|
|
821
991
|
let prompt = "";
|
|
822
992
|
while (true) {
|
|
@@ -921,7 +1091,7 @@ Prompt: ${prompt}`);
|
|
|
921
1091
|
if (activeTasks.has(userId)) {
|
|
922
1092
|
return "您有一个图像处理任务正在进行中,请等待完成";
|
|
923
1093
|
}
|
|
924
|
-
await session.send("
|
|
1094
|
+
await session.send("多张图片+描述");
|
|
925
1095
|
const collectedImages = [];
|
|
926
1096
|
let prompt = "";
|
|
927
1097
|
while (true) {
|
package/lib/providers/index.d.ts
CHANGED
package/lib/providers/types.d.ts
CHANGED