DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

xiaml666 /

xiaml666/dsh-plugin-cost

Verified

DSH 花费显示:每轮对话花了多少钱、整个会话累计多少钱,按官方价格表精确计价(自动刷新、高峰/低谷自动同步、多供应商多币种),并附带账户余额与可视化设置面板;余额与会话累计始终同币种。

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@ab7a3c21

dsh-plugin-cost · DSH 花费显示

在 DeepSeek Harness(DSH)的对话界面里直接看到每轮对话花了多少钱、整个会话累计花了多少钱,以及账户余额。

… 助手回答正文 …

👍  ⑂  · 12.3k tokens · 8.2s · 本轮 2 · ¥0.0873        ← 本插件接在同行末尾
                                        ↑ 点这里看明细

┌─────────────────────────────────────────────┐
│ 输入框                                      │
│ 本会话累计 ¥2.49 · 余额 ¥42.50              │ ← 本插件加在输入框下方
└─────────────────────────────────────────────┘
  • 每轮花费:直接接在该轮助手回答的收尾动作行上,就在 token 用量、耗时之后。 折叠态只显示金额;点击弹出明细,含模型路由、模型调用次数与 token 拆分。
  • 会话累计:显示在输入框下方,默认每 3 秒刷新。点击后是一个精简面板: 轮次 / 模型调用 / 已用 token / 当前模型(含该模型此刻的单价与所处时段)/ 价格来源, 右下角有「⚙ 设置」入口。
  • 账户余额:/user/balance 的实时读数,每 3 次会话刷新读一次(约 9 秒),仅作参照。 显示币种与会话累计一致:你改币种,余额跟着换算,不会一个美元一个人民币。
  • 多币种:金额按你选的币种显示,可再并排显示第二个币种。
  • 按模型判断高峰/低谷:有低谷价的模型显示时段与实时单价,全天同价的模型只显示单价。
  • 自动更新:模型、版本、价格、高峰时段、汇率全部会自己刷新。

安装

这个包没有任何运行时依赖,装法有三种:

# 1) 从 GitHub 装
dsh plugin --profile web add github:xiaml666/dsh-plugin-cost

# 2) 从 npm 装(若已发布)
dsh plugin --profile web add dsh-plugin-cost

# 3) 本地开发:直接 link 一个检出
dsh plugin --profile web add link:/absolute/path/to/dsh-plugin-cost

GitHub 装法的一个坑:pnpm 默认会拦截 git 托管包的 prepare 构建脚本,首次安装可能失败。 dsh 在失败时会打印 pnpm 给出的确切 key,把它加到该 profile 目录下 pnpm-workspace.yaml 的 allowBuilds 里再重跑即可。本包本身并不需要构建(lib/ 与 client/ 都是直接执行的普通 JS), 纯粹是 pnpm 对 git 来源包的一刀切拦截。

装完必须重启 dsh web:客户端 bundle 名册只在进程启动时组装一次,之后光刷新页面不够。 本包自带一个无人值守的重启脚本(会杀掉当前监听该端口的进程,请不要从正在使用该服务的会话里调用):

node scripts/restart-web.mjs

高峰 / 低谷时段

金额按 provider 上报的 token 精确计算,并按官方的高峰/低谷时段自动切换单价。

时段不是写死的:每次刷新价格页时,插件会同时解析官方那句时段说明, 按页面自己声明的时区计价。中英文两版官方页面写的是同一段时间的两种说法:

页面 原文
英文 Peak hours are 01:00 - 04:00 and 06:00 - 10:00 UTC, Monday through Friday
中文 高峰时段为北京时间周一至周五 9:00 - 12:00、14:00 - 18:00

两者等价(北京时间 = UTC+8),插件对任意时刻的判定完全一致。

官网改了时段,插件会在下一次刷新时自动跟上,不需要更新插件。

界面上会给出你所在时区的高峰时段,以及它的补集——空闲时段(两行,各带时区名与 UTC 偏移),并标出此刻是高峰还是低谷、以及下次切换的时刻——「显示的时间不对」绝大多数时候 是时区没对上,而不是规则算错。行与行的差别见下文「当前模型」一节。

空闲时段官网只写成一句话——中文页「其余为空闲时段」、英文页 all other hours are off-peak——从不列出具体钟点,所以插件是按高峰区间取补集反推的:星期一到星期五的高峰 之外,再加上周六、周日整天。反推出来的几段和高峰一样标在你自己的时区上, "什么时候便宜"看这一行就够了。

