koishi-plugin-aka-ai-generator 0.2.5 → 0.2.6

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.
Files changed (2) hide show
  1. package/lib/index.js +118 -83
  2. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -432,100 +432,135 @@ var GptGodProvider = class {
432
432
  }
433
433
  ]
434
434
  };
435
- try {
436
- const response = await ctx.http.post(
437
- GPTGOD_DEFAULT_API_URL,
438
- requestData,
439
- {
440
- headers: {
441
- "Content-Type": "application/json",
442
- "Authorization": `Bearer ${this.config.apiKey}`
443
- },
444
- timeout: this.config.apiTimeout * 1e3
435
+ const maxRetries = 3;
436
+ let lastError = null;
437
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
438
+ try {
439
+ const requestBodySize = JSON.stringify(requestData).length;
440
+ if (this.config.logLevel === "debug") {
441
+ logger.debug(`GPTGod API 请求 (尝试 ${attempt}/${maxRetries})`, {
442
+ requestBodySize: `${(requestBodySize / 1024).toFixed(2)} KB`,
443
+ imageCount: urls.length
444
+ });
445
445
  }
446
- );
447
- logger.success("GPTGod 图像编辑 API 调用成功");
448
- if (response?.choices?.length > 0) {
449
- const firstChoice = response.choices[0];
450
- const messageContent = firstChoice.message?.content;
451
- let errorMessage = "";
452
- if (typeof messageContent === "string") {
453
- errorMessage = messageContent;
454
- } else if (Array.isArray(messageContent)) {
455
- const textParts = messageContent.filter((part) => part?.type === "text" && part?.text).map((part) => part.text).join(" ");
456
- errorMessage = textParts;
457
- } else if (messageContent?.text) {
458
- errorMessage = messageContent.text;
446
+ const response = await ctx.http.post(
447
+ GPTGOD_DEFAULT_API_URL,
448
+ requestData,
449
+ {
450
+ headers: {
451
+ "Content-Type": "application/json",
452
+ "Authorization": `Bearer ${this.config.apiKey}`
453
+ },
454
+ timeout: this.config.apiTimeout * 1e3
455
+ }
456
+ );
457
+ logger.success("GPTGod 图像编辑 API 调用成功");
458
+ if (response?.choices?.length > 0) {
459
+ const firstChoice = response.choices[0];
460
+ const messageContent = firstChoice.message?.content;
461
+ let errorMessage = "";
462
+ if (typeof messageContent === "string") {
463
+ errorMessage = messageContent;
464
+ } else if (Array.isArray(messageContent)) {
465
+ const textParts = messageContent.filter((part) => part?.type === "text" && part?.text).map((part) => part.text).join(" ");
466
+ errorMessage = textParts;
467
+ } else if (messageContent?.text) {
468
+ errorMessage = messageContent.text;
469
+ }
470
+ 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"))) {
471
+ logger.error("内容被 Google Gemini 政策拦截", {
472
+ errorMessage: errorMessage.substring(0, 200),
473
+ finishReason: firstChoice.finish_reason
474
+ });
475
+ throw new Error("内容被安全策略拦截");
476
+ }
477
+ if (errorMessage && (errorMessage.toLowerCase().includes("error") || errorMessage.toLowerCase().includes("failed") || errorMessage.toLowerCase().includes("blocked")) && !errorMessage.match(/https?:\/\//)) {
478
+ logger.error("API 返回错误消息", {
479
+ errorMessage: errorMessage.substring(0, 200),
480
+ finishReason: firstChoice.finish_reason
481
+ });
482
+ const shortError = errorMessage.length > 50 ? errorMessage.substring(0, 50) + "..." : errorMessage;
483
+ throw new Error(`处理失败:${shortError}`);
484
+ }
459
485
  }
460
- 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"))) {
461
- logger.error("内容被 Google Gemini 政策拦截", {
462
- errorMessage: errorMessage.substring(0, 200),
463
- finishReason: firstChoice.finish_reason
486
+ if (this.config.logLevel === "debug") {
487
+ logger.debug("GPTGod API 响应结构", {
488
+ hasChoices: !!response?.choices,
489
+ choicesLength: response?.choices?.length,
490
+ hasImages: !!response?.images,
491
+ hasImage: !!response?.image,
492
+ responseKeys: Object.keys(response || {}),
493
+ 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"
464
494
  });
465
- throw new Error("内容被安全策略拦截");
466
495
  }
467
- if (errorMessage && (errorMessage.toLowerCase().includes("error") || errorMessage.toLowerCase().includes("failed") || errorMessage.toLowerCase().includes("blocked")) && !errorMessage.match(/https?:\/\//)) {
468
- logger.error("API 返回错误消息", {
469
- errorMessage: errorMessage.substring(0, 200),
470
- finishReason: firstChoice.finish_reason
496
+ const images = parseGptGodResponse(response, this.config.logLevel === "debug" ? logger : null);
497
+ if (images.length < numImages) {
498
+ const warnData = {
499
+ requested: numImages,
500
+ received: images.length
501
+ };
502
+ if (this.config.logLevel === "debug") {
503
+ warnData.responsePreview = JSON.stringify(response).substring(0, 500);
504
+ }
505
+ logger.warn("生成的图片数量不足", warnData);
506
+ if (images.length === 0 && response?.choices?.[0]?.message?.content) {
507
+ const content = response.choices[0].message.content;
508
+ const contentText = typeof content === "string" ? content : Array.isArray(content) ? content.map((p) => p?.text || "").join(" ") : "";
509
+ if (contentText && !contentText.match(/https?:\/\//)) {
510
+ const shortError = contentText.length > 50 ? contentText.substring(0, 50) + "..." : contentText;
511
+ throw new Error(`生成失败:${shortError}`);
512
+ }
513
+ }
514
+ }
515
+ return images;
516
+ } catch (error) {
517
+ lastError = error;
518
+ if (error?.message && (error.message.includes("内容被安全策略拦截") || error.message.includes("生成失败") || error.message.includes("处理失败"))) {
519
+ throw error;
520
+ }
521
+ const isRetryableError = error?.cause?.code === "UND_ERR_SOCKET" || // Socket 错误
522
+ error?.code === "UND_ERR_SOCKET" || error?.message?.includes("other side closed") || error?.message?.includes("fetch failed") || error?.message?.includes("ECONNRESET") || error?.message?.includes("ETIMEDOUT") || error?.response?.status >= 500 && error?.response?.status < 600;
523
+ if (isRetryableError && attempt < maxRetries) {
524
+ const delay = Math.min(1e3 * Math.pow(2, attempt - 1), 5e3);
525
+ logger.warn(`GPTGod API 调用失败,将在 ${delay}ms 后重试 (${attempt}/${maxRetries})`, {
526
+ error: error?.message || error?.cause?.message || "连接错误",
527
+ code: error?.code || error?.cause?.code
471
528
  });
472
- const shortError = errorMessage.length > 50 ? errorMessage.substring(0, 50) + "..." : errorMessage;
473
- throw new Error(`处理失败:${shortError}`);
529
+ await new Promise((resolve) => setTimeout(resolve, delay));
530
+ continue;
474
531
  }
475
- }
476
- if (this.config.logLevel === "debug") {
477
- logger.debug("GPTGod API 响应结构", {
478
- hasChoices: !!response?.choices,
479
- choicesLength: response?.choices?.length,
480
- hasImages: !!response?.images,
481
- hasImage: !!response?.image,
482
- responseKeys: Object.keys(response || {}),
483
- 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"
532
+ logger.error("GPTGod 图像编辑 API 调用失败", {
533
+ message: error?.message || "未知错误",
534
+ name: error?.name,
535
+ code: error?.code,
536
+ status: error?.response?.status,
537
+ statusText: error?.response?.statusText,
538
+ data: error?.response?.data,
539
+ stack: error?.stack,
540
+ cause: error?.cause,
541
+ attempt,
542
+ maxRetries,
543
+ // 如果是 axios 错误,通常会有 config 和 request 信息
544
+ url: error?.config?.url,
545
+ method: error?.config?.method,
546
+ headers: error?.config?.headers
484
547
  });
485
- }
486
- const images = parseGptGodResponse(response, this.config.logLevel === "debug" ? logger : null);
487
- if (images.length < numImages) {
488
- const warnData = {
489
- requested: numImages,
490
- received: images.length
491
- };
492
- if (this.config.logLevel === "debug") {
493
- warnData.responsePreview = JSON.stringify(response).substring(0, 500);
548
+ if (error?.cause?.code === "UND_ERR_SOCKET" || error?.message?.includes("other side closed")) {
549
+ throw new Error("图像处理失败:服务器连接中断,可能是服务器负载过高或网络不稳定,请稍后重试");
494
550
  }
495
- logger.warn("生成的图片数量不足", warnData);
496
- if (images.length === 0 && response?.choices?.[0]?.message?.content) {
497
- const content = response.choices[0].message.content;
498
- const contentText = typeof content === "string" ? content : Array.isArray(content) ? content.map((p) => p?.text || "").join(" ") : "";
499
- if (contentText && !contentText.match(/https?:\/\//)) {
500
- const shortError = contentText.length > 50 ? contentText.substring(0, 50) + "..." : contentText;
501
- throw new Error(`生成失败:${shortError}`);
502
- }
551
+ if (error?.message?.includes("fetch") && error?.message?.includes(GPTGOD_DEFAULT_API_URL)) {
552
+ throw new Error("图像处理失败:无法连接 GPTGod API 服务器,请检查网络连接或稍后重试");
503
553
  }
554
+ if (error?.response?.status === 413) {
555
+ throw new Error("图像处理失败:请求体过大,请尝试使用较小的图片");
556
+ }
557
+ if (error?.response?.status === 429) {
558
+ throw new Error("图像处理失败:请求过于频繁,请稍后重试");
559
+ }
560
+ throw new Error("图像处理API调用失败");
504
561
  }
505
- return images;
506
- } catch (error) {
507
- if (error?.message && (error.message.includes("内容被安全策略拦截") || error.message.includes("生成失败") || error.message.includes("处理失败"))) {
508
- throw error;
509
- }
510
- logger.error("GPTGod 图像编辑 API 调用失败", {
511
- message: error?.message || "未知错误",
512
- name: error?.name,
513
- code: error?.code,
514
- status: error?.response?.status,
515
- statusText: error?.response?.statusText,
516
- data: error?.response?.data,
517
- stack: error?.stack,
518
- cause: error?.cause,
519
- // 如果是 axios 错误,通常会有 config 和 request 信息
520
- url: error?.config?.url,
521
- method: error?.config?.method,
522
- headers: error?.config?.headers
523
- });
524
- if (error?.message?.includes("fetch") && error?.message?.includes(GPTGOD_DEFAULT_API_URL)) {
525
- throw new Error("图像处理失败:无法连接 GPTGod API 服务器,请稍后重试");
526
- }
527
- throw new Error("图像处理API调用失败");
528
562
  }
563
+ throw lastError;
529
564
  }
530
565
  };
531
566
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-aka-ai-generator",
3
3
  "description": "自用AI生成插件(GPTGod & Yunwu)",
4
- "version": "0.2.5",
4
+ "version": "0.2.6",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [