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 +365 -43
- package/lib/providers/gemini.d.ts +1 -1
- package/lib/providers/gptgod.d.ts +1 -1
- package/lib/providers/types.d.ts +2 -1
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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?.
|
|
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?.
|
|
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?.
|
|
649
|
+
logger?.info("从响应中提取到图片 (fileData)", {
|
|
650
|
+
fileUri: part.fileData.fileUri,
|
|
651
|
+
imageIndex: images.length - 1
|
|
652
|
+
});
|
|
559
653
|
} else if (part.text) {
|
|
560
|
-
logger?.warn("响应中包含文本而非图片", {
|
|
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", {
|
|
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,
|
|
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
|
-
|
|
1513
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
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
|
}
|
package/lib/providers/types.d.ts
CHANGED
|
@@ -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;
|