koishi-plugin-aka-ai-image-generator 0.5.21 → 0.5.23

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/CHANGELOG.md CHANGED
@@ -4,6 +4,21 @@
4
4
 
5
5
  - 暂无。
6
6
 
7
+ ## 0.5.23
8
+
9
+ ### Fixed
10
+
11
+ - 最终生成失败、未返回图片和内容安全拦截提示改为显式发送到聊天窗口,不再依赖 Koishi command action 的返回值自动回复。
12
+ - 保持中间重试、FormData fallback 等兼容性警告仅记录日志,避免把可恢复过程误提示给用户。
13
+
14
+ ## 0.5.22
15
+
16
+ ### Changed
17
+
18
+ - 精简 Koishi Console 模型映射与 Prompt 预设表格列标题,减少多行换行。
19
+ - 模型映射列标题调整为 `命令名`、`模型 ID`、`供应商`、`接口格式`、`限制项`。
20
+ - Prompt 预设列标题调整为 `命令名`、`生成模式`、`生成模型`、`帮助说明`、`提示词`。
21
+
7
22
  ## 0.5.21
8
23
 
9
24
  ### Changed
package/README.md CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/koishi-plugin-aka-ai-image-generator?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-aka-ai-image-generator)
4
4
 
5
- 自用 AI 图像生成插件 V2(image-only)。当前 `0.5.21` 版本采用供应商凭证 + 模型路由配置界面,并支持在每条模型映射中显式选择供应商与运行时协议。
5
+ 自用 AI 图像生成插件 V2(image-only)。当前 `0.5.23` 版本采用供应商凭证 + 模型路由配置界面,并支持在每条模型映射中显式选择供应商与运行时协议。
6
6
 
7
7
  > 范围:仅图像生成。视频生成、ChatLuna 桥接、积分账本和 Console WebUI 不在当前 `0.5.x` 运行时范围内。
8
8
 
9
9
  ## 当前版本状态
10
10
 
11
- - 当前包版本:`0.5.21`。
12
- - 当前稳定能力:供应商凭证分区、模型映射显式 supplier + protocol 路由、OpenAI-compatible 图像站点、OpenAI 官方 GPT、Gemini 官方、openai 协议、gemini 协议、基础图像命令、通用合成图、动态 styles / styleGroups 快捷命令、配置重载后动态命令刷新、styles 默认模式 / 默认模型、紧凑命令帮助、紧凑图像参数帮助、simple / detail 日志级别、统一聊天输出文案、显式 `-n`、受限模型权限拦截、额度查询、管理员只读查询、用量排行榜、基础配额 / 限流 / 管理员能力。
13
- - 当前活跃计划:第五阶段 `0.5.21` 日志级别语义和诊断日志门控已实现;后续以远端验证反馈和稳定化修复为主,见 `plans/ai-image-generator-phase5-command-prompt-plan.md`。
11
+ - 当前包版本:`0.5.23`。
12
+ - 当前稳定能力:供应商凭证分区、模型映射显式 supplier + protocol 路由、OpenAI-compatible 图像站点、OpenAI 官方 GPT、Gemini 官方、openai 协议、gemini 协议、基础图像命令、通用合成图、动态 styles / styleGroups 快捷命令、配置重载后动态命令刷新、styles 默认模式 / 默认模型、紧凑控制台表格列名、紧凑命令帮助、紧凑图像参数帮助、simple / detail 日志级别、最终失败显式聊天提示、统一聊天输出文案、显式 `-n`、受限模型权限拦截、额度查询、管理员只读查询、用量排行榜、基础配额 / 限流 / 管理员能力。
13
+ - 当前活跃计划:第五阶段 `0.5.23` 最终失败聊天提示稳定化已实现;后续以远端验证反馈和稳定化修复为主,见 `plans/ai-image-generator-phase5-command-prompt-plan.md`。
14
14
  - 当前插件路线图:见 `ROADMAP.md`。
15
15
  - 手动发布入口:从仓库根目录执行 `./push.sh aka-ai-image-generator`。
16
16
 
