koishi-plugin-aka-ai-generator 0.1.9 → 0.2.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.d.ts +8 -0
- package/lib/index.js +144 -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
|
@@ -571,6 +571,16 @@ var Config = import_koishi.Schema.intersect([
|
|
|
571
571
|
yunwuModelId: import_koishi.Schema.string().default("gemini-2.5-flash-image").description("云雾图像生成模型ID"),
|
|
572
572
|
gptgodApiKey: import_koishi.Schema.string().description("GPTGod API 密钥").role("secret").default(""),
|
|
573
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"),
|
|
574
584
|
apiTimeout: import_koishi.Schema.number().default(120).description("API请求超时时间(秒)"),
|
|
575
585
|
commandTimeout: import_koishi.Schema.number().default(180).description("命令执行总超时时间(秒)"),
|
|
576
586
|
// 默认设置
|
|
@@ -625,17 +635,89 @@ function apply(ctx, config) {
|
|
|
625
635
|
const logger = ctx.logger("aka-ai-generator");
|
|
626
636
|
const activeTasks = /* @__PURE__ */ new Map();
|
|
627
637
|
const rateLimitMap = /* @__PURE__ */ new Map();
|
|
628
|
-
const
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
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 rawArgs = [...argv.args || []].map((arg) => typeof arg === "string" ? arg.trim() : "").filter(Boolean);
|
|
676
|
+
if (!rawArgs.length && !argv.rest) return {};
|
|
677
|
+
const modifiers = { customAdditions: [] };
|
|
678
|
+
const flagCandidates = [];
|
|
679
|
+
let index = 0;
|
|
680
|
+
while (index < rawArgs.length) {
|
|
681
|
+
const token = rawArgs[index];
|
|
682
|
+
if (!token) {
|
|
683
|
+
index++;
|
|
684
|
+
continue;
|
|
685
|
+
}
|
|
686
|
+
const lower = token.toLowerCase();
|
|
687
|
+
if (lower.startsWith("-prompt:")) {
|
|
688
|
+
const promptHead = token.substring(token.indexOf(":") + 1);
|
|
689
|
+
const restTokens = rawArgs.slice(index + 1);
|
|
690
|
+
modifiers.customPromptSuffix = [promptHead, ...restTokens].join(" ").trim();
|
|
691
|
+
break;
|
|
692
|
+
}
|
|
693
|
+
if (lower === "-add") {
|
|
694
|
+
index++;
|
|
695
|
+
const additionTokens = [];
|
|
696
|
+
while (index < rawArgs.length && !rawArgs[index].startsWith("-")) {
|
|
697
|
+
additionTokens.push(rawArgs[index]);
|
|
698
|
+
index++;
|
|
699
|
+
}
|
|
700
|
+
if (additionTokens.length) {
|
|
701
|
+
modifiers.customAdditions.push(additionTokens.join(" "));
|
|
702
|
+
}
|
|
703
|
+
continue;
|
|
704
|
+
}
|
|
705
|
+
flagCandidates.push(token);
|
|
706
|
+
index++;
|
|
707
|
+
}
|
|
708
|
+
for (const arg of flagCandidates) {
|
|
709
|
+
if (!arg.startsWith("-")) continue;
|
|
710
|
+
const key = normalizeSuffix(arg);
|
|
711
|
+
if (!key) continue;
|
|
712
|
+
const mapping = modelMappingIndex.get(key);
|
|
713
|
+
if (mapping) {
|
|
714
|
+
modifiers.modelMapping = mapping;
|
|
715
|
+
break;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
return modifiers;
|
|
719
|
+
}
|
|
720
|
+
__name(parseStyleCommandModifiers, "parseStyleCommandModifiers");
|
|
639
721
|
function getStyleCommands() {
|
|
640
722
|
if (!config.styles || !Array.isArray(config.styles)) return [];
|
|
641
723
|
return config.styles.filter((style) => style.commandName && style.prompt).map((style) => ({
|
|
@@ -933,13 +1015,23 @@ function apply(ctx, config) {
|
|
|
933
1015
|
return url;
|
|
934
1016
|
}
|
|
935
1017
|
__name(getImageUrl, "getImageUrl");
|
|
936
|
-
async function requestProviderImages(prompt, imageUrls, numImages) {
|
|
937
|
-
|
|
1018
|
+
async function requestProviderImages(prompt, imageUrls, numImages, requestContext) {
|
|
1019
|
+
const providerType = requestContext?.provider || config.provider;
|
|
1020
|
+
const targetModelId = requestContext?.modelId;
|
|
1021
|
+
const providerInstance = getProviderInstance(providerType, targetModelId);
|
|
1022
|
+
if (config.logLevel === "debug") {
|
|
1023
|
+
logger.debug("准备调用图像供应商", {
|
|
1024
|
+
providerType,
|
|
1025
|
+
modelId: targetModelId || "default",
|
|
1026
|
+
numImages
|
|
1027
|
+
});
|
|
1028
|
+
}
|
|
1029
|
+
return await providerInstance.generateImages(prompt, imageUrls, numImages);
|
|
938
1030
|
}
|
|
939
1031
|
__name(requestProviderImages, "requestProviderImages");
|
|
940
|
-
async function processImageWithTimeout(session, img, prompt, styleName,
|
|
1032
|
+
async function processImageWithTimeout(session, img, prompt, styleName, requestContext) {
|
|
941
1033
|
return Promise.race([
|
|
942
|
-
processImage(session, img, prompt, styleName,
|
|
1034
|
+
processImage(session, img, prompt, styleName, requestContext),
|
|
943
1035
|
new Promise(
|
|
944
1036
|
(_, reject) => setTimeout(() => reject(new Error("命令执行超时")), config.commandTimeout * 1e3)
|
|
945
1037
|
)
|
|
@@ -951,12 +1043,12 @@ function apply(ctx, config) {
|
|
|
951
1043
|
});
|
|
952
1044
|
}
|
|
953
1045
|
__name(processImageWithTimeout, "processImageWithTimeout");
|
|
954
|
-
async function processImage(session, img, prompt, styleName,
|
|
1046
|
+
async function processImage(session, img, prompt, styleName, requestContext) {
|
|
955
1047
|
const userId = session.userId;
|
|
956
1048
|
if (activeTasks.has(userId)) {
|
|
957
1049
|
return "您有一个图像处理任务正在进行中,请等待完成";
|
|
958
1050
|
}
|
|
959
|
-
const imageCount = numImages || config.defaultNumImages;
|
|
1051
|
+
const imageCount = requestContext?.numImages || config.defaultNumImages;
|
|
960
1052
|
if (imageCount < 1 || imageCount > 4) {
|
|
961
1053
|
return "生成数量必须在 1-4 之间";
|
|
962
1054
|
}
|
|
@@ -964,17 +1056,21 @@ function apply(ctx, config) {
|
|
|
964
1056
|
if (!imageUrl) {
|
|
965
1057
|
return;
|
|
966
1058
|
}
|
|
1059
|
+
const providerType = requestContext?.provider || config.provider;
|
|
1060
|
+
const providerModelId = requestContext?.modelId || (providerType === "yunwu" ? config.yunwuModelId : config.gptgodModelId);
|
|
967
1061
|
logger.info("开始图像处理", {
|
|
968
1062
|
userId,
|
|
969
1063
|
imageUrl,
|
|
970
1064
|
styleName,
|
|
971
1065
|
prompt,
|
|
972
|
-
numImages: imageCount
|
|
1066
|
+
numImages: imageCount,
|
|
1067
|
+
provider: providerType,
|
|
1068
|
+
modelId: providerModelId
|
|
973
1069
|
});
|
|
974
1070
|
await session.send(`开始处理图片(${styleName})...`);
|
|
975
1071
|
try {
|
|
976
1072
|
activeTasks.set(userId, "processing");
|
|
977
|
-
const images = await requestProviderImages(prompt, imageUrl, imageCount);
|
|
1073
|
+
const images = await requestProviderImages(prompt, imageUrl, imageCount, requestContext);
|
|
978
1074
|
if (images.length === 0) {
|
|
979
1075
|
activeTasks.delete(userId);
|
|
980
1076
|
return "图像处理失败:未能生成图片";
|
|
@@ -1001,13 +1097,40 @@ function apply(ctx, config) {
|
|
|
1001
1097
|
if (config.styles && Array.isArray(config.styles)) {
|
|
1002
1098
|
for (const style of config.styles) {
|
|
1003
1099
|
if (style.commandName && style.prompt) {
|
|
1004
|
-
ctx.command(`${style.commandName} [img:text]`, "图像风格转换").option("num", "-n <num:number> 生成图片数量 (1-4)").action(async (
|
|
1100
|
+
ctx.command(`${style.commandName} [img:text]`, "图像风格转换").option("num", "-n <num:number> 生成图片数量 (1-4)").action(async (argv, img) => {
|
|
1101
|
+
const { session, options } = argv;
|
|
1005
1102
|
if (!session?.userId) return "会话无效";
|
|
1006
1103
|
const limitCheck = await checkDailyLimit(session.userId);
|
|
1007
1104
|
if (!limitCheck.allowed) {
|
|
1008
1105
|
return limitCheck.message;
|
|
1009
1106
|
}
|
|
1010
|
-
|
|
1107
|
+
const modifiers = parseStyleCommandModifiers(argv);
|
|
1108
|
+
const promptSegments = [style.prompt];
|
|
1109
|
+
if (modifiers.modelMapping?.promptSuffix) {
|
|
1110
|
+
promptSegments.push(modifiers.modelMapping.promptSuffix);
|
|
1111
|
+
}
|
|
1112
|
+
if (modifiers.customAdditions?.length) {
|
|
1113
|
+
promptSegments.push(...modifiers.customAdditions);
|
|
1114
|
+
}
|
|
1115
|
+
if (modifiers.customPromptSuffix) {
|
|
1116
|
+
promptSegments.push(modifiers.customPromptSuffix);
|
|
1117
|
+
}
|
|
1118
|
+
const mergedPrompt = promptSegments.filter(Boolean).join("\n\n");
|
|
1119
|
+
const requestContext = {
|
|
1120
|
+
numImages: options?.num
|
|
1121
|
+
};
|
|
1122
|
+
if (modifiers.modelMapping?.provider) {
|
|
1123
|
+
requestContext.provider = modifiers.modelMapping.provider;
|
|
1124
|
+
}
|
|
1125
|
+
if (modifiers.modelMapping?.modelId) {
|
|
1126
|
+
requestContext.modelId = modifiers.modelMapping.modelId;
|
|
1127
|
+
}
|
|
1128
|
+
if (modifiers.modelMapping?.description) {
|
|
1129
|
+
await session.send(`已启用 ${modifiers.modelMapping.description} 配置`);
|
|
1130
|
+
} else if (modifiers.modelMapping?.suffix) {
|
|
1131
|
+
await session.send(`已启用后缀「-${modifiers.modelMapping.suffix}」配置`);
|
|
1132
|
+
}
|
|
1133
|
+
return processImageWithTimeout(session, img, mergedPrompt, style.commandName, requestContext);
|
|
1011
1134
|
});
|
|
1012
1135
|
logger.info(`已注册命令: ${style.commandName}`);
|
|
1013
1136
|
}
|