← 返回列表
未验证
把用量转成可核验的 JSONL 成本收据并回读
尚未跑自动兼容性验证,可查看页面内的依赖与入口分析。 · 最近上游提交 2026/9/1 · 已提供中文文档
一个面向 DeepSeek Harness 的缓存感知 JSONL 成本收据插件。
综合分
28.4
GitHub 分
28.4
用户评分
—
★ Stars
0
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add jwilson411/dsh-spend-receipt该插件未发布到 npm,走 GitHub 源安装(pnpm 若拦截 prepare 脚本,按其提示在 pnpm-workspace.yaml 的 allowBuilds 中放行后重跑)
数据截至 2026/9/16(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-tools@deepseek-ai/cordis用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动
README
dsh-spend-receipt
一个 DeepSeek Harness 函数插件,它将上报的用量转换为可引用、仅追加的 JSONL 成本收据,并提供一个 spend_receipt 工具用于回读:最后 N 行、总计以及缓存命中率。
关键在于这个数字是可核验的。每一行都记录了提供商实际上报的 token 数量、定价所依据的带日期费率表、它落在非高峰时段的哪一侧,以及它的来源——因此读者无需信任本包即可重新推导出成本。没有任何估算:不对文本进行分词,缺失的计数不会用零填充,无法定价的调用会连同其 token 和 usd: null 一起列出,而不是给出猜测。
安装
dsh plugin --profile web add github:jwilson411/dsh-spend-receipt
dsh plugin 会在 $DSH_HOME/profiles/web 内转发给 pnpm,然后协调 profile:由于本包的清单声明了 dsh.bundle.patch,它会被追加到 profile 清单有序的 dsh.profile.bundles 列表中,其 cordis.patch.yml 会成为一层。移除方式相同,只需将 add 换成 remove。
从 profile 自己的 cordis.patch.yml 中将其指向你的路径——注意,以 id 为目标的补丁会替换该行的整个 config,因此请重新声明你想保留的每个字段:
- id: spend-receipt
config:
receiptPath: ~/.dsh/spend-receipt.jsonl
sessionLog: ~/.dsh/sessions/current.jsonl
| 配置键 | 环境变量回退 | 默认值 |
|---|---|---|
| receiptPath | DSH_SPEND_RECEIPT_PATH | ./spend-receipt.jsonl |
| sessionLog | DSH_SPEND_RECEIPT_SESSION_LOG | 未设置——只读模式 |
| sessionId | DSH_SPEND_RECEIPT_SESSION_ID | 未设置——行记录 null |
| limit | — | 20 |
输入假设
收据行来自以下两个来源之一,绝不来自猜测。
用量事件。 已经拥有用量钩子的宿主会调用 recordUsage(event, { receiptPath });CLI 的 record 命令对 stdin 上的 JSONL 执行相同操作。事件是任何带有模型和 token 计数的对象,位于顶层或嵌套在 usage、usage.details 或 response.usage 之下。蛇形和驼峰两种拼写都会被读取(prompt_tokens / input_tokens / promptTokens,prompt_tokens_details 下的 prompt_cache_hit_tokens / cache_hit_tokens / cached_tokens,completion_tokens / output_tokens)。如果事件报告了缓存命中和未命中的两半但没有提示总数,则总数是二者之和——这是唯一允许的推导,因为它重述的是已上报的计数,而不是测量任何新内容。
仅追加的会话日志。 syncFromSessionLog({ logPath, receiptPath }) 将日志作为 JSONL 读取:携带用量的行成为收据行,仅携带会话 id 的行会为其下方的行设置会话,其他所有内容都被忽略。日志必须是真正仅追加的——恢复没有单独的游标文件,它就是收据已经
引用,因此重写后的日志需要一份新的回执。在持续增长的日志上重新运行同步只会追加新增内容。
回执采用 JSONL 格式,且只追加写入;一个批次会被序列化并一次性追加写入,因此崩溃不会导致半行内容交错。一行损坏的内容会被计数并报告,而不会被抛出——它不会使其余历史记录变得不可读。
一行,格式化后:
{
"ts": "2026-08-29T09:00:12.000Z",
"session_id": "sess-7c2",
"model": "deepseek-v4-pro",
"input_tokens": 12000,
"cache_hit_tokens": 9000,
"output_tokens": 800,
"usd": 0.007524,
"currency": "USD",
"nano_usd": 7524000,
"rate_table": "deepseek-v4@2026-08-29",
"rate_tier": "peak",
"ts_source": "event",
"source": { "kind": "session-log", "path": "…/session.jsonl", "line": 3 }
}
input_tokens 是计费的提示词总量,其中 cache_hit_tokens 由缓存提供。nano_usd 是 usd 四舍五入所依据的、以 1e-9 USD 为单位的精确整数成本;总计由它累加得出,因此一千行的回执相加也不会出现浮点漂移。当时间戳来自用量事件时,ts_source 为 event;当事件未携带时间戳、该值为写入该行的时间时,ts_source 为 recorded。
费率表
固定并标注日期为 2026-08-29,读取自 DeepSeek 公布的价格列表:
。当费率变化时,请更新 RATE_TABLE_DATE 和 src/rates.js 中的 id,以便旧行仍可归因于实际产生它们的数字。
每 1M tokens 的 USD 价格:
| model | tier | cache hit | cache miss | output |
|---|---|---|---|---|
| deepseek-v4-pro | peak | $0.044 | $1.32 | $3.96 |
| deepseek-v4-pro | off-peak | $0.022 | $0.66 | $1.98 |
| deepseek-v4-flash | peak | $0.014 | $0.44 | $1.32 |
| deepseek-v4-flash | off-peak | $0.007 | $0.22 | $0.66 |
高峰时段为 UTC 周一至周五的 01:00–04:00 和 06:00–10:00。 其他所有时间均为非高峰时段,按高峰费率的一半计费——包括两个时间窗之间的间隔、每个傍晚和夜间,以及整个周六和周日。高峰是较窄的情况,因此除非调用落在工作日这两个时间窗之一内,否则均为非高峰。
时间窗为半开区间 [start, end):04:00:00 已属于非高峰。费率档位根据调用自身的时间戳按 UTC 读取,无论偏移量以哪个时区书写——2026-08-29T03:00:00+02:00 是周六的 01:00 UTC,因此尽管在本地读起来像高峰时段,它仍是非高峰。
费率以每 token 的整数 nano-USD 存储,而非浮点数,因此定价和总计均为整数运算,转换为 USD 只在边缘发生一次。
deepseek/deepseek-v4-pro、deepseek/deepseek-v4-flash 以及两个 -latest 指针均解析为上述行。只有同一模型的不同拼写才会被别名映射——将近邻模型映射到已定价的行上会凭空捏造价格。
当调用无法定价时
tokens 仍会被记录,usd 和 nano_usd 为 null,并且
unpriced_reason 说明三者之中缺少的是哪一项:
- model-not-in-rate-table —— 未知或更新的模型。它的 token 会出现在 token 总数中,也会被计入 unpriced_lines,但它对 usd 没有任何贡献,因此总计只会少算而不会凭空捏造。
- cache-split-unknown —— 只有 prompt 总数,没有报告缓存拆分。两半的计费费率相差约 30 倍,因此仅凭总数无法为该调用定价。这类行也会被排除在缓存命中率的两半之外,而不是被计为未命中。
- timestamp-unknown —— 没有可用的时刻,因此无法确定高峰/非高峰档位。未知档位不会悄悄变成高峰档位。
spend_receipt 工具
| | |
|---|---|
| Cordis 插件 id | spend-receipt(cordis.patch.yml 中的行 id) |
| 注入 | tools —— 硬依赖;插件会等待而不是降级 |
| 工具 | spend_receipt |
| 参数 | limit(整数,可选)、sync(布尔值,可选) |
limit 是要返回的最近收据行数,默认为配置的 limit。sync 默认为 true:该工具会先扫描配置的 sessionLog 以获取新用量,然后再报告。传入 sync: false 则只报告已记录的内容。无法运行的扫描——日志路径拼写错误或尚未创建——会在 synced.error 中报告其失败,而不会使收据无法读取。
它返回 receipt_path、其定价所依据的 rate_table、lines 本身、整个收据的 totals 和 cache_hit_rate、仅针对所返回行的 window_totals 和 window_cache_hit_rate、malformed_lines 以及 synced。缓存命中率为已知拆分行的 cache_hit_tokens / input_tokens,并附带一个 basis,说明它是基于收据的多少部分计算得出的。
该工具读取文件且不访问网络,因此不需要 API 密钥。
CLI
从 shell 调用相同的功能。它不导入 node: 和本包之外的任何内容,因此可以在未安装依赖的检出上运行——当你需要的是收据、而坏掉的正是运行框架时,这很有用。
dsh-spend-receipt show [--receipt ] [-n ] [--json]
dsh-spend-receipt sync --log [--receipt ] [--session ] [--json]
dsh-spend-receipt record [--receipt ] [--session ] [--json] # events on stdin
dsh-spend-receipt rates [--json]
$ dsh-spend-receipt sync --log ~/.dsh/sessions/current.jsonl
appended 6 line(s) to /home/you/spend-receipt.jsonl
from /home/you/.dsh/sessions/current.jsonl, 12 new log line(s) scanned, cursor now 12
skipped log line 6: unparseable-json
skipped log line 9: invalid-count
$ dsh-spend-receipt show -n 2
receipt: /home/you/spend-receipt.jsonl
2026-08-29T20:15:00.000Z deepseek/deepseek-v4-pro off-peak in 2000 (cached 0) out 100 $0.001518
2026-08-29T12:00:00.000Z deepseek-v4-flash peak in 100 (cached 50) out 10 $0.000036
showing 2 of 6 line(s)
总计(整张收据):19600 输入 token(其中 13050 已缓存)和 1210 输出 token,共 $0.009491 USD
缓存命中率:68.0%(基于 5 行已知拆分)
未定价:2 行贡献了 token 但没有成本
rates 打印固定的费率表,这是检查一张收据是按什么定价的最快方式。
布局
package.json manifest + dsh.bundle.patch —— 使其成为 bundle 的关键
cordis.patch.yml bundle 的补丁层:一次插入,一行插件
src/rates.js 固定的、带日期的费率表和精确到纳美元(nano-USD)的定价
src/usage.js 读取上报的计数,不凭空捏造任何数据
src/receipt.js 构建、追加、读取和同步收据行
src/summary.js 最后 N 行、总计和缓存命中率
src/index.js 插件:name、inject、apply(ctx, config)
bin/ CLI
test/ 针对已检入的 fixture 的离线测试
package-lock.json CI 中 npm ci 安装的固定依赖树
测试
sh
npm install
npm test
从构造上就是离线的,且仅使用 fixture。测试套件读取 test/fixtures/ 中已检入的 JSONL
fixture,并将临时收据写入临时目录:不启动任何 profile,不打开任何 socket,不读取任何密钥,也不查询
任何时钟——记录时间是通过参数传入的,因此即使某行的事件没有携带时间戳,结果仍然是确定性的。
只有 test/plugin.test.js 需要一个依赖:它针对一个 stub 上下文注册插件,并用真实的
@deepseek-ai/dsh-tools 验证该工具的结果,该依赖在 devDependencies 和
package-lock.json 中固定为 0.1.1-rc.2,以便针对一个已知 API 测试契约。其他所有内容——库、CLI
以及另外四个测试文件——都不导入 node: 和本包之外的任何东西。
CI(.github/workflows/ci.yml)在 Node 22 和 24 上,从已提交的 lockfile 运行 npm ci 和 npm test,
仅使用公共 registry。它不需要任何凭据,测试套件也不访问网络。
许可证
MIT —— 见 LICENSE。同作者(jwilson411)的其他插件
扫码进群