🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

Token 用量统计 mathangler/dsh-token-stats

DeepSeek Harnessv0.2.0
mathangler13 天前有提交已提供中文文档扫描:含敏感能力

DeepSeek Harness 的 Token 用量统计插件,挂在设置面板里作为一个独立页面。

30/100
综合分
0
GitHub 星标
—
npm 周下载
—/5
用户评分

数据截至 2026/11/3 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

自动兼容检查有告警:npm 同名包「dsh-token-stats」归属 h1a3x/dsh-token-stats,装到的可能不是本插件。
安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add mathangler/dsh-token-stats
该包不可直接 npm 安装,改从 GitHub 源安装(明细见下方安装检查)

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · model
装得上吗
本站已在 dsh 0.2.1-rc.5 下真实安装成功
非静态推断
L5 · 运行时冒烟等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 13 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查⚠ 装前注意
要求 Node:>=20(dsh 本身要求 Node ≥ 22.19)

npm 同名包「dsh-token-stats」归属 h1a3x/dsh-token-stats,装到的可能不是本插件

展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-token-stats @ 0.2.0
✓Node 引擎要求 >=20 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/10/7 19:26:47

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件发起外部网络请求
查看扫描证据(共 5 处,展示前 5 条)
  • 读取本地文件
    lib/host-core.js:762
    const parsed = JSON.parse(await readFile(path, 'utf8'));
  • 写入本地文件
    lib/host-core.js:836
    await mkdir(dirname(state.cachePath), { recursive: true });
  • 写入本地文件
    lib/host-core.js:837
    await writeFile(temporary, body, 'utf8');
  • 写入本地文件
    lib/host-core.js:838
    await rename(temporary, state.cachePath);
  • 发起网络请求
    lib/client.js:2023
    pending = fetch(CHANNEL + '/' + method, {
扫描于 2026/11/4(0 天前) · 范围 入口闭包 + 发布文件清单 · 来源 GitHub 仓库

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

功能亮点

  • 设置页展示用量
  • 贡献热力图
  • 模型趋势与占比

DeepSeek Harness 的 token 用量统计:一个设置页,带贡献热力图、各模型趋势和模型占比。

README

由 DeepSeek 最新模型翻译生成
dsh-token-stats DeepSeek Harness 的 Token 用量统计插件,挂在设置面板里作为一个独立页面。 它读取本机已有的持久会话日志,把你平时只能靠猜的数字算出来。 展示内容 | 区块 | 说明 | | 看板 | 六个数字:累计用量、单日峰值(带日期)、今日用量、近 7 天、日均用量(按活跃天)、缓存读占总量比例 | | 每日 | GitHub 贡献图式热力图,总以今天结尾:固定 18 周、26px 方格,在卡片里居中。窗口刻意不再由卡片宽度推导 —— 那会让版块在宽屏上膨胀、在窄卡片里把方格压小;卡片窄于这个版块时只把方格缩到刚好放下 18 列。颜色深浅是连续的(见下文)。尚未发生的日期保留虚线格 | | 每周 | 每周一根柱,与每日网格是同一个版块:同样的窗口、列距、宽高,逐列对齐;每根柱在自己的列内两侧留白,所以相邻两周读起来是两根独立的柱子 | | 趋势 | 每个模型一条平滑折线。横轴粒度随范围自适应:一个月内按天、半年内按周、两年内按月、更久按年 | | 占比 | 环状图展示所选范围内各模型占比,配完整图例,中心显示该范围合计 | 往前翻看更早的历史 日粒度版块只有一个固定窗口,所以窗口可以移动:标题右侧的两个箭头按「一整个窗口」为步长前后翻页,中间的标签写明当前显示的是哪一段。
展开完整文档(README 剩余 1 节)
dsh-token-stats

DeepSeek Harness 的 Token 用量统计插件,挂在设置面板里作为一个独立页面。

它读取本机已有的持久会话日志,把你平时只能靠猜的数字算出来。

展示内容

| 区块 | 说明 |
| 看板 | 六个数字:累计用量、单日峰值(带日期)、今日用量、近 7 天、日均用量(按活跃天)、缓存读占总量比例 |
| 每日 | GitHub 贡献图式热力图,总以今天结尾:固定 18 周、26px 方格,在卡片里居中。窗口刻意不再由卡片宽度推导 —— 那会让版块在宽屏上膨胀、在窄卡片里把方格压小;卡片窄于这个版块时只把方格缩到刚好放下 18 列。颜色深浅是连续的(见下文)。尚未发生的日期保留虚线格 |
| 每周 | 每周一根柱,与每日网格是同一个版块:同样的窗口、列距、宽高,逐列对齐;每根柱在自己的列内两侧留白,所以相邻两周读起来是两根独立的柱子 |
| 趋势 | 每个模型一条平滑折线。横轴粒度随范围自适应:一个月内按天、半年内按周、两年内按月、更久按年 |
| 占比 | 环状图展示所选范围内各模型占比,配完整图例,中心显示该范围合计 |

往前翻看更早的历史

日粒度版块只有一个固定窗口,所以窗口可以移动:标题右侧的两个箭头按「一整个窗口」为步长前后翻页,中间的标签写明当前显示的是哪一段。每日网格和每周柱状图一起翻 —— 它们本来就是同一个版块的两个视图,翻页对当前视图生效。

箭头只在该方向确实有用量时才可以点;不可点时是变淡而不是消失,这样控件不会在指针下跳动,也不会掩盖"那个方向还有历史"这件事。翻页会跳过当前口径下没有用量的整段,而不是让用户一页页翻空窗口 —— 页面集合由数据本身算出,所以永远不会落在空白页上。

控件始终可见,包括全部历史都装得进一个窗口时。这时网格仍然是「到今天为止的最近 18 周」,两个箭头都是变淡且点不动 —— 这是有意为之:让人在还没有历史可翻的时候就知道有这个功能,同时标签一直写明当前显示的是哪一段(一旦窗口可以移动,这一点就不再是显然的)。箭头只在该方向确实有用量可翻时才变亮可点。

一个范围选择器同时驱动趋势图与占比图 —— 两者在同一张卡片内,作用域只声明一次。

指标切换器同时驱动热力图、趋势图和环图:

* 总量 —— 未缓存输入 + 输出 + 缓存读 + 缓存写,与提供方上报的 totalTokens 完全一致。
* 输入+输出 —— 不含缓存读的窄口径。热会话里缓存读占绝对多数(开发这台机器上是 98.8%),所以两个口径并列提供而不混合。
* 输入、输出、缓存读、缓存写 —— 最后一项只在本环境真的上报缓存写时才出现;当前挂载的 provider 都不上报。

所有展示数字按当前语言使用单位:中文为万 / 亿,英文为 K / M / B。

安装

dsh plugin --profile web add github:mathangler/dsh-token-stats

装完重启正在运行的 dsh web 进程。

补丁会往 profile 里插一行宿主半边。浏览器半边由 package.json 里的 dsh.client.platform: web 声明自动加载。开发与验证基于 DSH 0.1.5-rc.1。

面板跟随宿主语言:所有文案都以中英文注册到 locale 服务,切换语言会即时重绘。
插件在插件清单里的条目也一样 —— locale/en.json 与 locale/zh.json 提供它的标题和说明,宿主不激活插件就能从导出的资源里读到,所以即使插件处于禁用状态,那一行也是翻译过的。本 profile 的语言偏好是 zh,正好对应 locale/zh.json;英文文件同时是其它语言的兜底。图标沿用本 profile 其它插件的同一套样式 —— 36×36 圆角渐变底 + 白色 1.6px 线条,图形是贡献网格,也就是这个插件本来要画的东西。

刷新会立即返回,并在后台重新扫描,这期间屏幕上保留原有数字。宿主的 sessionQuery 提供 observeSession 时,正在运行的会话通过该观测读取;不提供时插件依然可用,只是改为整份读取会话日志。

要更新已安装的副本,必须先移除再安装 —— spec 不变时 pnpm 不会重新解析 git HEAD:

dsh plugin --profile web remove dsh-token-stats
dsh plugin --profile web add github:mathangler/dsh-token-stats

在网络受限的环境里 github.com 本身可能不可达,但安装仍然可用:
pnpm 是从 codeload.github.com 取 tarball 的,那是另一个主机。

数字是怎么来的

Harness 只把 token 用量记录在一种持久事件上:assistant/message 的 data.usage;生产这条消息的 provider/model 就在同一事件的 data.message.source。也就是说,一条 assistant 消息是唯一同时携带用量样本和归属路由的原子单位 —— 这就是折叠按消息级而不是按 turn 级进行的原因。

* 尝试槽位:一次计费尝试可能产生多条 assistant/message 样本。样本以 (turn, step, epoch) 为键,epoch 在 llm/retry-started 和 assistant/attempt 上递增;同一槽位内后到的样本替换先到的,因此重试会被再次计费,而流式样本不会被重复计数。
* 分叉会话:带 seed 的会话日志把父会话的事件作为前缀包含在内。前缀长度 inheritedEventCount 会被跳过,分叉永远不会被计两次。
* 天的边界跟随本机本地时区。
* 排名按总量排序;超过 8 个模型时,尾部会合并成一个「其他模型」分片而不是被丢弃。
* 已移除的会话继续计数:从列表里消失的会话保留它最后一次已知的用量 —— 一个会因为清理历史而变小的「累计」数字毫无意义。脚注会显示这类会话的数量。把 retainSessions 设为 false 则只统计仍在列表里的日志(此时总量会随删除而减少)。
* 读不了的会话不会被吞掉:被读路径拒绝的日志会计入并在脚注的 hover 里给出原因 ——因为少读一个会话会让所有总量偏小。

平台自带的 deriveTurnTokenUsage() 不是数据来源。它是 turn 级的,无法把用量归到某个模型,而且任何包含 compaction 的 turn 都返回 undefined。在开发这台机器上,它只统计到 294.7M token,而本插件的折叠统计到 340.2M —— 它悄悄丢掉了约 13% 的真实用量。因此它只作为测试里的交叉校验:在它能推导的每个 turn 上,本插件的折叠必须四个桶全部完全一致。

颜色标尺

热力图以当前视图内最忙的一天为锚点,用幂律映射: 不透明度 = 0.15 + 0.85 * (value / max) ** 0.45。三种候选都在真实数据上量过:

| 当天用量 | 线性 | 对数 | 幂律 0.45 |
| 8,885 | 0.150 | 0.200 | 0.158 |
| 14,963,448 | 0.198 | 0.777 | 0.383 |
| 25,885,348 | 0.233 | 0.819 | 0.449 |
| 111,566,640 | 0.508 | 0.933 | 0.726 |
| 264,785,241 | 1.000 | 1.000 | 1.000 |

* 线性让普通日子变得无法区分:15M 与 26M 只差 0.035。
* 对数让忙碌的日子变得无法区分:111M 与 265M(相差 1.53 亿)只差 0.067,也就是看不出来 —— 它恰好压平了重度用户最关心的区间。
* 幂律 0.45 两者都能分开:最忙两天差 0.274,普通两天差 0.065。

指数是命名常量 INTENSITY_GAMMA:调小则高位对比更强,调大则低位更可分。任何单一标尺都有固有代价:出现极端离群值时,其余日子会被推向底部。

性能与缓存

一个会话日志的成本只在于「解码」,其他部分可以忽略。 在真实语料上实测(36 个会话、190 个已知 id、其中一个是 14MB 日志):

| 操作 | 14MB 日志 | 5MB 日志 |
| persistence.open() —— zstd 解码 | 1062 毫秒 | 435 毫秒 |
| handle.read() —— 解析事件 | 16 毫秒 | 14 毫秒 |
| foldSession 折叠 18151 个事件 | 10 毫秒 | — |
| 再次 open()(命中后端 memo) | 6 毫秒 | 11 毫秒 |
| query.readSession(校验读:重放整个日志) | 11293 毫秒 | 4456 毫秒 |

所以少读一点日志省不到钱(解码不可避免,而且高两个数量级),但不打开日志能省掉全部成本。下面每一条策略都是从这个事实推出来的。

* 首先读平台的 tokenUsage 投影 —— 但不是走它的 view。dsh-token-meter 注册了投影单元,dsh-session-projection-cache 在每个 turn 边界把它落盘检查点。也就是说平台早就把每个会话的 provider 上报总量折叠并存好了。但通过注册表那套"受支持"的读法取不回来: viewCheckpoint(以及建立在它之上的 cachedSnapshot)只服务声明了 wire view 的单元,而 tokenUsage 没有声明 —— 实测对这个 key 它们什么都不返回,可磁盘上的行是完整的。那一行是文档化的 ProjectionCheckpointRow({ ver, seq, val },val 为 { totals, last }),所以直接读它并做形状校验。这正是"日志读不出来的会话仍然按平台总量计入"的依据 —— 否则它会从一个平台还算得出来的「累计用量」里凭空消失。
* 请求路径上永不解码日志:请求只答宿主已经维护好的状态。刚启动的宿主收到第一个请求时,以前会阻塞等完一整轮扫描 —— 实测 4 秒的列表要 4014 毫秒、9 秒的要 9014 毫秒,而这恰好是用户正盯着看的时刻。现在它立刻回「正在预热」,让面板自己补拉。
* 一轮扫描最多只付 MAX_DECODES_PER_SCAN(2)次解码,另有 1 秒预算和 20 秒上限。
单靠时间预算约束不住:它在解码之前检查,而一次冷解码(1062 毫秒)比整个预算还长 ——真实环境里一次读 4 个会话的扫描因此花了 7.4 秒。直接数解码次数才能真正兜住最坏情况。
* 一轮扫描留下的工作由它自己排队继续:光有上限不够 —— 剩下的会话就只会在「有人要求扫描」时才推进,而面板开着、聚合结果还在 TTL 内时没有任何东西在要求扫描。实测冷启动后热力图会无限期停在 15 天里的 8 天、9 个模型里的 1 个。现在每一轮仍然有上限、都不碰请求,所以冷语料会在几秒内于后台自己补齐。
* 被延后的会话会被回头处理,而它的总量当下就是对的:一轮扫描没有预算处理的会话,仍然报告平台给的精确总量(挂在未归属模型、会话创建那天),所以「累计用量」永远不是占位数据;只有日历位置是,而条目用 placed 记下了这一点,后面某一轮会回来处理。把「被延后的条目」当成「已经拆好的拆分」,正是让部分热力图变成永久状态的错误。
* 只沿用真正的拆分:当平台该会话的记录自上次拆分以来没有移动时,这个拆分就被证明仍然有效 —— 因为日志的用量内容正是产生那行记录的东西。这个判断只要一次比较,取代了以前算 marker 所需的 stat/observeSession —— 而后者对活跃会话就是对 13MB 日志做一次 observeSession,每次刷新都付,只为得出「什么都没变」。
* 每个会话只 open 一次,不是两次:事件和继承前缀长度从同一个 handle 一起取回,因为冷 memo 上的第二次 open 就是又一次完整解码。
* 已封闭会话走持久化 handle,而不是 readSession:readSession 返回的是校验过的 快照 —— 它要重放整个日志来证明格式正确,这在恢复会话时是对的,在这里则贵了 8 倍(同一个 14MB 日志 11293 毫秒 对 1355 毫秒,总量完全一致)。
* 落盘状态不再因为「旧」而被丢弃:以前超过 24 小时就整份作废,于是每个会话的拆分(包括已删除会话的历史 —— 那些数据别处不存在)都被扔掉,还得从头冷推一遍。现在改为逐会话与平台自己的记录对账,所以「文件的年龄」不说明任何问题,而过期只会白白毁掉已完成的工作。
* rollup 的格式版本不是删除键:v1 仍然会被读取 —— 那些日志已经不存在的会话,其按天/按模型贡献无法从任何地方重新推导,所以一次「升版本就丢文件」会让累计用量缩水。
* 扫描失败会如实报告,而不是让 stale 永远为真:以前被拒绝的后台扫描被吞掉,而 stale 是照请求重算的,于是它一直为真,面板会为一个早已死掉的扫描轮询两分钟 —— 这就是「刷新失败」的来源。现在失败会被记下、stale 转为 false、原因随数字一起返回,并有冷却期避免重试风暴。
* listSessions 有超时:它是一轮扫描里唯一可能挂住的调用,而组合里没有任何东西接受中断信号;一个永不返回的列表以前会让请求无限挂住。
* 连按刷新只花一次扫描:落在刚完成那次扫描之后 DEFAULT_REFRESH_FLOOR_MS (600 毫秒)以内的按键直接由那次扫描作答,并且既不算 stale 也不算 scanning。它是「重复扫描的下限」而不是结果缓存,而且是 opt-in 的:插件自己打开它,直接调用核心的调用方除非主动要求,否则仍然拿到保证全新的扫描。
* 最后一次聚合结果和 rollup 一起落盘:宿主刚重启后的第一次打开也能直接从磁盘作答。

在插件自己的路由上端到端实测(同一份语料):

| | 优化前 | 现在 |
| 启动后第一次刷新(落盘状态缺失) | 阻塞 4–9 秒 | 5 毫秒(回「预热中」),随后在后台补齐 |
| 再按一次刷新 | 每按一次 4–5 秒 | 5–10 毫秒,且 read 0 |
| 冷启动(直到每个会话都定位) | 约 20 秒日志读取 | 约 3 秒的后台分批补齐 |
| 重启(磁盘上有 rollup) | 31.4 秒 | 3 毫秒 |
| 打开面板(聚合已缓存) | 66–83 毫秒 | 5–47 毫秒 |

稳定态的刷新完全不解码任何日志(read 0, reused 36),剩下的只是一次 listSessions。
稳定态下连按刷新时显示的总量不变,这是正常的:只有会话日志增长时数字才会动。

这些数字不是近似平台:每个会话折叠出的桶都会与它的 tokenUsage 总量对账,整份语料加总后与平台的差值实测为 0.000%。

「完全对得上」是拿什么校验的

校验基准是平台自己存的总量,与插件自己的记账无关。本机语料实测:

| 校验项 | 结果 |
| 每个会话独立折叠 vs 平台存储总量 | 37/37 完全一致,0 个不符 |
| 所有会话折叠求和 vs 平台存储 | 3381.151M vs 3381.263M |
| payload 按天求和 vs 按模型求和 | 完全相同 —— 没有重复计入 |
| failed / pending / retired | 0 / 0 / 0 —— 没有会话下落不明 |

剩下的 0.113M 是本宿主从未见过就已删除的会话留下的平台记录:平台还保留它的总量,但列表里没有它、日志也已不存在,无从得知它花在哪几天。这一笔故意不硬塞到某一天 ——把一个无归属的数字放在猜测的日期上,是为了粉饰总量而破坏日历。插件见过的会话则正是为此保留按天历史,而且这份历史从不合并、不过期、不丢弃:

* 从列表中消失的会话仍然保留自己的按会话、按天、按模型明细 —— 包括一年多以前的那一天。
绝不折叠成一个「已删会话」汇总总量:汇总后总量没有"天"可落,合并恰恰是让热力图掉天数的原因。
* 日志读不出来的会话保留上一次已知的贡献,而不是被清空。清空会让一次读取失败就把「累计用量」变小。
* 宿主运行期间新出现的会话立刻折叠,不受解码预算限制 —— 新会话日志很小,而它的天数恰恰是「一旦被删就无法找回」的东西。只有宿主启动时就已存在的积压才分批处理。
* 落盘状态没有过期机制,rollup 的格式版本也不是删除键,所以升级不会让历史损失。

打开面板都是立即作答,来源依次是 60 秒聚合缓存、落盘的聚合结果,最后才是 stale: true 的旧数据。列会话失败(宿主繁忙)的那一轮是「跳过」而不是「失败」:手里的数字照常发布并附一句安静说明,那一轮自己重试 —— 进程抢不到 CPU 不能说明数字有问题。

打开面板都是立即作答,来源依次是 60 秒聚合缓存、落盘的聚合结果,最后才是 stale: true 的旧数据 —— 后者会在响应之后才重扫,面板同时做有限次补拉并显示一个安静的「刷新中…」提示。手动刷新走同样的路径。

面板在挂载槽位的 ref 回调里测量宽度(而不是放在 effect 里 —— 冷启动时槽位元素还不存在, effect 早就跑过了),并把宽度记到模块作用域。两个视图由同一套几何生成 —— 相同的周数、列距、宽度和高度 —— 槽位预留的就是这个版块本身,而且只在真的量到宽度之后才预留。

宿主侧在服务挂载后立刻做一次预热扫描,而不是等固定 10 秒:这样第一次打开面板时聚合已经在内存里。没有轮询,也没有定时器。

隐私

全部计算都在本地、基于磁盘上已有的会话日志完成,没有任何数据离开本机。插件不添加任何模型可见内容:没有提示词段落、没有工具、也没有自己的模型调用。

配置

宿主行接受可选配置:

- id: token-stats
name: dsh-token-stats
config:
maxModels: 8
retainSessions: true

测试

node --test test/

分层:load(bundle 契约、样式表与表面审计、导航图标标记、纯函数,以及一个真正渲染每个图表与面板本身的冒烟测试)、route(信任围栏、协议层错误、信封)、 host-core(折叠、payload 构建、增量扫描,以及与本机真实会话日志上的平台 deriver 做交叉校验)。

许可证

MIT
预览分享卡片长什么样 ↓mathangler/dsh-token-stats
分享 · DeepSeek Harness Hub
mathangler/dsh-token-stats

DeepSeek Harness 的 Token 用量统计插件,挂在设置面板里作为一个独立页面。

这是你的仓库?认领条目

回到顶部