aliensweety/dsh-usage-dock0

dsh-usage-dock

Per-session CNY cost pill in the composer dock: rides the shipped stats row and expands into the token buckets and both tariff bounds.

包名
dsh-usage-dock
版本
0.4.0
许可证
MIT
最近更新
2026年9月12日

安装

$npx -p @deepseek-ai/dsh dsh plugin --profile web add github:aliensweety/dsh-usage-dock

dsh-usage-dock

在输入框下方那排统计药丸(官方 Session 信息 + Token 用量)后面并排追加一颗只显示金额的药丸,样式与官方药丸一致、三粒作为一组居中:

[ Session 信息 ]  [ Token 用量 ]  [¥ 0.47]

点开是明细:未缓存输入 / 缓存读取 / 缓存写入 / 输出 / 总计 / 高峰价与空闲价两档费用 / 缓存命中率。

官方那排已经在显示 Session 与 Token 用量了,所以这颗药丸不再重复这些数字:收起态只有一个金额,其余信息放在 tooltip 与展开面板里。

费用估算口径

  • 价格来源:DeepSeek 官方定价页(人民币/百万 tokens,高峰价)——deepseek-flash 输入未命中 ¥2、缓存命中 ¥0.04、输出 ¥8;deepseek-v4-pro 未命中 ¥9、缓存命中 ¥0.30、输出 ¥27。空闲档一律为高峰价的一半。
  • 模型匹配:读 modelSelection 投影的 lastUsed.model,id 含 pro 按 v4-pro 计价,其余按 flash 计价。
  • 只对 deepseek-official 路由计价lastUsed.provider 是别的路由时整颗药丸不渲染。用 DeepSeek 价目表去算 Claude/GPT 的 token 会得出一个"很确定的错数字",宁可沉默。
  • 药丸上的金额:按查看时刻所处时段自动取高峰或空闲档;面板里两档始终并列展示,所以跨时段的会话其真实费用介于两者之间。
  • 缓存写入不计价:官方价格表没有单独的缓存写入价(本机数据里 cacheWrite 也恒为 0),字段保留、默认 0。

价格与时段(已核对,截至 2026-09-12)

lib/client.js 里的 PRICING / isPeakNow() 与官方口径一致:

模型档位缓存命中未命中输出
deepseek-v4-pro高峰0.30927
deepseek-v4-pro空闲0.154.513.5
deepseek-v4.1-flash(含 deepseek-flash高峰0.0428
deepseek-v4.1-flash空闲0.0214

单位:元/百万 tokens。峰谷时段:北京时间工作日 9:00–12:00、14:00–18:00 为高峰,其余(含夜间与周末)为空闲、按高峰价的一半计。

关键点:V4 Pro 独立计费——它继续调用 V4 Pro 本身,不会路由到 V4.1 Flash(原定 2026-09-14 12:00 的路由计划已于 09-11 取消),所以两者的价目表不会互相套用。这也是本插件按 modelSelection.lastUsed.model 取价、而不是按"当前默认模型"取价的原因。

价格调整后无需改代码,在 Web 控制台执行(对所有已计价模型生效,均填高峰价):

localStorage.setItem('dsh-usage-dock.pricing', JSON.stringify({ miss: 2, hit: 0.04, out: 8, write: 0 }))

删除该 key 即恢复内置价格。

数据来源

只读 DSH 内置的会话投影 tokenUsagemodelSelection不读会话日志文件、不发网络请求、不碰任何凭证tokenUsage 由 provider 上报的用量累加而成,整段会话一个值,带 seq 单调保护,所以它是权威值而非本地估算;它的线上视图是扁平四桶:

{ uncachedInputTokens, outputTokens, cacheReadTokens, cacheWriteTokens }

样式、位置与"明细放哪"

样式:与官方药丸逐条对齐——padding:1px 8pxborder-radius:24pxgap:6px--dsw-alias-label-tertiary 文字色与 --dsw-alias-interactive-bg-hover 悬停底色、font:inherit / line-height:inherit;宽屏带一颗 14px 图标,窄屏(≤640px)图标让位给一个 ¥ 字符,把宽度留给三个药丸。

位置:composer 下方那个 dock 是一个 column flex 容器flex-direction:column; align-items:center),slot outlet 自己带 display:contents,所以第二个条目天然会另起一行;slot 契约里也没有"加入官方那一行"的入口(官方那颗 StatsPills 是自带排版的完整条目,也不声明子 slot)。

所以本插件用 ReactDOM.createPortal 把药丸直接渲染进官方那一排的 DOM 里(官方药丸的浮层也是用同一个 API):

  • 官方那排是 display:flex; gap:12px; justify-content:center,药丸进去后自动拿到同样的 12px 间距、并和它们一起居中——不需要平移、不需要改官方任何元素、不需要测量像素;
  • 靠一个 display:none 的宿主节点留在 outlet 里,只用来找那一排(data-composer-stats 标记);每次提交都重新解析,composer 重挂载换了节点、或那一排消失时会自动跟上;
  • 退化行为:找不到那一排(或宿主没提供 react-dom)时,药丸退回"自己一行居中"的原状。
  • 药丸带 flex:none:窄屏上被压缩的是官方那两粒(它们本来就会 ellipsis),本插件这一粒金额始终完整可读。

明细放在官方 token 用量卡里:点官方那粒 21.1M tok · Cache hit 99% 打开的卡片,本来就已经列出四个 token 桶;本插件把那两行费用(高峰价 / 空闲价)和一个估算说明追加进那张卡的同一个 ``

  • 那张卡的行是 dt/dd 对,而它的样式是元素选择器.xxx_details dt / .xxx_details dd{display:grid;…text-align:right}),所以追加进去的 / 自动拿到和官方行完全一致的排版与配色,不需要写任何上游类名;
  • 检测方式:那一排里最后一颗 button[aria-haspopup="dialog"] 就是官方 token 用量药丸,它 aria-expanded="true" 时出现的 div[role="dialog"] 就是它自己的卡(官方一次只开一张卡);MutationObserverdocument.bodychildList,卡片一出现就写入,卡片还开着时数值变化就地更新;
  • 点本插件这颗药丸 = 直接打开那张官方卡片(没有自己的浮层,所以窄屏不会再溢出);
  • 写入失败(上游改了结构、宿主没给 react-dom、或那一排/那粒药丸不存在)时:药丸的金额照常显示,明细退回自带的兜底面板(该面板只在对齐到屏幕外时才翻转贴边,max-width 也限制在视口内)。

安装