koishi-plugin-chatluna-think-viewer 1.0.15 → 1.0.17

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.
Files changed (3) hide show
  1. package/README.md +7 -8
  2. package/index.js +41 -23
  3. package/package.json +40 -40
package/README.md CHANGED
@@ -1,19 +1,18 @@
1
- # koishi-plugin-chatluna-think-viewer
1
+ # koishi-plugin-chatluna-think-viewer
2
2
 
3
3
  通过命令或快捷关键词查看 `chatluna-character` 最近一次回复中的 `<think>` 思考内容,便于调试和复盘。
4
4
 
5
5
  ## 特性
6
- - 复用 `chatluna-character` 内存,不额外占用数据库。
6
+ - 复用 `chatluna-character` 的内存,不额外占用数据库。
7
7
  - 支持命令与无前缀关键词触发。
8
8
  - 群聊可用,默认禁止私聊(可配置)。
9
9
 
10
10
  ## 安装
11
11
  ```bash
12
- # 使用 Koishi 控制台市场搜索「chatluna-think-viewer」安装
13
- # 或者 npm/yarn 安装:
12
+ # Koishi 控制台市场搜索「chatluna-think-viewer」安装
13
+ # 或者 npm/yarn 安装
14
14
  npm install koishi-plugin-chatluna-think-viewer
15
- #
16
- yarn add koishi-plugin-chatluna-think-viewer
15
+ # yarn add koishi-plugin-chatluna-think-viewer
17
16
  ```
18
17
 
19
18
  ## 配置示例 (koishi.yml)
