dsh-model-billing 0.1.1 → 0.2.0

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/CHANGELOG.md CHANGED
@@ -1,17 +1,61 @@
1
- # Changelog
2
-
3
- ## [0.1.1] - 2026-10-21
4
-
5
- ### Added
6
-
7
- - Show the model or models used by each turn at the end of that turn, next to the per-turn cost.
8
-
9
- ## [0.1.0] - 2026-10-20
10
-
11
- ### Added
12
-
13
- - Custom ordering for configured models in the composer model picker.
14
- - Three editable per-million-token prices per model: cache-hit input, cache-miss input, and output.
15
- - Built-in official DeepSeek peak/off-peak prices for `deepseek-flash` and `deepseek-v4-pro`, selected from each usage event's Beijing time.
16
- - Per-turn and session cost estimates with a composer summary, turn-tail amount, and a detail panel grouped by turn and model.
17
- - Local persistence at `~/.dsh/model-billing.json` with atomic writes.
1
+ # Changelog
2
+
3
+ ## [0.2.0] - 2026-10-28
4
+
5
+ ### Added
6
+
7
+ - Historical price book under Settings → Models & Cost: add, edit, or remove three-price entries for models that are no longer in the configured list. Removing a model keeps its price, so past turns keep their cost and fall back to the raw model id when no display name exists.
8
+ - Price-source labels and one-click restore: a complete custom price overrides the built-in DeepSeek table and is marked “Custom”; the official peak/off-peak table can be restored without another request.
9
+ - Unpriced-route list in the cost panel, plus a switch to hide unpriced notices.
10
+ - Separate settings sections for the turn tail and the composer cost estimate, each with a larger master switch and indented sub-switches. The composer summary now has its own `showDock` switch and the turn tail has `showTurnTail`.
11
+ - LAN and reverse-proxy access: besides loopback, a request is trusted when its Host header matches the address the socket actually listens on. A forged Host is still rejected.
12
+ - Keyboard access: the cost panel is a modal dialog that takes focus on open; the picker supports ↑/↓/Home/End with a roving tabindex.
13
+ - Regression tests for pricing consistency, arrival-time peak/off-peak, catalog and usage caches, concurrent saves, 413 preservation, archive pricing, historical billing after model removal, and trusted authorities.
14
+
15
+ ### Changed
16
+
17
+ - Peak/off-peak is now chosen from the `request/header` arrival time instead of the completion time.
18
+ - A `totalTokens` that disagrees with the three usage buckets keeps the buckets and marks the attempt partial instead of dropping it.
19
+ - Catalog and usage reads are cached; catalog caching can be bypassed with `?refresh=1`.
20
+ - The composer summary and detail panel are labelled “Composer cost estimate” (输入框下方费用估算), avoiding the “费用明细” name used by another plugin.
21
+ - Cost figures no longer carry a trailing asterisk; partial state is shown by the notice instead.
22
+ - Historical price rows use a two-row layout so provider, model, and all three prices stay readable; the remove button reads “删除”.
23
+ - Requires DSH `^0.2.1-rc.3`.
24
+
25
+ ### Fixed
26
+
27
+ - Saves that arrive together are serialized so one cannot overwrite the other; oversized bodies keep their 413 status on both `/save` and `/select`.
28
+ - Stale temporary state files are removed on load; state written by a newer plugin version is read-only instead of being overwritten.
29
+ - The picker no longer takes over when the catalog is unavailable, so the built-in picker remains usable.
30
+ - Display settings have a single source of truth in Host state; legacy localStorage values are cleared once.
31
+ - Turn-tail and detail-panel amounts share one formatter, and the settings save button is present even with no models configured.
32
+
33
+ ## [0.1.2] - 2026-10-22
34
+
35
+ ### Added
36
+
37
+ - Turn-tail display settings under Settings → Models & Cost: choose models, model provider, cost, tokens, cache-hit rate, and text labels; adjust font size, spacing, and weight.
38
+ - A switch to let another picker take over the composer model selector; turning it off restores the built-in DSH picker.
39
+ - Declared the supported DSH host range in `peerDependencies` so dsh-market can show host compatibility.
40
+
41
+ ### Changed
42
+
43
+ - Turn-tail values now use the same secondary text color as the surrounding chat metadata instead of bright white.
44
+ - Turn-tail fields are separated by spacing rather than middle dots, and the hover explanation has been removed.
45
+ - The model column in the cost details panel is wide enough to show `DeepSeek-V4.1-Flash` without truncation.
46
+
47
+ ## [0.1.1] - 2026-10-21
48
+
49
+ ### Added
50
+
51
+ - Show the model or models used by each turn at the end of that turn, next to the per-turn cost.
52
+
53
+ ## [0.1.0] - 2026-10-20
54
+
55
+ ### Added
56
+
57
+ - Custom ordering for configured models in the composer model picker.
58
+ - Three editable per-million-token prices per model: cache-hit input, cache-miss input, and output.
59
+ - Built-in official DeepSeek peak/off-peak prices for `deepseek-flash` and `deepseek-v4-pro`, selected from each usage event's Beijing time.
60
+ - Per-turn and session cost estimates with a composer summary, turn-tail amount, and a detail panel grouped by turn and model.
61
+ - Local persistence at `~/.dsh/model-billing.json` with atomic writes.
package/README.md CHANGED
@@ -1,219 +1,247 @@
1
- <p align="center">
2
- <img src="https://unpkg.com/dsh-model-billing@0.1.0/icon.svg" width="72" height="72" alt="dsh-model-billing icon">
3
- </p>
4
-
5
- <h1 align="center">dsh-model-billing</h1>
6
-
7
- <p align="center">为 DeepSeek Harness Web GUI 调整模型顺序,并按轮次估算模型费用。</p>
8
-
9
- <p align="center">
10
- <a href="https://www.npmjs.com/package/dsh-model-billing"><img alt="npm version" src="https://img.shields.io/npm/v/dsh-model-billing.svg"></a>
11
- <a href="https://github.com/likaileave/dsh-model-billing/releases"><img alt="GitHub release" src="https://img.shields.io/github/v/release/likaileave/dsh-model-billing"></a>
12
- <a href="LICENSE"><img alt="License" src="https://img.shields.io/github/license/likaileave/dsh-model-billing"></a>
13
- </p>
14
-
15
- <p align="center">
16
- <a href="#中文">中文</a> · <a href="#english">English</a> · <a href="CHANGELOG.md">Changelog</a>
17
- </p>
18
-
19
- > **AI 生成声明**:本项目的代码、文档和图标由 AI 辅助生成,并经人工审核、修改和测试;发布者对最终发布内容负责。
20
- >
21
- > **AI-generated notice**: The code, documentation, and icon in this project were generated with AI assistance, then reviewed, modified, and tested by a human; the publisher is responsible for the released content.
22
-
23
- ## 中文
24
-
25
- `dsh-model-billing` 是一个 DSH Web 客户端插件。它接管输入框右下角的模型选择器,让你按自己的顺序排列模型,并为每个模型维护三档 token 价格;会话过程中按实际 usage 估算每一轮和整个会话的费用。
26
-
27
- ### 功能
28
-
29
- - **自定义模型顺序**:在“设置 → 模型与费用”中用 ↑ / ↓ 调整已配置模型的顺序。模型选择器按这个顺序展示,跨提供方也保持你排的顺序,并用提供方名称分组。
30
- - **三档 token 价格**:每个非 DeepSeek 模型可填写每百万 tokens 的缓存命中输入、缓存未命中输入、输出价格。`0` 是有效价格,留空表示未设置。
31
- - **DeepSeek 官方价内置**:`deepseek-flash` 和 `deepseek-v4-pro` 直接使用 DeepSeek 官网的峰谷价,只读展示,不需要填写。
32
- - **逐轮费用与使用模型**:每轮结束后在轮尾显示这一轮使用的模型,以及该轮费用和缓存命中率。
33
- - **本会话费用**:输入框下方显示本会话累计估算、最近一轮费用和缓存命中率;展开后可查看按轮次和按模型的明细。
34
- - **DeepSeek 也计入金额**:每条 usage 按事件发生时的北京时间自动选择高峰或空闲价,与第三方模型一起进入统计。
35
-
36
- ### 估算口径
37
-
38
- ```text
39
- 缓存命中输入 × 命中价
40
- +(未缓存输入 + 缓存写入)× 未命中价
41
- + 输出 × 输出价
42
- ```
43
-
44
- 价格单位是“元 / 百万 tokens”。缓存写入按缓存未命中价计算,这是三档价格模型下的近似处理。金额来自会话日志中的 provider usage,实际账单以 provider 为准。
45
-
46
- ### DeepSeek 官方价
47
-
48
- 以下价格内置在 [`host.js`](host.js) 的 `DEEPSEEK_PRICES` 中,单位“元 / 百万 tokens”。来源:[DeepSeek 模型 & 价格](https://api-docs.deepseek.com/zh-cn/quick_start/pricing/)(查询于 2026-10-19)。
49
-
50
- | 模型 | 时段 | 缓存命中 | 缓存未命中 | 输出 |
51
- |---|---|---:|---:|---:|
52
- | deepseek-flash | 高峰 | 0.04 | 2 | 8 |
53
- | deepseek-flash | 空闲 | 0.02 | 1 | 4 |
54
- | deepseek-v4-pro | 高峰 | 0.30 | 9 | 27 |
55
- | deepseek-v4-pro | 空闲 | 0.15 | 4.5 | 13.5 |
56
-
57
- 高峰时段为北京时间周一至周五 `9:00–12:00`、`14:00–18:00`(中国法定节假日除外);其余时间、周末和法定节假日全天为空闲时段。空闲价是高峰价的一半。
58
-
59
- DeepSeek 价格只读,不写入本地价格配置。官网调价或节假日安排变化后,需要更新 `host.js` 中的 `DEEPSEEK_PRICES` 和 `DEEPSEEK_HOLIDAYS`;目前内置的是 2026 年节假日。
60
-
61
- ### 其他模型价格
62
-
63
- 插件**不预置**第三方模型价格。首次使用时,在“设置 → 模型与费用”中按 provider 的官方价填写三档价格即可;未填完整价格的模型不会产生金额,界面会标记“未定价”或“部分未定价”。
64
-
65
- 不同平台的缓存折扣、上下文阶梯价和活动价差异较大,建议直接使用你的实际结算价。插件不做自动抓取,也不猜测默认价。
66
-
67
- ### 安装
68
-
69
- **npm**
70
-
71
- ```sh
72
- dsh plugin --profile desktop add dsh-model-billing
73
- ```
74
-
75
- **从 dsh-market 安装**
76
-
77
- 在 dsh-market 中搜索 `dsh-model-billing` 安装。
78
-
79
- **从 GitHub Release 手动安装**
80
-
81
- 1. 从 [Releases](https://github.com/likaileave/dsh-model-billing/releases) 下载 `dsh-model-billing.tgz`。
82
- 2. 执行:
83
-
84
- ```sh
85
- dsh plugin --profile desktop add ./dsh-model-billing.tgz
86
- ```
87
-
88
- 把 `desktop` 换成你实际使用的 profile。
89
-
90
- **从源码安装**
91
-
92
- ```sh
93
- git clone https://github.com/likaileave/dsh-model-billing.git
94
- dsh plugin --profile desktop add ./dsh-model-billing
95
- ```
96
-
97
- 安装或更新后刷新 Web 页面。设置页位于“模型与费用”。
98
-
99
- ### 发布
100
-
101
- npm 包和 GitHub Release 使用相同版本号,两个渠道分别发布:
102
-
103
- - npm:<https://www.npmjs.com/package/dsh-model-billing>
104
- - GitHub Releases:<https://github.com/likaileave/dsh-model-billing/releases>
105
-
106
- 两个渠道是并列的发布渠道,不承诺自动同步。
107
-
108
- ### 数据位置
109
-
110
- 模型顺序和价格保存在:
111
-
112
- ```text
113
- ~/.dsh/model-billing.json
114
- ```
115
-
116
- 若设置了 `DSH_HOME`,则为 `$DSH_HOME/model-billing.json`。保存时先写临时文件再原子替换;文件损坏时回退为空配置,不会在未保存的情况下覆盖原文件。
117
-
118
- ### 已知限制
119
-
120
- - 模型选择器由本插件接管,依赖 `ctx.modelDirectories`。该服务不可用时,插件不会强行覆盖内置选择器,价格和费用统计不受影响。
121
- - `/model` 命令仍由 DSH 自带实现,顺序不保证与右下角选择器一致。
122
- - 一轮里的多次模型尝试(重试、失败等)会分别计入;失败调用如果 provider 没有返回 usage,则无法计入。
123
- - 只统计当前载入 Host 的会话;未打开的历史会话在进入后按需计算。
124
- - 第三方价格是静态配置,不会自动跟随官网调价。
125
-
126
- ### 开发
127
-
128
- ```sh
129
- node --check host.js
130
- node --check client.js
131
- node --test test/host.test.mjs test/routes.test.mjs
132
- ```
133
-
134
- 当前 10 个测试通过。插件没有运行时 npm 依赖。
135
-
136
- ### License
137
-
138
- [MIT](LICENSE) © 2026 likaileave
139
-
140
- ---
141
-
142
- ## English
143
-
144
- `dsh-model-billing` is a DSH Web GUI plugin that takes over the composer's model picker, lets you order configured models yourself, stores three per-million-token prices per model, and estimates per-turn and session cost from provider usage.
145
-
146
- ### Features
147
-
148
- - **Custom model order** in Settings → Models & Cost; the composer picker follows that order across providers and groups models by provider.
149
- - **Three prices per model**: cache-hit input, cache-miss input, and output, in CNY per million tokens. `0` is a valid price; blank means unset.
150
- - **Built-in official DeepSeek pricing** for `deepseek-flash` and `deepseek-v4-pro`, selected per usage event between peak and off-peak Beijing time.
151
- - **Per-turn model and amount** at the end of each turn, together with the turn's cache-hit rate.
152
- - **Session summary** below the composer plus a detail panel grouped by turn and by model.
153
- - **DeepSeek is billed too**, using the same three-price formula and the event's timestamp.
154
-
155
- ### Cost formula
156
-
157
- ```text
158
- cache-hit input × hit price
159
- + (uncached input + cache write) × miss price
160
- + output × output price
161
- ```
162
-
163
- Prices are CNY per million tokens. Cache writes are approximated at the cache-miss price. Figures come from provider usage in the session log; the provider's invoice is authoritative.
164
-
165
- The plugin ships **no third-party price defaults**. Enter your provider's actual rates in Settings → Models & Cost. Models without a complete three-price set are marked as unpriced and contribute no amount. DeepSeek prices are read-only and built into `host.js`.
166
-
167
- ### Install
168
-
169
- **npm**
170
-
171
- ```sh
172
- dsh plugin --profile desktop add dsh-model-billing
173
- ```
174
-
175
- **From dsh-market**
176
-
177
- Search for `dsh-model-billing` in dsh-market.
178
-
179
- **From a GitHub Release**
180
-
181
- Download `dsh-model-billing.tgz` from [Releases](https://github.com/likaileave/dsh-model-billing/releases), then:
182
-
183
- ```sh
184
- dsh plugin --profile desktop add ./dsh-model-billing.tgz
185
- ```
186
-
187
- **From a checkout**
188
-
189
- ```sh
190
- git clone https://github.com/likaileave/dsh-model-billing.git
191
- dsh plugin --profile desktop add ./dsh-model-billing
192
- ```
193
-
194
- Refresh the Web page after installing or updating.
195
-
196
- ### Release channels
197
-
198
- The npm package and the GitHub Release use the same version number and are published separately:
199
-
200
- - npm: <https://www.npmjs.com/package/dsh-model-billing>
201
- - GitHub Releases: <https://github.com/likaileave/dsh-model-billing/releases>
202
-
203
- They are parallel distribution channels, not an automatic mirror.
204
-
205
- ### Data
206
-
207
- Order and prices live in `~/.dsh/model-billing.json` (or `$DSH_HOME/model-billing.json`). Writes use a temporary file plus atomic replace.
208
-
209
- ### Limitations
210
-
211
- - The custom picker depends on `ctx.modelDirectories`; when that service is unavailable the plugin leaves the built-in picker alone.
212
- - The `/model` command is still implemented by DSH and may not follow the custom order.
213
- - Multiple attempts within a turn are counted separately; failed calls without provider usage cannot be counted.
214
- - Only sessions loaded by the current Host are counted.
215
- - Third-party prices are static and are not refreshed automatically.
216
-
217
- ### License
218
-
219
- [MIT](LICENSE) © 2026 likaileave
1
+ <p align="center">
2
+ <img src="https://unpkg.com/dsh-model-billing@latest/icon.svg" width="72" height="72" alt="dsh-model-billing icon">
3
+ </p>
4
+
5
+ <h1 align="center">dsh-model-billing</h1>
6
+
7
+ <p align="center">为 DeepSeek Harness Web GUI 调整模型顺序,并按轮次估算模型费用。</p>
8
+
9
+ <p align="center">
10
+ <a href="https://www.npmjs.com/package/dsh-model-billing"><img alt="npm version" src="https://img.shields.io/npm/v/dsh-model-billing.svg"></a>
11
+ <a href="https://github.com/likaileave/dsh-model-billing/releases"><img alt="GitHub release" src="https://img.shields.io/github/v/release/likaileave/dsh-model-billing"></a>
12
+ <a href="LICENSE"><img alt="License" src="https://img.shields.io/github/license/likaileave/dsh-model-billing"></a>
13
+ <a href="https://github.com/topics/dsh-plugin"><img alt="DeepSeek Harness plugin" src="https://img.shields.io/badge/DeepSeek%20Harness-dsh--plugin-4D6BFE"></a>
14
+ <a href="https://awesome-dsh-plugin.com"><img alt="Awesome DSH Plugin" src="https://awesome-dsh-plugin.com/badge.svg"></a>
15
+ </p>
16
+
17
+ <p align="center">
18
+ <a href="#中文">中文</a> · <a href="#english">English</a> · <a href="CHANGELOG.md">Changelog</a>
19
+ </p>
20
+
21
+ > **AI 生成声明**:本项目的代码、文档和图标由 AI 辅助生成,并经人工审核、修改和测试;发布者对最终发布内容负责。
22
+ >
23
+ > **AI-generated notice**: The code, documentation, and icon in this project were generated with AI assistance, then reviewed, modified, and tested by a human; the publisher is responsible for the released content.
24
+
25
+ ## 中文
26
+
27
+ `dsh-model-billing` 是一个 DSH Web 客户端插件。它可以接管输入框右下角的模型选择器,让你按自己的顺序排列模型,并为每个模型维护三档 token 价格;会话过程中按实际 usage 估算每一轮和整个会话的费用。
28
+
29
+ ### 功能
30
+
31
+ - **自定义模型顺序**:在“设置 → 模型与费用”中用 ↑ / ↓ 调整已配置模型的顺序。模型选择器按这个顺序展示,跨提供方也保持你排的顺序,并用提供方名称分组。
32
+ - **三档 token 价格**:每个模型可填写每百万 tokens 的缓存命中输入、缓存未命中输入、输出价格。`0` 是有效价格,留空表示未设置。
33
+ - **DeepSeek 官方价内置**:`deepseek-flash` 和 `deepseek-v4-pro` 默认使用 DeepSeek 官网的峰谷价,并在设置页标明“官方价”;填入完整的三档自定义价即可覆盖官方价,标为“自定义”。
34
+ - **逐轮费用与使用模型**:每轮结束后在轮尾显示这一轮使用的模型,以及该轮费用和缓存命中率。
35
+ - **可配置轮尾**:在设置中总开关轮尾显示,并可分别开关使用模型、模型提供商、费用、token、缓存命中率、文字标签,调整字号、间距和字重;也可以关闭选择器接管,恢复 DSH 内置模型选择器。输入框下方的费用估算另有独立开关。
36
+ - **本会话费用**:输入框下方显示本会话累计估算、最近一轮费用和缓存命中率;展开后可查看按轮次和按模型的明细。
37
+ - **历史价格与未定价路由**:设置页的“历史价格”区可以为已移出配置的模型补价、改价或删除,不必把模型加回列表;从模型列表删除模型不会删除它的价格,历史调用仍按保存的价格计算,查不到名称时显示原始模型 id。缺价时显示统一提示,可在设置里关闭这条提示。
38
+ - **DeepSeek 也计入金额**:每条 usage 按事件发生时的北京时间自动选择高峰或空闲价,与第三方模型一起进入统计。
39
+
40
+ ### 估算口径
41
+
42
+ ```text
43
+ 缓存命中输入 × 命中价
44
+ +(未缓存输入 + 缓存写入)× 未命中价
45
+ + 输出 × 输出价
46
+ ```
47
+
48
+ 价格单位是“元 / 百万 tokens”。缓存写入按缓存未命中价计算,这是三档价格模型下的近似处理。金额来自会话日志中的 provider usage,实际账单以 provider 为准。
49
+
50
+ ### DeepSeek 官方价
51
+
52
+ 以下价格内置在 [`host.js`](host.js) 的 `DEEPSEEK_PRICES` 中,单位“元 / 百万 tokens”。来源:[DeepSeek 模型 & 价格](https://api-docs.deepseek.com/zh-cn/quick_start/pricing/)(查询于 2026-10-19)。
53
+
54
+ | 模型 | 时段 | 缓存命中 | 缓存未命中 | 输出 |
55
+ |---|---|---:|---:|---:|
56
+ | deepseek-flash | 高峰 | 0.04 | 2 | 8 |
57
+ | deepseek-flash | 空闲 | 0.02 | 1 | 4 |
58
+ | deepseek-v4-pro | 高峰 | 0.30 | 9 | 27 |
59
+ | deepseek-v4-pro | 空闲 | 0.15 | 4.5 | 13.5 |
60
+
61
+ 高峰时段为北京时间周一至周五 `9:00–12:00`、`14:00–18:00`(中国法定节假日除外);其余时间、周末和法定节假日全天为空闲时段。空闲价是高峰价的一半。
62
+
63
+ DeepSeek 价格默认只读展示;如需按其他渠道或折扣价计费,可在设置页点“自定义价格”填入完整三档价格,保存后以自定义价为准并标为“自定义”;三项清空后保存即可恢复官方价。官网调价或节假日安排变化后,需要更新 `host.js` 中的 `DEEPSEEK_PRICES` 和 `DEEPSEEK_HOLIDAYS`;目前内置的是 2026 年节假日。
64
+
65
+ ### 其他模型价格
66
+
67
+ 插件**不预置**第三方模型价格。首次使用时,在“设置 → 模型与费用”中按 provider 的官方价填写三档价格即可;未填完整价格的模型不会产生金额,界面会标记“未定价”或“部分未定价”。
68
+
69
+ 不同平台的缓存折扣、上下文阶梯价和活动价差异较大,建议直接使用你的实际结算价。插件不做自动抓取,也不猜测默认价。
70
+
71
+ ### 安装
72
+
73
+ 需要 DSH `^0.2.1-rc.3`。
74
+
75
+ **npm**
76
+
77
+ ```sh
78
+ dsh plugin --profile desktop add dsh-model-billing
79
+ ```
80
+
81
+ **从 dsh-market 安装**
82
+
83
+ 在 dsh-market 中搜索 `dsh-model-billing` 安装。
84
+
85
+ **从 GitHub Release 手动安装**
86
+
87
+ 1. 从 [Releases](https://github.com/likaileave/dsh-model-billing/releases) 下载 `dsh-model-billing.tgz`。
88
+ 2. 执行:
89
+
90
+ ```sh
91
+ dsh plugin --profile desktop add ./dsh-model-billing.tgz
92
+ ```
93
+
94
+ 把 `desktop` 换成你实际使用的 profile。
95
+
96
+ **从源码安装**
97
+
98
+ ```sh
99
+ git clone https://github.com/likaileave/dsh-model-billing.git
100
+ dsh plugin --profile desktop add ./dsh-model-billing
101
+ ```
102
+
103
+ 安装或更新后刷新 Web 页面。设置页位于“模型与费用”。
104
+
105
+ ### 发布
106
+
107
+ npm 包和 GitHub Release 使用相同版本号,两个渠道分别发布:
108
+
109
+ - npm:<https://www.npmjs.com/package/dsh-model-billing>
110
+ - GitHub Releases:<https://github.com/likaileave/dsh-model-billing/releases>
111
+
112
+ 两个渠道是并列的发布渠道,不承诺自动同步。
113
+
114
+ ### 数据位置
115
+
116
+ 模型顺序和价格保存在:
117
+
118
+ ```text
119
+ ~/.dsh/model-billing.json
120
+ ```
121
+
122
+ 若设置了 `DSH_HOME`,则为 `$DSH_HOME/model-billing.json`。保存时先写临时文件再原子替换;文件损坏时回退为空配置,不会在未保存的情况下覆盖原文件。
123
+
124
+ ### 已知限制
125
+
126
+ - DeepSeek 官方峰谷价只对 `deepseek` 开头的官方 provider(如 `deepseek-account`)且模型名在官方价表内时生效;`openrouter`、`my-deepseek-relay` 这类第三方即使模型名含 `deepseek` 也按普通规则计费,可自行填价。
127
+ - 显示设置(轮尾开关、字号、间距、是否接管选择器等)只保存在 Host 的 `model-billing.json` 里,不再镜像到浏览器 localStorage,因此换设备或换浏览器不会不同步;旧版本写入的 localStorage 键会被自动清除。
128
+ - 除本机回环地址(`127.0.0.1` / `localhost` / `[::1]`)外,Host 头与该连接实际监听的地址(`socket.localAddress` + `localPort`)一致时也会放行,因此 `--host 0.0.0.0` 后用局域网 IP 访问可用;反向代理若改写了 Host,需要让它保留原 Host 或与实际监听地址一致,否则四个路由返回 403,此时模型选择器回退为 DSH 内置版本(费用与排序功能不可用),不会影响正常聊天。
129
+ - 模型选择器由本插件接管,依赖 `ctx.modelDirectories`。该服务不可用或 catalog 尚未加载完成时,插件不会强行覆盖内置选择器,价格和费用统计不受影响。
130
+ - `/model` 命令仍由 DSH 自带实现,顺序不保证与右下角选择器一致。
131
+ - 一轮里的多次模型尝试(重试、失败等)会分别计入;失败调用如果 provider 没有返回 usage,则无法计入。
132
+ - 只统计当前载入 Host 的会话;未打开的历史会话在进入后按需计算。
133
+ - provider 返回的 `totalTokens` 与三档明细不一致时,保留三档明细并把该轮标为“部分未定价/不完整”,不再整条丢弃,避免大幅少报。
134
+ - 第三方价格是静态配置,不会自动跟随官网调价;内置节假日表目前只覆盖 2026 年,2027 年起需更新 `DEEPSEEK_HOLIDAYS`。
135
+
136
+ ### 开发
137
+
138
+ ```sh
139
+ node --check host.js
140
+ node --check client.js
141
+ node --test test/*.test.mjs
142
+ ```
143
+
144
+ 当前 25 个测试通过。插件没有运行时 npm 依赖。
145
+
146
+ ### License
147
+
148
+ [MIT](LICENSE) © 2026 likaileave
149
+
150
+ ---
151
+
152
+ ## English
153
+
154
+ `dsh-model-billing` is a DSH Web GUI plugin that can take over the composer's model picker, lets you order configured models yourself, stores three per-million-token prices per model, and estimates per-turn and session cost from provider usage.
155
+
156
+ ### Features
157
+
158
+ - **Custom model order** in Settings → Models & Cost; the composer picker follows that order across providers and groups models by provider.
159
+ - **Three prices per model**: cache-hit input, cache-miss input, and output, in CNY per million tokens. `0` is a valid price; blank means unset.
160
+ - **Built-in official DeepSeek pricing** for `deepseek-flash` and `deepseek-v4-pro`, selected per usage event between peak and off-peak Beijing time. A complete set of custom prices overrides the table and is labelled "Custom".
161
+ - **Per-turn model and amount** at the end of each turn, together with the turn's cache-hit rate.
162
+ - **Configurable turn tail**: a master switch for the turn tail, plus individual toggles for models, provider names, cost, tokens, cache-hit rate, and text labels; adjust font size, spacing, and weight; or disable the picker takeover to restore the built-in DSH picker. The composer cost estimate has its own switch.
163
+ - **Session summary** below the composer plus a detail panel grouped by turn and by model.
164
+ - **Historical prices and unpriced routes**: the "Historical prices" section in Settings lets you add, edit, or remove prices for models no longer in your order without re-adding them. Removing a model from the list keeps its price, so historical calls still bill correctly; when the name is unknown, the raw model id is shown. Unpriced calls share one notice that can be switched off.
165
+ - **DeepSeek is billed too**, using the same three-price formula and the event's timestamp.
166
+
167
+ ### Cost formula
168
+
169
+ ```text
170
+ cache-hit input × hit price
171
+ + (uncached input + cache write) × miss price
172
+ + output × output price
173
+ ```
174
+
175
+ Prices are CNY per million tokens. Cache writes are approximated at the cache-miss price. Figures come from provider usage in the session log; the provider's invoice is authoritative.
176
+
177
+ The plugin ships **no third-party price defaults**. Enter your provider's actual rates in Settings → Models & Cost. Models without a complete three-price set are marked as unpriced and contribute no amount. DeepSeek prices come from the built-in official table; enter a complete custom set to override it, or clear all three fields to restore the official rates.
178
+
179
+ ### Install
180
+
181
+ Requires DSH `^0.2.1-rc.3`.
182
+
183
+ **npm**
184
+
185
+ ```sh
186
+ dsh plugin --profile desktop add dsh-model-billing
187
+ ```
188
+
189
+ **From dsh-market**
190
+
191
+ Search for `dsh-model-billing` in dsh-market.
192
+
193
+ **From a GitHub Release**
194
+
195
+ Download `dsh-model-billing.tgz` from [Releases](https://github.com/likaileave/dsh-model-billing/releases), then:
196
+
197
+ ```sh
198
+ dsh plugin --profile desktop add ./dsh-model-billing.tgz
199
+ ```
200
+
201
+ **From a checkout**
202
+
203
+ ```sh
204
+ git clone https://github.com/likaileave/dsh-model-billing.git
205
+ dsh plugin --profile desktop add ./dsh-model-billing
206
+ ```
207
+
208
+ Refresh the Web page after installing or updating.
209
+
210
+ ### Release channels
211
+
212
+ The npm package and the GitHub Release use the same version number and are published separately:
213
+
214
+ - npm: <https://www.npmjs.com/package/dsh-model-billing>
215
+ - GitHub Releases: <https://github.com/likaileave/dsh-model-billing/releases>
216
+
217
+ They are parallel distribution channels, not an automatic mirror.
218
+
219
+ ### Data
220
+
221
+ Order and prices live in `~/.dsh/model-billing.json` (or `$DSH_HOME/model-billing.json`). Writes use a temporary file plus atomic replace.
222
+
223
+ ### Limitations
224
+
225
+ - Official DeepSeek peak/off-peak rates apply only to providers whose id starts with `deepseek` (such as `deepseek-account`) and to models present in the official table. A third-party route such as `openrouter` or `my-deepseek-relay` is billed by the normal rules even when the model name contains `deepseek`, and you can price it yourself.
226
+ - Display settings (turn-tail switches, font size, spacing, picker takeover) are stored only in the Host's `model-billing.json`; they are no longer mirrored into browser localStorage, so they do not sync across devices or browsers. Any key written by older builds is cleared automatically.
227
+ - Besides loopback (`127.0.0.1`, `localhost`, `[::1]`), a request is accepted when its Host header matches the address this socket actually listens on (`socket.localAddress` + `localPort`), so a LAN IP works with `--host 0.0.0.0`. A reverse proxy that rewrites Host must preserve it or match the listening address, otherwise the four routes return 403 and the picker falls back to the DSH built-in one; chat itself keeps working.
228
+ - The custom picker depends on `ctx.modelDirectories`; when that service is unavailable or the catalog has not loaded yet, the plugin leaves the built-in picker alone.
229
+ - The `/model` command is still implemented by DSH and may not follow the custom order.
230
+ - Multiple attempts within a turn are counted separately; failed calls without provider usage cannot be counted.
231
+ - Only sessions loaded by the current Host are counted.
232
+ - When a provider's `totalTokens` disagrees with its three buckets, the buckets are kept and the turn is marked partial instead of being dropped, which avoids large under-reporting.
233
+ - Third-party prices are static and are not refreshed automatically. The bundled holiday table covers 2026 only; update `DEEPSEEK_HOLIDAYS` for 2027 onward.
234
+
235
+ ### Development
236
+
237
+ ```sh
238
+ node --check host.js
239
+ node --check client.js
240
+ node --test test/*.test.mjs
241
+ ```
242
+
243
+ 25 tests currently pass. The plugin has no runtime npm dependencies.
244
+
245
+ ### License
246
+
247
+ [MIT](LICENSE) © 2026 likaileave