dsh-repeat-guard 0.2.1 → 0.2.2

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 yuqing
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 yuqing
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,115 +1,121 @@
1
- # dsh-repeat-guard
2
-
3
- > 解决大模型用你的token唱歌的糟糕问题
4
-
5
- 我们发现部分模型(尤其是`deepseek v4.1 flash`)很容易在长上下文时进入复读状态。
6
- dsh社区中称呼这种情况为“唱歌”,典型的表现如下所示:
7
- ```
8
- Let me do it.
9
- Let me run.
10
- Let me go.
11
- Let me do it.
12
- ...
13
- ```
14
- 如果在预设或者`AGENTS.md`中约束使用中文思考,则会变成如下的场景:
15
- ```
16
- 好。
17
- 做。
18
- 开始。
19
- 做。
20
- 嗯。
21
- 做。
22
- 我必须开始工作了。
23
- 好。
24
- 做。
25
- ...
26
- ```
27
- 配合`deepseek v4.1 flash`高达 300tok/s 的恐怖输出速率,模型可能会以极为可观的速度消耗token,而不产生任何有效输出。
28
-
29
- 这通常是比较隐蔽的,尤其是在用户没有注意模型的思考过程的时候。用户实际上只会看到思考过程快速闪过很短的句子,回答好像很正常。
30
-
31
- 在上下文长度达到 300k 以上的时候,该现象会变得十分频繁。
32
-
33
- 不仅如此,我们也观察到`deepseek v4.1 flash`有着在已经思考到需要工具调用的时候输出一个短句,接下来又进入另一个方向的复杂思考过程的倾向。如下所示:
34
- ```
35
- 我已经收集到了足够的信息,接下来开始操作。先修改xxx文件。
36
- 做。
37
- 但是我还发现仓库里有其它的内容。注意到用户提到xxxxx
38
- ...
39
- ```
40
-
41
- 显然这不是我们想要的行为。既然模型已经清楚下一步的工作是什么,就应该直接开始工具调用。
42
-
43
- 我们还注意到上述问题只出现于思考过程而不出现于回答内容。
44
- ## 此插件的功能
45
-
46
- 此插件致力于解决上述模型复读和无端长思考的问题。它会持续检测模型的输出,如果思考过程出现了一个**白名单**中的短句且单独成段,且模型在输出这个短句后既然有继续思考的意图,我们就会强行中断对话,并发送一句提醒,让模型直接开始下一轮的输出。
47
-
48
- 例如
49
- ```
50
- [深度思考]
51
- 直接检查服务器状态和崩溃日志。
52
-
53
- 关键:先看最新日志和 crash-reports。
54
-
55
- 执行。 <--打断
56
-
57
- [复读拦截]
58
- 你上一段思考退化成了碎片复读(反复输出"好。"一类的空话),已被系统截断。
59
- 请直接继续执行下一步,不要再输出任何确认语、寒暄或空话。
60
-
61
- [深度思考]
62
- 直接调用工具,检查服务器状态和崩溃日志。
63
-
64
- [tool calls]
65
- ssh -o ConnectTimeout=10 -o BatchMode=yes ...
66
- ...
67
- ```
68
-
69
- ## 工作原理
70
-
71
- 逐 chunk 观察 `reasoning-delta`,检查思考文本。
72
-
73
- 独立段命中白名单时,向模型传递中断请求,掐断模型输出的片段,并补充结束标志,伪装成正常结束的思考过程。
74
-
75
- 向模型发送一条注入提示词,以恢复`agent-loop`。
76
-
77
- 用户会通过注入提示词这个步骤看到大模型被中断的过程,因此步骤可能会变得很多。这是不得不付出的代价。
78
-
79
- > 虽然我们可以将下一步思考的过程接到上一轮打断的位置以伪装成什么都没发生过的样子,但是一方面这种做法有风险,另一方面不是很有利于缓存命中。更何况,这也能帮助用户判断插件是否在正常工作。
80
-
81
- ## 可配置选项
82
-
83
- 在 **设置 → 复读打断** 里查看,保存后生效,无需重启。
84
-
85
- | 项 | 含义 |
86
- |---|---|
87
- | 累计拦截 | 掐断次数(有清零按钮) |
88
- | 拦截短句 | 触发打断的白名单,需要加上标点 |
89
- | 连续命中次数\* | 连续命中的阈值,如果为2就需要连续出现两个短句才触发 |
90
- | 行内重复检测\* | 打开后"好。好。好。"这种挤在一行里的重复也算命中。 |
91
- | 续跑指令 | 截断后推给模型的提示词 |
92
- | 折叠行摘要 | 注入段用户可见的简要信息 |
93
-
94
- > \* 连续的意思是中间没有长段。如果配置成2就只会拦截循环,默认配置连一个短句后的分支思考过程也会拦下。
95
-
96
- > \*deepseek不存在行内重复问题,这个选项是为 qwen 35B 这样的模型准备的。
97
-
98
-
99
- ## 安装到 dsh
100
-
101
- ```bash
102
- dsh plugin --profile web add dsh-repeat-guard
103
- ```
104
-
105
- > **兼容性**:`0.2.0` 起要求 dsh **0.1.7 及以上**——0.1.7 起宿主会在装载前校验插件的
106
- > peer 声明,配置表单也换成了 entry Config 模型,旧写法在那上面不再工作。
107
- > dsh `0.1.0-rc.8` ~ `0.1.6` 请装 `0.1.5`:
108
- >
109
- > ```bash
110
- > dsh plugin --profile web add dsh-repeat-guard@0.1.5
111
- > ```
112
-
113
- ## 许可
114
-
115
- MIT,见 `LICENSE`。
1
+ # dsh-repeat-guard
2
+
3
+ > 解决大模型用你的token唱歌的糟糕问题
4
+
5
+ 我们发现部分模型(尤其是`deepseek v4.1 flash`)很容易在长上下文时进入复读状态。
6
+ dsh社区中称呼这种情况为“唱歌”,典型的表现如下所示:
7
+ ```
8
+ Let me do it.
9
+ Let me run.
10
+ Let me go.
11
+ Let me do it.
12
+ ...
13
+ ```
14
+ 如果在预设或者`AGENTS.md`中约束使用中文思考,则会变成如下的场景:
15
+ ```
16
+ 好。
17
+ 做。
18
+ 开始。
19
+ 做。
20
+ 嗯。
21
+ 做。
22
+ 我必须开始工作了。
23
+ 好。
24
+ 做。
25
+ ...
26
+ ```
27
+ 配合`deepseek v4.1 flash`高达 300tok/s 的恐怖输出速率,模型可能会以极为可观的速度消耗token,而不产生任何有效输出。
28
+
29
+ 这通常是比较隐蔽的,尤其是在用户没有注意模型的思考过程的时候。用户实际上只会看到思考过程快速闪过很短的句子,回答好像很正常。
30
+
31
+ 在上下文长度达到 300k 以上的时候,该现象会变得十分频繁。
32
+
33
+ 不仅如此,我们也观察到`deepseek v4.1 flash`有着在已经思考到需要工具调用的时候输出一个短句,接下来又进入另一个方向的复杂思考过程的倾向。如下所示:
34
+ ```
35
+ 我已经收集到了足够的信息,接下来开始操作。先修改xxx文件。
36
+ 做。
37
+ 但是我还发现仓库里有其它的内容。注意到用户提到xxxxx
38
+ ...
39
+ ```
40
+
41
+ 显然这不是我们想要的行为。既然模型已经清楚下一步的工作是什么,就应该直接开始工具调用。
42
+
43
+ 我们还注意到上述问题只出现于思考过程而不出现于回答内容。
44
+ ## 此插件的功能
45
+
46
+ 此插件致力于解决上述模型复读和无端长思考的问题。它会持续检测模型的输出,如果思考过程出现了一个**白名单**中的短句且单独成段,且模型在输出这个短句后既然有继续思考的意图,我们就会强行中断对话,并发送一句提醒,让模型直接开始下一轮的输出。
47
+
48
+ 例如
49
+ ```
50
+ [深度思考]
51
+ 直接检查服务器状态和崩溃日志。
52
+
53
+ 关键:先看最新日志和 crash-reports。
54
+
55
+ 执行。 <--打断
56
+
57
+ [复读拦截]
58
+ 你上一段思考退化成了碎片复读(反复输出"好。"一类的空话),已被系统截断。
59
+ 请直接继续执行下一步,不要再输出任何确认语、寒暄或空话。
60
+
61
+ [深度思考]
62
+ 直接调用工具,检查服务器状态和崩溃日志。
63
+
64
+ [tool calls]
65
+ ssh -o ConnectTimeout=10 -o BatchMode=yes ...
66
+ ...
67
+ ```
68
+
69
+ ## 工作原理
70
+
71
+ 逐 chunk 观察 `reasoning-delta`,检查思考文本。
72
+
73
+ 独立段命中白名单时,向模型传递中断请求,掐断模型输出的片段,并补充结束标志,伪装成正常结束的思考过程。
74
+
75
+ 向模型发送一条注入提示词,以恢复`agent-loop`。
76
+
77
+ 用户会通过注入提示词这个步骤看到大模型被中断的过程,因此步骤可能会变得很多。这是不得不付出的代价。
78
+
79
+ > 虽然我们可以将下一步思考的过程接到上一轮打断的位置以伪装成什么都没发生过的样子,但是一方面这种做法有风险,另一方面不是很有利于缓存命中。更何况,这也能帮助用户判断插件是否在正常工作。
80
+
81
+ ## 可配置选项
82
+
83
+ 在 **设置 → 复读打断** 里查看,保存后生效,无需重启。
84
+
85
+ | 项 | 含义 |
86
+ |---|---|
87
+ | 累计拦截 | 掐断次数(有清零按钮) |
88
+ | 拦截短句 | 触发打断的白名单,需要加上标点 |
89
+ | 连续命中次数\* | 连续命中的阈值,如果为2就需要连续出现两个短句才触发 |
90
+ | 行内重复检测\* | 打开后"好。好。好。"这种挤在一行里的重复也算命中。 |
91
+ | 续跑指令 | 截断后推给模型的提示词 |
92
+ | 折叠行摘要 | 注入段用户可见的简要信息 |
93
+
94
+ > \* 连续的意思是中间没有长段。如果配置成2就只会拦截循环,默认配置连一个短句后的分支思考过程也会拦下。
95
+
96
+ > \*deepseek不存在行内重复问题,这个选项是为 qwen 35B 这样的模型准备的。
97
+
98
+
99
+ ## 安装到 dsh
100
+
101
+ ```bash
102
+ dsh plugin --profile web add dsh-repeat-guard
103
+ ```
104
+
105
+ > **兼容性**:`0.2.0` 起要求 dsh **0.1.7 及以上**——0.1.7 起宿主会在装载前校验插件的
106
+ > peer 声明,配置表单也换成了 entry Config 模型,旧写法在那上面不再工作。
107
+ > 插件用到的宿主接口在 **0.2.0** 上没有变化(`llm/stream` 的 waterfall 形态、
108
+ > `agent.steer`、`agent/turn-stopping` 的重读机制、`settings` 与客户端 `configForms`
109
+ > 的形状均逐字相同),因此 `0.2.2` 起 peer 范围不再设上限。
110
+ > 写法是 `>=0.1.7-0 || >=0.2.0-0` 而不是 `>=0.1.7-0`:按 semver 规则,预发布版本只被
111
+ > 「版本号相同的预发布比较器」接受,单写 `>=0.1.7-0` 会漏掉 `0.2.0-rc.x`;`||` 里那一段
112
+ > 专门给 0.2 的预发布开口,0.1.7 及更早的正式版仍由前一段覆盖。
113
+ > dsh `0.1.0-rc.8` ~ `0.1.6` 请装 `0.1.5`:
114
+ >
115
+ > ```bash
116
+ > dsh plugin --profile web add dsh-repeat-guard@0.1.5
117
+ > ```
118
+
119
+ ## 许可
120
+
121
+ MIT,见 `LICENSE`。
package/cordis.patch.yml CHANGED
@@ -1,7 +1,7 @@
1
- # dsh-repeat-guard 的 bundle 补丁:把本插件插进宿主配置。
2
- #
3
- # 本文件由 package.json 的 dsh.bundle.patch 字段声明,安装后由 dsh 自动加载,
4
- # 装在 profile 里的用户不需要改自己的 cordis.patch.yml。
5
- - insert:
6
- - id: dsh-repeat-guard
7
- name: dsh-repeat-guard
1
+ # dsh-repeat-guard 的 bundle 补丁:把本插件插进宿主配置。
2
+ #
3
+ # 本文件由 package.json 的 dsh.bundle.patch 字段声明,安装后由 dsh 自动加载,
4
+ # 装在 profile 里的用户不需要改自己的 cordis.patch.yml。
5
+ - insert:
6
+ - id: dsh-repeat-guard
7
+ name: dsh-repeat-guard
package/package.json CHANGED
@@ -1,64 +1,64 @@
1
- {
2
- "name": "dsh-repeat-guard",
3
- "version": "0.2.1",
4
- "description": "流式输出退化拦截:检测碎片复读,掐断本次生成并提醒模型直接执行工具调用",
5
- "type": "module",
6
- "main": "lib/index.js",
7
- "types": "lib/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./lib/index.d.ts",
11
- "default": "./lib/index.js"
12
- },
13
- "./client": {
14
- "default": "./lib/client.js"
15
- },
16
- "./package.json": "./package.json"
17
- },
18
- "files": [
19
- "lib",
20
- "src",
21
- "cordis.patch.yml"
22
- ],
23
- "dsh": {
24
- "bundle": {
25
- "patch": "./cordis.patch.yml"
26
- },
27
- "client": {
28
- "platform": "web",
29
- "inject": [
30
- "@deepseek-ai/dsh-client-ui-renderer",
31
- "@deepseek-ai/dsh-client-ui-settings"
32
- ]
33
- }
34
- },
35
- "scripts": {
36
- "build": "tsc && node scripts/build-client.mjs",
37
- "prepack": "npm run build"
38
- },
39
- "license": "MIT",
40
- "author": "yuqing",
41
- "peerDependencies": {
42
- "@deepseek-ai/cordis": "^4.0.4",
43
- "@deepseek-ai/dsh-agent": ">=0.1.7-0 <0.1.8-0",
44
- "@deepseek-ai/dsh-client-ui-renderer": ">=0.1.7-0 <0.1.8-0",
45
- "@deepseek-ai/dsh-client-ui-settings": ">=0.1.7-0 <0.1.8-0",
46
- "@deepseek-ai/dsh-llm": ">=0.1.7-0 <0.1.8-0",
47
- "@deepseek-ai/dsh-settings": ">=0.1.7-0 <0.1.8-0",
48
- "@deepseek-ai/schemastery": "^3.18.4",
49
- "react": "^18.2.0"
50
- },
51
- "devDependencies": {
52
- "@deepseek-ai/cordis": "4.0.4",
53
- "@deepseek-ai/dsh-agent": "0.1.7-rc.2",
54
- "@deepseek-ai/dsh-client-ui-renderer": "0.1.7-rc.2",
55
- "@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.2",
56
- "@deepseek-ai/dsh-llm": "0.1.7-rc.2",
57
- "@deepseek-ai/dsh-settings": "0.1.7-rc.2",
58
- "@deepseek-ai/schemastery": "3.18.4",
59
- "@types/node": "^22.0.0",
60
- "@types/react": "^19.3.0",
61
- "esbuild": "^0.28.2",
62
- "typescript": "^7.0.2"
63
- }
64
- }
1
+ {
2
+ "name": "dsh-repeat-guard",
3
+ "version": "0.2.2",
4
+ "description": "流式输出退化拦截:检测碎片复读,掐断本次生成并提醒模型直接执行工具调用",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/index.d.ts",
11
+ "default": "./lib/index.js"
12
+ },
13
+ "./client": {
14
+ "default": "./lib/client.js"
15
+ },
16
+ "./package.json": "./package.json"
17
+ },
18
+ "files": [
19
+ "lib",
20
+ "src",
21
+ "cordis.patch.yml"
22
+ ],
23
+ "dsh": {
24
+ "bundle": {
25
+ "patch": "./cordis.patch.yml"
26
+ },
27
+ "client": {
28
+ "platform": "web",
29
+ "inject": [
30
+ "@deepseek-ai/dsh-client-ui-renderer",
31
+ "@deepseek-ai/dsh-client-ui-settings"
32
+ ]
33
+ }
34
+ },
35
+ "scripts": {
36
+ "build": "tsc && node scripts/build-client.mjs",
37
+ "prepack": "npm run build"
38
+ },
39
+ "license": "MIT",
40
+ "author": "yuqing",
41
+ "peerDependencies": {
42
+ "@deepseek-ai/cordis": "^4.0.4",
43
+ "@deepseek-ai/dsh-agent": ">=0.1.7-0 || >=0.2.0-0",
44
+ "@deepseek-ai/dsh-client-ui-renderer": ">=0.1.7-0 || >=0.2.0-0",
45
+ "@deepseek-ai/dsh-client-ui-settings": ">=0.1.7-0 || >=0.2.0-0",
46
+ "@deepseek-ai/dsh-llm": ">=0.1.7-0 || >=0.2.0-0",
47
+ "@deepseek-ai/dsh-settings": ">=0.1.7-0 || >=0.2.0-0",
48
+ "@deepseek-ai/schemastery": "^3.18.4",
49
+ "react": "^18.2.0"
50
+ },
51
+ "devDependencies": {
52
+ "@deepseek-ai/cordis": "4.0.4",
53
+ "@deepseek-ai/dsh-agent": "0.2.0-rc.2",
54
+ "@deepseek-ai/dsh-client-ui-renderer": "0.2.0-rc.2",
55
+ "@deepseek-ai/dsh-client-ui-settings": "0.2.0-rc.2",
56
+ "@deepseek-ai/dsh-llm": "0.2.0-rc.2",
57
+ "@deepseek-ai/dsh-settings": "0.2.0-rc.2",
58
+ "@deepseek-ai/schemastery": "3.18.4",
59
+ "@types/node": "^22.0.0",
60
+ "@types/react": "^19.3.0",
61
+ "esbuild": "^0.28.2",
62
+ "typescript": "^7.0.2"
63
+ }
64
+ }