@@ -30,11 +29,11 @@ plugins:
30
29
  ```
31
30
 
32
31
  ## 使用
33
- - 群聊中发送 `think`(按你的命令前缀)或关键词“查看思考”/“上次思考”,返回上一条回复的 `<think>` 内容。
32
+ - 在群聊中发送 `think`(根据你的命令前缀)或关键词 “查看思考 / 上次思考”,返回上一条回复的 `<think>` 内容。
34
33
 
35
34
  ## 依赖
36
35
  - koishi >= 4.18.0
37
36
  - koishi-plugin-chatluna-character >= 0.0.180
38
37
 
39
38
  ## 协议
40
- MIT
39
+ MIT
package/index.js CHANGED
@@ -8,11 +8,11 @@ const inject = {
8
8
  };
9
9
 
10
10
  const Config = Schema.object({
11
- command: Schema.string().default('think').description('命令名称'),
12
- keywords: Schema.array(Schema.string()).default(['查看思考', '上次思考']).description('无需前缀即可触发的关键词'),
13
- allowPrivate: Schema.boolean().default(false).description('是否允许在私聊中使用'),
14
- emptyMessage: Schema.string().default('暂时没有可用的思考记录。').description('没有记录时的提示文案'),
15
- renderImage: Schema.boolean().default(false).description('尝试使用 ChatLuna 的 image renderer 将思考内容渲染为图片发送,失败则回退文本'),
11
+ command: Schema.string().default('think').description('命令名。'),
12
+ keywords: Schema.array(Schema.string()).default(['查看思考', '上次思考']).description('可无前缀触发的关键词。'),
13
+ allowPrivate: Schema.boolean().default(false).description('是否允许在私聊中使用。'),
14
+ emptyMessage: Schema.string().default('暂时没有可用的思考记录。').description('没有记录时的提示文本。'),
15
+ renderImage: Schema.boolean().default(false).description('是否用 ChatLuna 的 image renderer 将思考渲染为图片,失败时回退文本。'),
16
16
  });
17
17
 
18
18
  function extractText(content) {
@@ -37,7 +37,7 @@ function extractText(content) {
37
37
  }
38
38
 
39
39
  function extractThink(text) {
40
- // 有些模型/中间件会在同一条消息里多次输出 <think>,取最后一段避免误用旧片段
40
+ // 某些模型/中间件会在同一条消息里多次出现 <think>,取最后一次出现的片段
41
41
  let last = '';
42
42
  const regex = /<think>([\s\S]*?)<\/think>/gi;
43
43
  let m;
@@ -49,12 +49,12 @@ function extractThink(text) {
49
49
 
50
50
  function formatThink(text) {
51
51
  if (!text) return text;
52
- // 尝试 JSON 美化
52
+ // 尝试格式化 JSON,失败则做基础去空行/缩进美化
53
53
  try {
54
54
  const parsed = JSON.parse(text);
55
55
  return JSON.stringify(parsed, null, 2);
56
56
  } catch {
57
- // 保留原文,去掉多余空行与统一左侧缩进
57
+ // 保留原文,去掉多余空行并统一缩进
58
58
  const lines = text.split('\n').map((l) => l.trimEnd());
59
59
  const filtered = lines.filter((l, idx, arr) => !(l === '' && arr[idx - 1] === ''));
60
60
  const nonEmpty = filtered.filter((l) => l.trim().length > 0);
@@ -108,10 +108,24 @@ function getNthThink(messages, n = 1) {
108
108
  return null;
109
109
  }
110
110
 
111
+ function getLatestRawThink(temp) {
112
+ if (!temp) return '';
113
+ const candidates = [
114
+ temp?.lastCompletion?.raw?.choices?.[0]?.message?.content,
115
+ temp?.lastCompletion?.raw?.content,
116
+ temp?.lastCompletion?.content,
117
+ ];
118
+ for (const c of candidates) {
119
+ const think = extractThink(extractText(c));
120
+ if (think) return think;
121
+ }
122
+ return '';
123
+ }
124
+
111
125
  function apply(ctx, config) {
112
126
  const cmd = ctx
113
- .command(`${config.command} [index:string]`, '获取上一条回复中的 <think> 内容(可指定倒数第 N 条)')
114
- .usage('不带参数默认读取最近一条;例如 think 2 读取倒数第二条 AI 回复的思考');
127
+ .command(`${config.command} [index:string]`, '读取上一条回复里的 <think> 内容,可指定倒数第 N ')
128
+ .usage('不带参数默认读取最新一条;示例:think 2 读取倒数第 2 条 AI 回复的思考');
115
129
 
116
130
  for (const keyword of config.keywords || []) {
117
131
  cmd.shortcut(keyword, { prefix: false });
@@ -119,31 +133,35 @@ function apply(ctx, config) {
119
133
 
120
134
  cmd.action(async ({ session, args }, rawIndex) => {
121
135
  if (!config.allowPrivate && !session.guildId) {
122
- return '仅支持在群聊中查询。';
136
+ return '不支持在私聊中查询。';
123
137
  }
124
138
 
125
139
  const service = ctx.chatluna_character;
126
- if (!service) return 'chatluna-character 未启用。';
140
+ if (!service) return 'chatluna-character 未加载。';
127
141
 
128
142
  const temp = await service.getTemp(session);
129
- const messages = temp?.completionMessages || [];
130
- if (!messages.length) return config.emptyMessage;
131
-
132
143
  const targetIndex = parseIndex(rawIndex ?? args?.[0]);
133
144
 
134
- const rawThink = getNthThink(messages, targetIndex);
135
- const think = rawThink
136
- ? formatThink(rawThink)
137
- : formatThink(extractThink(extractText(getNthAiMessage(messages, targetIndex)?.content)));
138
- if (!think) return config.emptyMessage;
145
+ // 1) 优先读取最新一次原始响应(通常仍含 <think>),只对第 1 条有效
146
+ const thinkFromRaw = targetIndex === 1 ? getLatestRawThink(temp) : '';
147
+
148
+ // 2) 历史 completionMessages 中真正带 <think> 的 AI 消息
149
+ const messages = temp?.completionMessages || [];
150
+ const thinkFromHistory = thinkFromRaw ? '' : getNthThink(messages, targetIndex);
151
+
152
+ // 3) 回退:第 N 条 AI 消息再尝试抽取
153
+ const fallbackMsg = thinkFromRaw || thinkFromHistory ? null : getNthAiMessage(messages, targetIndex);
154
+ const think = thinkFromRaw || thinkFromHistory || extractThink(extractText(fallbackMsg?.content));
155
+ const formatted = formatThink(think);
156
+ if (!formatted) return config.emptyMessage;
139
157
 
140
158
  if (config.renderImage && ctx.chatluna?.renderer) {
141
159
  try {
142
160
  const title = `### 上一条思考(倒数第 ${targetIndex} 条)`;
