dsh-plugin-usage
DeepSeek Harness(DSH)Web 端的用量与费用看板插件。 用量数据从本地会话日志全量重建,费用按 DeepSeek 官方峰谷价估算。
A usage & cost dashboard plugin for the DeepSeek Harness Web UI. Usage is rebuilt from the local session logs, and cost is estimated with DeepSeek's official peak/off-peak pricing.
它长什么样
插件在输入框工具条的右侧内联一个小按钮(与「完全权限 / 模型选择 / 上下文计量」同一行),显示今日消费; 点开后在按钮正上方弹出看板:
- 时间维度:今日 / 本周 / 本月 / 全部(本周从周一算起、本月从 1 号算起,均按北京时间)
- 三个指标:消费金额、API 请求次数、token 消耗
- 趋势图:今日/本周看近 7 天,本月/全部看近 30 天
- 明细:可按模型或按 API Key 分组
- 另有
Token页签(未命中 / 命中 / 输出 的构成与占比)与余额页签
为什么不做悬浮窗:早期版本用过全屏浮层和贴边悬浮条,无论停在哪里都会压住对话内容。 现在它注册进 DSH 的
conversation.input.right插槽,属于输入框工具条的一部分,不覆盖任何内容, 只在点开时弹出面板(点面板外或按Esc收起)。
特性
| 模块 | 说明 |
|---|---|
| 全量历史 | 启动时扫描 $DSH_HOME/sessions/**/session.jsonl.zstd 全量重建,包含插件安装之前的用量 |
| 账户余额 | 直连 DeepSeek 官方 /user/balance,区分充值余额与赠送余额;低于 10 元变黄、低于 3 元变红 |
| 消费金额 | 按北京时间自然日 / 周 / 月统计,含峰谷分时计价 |
| API 请求次数 | 同一 turn/step 的多次流式上报只算一次请求 |
| token 明细 | 按「未命中缓存 / 命中缓存 / 输出」拆分并给出占比 |
| 趋势图表 | 近 7 / 30 天每日消费柱状图 |
| 分组明细 | 按模型 / 按 API Key(provider) |
安装
需要 Node.js 20+ 与 DSH 0.1.5 及以上。
git clone https://github.com/1998moye/dsh-plugin-usage.git
dsh plugin --profile web add /path/to/dsh-plugin-usage
# 随后重启 dsh web,并强制刷新浏览器(Ctrl+F5 / Cmd+Shift+R)
也可以直接指向本地开发目录(改动即时生效,重启后加载):
dsh plugin --profile web add /path/to/dsh-plugin-usage
卸载:
dsh plugin --profile web remove dsh-plugin-usage
dsh plugin add会把包名写进 profile 的dsh.profile.bundles。 bundle 清单就是挂载清单 —— 只有node_modules里存在而清单缺失时,插件不会被加载。主进程或 profile 有改动时需要重启 dsh web;只改浏览器端文件时刷新浏览器即可。
计价规则(内置官方定价)
单位:人民币元 / 百万 tokens。来源:DeepSeek 开放平台官方定价页,2026-09-10 12:00 起执行的 Flash 系列峰谷新价。
| 模型 | 计费项 | 空闲时段 | 高峰时段 |
|---|---|---|---|
| deepseek-flash | 输入·缓存命中 | 0.02 | 0.04 |
| deepseek-flash | 输入·缓存未命中 | 1 | 2 |
| deepseek-flash | 输出 | 4 | 8 |
| deepseek-v4-pro | 输入·缓存命中 | 0.15 | 0.3 |
| deepseek-v4-pro | 输入·缓存未命中 | 4.5 | 9 |
| deepseek-v4-pro | 输出 | 13.5 | 27 |
- 高峰时段:北京时间周一至周五 9:00–12:00、14:00–18:00;其余时间与周末全天按空闲价。
- 时区判定基于时间戳整体平移 +8 小时后用
getUTC*读取,不受宿主机时区设置影响。 - 旧模型名与带后缀的变体(
deepseek-v4-flash、deepseek-v4-flash-vision-exp、deepseek-v4.1-flash-expires-on-0910等)由同一模型提供服务,统一按 Flash 价计费。 - 未收录的模型只统计 token、不计费用(宁可少算,也不猜价),并在宿主日志给出一条提示。
价格调整时只需修改 入口/主进程.js 顶部的 定价表。
计费口径
一次请求的 token 拆成三个计费项:
- 未命中 =
inputTokens + cacheWriteTokens(按未命中单价) - 命中 =
cacheReadTokens(按命中单价,Flash 空闲时仅为未命中的 1/50) - 输出 =
outputTokens
数据来源与隐私
- 用量账本落盘于
$DSH_HOME/storages/用量看板.json,只记录数值型 token 与费用,不含提示词、回复正文。 - 余额查询由宿主进程发起,API 密钥取自 DSH 凭据(
DEEPSEEK_API_KEY),不下发到浏览器。 - 除 DeepSeek 官方余额接口外没有任何外部请求。
- 重建每次从零累加,天然幂等,不会重复计费;按文件指纹(大小 + 修改时间)缓存每个文件的折叠结果, 常规刷新只解压正在写入的那一个文件。
- 会话事件(
session/event)只作为「该刷新了」的信号,触发 3 秒防抖重建。
已知边界
- 可统计范围取决于本地日志:日志已被清理的时段无法补出。若官方面板显示的区间大于本地日志覆盖范围,两者对不上是正常的。
- 只统计经过 DSH 的请求;用同一密钥在其他工具里的消耗不计入。
- 费用为按 token 与官方单价估算,与账单实际扣费可能因官方调价、赠送余额抵扣顺序而略有差异。
- 余额查询结果缓存 30 秒。
- 缓存命中率通常很高(实测 98.9%),token 总数主要由命中缓存构成,属真实构成而非重复计数。
- 明细里的「API Key」维度实际是 provider:DSH 会话日志的
request/header.config里没有 Key 字段, 只有provider。同一 provider 即同一份 Key 配置;若多个 Key 共用同一 provider 则无法区分。 - 插件按钮挂在
conversation.input.right插槽上,只在有会话时显示(欢迎页没有输入工具条)。
目录结构
dsh-plugin-usage/
├── package.json # 插件清单:host/client 入口与挂载 patch
├── 挂载配置.yml # profile 挂载条目(cordis patch)
├── 入口/
│ ├── 主进程.js # 重建账本、计价、落盘、只读接口(ESM)
│ ├── 日志重建.js # 会话日志解压与折叠(多帧 zstd),主进程与测试共用
│ └── 浏览器端.js # 输入框工具条内的用量按钮与弹出看板(CJS,无打包器)
└── 测试/
├── 验证主进程.mjs # 计价 / 去重 / 幂等 / 接口,用临时 DSH_HOME
├── 验证重建接入.mjs # 用替身 ctx 跑 apply,核对启动重建与落盘账本
├── 验证浏览器端.cjs # 按 DSH 真实约定加载客户端模块并断言注册
├── 重建账本.mjs # 全量重建报告(不写盘,加 --写入 才保存)
├── 验证接口.mjs # 对运行中的实例做接口联通性验证
└── 诊断页面.mjs # 无头 Chrome + CDP 端到端诊断(真实鼠标事件、命中测试)
文件名使用中文,是为与作者其他项目保持一致的命名习惯;代码与协议标识符均为英文/ASCII。
开发与测试
node 测试/验证主进程.mjs # 计价、去重、幂等、接口
node 测试/验证重建接入.mjs # 启动重建是否真的发生、账本字段是否齐全
node 测试/验证浏览器端.cjs # 客户端模块是否按 DSH 约定注册
node 测试/重建账本.mjs # 全量日志重建报告
node 测试/诊断页面.mjs "<带 token 的页面 URL>" # 真实浏览器渲染诊断(需 Chrome)
覆盖范围:峰谷分段计价、同一 turn:step 重复上报去重、重建幂等、未收录模型不猜价、
多帧 zstd 解压、按文件指纹缓存重建。前两项测试全程使用临时 DSH_HOME,不触碰真实数据。
诊断脚本通过环境变量 CHROME_PATH 指定 Chrome 可执行文件;未设置时会尝试常见安装路径。
实现上的两个坑(值得记一笔)
- DSH 的会话日志是多帧 zstd:每次追加写一小帧,单文件可达数百帧。
Node 的
zstdDecompressSync默认只解第一帧、decodeAll对这类文件也无效, 因此必须按 zstd 魔数(28 b5 2f fd)切帧后逐帧解压。 - 客户端插件的验证必须走真实鼠标事件:
element.click()会绕过命中测试与指针捕获, 曾同时掩盖「被遮罩层挡住」与「指针捕获抢走 click」两类缺陷。 诊断脚本因此改用 CDP 的Input.dispatchMouseEvent,并断言「点击后状态真的变了」。
No comments yet. Be the first to write one.