koishi-plugin-aka-ai-generator 0.1.8 → 0.2.0
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 +8 -0
- package/lib/index.js +122 -21
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { Context, Schema } from 'koishi';
|
|
2
2
|
export declare const name = "aka-ai-generator";
|
|
3
3
|
export type ImageProvider = 'yunwu' | 'gptgod';
|
|
4
|
+
export interface ModelMappingConfig {
|
|
5
|
+
suffix: string;
|
|
6
|
+
modelId: string;
|
|
7
|
+
provider?: ImageProvider;
|
|
8
|
+
promptSuffix?: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
}
|
|
4
11
|
export interface StyleConfig {
|
|
5
12
|
commandName: string;
|
|
6
13
|
prompt: string;
|
|
@@ -27,6 +34,7 @@ export interface Config {
|
|
|
27
34
|
yunwuModelId: string;
|
|
28
35
|
gptgodApiKey: string;
|
|
29
36
|
gptgodModelId: string;
|
|
37
|
+
modelMappings?: ModelMappingConfig[];
|
|
30
38
|
apiTimeout: number;
|
|
31
39
|
commandTimeout: number;
|
|
32
40
|
defaultNumImages: number;
|
package/lib/index.js
CHANGED
|
@@ -513,6 +513,9 @@ var GptGodProvider = class {
|
|
|
513
513
|
status: error?.response?.status,
|
|
514
514
|
data: error?.response?.data
|
|
515
515
|
});
|
|
516
|
+
if (error?.message?.includes("fetch") && error?.message?.includes(GPTGOD_DEFAULT_API_URL)) {
|
|
517
|
+
throw new Error("图像处理失败:无法连接 GPTGod API 服务器,请稍后重试");
|
|
518
|
+
}
|
|
516
519
|
throw new Error("图像处理API调用失败");
|
|
517
520
|
}
|
|
518
521
|
}
|
|
@@ -568,6 +571,16 @@ var Config = import_koishi.Schema.intersect([
|
|
|
568
571
|
yunwuModelId: import_koishi.Schema.string().default("gemini-2.5-flash-image").description("云雾图像生成模型ID"),
|
|
569
572
|
gptgodApiKey: import_koishi.Schema.string().description("GPTGod API 密钥").role("secret").default(""),
|
|
570
573
|
gptgodModelId: import_koishi.Schema.string().default("nano-banana").description("GPTGod 模型ID"),
|
|
574
|
+
modelMappings: import_koishi.Schema.array(import_koishi.Schema.object({
|
|
575
|
+
suffix: import_koishi.Schema.string().required().description("指令后缀(例如 4K,对应输入 -4K)"),
|
|
576
|
+
provider: import_koishi.Schema.union([
|
|
577
|
+
import_koishi.Schema.const("yunwu").description("云雾 Gemini 服务"),
|
|
578
|
+
import_koishi.Schema.const("gptgod").description("GPTGod 服务")
|
|
579
|
+
]).description("可选:覆盖供应商"),
|
|
580
|
+
modelId: import_koishi.Schema.string().required().description("触发该后缀时使用的模型 ID"),
|
|
581
|
+
promptSuffix: import_koishi.Schema.string().role("textarea", { rows: 2 }).description("附加在风格 prompt 末尾的提示语"),
|
|
582
|
+
description: import_koishi.Schema.string().description("该后缀的说明,方便记忆")
|
|
583
|
+
})).role("table").default([]).description("根据 -后缀切换模型/供应商与附加 prompt"),
|
|
571
584
|
apiTimeout: import_koishi.Schema.number().default(120).description("API请求超时时间(秒)"),
|
|
572
585
|
commandTimeout: import_koishi.Schema.number().default(180).description("命令执行总超时时间(秒)"),
|
|
573
586
|
// 默认设置
|
|
@@ -622,17 +635,67 @@ function apply(ctx, config) {
|
|
|
622
635
|
const logger = ctx.logger("aka-ai-generator");
|
|
623
636
|
const activeTasks = /* @__PURE__ */ new Map();
|
|
624
637
|
const rateLimitMap = /* @__PURE__ */ new Map();
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
638
|
+
const providerCache = /* @__PURE__ */ new Map();
|
|
639
|
+
function getProviderInstance(providerType, modelId) {
|
|
640
|
+
const cacheKey = `${providerType}:${modelId || "default"}`;
|
|
641
|
+
if (!providerCache.has(cacheKey)) {
|
|
642
|
+
providerCache.set(cacheKey, createImageProvider({
|
|
643
|
+
provider: providerType,
|
|
644
|
+
yunwuApiKey: config.yunwuApiKey,
|
|
645
|
+
yunwuModelId: providerType === "yunwu" ? modelId || config.yunwuModelId : config.yunwuModelId,
|
|
646
|
+
gptgodApiKey: config.gptgodApiKey,
|
|
647
|
+
gptgodModelId: providerType === "gptgod" ? modelId || config.gptgodModelId : config.gptgodModelId,
|
|
648
|
+
apiTimeout: config.apiTimeout,
|
|
649
|
+
logLevel: config.logLevel,
|
|
650
|
+
logger,
|
|
651
|
+
ctx
|
|
652
|
+
}));
|
|
653
|
+
}
|
|
654
|
+
return providerCache.get(cacheKey);
|
|
655
|
+
}
|
|
656
|
+
__name(getProviderInstance, "getProviderInstance");
|
|
657
|
+
getProviderInstance(config.provider);
|
|
658
|
+
const modelMappingIndex = buildModelMappingIndex(config.modelMappings);
|
|
659
|
+
function normalizeSuffix(value) {
|
|
660
|
+
return value?.replace(/^\-+/, "").trim().toLowerCase();
|
|
661
|
+
}
|
|
662
|
+
__name(normalizeSuffix, "normalizeSuffix");
|
|
663
|
+
function buildModelMappingIndex(mappings) {
|
|
664
|
+
const map = /* @__PURE__ */ new Map();
|
|
665
|
+
if (!Array.isArray(mappings)) return map;
|
|
666
|
+
for (const mapping of mappings) {
|
|
667
|
+
const key = normalizeSuffix(mapping?.suffix);
|
|
668
|
+
if (!key || !mapping?.modelId) continue;
|
|
669
|
+
map.set(key, mapping);
|
|
670
|
+
}
|
|
671
|
+
return map;
|
|
672
|
+
}
|
|
673
|
+
__name(buildModelMappingIndex, "buildModelMappingIndex");
|
|
674
|
+
function parseStyleCommandModifiers(argv) {
|
|
675
|
+
const allArgs = [...argv.args || []].map((arg) => typeof arg === "string" ? arg.trim() : "").filter(Boolean);
|
|
676
|
+
if (!allArgs.length && !argv.rest) return {};
|
|
677
|
+
const modifiers = {};
|
|
678
|
+
let remainingArgs = [...allArgs];
|
|
679
|
+
const promptIndex = remainingArgs.findIndex((arg) => arg.toLowerCase().startsWith("-prompt:"));
|
|
680
|
+
if (promptIndex >= 0) {
|
|
681
|
+
const [promptToken, ...afterPrompt] = remainingArgs.slice(promptIndex);
|
|
682
|
+
const promptHead = promptToken.substring(promptToken.indexOf(":") + 1);
|
|
683
|
+
modifiers.customPromptSuffix = [promptHead, ...afterPrompt].join(" ").trim();
|
|
684
|
+
remainingArgs = remainingArgs.slice(0, promptIndex);
|
|
685
|
+
}
|
|
686
|
+
for (const arg of remainingArgs) {
|
|
687
|
+
if (!arg.startsWith("-")) continue;
|
|
688
|
+
const key = normalizeSuffix(arg);
|
|
689
|
+
if (!key) continue;
|
|
690
|
+
const mapping = modelMappingIndex.get(key);
|
|
691
|
+
if (mapping) {
|
|
692
|
+
modifiers.modelMapping = mapping;
|
|
693
|
+
break;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return modifiers;
|
|
697
|
+
}
|
|
698
|
+
__name(parseStyleCommandModifiers, "parseStyleCommandModifiers");
|
|
636
699
|
function getStyleCommands() {
|
|
637
700
|
if (!config.styles || !Array.isArray(config.styles)) return [];
|
|
638
701
|
return config.styles.filter((style) => style.commandName && style.prompt).map((style) => ({
|
|
@@ -930,13 +993,23 @@ function apply(ctx, config) {
|
|
|
930
993
|
return url;
|
|
931
994
|
}
|
|
932
995
|
__name(getImageUrl, "getImageUrl");
|
|
933
|
-
async function requestProviderImages(prompt, imageUrls, numImages) {
|
|
934
|
-
|
|
996
|
+
async function requestProviderImages(prompt, imageUrls, numImages, requestContext) {
|
|
997
|
+
const providerType = requestContext?.provider || config.provider;
|
|
998
|
+
const targetModelId = requestContext?.modelId;
|
|
999
|
+
const providerInstance = getProviderInstance(providerType, targetModelId);
|
|
1000
|
+
if (config.logLevel === "debug") {
|
|
1001
|
+
logger.debug("准备调用图像供应商", {
|
|
1002
|
+
providerType,
|
|
1003
|
+
modelId: targetModelId || "default",
|
|
1004
|
+
numImages
|
|
1005
|
+
});
|
|
1006
|
+
}
|
|
1007
|
+
return await providerInstance.generateImages(prompt, imageUrls, numImages);
|
|
935
1008
|
}
|
|
936
1009
|
__name(requestProviderImages, "requestProviderImages");
|
|
937
|
-
async function processImageWithTimeout(session, img, prompt, styleName,
|
|
1010
|
+
async function processImageWithTimeout(session, img, prompt, styleName, requestContext) {
|
|
938
1011
|
return Promise.race([
|
|
939
|
-
processImage(session, img, prompt, styleName,
|
|
1012
|
+
processImage(session, img, prompt, styleName, requestContext),
|
|
940
1013
|
new Promise(
|
|
941
1014
|
(_, reject) => setTimeout(() => reject(new Error("命令执行超时")), config.commandTimeout * 1e3)
|
|
942
1015
|
)
|
|
@@ -948,12 +1021,12 @@ function apply(ctx, config) {
|
|
|
948
1021
|
});
|
|
949
1022
|
}
|
|
950
1023
|
__name(processImageWithTimeout, "processImageWithTimeout");
|
|
951
|
-
async function processImage(session, img, prompt, styleName,
|
|
1024
|
+
async function processImage(session, img, prompt, styleName, requestContext) {
|
|
952
1025
|
const userId = session.userId;
|
|
953
1026
|
if (activeTasks.has(userId)) {
|
|
954
1027
|
return "您有一个图像处理任务正在进行中,请等待完成";
|
|
955
1028
|
}
|
|
956
|
-
const imageCount = numImages || config.defaultNumImages;
|
|
1029
|
+
const imageCount = requestContext?.numImages || config.defaultNumImages;
|
|
957
1030
|
if (imageCount < 1 || imageCount > 4) {
|
|
958
1031
|
return "生成数量必须在 1-4 之间";
|
|
959
1032
|
}
|
|
@@ -961,17 +1034,21 @@ function apply(ctx, config) {
|
|
|
961
1034
|
if (!imageUrl) {
|
|
962
1035
|
return;
|
|
963
1036
|
}
|
|
1037
|
+
const providerType = requestContext?.provider || config.provider;
|
|
1038
|
+
const providerModelId = requestContext?.modelId || (providerType === "yunwu" ? config.yunwuModelId : config.gptgodModelId);
|
|
964
1039
|
logger.info("开始图像处理", {
|
|
965
1040
|
userId,
|
|
966
1041
|
imageUrl,
|
|
967
1042
|
styleName,
|
|
968
1043
|
prompt,
|
|
969
|
-
numImages: imageCount
|
|
1044
|
+
numImages: imageCount,
|
|
1045
|
+
provider: providerType,
|
|
1046
|
+
modelId: providerModelId
|
|
970
1047
|
});
|
|
971
1048
|
await session.send(`开始处理图片(${styleName})...`);
|
|
972
1049
|
try {
|
|
973
1050
|
activeTasks.set(userId, "processing");
|
|
974
|
-
const images = await requestProviderImages(prompt, imageUrl, imageCount);
|
|
1051
|
+
const images = await requestProviderImages(prompt, imageUrl, imageCount, requestContext);
|
|
975
1052
|
if (images.length === 0) {
|
|
976
1053
|
activeTasks.delete(userId);
|
|
977
1054
|
return "图像处理失败:未能生成图片";
|
|
@@ -998,13 +1075,37 @@ function apply(ctx, config) {
|
|
|
998
1075
|
if (config.styles && Array.isArray(config.styles)) {
|
|
999
1076
|
for (const style of config.styles) {
|
|
1000
1077
|
if (style.commandName && style.prompt) {
|
|
1001
|
-
ctx.command(`${style.commandName} [img:text]`, "图像风格转换").option("num", "-n <num:number> 生成图片数量 (1-4)").action(async (
|
|
1078
|
+
ctx.command(`${style.commandName} [img:text]`, "图像风格转换").option("num", "-n <num:number> 生成图片数量 (1-4)").action(async (argv, img) => {
|
|
1079
|
+
const { session, options } = argv;
|
|
1002
1080
|
if (!session?.userId) return "会话无效";
|
|
1003
1081
|
const limitCheck = await checkDailyLimit(session.userId);
|
|
1004
1082
|
if (!limitCheck.allowed) {
|
|
1005
1083
|
return limitCheck.message;
|
|
1006
1084
|
}
|
|
1007
|
-
|
|
1085
|
+
const modifiers = parseStyleCommandModifiers(argv);
|
|
1086
|
+
const promptSegments = [style.prompt];
|
|
1087
|
+
if (modifiers.modelMapping?.promptSuffix) {
|
|
1088
|
+
promptSegments.push(modifiers.modelMapping.promptSuffix);
|
|
1089
|
+
}
|
|
1090
|
+
if (modifiers.customPromptSuffix) {
|
|
1091
|
+
promptSegments.push(modifiers.customPromptSuffix);
|
|
1092
|
+
}
|
|
1093
|
+
const mergedPrompt = promptSegments.filter(Boolean).join("\n\n");
|
|
1094
|
+
const requestContext = {
|
|
1095
|
+
numImages: options?.num
|
|
1096
|
+
};
|
|
1097
|
+
if (modifiers.modelMapping?.provider) {
|
|
1098
|
+
requestContext.provider = modifiers.modelMapping.provider;
|
|
1099
|
+
}
|
|
1100
|
+
if (modifiers.modelMapping?.modelId) {
|
|
1101
|
+
requestContext.modelId = modifiers.modelMapping.modelId;
|
|
1102
|
+
}
|
|
1103
|
+
if (modifiers.modelMapping?.description) {
|
|
1104
|
+
await session.send(`已启用 ${modifiers.modelMapping.description} 配置`);
|
|
1105
|
+
} else if (modifiers.modelMapping?.suffix) {
|
|
1106
|
+
await session.send(`已启用后缀「-${modifiers.modelMapping.suffix}」配置`);
|
|
1107
|
+
}
|
|
1108
|
+
return processImageWithTimeout(session, img, mergedPrompt, style.commandName, requestContext);
|
|
1008
1109
|
});
|
|
1009
1110
|
logger.info(`已注册命令: ${style.commandName}`);
|
|
1010
1111
|
}
|