143
- const markdown = `<div align="center">\n${title}\n</div>\n\n<div align="left">\n${think}\n</div>`;
161
+ const markdown = `<div align="center">\n${title}\n</div>\n\n<div align="left">\n${formatted}\n</div>`;
144
162
  const rendered = await ctx.chatluna.renderer.render(
145
163
  {
146
- // 中间标题居中、正文左对齐,避免整段贴左侧
164
+ // 居中标题、左对齐正文,保持 renderer 兼容
147
165
  content: [{ type: 'text', text: markdown }],
148
166
  },
149
167
  { type: 'image', session },
@@ -154,7 +172,7 @@ function apply(ctx, config) {
154
172
  }
155
173
  }
156
174
 
157
- return `上一条思考:\n${think}`;
175
+ return `上一条思考(倒数第 ${targetIndex} 条)\n${formatted}`;
158
176
  });
159
177
  }
160
178
 
package/package.json CHANGED
@@ -1,40 +1,40 @@
1
- {
2
- "name": "koishi-plugin-chatluna-think-viewer",
3
- "version": "1.0.15",
4
- "main": "index.js",
5
- "description": "Expose a command/shortcut to read the latest <think> block from chatluna-character.",
6
- "license": "MIT",
7
- "keywords": [
8
- "koishi",
9
- "chatluna",
10
- "character",
11
- "think"
12
- ],
13
- "homepage": "https://github.com/sCR0WN-s/koishi-plugin-chatluna-think-viewer",
14
- "repository": {
15
- "type": "git",
16
- "url": "git+https://github.com/sCR0WN-s/koishi-plugin-chatluna-think-viewer.git"
17
- },
18
- "contributors": [
19
- "sCR0WN-s <2892511968@qq.com>"
20
- ],
21
- "peerDependencies": {
22
- "koishi": "^4.18.0",
23
- "koishi-plugin-chatluna-character": "^0.0.180"
24
- },
25
- "koishi": {
26
- "description": {
27
- "zh": "通过命令/关键词查看 chatluna-character 最近一次回复的 <think> 思考内容。",
28
- "en": "Expose a command/shortcut to read the last <think> block from chatluna-character."
29
- },
30
- "service": {
31
- "required": [
32
- "chatluna_character"
33
- ]
34
- }
35
- },
36
- "files": [
37
- "index.js",
38
- "README.md"
39
- ]
40
- }
1
+ {
2
+ "name": "koishi-plugin-chatluna-think-viewer",
3
+ "version": "1.0.17",
4
+ "main": "index.js",
5
+ "description": "Expose a command/shortcut to read the latest <think> block from chatluna-character.",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "koishi",
9
+ "chatluna",
10
+ "character",
11
+ "think"
12
+ ],
13
+ "homepage": "https://github.com/sCR0WN-s/koishi-plugin-chatluna-think-viewer",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/sCR0WN-s/koishi-plugin-chatluna-think-viewer.git"
17
+ },
18
+ "contributors": [
19
+ "sCR0WN-s <2892511968@qq.com>"
20
+ ],
21
+ "peerDependencies": {
22
+ "koishi": "^4.18.0",
23
+ "koishi-plugin-chatluna-character": "^0.0.180"
24
+ },
25
+ "koishi": {
26
+ "description": {
27
+ "zh": "通过命令/关键词查看 chatluna-character 最近一次回复的 <think> 思考内容。",
28
+ "en": "Expose a command/shortcut to read the last <think> block from chatluna-character."
29
+ },
30
+ "service": {
31
+ "required": [
32
+ "chatluna_character"
33
+ ]
34
+ }
35
+ },
36
+ "files": [
37
+ "index.js",
38
+ "README.md"
39
+ ]
40
+ }