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.
- package/README.md +7 -8
- package/index.js +41 -23
- 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
|
-
#
|
|
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
|
-
-
|
|
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('
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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]`, '
|
|
114
|
-
.usage('
|
|
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
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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${
|
|
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
|
|
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.
|
|
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
|
+
}
|