bowenliang123/dsh-context
bowenliang123/dsh-contextDSH 上下文洞察面板:Context 仪表盘 + /context命令 + Context 浏览器,查看 Context的分类组成、内容详情、演进趋势、压缩/注入事件、统计等一站式 Context 全生命周期管理。
在侧边栏显示 DeepSeek 账户余额与当前峰谷时段,并按官方高峰/空闲价估算每个会话与每一轮的花费。
DeepSeek Harness 插件:在侧边栏左下角用绿色显示 DeepSeek 账户余额与当前峰谷时段 (空闲 / 忙碌),点击展开账户余额、峰谷切换倒计时、忙碌时段时间表与近期用量统计; 并在输入框下方与每一轮的用量旁边,按官方高峰 / 空闲价估算每个会话与每一轮的花费。
只依赖 DSH 官方账号服务与本地会话日志,不接入任何第三方服务。
A DeepSeek Harness (DSH) plugin that adds cost awareness to the Web UI. It shows the DeepSeek account balance and the current peak / off-peak phase in the sidebar, and estimates per-session and per-turn cost using the official peak and off-peak rates. The balance comes from DSH's own account service and the cost is estimated from local session logs — it talks to no third-party service.
本会话消耗 ≈¥x pill next to DSH's own session usage line. Clicking
expands an anchored panel upwards (a position: fixed portal, so the row never grows).本轮花费 ≈¥x trigger rendered in the same row as DSH's
built-in 本轮用量 trigger, using flex order to sit to its right.Pricing follows the official rules: peak is Mon–Fri 09:00–12:00 and 14:00–18:00 Beijing time; weekends and Chinese public holidays are off-peak all day. Un-cached input, cache reads, cache writes and output are priced separately, using the token fields DSH records per request.
Everything is local: the client talks to the host half over one read-only loopback route
(/dsh-balance-usage/summary and /dsh-balance-usage/session). Credentials are never touched by
the plugin — the balance comes from DSH's account service, or from a DEEPSEEK_API_KEY only when
the official account is not signed in.
npm test runs test/verify.mjs, a dependency-free offline end-to-end verifier (176 assertions)
that drives the real host route handler with fake services and renders the client half through a
minimal React compatibility layer.
The rest of this document is in Chinese.
余额 + 金额胶囊 + 时段圆点 + 时段文字。¥12.34,圆点颜色依旧表示峰谷。--dsw-alias-state-success-primary)lowBalanceThreshold,默认 10)| 区块 | 内容 |
|---|---|
| 账户余额 | 总额(大字、按状态着色,点击直接跳官方账号页)、充值 / 赠送拆分、余额来源;「官方账号」与「充值」分别打开官方用量页 / 充值页 |
| 当前计价时段 | 空闲 / 忙碌胶囊、当前北京时间、距下一次切换的实时倒计时与切换时刻 |
| 忙碌时段(高峰计价) | 明确列出 周一至周五 09:00–12:00、14:00–18:00(北京时间),并说明高峰价 = 空闲价 × 2、周末与法定节假日全天按空闲计价 |
| 近期用量 | 今日 / 近 7 天 / 近 N 天的 Token、估算费用、请求次数;近 14 天每日 Token 柱状图;按模型拆分表 |
react-dom 的 portal 渲染到 document.body,用 position: fixed 贴住左下角条目向上展开,
因此不受侧边栏 overflow 与过渡动画的裁剪影响。Esc 关闭;点「刷新」、右键余额条目、或窗口重新获得焦点都会立即刷新。localStorage(键名 dsh-balance-usage.prefs)。默认值来自 Host 的
config.refreshSeconds(出厂 60 秒)。dsh-balance-usage.prefs,两个浮层(左下角面板、底部与每轮的花费浮层)一起生效。
深色 / 浅色不是另画一套配色,而是在浮层根元素上覆盖官方主题 token
(--dsw-alias-bg-overlay、--dsw-alias-label-*、--dsw-alias-border-* 等),
浮层里所有既有规则整体跟随,不会出现「不深不浅」的半吊子配色。注册进 conversation.composer.dock(输入框下方那一栏):
🎛 本会话消耗 ≈ ¥5.67 ← 胶囊是 inline-flex,和官方那行用量统计并排同一行
点一下向上弹出明细浮层:
┌───────────────────────────────┐
│ 🎛 本会话金额 ≈ ¥5.67 │
│ ───────────────────────────── │
│ 高峰花费 ≈ ¥1.20 │
│ 空闲花费 ≈ ¥4.47 │
│ 请求次数 128 次 │
│ 缓存命中率 99.4% │
│ 模型 deepseek-flash │
│ │
│ 本地按官方价表估算,非官方账单。│
└───────────────────────────────┘
关键是展开不会改变那一栏的高度(早期版本就是在这一点上做错了):
display:inline-flex,与官方那行用量统计并排,不另起一行;document.body 的 position: fixed 元素,用官方基元
primitives.useAnchoredPosition({ side: "top", gap: 6, margin: 8 }) 锚在胶囊上沿并向上展开 ——
浮层完全脱离文档流,展开 / 收起时那一栏高度一个像素都不变;primitives.useDismissOnOutsidePointer,另加 Esc;bottom 定位 + 自己监听外部点击),功能不受影响;未缓存输入 / 缓存读取 / 输出 那些数字;数据来自 Host 的 /dsh-balance-usage/session?sessionId=…(按会话统计整个对话,不按时间窗口截断,
带 15 秒缓存),随自动刷新间隔一起更新。
挂在 conversation.chat.assistant-actions —— 也就是官方「本轮用量」所在的那一行。
DSH 内部把该槽位的内容当作 extraActions 传给 MessageIconActions,而官方的
「本轮用量」正是同一个组件的 usageAction:
<MessageIconActions
extraActions={renderSlot("conversation.chat.assistant-actions", { messageId })}
usageAction={<TurnUsagePanel usage={data.tokenUsage} />} // 官方「本轮用量 11.14M tok」
/>
extraActions 在 usageAction 之前渲染,而 .actions 是 flex 容器,
所以本插件用 order: 1 把自己排到所有默认 order: 0 的兄弟之后,落到行尾:
… ⧉ ⑂ | 本轮用量 11.14M tok 12:34 | 🎛 本轮花费 ≈¥0.46
↑ 本插件(order:1 排到最后)
TurnUsagePanel 的触发器:inline-flex、高
calc(28px + var(--dsh-content-font-delta,0px))、border-radius:var(--dsw-radius-sm),
hover / 展开时用 --dsw-alias-interactive-bg-hover —— 和官方那个触发器是同一套控件;IconGaugeOutlineRegular(仪表盘 = 计量 / 消耗)。
官方「本轮用量」用的是 IconDatabaseOutlineRegular,所以花费这边刻意不复用它;position: fixed +
useAnchoredPosition({ side: "top" })):标题「本轮金额」+ 高峰花费 / 空闲花费 / 请求次数 / 模型;messageId,所以 Host 会把每一轮包含哪些 assistant 消息 id 一起返回,
客户端按 messageId → 轮次 → 花费 反查;null(该 slot 的契约明确允许「无内容的条目返回 null」),
不会留空位、不会多出空白。依据 DeepSeek 官方定价文档:
Off-peak rates are half of the peak rates. Peak hours are 01:00 - 04:00 and 06:00 - 10:00 UTC, Monday through Friday, excluding Chinese public holidays. All other hours are off-peak, including weekends and Chinese public holidays in full.
换算为北京时间(UTC+8)即:
| 时间 | |
|---|---|
| 忙碌(高峰计价) | 周一至周五 09:00–12:00、14:00–18:00 |
| 空闲(低谷计价) | 工作日其余时间、周六周日全天、中国法定节假日全天 |
内置了 2026 年法定节假日表(依据国务院办公厅 2025-11-04 通知),
可用 holidays 配置项整体覆盖。
峰谷归属按请求发起时刻(step/start 事件时间)判定,取不到时回退到消息完成时刻。
官方未公开跨时段请求按发起还是按完成计费,这是合理近似;实测请求耗时在秒级,边界影响极小。
| 模型 | 输入(缓存未命中) | 输入(缓存命中) | 输出 |
|---|---|---|---|
deepseek-flash | 1 | 0.02 | 4 |
deepseek-v4-pro | 4.5 | 0.15 | 13.5 |
deepseek-chat(旧) | 2 | 0.5 | 3 |
deepseek-reasoner(旧) | 4 | 1 | 16 |
高峰时段按 ×2 计。模型名做了归一化:含 pro → v4-pro,含 flash → flash,
因此 deepseek-flash 与旧名 deepseek-v4-flash 走同一套价。
计费口径:DSH 的
TokenUsage.inputTokens是未缓存输入,不含缓存命中的部分 —— 实测inputTokens + cacheReadTokens + cacheWriteTokens + outputTokens === totalTokens, 正好和官方价表的「INPUT (CACHE MISS)」/「INPUT (CACHE HIT)」两栏一一对应。 所以未缓存输入按 input 价、缓存读取按 cacheRead 价分别计价(早期版本曾把缓存读取从输入里 再减一次,导致未缓存输入被算成 0、费用偏低,已修正)。
本插件是一个标准的 DSH bundle 包(package.json 里带 dsh.bundle.patch 与 dsh.client),
安装后由 profile 的 bundle 栈自动挂载,无需手改 cordis.patch.yml。
cd $env:DSH_PROFILE_DIR # 通常是 C:\Users\<你>\.dsh\profiles\desktop
pnpm add "link:C:\Users\<你>\Documents\deepseek-harness\default-workspace\dsh-balance-usage"
然后把 dsh-balance-usage 加进该 profile package.json 的 dsh.profile.bundles 数组:
{
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-balance-usage"
]
}
}
}
link: 会建立指向源码目录的符号链接,改完源码刷新页面即可看到客户端改动
(服务端改动需要重启 DSH Desktop)。
所有字段都是可选的,写在注册项的 config 块里(cordis.patch.yml):
- id: balance-usage
name: dsh-balance-usage
config:
utcOffsetHours: 8
peakWindows:
- { start: 9, end: 12 }
- { start: 14, end: 18 }
peakWeekdays: [1, 2, 3, 4, 5] # 0=周日 … 6=周六
peakMultiplier: 2
holidays: ["2026-10-01"] # 覆盖内置节假日表
scanDays: 30 # 用量回溯天数
maxSessions: 60 # 单次统计最多扫描的会话数
cacheTtlMs: 300000 # 用量汇总的服务端缓存(5 分钟)
scanConcurrency: 2 # 扫描会话日志的并发度(上限 4)
usageScan: true # false = 完全不扫描会话日志,只显示余额与时段
refreshSeconds: 60 # 客户端自动刷新间隔(秒),0 = 不自动刷新
apiKey: "" # 未登录官方账号时的回退 API Key
lowBalanceThreshold: 10 # 低于该值金额转琥珀色
currency: "" # 强制币种,如 CNY / USD
price: # 逐项覆盖价格(空闲时段价)
deepseek-flash: { input: 1, cacheRead: 0.02, cacheWrite: 0, output: 4 }
refreshSeconds 是 Host 侧配置,改动后需要重启 DSH Desktop;
客户端下拉框里的选择只覆盖它,可随时改、无需重启。
apiKey 留空时依次尝试环境变量 DEEPSEEK_API_KEY。已登录 DSH 官方账号时不会用到它。
「读余额」和「统计用量」是两类完全不同的开销,所以本插件把它们拆成了两条路径:
| 路径 | 触发条件 | 做什么 | 开销 |
|---|---|---|---|
| 轻 | 左下角条目自动刷新(默认每 60 秒) | 只问账号服务要余额 + 重算峰谷时段 | 一次内存查询,缓存 30 秒 |
| 重 | 面板打开时(客户端带 usage=1) | 扫描会话日志汇总用量 | 每个会话整份解码,缓存 5 分钟 |
也就是说 面板关着的时候,本插件完全不读会话日志。会话日志解码(zstd / SQLite)是 native 重活,所以这里刻意保守:默认最多扫 60 个会话、并发 2、结果缓存 5 分钟, 并且缓存失败结果以外的请求不会重复扫描。
如果你怀疑本插件与 Host 进程崩溃(Windows 退出码 0xC0000005 之类)有关,
把用量扫描整个关掉,就可以把它对 native 层的负载降到零(只剩一次余额查询):
- id: balance-usage
name: dsh-balance-usage
config:
usageScan: false
此时面板仍会显示余额、峰谷时段与忙碌时段时间表,只是用量区块显示为空。
lib/ 里没有任何 native 依赖,也没有 postinstall;本插件只会调用 DSH 自己的
sessionQuery 服务。所以它能影响的只有「调用频率与并发」,而这两项现在都由你控制。
api.deepseek.com/user/balance 发一次余额查询。ctx.sessionQuery 读取 assistant/message 的 token usage)计算,
不向任何外部服务发送会话内容。/dsh-balance-usage/summary 通信。maxSessions)、最近 30 天(scanDays),
面板上会显示「已扫描 N 个会话」。request/header 推断,同名不同版本无法区分。holidays 配置补充。客户端 bundle 里 require("@deepseek-ai/dsh-client-ui-primitives") 就能拿到官方 UI 库
(由 harness 在运行时提供,不需要写进 dsh.client.inject)。下面是官方自己正在使用的那批导出:
| 类别 | 可用项 |
|---|---|
| 图标 | 约 200 个 Icon*,每个都有 OutlineRegular(线框)/ Medium / Artwork 三档,例如 IconGaugeOutlineRegular、IconDatabaseOutlineRegular、IconClockOutlineRegular、IconDataOutlineRegular、IconSparkleRegular、IconRefreshOutlineRegular、IconWarningOutlineRegular、IconInfoOutlineRegular、IconCopyOutlineRegular、IconChevronDownOutlineRegular、IconRightUpOutlineRegular、IconLinkOutlineRegular |
| 基础控件 | Button、Tooltip、Modal、Menu / MenuItemButton / MenuSurface / MenuGroup、HoverCard、Toast、Tag、Pill、StateDot、Switch、Checkbox、Input、ShortcutKeys、SegmentedTabs / SegmentedControl、DisclosureRow、TextShimmer |
| 内容展示 | MarkdownText、CodeBlock、JsonTree / JsonBlock、DiffBlock、ReadBlock、SearchBlock、WebBlock、TerminalBlock、PathLabel、FileTypeIcon、ImageLightbox |
| 设置页 | SettingsForm / SettingsFormModel / SettingsValueField / SettingsSecretField、settingsNumberField、settingsTextField |
| Hook | useAnchoredPosition、useAnchoredMaxHeight、useDismissOnOutsidePointer、useModalLayer、observeComposition、pointerModality |
| 工具函数 | writeClipboard、relativeTime、focusWithoutRing、closeTopModal、isBehindModal、classifyFileType、fileSizeText、languageForPath |
本插件用到的是 IconGaugeOutlineRegular(花费)、useAnchoredPosition(side: "top" 向上锚定)、
useDismissOnOutsidePointer(点击外部关闭)。三者缺失时都有降级实现,功能不受影响。
test/verify.mjs 是离线端到端验证脚本,不需要 DSH、不需要网络、没有任何依赖:
用假的 ctx / sessionQuery / deepseekAccount 驱动 Host 半体并直接调用它注册的 HTTP
路由处理器,再用一层极简 React 兼容层渲染客户端半体(含 ref 提交、依赖数组、document
事件与 portal 模拟)。
176 项断言,覆盖:
position: fixed 元素且 side: "top"(展开不撑大布局);Esc、以及各种缺参数时的降级。npm test # 等价于 node test/verify.mjs
| 改动的文件 | 生效方式 |
|---|---|
lib/client.js | 直接改即生效(link: 安装下 DSH 的 client HMR 会发布新版本);刷新页面即可看到 |
lib/index.js、package.json、cordis.patch.yml | 属于 Host 侧,需重启 DSH Desktop |
MIT