@@ -42,7 +42,7 @@
42
42
  - 第三方 Gemini generateContent(如云雾):供应商选第三方,协议选 `gemini`。
43
43
  - Gemini 官方:供应商选 Gemini,协议选 `gemini`。
44
44
 
45
- 配置页里的次数、窗口、超时等数值字段使用数字输入,不使用滑竿。低频设置使用 Koishi Schema 的嵌套 `.collapse()` 模式默认收起。配置页顶部提供只读初始化说明,引导用户按“供应商凭证 → 模型映射 → 快捷命令”的顺序完成首次配置。配置页文案采用分层策略:下拉选项和表格列名保持极简,字段描述适度说明用途、单位和权限影响。日志级别使用 `simple` / `detail`:`simple` 记录关键流程,`detail` 额外记录脱敏请求诊断。
45
+ 配置页里的次数、窗口、超时等数值字段使用数字输入,不使用滑竿。低频设置使用 Koishi Schema 的嵌套 `.collapse()` 模式默认收起。配置页顶部提供只读初始化说明,引导用户按“供应商凭证 → 模型映射 → 快捷命令”的顺序完成首次配置。配置页文案采用分层策略:下拉选项和表格列名保持极简,字段描述适度说明用途、单位和权限影响;表格列名优先使用短标签,减少控制台多行换行。日志级别使用 `simple` / `detail`:`simple` 记录关键流程,`detail` 额外记录脱敏请求诊断。
46
46
 
47
47
  ## 命令(v0.5.x MVP)
48
48
 
@@ -105,10 +105,12 @@
105
105
  - v0.5.19:统一润色聊天可见输出,额度、查询、排行榜、输入引导、生成状态、失败和权限提示改为短标题与一行式条目格式。
106
106
  - v0.5.20:将参数帮助命令从 `参数指令` 改名为 `图像参数`,让两个帮助入口统一以 `图像` 开头。
107
107
  - v0.5.21:将日志级别显示改为 `simple` / `detail`,并让 `detail` 通过插件配置显式控制脱敏请求诊断日志。
108
+ - v0.5.22:精简模型映射与 Prompt 预设表格列名,降低 Koishi Console 表头换行。
109
+ - v0.5.23:最终生成失败、未返回图片和内容安全拦截提示改为显式发送到聊天窗口,降低长耗时命令依赖 action 返回值自动回复的风险。
108
110
 
109
111
  ## 后续计划
110
112
 
111
- - `0.5.22`:预留为后续收敛版本;重点处理 `0.5.21` 远端验证反馈和命令交互细节修正。`图像充值` 与更多风格迁移类 prompt 预设需重新评估后再进入后续版本。
113
+ - `0.5.24`:预留为后续收敛版本;重点处理 `0.5.23` 远端验证反馈和命令交互细节修正。`图像充值` 与更多风格迁移类 prompt 预设需重新评估后再进入后续版本。
112
114
  - `0.6.0+`:积分制计费、用户数据 v2、账本与充值审计。
113
115
 
114
116
  ## 远端验证建议
@@ -116,8 +118,8 @@
116
118
  用户发布并在目标 Koishi 环境升级后,建议优先验证:
117
119
 
118
120
  1. 配置页顶部显示 `📌 初始化说明` 只读引导,并按供应商凭证、模型映射、Prompt 预设 / 快捷命令、管理员与权限、配额与限流、安全策略、通用设置分组显示;低频配额与安全策略默认折叠。
119
- 2. 配置页 `styles` 每行可设置默认模式和默认模型后缀,模型映射分组位于 Prompt 预设分组上方。
120
- 3. 模型映射每行可显式设置供应商与协议,供应商显示为第三方 / OpenAI / Gemini,并能区分 OpenAI 官方 GPT、OpenAI-compatible GPT、OpenAI-compatible Gemini、Gemini 官方;字段说明能解释供应商决定凭证、协议决定接口格式。
121
+ 2. 配置页 `styles` 每行可设置生成模式和生成模型,模型映射分组位于 Prompt 预设分组上方。
122
+ 3. 模型映射每行可显式设置供应商与接口格式,供应商显示为第三方 / OpenAI / Gemini,并能区分 OpenAI 官方 GPT、OpenAI-compatible GPT、OpenAI-compatible Gemini、Gemini 官方;表格列名保持短标签,减少多行换行。
121
123
  4. OpenAI-compatible + `openai` 可完成 `文生图 一只猫`。
