koishi-plugin-aka-ai-generator 0.1.9 → 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 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,67 @@ 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 imageProvider = createImageProvider({
629
- provider: config.provider,
630
- yunwuApiKey: config.yunwuApiKey,
631
- yunwuModelId: config.yunwuModelId,
632
- gptgodApiKey: config.gptgodApiKey,
633
- gptgodModelId: config.gptgodModelId,
634
- apiTimeout: config.apiTimeout,
635
- logLevel: config.logLevel,
636
- logger,
637
- ctx
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 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");
639
699
  function getStyleCommands() {
640
700
  if (!config.styles || !Array.isArray(config.styles)) return [];
641
701
  return config.styles.filter((style) => style.commandName && style.prompt).map((style) => ({
@@ -933,13 +993,23 @@ function apply(ctx, config) {
933
993
  return url;
934
994
  }
935
995
  __name(getImageUrl, "getImageUrl");
936
- async function requestProviderImages(prompt, imageUrls, numImages) {
937
- return await imageProvider.generateImages(prompt, imageUrls, numImages);
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);
938
1008
  }
939
1009
  __name(requestProviderImages, "requestProviderImages");
940
- async function processImageWithTimeout(session, img, prompt, styleName, numImages) {
1010
+ async function processImageWithTimeout(session, img, prompt, styleName, requestContext) {
941
1011
  return Promise.race([
942
- processImage(session, img, prompt, styleName, numImages),
1012
+ processImage(session, img, prompt, styleName, requestContext),
943
1013
  new Promise(
944
1014
  (_, reject) => setTimeout(() => reject(new Error("命令执行超时")), config.commandTimeout * 1e3)
945
1015
  )
@@ -951,12 +1021,12 @@ function apply(ctx, config) {
951
1021
  });
952
1022
  }
953
1023
  __name(processImageWithTimeout, "processImageWithTimeout");
954
- async function processImage(session, img, prompt, styleName, numImages) {
1024
+ async function processImage(session, img, prompt, styleName, requestContext) {
955
1025
  const userId = session.userId;
956
1026
  if (activeTasks.has(userId)) {
957
1027
  return "您有一个图像处理任务正在进行中,请等待完成";
958
1028
  }
959
- const imageCount = numImages || config.defaultNumImages;
1029
+ const imageCount = requestContext?.numImages || config.defaultNumImages;
960
1030
  if (imageCount < 1 || imageCount > 4) {
961
1031
  return "生成数量必须在 1-4 之间";
962
1032
  }
@@ -964,17 +1034,21 @@ function apply(ctx, config) {
964
1034
  if (!imageUrl) {
965
1035
  return;
966
1036
  }
1037
+ const providerType = requestContext?.provider || config.provider;
1038
+ const providerModelId = requestContext?.modelId || (providerType === "yunwu" ? config.yunwuModelId : config.gptgodModelId);
967
1039
  logger.info("开始图像处理", {
968
1040
  userId,
969
1041
  imageUrl,
970
1042
  styleName,
971
1043
  prompt,
972
- numImages: imageCount
1044
+ numImages: imageCount,
1045
+ provider: providerType,
1046
+ modelId: providerModelId
973
1047
  });
974
1048
  await session.send(`开始处理图片(${styleName})...`);
975
1049
  try {
976
1050
  activeTasks.set(userId, "processing");
977
- const images = await requestProviderImages(prompt, imageUrl, imageCount);
1051
+ const images = await requestProviderImages(prompt, imageUrl, imageCount, requestContext);
978
1052
  if (images.length === 0) {
979
1053
  activeTasks.delete(userId);
980
1054
  return "图像处理失败:未能生成图片";
@@ -1001,13 +1075,37 @@ function apply(ctx, config) {
1001
1075
  if (config.styles && Array.isArray(config.styles)) {
1002
1076
  for (const style of config.styles) {
1003
1077
  if (style.commandName && style.prompt) {
1004
- ctx.command(`${style.commandName} [img:text]`, "图像风格转换").option("num", "-n <num:number> 生成图片数量 (1-4)").action(async ({ session, options }, img) => {
1078
+ ctx.command(`${style.commandName} [img:text]`, "图像风格转换").option("num", "-n <num:number> 生成图片数量 (1-4)").action(async (argv, img) => {
1079
+ const { session, options } = argv;
1005
1080
  if (!session?.userId) return "会话无效";
1006
1081
  const limitCheck = await checkDailyLimit(session.userId);
1007
1082
  if (!limitCheck.allowed) {
1008
1083
  return limitCheck.message;
1009
1084
  }
1010
- return processImageWithTimeout(session, img, style.prompt, style.commandName, options?.num);
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);
1011
1109
  });
1012
1110
  logger.info(`已注册命令: ${style.commandName}`);
1013
1111
  }
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.1.9",
4
+ "version": "0.2.0",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [