koishi-plugin-aka-ai-generator 0.3.9 → 0.4.1
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 +112 -6
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -587,11 +587,64 @@ async function downloadImageAsBase643(ctx, url, timeout, logger) {
|
|
|
587
587
|
}
|
|
588
588
|
}
|
|
589
589
|
__name(downloadImageAsBase643, "downloadImageAsBase64");
|
|
590
|
-
function parseGeminiResponse(response) {
|
|
590
|
+
function parseGeminiResponse(response, logger) {
|
|
591
591
|
try {
|
|
592
592
|
const images = [];
|
|
593
|
+
if (!response) {
|
|
594
|
+
logger?.error("Gemini API 响应为空");
|
|
595
|
+
return [];
|
|
596
|
+
}
|
|
597
|
+
if (response.error) {
|
|
598
|
+
logger?.error("Gemini API 返回错误", { error: response.error });
|
|
599
|
+
throw new Error(`Gemini API 错误: ${response.error.message || JSON.stringify(response.error)}`);
|
|
600
|
+
}
|
|
601
|
+
if (response.promptFeedback) {
|
|
602
|
+
const blockReason = response.promptFeedback.blockReason;
|
|
603
|
+
const safetyRatings = response.promptFeedback.safetyRatings;
|
|
604
|
+
if (blockReason) {
|
|
605
|
+
logger?.error("Gemini API 请求被阻止", {
|
|
606
|
+
blockReason,
|
|
607
|
+
safetyRatings,
|
|
608
|
+
blockReasonMessage: response.promptFeedback.blockReasonMessage
|
|
609
|
+
});
|
|
610
|
+
let errorMessage = "请求被 Gemini API 阻止";
|
|
611
|
+
switch (blockReason) {
|
|
612
|
+
case "SAFETY":
|
|
613
|
+
errorMessage = "内容被安全策略阻止,可能包含不安全的内容";
|
|
614
|
+
break;
|
|
615
|
+
case "OTHER":
|
|
616
|
+
errorMessage = "请求被阻止(原因:OTHER),可能是内容不符合使用政策或模型无法处理";
|
|
617
|
+
if (response.promptFeedback.blockReasonMessage) {
|
|
618
|
+
errorMessage += `:${response.promptFeedback.blockReasonMessage}`;
|
|
619
|
+
}
|
|
620
|
+
break;
|
|
621
|
+
case "RECITATION":
|
|
622
|
+
errorMessage = "内容包含受版权保护的内容";
|
|
623
|
+
break;
|
|
624
|
+
default:
|
|
625
|
+
errorMessage = `请求被阻止(原因:${blockReason})`;
|
|
626
|
+
}
|
|
627
|
+
if (safetyRatings && Array.isArray(safetyRatings) && safetyRatings.length > 0) {
|
|
628
|
+
const ratings = safetyRatings.map((r) => `${r.category}:${r.probability}`).join(", ");
|
|
629
|
+
errorMessage += ` [安全评分: ${ratings}]`;
|
|
630
|
+
}
|
|
631
|
+
throw new Error(errorMessage);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
593
634
|
if (response.candidates && response.candidates.length > 0) {
|
|
594
635
|
for (const candidate of response.candidates) {
|
|
636
|
+
if (candidate.finishReason && candidate.finishReason !== "STOP") {
|
|
637
|
+
logger?.warn("Gemini 响应 finishReason 异常", {
|
|
638
|
+
finishReason: candidate.finishReason,
|
|
639
|
+
safetyRatings: candidate.safetyRatings
|
|
640
|
+
});
|
|
641
|
+
if (candidate.finishReason === "SAFETY" || candidate.finishReason === "RECITATION") {
|
|
642
|
+
throw new Error(`内容被阻止: ${candidate.finishReason},可能包含不安全的内容`);
|
|
643
|
+
}
|
|
644
|
+
if (candidate.finishReason !== "MAX_TOKENS") {
|
|
645
|
+
logger?.warn("Gemini 响应可能不完整", { finishReason: candidate.finishReason });
|
|
646
|
+
}
|
|
647
|
+
}
|
|
595
648
|
if (candidate.content && candidate.content.parts) {
|
|
596
649
|
for (const part of candidate.content.parts) {
|
|
597
650
|
if (part.inlineData && part.inlineData.data) {
|
|
@@ -599,21 +652,54 @@ function parseGeminiResponse(response) {
|
|
|
599
652
|
const mimeType = part.inlineData.mimeType || "image/jpeg";
|
|
600
653
|
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
601
654
|
images.push(dataUrl);
|
|
655
|
+
logger?.debug("从响应中提取到图片 (inlineData)", { mimeType, dataLength: base64Data.length });
|
|
602
656
|
} else if (part.inline_data && part.inline_data.data) {
|
|
603
657
|
const base64Data = part.inline_data.data;
|
|
604
658
|
const mimeType = part.inline_data.mime_type || "image/jpeg";
|
|
605
659
|
const dataUrl = `data:${mimeType};base64,${base64Data}`;
|
|
606
660
|
images.push(dataUrl);
|
|
661
|
+
logger?.debug("从响应中提取到图片 (inline_data)", { mimeType, dataLength: base64Data.length });
|
|
607
662
|
} else if (part.fileData && part.fileData.fileUri) {
|
|
608
663
|
images.push(part.fileData.fileUri);
|
|
664
|
+
logger?.debug("从响应中提取到图片 (fileData)", { fileUri: part.fileData.fileUri });
|
|
665
|
+
} else if (part.text) {
|
|
666
|
+
logger?.warn("响应中包含文本而非图片", { text: part.text.substring(0, 100) });
|
|
609
667
|
}
|
|
610
668
|
}
|
|
669
|
+
} else {
|
|
670
|
+
logger?.warn("候选响应中没有 content.parts", { candidate: JSON.stringify(candidate).substring(0, 200) });
|
|
611
671
|
}
|
|
612
672
|
}
|
|
673
|
+
} else {
|
|
674
|
+
const hasPromptFeedback = !!response.promptFeedback;
|
|
675
|
+
const responseKeys = Object.keys(response);
|
|
676
|
+
logger?.error("Gemini API 响应中没有 candidates", {
|
|
677
|
+
response: JSON.stringify(response).substring(0, 500),
|
|
678
|
+
hasPromptFeedback,
|
|
679
|
+
responseKeys
|
|
680
|
+
});
|
|
681
|
+
if (!hasPromptFeedback) {
|
|
682
|
+
throw new Error("Gemini API 响应格式异常:既没有生成内容也没有反馈信息");
|
|
683
|
+
}
|
|
684
|
+
if (hasPromptFeedback && !response.promptFeedback.blockReason) {
|
|
685
|
+
logger?.warn("有 promptFeedback 但没有 blockReason,也没有 candidates", {
|
|
686
|
+
promptFeedback: response.promptFeedback
|
|
687
|
+
});
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
if (images.length === 0) {
|
|
691
|
+
logger?.error("未能从 Gemini API 响应中提取到任何图片", {
|
|
692
|
+
hasCandidates: !!response.candidates,
|
|
693
|
+
candidatesCount: response.candidates?.length || 0,
|
|
694
|
+
responseKeys: Object.keys(response),
|
|
695
|
+
firstCandidate: response.candidates?.[0] ? JSON.stringify(response.candidates[0]).substring(0, 300) : null,
|
|
696
|
+
promptFeedback: response.promptFeedback ? JSON.stringify(response.promptFeedback) : null
|
|
697
|
+
});
|
|
613
698
|
}
|
|
614
699
|
return images;
|
|
615
700
|
} catch (error) {
|
|
616
|
-
|
|
701
|
+
logger?.error("解析 Gemini 响应时出错", { error: error.message, stack: error.stack });
|
|
702
|
+
throw error;
|
|
617
703
|
}
|
|
618
704
|
}
|
|
619
705
|
__name(parseGeminiResponse, "parseGeminiResponse");
|
|
@@ -626,12 +712,18 @@ var GeminiProvider = class {
|
|
|
626
712
|
this.config = config;
|
|
627
713
|
}
|
|
628
714
|
async generateImages(prompt, imageUrls, numImages) {
|
|
629
|
-
|
|
715
|
+
let urls = [];
|
|
716
|
+
if (Array.isArray(imageUrls)) {
|
|
717
|
+
urls = imageUrls.filter((url) => url && typeof url === "string" && url.trim());
|
|
718
|
+
} else if (imageUrls && typeof imageUrls === "string" && imageUrls.trim()) {
|
|
719
|
+
urls = [imageUrls];
|
|
720
|
+
}
|
|
630
721
|
const logger = this.config.logger;
|
|
631
722
|
const ctx = this.config.ctx;
|
|
632
|
-
logger.debug("
|
|
723
|
+
logger.debug("开始处理图片输入", { urls, promptLength: prompt.length, isTextToImage: urls.length === 0 });
|
|
633
724
|
const imageParts = [];
|
|
634
725
|
for (const url of urls) {
|
|
726
|
+
if (!url || !url.trim()) continue;
|
|
635
727
|
const { data, mimeType } = await downloadImageAsBase643(
|
|
636
728
|
ctx,
|
|
637
729
|
url,
|
|
@@ -678,14 +770,24 @@ var GeminiProvider = class {
|
|
|
678
770
|
timeout: this.config.apiTimeout * 1e3
|
|
679
771
|
}
|
|
680
772
|
);
|
|
681
|
-
const images = parseGeminiResponse(response);
|
|
773
|
+
const images = parseGeminiResponse(response, logger);
|
|
774
|
+
if (images.length === 0) {
|
|
775
|
+
logger.warn("Gemini API 调用成功但未解析到图片", {
|
|
776
|
+
current: i + 1,
|
|
777
|
+
total: numImages,
|
|
778
|
+
responseHasCandidates: !!response.candidates,
|
|
779
|
+
responseKeys: Object.keys(response)
|
|
780
|
+
});
|
|
781
|
+
} else {
|
|
782
|
+
logger.success("Gemini API 调用成功", { current: i + 1, total: numImages, imagesCount: images.length });
|
|
783
|
+
}
|
|
682
784
|
allImages.push(...images);
|
|
683
|
-
logger.success("Gemini API 调用成功", { current: i + 1, total: numImages });
|
|
684
785
|
} catch (error) {
|
|
685
786
|
logger.error("Gemini API 调用失败", {
|
|
686
787
|
message: error?.message || "未知错误",
|
|
687
788
|
code: error?.code,
|
|
688
789
|
status: error?.response?.status,
|
|
790
|
+
responseData: error?.response?.data ? JSON.stringify(error.response.data).substring(0, 500) : void 0,
|
|
689
791
|
current: i + 1,
|
|
690
792
|
total: numImages
|
|
691
793
|
});
|
|
@@ -696,6 +798,10 @@ var GeminiProvider = class {
|
|
|
696
798
|
throw new Error(`图像处理API调用失败: ${error?.message || "未知错误"}`);
|
|
697
799
|
}
|
|
698
800
|
}
|
|
801
|
+
if (allImages.length === 0) {
|
|
802
|
+
logger.error("所有 Gemini API 调用都未生成图片", { numImages });
|
|
803
|
+
throw new Error("未能从 Gemini API 生成图片,请检查 prompt 和模型配置");
|
|
804
|
+
}
|
|
699
805
|
return allImages;
|
|
700
806
|
}
|
|
701
807
|
};
|