122
124
  5. `文生图 -n 2 一只猫` 使用 2 张图进行额度预检和生成。
123
125
  6. `文生图 -16:9 -2k 一座城市` 能带入画幅与分辨率。
@@ -135,7 +137,8 @@
135
137
  18. 非白名单用户调用 restricted 模型后缀会被 `模型受限` 分区提示拒绝,管理员和模型白名单用户可用。
136
138
  19. `图像额度` 能返回 `图像额度` 标题与用户、今日免费、已购次数、合计可用、历史用量等条目。
137
139
  20. 输入引导、生成开始、生成完成、额度不足、内容安全拦截和上游失败提示均保持短标题与一行式条目格式,日志不泄露 API key。
138
- 21. 日志级别为 `simple` 时仅记录关键流程;切换到 `detail` 后会额外输出请求 URL、模型路由、脱敏 headers、请求体摘要、超时、尺寸和比例等诊断信息。
140
+ 21. 模拟或等待一次最终上游失败时,聊天窗口能收到 `生成失败` `内容安全拦截` 提示;中间 fallback / retry 警告仍只写入日志。
141
+ 22. 日志级别为 `simple` 时仅记录关键流程;切换到 `detail` 后会额外输出请求 URL、模型路由、脱敏 headers、请求体摘要、超时、尺寸和比例等诊断信息。
139
142
 
140
143
  ## 发布边界
141
144
 
package/dist/index.js CHANGED
@@ -567,6 +567,17 @@ function createImageGenerationHandlers(params) {
567
567
  }
568
568
  return { value: String(error ?? "") };
569
569
  }
570
+ async function sendFinalText(session, message, userId, logLabel) {
571
+ try {
572
+ await session.send(message);
573
+ } catch (sendError) {
574
+ logger.error(logLabel, {
575
+ userId,
576
+ ...sanitizeForLog(sendError)
577
+ });
578
+ }
579
+ return "";
580
+ }
570
581
  async function collectTextInput(session, initialPrompt) {
571
582
  const config = getConfig();
572
583
  const trimmed = typeof initialPrompt === "string" ? initialPrompt.trim() : "";
@@ -813,7 +824,12 @@ function createImageGenerationHandlers(params) {
813
824
  }
814
825
  return "";
815
826
  }
816
- return ["\u751F\u6210\u5931\u8D25", "", "- \u539F\u56E0\uFF5C\u672A\u8FD4\u56DE\u56FE\u7247", "- \u5EFA\u8BAE\uFF5C\u7A0D\u540E\u91CD\u8BD5\u6216\u8C03\u6574\u63CF\u8FF0"].join("\n");
827
+ return sendFinalText(
828
+ session,
829
+ ["\u751F\u6210\u5931\u8D25", "", "- \u539F\u56E0\uFF5C\u672A\u8FD4\u56DE\u56FE\u7247", "- \u5EFA\u8BAE\uFF5C\u7A0D\u540E\u91CD\u8BD5\u6216\u8C03\u6574\u63CF\u8FF0"].join("\n"),
830
+ userId,
831
+ "\u53D1\u9001\u751F\u6210\u5931\u8D25\u63D0\u793A\u5931\u8D25"
832
+ );
817
833
  } catch (error) {
818
834
  logger.error("\u56FE\u50CF\u751F\u6210\u6D41\u7A0B\u5F02\u5E38", {
819
835
  userId,
@@ -824,7 +840,12 @@ function createImageGenerationHandlers(params) {
824
840
  try {
825
841
  const result = await userManager.recordSecurityBlock(userId, config);
826
842
  if (result.shouldWarn) {
827
- return ["\u5185\u5BB9\u5B89\u5168\u62E6\u622A", "", "\u8BF7\u8C03\u6574\u63CF\u8FF0\u540E\u518D\u8BD5\uFF1B\u591A\u6B21\u89E6\u53D1\u4F1A\u5F71\u54CD\u540E\u7EED\u4F7F\u7528"].join("\n");
843
+ return sendFinalText(
844
+ session,
845
+ ["\u5185\u5BB9\u5B89\u5168\u62E6\u622A", "", "\u8BF7\u8C03\u6574\u63CF\u8FF0\u540E\u518D\u8BD5\uFF1B\u591A\u6B21\u89E6\u53D1\u4F1A\u5F71\u54CD\u540E\u7EED\u4F7F\u7528"].join("\n"),
846
+ userId,
847
+ "\u53D1\u9001\u5185\u5BB9\u5B89\u5168\u62E6\u622A\u63D0\u793A\u5931\u8D25"
848
+ );
828
849
  }
829
850
  } catch (recordErr) {
830
851
  logger.error("\u8BB0\u5F55\u5B89\u5168\u963B\u65AD\u5931\u8D25", {
@@ -834,7 +855,12 @@ function createImageGenerationHandlers(params) {
834
855
  }
835
856
  }
836
857
  const message = error instanceof Error ? error.message : String(error);
837
- return ["\u751F\u6210\u5931\u8D25", "", `- \u539F\u56E0\uFF5C${message}`].join("\n");
858
+ return sendFinalText(
859
+ session,
860
+ ["\u751F\u6210\u5931\u8D25", "", `- \u539F\u56E0\uFF5C${message}`].join("\n"),
861
+ userId,
862
+ "\u53D1\u9001\u751F\u6210\u5931\u8D25\u63D0\u793A\u5931\u8D25"
863
+ );
838
864
  } finally {
839
865
  userManager.endTask(userId, requestId);
840
866
  }
@@ -3198,15 +3224,15 @@ var SETUP_GUIDE = [
3198
3224
  "\u53D7\u9650\u6A21\u578B\u4EC5\u7BA1\u7406\u5458\u6216\u6A21\u578B\u767D\u540D\u5355\u53EF\u7528\uFF1B\u6C38\u4E45\u4F1A\u5458\u53EA\u8DF3\u8FC7\u989D\u5EA6\u548C\u9650\u6D41\u3002"
3199
3225
  ].join("\n");
3200
3226
  var StyleItemSchema = import_koishi6.Schema.object({
3201
- commandName: import_koishi6.Schema.string().required().description("\u547D\u4EE4\u540D\uFF0C\u7528\u6237\u53EF\u76F4\u63A5\u53D1\u9001\u5B83\u6765\u8C03\u7528\u8BE5\u9884\u8BBE").role("table-cell", { width: 24 }),
3227
+ commandName: import_koishi6.Schema.string().required().description("\u547D\u4EE4\u540D").role("table-cell", { width: 24 }),
3202
3228
  mode: import_koishi6.Schema.union([
3203
3229
  import_koishi6.Schema.const("text-to-image").description("\u6587\u751F\u56FE"),
3204
3230
  import_koishi6.Schema.const("image-to-image").description("\u56FE\u751F\u56FE"),
3205
3231
  import_koishi6.Schema.const("compose-image").description("\u5408\u6210\u56FE")
3206
- ]).default("image-to-image").description("\u9ED8\u8BA4\u751F\u6210\u94FE\u8DEF\uFF0C\u51B3\u5B9A\u9700\u8981\u6587\u5B57\u3001\u5355\u56FE\u8FD8\u662F\u591A\u56FE\u8F93\u5165").role("table-cell", { width: 24 }),
3207
- modelSuffix: import_koishi6.Schema.string().default("").description("\u9ED8\u8BA4\u6A21\u578B\u540E\u7F00\uFF1B\u7559\u7A7A\u65F6\u4F7F\u7528\u6A21\u578B\u6620\u5C04\u7B2C\u4E00\u6761").role("table-cell", { width: 24 }),
3208
- description: import_koishi6.Schema.string().role("textarea", { rows: 2 }).description("\u5728\u5E2E\u52A9\u4E2D\u5C55\u793A\u7684\u7B80\u77ED\u8BF4\u660E"),
3209
- prompt: import_koishi6.Schema.string().role("textarea", { rows: 6 }).required().description("\u9884\u8BBE\u63D0\u793A\u8BCD\uFF0C\u4F1A\u4E0E\u7528\u6237\u8FFD\u52A0\u5185\u5BB9\u5408\u5E76")
3232
+ ]).default("image-to-image").description("\u751F\u6210\u6A21\u5F0F").role("table-cell", { width: 24 }),
3233
+ modelSuffix: import_koishi6.Schema.string().default("").description("\u751F\u6210\u6A21\u578B").role("table-cell", { width: 24 }),
3234
+ description: import_koishi6.Schema.string().role("textarea", { rows: 2 }).description("\u5E2E\u52A9\u8BF4\u660E"),
3235
+ prompt: import_koishi6.Schema.string().role("textarea", { rows: 6 }).required().description("\u63D0\u793A\u8BCD")
3210
3236
  });
3211
3237
  var ProviderSettingsSchema = import_koishi6.Schema.object({
3212
3238
  openaiCompatibleApiKey: import_koishi6.Schema.string().role("secret").default("").description("\u7B2C\u4E09\u65B9 Key\uFF0C\u7528\u4E8E\u4E91\u96FE\u7B49\u517C\u5BB9\u7AD9\u70B9"),
@@ -3229,18 +3255,18 @@ var Config = import_koishi6.Schema.intersect([
3229
3255
  import_koishi6.Schema.object({
3230
3256
  modelMappings: import_koishi6.Schema.array(
3231
3257
  import_koishi6.Schema.object({
3232
- suffix: import_koishi6.Schema.string().required().description("\u540E\u7F00\uFF1B\u547D\u4EE4\u4E2D\u7528 -\u540E\u7F00 \u9009\u62E9\u6A21\u578B"),
3233
- modelId: import_koishi6.Schema.string().required().description("\u6A21\u578B ID\uFF0C\u4E0A\u6E38\u771F\u5B9E\u6A21\u578B\u540D"),
3258
+ suffix: import_koishi6.Schema.string().required().description("\u547D\u4EE4\u540D"),
3259
+ modelId: import_koishi6.Schema.string().required().description("\u6A21\u578B ID"),
3234
3260
  supplier: import_koishi6.Schema.union([
3235
3261
  import_koishi6.Schema.const("openai-compatible").description("\u7B2C\u4E09\u65B9"),
3236
3262
  import_koishi6.Schema.const("gpt-official").description("OpenAI"),
3237
3263
  import_koishi6.Schema.const("gemini-official").description("Gemini")
3238
- ]).default("openai-compatible").description("\u4F9B\u5E94\u5546\uFF0C\u51B3\u5B9A\u4F7F\u7528\u54EA\u7EC4\u51ED\u8BC1"),
3264
+ ]).default("openai-compatible").description("\u4F9B\u5E94\u5546"),
3239
3265
  protocol: import_koishi6.Schema.union([
3240
3266
  import_koishi6.Schema.const("openai").description("OpenAI"),
3241
3267
  import_koishi6.Schema.const("gemini").description("Gemini")
3242
- ]).default("openai").description("\u534F\u8BAE\uFF0C\u51B3\u5B9A\u6309\u54EA\u79CD\u63A5\u53E3\u683C\u5F0F\u8BF7\u6C42"),
3243
- restricted: import_koishi6.Schema.boolean().default(false).description("\u53D7\u9650\uFF1B\u5F00\u542F\u540E\u4EC5\u7BA1\u7406\u5458\u6216\u6A21\u578B\u767D\u540D\u5355\u53EF\u7528")
3268
+ ]).default("openai").description("\u63A5\u53E3\u683C\u5F0F"),
3269
+ restricted: import_koishi6.Schema.boolean().default(false).description("\u9650\u5236\u9879")
3244
3270
  }).collapse()
3245
3271
  ).role("table").default([
3246
3272
  {