dsh-cost-ledger
给 DeepSeek Harness 的可核对 token 成本账:每次模型调用记下 provider / model / 输入 / 输出 / 缓存命中 token 和按本地价目表算出的 CNY;按小时、日、会话三档聚合;支持 JSON / CSV 导出与 dry-run 预览;并把你手填的官方账单数字并列成一列,直接看「我算的」和「平台收的」差多少。
它不画充能动画,也不猜余额。它只做一件事:把账留下来,并且让估算和账单事实分得开。
灵感来源与独立实现
灵感来自 wssfk12138/dsh-damage-pulse(MIT):它把 token 消耗做成鲸鱼娘充能/受击动画,围绕余额、峰谷价、预算提醒和微信通知做了很完整的可视化。
本仓库是独立实现,不是 fork:
- 没有
gh repo fork,没有 clone 原仓库后修改,没有复制它的源码、图片、字体或文案。
- 只借鉴了「模型调用有成本、应该被看见」这个问题意识。
- 官方价格、峰谷规则和 2026 年法定节假日是公开事实,单独标注来源(见下文),不来自原版代码。
- 原版是 MIT;本项目也是 MIT。两者可以各自独立安装、互不依赖。
和原版的具体差异
| dsh-damage-pulse | dsh-cost-ledger |
|---|
| 核心目标 | 余额监控 + 充能动画 + 预算/峰谷/微信提醒 | 逐次记录 + 导出 + 与实际账单对账 |
| 捕获点 | 从会话事件(session/event)收用量记录 | llm/stream waterfall 包住每次流式调用,读 adapter 的 usage chunk |
| 账本 | 用量/余额体系,含会话投影与可视化明细窗口 | append-only JSONL,一行一次调用;费用在调用时冻结 |
| 导出 | README 功能表未见 CLI 级 JSON/CSV 导出 | `export --json |
| 实际账单 | README 功能表未见「手填官方账单 vs 本地估算」差异列 | 手填 hour/day/session 账单;导出/面板并列 actual_cny 与 diff_cny |
| 未定价处理 | 有 provider 资格判定与可编辑计费规则 | 只认显式 provider+model;未知路线计入 unpriced,绝不套用相似模型价 |
| UI | 鲸鱼娘待机/扣费/复苏动画、悬浮卡 | 固定的成本表 + 一个很轻的「记录中」呼吸点,没有角色动画 |
| 余额/通知 | 官方余额校准、预算/峰谷提醒、ClawBot 微信通知 | 不查余额、不发送通知 |
| 依赖 | 完整插件工程 | 运行时/构建期均零依赖;node --test 直接跑 |
说明:原版很强,以上不是「它做不到」的绝对判断,而是按它公开 README 功能表和本项目实现范围做出的差异说明;我没有找到原版提供 CLI JSON/CSV 导出和手填账单差异列的证据,所以本插件补的是这两块,而不是重做它的动画和余额。
它记录什么
每次 llm/stream 流结束时写一行:
- 时间、
hour / day(默认北京时间)、session(会话 id;没有会话的插件调用记为 purpose:<purpose> 或 unattributed)
provider、model、purpose
inputTokens(缓存未命中输入)、outputTokens、cacheHitTokens、cacheWriteTokens、reasoningTokens
usageSource:adapter(adapter 上报的 usage)/ estimated(adapter 没给 usage,按字符比例估算)/ missing
- 命中的价目表条目、峰/谷档位、四个分项费用与总费用,单位是整数 nano-CNY(1 CNY = 10⁹ nano-CNY,聚合时不会浮点漂移)
status / finish / ms / error
不记录 prompt 正文、回复正文、工具参数内容或任何凭据。 账本里只有计费元数据。
两种数字,永不混写
- 本地估算:
adapter 上报的 token × prices 文件里的费率。没有 usage 时,用官方文档给出的字符比例做估算,并明确标成 estimated。
- 实际账单:你在面板或 CLI 里手填的平台金额。它只进入
actual_cny 列,不覆盖估算。
- 差异:
diff = actual − estimated。正数表示平台收得比本地估算多。没有手填账单的时段留空,不补零、不猜测。
安装
从 GitHub 装(当前没有 npm 发布):
dsh plugin --profile <你的 profile> add github:dingchenhui0618-arch/dsh-cost-ledger
也可以从本地目录装(开发时):
dsh plugin --profile <你的 profile> add D:\Projects\dsh-cost-ledger
装好后重启对应的 profile。对话视图里会出现「成本账」Tab。
说明:这是新插件,目前只在开发机上跑过测试和构建;没有宣称在每一个 DSH 版本上都验证过。遇到 slot 契约变化时,面板会降级消失,但 host 端的记录仍会继续。
面板
对话视图 →「成本账」:
- 本地估算合计 / 日账单合计 / 差异 / 调用数 / 缓存命中 tokens / 未定价调用
- 按小时、日、会话切换聚合;每个时段显示调用数、输入、输出、缓存命中、估算、实际账单、差异
- 「填账单 / 改账单」:把手填金额、备注写到
cost-ledger-bills.json
- 最近调用明细:model、会话、token、费用、用量来源、状态
- 导出 JSON / CSV、dry-run 预览、重载价目表
- 价目表来源、版本、文件路径、模型条目数
表格只展示元数据,不含 prompt 正文。
价目表
默认读取 $DSH_HOME/cost-ledger-prices.json;不存在时使用内置表。
内置表只覆盖 DeepSeek 官方 provider(deepseek-official、deepseek-account)的公开价格:
中转 provider 不会被自动套官方价。 例如 chenyuzhiyun(TokenRouter)或 opencodex 的 model 名即使叫 deepseek-flash,只要没有显式条目,就记为未定价、费用 0,并在面板/导出里显示 unpriced。这比「看起来算出来了但其实是错的」更诚实。
自定义价目表结构(examples/prices.example.json 有完整文件):
{
"version": "my-rates-2026-10",
"timeZone": "Asia/Shanghai",
"peakHours": [],
"holidays": [],
"models": {
"chenyuzhiyun": {
"deepseek-flash": {
"offPeak": { "input": 1, "output": 4, "cacheHit": 0.02 }
}
},
"opencodex": {
"gpt-5.5": {
"offPeak": { "input": 10, "output": 30, "cacheHit": 1 }
}
}
}
}
规则:
- 先按
provider 精确匹配;没有该 provider 组时才尝试 "*" 通配组。
- model 先精确匹配,再匹配最长的
name- 前缀(兼容 deepseek-flash-20261017 这类带日期后缀的 id)。
- 不会跨 model 或跨 provider 猜价格。 未命中就是未定价。
- 自定义高峰价时给条目加
peak: { input, output, cacheHit, cacheWrite },并配置 peakHours / holidays。
- 改价目表不会回写历史记录;旧记录保留调用时冻结的费率与费用。
如果价目表 JSON 损坏,host 会回退到内置表,并在面板上显示错误,不静默假装正常。
导出与 dry-run
面板里有「导出 JSON / 导出 CSV / dry-run 预览」。命令行等价:
# 默认按日聚合,JSON 到 stdout
node bin/cost-ledger.mjs export --json --scope day
# CSV 写到文件
node bin/cost-ledger.mjs export --csv --scope session --out ledger.csv
# 逐次调用明细
node bin/cost-ledger.mjs export --json --detail
# 只预览,不写任何文件
node bin/cost-ledger.mjs export --dry-run --scope hour
# 时间筛选(北京时间日期边界,含首尾)
node bin/cost-ledger.mjs export --json --since 2026-10-18 --until 2026-10-19
装成 npm bin 后命令名是 dsh-cost-ledger;在仓库内直接跑 node bin/cost-ledger.mjs 即可。
聚合导出列(节选):
scope,period,calls,priced_calls,unpriced_calls,adapter_calls,estimated_calls,
input_tokens,output_tokens,cache_hit_tokens,cache_write_tokens,
estimated_nano_cny,estimated_cny,actual_nano_cny,actual_cny,diff_nano_cny,diff_cny,diff_pct,...
明细导出列(节选):
id,at,at_iso,hour,day,session,provider,model,usage_source,status,
input_tokens,output_tokens,cache_hit_tokens,cache_write_tokens,
price_tier,price_model,priced,cost_nano_cny,cost_cny,...
CSV 会中和以 = + - @ 开头的字符串,避免在表格软件里被当成公式执行。
手填账单
node bin/cost-ledger.mjs bill set --scope day --key 2026-10-19 --amount 12.34 --note "平台账单页"
node bin/cost-ledger.mjs bill list
node bin/cost-ledger.mjs bill delete --scope day --key 2026-10-19
key 的口径:
hour: YYYY-MM-DDTHH(北京时间)
day: YYYY-MM-DD(北京时间)
session: 会话 id,如 session-...
数据文件
| 文件 | 默认路径 | 说明 |
|---|
| 调用账本 | $DSH_HOME/cost-ledger.jsonl | append-only,一行一次流式调用 |
| 手填账单 | $DSH_HOME/cost-ledger-bills.json | 用户输入的官方金额与备注 |
| 价目表 | $DSH_HOME/cost-ledger-prices.json | 可选;不存在时用内置官方表 |
账本不会自动轮转/删除;需要归档时自行备份后截断。所有文件都在本机,插件不联网。
边界
- 不是官方账单。 本地估算是「token × 你认可的价目表」,不是平台结算凭据;平台改价、优惠、赠送余额、缓存计费差异都可能让两者不同。差异列正是用来暴露这些不同的。
- 不评估模型质量。 这里只有成本元数据;没有正确率、延迟或偏好判断。
- 不查余额、不发送通知、不做动画。 这些是原版的强项,本插件不重复。
- usage 缺失时只能估算。 有些 adapter 不上报 usage,此时按字符比例估算并标
estimated;不要把估算当 adapter 事实。
- 不是安全边界。 账本文件在用户目录,任何同权限进程都能改;它用于核对,不用于防篡改。
- 价目表和节假日会过期。 内置表是 2026-10-19 抓取的公开价格;之后改价请自行更新
prices.json。法定节假日目前只内置 2026 年,其他年份请补 holidays。
开发
node scripts/build.mjs # 零依赖构建:lib/index.js、lib/client.js 与 host 模块
node --test # 48 个测试
测试覆盖:价目表/峰谷/节假日、token 估算、nano-CNY 金额解析、三档聚合、账单差异、CSV 公式注入、JSONL 容错、host 的 llm/stream 捕获、错误/中断/未定价、路由与卸载、CLI 导出/dry-run/账单命令、client 的 conversation.view 注册与 CSS 类覆盖。
lib/ 是入库的构建产物;改 src/ 后重新 build 再提交。
License
MIT。原版 dsh-damage-pulse 也是 MIT;本项目未复制其源码或视觉资产。官方价格页与节假日通知仅作为公开事实引用。