koishi-plugin-aka-ai-generator 0.6.9 → 0.6.11

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.js CHANGED
@@ -295,7 +295,7 @@ var GptGodProvider = class {
295
295
  constructor(config) {
296
296
  this.config = config;
297
297
  }
298
- async generateImages(prompt, imageUrls, numImages) {
298
+ async generateImages(prompt, imageUrls, numImages, onImageGenerated) {
299
299
  const urls = Array.isArray(imageUrls) ? imageUrls : [imageUrls];
300
300
  const logger = this.config.logger;
301
301
  const ctx = this.config.ctx;
@@ -410,7 +410,60 @@ var GptGodProvider = class {
410
410
  throw new Error(`生成失败:${shortError}`);
411
411
  }
412
412
  }
413
- allImages.push(...images);
413
+ logger.debug("开始流式处理图片 (GPTGod)", {
414
+ imagesCount: images.length,
415
+ hasCallback: !!onImageGenerated,
416
+ current: i + 1,
417
+ total: numImages
418
+ });
419
+ for (let imgIdx = 0; imgIdx < images.length; imgIdx++) {
420
+ const imageUrl = images[imgIdx];
421
+ const currentIndex = allImages.length;
422
+ allImages.push(imageUrl);
423
+ logger.debug("准备处理单张图片 (GPTGod)", {
424
+ imgIdx,
425
+ currentIndex,
426
+ total: numImages,
427
+ imageUrlType: typeof imageUrl,
428
+ imageUrlLength: imageUrl?.length || 0,
429
+ imageUrlPrefix: imageUrl?.substring(0, 50) || "null"
430
+ });
431
+ if (onImageGenerated) {
432
+ logger.info("准备调用图片生成回调函数 (GPTGod)", {
433
+ hasCallback: true,
434
+ currentIndex,
435
+ total: numImages,
436
+ imageUrlLength: imageUrl?.length || 0
437
+ });
438
+ try {
439
+ await onImageGenerated(imageUrl, currentIndex, numImages);
440
+ logger.info("图片生成回调函数执行成功 (GPTGod)", {
441
+ currentIndex,
442
+ total: numImages,
443
+ imageUrlLength: imageUrl?.length || 0
444
+ });
445
+ } catch (callbackError) {
446
+ logger.error("图片生成回调函数执行失败 (GPTGod)", {
447
+ error: sanitizeError(callbackError),
448
+ errorMessage: callbackError?.message,
449
+ errorStack: callbackError?.stack,
450
+ currentIndex,
451
+ total: numImages,
452
+ imageUrlLength: imageUrl?.length || 0
453
+ });
454
+ }
455
+ } else {
456
+ logger.warn("图片生成回调函数未提供,跳过流式发送 (GPTGod)", {
457
+ currentIndex,
458
+ total: numImages,
459
+ imageUrlLength: imageUrl?.length || 0
460
+ });
461
+ }
462
+ }
463
+ logger.debug("流式处理图片完成 (GPTGod)", {
464
+ processedCount: images.length,
465
+ allImagesCount: allImages.length
466
+ });
414
467
  logger.success("GPTGod 图像编辑 API 调用成功", { current: i + 1, total: numImages });
415
468
  success = true;
416
469
  break;
@@ -481,6 +534,11 @@ var GptGodProvider = class {
481
534
  function parseGeminiResponse(response, logger) {
482
535
  try {
483
536
  const images = [];
537
+ logger?.debug("开始解析 Gemini API 响应", {
538
+ hasResponse: !!response,
539
+ responseType: typeof response,
540
+ responseKeys: response ? Object.keys(response) : []
541
+ });
484
542
  if (!response) {
485
543
  logger?.error("Gemini API 响应为空");
486
544
  return [];
@@ -526,7 +584,16 @@ function parseGeminiResponse(response, logger) {
526
584
  }
527
585
  }
528
586
  if (response.candidates && response.candidates.length > 0) {
529
- for (const candidate of response.candidates) {
587
+ logger?.debug("找到 candidates", { candidatesCount: response.candidates.length });
588
+ for (let candIdx = 0; candIdx < response.candidates.length; candIdx++) {
589
+ const candidate = response.candidates[candIdx];
590
+ logger?.debug("处理 candidate", {
591
+ index: candIdx,
592
+ hasContent: !!candidate.content,
593
+ hasParts: !!candidate.content?.parts,
594
+ partsCount: candidate.content?.parts?.length || 0,
595
+ finishReason: candidate.finishReason
596
+ });
530
597
  if (candidate.finishReason && candidate.finishReason !== "STOP") {
531
598
  logger?.warn("Gemini 响应 finishReason 异常", {
532
599
  finishReason: candidate.finishReason,
@@ -540,28 +607,67 @@ function parseGeminiResponse(response, logger) {
540
607
  }
541
608
  }
542
609
  if (candidate.content && candidate.content.parts) {
543
- for (const part of candidate.content.parts) {
610
+ logger?.debug("处理 candidate.content.parts", {
611
+ partsCount: candidate.content.parts.length,
612
+ partsKeys: candidate.content.parts.map((p) => Object.keys(p))
613
+ });
614
+ for (let partIdx = 0; partIdx < candidate.content.parts.length; partIdx++) {
615
+ const part = candidate.content.parts[partIdx];
616
+ const partKeys = Object.keys(part);
617
+ logger?.debug("处理 part", {
618
+ partIndex: partIdx,
619
+ partKeys,
620
+ hasInlineData: !!part.inlineData,
621
+ hasInline_data: !!part.inline_data,
622
+ hasFileData: !!part.fileData,
623
+ hasText: !!part.text
624
+ });
544
625
  if (part.inlineData && part.inlineData.data) {
545
626
  const base64Data = part.inlineData.data;
546
627
  const mimeType = part.inlineData.mimeType || "image/jpeg";
547
628
  const dataUrl = `data:${mimeType};base64,${base64Data}`;
548
629
  images.push(dataUrl);
549
- logger?.debug("从响应中提取到图片 (inlineData)", { mimeType, dataLength: base64Data.length });
630
+ logger?.info("从响应中提取到图片 (inlineData)", {
631
+ mimeType,
632
+ dataLength: base64Data.length,
633
+ dataUrlLength: dataUrl.length,
634
+ imageIndex: images.length - 1
635
+ });
550
636
  } else if (part.inline_data && part.inline_data.data) {
551
637
  const base64Data = part.inline_data.data;
552
638
  const mimeType = part.inline_data.mime_type || "image/jpeg";
553
639
  const dataUrl = `data:${mimeType};base64,${base64Data}`;
554
640
  images.push(dataUrl);
555
- logger?.debug("从响应中提取到图片 (inline_data)", { mimeType, dataLength: base64Data.length });
641
+ logger?.info("从响应中提取到图片 (inline_data)", {
642
+ mimeType,
643
+ dataLength: base64Data.length,
644
+ dataUrlLength: dataUrl.length,
645
+ imageIndex: images.length - 1
646
+ });
556
647
  } else if (part.fileData && part.fileData.fileUri) {
557
648
  images.push(part.fileData.fileUri);
558
- logger?.debug("从响应中提取到图片 (fileData)", { fileUri: part.fileData.fileUri });
649
+ logger?.info("从响应中提取到图片 (fileData)", {
650
+ fileUri: part.fileData.fileUri,
651
+ imageIndex: images.length - 1
652
+ });
559
653
  } else if (part.text) {
560
- logger?.warn("响应中包含文本而非图片", { text: part.text.substring(0, 100) });
654
+ logger?.warn("响应中包含文本而非图片", {
655
+ text: part.text.substring(0, 100),
656
+ textLength: part.text.length
657
+ });
658
+ } else {
659
+ logger?.warn("part 中没有找到图片或文本数据", {
660
+ partKeys,
661
+ part: JSON.stringify(part).substring(0, 200)
662
+ });
561
663
  }
562
664
  }
563
665
  } else {
564
- logger?.warn("候选响应中没有 content.parts", { candidate: JSON.stringify(candidate).substring(0, 200) });
666
+ logger?.warn("候选响应中没有 content.parts", {
667
+ candidateIndex: candIdx,
668
+ candidateKeys: Object.keys(candidate),
669
+ candidate: JSON.stringify(candidate).substring(0, 200)
670
+ });
565
671
  }
566
672
  }
567
673
  } else {
@@ -581,13 +687,19 @@ function parseGeminiResponse(response, logger) {
581
687
  });
582
688
  }
583
689
  }
690
+ logger?.debug("parseGeminiResponse 完成", {
691
+ extractedImagesCount: images.length,
692
+ hasCandidates: !!response.candidates,
693
+ candidatesCount: response.candidates?.length || 0
694
+ });
584
695
  if (images.length === 0) {
585
696
  logger?.error("未能从 Gemini API 响应中提取到任何图片", {
586
697
  hasCandidates: !!response.candidates,
587
698
  candidatesCount: response.candidates?.length || 0,
588
699
  responseKeys: Object.keys(response),
589
- firstCandidate: response.candidates?.[0] ? JSON.stringify(response.candidates[0]).substring(0, 300) : null,
590
- promptFeedback: response.promptFeedback ? JSON.stringify(response.promptFeedback) : null
700
+ firstCandidate: response.candidates?.[0] ? JSON.stringify(response.candidates[0]).substring(0, 500) : null,
701
+ promptFeedback: response.promptFeedback ? JSON.stringify(response.promptFeedback) : null,
702
+ fullResponse: JSON.stringify(response).substring(0, 1e3)
591
703
  });
592
704
  }
593
705
  return images;
@@ -609,7 +721,7 @@ var GeminiProvider = class {
609
721
  constructor(config) {
610
722
  this.config = config;
611
723
  }
612
- async generateImages(prompt, imageUrls, numImages) {
724
+ async generateImages(prompt, imageUrls, numImages, onImageGenerated) {
613
725
  let urls = [];
614
726
  if (Array.isArray(imageUrls)) {
615
727
  urls = imageUrls.filter((url) => url && typeof url === "string" && url.trim());
@@ -682,8 +794,61 @@ var GeminiProvider = class {
682
794
  });
683
795
  } else {
684
796
  logger.success("Gemini API 调用成功", { current: i + 1, total: numImages, imagesCount: images.length });
797
+ logger.debug("开始流式处理图片", {
798
+ imagesCount: images.length,
799
+ hasCallback: !!onImageGenerated,
800
+ current: i + 1,
801
+ total: numImages
802
+ });
803
+ for (let imgIdx = 0; imgIdx < images.length; imgIdx++) {
804
+ const imageUrl = images[imgIdx];
805
+ const currentIndex = allImages.length;
806
+ allImages.push(imageUrl);
807
+ logger.debug("准备处理单张图片", {
808
+ imgIdx,
809
+ currentIndex,
810
+ total: numImages,
811
+ imageUrlType: typeof imageUrl,
812
+ imageUrlLength: imageUrl?.length || 0,
813
+ imageUrlPrefix: imageUrl?.substring(0, 50) || "null"
814
+ });
815
+ if (onImageGenerated) {
816
+ logger.info("准备调用图片生成回调函数", {
817
+ hasCallback: true,
818
+ currentIndex,
819
+ total: numImages,
820
+ imageUrlLength: imageUrl?.length || 0
821
+ });
822
+ try {
823
+ await onImageGenerated(imageUrl, currentIndex, numImages);
824
+ logger.info("图片生成回调函数执行成功", {
825
+ currentIndex,
826
+ total: numImages,
827
+ imageUrlLength: imageUrl?.length || 0
828
+ });
829
+ } catch (callbackError) {
830
+ logger.error("图片生成回调函数执行失败", {
831
+ error: sanitizeError(callbackError),
832
+ errorMessage: callbackError?.message,
833
+ errorStack: callbackError?.stack,
834
+ currentIndex,
835
+ total: numImages,
836
+ imageUrlLength: imageUrl?.length || 0
837
+ });
838
+ }
839
+ } else {
840
+ logger.warn("图片生成回调函数未提供,跳过流式发送", {
841
+ currentIndex,
842
+ total: numImages,
843
+ imageUrlLength: imageUrl?.length || 0
844
+ });
845
+ }
846
+ }
847
+ logger.debug("流式处理图片完成", {
848
+ processedCount: images.length,
849
+ allImagesCount: allImages.length
850
+ });
685
851
  }
686
- allImages.push(...images);
687
852
  } catch (error) {
688
853
  const sanitizedError = sanitizeError(error);
689
854
  const safeMessage = typeof error?.message === "string" ? sanitizeString(error.message) : "未知错误";
@@ -1505,18 +1670,33 @@ function apply(ctx, config) {
1505
1670
  return { images: collectedImages, text: collectedText };
1506
1671
  }
1507
1672
  __name(getInputData, "getInputData");
1508
- async function requestProviderImages(prompt, imageUrls, numImages, requestContext) {
1673
+ async function requestProviderImages(prompt, imageUrls, numImages, requestContext, onImageGenerated) {
1509
1674
  const providerType = requestContext?.provider || config.provider;
1510
1675
  const targetModelId = requestContext?.modelId;
1511
1676
  const providerInstance = getProviderInstance(providerType, targetModelId);
1512
- if (config.logLevel === "debug") {
1513
- logger.debug("准备调用图像供应商", {
1677
+ logger.info("requestProviderImages 调用", {
1678
+ providerType,
1679
+ modelId: targetModelId || "default",
1680
+ numImages,
1681
+ hasCallback: !!onImageGenerated,
1682
+ promptLength: prompt.length,
1683
+ imageUrlsCount: Array.isArray(imageUrls) ? imageUrls.length : imageUrls ? 1 : 0
1684
+ });
1685
+ try {
1686
+ const result = await providerInstance.generateImages(prompt, imageUrls, numImages, onImageGenerated);
1687
+ logger.info("requestProviderImages 完成", {
1514
1688
  providerType,
1515
- modelId: targetModelId || "default",
1516
- numImages
1689
+ resultCount: result.length
1517
1690
  });
1691
+ return result;
1692
+ } catch (error) {
1693
+ logger.error("requestProviderImages 失败", {
1694
+ providerType,
1695
+ error: sanitizeError(error),
1696
+ errorMessage: error?.message
1697
+ });
1698
+ throw error;
1518
1699
  }
1519
- return await providerInstance.generateImages(prompt, imageUrls, numImages);
1520
1700
  }
1521
1701
  __name(requestProviderImages, "requestProviderImages");
1522
1702
  async function processImageWithTimeout(session, img, prompt, styleName, requestContext, displayInfo, mode = "single") {
@@ -1612,21 +1792,90 @@ ${infoParts.join("\n")}`;
1612
1792
  }
1613
1793
  statusMessage += "...";
1614
1794
  await session.send(statusMessage);
1615
- const images = await requestProviderImages(finalPrompt, imageUrls, imageCount, requestContext);
1795
+ const generatedImages = [];
1796
+ let creditDeducted = false;
1797
+ const onImageGenerated = /* @__PURE__ */ __name(async (imageUrl, index, total) => {
1798
+ logger.info("流式回调被调用", {
1799
+ userId,
1800
+ index,
1801
+ total,
1802
+ imageUrlType: typeof imageUrl,
1803
+ imageUrlLength: imageUrl?.length || 0,
1804
+ imageUrlPrefix: imageUrl?.substring(0, 50) || "null",
1805
+ hasImageUrl: !!imageUrl
1806
+ });
1807
+ if (checkTimeout && checkTimeout()) {
1808
+ logger.error("流式回调:检测到超时", { userId, index, total });
1809
+ throw new Error("命令执行超时");
1810
+ }
1811
+ generatedImages.push(imageUrl);
1812
+ logger.debug("图片已添加到 generatedImages", {
1813
+ userId,
1814
+ currentCount: generatedImages.length,
1815
+ index,
1816
+ total
1817
+ });
1818
+ if (!creditDeducted && generatedImages.length > 0) {
1819
+ creditDeducted = true;
1820
+ logger.info("准备扣除积分", { userId, totalImages: total, currentIndex: index });
1821
+ try {
1822
+ await recordUserUsage(session, styleName, total);
1823
+ logger.info("流式处理:第一张图片生成,积分已扣除", {
1824
+ userId,
1825
+ totalImages: total,
1826
+ currentIndex: index
1827
+ });
1828
+ } catch (creditError) {
1829
+ logger.error("扣除积分失败", {
1830
+ userId,
1831
+ error: sanitizeError(creditError),
1832
+ totalImages: total
1833
+ });
1834
+ throw creditError;
1835
+ }
1836
+ }
1837
+ logger.info("准备发送图片", { userId, index: index + 1, total, imageUrlLength: imageUrl?.length || 0 });
1838
+ try {
1839
+ await session.send(import_koishi2.h.image(imageUrl));
1840
+ logger.info("流式处理:图片已发送", { index: index + 1, total, userId });
1841
+ } catch (sendError) {
1842
+ logger.error("发送图片失败", {
1843
+ userId,
1844
+ error: sanitizeError(sendError),
1845
+ errorMessage: sendError?.message,
1846
+ index: index + 1,
1847
+ total
1848
+ });
1849
+ throw sendError;
1850
+ }
1851
+ if (total > 1 && index < total - 1) {
1852
+ logger.debug("多张图片,添加延时", { index, total });
1853
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
1854
+ }
1855
+ }, "onImageGenerated");
1856
+ logger.info("准备调用 requestProviderImages,已设置回调函数", {
1857
+ userId,
1858
+ hasCallback: !!onImageGenerated,
1859
+ imageCount,
1860
+ promptLength: finalPrompt.length,
1861
+ imageUrlsCount: Array.isArray(imageUrls) ? imageUrls.length : imageUrls ? 1 : 0
1862
+ });
1863
+ const images = await requestProviderImages(finalPrompt, imageUrls, imageCount, requestContext, onImageGenerated);
1864
+ logger.info("requestProviderImages 返回", {
1865
+ userId,
1866
+ imagesCount: images.length,
1867
+ generatedImagesCount: generatedImages.length,
1868
+ creditDeducted
1869
+ });
1616
1870
  if (checkTimeout && checkTimeout()) throw new Error("命令执行超时");
1617
1871
  if (images.length === 0) {
1618
1872
  return "图像处理失败:未能生成图片";
1619
1873
  }
1620
- await recordUserUsage(session, styleName, images.length);
1621
- if (checkTimeout && checkTimeout()) throw new Error("命令执行超时");
1622
- await session.send("图像处理完成!");
1623
- for (let i = 0; i < images.length; i++) {
1624
- if (checkTimeout && checkTimeout()) break;
1625
- await session.send(import_koishi2.h.image(images[i]));
1626
- if (images.length > 1 && i < images.length - 1) {
1627
- await new Promise((resolve) => setTimeout(resolve, 1e3));
1628
- }
1874
+ if (!creditDeducted) {
1875
+ await recordUserUsage(session, styleName, images.length);
1876
+ logger.warn("流式处理:积分在最后扣除(异常情况)", { userId, imagesCount: images.length });
1629
1877
  }
1878
+ await session.send("图像处理完成!");
1630
1879
  } finally {
1631
1880
  userManager.endTask(userId);
1632
1881
  }
@@ -1648,7 +1897,8 @@ ${infoParts.join("\n")}`;
1648
1897
  }
1649
1898
  const userPromptText = userPromptParts.join(" - ");
1650
1899
  const numImages = options?.num || config.defaultNumImages;
1651
- const limitCheck = await userManager.checkDailyLimit(session.userId, config, numImages);
1900
+ const userName = session.username || session.userId || "未知用户";
1901
+ const limitCheck = await userManager.checkAndReserveQuota(session.userId, userName, numImages, config);
1652
1902
  if (!limitCheck.allowed) {
1653
1903
  return limitCheck.message;
1654
1904
  }
@@ -1684,7 +1934,8 @@ ${infoParts.join("\n")}`;
1684
1934
  ctx.command(`${COMMANDS.TXT_TO_IMG} [prompt:text]`, "根据文字描述生成图像").option("num", "-n <num:number> 生成图片数量 (1-4)").action(async ({ session, options }, prompt) => {
1685
1935
  if (!session?.userId) return "会话无效";
1686
1936
  const numImages = options?.num || config.defaultNumImages;
1687
- const limitCheck = await userManager.checkDailyLimit(session.userId, config, numImages);
1937
+ const userName = session.username || session.userId || "未知用户";
1938
+ const limitCheck = await userManager.checkAndReserveQuota(session.userId, userName, numImages, config);
1688
1939
  if (!limitCheck.allowed) {
1689
1940
  return limitCheck.message;
1690
1941
  }
@@ -1697,7 +1948,8 @@ ${infoParts.join("\n")}`;
1697
1948
  if (!session?.userId) return "会话无效";
1698
1949
  const numImages = options?.num || config.defaultNumImages;
1699
1950
  const mode = options?.multiple ? "multiple" : "single";
1700
- const limitCheck = await userManager.checkDailyLimit(session.userId, config, numImages);
1951
+ const userName = session.username || session.userId || "未知用户";
1952
+ const limitCheck = await userManager.checkAndReserveQuota(session.userId, userName, numImages, config);
1701
1953
  if (!limitCheck.allowed) {
1702
1954
  return limitCheck.message;
1703
1955
  }
@@ -1759,7 +2011,8 @@ ${infoParts.join("\n")}`;
1759
2011
  if (imageCount < 1 || imageCount > 4) {
1760
2012
  return "生成数量必须在 1-4 之间";
1761
2013
  }
1762
- const limitCheck = await userManager.checkDailyLimit(userId, config, imageCount);
2014
+ const userName = session.username || userId || "未知用户";
2015
+ const limitCheck = await userManager.checkAndReserveQuota(userId, userName, imageCount, config);
1763
2016
  if (!limitCheck.allowed) {
1764
2017
  return limitCheck.message;
1765
2018
  }
@@ -1773,21 +2026,90 @@ ${infoParts.join("\n")}`;
1773
2026
  });
1774
2027
  await session.send(`开始合成图(${collectedImages.length}张)...
1775
2028
  Prompt: ${prompt}`);
1776
- const resultImages = await requestProviderImages(prompt, collectedImages, imageCount);
2029
+ const generatedImages = [];
2030
+ let creditDeducted = false;
2031
+ const onImageGenerated = /* @__PURE__ */ __name(async (imageUrl, index, total) => {
2032
+ logger.info("流式回调被调用 (COMPOSE_IMAGE)", {
2033
+ userId,
2034
+ index,
2035
+ total,
2036
+ imageUrlType: typeof imageUrl,
2037
+ imageUrlLength: imageUrl?.length || 0,
2038
+ imageUrlPrefix: imageUrl?.substring(0, 50) || "null",
2039
+ hasImageUrl: !!imageUrl
2040
+ });
2041
+ if (isTimeout) {
2042
+ logger.error("流式回调:检测到超时 (COMPOSE_IMAGE)", { userId, index, total });
2043
+ throw new Error("命令执行超时");
2044
+ }
2045
+ generatedImages.push(imageUrl);
2046
+ logger.debug("图片已添加到 generatedImages (COMPOSE_IMAGE)", {
2047
+ userId,
2048
+ currentCount: generatedImages.length,
2049
+ index,
2050
+ total
2051
+ });
2052
+ if (!creditDeducted && generatedImages.length > 0) {
2053
+ creditDeducted = true;
2054
+ logger.info("准备扣除积分 (COMPOSE_IMAGE)", { userId, totalImages: total, currentIndex: index });
2055
+ try {
2056
+ await recordUserUsage(session, COMMANDS.COMPOSE_IMAGE, total);
2057
+ logger.info("流式处理:第一张图片生成,积分已扣除 (COMPOSE_IMAGE)", {
2058
+ userId,
2059
+ totalImages: total,
2060
+ currentIndex: index
2061
+ });
2062
+ } catch (creditError) {
2063
+ logger.error("扣除积分失败 (COMPOSE_IMAGE)", {
2064
+ userId,
2065
+ error: sanitizeError(creditError),
2066
+ totalImages: total
2067
+ });
2068
+ throw creditError;
2069
+ }
2070
+ }
2071
+ logger.info("准备发送图片 (COMPOSE_IMAGE)", { userId, index: index + 1, total, imageUrlLength: imageUrl?.length || 0 });
2072
+ try {
2073
+ await session.send(import_koishi2.h.image(imageUrl));
2074
+ logger.info("流式处理:图片已发送 (COMPOSE_IMAGE)", { index: index + 1, total, userId });
2075
+ } catch (sendError) {
2076
+ logger.error("发送图片失败 (COMPOSE_IMAGE)", {
2077
+ userId,
2078
+ error: sanitizeError(sendError),
2079
+ errorMessage: sendError?.message,
2080
+ index: index + 1,
2081
+ total
2082
+ });
2083
+ throw sendError;
2084
+ }
2085
+ if (total > 1 && index < total - 1) {
2086
+ logger.debug("多张图片,添加延时 (COMPOSE_IMAGE)", { index, total });
2087
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
2088
+ }
2089
+ }, "onImageGenerated");
2090
+ logger.info("准备调用 requestProviderImages (COMPOSE_IMAGE),已设置回调函数", {
2091
+ userId,
2092
+ hasCallback: !!onImageGenerated,
2093
+ imageCount,
2094
+ promptLength: prompt.length,
2095
+ collectedImagesCount: collectedImages.length
2096
+ });
2097
+ const resultImages = await requestProviderImages(prompt, collectedImages, imageCount, void 0, onImageGenerated);
2098
+ logger.info("requestProviderImages 返回 (COMPOSE_IMAGE)", {
2099
+ userId,
2100
+ imagesCount: resultImages.length,
2101
+ generatedImagesCount: generatedImages.length,
2102
+ creditDeducted
2103
+ });
1777
2104
  if (isTimeout) throw new Error("命令执行超时");
1778
2105
  if (resultImages.length === 0) {
1779
2106
  return "图片合成失败:未能生成图片";
1780
2107
  }
1781
- await recordUserUsage(session, COMMANDS.COMPOSE_IMAGE, resultImages.length);
1782
- if (isTimeout) throw new Error("命令执行超时");
1783
- await session.send("图片合成完成!");
1784
- for (let i = 0; i < resultImages.length; i++) {
1785
- if (isTimeout) break;
1786
- await session.send(import_koishi2.h.image(resultImages[i]));
1787
- if (resultImages.length > 1 && i < resultImages.length - 1) {
1788
- await new Promise((resolve) => setTimeout(resolve, 1e3));
1789
- }
2108
+ if (!creditDeducted) {
2109
+ await recordUserUsage(session, COMMANDS.COMPOSE_IMAGE, resultImages.length);
2110
+ logger.warn("流式处理:积分在最后扣除(异常情况)", { userId, imagesCount: resultImages.length });
1790
2111
  }
2112
+ await session.send("图片合成完成!");
1791
2113
  } finally {
1792
2114
  userManager.endTask(userId);
1793
2115
  }
@@ -7,5 +7,5 @@ export interface GeminiConfig extends ProviderConfig {
7
7
  export declare class GeminiProvider implements ImageProvider {
8
8
  private config;
9
9
  constructor(config: GeminiConfig);
10
- generateImages(prompt: string, imageUrls: string | string[], numImages: number): Promise<string[]>;
10
+ generateImages(prompt: string, imageUrls: string | string[], numImages: number, onImageGenerated?: (imageUrl: string, index: number, total: number) => void | Promise<void>): Promise<string[]>;
11
11
  }
@@ -6,5 +6,5 @@ export interface GptGodConfig extends ProviderConfig {
6
6
  export declare class GptGodProvider implements ImageProvider {
7
7
  private config;
8
8
  constructor(config: GptGodConfig);
9
- generateImages(prompt: string, imageUrls: string | string[], numImages: number): Promise<string[]>;
9
+ generateImages(prompt: string, imageUrls: string | string[], numImages: number, onImageGenerated?: (imageUrl: string, index: number, total: number) => void | Promise<void>): Promise<string[]>;
10
10
  }
@@ -4,9 +4,10 @@ export interface ImageProvider {
4
4
  * @param prompt 提示词
5
5
  * @param imageUrls 输入图片 URL 数组
6
6
  * @param numImages 需要生成的图片数量
7
+ * @param onImageGenerated 可选的回调函数,每生成一张图片时调用(用于流式处理)
7
8
  * @returns 生成的图片 URL 数组(data: URL 或 http URL)
8
9
  */
9
- generateImages(prompt: string, imageUrls: string | string[], numImages: number): Promise<string[]>;
10
+ generateImages(prompt: string, imageUrls: string | string[], numImages: number, onImageGenerated?: (imageUrl: string, index: number, total: number) => void | Promise<void>): Promise<string[]>;
10
11
  }
11
12
  export interface ProviderConfig {
12
13
  apiTimeout: number;
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.6.9",
4
+ "version": "0.6.11",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [