@yfwu2020/dsh-think-flow
Chain-of-thought panel for the DSH Web right sidebar: folds the live reasoning stream of each session into turn → step groups carrying the reasoning text and the tool calls made in that step. Only the newest turn is expanded by default and earlier turns collapse to one line; steps are grouped into a
Install
dsh plugin --profile web add @yfwu2020/dsh-think-flow
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✓ |
| 2 list | ✓ |
| 3 mount | ✓ |
| 4 boot | — |
Four stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
dsh-think-flow · 思维链可视化
在 DSH Web 的右侧栏开一个「思维链」标签页,
把模型的实时思考按 turn → step →(思考原文 + 工具调用) 聚合成能读的结构:
每一步在干嘛、调了什么工具、它在读哪个文件、哪一段正在等外部接口。





它解决什么问题
DSH 的主对话界面不显示思维链。 而模型的思考恰恰是最值得看的东西:
它决定得对不对、有没有绕路、卡在哪一步。
但直接把思考流倒出来是没法读的。这是真实会话里量出来的一个 turn:
· 实测
一个 turn 的步数 · 53
一个 turn 的时长 · 1550 秒(约 26 分钟)
一个 turn 的思考量 · 7.1 万字
一个 turn 的工具调用 · 68 次
所以这个面板做了五件事,每一件都对应上面某个数字:
- 按 step 聚合,不逐字渲染。reasoning 每秒几百个事件,逐事件重绘会把面板刷成幻灯片。
宿主侧按 step 折叠 + 120ms 合并推送(sseThrottleMs)。
- 分层折叠。53 步全展开要滚几十屏;把"同一类活动"合并成阶段块。默认只展开最新一轮,
那一轮里只展开当前所在阶段;更早的轮次收成一行,点开才看。
例外:实时看过的那一轮不自动收 —— 生成中收起来会把前面那些组缩成一行标题,
生成完再收又会把读到一半的位置顶掉;要收手动点组头。
- 显式画出「在等工具」。实测两步之间能有 47 秒模型零输出(联网检索中)——
不画出来,界面在这 47 秒里与卡死无法区分。
- 轮次可回溯。正文只常驻最近 20 轮,更早的走全轮目录按需折回来;
翻到 100 轮之前也不用把它全塞进内存。
- 思考可以变中文。英文 CoT 按段落机翻(官方 translator 服务,Bing/Google 匿名端点),
翻好一段显示一段;关掉就是原文。见 [中文思考(机翻)](#中文思考机翻)。
长什么样
<table>
<tr>
<td><img src="assets/panel-running.png" alt="正在思考" width="300"></td>
<td><img src="assets/panel-waiting.png" alt="正在等工具" width="300"></td>
</tr>
<tr>
<td align="center"><b>① 正在思考</b><br>当前步整块高亮、呼吸环 + 秒表实时涨;<br>每行右侧容量条 = 那一步的思考 Token 数</td>
<td align="center"><b>② 正在等工具</b><br>唯一的高亮块:<code>web_search 执行 · 已等 23s</code><br>把"在等外部接口"和"卡死"分开</td>
</tr>
</table>
<img src="assets/panel-done.png" alt="本轮完成:阶段分布小结" width="380">
本轮结束后补一块「这一轮想完了」:多少步、思考多少 Token、调了多少次工具,以及
思考花在哪了(读代码 / 跑命令 / 改文件 / 外部检索 / 问用户的比例)。
分布用同一色相只变深浅,不给每个阶段编一个颜色。
<table>
<tr>
<td><img src="assets/panel-expanded.png" alt="展开某一步" width="300"></td>
<td><img src="assets/panel-directory.png" alt="全轮目录" width="300"></td>
</tr>
<tr>
<td align="center"><b>③ 展开某一步</b><br>模型标题 → 次级标题(按工具参数算的派生标题)<br>→ 工具详情 → 按需取回的思考原文</td>
<td align="center"><b>④ 全轮目录</b><br>第二行标题<b>双击</b>就地变搜索框;<br>12 轮、100 轮都在这儿翻,搜索全在本地算</td>
</tr>
</table>
<img src="assets/panel-failure.png" alt="工具失败" width="380">
工具失败照宿主的判定显示(isError + error.code):行里一枚红标,
为什么失败在展开区紧跟它自己那条工具行(上图 ✗ INVALID_ARGS …)。
红只给错误文本,工具名不动 —— 失败的是这次调用,不是这个工具。
上面这些图不是另画的示意图:由 npm run shots 用构建产物里真正的组件渲染、
配宿主主题包里整段抓出来的真 token、headless Chrome 拍下来,宽度就是右侧栏真实的 380px。
数据来自 scripts/preview/demo-data.mjs,是合成的一段会话。
安装
# 从 npm(推荐) dsh plugin --profile web add @yfwu2020/dsh-think-flow # 或从 GitHub Release 的 tgz(同一个产物,不走 npm) dsh plugin --profile web add /path/to/yfwu2020-dsh-think-flow-0.3.3.tgz # 或从本地目录(开发用:改完不用重装,只重建) dsh plugin --profile web add link:/path/to/dsh-think-flow
装完在 DSH 里落成三处:
位置 · 内容
~/.dsh/profiles/web/package.json · "@yfwu2020/dsh-think-flow": "<版本或 link:路径>"
~/.dsh/profiles/web/node_modules/@yfwu2020/dsh-think-flow · 软链 → 插件目录
profile 的 dsh.profile.bundles · 包名(按包名引用,与路径无关)
打开方式有两个:会话头部那个折线图标,或者右侧栏标签条上的「思维链」。
⚠️ 装完要重建进程:host 半是新的 bundle,需要重启 dsh web,之后硬刷新浏览器
(Cmd/Ctrl+Shift+R)。此后只改 client 半的改动会被 client-hmr 就地替换,不用刷新。
要求:DSH 运行时 >=0.1.7-rc(见 package.json 的 peerDependencies)。
0.1.7-rc 起 createSystemMessage 去掉了插件署名参数;更旧的运行时上行为不一致。
⚠️ npm 上@deepseek-ai/*这些包的latest还停在更旧的0.0.1-rc.x,缺WebServer
等 API —— 从源码构建时按package.json里钉的版本装,别用latest。
配置项
改 profile 的 cordis.patch.yml 里 id: think-flow 那一行的 config:(覆盖,不要再 insert):
- id: think-flow
config:
maxTurnsPerSession: 20
键 · 默认 · 范围 · 含义
maxTurnsPerSession · 20 · 0–500 · 每个会话常驻多少轮的正文(0 = 不限)。⚠️ 这是正文窗口,不是"能看到多少轮"
maxIndexTurns · 2000 · 0–20000 · 目录(全轮骨架)最多记多少轮。一轮骨架几百字节,所以给得宽
coldTtlMs · 7200000 · 0–86400000 · 按需折进来的冷轮正文保留多久(空闲计时:读一次续一次期。0 = 不按时间收)
maxColdChars · 4000000 · 0–2e8 · 冷轮正文的硬上限。和 coldTtlMs 是两条独立策略,谁先到算谁
maxReasoningCharsPerStep · 120000 · 1000–400000 · 单个 step 的思考原文上限,超了从尾部保留并打上"这段不全"
maxSessions · 32 · 1–256 · 内存里同时跟踪多少个会话(LRU)
resyncSilenceMs · 15000 · 20–600000 · 内存轨迹多久没收到实时动静就算"可疑"(也是重折退避的基数,见下)
resyncMaxGapMs · 300000 · 0–3600000 · 重折退避的上限(0 = 关掉过期自愈)。不是"最多重折几次"—— 见下面的说明
sseThrottleMs · 120 · 0–2000 · 合并推送的最小间隔。别调到 0:流式每秒几百个事件
heartbeatMs · 15000 · 2000–120000 · SSE 心跳,防代理掐连接
snapshotTailChars · 4000 · 200–20000 · 连接快照里给"当前步"带多少字符原文(其余步骤走 /step 按需取)
titleStepChars · 420 · 80–4000 · 生成中文标题时,每一步最多喂多少字思考原文
titleTotalChars · 24000 · 2000–200000 · 一次标题请求的字符上限(整轮一起总结,一次调用)
titleReasoningEffort · 'low' · — · 生成标题的推理档位(压缩任务,低档就够)
titleCachePath · '' · — · 标题缓存路径。空 = ~/.dsh/think-flow/titles.json
autoTickMs · 5000 · 20–60000 · 自动标题(面板上的「实时」)的节拍器间隔
maxHydrateEvents · 20000 · 0–200000 · 历史回看时最多折入多少条落盘事件(从最新往回取)
maxTitledTurns · 640 · 0–10000 · 标题缓存最多保留多少轮
translateTarget · 'zh-CN' · — · 思考原文机翻的目标语言
translateProvider · '' · — · 机翻提供方(bing / google)。空 = 跟随 translator 服务自身的配置(默认 bing)。付费路由要写会话日志,本插件对会话只读,别用
translateMaxChars · 3600 · 200–4000 · 单个翻译请求的字符上限(再和 translator 的 maxTextChars 取小)
translateTimeoutMs · 20000 · 1000–120000 · 单次翻译请求超时
translateCachePath · '' · — · 译文缓存路径。空 = ~/.dsh/think-flow/translations.json
maxTranslationEntries · 4000 · 0–100000 · 译文缓存最多保留多少条(一段一条,LRU)
三条策略要一起看(它们管的是同一块内存):
turns(正文) maxTurnsPerSession = 20 ← 常驻
└ 更早的轮次按需折进来 = 冷轮正文
├ coldTtlMs = 7,200,000 空闲 2 小时收回
└ maxColdC
…(truncated)Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::8,最后更新 2026-10-24