dsh-turn-trace
Verifieddsh-turn-trace · v1.4.8 · MIT · Web UI
Client-only Turns tab for DeepSeek Harness: expand a turn to see its money, expand a step to see what that cost.
Install
dsh plugin add dsh-turn-trace Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-turn-trace
每一轮对话的一张账单。 在 DeepSeek Harness 里新增一个 Turns 页签:展开一轮看这轮花了多少,再展开一步看这一步花了多少。
English: README.md

它做什么
- 轮次是一个列表,默认折叠,每行就是「钱 + 步数」。
- 展开一轮:每一步显示
Step i/n、这一步的 token、这一步的钱。 - 步里面直接列出调用:
llm · model · 6.4s、bash · ok · 41s。
工具本身不消耗 token——一步的 token 永远属于这一步的模型调用,所以钱记在步上,不记在工具上。
为什么不直接用 Trajectory?
Trajectory 回答「什么时候」:事件账本和耗时。Turns 回答「花了多少」:每一步的 token、金额,以及这一步调用了什么。它是增量式的,Chat 和 Trajectory 都留在原位。
播放、墙钟甘特图、跨会话 dashboard 都刻意不做——那些已经有更合适的地方。
安装
需要 DeepSeek Harness 桌面 App(Homebrew cask deepseek-harness),或任何提供 Web UI 的 dsh profile。
# 从 npm 安装
dsh plugin --profile desktop add dsh-turn-trace
# 从本地源码安装
git clone https://github.com/nrenvo/dsh-turn-trace
cd dsh-turn-trace
pnpm install && pnpm build
dsh plugin --profile desktop add link:$PWD
重启 App,打开任意会话,点击 Chat / Trajectory 旁边的 Turns。
界面长什么样

7 秒演示:展开一轮、点开一个调用、打开右侧检查器。
| 层级 | 内容 |
|---|---|
| 顶栏 | 轮数、会话总金额、缓存省下的钱、USD/CNY 切换 |
| 分页条 | 每页 10 轮,第 1 页最新;Older 在需要时拉取下一段历史 |
| 轮次 | 步数、墙钟耗时、金额、缓存省下的钱 |
| 步 | Step i/n、金额、输入/输出 token、缓存省下的钱、最贵一步标记、缓存未命中标记 |
| 调用 | llm:模型 + 耗时 · tool:状态、错误码、耗时、参数摘要 · user:提示词原文 |
| 右侧检查器 | 点任意一行:概述、参数、原始返回、来源 |
文案跟随 DSH 的语言设置(中文 / English),走宿主的 locale 服务,不靠浏览器语言猜。
原理
纯客户端插件。它读界面已经持有的会话快照(useChat → order + nodes),折叠成「轮 → 步 → 金额」。没有 host 插件、没有包内 RPC、没有轮询:会话跑起来时快照本身在更新,视图跟着重算。
计价
每次调用按 DeepSeek 公布的单 token 价格计价,用调用自己的时间戳判断峰/谷(峰时:UTC 01:00–04:00、06:00–10:00 的工作日)。费率写在 src/pricing.ts,只改一处。金额是估算,不是账单。
精度与限制
- 每一步按自己记录的模型 id 计价(先取
finalNode.requestConfig.model,再取providerMetadata.model)。没有模型 id 的步会继承最近的已记录模型,并在顶栏计数;如果整场会话都没记录模型,这一步就保持未计价,而不是猜一个。 - 中国法定节假日没有建模,节假日的会话会略微偏高。
- 只折叠当前已加载的窗口;Older 会走插槽自带的
loadOlder往前翻。
开发
pnpm install
pnpm test # 折叠与计价的断言,不需要起运行时
pnpm test:activation # 启动安全:apply() 不能抛(需要先 build)
pnpm typecheck # tsc --noEmit
pnpm build # tsdown,然后把 lib/client.js 包进 __ModuleLoader__.load(...)
客户端产物通过 window.__ModuleLoader__.load({ id, factory }) 注册;
scripts/wrap-client.mjs 在 CJS 构建之后加这层外壳,浏览器半边因此用的是运行时的
require('react'),而不是自带一份 React。