dsh-model-billing 0.1.0 → 0.1.1

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,5 +1,11 @@
1
1
  # Changelog
2
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
+
3
9
  ## [0.1.0] - 2026-10-20
4
10
 
5
11
  ### Added
package/README.md CHANGED
@@ -1,15 +1,25 @@
1
1
  <p align="center">
2
- <img src="icon.svg" width="72" height="72" alt="dsh-model-billing icon">
2
+ <img src="https://unpkg.com/dsh-model-billing@0.1.0/icon.svg" width="72" height="72" alt="dsh-model-billing icon">
3
3
  </p>
4
4
 
5
5
  <h1 align="center">dsh-model-billing</h1>
6
6
 
7
7
  <p align="center">为 DeepSeek Harness Web GUI 调整模型顺序,并按轮次估算模型费用。</p>
8
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
+
9
15
  <p align="center">
10
16
  <a href="#中文">中文</a> · <a href="#english">English</a> · <a href="CHANGELOG.md">Changelog</a>
11
17
  </p>
12
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
+
13
23
  ## 中文
14
24
 
15
25
  `dsh-model-billing` 是一个 DSH Web 客户端插件。它接管输入框右下角的模型选择器,让你按自己的顺序排列模型,并为每个模型维护三档 token 价格;会话过程中按实际 usage 估算每一轮和整个会话的费用。
@@ -19,7 +29,7 @@
19
29
  - **自定义模型顺序**:在“设置 → 模型与费用”中用 ↑ / ↓ 调整已配置模型的顺序。模型选择器按这个顺序展示,跨提供方也保持你排的顺序,并用提供方名称分组。
20
30
  - **三档 token 价格**:每个非 DeepSeek 模型可填写每百万 tokens 的缓存命中输入、缓存未命中输入、输出价格。`0` 是有效价格,留空表示未设置。
21
31
  - **DeepSeek 官方价内置**:`deepseek-flash` 和 `deepseek-v4-pro` 直接使用 DeepSeek 官网的峰谷价,只读展示,不需要填写。
22
- - **逐轮费用**:每轮结束后在轮尾显示该轮费用。
32
+ - **逐轮费用与使用模型**:每轮结束后在轮尾显示这一轮使用的模型,以及该轮费用和缓存命中率。
23
33
  - **本会话费用**:输入框下方显示本会话累计估算、最近一轮费用和缓存命中率;展开后可查看按轮次和按模型的明细。
24
34
  - **DeepSeek 也计入金额**:每条 usage 按事件发生时的北京时间自动选择高峰或空闲价,与第三方模型一起进入统计。
25
35
 
@@ -56,7 +66,13 @@ DeepSeek 价格只读,不写入本地价格配置。官网调价或节假日
56
66
 
57
67
  ### 安装
58
68
 
59
- **从 dsh-market 安装(上架后)**
69
+ **npm**
70
+
71
+ ```sh
72
+ dsh plugin --profile desktop add dsh-model-billing
73
+ ```
74
+
75
+ **从 dsh-market 安装**
60
76
 
61
77
  在 dsh-market 中搜索 `dsh-model-billing` 安装。
62
78
 
@@ -80,6 +96,15 @@ dsh plugin --profile desktop add ./dsh-model-billing
80
96
 
81
97
  安装或更新后刷新 Web 页面。设置页位于“模型与费用”。
82
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
+
83
108
  ### 数据位置
84
109
 
85
110
  模型顺序和价格保存在:
@@ -123,7 +148,7 @@ node --test test/host.test.mjs test/routes.test.mjs
123
148
  - **Custom model order** in Settings → Models & Cost; the composer picker follows that order across providers and groups models by provider.
124
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.
125
150
  - **Built-in official DeepSeek pricing** for `deepseek-flash` and `deepseek-v4-pro`, selected per usage event between peak and off-peak Beijing time.
126
- - **Per-turn amount** at the end of each turn.
151
+ - **Per-turn model and amount** at the end of each turn, together with the turn's cache-hit rate.
127
152
  - **Session summary** below the composer plus a detail panel grouped by turn and by model.
128
153
  - **DeepSeek is billed too**, using the same three-price formula and the event's timestamp.
129
154
 
@@ -141,13 +166,25 @@ The plugin ships **no third-party price defaults**. Enter your provider's actual
141
166
 
142
167
  ### Install
143
168
 
144
- From dsh-market after listing, or manually from a [Release](https://github.com/likaileave/dsh-model-billing/releases):
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:
145
182
 
146
183
  ```sh
147
184
  dsh plugin --profile desktop add ./dsh-model-billing.tgz
148
185
  ```
149
186
 
150
- From a checkout:
187
+ **From a checkout**
151
188
 
152
189
  ```sh
153
190
  git clone https://github.com/likaileave/dsh-model-billing.git
@@ -156,6 +193,15 @@ dsh plugin --profile desktop add ./dsh-model-billing
156
193
 
157
194
  Refresh the Web page after installing or updating.
158
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
+
159
205
  ### Data
160
206
 
161
207
  Order and prices live in `~/.dsh/model-billing.json` (or `$DSH_HOME/model-billing.json`). Writes use a temporary file plus atomic replace.
package/client.js CHANGED
@@ -78,6 +78,8 @@ window.__ModuleLoader__.load({
78
78
  partialShort: '部分未定价',
79
79
  partialNote: '有非 DeepSeek 调用尚未配置完整价格,合计只包含已定价部分。',
80
80
  estimateNote: '金额由会话日志中的 token 用量和缓存命中估算;DeepSeek 按事件时间使用内置峰谷价,实际账单以提供方为准。',
81
+ turnModelTitle: '本轮实际调用的模型(多个模型按名称排序)',
82
+ listSep: '、',
81
83
  turnCost: '费用',
82
84
  turnCostTitle: '根据本轮 token 用量、缓存命中与价格估算',
83
85
  chooseModel: '选择模型',
@@ -150,6 +152,8 @@ window.__ModuleLoader__.load({
150
152
  partialShort: 'partly unpriced',
151
153
  partialNote: 'Some non-DeepSeek calls do not have a complete price set; the total includes only priced calls.',
152
154
  estimateNote: 'Amounts are estimated from token usage and cache hits in the session log; DeepSeek uses built-in peak/off-peak prices at the event time, and the provider bill is authoritative.',
155
+ turnModelTitle: 'Models actually called this turn (multiple models sorted by name)',
156
+ listSep: ', ',
153
157
  turnCost: 'Cost',
154
158
  turnCostTitle: 'Estimated from this turn’s tokens, cache hits and prices',
155
159
  chooseModel: 'Select model',
@@ -1030,20 +1034,42 @@ window.__ModuleLoader__.load({
1030
1034
  // Per-turn cost chip
1031
1035
  // ---------------------------------------------------------------------
1032
1036
 
1033
- function TurnCost({ turn, sessionId, useProjection }) {
1037
+ function turnModelText(models, translate) {
1038
+ const names = (models ?? []).map((entry) => {
1039
+ if (entry === undefined || entry === null) return null
1040
+ if (typeof entry.name === 'string' && entry.name !== '') return entry.name
1041
+ return entry.provider !== undefined && entry.model !== undefined
1042
+ ? `${entry.provider} / ${entry.model}`
1043
+ : null
1044
+ }).filter((name) => name !== null)
1045
+ if (names.length === 0) return null
1046
+ return names.join(translate('listSep'))
1047
+ }
1048
+
1049
+ function TurnCost(props) {
1050
+ const { turn } = props
1051
+ // The turnTail owner hands over the whole folded turn life cycle, not just its number.
1052
+ const turnNumber = turn === null || turn === undefined
1053
+ ? undefined
1054
+ : typeof turn === 'number' ? turn : turn.turn
1055
+ const sessionId = props.sessionId
1056
+ const useProjection = props.useProjection
1034
1057
  const translate = useT()
1035
- const tokenUsage = useProjection('tokenUsage')
1058
+ const tokenUsage = typeof useProjection === 'function' ? useProjection('tokenUsage') : undefined
1036
1059
  const revision = tokenUsageRevision(tokenUsage)
1037
1060
  const usage = useSessionUsage(sessionId, revision)
1038
- const row = usage.data?.turns?.find((entry) => entry.turn === turn)
1039
- if (row === undefined || row.hasBillable !== true) return null
1061
+ const rows = usage.data?.turns
1062
+ const row = rows?.find((entry) => entry.turn === turnNumber)
1063
+ if (row === undefined) return null
1040
1064
  const costText = row.costKnown
1041
1065
  ? `≈ ${formatMoney(row.cost)}`
1042
1066
  : row.pricedAttempts > 0 ? `≈ ${formatMoney(row.cost)}*` : translate('pricePlaceholder')
1067
+ const modelText = turnModelText(row.models, translate)
1043
1068
  return h('span', {
1044
1069
  className: 'mb-turn',
1045
- title: translate('turnCostTitle'),
1070
+ title: `${translate('turnModelTitle')}\n${translate('turnCostTitle')}`,
1046
1071
  },
1072
+ modelText === null ? null : h(Fragment, null, translate('model'), ' ', h('strong', null, modelText), ' · '),
1047
1073
  translate('turnCost'), ' ', h('strong', null, costText),
1048
1074
  row.cacheHit === null ? null : ` · ${translate('cacheHit')} ${formatPercent(row.cacheHit)}`,
1049
1075
  row.partial ? ` · ${translate('partialShort')}` : '')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-model-billing",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "type": "module",
5
5
  "description": "Reorder models in the DSH composer picker, store three per-million-token prices, and estimate per-turn and session cost with built-in DeepSeek peak/off-peak rates.",
6
6
  "keywords": [