@rayadesu/dsh-billing 0.3.11 → 0.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +26 -4
- package/README.i18n.yaml +2 -2
- package/README.md +37 -17
- package/README.zh.md +34 -18
- package/package.json +7 -7
- package/preview-detail.png +0 -0
- package/preview-overview.png +0 -0
package/AGENTS.md
CHANGED
|
@@ -11,7 +11,7 @@ deepseek-harness 官方仓库([deepseek-ai/deepseek-harness](https://github.co
|
|
|
11
11
|
```
|
|
12
12
|
packages/llm-billing/ 宿主插件 @rayadesu/dsh-llm-billing(/user/balance 传输、峰谷计价、billing Remote)
|
|
13
13
|
packages/ui-billing/ 浏览器插件 @rayadesu/dsh-client-ui-billing(会话头部徽标与详情面板)
|
|
14
|
-
packages/typert-protocol/ 内嵌 Typert 协议声明(@deepseek-ai/dsh-typert-protocol@0.1.
|
|
14
|
+
packages/typert-protocol/ 内嵌 Typert 协议声明(@deepseek-ai/dsh-typert-protocol@0.1.6-alpha.1 的 lib/types),构建期供 typert 生成器识别装饰器;刻意不在 pnpm workspace 内,让 @deepseek-ai/dsh-typert-protocol 从 npm 解析(内嵌副本只有声明,无运行时实现)。它没有自己的 node_modules——其中的 cordis 必须与 workspace 根解析到同一个副本,否则 `TypertRemoteService` 的 `Context` 会与插件源码的 `Context` 变成两个类型。
|
|
15
15
|
cordis.patch.yml DSH profile bundle 补丁层:挂载 llm-billing + ui-billing 两个插件行
|
|
16
16
|
```
|
|
17
17
|
|
|
@@ -41,16 +41,38 @@ cordis.patch.yml DSH profile bundle 补丁层:挂载 llm-billing + ui-
|
|
|
41
41
|
- **发布前校验**:`pnpm run verify`(每个包 `prepublishOnly` 自动运行)检查
|
|
42
42
|
`lib/typert.host.js` 的 `TYPERT.package` 必须等于导出它的包名,且 lib 中不得残留
|
|
43
43
|
其他包名的清单;失败即禁止发布。
|
|
44
|
-
- **依赖以发布形态声明**:`@deepseek-ai/dsh-*` 依赖写 `^0.1.
|
|
44
|
+
- **依赖以发布形态声明**:`@deepseek-ai/dsh-*` 依赖写 `^0.1.6-alpha.1`(对应官方 monorepo 当前发布基线,monorepo 内为
|
|
45
45
|
`workspace:^`);本插件的三个包发布到 npm 的
|
|
46
46
|
`@rayadesu` scope,直接 `dsh plugin add @rayadesu/...` 安装。
|
|
47
|
+
- **补客户端包漏声明的运行时依赖**:`dsh-client-store`(`zustand`/`immer`)与
|
|
48
|
+
`dsh-client-ui-primitives`(markdown 视图栈:`mdast-util-*`、`micromark-*`、`shiki`、
|
|
49
|
+
`katex`、`diff`、`anser`、`clsx`)把它们只声明为上游 devDependencies,产物却直接 import;
|
|
50
|
+
发布 bundle 运行时由 dsh 安装提供,独立 workspace 的测试要自己解析,故在 `ui-billing`
|
|
51
|
+
的 devDependencies 里按上游同版本范围补齐。dsh 依赖线升级后若测试报 `Cannot find package`,
|
|
52
|
+
照此补即可。
|
|
53
|
+
- **typert codec 跨基线桥接(`scripts/typert-compat.mjs`)**:npm 上最新的
|
|
54
|
+
`@deepseek-ai/dsh-typert-generator@0.1.6-alpha.1` 把 strict codec 生成为
|
|
55
|
+
`{ mode, typeSymbol, schema }`,而 dsh checkout(HEAD,含 `perf(typert): materialize
|
|
56
|
+
generated schemas on first use`)改成 `{ mode, typeSymbol, create() }`,其 `dsh-typert-loader`
|
|
57
|
+
见不到 `create` 就拒绝注册(`... parameter codec has no create() factory`),宿主行随之
|
|
58
|
+
启动失败、Web 启动页报 `1 entry did not activate`。两个读取方都不拒绝自己不读的字段,
|
|
59
|
+
所以产物**两个字段都带**即可同时满足 checkout 与 npm alpha 线。该步骤挂在 `build:host`
|
|
60
|
+
末尾(tsdown 之后),幂等;`verify` 检查 strict codec 数 == `create: () =>` 数。
|
|
61
|
+
**改完与 typert 产物相关的代码务必走 `pnpm run build:host`,不要只跑 tsdown。**
|
|
62
|
+
- **workspace 关掉 pnpm 发布龄门槛**:`pnpm-workspace.yaml` 显式 `minimumReleaseAge: 0`。
|
|
63
|
+
pnpm ≥11 默认 1 天门槛会把刚发布的 alpha 包挡在 lockfile 校验外,而校验阶段不认
|
|
64
|
+
`minimumReleaseAgeExclude`(那是解析期自动追加的),本仓库又要紧跟 DSH alpha 基线。
|
|
47
65
|
- **密钥不进仓库**:`DEEPSEEK_API_KEY` 等一律由用户环境或凭据 seam 提供,仓库不含真实值。
|
|
48
66
|
- **README 双语**:每个 README 遵循 DSH 结构 `README.md`(EN) + `README.zh.md`(ZH) +
|
|
49
67
|
`README.i18n.yaml`(记录两文件 git blob hash,改动后需更新)。
|
|
50
|
-
- **版本对齐**:根 bundle 与两个包统一版本号(当前 0.3.
|
|
51
|
-
- **提交与发布流程**:见 `.agents/skills/dsh-release/SKILL.md` —— 阶段 A(改代码 →
|
|
68
|
+
- **版本对齐**:根 bundle 与两个包统一版本号(当前 0.3.13),`pnpm-lock.yaml` 随依赖变更更新。
|
|
69
|
+
- **提交与发布流程**:见 `.agents/skills/dsh-release/SKILL.md` —— 阶段 A(改代码 → 按档位校验/打包 →
|
|
52
70
|
本地 pack 安装 → 交用户验证)**不提交**,改动留在工作区;用户说「发布」进入阶段 B 才 bump 版本、
|
|
53
71
|
**按类型分别提交**、推送、发 npm 与 GitHub Release。
|
|
72
|
+
- **成本纪律(省 token)**:一轮的开销 ≈ 请求数 × 当时上下文,所以按改动定档做事——文案/样式/注释这类
|
|
73
|
+
微调只跑受影响用例、只重打并重装改动的那个包;工具输出只留尾巴(`Select-Object -Last/First N`、
|
|
74
|
+
`git diff -U0`);全套 `test`/`build`/`verify` 一轮只跑一次;同一批微调的文档与 hash 攒到定稿后一次补。
|
|
75
|
+
细则见 `.agents/skills/dsh-release/SKILL.md` 的「成本纪律」。
|
|
54
76
|
- **文本规范**:LF 换行、文件末尾一个换行(`.editorconfig`/`.gitattributes` 已声明)。
|
|
55
77
|
|
|
56
78
|
## 常用命令
|
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# last confirmed-consistent state. Both languages carry equal authority; after
|
|
3
3
|
# editing either side, bring the other along and re-record both hashes with:
|
|
4
4
|
# git hash-object README.md README.zh.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: fee1f7f0233cb3b78b0416bfd17e507875479a8b
|
|
6
|
+
README.zh.md: 568258156c786048433fe8b58fe6999831c7c50e
|
package/README.md
CHANGED
|
@@ -8,26 +8,26 @@ A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin tha
|
|
|
8
8
|
|
|
9
9
|
## What it shows
|
|
10
10
|
|
|
11
|
-
- **Session-header badge** — two lines: remaining balance (
|
|
12
|
-
- **Detail panel** — the remaining amount
|
|
11
|
+
- **Session-header badge** — two lines: remaining balance (`剩余金额:¥X`, the panel's headline without its `API` prefix — the chip is narrow) and this conversation's billed spend (`本会话花费:¥X`, its own work plus every subagent session it delegated, worded exactly as the panel labels it).
|
|
12
|
+
- **Detail panel** — the remaining amount; today's billed token count next to today's all-session spend (`今日 Token` / `今日花费`, one row; the count is DSH's compact notation with its own unit — `12.2K tok`, followed by the day's cache-hit share as a bare, unparenthesized percentage, rendered by DSH's own hit-rate rule: an integer percent that grows decimals only as far as a partial hit needs to stay below 100, and none at all when the day billed no prompt-side input); directly under that row, today's two bucket detail lines — tokens on top, costs below, each on its own with its natural ` · ` spacing (no column alignment between them), in the per-model breakdown line's typography but on the third section's row spacing (the today session-spend ranking's tight 6px rhythm); every spend amount renders at **three significant digits** (`¥9.58`) but never finer than four decimals — an amount below ¥0.0001 reads `¥0` — while the balance line alone keeps four decimals; this conversation's spend on its own line below them (`本会话花费` — this session plus the subagent sessions it delegated, per-model rows included; with this conversation's share of today trailing it, bare and unparenthesized (`¥X`), rendered **only when the conversation did not start today** — the session's own creation day decides (the delegated read reports `crossedDay`), not a comparison of the two amounts, because the live session figure and the 60-second-cached ranking row routinely differ mid-turn; one priced row per model (`未缓存输入 ¥X · 缓存读取 ¥Y · 输出 ¥Z` — DSH's own bucket wording and row order), plus a manual refresh action and a spend disclaimer on the `?` button (one line of estimate scope, then a line stating that the amounts include the subagent sessions this session delegated, then a line naming the amount trailing the session figure as this conversation's spend today, and the running plugin version as the last line, e.g. `v0.3.13`). The panel ends with a **today session-spend ranking**: sessions sorted by today's spend, highest first — one row per conversation, since each subagent session's spend is merged into the row of the session that delegated it (names come from the log's Chinese titles and follow renames automatically; at most the top 10 rows, with a "…N more sessions" hint).
|
|
13
13
|
- **Turn cost amount** — each completed turn's closing message shows a plain static `¥X` at the **end** of the actions row, after the clock: non-interactive (no icon, no "cost" word, no card), its typography replicates the clock text (13px secondary tier, tertiary tone, nowrap), and it is **always visible** (not hover-revealed like the clock text — the row's own hover reveal shows both together); turns without DeepSeek usage (zero cost) or failed loads stay hidden.
|
|
14
14
|
- **Failures and empty states** — a session or day without priced usage shows "no usage recorded" instead of a fabricated figure; a missing key, rejected credential, or transport error renders a muted "Balance unavailable" whose tooltip carries the Remote's own error message.
|
|
15
15
|
|
|
16
16
|
## Data update mechanics
|
|
17
17
|
|
|
18
|
-
- **Session spend follows the conversation** — the host prices every committed event into a per-session projection (`billingTodaySpend`) and pushes it to the browser, so **this session's spend** updates live with no Remote call; the Remote read remains the fallback when the projection registry is absent. **Today's spend** is recomputed on turn settle (one shared scan serves
|
|
18
|
+
- **Session spend follows the conversation** — the host prices every committed event into a per-session projection (`billingTodaySpend`) and pushes it to the browser, so **this session's own spend** updates live with no Remote call; the Remote read remains the fallback when the projection registry is absent, and `billing/getDelegatedSpend` adds the subagent sessions this session delegated (fetched on mount, on refresh, and when a turn settles) so the amount shown is the whole conversation's. **Today's spend** is recomputed on turn settle (one shared scan serves the aggregate, the ranking, and the delegated subtotal), and the parenthesized today share on that same session line reads that same ranking fetch (no extra request, at the cost of moving with it); each turn's cost comes from **one batch fetch per session** instead of one call per rendered message.
|
|
19
19
|
- **Balance is cached, not polled** — the host reuses one `/user/balance` snapshot for 15 seconds (manual refresh forces a fresh one) and caps each request at 5 seconds; the browser keeps the last settled value so a session switch renders the amount immediately and revalidates in the background. There is still no polling.
|
|
20
20
|
- **Old values survive refreshes** — a failed refresh keeps the last good value instead of blanking it.
|
|
21
21
|
|
|
22
22
|
## Preview
|
|
23
23
|
|
|
24
|
-
A real session: the session-header badge
|
|
24
|
+
A real session: the session-header badge and the open detail panel:
|
|
25
25
|
|
|
26
|
-
<img width="1200" alt="Billing plugin overview: session header badge
|
|
26
|
+
<img width="1200" alt="Billing plugin overview: session header badge and the open detail panel (remaining amount, today's tokens and spend, this session's spend, per-model breakdown, today's session ranking)" src="preview-overview.png" />
|
|
27
27
|
|
|
28
|
-
Close-up of the detail panel — the `API 剩余金额` figure, `本会话花费` (with its parenthesized today share
|
|
28
|
+
Close-up of the detail panel — the `API 剩余金额` figure, `今日 Token` and `今日花费`, `本会话花费` (with its parenthesized today share once the session crossed a day), the per-model breakdown (`未缓存输入 · 缓存读取 · 输出`), and the today session-spend ranking:
|
|
29
29
|
|
|
30
|
-
<img width="
|
|
30
|
+
<img width="496" alt="Detail panel close-up: API remaining amount, today's tokens and spend, this session's spend with its parenthesized today share, the uncached-input / cached-input / output breakdown, and the today session ranking" src="preview-detail.png" />
|
|
31
31
|
|
|
32
32
|
Close-up of the turn-cost amount — the static `¥` amount at the end of the actions row, after the clock:
|
|
33
33
|
|
|
@@ -130,13 +130,21 @@ commands.
|
|
|
130
130
|
|
|
131
131
|
The two plugin packages declare the DeepSeek Harness packages they build on
|
|
132
132
|
(`@deepseek-ai/cordis`, `@deepseek-ai/dsh-credentials`, `@deepseek-ai/dsh-session`,
|
|
133
|
-
and the client runtime packages) as `peerDependencies` at `^0.1.
|
|
133
|
+
and the client runtime packages) as `peerDependencies` at `^0.1.6-alpha.1`. A dsh
|
|
134
134
|
profile does not auto-install peers, so these are provided by the dsh
|
|
135
135
|
installation itself through the `profiles/node_modules` fallback rather than
|
|
136
136
|
fetched from the registry — no extra packages to install, and no registry token
|
|
137
137
|
needed on the installing machine.
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
Two of those published client packages import runtime modules their manifests
|
|
140
|
+
list only under `devDependencies` (`dsh-client-store` → `zustand`/`immer`;
|
|
141
|
+
`dsh-client-ui-primitives` → the markdown view stack: `mdast-util-*`,
|
|
142
|
+
`micromark-*`, `shiki`, `katex`, `diff`, `anser`, `clsx`). The shipped bundles
|
|
143
|
+
are external and load them from the dsh installation at runtime, but this
|
|
144
|
+
standalone workspace resolves them itself, so `ui-billing` declares them as its
|
|
145
|
+
own `devDependencies` for the browser-half specs.
|
|
146
|
+
|
|
147
|
+
The plugin builds against the 0.1.6-alpha.1 published line and keeps both DSH
|
|
140
148
|
runtime families readable: the live `Session` log surface
|
|
141
149
|
(`Session.events` + `header.seedLength` at/before 0.1.1-rc.2,
|
|
142
150
|
`snapshotEvents()` + `inheritedEventCount` since 0.1.2-alpha.4), and the
|
|
@@ -193,9 +201,19 @@ it. If a typert manifest ever names a package other than its own
|
|
|
193
201
|
|
|
194
202
|
The typert generator recognizes `Remote`/`TypertRemoteService` only from a
|
|
195
203
|
workspace-registered protocol package, so `packages/typert-protocol` vendors
|
|
196
|
-
the published `@deepseek-ai/dsh-typert-protocol@0.1.
|
|
204
|
+
the published `@deepseek-ai/dsh-typert-protocol@0.1.6-alpha.1` declarations; when
|
|
197
205
|
the dsh dependency line moves, refresh it from the installed package.
|
|
198
206
|
|
|
207
|
+
The generated codecs also straddle a baseline skew. The newest *published*
|
|
208
|
+
generator emits `{ mode, typeSymbol, schema }`, while the harness checkout the
|
|
209
|
+
web app actually runs from is ahead of that publish (`perf(typert): materialize
|
|
210
|
+
generated schemas on first use`) and reads `{ mode, typeSymbol, create() }`,
|
|
211
|
+
refusing any codec whose `create` is not a function. `scripts/typert-compat.mjs`
|
|
212
|
+
— run at the end of `build:host`, enforced by `verify` — adds the `create`
|
|
213
|
+
factory beside the emitted `schema`, so a single artifact loads on both lines.
|
|
214
|
+
Run `pnpm run build:host` (not bare `tsdown`) after touching anything that
|
|
215
|
+
regenerates these artifacts.
|
|
216
|
+
|
|
199
217
|
Publishing (the bundle and both plugins share one version; `prepublishOnly`
|
|
200
218
|
runs the `verify` gate automatically). Use `npm publish` from **inside each
|
|
201
219
|
package directory** — `pnpm publish` fails (token resolution) and a folder
|
|
@@ -243,18 +261,20 @@ Both packages ship sane defaults; everything below is optional.
|
|
|
243
261
|
## How session spend is computed
|
|
244
262
|
|
|
245
263
|
- Each `assistant/message` event reports three billed token buckets: **cache-hit input**, **cache-miss input** (uncached input + cache writes), and **output** (including reasoning). A failed or retried `assistant/attempt` reports its usage only in its embedded stream; that sample is priced too (with the model of the latest `request/header`), a later sample for the same `(turn, step)` **replaces** the earlier one, and `llm/retry-started` makes the retried attempt **add** — the same accounting DSH's own turn-usage disclosure uses.
|
|
246
|
-
- Each sample is priced at the peak/off-peak rate of its own **Beijing-time** hour — and of the rate revision in effect at its own timestamp — the three buckets are billed separately (
|
|
247
|
-
- **Today's spend** aggregates every session's events on the current Beijing-time calendar day with the same pricing rules; event dates are also assigned in Beijing time.
|
|
264
|
+
- Each sample is priced at the peak/off-peak rate of its own **Beijing-time** hour — and of the rate revision in effect at its own timestamp — the three buckets are billed separately (`未缓存输入 ¥X · 缓存读取 ¥Y · 输出 ¥Z`, DSH's own bucket names), then summed per model. Peak windows apply weekdays (Monday–Friday) only; weekends are always off-peak.
|
|
265
|
+
- **Today's spend** aggregates every session's events on the current Beijing-time calendar day with the same pricing rules; **today's tokens** is the sum of those same priced rows' three billing buckets (cache-hit input / cache-miss input / output), rendered in DSH's compact notation with its ` tok` unit (`517 tok`, `12.2K tok`, `1.2M tok`); event dates are also assigned in Beijing time.
|
|
248
266
|
- **Turn cost** prices the events inside the turn's `turn/start`..`turn/end` range with the same rules (located by the closing message's session id + message id), folded in one pass for the whole session and served as a `messageId → cost` map.
|
|
249
|
-
- **Today session ranking** aggregates today's spend per session with the same rules (a cross-day session counts only today's part), sorted descending; names come from the log's latest `session/title` event (the auto-generated Chinese title or a user rename).
|
|
267
|
+
- **Today session ranking** aggregates today's spend per session with the same rules (a cross-day session counts only today's part), sorted descending; names come from the log's latest `session/title` event (the auto-generated Chinese title or a user rename). It ranks **conversations**: a subagent session (DSH stamps its header with `origin: 'subagent'` and a `delegationDepth`; a user fork carries neither) is merged into the row of the top-level session that delegated it, so one row is one conversation. Each row reports `total` (the conversation's whole day, subagents included) plus `ownTotal` (that session's own spend) — the panel's trailing share reads `ownTotal`, and the day's aggregate total is unchanged by the regrouping.
|
|
250
268
|
- Models without a rate row are not priced (the built-in table covers the DSH `llm-deepseek` catalog — V4.1 Flash `deepseek-flash`, V4 Flash, V4 Pro, V4 Flash Vision Exp — plus the retired `deepseek-v4.1-flash-expires-on-0910` preview id and the MiMo-V2.5 series; every flash-series route shares the same pair). Each sample takes the rate revision in effect at its own timestamp: the base schedule is the DeepSeek pricing effective **August 17**; the **flash series** (V4.1 Flash, V4 Flash, V4 Flash Vision Exp, and the retired id) was re-priced effective **September 10, 12:00 Beijing time** to off-peak 0.02 / 1.0 / 4.0 CNY per 1M tokens with peak at twice those prices — samples from before that instant, the V4.1 Flash route's own earlier usage included, keep the superseded rates; the **V4 Pro** route is announced to switch to V4.1 Flash and its rates on **September 14, 12:00 Beijing time**; the MiMo-V2.5 series is untouched. The weekend-off-peak rule (weekends billed at off-peak prices all day) follows the adjustment effective **August 23**.
|
|
251
269
|
|
|
252
270
|
## Known limitations
|
|
253
271
|
|
|
254
|
-
- **Priced rows only** — the session, turn, and today spends only price models that have a `billing.models` row.
|
|
255
|
-
- **On-demand aggregation** — today's spend and the session ranking are computed on the host behind a 60-second cache and share ONE scan; a miss resolves live sessions from their eager projection cells and cold sessions from the zero-I/O projection-cache row when that row's own day is not the queried one, reading a log only for sessions whose persisted revision changed (or whose cached row covers the queried day). A failed resolution is remembered by revision instead of being retried every scan. The ranking is also fetched only on demand — see the next bullet — and the
|
|
256
|
-
- **Ranking is fetched on demand** — the panel loads the ranking when it is opened (and on refresh), so a badge that stays closed never pays for the all-session scan.
|
|
257
|
-
- **
|
|
272
|
+
- **Priced rows only** — the session, turn, and today spends only price models that have a `billing.models` row (today's token count reads those same rows, so it covers priced models only).
|
|
273
|
+
- **On-demand aggregation** — today's spend and the session ranking are computed on the host behind a 60-second cache and share ONE scan; a miss resolves live sessions from their eager projection cells and cold sessions from the zero-I/O projection-cache row when that row's own day is not the queried one, reading a log only for sessions whose persisted revision changed (or whose cached row covers the queried day). A failed resolution is remembered by revision instead of being retried every scan. The ranking's subagent roll-up regroups those rows in the same pass, with no extra read, and the aggregate still sums every session — so merging never moves money. The ranking is also fetched only on demand — see the next bullet — and the trailing today share on the 本会话花费 row rides that same fetch, so it costs no extra request ((—) until the first one settles, and up to 60 seconds behind afterwards).
|
|
274
|
+
- **Ranking is fetched on demand** — the panel loads the ranking when it is opened (and on refresh), so a badge that stays closed never pays for the all-session scan. The trailing today share on the 本会话花费 row rides that same fetch (reading the row's `ownTotal`, so a merged subagent total never poses as this session's own), so it costs no extra request (nothing shows until the ranking settles, and it can be up to 60 seconds behind afterwards).
|
|
275
|
+
- **The delegated subtotal moves with the turn, not per event** — the session line's own part is live (pushed projection), while the subagent part comes from `billing/getDelegatedSpend`, refreshed on mount, on manual refresh, and when a turn settles, and served from the same 60-second host cache as today's spend. A subagent that burns money mid-turn therefore lands on the parent's amount at the next read (turn settle, refresh, or a panel open), not per event.
|
|
276
|
+
- **A merged row can be untitled** — a subagent whose parent session is outside the scan (a deleted or archived parent log, for instance) is still attributed to the parent id its own header names, but that log is never read, so the merged row shows the untitled fallback until the parent is scanned.
|
|
277
|
+
- **Ranking capped at 10** — the panel shows at most the top 10 conversations (the host's subagent roll-up runs first, so one row is one conversation), with a "…N more sessions" hint.
|
|
258
278
|
- **Turn cost needs a finalized closing message** — interrupted turns have no actions row, so no turn cost; cold sessions served straight from the projection cache may rank with an "Untitled" name until their log is read again.
|
|
259
279
|
- **Balance is TTL-cached** — the host reuses one snapshot for up to 15 seconds and each request aborts after 5 seconds; spending from another client does not move the shown value until the TTL expires, a manual refresh, or a browser reload (there is no polling).
|
|
260
280
|
- **Estimate, not a promise** — the session spend prices tokens at official rates; the provider's actual billing prevails.
|
package/README.zh.md
CHANGED
|
@@ -8,26 +8,26 @@
|
|
|
8
8
|
|
|
9
9
|
## 显示什么
|
|
10
10
|
|
|
11
|
-
- **会话头部徽标** ——
|
|
12
|
-
- **详情面板** ——
|
|
11
|
+
- **会话头部徽标** —— 两行:剩余余额(`剩余金额:¥X`,是面板标题去掉 `API` 前缀的短版,徽标较窄)+ 本轮对话的计费花费(`本会话花费:¥X`,含本次对话委派的每个子代理会话,文案与面板完全一致)。
|
|
12
|
+
- **详情面板** —— 剩余金额;今日计费 token 数与今日所有会话合计同一行(`今日 Token` / `今日花费`,token 数按 DSH 的紧凑记数法带自己的单位,如 `12.2K tok`,token 数值后紧跟当天的缓存命中率(裸数字、无括号)——按 DSH 官方命中率规则渲染:整数百分比,只有部分命中会被凑到 100% 时才逐位多留小数;当天没有 prompt 侧输入就不显示);紧跟该行下面是**今日两行桶明细**——token 在上、花费在下——两行各自成行、各自保持自然的 ` · ` 间距(两行之间不做列对齐),排版沿用下方模型分项那一套,行距用第三部分(今日会话花费排行)那一套紧凑节奏;**面板上所有花费金额都按三位有效数字渲染**(`¥9.58`),但**不细于四位小数**——低于 ¥0.0001 的金额显示 `¥0`;只有余额行保留四位小数;下一行才是本会话花费(`本会话花费`——本会话加上它委派的子代理会话,模型分项同样合并;金额后**只在这段对话不是今天才创建时**才紧跟今日份金额 `¥X`——判定用的是会话自身的创建日(子代理读取给出 `crossedDay`),不再比较两个金额,因为实时的本会话数字与 60 秒缓存的排行行在回合中途本来就会不一致;以及每个模型一行的花费分项(用 DSH 自己的桶名与行序:`未缓存输入 ¥X · 缓存读取 ¥Y · 输出 ¥Z`),外加手动刷新按钮与「?」上的花费说明(一句估算口径,一行说明金额含本会话委派的子代理会话,一行说明紧跟的金额是本次对话今日花费,最后一行顶格是当前插件版本号,如 `v0.3.13`);面板底部是**今日会话花费排行**:按今日花费从高到低排列的会话列表——一行就是一个对话,子代理会话的花费已并入委派它的那个会话行(会话名取日志中的中文标题,重命名后自动同步;最多显示前 10 条,其余以「…还有 N 个会话」提示)。
|
|
13
13
|
- **本轮花费金额** —— 每条已完成回合的收尾消息操作行**行尾**(时钟之后)显示纯静态的 `¥X`:不可点击、无图标、无「花费」字样、不弹卡片,字体样式逐项复刻时钟文本(13px 次级字号、tertiary 色、nowrap),并且**始终显示**(不随悬停隐藏,与时钟文本一致——整行的悬停显隐规则让两者同进退);回合没有 DeepSeek 用量(花费为 0)或加载失败时不显示。
|
|
14
14
|
- **失败与空态** —— 会话或今日没有可计价消耗时显示「暂无消耗记录」而不是编造数字;未配置 key、凭据被拒或传输错误时显示弱化的「额度不可用」,其提示携带 Remote 自己的错误信息。
|
|
15
15
|
|
|
16
16
|
## 数据更新机制
|
|
17
17
|
|
|
18
|
-
- **会话花费自动跟随** —— 主机端把每条已提交事件计价进每会话投影(`billingTodaySpend
|
|
18
|
+
- **会话花费自动跟随** —— 主机端把每条已提交事件计价进每会话投影(`billingTodaySpend`)并推送给浏览器,**本会话自身的花费**因此零 Remote 调用、实时更新;投影注册表不存在时回退到 Remote 读取,而 `billing/getDelegatedSpend` 再把本会话委派的子代理会话加上去(挂载时、手动刷新时与回合结束时各拉一次),所以显示的是整次对话的金额。**今日花费**(与同一行的**今日 Token**)在回合结束时重算(聚合、排行与子代理小计共用同一次扫描),本会话花费行紧跟的今日份金额也来自这次排行读取(不额外发请求,代价是与排行同进同退);每条消息的行尾金额来自**每会话一次批量拉取**,不再逐条消息各发一次请求。
|
|
19
19
|
- **额度有缓存、但不轮询** —— 主机端 15 秒内复用同一份 `/user/balance` 快照(手动刷新强制取新),单次请求 5 秒超时;浏览器保留最后一次结果,切会话时立即渲染旧值并在后台校验。仍然**没有轮询**。
|
|
20
20
|
- **刷新期间旧值保留** —— 刷新失败保留上一次有效值,不会清空。
|
|
21
21
|
|
|
22
22
|
## 显示样式
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
真实会话中的会话头部徽标与展开的详情面板:
|
|
25
25
|
|
|
26
|
-
<img width="1200" alt="
|
|
26
|
+
<img width="1200" alt="计费插件总览:会话头部徽标与详情面板(剩余金额、今日 Token 与今日花费、本会话花费、按模型分项与今日会话花费排行)" src="preview-overview.png" />
|
|
27
27
|
|
|
28
|
-
详情面板特写 —— `API
|
|
28
|
+
详情面板特写 —— `API 剩余金额`、`今日 Token` 与 `今日花费`、`本会话花费`(跨天时含括号内的今日份金额)、按模型分项(`未缓存输入 · 缓存读取 · 输出`)与今日会话花费排行:
|
|
29
29
|
|
|
30
|
-
<img width="
|
|
30
|
+
<img width="496" alt="详情面板特写:API 剩余金额、今日 Token 与今日花费、本会话花费(含括号内的今日份金额)、未缓存输入/缓存读取/输出分项与今日会话花费排行" src="preview-detail.png" />
|
|
31
31
|
|
|
32
32
|
本轮花费金额特写 —— 操作行行尾(时钟之后)的静态 `¥` 金额:
|
|
33
33
|
|
|
@@ -121,11 +121,17 @@ dsh plugin --profile web update --latest # 忽略声明的版本区间,把所
|
|
|
121
121
|
|
|
122
122
|
两个插件包把它们依赖的 DeepSeek Harness 包(`@deepseek-ai/cordis`、
|
|
123
123
|
`@deepseek-ai/dsh-credentials`、`@deepseek-ai/dsh-session` 以及客户端运行时包)
|
|
124
|
-
声明为 `peerDependencies`(`^0.1.
|
|
124
|
+
声明为 `peerDependencies`(`^0.1.6-alpha.1`)。dsh profile 默认不自动安装 peer,所以
|
|
125
125
|
这些由 dsh 安装本身通过 `profiles/node_modules` 回退提供,而不是从 registry 拉取——
|
|
126
126
|
无需额外安装,安装机也不需要 registry token。
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
这些已发布的客户端包里有两个会把只写在 `devDependencies` 里的运行时模块直接 import 进
|
|
129
|
+
产物(`dsh-client-store` → `zustand`/`immer`;`dsh-client-ui-primitives` → markdown 视图栈
|
|
130
|
+
`mdast-util-*`、`micromark-*`、`shiki`、`katex`、`diff`、`anser`、`clsx`)。发布的 bundle
|
|
131
|
+
把这些留作外部依赖、运行时由 dsh 安装提供,但本仓库是独立 workspace、要自己解析,所以
|
|
132
|
+
`ui-billing` 把它们声明为自己的 `devDependencies` 供浏览器半测使用。
|
|
133
|
+
|
|
134
|
+
插件按 0.1.6-alpha.1 发布线构建,同时兼容读取两代 DSH 运行时:live `Session` 日志面
|
|
129
135
|
(0.1.1-rc.2 及以前为 `Session.events` + `header.seedLength`,0.1.2-alpha.4 起为
|
|
130
136
|
`snapshotEvents()` + `inheritedEventCount`),以及持久化服务面(0.1.1-rc.2 及以前为
|
|
131
137
|
`inspect`/`listSnapshots`,0.1.2-alpha.5 的 handle 化改造后为
|
|
@@ -174,9 +180,17 @@ host 面会从源码重新生成 `lib/typert.host.js` 与 `lib/typert.remote-cli
|
|
|
174
180
|
的 name 不一致,`verify` 会在发布前直接失败。
|
|
175
181
|
|
|
176
182
|
typert 生成器只认工作区内已注册协议包里的 `Remote`/`TypertRemoteService` 声明,所以
|
|
177
|
-
`packages/typert-protocol` 内嵌了 npm 上 `@deepseek-ai/dsh-typert-protocol@0.1.
|
|
183
|
+
`packages/typert-protocol` 内嵌了 npm 上 `@deepseek-ai/dsh-typert-protocol@0.1.6-alpha.1` 的
|
|
178
184
|
声明文件;dsh 依赖线升级时,从安装包重新刷新它。
|
|
179
185
|
|
|
186
|
+
生成出来的 codec 还横跨了一次基线错位:npm 上最新的**发布版**生成器输出
|
|
187
|
+
`{ mode, typeSymbol, schema }`,而 Web 实际运行的那份 harness checkout 领先于该次发布
|
|
188
|
+
(`perf(typert): materialize generated schemas on first use`),读的是
|
|
189
|
+
`{ mode, typeSymbol, create() }`,`create` 不是函数就拒绝注册。`scripts/typert-compat.mjs`
|
|
190
|
+
挂在 `build:host` 末尾、并由 `verify` 把关,在生成的 `schema` 旁补上 `create` 工厂,
|
|
191
|
+
让同一份产物在两条线上都能加载。**动到会重新生成这些产物的代码后,请跑
|
|
192
|
+
`pnpm run build:host`,不要只跑 tsdown。**
|
|
193
|
+
|
|
180
194
|
发布(bundle 与两个插件包统一版本号;`prepublishOnly` 会自动跑 `verify` 门禁)。
|
|
181
195
|
要用 `npm publish` 且**必须在各包目录内执行**——`pnpm publish` 会失败(token 读取方式问题),
|
|
182
196
|
而 `npm publish packages/llm-billing` 这种带路径参数的形式会被 npm 解析成 GitHub 仓库简写,
|
|
@@ -218,19 +232,21 @@ npm publish # @rayadesu/dsh-billing bundle(仓库根)
|
|
|
218
232
|
|
|
219
233
|
## 会话花费是怎么算的
|
|
220
234
|
|
|
221
|
-
- 每条 `assistant/message` 事件报告三个计费 token
|
|
222
|
-
-
|
|
223
|
-
-
|
|
235
|
+
- 每条 `assistant/message` 事件报告三个计费 token 桶:**缓存读取**、**未缓存输入**(未缓存输入 + 缓存写入,宿主按未命中单价合并计价)、**输出**(含推理)。失败或重试的 `assistant/attempt` 只在自身内嵌 stream 里报告用量,这份样本同样计价(模型取最近一条 `request/header`);同一 `(turn, step)` 的后一份样本**替换**前一份,`llm/retry-started` 之后重试的那次**累加** —— 与 DSH 自己的回合用量口径一致。
|
|
236
|
+
- 每份样本按其**发生时刻**(北京时间)所在的峰/谷时段单价——以及该时刻生效的费率版本——计价,三个桶分别计费(`未缓存输入 ¥X · 缓存读取 ¥Y · 输出 ¥Z`,桶名用 DSH 自己的说法),再按模型汇总。高峰窗口仅周一至周五适用;周末全天按低谷价计费。
|
|
237
|
+
- **今日花费**按同一个计价规则汇总当天(北京时间自然日)所有会话的事件,**今日 Token** 是同一批计价行的三个计费桶(缓存读取 / 未缓存输入 / 输出)之和,按 DSH 的紧凑记数法加 ` tok` 单位渲染(`517 tok`、`12.2K tok`、`1.2M tok`);事件归属的日期同样按北京时间计算。
|
|
224
238
|
- **本轮花费**按同一规则计价该回合 `turn/start`..`turn/end` 区间内的事件(定位到收尾消息的会话 id + 消息 id),整会话一趟折出 `messageId → 金额` 映射后下发。
|
|
225
|
-
- **今日会话花费排行**按同一规则按会话汇总今日花费(跨天会话只统计今天的部分),从高到低排序;会话名取日志中最后一条 `session/title`
|
|
239
|
+
- **今日会话花费排行**按同一规则按会话汇总今日花费(跨天会话只统计今天的部分),从高到低排序;会话名取日志中最后一条 `session/title` 事件(自动生成的中文标题或用户重命名的新标题)。排行排的是**对话**:子代理会话(DSH 在其 header 上盖 `origin: 'subagent'` 与 `delegationDepth`;用户手动分叉两者都没有)会并入委派它的顶层会话那一行,所以一行就是一个对话。每行给出 `total`(这次对话的整日花费,含子代理)与 `ownTotal`(该会话自己的花费);面板里紧跟的今日份金额读的是 `ownTotal`,而归组不改变整日合计。
|
|
226
240
|
- 没有费率行的模型不计入(内置价目表覆盖 DSH `llm-deepseek` 目录——V4.1 Flash `deepseek-flash`、V4 Flash、V4 Pro、V4 Flash Vision Exp——外加已退役的 `deepseek-v4.1-flash-expires-on-0910` 预览 id 与 MiMo-V2.5 系列;flash 系列各路由同价)。每份样本取**自身时刻生效的费率版本**:基础价目为 DeepSeek **8 月 17 日实行**的费率;**flash 系列**(V4.1 Flash、V4 Flash、V4 Flash Vision Exp 及退役 id)自 **9 月 10 日 12:00(北京时间)** 起降为谷时 0.02 / 1.0 / 4.0 元每百万 token、峰时为其两倍——该时刻之前的样本(含 V4.1 Flash 路由自身的早先用量的)沿用被取代的旧价;**V4 Pro** 路由公告于 **9 月 14 日 12:00(北京时间)** 改由 V4.1 Flash 服务并按其实施费率计费;MiMo-V2.5 系列不受影响。**周末按低谷价计费**的规则按 **8 月 23 日**生效的调整执行。
|
|
227
241
|
|
|
228
242
|
## 已知限制
|
|
229
243
|
|
|
230
|
-
- **有费率行才计价** ——
|
|
231
|
-
- **按需聚合** ——
|
|
232
|
-
- **排行按需拉取** ——
|
|
233
|
-
- **排行只显示前 10** —— 详情面板最多展示前 10 个会话,其余以「…还有 N
|
|
244
|
+
- **有费率行才计价** —— 会话花费、本轮花费与今日花费(以及同一行的今日 Token:token 数也只统计这些行)只统计价目表(`billing.models`)里有的模型。
|
|
245
|
+
- **按需聚合** —— **今日花费/今日 Token** 与今日会话排行在主机端 60 秒缓存之后计算,且共用同一次扫描;未命中时,活跃会话直接读投影单元,冷会话若缓存行自身的日期不是查询日则零 I/O 直接作答,只有持久化修订变化(或缓存行覆盖查询日)的会话才读日志。读取失败的会话按修订号记住,不再每轮重试。排行的子代理归组在同一趟扫描里完成,不额外读日志;合计仍然对每个会话求和,所以合并只在总额之间归组、不搬钱。
|
|
246
|
+
- **排行按需拉取** —— 详情面板打开(或手动刷新)时才拉取排行,徽标一直关着就不会为全量会话扫描买单;「本会话花费」行紧跟的今日份金额也来自这次读取(读该行的 `ownTotal`,合并进来的子代理金额不会被当成这个会话自己的),所以它不额外发请求(代价是排行未落定时不显示这个数字,且最多滞后 60 秒)。
|
|
247
|
+
- **排行只显示前 10** —— 详情面板最多展示前 10 个会话,其余以「…还有 N 个会话」提示;上限在子代理合并之后生效,所以一行就是一个对话。
|
|
248
|
+
- **子代理小计随回合更新,不逐事件跟进** —— 会话行里本会话自身那部分是实时的(推送投影),子代理那部分来自 `billing/getDelegatedSpend`:挂载、手动刷新与回合结束时各拉一次,并与今日花费共用同一个 60 秒宿主缓存。因此子代理在回合中途烧掉的钱会在下一次读取(回合结束、手动刷新或打开面板)时落到父会话金额上,而不是逐事件实时跳动。
|
|
249
|
+
- **合并行可能显示「未命名」** —— 子代理的父会话不在本次扫描范围内时(例如父日志已被删除或归档),该行仍按 header 里写的父会话 id 归属,但那份日志从未被读取,标题要等父会话被扫描到才显示。
|
|
234
250
|
- **本轮花费只出现在已定稿的收尾消息** —— 中断的回合没有操作行,不显示本轮花费;冷会话(投影缓存直接命中)排行标题可能显示「未命名」,待其日志被重新读取后恢复。
|
|
235
251
|
- **额度带 TTL 缓存** —— 主机端最多复用 15 秒内的同一份快照,单次请求 5 秒超时;账户在其他客户端产生消耗时,界面值要等 TTL 过期、手动刷新或刷新浏览器才变(仍无轮询)。
|
|
236
252
|
- **是估算,不是承诺** —— 会话花费按官方单价对 token 计价;实际计费以服务商为准。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rayadesu/dsh-billing",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.13",
|
|
4
4
|
"description": "DeepSeek Harness billing plugin: account balance and this session's billed spend with a session-header badge.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
},
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "pnpm run build:host && pnpm run build:client",
|
|
21
|
-
"build:host": "tsc -b tsconfig.host.json && tsdown --env.DSH_BUILD_FACE host",
|
|
21
|
+
"build:host": "tsc -b tsconfig.host.json && tsdown --env.DSH_BUILD_FACE host && node scripts/typert-compat.mjs",
|
|
22
22
|
"build:client": "tsc -b tsconfig.client.json && tsdown --env.DSH_BUILD_FACE client",
|
|
23
23
|
"typecheck": "tsc -b tsconfig.host.json && tsc -b tsconfig.client.json",
|
|
24
24
|
"lint": "eslint .",
|
|
@@ -40,13 +40,13 @@
|
|
|
40
40
|
"access": "public"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@rayadesu/dsh-client-ui-billing": "^0.3.
|
|
44
|
-
"@rayadesu/dsh-llm-billing": "^0.3.
|
|
43
|
+
"@rayadesu/dsh-client-ui-billing": "^0.3.13",
|
|
44
|
+
"@rayadesu/dsh-llm-billing": "^0.3.13"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@deepseek-ai/cordis": "^4.0.
|
|
48
|
-
"@deepseek-ai/dsh-invariants": "^0.1.
|
|
49
|
-
"@deepseek-ai/dsh-typert-generator": "^0.1.
|
|
47
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
48
|
+
"@deepseek-ai/dsh-invariants": "^0.1.6-alpha.1",
|
|
49
|
+
"@deepseek-ai/dsh-typert-generator": "^0.1.6-alpha.1",
|
|
50
50
|
"@eslint/js": "^10.0.1",
|
|
51
51
|
"@types/node": "^22.20.0",
|
|
52
52
|
"eslint": "^10.9.1",
|
package/preview-detail.png
CHANGED
|
Binary file
|
package/preview-overview.png
CHANGED
|
Binary file
|