需要的话也可以在设置面板里固定成自定义时段(自定义时区、星期、区间、低谷倍率), 适合高峰规则与官网不一致的第三方渠道。

不是每个模型都有高峰/低谷

高峰/低谷是模型自己的属性,不是全局开关。插件按当前模型的费率行来判断:

  • 费率行带低谷价(DeepSeek 官方表就是这种,cacheMissOffPeak 等)→ 这个模型按时段浮动, 面板会给出当前是高峰还是低谷、时段、下次切换时刻,以及此刻实际生效的单价;
  • 费率行没有低谷价(多数非 DeepSeek 供应商)→ 这个模型全天同价,面板只给出单价, 并明确标注"无高峰/低谷,全天同价"——给它显示一条高峰时段是在误导。

判定读的就是计价用的那一行,所以显示与实际扣费口径永远一致,不会出现 "界面说有低谷、算钱时却没打折"的情况。

当前模型

会话详情面板和设置面板的顶部都有一块当前模型读数,内容随模型而变。 每一项都单独占一行,而且每一行只要写了时间,就一并写出它用的是哪个时区—— 只给一个光秃秃的 UTC 配一串时间,等于让人无法核对:

模型          deepseek-official/deepseek-flash
时段          当前 低谷(半价)
高峰时段       周一至周五 09:00-12:00、14:00-18:00(Asia/Shanghai UTC+08:00)
空闲时段       周日 00:00-24:00;周一至周五 00:00-09:00、12:00-14:00、18:00-24:00;周六 00:00-24:00(Asia/Shanghai UTC+08:00)
下次切换       周一 09:00(Asia/Shanghai UTC+08:00)
现价(每百万 tokens)
  未命中输入     ¥1.06
  缓存命中       ¥0.0213
  输出          ¥4.26
模型          anthropic/claude-sonnet-5
时段          无高峰/低谷,全天同价
单价(每百万 tokens)
  未命中输入     ¥14.20
  输出          ¥71.00

时段为什么按这个顺序排

  • 高峰时段 用你自己的时区,排在最前:这才是你"什么时候全价"要看的那一行。 官方页面写的是 UTC,你在东八区看到的就是 09:00-12:00、14:00-18:00。
  • 空闲时段 是高峰的补集,同样用你自己的时区:官网只说"其余为空闲时段",不给钟点, 所以这一行由插件按高峰区间反推。星期一到星期五的高峰之外,周六、周日整天都算在内; 你就是 UTC 时也照样显示——它不会和上面那行重复。
  • 官网原文里的钟点不能直接拿来当空闲时段:官方那句话给的 01:00-04:00、06:00-10:00 UTC 是高峰钟点,把它标成空闲时段就等于把贵的时段说成便宜的。出处仍然保留—— 设置面板的「官网原文」一行原样给出整句话。
  • 下次切换 带星期几:只写 09:00 会被读成"今天早上 9 点", 而它很可能是下周一。星期一到星期五的高峰,周五下午的下一次切换其实是周一。
  • 跨日会被算对:时区偏移会把窗口挪到前一天/后一天,甚至跨过当地午夜。 太平洋时间下的 01:00-04:00 UTC 是周日 18:00-21:00,06:00-10:00 UTC 会拆成 周日 23:00-24:00 + 周一 00:00-03:00——按天分列,而不是印一个需要猜的 22:00-02:00。
  • 半小时时区(如 +05:30)按半小时平移,不四舍五入到整点。

当前模型取本会话最新一轮所用的路由(中途换过模型时以最后一次为准); 费率按当前时段与所选币种实时换算。没有任何价格时显示**"未配置价格"**, 而不是一个看起来像真数字的 0。

这块读数由浏览器端自己算:宿主半也会在会话载荷里给出同样的描述,但宿主模块被 Node 的 ESM 缓存按 URL 缓存住,插件就地热更新时可能"浏览器半已经是新版、宿主半还是旧版"。 所以面板会在缺少宿主字段时,用价格表 + 会话轮次自己推导,读数不会因此变空。

币种

currency 选主币种(内置 30 种常用币种的符号与名称,实际可选任意三字母代码), secondaryCurrency 可再并排显示一个,例如主显示人民币、括号里显示美元:

本会话累计 ¥2.49 ($0.35)

默认是「跟随账户」:币种取余额接口自己报的那个(官方账户是 CNY), 所以会话累计与账户余额天生同币种。余额读不到时退回官方币种 CNY。 在下拉里选一个具体代码就是固定币种;选回「跟随账户」即可恢复默认。

改币种时余额一起变:余额接口报的是账户自己的币种,面板显示的是你选的币种, 插件会按 per-USD 汇率表把余额换算过去——否则会出现「累计已经是美元、余额还写着 ¥」 的分裂状态。换算前会先看两者是否同币,同币时原样显示接口给的数字, 不做多余的乘除;余额那种汇率表里没有的币种则保留原币种金额与代码, 宁可标着原币种,也不给一个瞎换的数字。

汇率来自公开的 per-USD 汇率表(无需 API key,覆盖其提供的全部币种), 按 fxRefreshHours 刷新;取不到时退回配置里的 cnyPerUsd 兜底值, 不会把界面刷成错误状态。

模型与价格(含第三方渠道)

这一节讲的是配置文件里的能力。图形面板不提供费率编辑器(原因见上文), 这些字段直接写在 $DSH_HOME/plugin-cost.json 里,或写进价格目录。

价格表按 provider/model 组织,支持三层通配:

键 作用
deepseek-official/deepseek-flash 精确路由
deepseek-official/* 该供应商下所有模型
*/deepseek-flash 所有供应商下同名的模型

实际用到的路由如果不在表里,界面会明确显示**"未配置价格"**,而不是当成 0 ——宁可说不知道,也不给一个看起来像真数字的少算结果。 (同理,只公布了输入/输出单价、没公布缓存单价的模型,在发生缓存读取时也会标为未配置价格。)

倍率套餐

第三方渠道常见 某某模型 (1.20x) 这种倍率命名。在 routes 里把它指到一个已知路由上并 填倍率即可(provider/model 要按实际会话日志里出现的样子写,含空格与括号):

"routes": {
  "my-relay/DeepSeek-V4.1-Flash (1.20x)": {
    "use": "deepseek-official/deepseek-flash",
    "multiplier": 1.2
  }
}

也可以改用 models 直接写死单价(缓存命中 / 未命中 / 输出),优先级最高:

"models": {
  "my-relay/Some-Model": { "cacheHit": 0.1, "cacheMiss": 1.25, "output": 10 }
}

自动更新

三条通道各自独立按间隔刷新,各自失败时保留上一次成功的数据:

通道 默认间隔 更新什么 来源
官方价格页 12 小时 模型列表、各桶单价、高峰时段 api-docs.deepseek.com/quick_start/pricing
远程价格目录 12 小时 新增供应商、新增模型、价格变动 catalogUrl(见下)
汇率 6 小时 各币种对美元汇率 open.er-api.com/v6/latest/USD

价格目录:改文件即可更新,不必发版

catalog/prices.json 是插件的带外更新通道:

{
  "providers": {
    "openai": {
      "label": { "zh": "OpenAI", "en": "OpenAI" },
      "docs": "https://platform.openai.com/docs/pricing",
      "models": {
        "gpt-某版本": { "cacheHit": 0.1, "cacheMiss": 1.25, "output": 10 }
      }
    }
  },
  "models": { "某供应商/某模型": { "cacheMiss": 2, "output": 4 } }
}
  • 包内自带一份,离线也有价格;
  • 把 catalogUrl 指向网络上的同结构 JSON,插件就会按间隔自动拉取;
  • 默认值由 package.json 的 repository 推导,fork 出来的仓库自动就是自己的更新通道, 源码里不写死任何用户名。

所以:官方新增模型、新增版本、调价——只需要更新这个 JSON,已安装的插件会自动跟上。

仓库自带的目录里,openai 与 google 两个供应商只有元数据、没有价格: 抓取时官方页面被 Cloudflare 拦截 / 网络不可达,我们没有编造数字。 你可以把实际用到的价格填进目录,或在 $DSH_HOME/plugin-cost.json 的 models / routes 里直接填(设置面板不提供费率编辑器,原因见上文)。

模型下线交接

目录行可以写 retiredAt 与 billAs,到点自动改用目标路由的费率:

"deepseek-v4-pro": {
  "cacheMiss": 1.32, "output": 3.96,
  "retiredAt": "2026-09-14T04:00:00Z",
  "billAs": "deepseek-official/deepseek-flash"
}

内置目录已按官方公告写好这条:V4 Pro 自北京时间 2026-09-14 12:00 起 由 V4.1 Flash 承接并按 Flash 价格计费,到点自动切换。

设置面板

点开会话累计 → 面板右下角「⚙ 设置」。分五块:

分区 内容
当前模型 只读:本会话在用哪个模型、它有没有高峰/低谷、此刻生效的单价(每项一行)
币种 主币种(含「跟随账户」)、换算显示币种、自定义代码
高峰/低谷 跟随官网,或自定义时区/星期/区间/低谷倍率;显示官网原文与当前生效值(生效日 / 时区 / 高峰区间 / 本地时段各一行,都带时区)
显示 账户余额、token 明细、高峰说明
自动更新 目录地址、三条通道的刷新间隔、立即刷新、各来源的新鲜度与错误(每条通道一行)

写入走白名单校验:未知字段、越界数值、类型不符会被拒绝并给出原因, 不会写进配置文件。

面板不再提供逐路由的费率编辑器。那张表需要把整份费率回传才能保存, 一旦与手写的 routes/models 冲突就会把它们覆盖掉;现在它是纯配置文件功能 (见下文),面板保存时完全不发送这两个字段,所以手写的表不会被碰。

配置

配置文件在首次启动时自动生成($DSH_HOME/plugin-cost.json, Windows 上通常是 C:\Users\<你>\.dsh\plugin-cost.json)。

面板里能改的都在这里,手改文件同样生效:

{
  "provider": "deepseek-official", // 默认跟踪哪家官方页
  "currency": "",                  // 主币种;"" = 跟随账户(余额接口报的币种),否则填三字母代码
  "secondaryCurrency": "",         // 换算显示币种,"" 表示不显示
  "schedule": null,                // null = 跟随官网;对象 = 固定自定义时段
  "offPeakRatio": 0.5,             // 兜底低谷倍率
  "cnyPerUsd": 7.1,                // 汇率兜底值
  "priceRefreshHours": 12,         // 官方价格页刷新间隔
  "catalogRefreshHours": 12,       // 远程目录刷新间隔
  "fxRefreshHours": 6,             // 汇率刷新间隔
  "catalogUrl": "",                // 留空则只读包内目录;也可指向任意同结构 JSON
  "balanceTtlSeconds": 3,          // 余额缓存时长
  "showBalance": true,             // 关掉就不再查余额
  "showTokens": true,              // 是否显示 token 明细
  "showPeakNote": true,            // 是否显示高峰说明
  "routes": null,                  // 路由定价映射(见上)
  "models": null,                  // 直接指定的费率,优先级最高
  "fallback": null                 // 未配置价格的模型:null = 显示"未配置价格"
}

刷新节奏

项 节奏 为什么
会话累计 3 秒 一轮结束后,provider 的用量一落盘金额就会出现
会话累计(日志已安静超过 45s) 退避到 25 秒 没有新轮次时没什么可变的,避免空转
余额 每 3 次会话刷新读一次(≈9 秒) 官方没有公开的余额接口频控,但通用 429 仍可能触发;9 秒一次约 6.7 次/分钟,属保守范围
页面切到后台 全部暂停 不可见时不做任何请求,切回来立刻刷一次

余额读取失败时会保留上一次的读数并退避 5 秒重试,不会把界面刷成错误状态。

为什么每轮花费不按"余额相减"算

余额只能给出时间点快照,而一轮对话往往是几十次并发/串行的模型调用:

  • 快照落后于实际扣费,并发会话、子智能体、标题/压缩等辅助调用会混进同一次差值里;
  • 一轮里的多次调用无法从两次快照中拆分出来,做不出"每轮";
  • 缓存命中与未命中单价相差 50 倍,只有 token 分桶才能算准。

所以花费一律按 provider 上报的 token 精确计算:未命中输入、缓存命中、缓存写入、输出四个互不重叠的桶,各自乘对应单价,并按时段自动切换。余额仍然显示,但只当参考。

宿主接口

方法 路径 用途
GET /cost/session?id=<sessionId> 整个会话日志折叠成每轮 token 与花费(含已滚出屏幕的历史轮次)
GET /cost/balance 代理官方余额接口,API Key 不下发到浏览器
GET /cost/prices?refresh=1 当前生效费率表、供应商、币种、时段、来源与新鲜度
GET /cost/config 设置面板读取的配置与可选项
POST /cost/config 保存设置(白名单校验)
POST /cost/refresh 立即刷新全部数据源

所有路由都走与 Host API 相同的签名 Cookie 鉴权(ctx.connection.requestRejection)。

读日志的策略:优先取进程内的活动会话(最新、无磁盘开销),否则通过 sessionPersistence 读该会话的持久日志,并只提交已落盘的轮次;正在进行的轮次在界面上标记"进行中",落盘后同一位置自动变成实际金额。

工作原理

lib/config.js      配置读写 + 校验边界 + 种子费率 + 路由映射展开
lib/cost-core.js   纯函数:时段解析、日志折叠成每轮用量、计价、价格表解析、币种换算
lib/index.js       宿主半:六条只读/写入路由、官方页/目录/汇率三条刷新通道
client/client.js   浏览器半:两个插槽座位,共用一个按会话共享的轮询源
catalog/prices.json 带外价格目录(可远程覆盖)
cordis.patch.yml   把插件行插入 DSH 组合层(自包含,装完即生效)

每轮金额是怎么定位到那一行的:收尾动作行只拿到一个 messageId,所以宿主在折叠时把每一轮的 assistant/message 消息 id 记进该轮(turn.messageIds),浏览器端再据此反查自己属于哪一轮。

开发

npm run check        # 四个文件的语法检查
npm test             # 全部:语法 + 离线 smoke + 宿主路由 smoke
npm run smoke        # 计价/解析/目录/配置 + bundle 渲染
npm run smoke:host   # 宿主半:五条路由注册、设置读写往返、会话折叠、余额降级
npm run verify:live  # 核对线上实例是否加载了本插件并返回数据

测试是确定性的:夹具 scripts/fixtures/session-events.json 是手工编写的合成日志, 不需要本机跑过 DSH 就能完整通过。如果你本机有真实会话日志,smoke.mjs 会额外只读 折叠一次做交叉验证:

node scripts/smoke.mjs "C:\Users\...\session.v3.jsonl.zstd"

⚠️ 不要把真实会话日志提交进仓库。 scripts/fixtures/session-events.json 是合成数据。 早期版本曾用最新真实日志覆盖它,等于把操作者的完整对话(系统提示词、提问、推理、 工具参数)写进了待提交文件;该行为已移除,测试脚本现在不写入任何文件, host-smoke.mjs 也把 $DSH_HOME 指向临时目录。

verify-live.mjs 需要会话 id 才能核对会话路由:

node scripts/verify-live.mjs http://127.0.0.1:3080 <sessionId>

已知限制

  • 官方价格页靠页面解析:官方改版会导致抓不到;此时保留上一张成功的表 / 包内目录, 界面会显示价格来源,不会静默算错。高峰时段同理,解析不到就退回内置时段。
  • 只统计 provider 上报用量的请求:一轮里没有 usage 的调用(极少)不计费; compaction / 标题等辅助调用若不写进本会话日志,则不计入本会话。
  • 进行中的轮次显示进行中:provider 的用量只在调用结束后上报,本轮金额在收尾时出现。
  • 部分供应商价格需自行补齐:见上文"价格目录"——拿不到官方价的供应商不会猜。
  • 第三方渠道的倍率套餐需要配一条路由映射,否则显示"未配置价格"。

发布到 GitHub

这个包是零依赖的纯 JS,仓库里没有构建产物需要生成。推到 https://github.com/xiaml666/dsh-plugin-cost 之后:

  • package.json 的 repository / bugs / homepage 三处已经指向该地址;
  • 远程价格目录的默认地址会自动从 repository 推导成 https://raw.githubusercontent.com/xiaml666/dsh-plugin-cost/main/catalog/prices.json ——所以以后要加供应商、加模型、调价,只改并推送 catalog/prices.json 即可, 已安装的插件会在下次刷新时自动跟上,不需要重新发版。
git init -b main
git add .
git commit -m "dsh-plugin-cost 1.2.5"
git remote add origin https://github.com/xiaml666/dsh-plugin-cost.git
git push -u origin main

仓库名必须是 dsh-plugin-cost,且默认分支为 main,上面的默认目录地址才成立; 若用别的分支,请把 catalogUrl 一并改掉。

仓库自带的 GitHub Actions(.github/workflows/ci.yml)会在 push 与 PR 上跑 npm run check / npm run smoke / npm run smoke:host,不需要任何 secrets, 也不联网。

License

MIT

—/ 5

No ratings yet

Verified DSH bundle

Commit ab7a3c21945d

Community comments

No comments yet. Be the first to write one.

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout