dsh-token-usage-stats
已验证@lmber/dsh-token-usage-stats · v0.4.5 · MIT · Web 界面
Token usage statistics plugin for DeepSeek Harness: records per-call token usage and shows it in a settings page
安装
dsh plugin add @lmber/dsh-token-usage-stats 用 dsh --profile default --dump-config 确认 layer 已生效 —— 参见安装指南。
源码
标签
作者
说明文档
@lmber/dsh-token-usage-stats
为 DeepSeek Harness 提供 token 用量统计的第三方插件:自动记录每次模型调用的 token 消耗(含速度指标),在设置页提供独立查看界面,并可把账本增量同步到远程聚合服务,实现多设备用量集中统计。
安装不需要改动 DSH 源码,配置持久保存在 $DSH_HOME,重启后继续生效。
功能
- 每次模型调用自动追加一条记录到 JSONL 账本(schema v2:含
deviceId、事件时间戳、durationMs、firstTokenMs、outputTokensPerSec) - 设置页内独立的「Token 使用统计」面板
- 范围筛选:今日 / 昨天 / 前天 / 本周 / 本月 / 全部,按月直接选择(
<input type="month">+ 上/下月快捷按钮),也支持自定义起止日期 - SVG 柱状图展示消耗趋势:按天(近 30 天) / 按月(近 14 个月)切换;无记录的日子显示为基线,不再跳天;悬停柱子弹出精确数值
- 各模型每日/每月用量堆叠趋势图,Top 6 模型 + 其余合并,图例着色
- 自然月历热力图:按真实日历布局显示所选月份的每日消耗,可前后翻月,不再偏移错位
- 饼图展示模型用量占比 Top 10 + 其余合并,图例与悬停提示带百分比;调色板为柔和低饱和 12 色(无刺眼的大红大黄)
- 卡片新增输出速度(tok/s)、平均首字延迟、平均单次耗时;汇总卡片含输入 / 输出 / 缓存命中(缓存写入在当前提供商无数据,未展示)
- 模型输出速度排名面板:按输出速度降序(
总输出 tokens ÷ 总解码耗时聚合,而非各次调用速度的算术平均),附带首字延迟与平均耗时,可直接比较各模型快慢 - 输出速度趋势面板:按天 / 按月展示每个时间段的输出速度(tok/s,同样按总量比值聚合),用于比较不同日期的速度差异
- 多设备远程汇总:可在设置面板内直接配置
remoteUrl与鉴权 token(无需改 cordis 配置);账本增量推送到远程服务(按deviceId隔离存储、可跨设备聚合),面板可切换「本机 / 远程汇总」数据源,并展示同步状态(待同步条数、上次同步时间、失败原因) - 设备标识自动生成 UUID 并持久化,可用配置覆盖
- 大数字自动换算单位(
k / M / B / T),卡片、表格、图例、悬停提示统一缩写 - 进入面板与切换筛选时自动拉取最新数据,无需手动刷新
安装
dsh plugin --profile web add @lmber/dsh-token-usage-stats
然后重启 DSH。插件自带自挂载配置(dsh.bundle.patch),装上即生效,不需要手写任何 cordis 配置。
打开设置,左侧应出现「Token 使用统计」一栏。
卸载
dsh plugin --profile web remove @lmber/dsh-token-usage-stats
重启后插件及其设置页消失。账本文件会保留,需要的话手动删除。
账本位置与格式
默认写入 $DSH_HOME/token-usage-ledger.jsonl(DSH_HOME 未设置时为 ~/.dsh)。
每行一条 JSON 记录(schema v2):
{
"type": "model-call",
"v": 2,
"ts": 1787102662243,
"iso": "2026-08-19T01:24:22.243Z",
"deviceId": "dev-8f2a…",
"sessionId": "session-xxx",
"provider": "deepseek-official",
"model": "deepseek-chat",
"inputTokens": 1000,
"outputTokens": 500,
"cacheReadTokens": 0,
"cacheWriteTokens": 0,
"reasoningTokens": 0,
"totalTokens": 1500,
"durationMs": 4210.5,
"firstTokenMs": 380.2,
"outputTokensPerSec": 92.4,
"workspacePath": "D:/work/repo",
"workspaceTitle": "repo"
}
totalTokens是计费 token 总量:inputTokens + outputTokens + cacheReadTokens + cacheWriteTokens,四项互不重叠,其中inputTokens只含未命中缓存的输入;reasoningTokens已包含在outputTokens内,不重复计入。ts取会话事件自身的time字段(精确到每次调用的完成时刻)。计时指标自 v0.4.3 起按当前 DSH 会话格式推导:每次调用的起点锚定在最近的step/start/request/header(工具循环中会被tool/result/assistant/attempt推进),首字时间取自assistant/message内嵌流(format v2+,text-chunks/reasoning-chunks/tool-call-chunks的首个 token 记录)与assistant/message完成时间的差值,分别得到durationMs(请求发出到完成)与firstTokenMs(首字延迟)、outputTokensPerSec(本次调用的输出速度,outputTokens ÷ (durationMs − firstTokenMs))。旧版本 DSH 的assistant/chunk事件流仍受支持。账本行保留每次调用的原始速度;面板与接口展示的速度是聚合值(Σ输出 tokens ÷ Σ解码耗时,与 DSH 原生会话统计一致),单个「首字几乎等于完成时间」的调用不会拉高整体读数。- v0.2.0 及更早的记录没有
v与速度/设备字段,读取时自动兼容;速度类指标只在有新字段的记录上统计。
账本是纯 JSONL,可以直接用其他工具分析:
# 总调用次数
wc -l < ~/.dsh/token-usage-ledger.jsonl
多设备远程同步
配置(两种方式皆可,设置面板优先)
方式一:设置面板(推荐) — 打开「Token 使用统计」,在「远程同步配置」卡片填写服务地址与可选的鉴权 token,点击「保存配置」。配置立即生效并持久化到 $DSH_HOME/token-usage-config.json(token 输入框留空表示保持不变)。
方式二:cordis 配置(部署时预设)— profile 的 cordis.patch.yml:
- id: token-usage-stats
config:
remoteUrl: "https://stats.example.com"
remoteToken: "your-token" # 可选;与远程服务端 DSH_TS_TOKEN 对应
# deviceId: "my-laptop" # 可选;覆盖自动生成的 UUID
# deviceName: "办公笔记本" # 可选;默认取主机名
# syncIntervalMs: 60000 # 可选;推送间隔,默认 60 秒
- 未配置
remoteUrl时行为与旧版完全一致:仅本地记账与展示。 - 配置后,插件每分钟(或按
syncIntervalMs)把未同步的账本行批量POST到{remoteUrl}/api/v1/ledger/upload,并持久化水位(token-usage-sync.json),失败自动留待下次重试,绝不影响本地记账。 - 设置面板保存的值优先于 cordis 配置,且无需重启即生效;切换
remoteUrl会重新建立推送水位(新目标从头同步)。 - 面板顶部的数据来源开关(「本机 / 远程汇总」)仅在启用远程后出现;远程视图通过本机插件代理查询,浏览器不直连远程,避免 CORS 且统一鉴权。
- 设备 UUID 自动生成并保存在
$DSH_HOME/token-usage-device.json,可用deviceId配置覆盖。
参考服务端
本包附带一个零依赖的 Node 聚合服务(server/index.js),存储按设备隔离(<dataDir>/<deviceId>.jsonl),查询时跨设备聚合。可用于任何一台常开的机器:
node server/index.js
# 环境变量:
# PORT 监听端口(默认 8787)
# DSH_TS_DATA_DIR 数据目录(默认 ./data)
# DSH_TS_TOKEN 鉴权 token;设置后所有请求需带 Authorization: Bearer <token>
服务端接口(与插件本机路由同语义):
POST /api/v1/ledger/upload— 设备推送账本行{ deviceId, deviceName, rows }GET /api/v1/ledger/summary?range=&from=&to=&devices=— 跨设备汇总(devices=all或指定deviceId)GET /api/v1/ledger/series?granularity=&limit=&devices=— 连续窗口时序GET /api/v1/ledger/series-by-model?granularity=&limit=&devices=— 按模型时序GET /api/v1/ledger/devices— 设备列表与各自统计
HTTP 接口
主机端注册了只读路由,浏览器界面通过它取数据,也可以自己调用:
GET /api/token-usage-stats?range=day|yesterday|day-before|week|month|all|YYYY-MM
GET /api/token-usage-stats?from=YYYY-MM-DD&to=YYYY-MM-DD
GET /api/token-usage-stats?source=local|remote
range=YYYY-MM直接选择某个月(如2026-08),from/to按本地时区解析,含首尾两天,两者都可省略。day/yesterday/day-before各覆盖对应那一个本地日;week/month/all从各自起点到当前时刻。非法参数返回 400。source=remote时本机插件代理到远程服务做同样的查询(需要已配置remoteUrl)。
柱状图与模型趋势的数据来自分桶时间序列接口:
GET /api/token-usage-stats/series?granularity=day|hour|month&limit=N&source=
GET /api/token-usage-stats/series-by-model?granularity=day|hour|month&limit=N&source=
- 返回连续窗口:窗口内无记录的桶为
{ tokens: 0, calls: 0 },柱状图因此不跳天。day窗口止于今天,month止于本月,limit只返回最新 N 桶(默认按最早记录起算)。每个桶额外携带avgTokensPerSec(该窗口内所有调用的输出速度,按Σ输出 tokens ÷ Σ解码耗时聚合;无速度数据时为null)。 series-by-model返回{ granularity, buckets, series },所有模型共享同一窗口便于对齐。- 汇总接口的
byProvider/byModel每行额外携带avgOutputTokensPerSec、avgFirstTokenMs、avgDurationMs(仅统计有对应数据的行)。avgOutputTokensPerSec与 DSH 原生会话统计同义:Σ输出 tokens ÷ Σ(durationMs − firstTokenMs),首字延迟与完成时间几乎重合的调用(如纯工具调用响应)不会像"各调用速度算术平均"那样把读数拉高。
设备与同步状态:
GET /api/token-usage-stats/meta
返回 deviceId、deviceName、entryCount 与 sync 状态(enabled、pendingCount、lastSyncAt、lastError)。
远程同步配置(设置面板读写):
GET /api/token-usage-stats/config
PUT /api/token-usage-stats/config # body: { "remoteUrl": "...", "remoteToken": "..." }
GET返回remoteUrl、remoteUrlSet、remoteTokenSet(不含 token 明文)。PUT应用并持久化配置;remoteUrl传空字符串停用远程;不传remoteToken字段表示保持原值。切换remoteUrl会重置推送水位并立即重启同步。
该路由绑定在 DSH web 服务器上,跟随其监听地址(默认仅本机 127.0.0.1)。它没有独立鉴权,与 DSH web 界面本身共享同一信任范围 —— 如果把 DSH 暴露到非本机地址,这份用量数据同样会被暴露。
本地开发
node tests/smoke.mjs # 聚合逻辑冒烟测试(真实账本,无账本时用合成数据)
node tests/integration.mjs # cordis 集成测试:真实运行时内验证记账/路由/远程同步全链路
node server/index.js # 启动参考聚合服务
tests/integration.mjs 在隔离的 cordis Context 中加载插件的 apply(),驱动模拟的 session/event 事件流(既含 format v2+ 的 step/start → request/header → 内嵌流 assistant/message → tool/result 工具循环,也覆盖旧版 assistant/chunk 事件流),断言账本行的时间/速度/路由/工作区字段、HTTP 路由响应,并起一个临时参考服务验证增量推送、水位持久化与远程代理查询。需要本机存在 DSH profile 安装(用于定位 @deepseek-ai/cordis),否则自动跳过。
兼容性
依赖 DSH 的 session/event 事件流、webServer 服务和 settings.section 插槽。DSH 尚未正式发版,这些接口仍可能变动;插件在这些接口变更后可能需要同步更新。
webServer 服务缺失时(例如 headless profile)只记录账本,不注册 HTTP 路由;远程推送在 webServer 缺失时仍然工作(记账与推送不依赖 webServer)。
已知限制
- 统计基于插件安装后产生的记录,装之前的历史调用无法追溯;速度类指标自 v0.4.0 起才有(旧记录显示「—」)。
- 展示的速度是聚合值(
Σ输出 tokens ÷ Σ解码耗时);账本行保留每次调用原始速度(outputTokensPerSec),需要逐次分析时直接读账本。 - 单次调用若「首字时间」记录的流式片段极少(如纯工具调用响应被提供商缓冲到结尾才吐出),其
durationMs − firstTokenMs会非常小、单次outputTokensPerSec会异常高——这是记录本身的真实时序,聚合展示已消除其影响;若需要单次速度请以账本行为准并结合firstTokenMs/durationMs判断。 - 账本只追加不轮转,长期使用会持续增长,需要时自行归档。
- 面板在进入或切换筛选时自动拉取数据(连接打开期间不轮询,需要最新数字可重新进入面板)。
- 远程汇总视图反映的是「已成功推送到服务端」的数据,未推送行在「待同步」计数中可见。
- 热力图默认显示当前月;翻到数据窗口(约 62 天)之外的月份会显示空月。
License
MIT