@kenz1117/dsh-ui-usage-billing 0.2.3 → 0.2.5
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/README.md +52 -4
- package/lib/client.js +73 -72
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,6 +14,11 @@ DeepSeek Harness 计费仪表盘插件。从持久化会话日志实时聚合模
|
|
|
14
14
|
- **真实用量**:服务端从会话日志实时聚合,无需手工维护统计文件。
|
|
15
15
|
- **模型健康探测**:模型行的圆点反映各厂商接入状态(正常绿 / 异常红 / 未接入灰)。
|
|
16
16
|
- **订阅计划豁免**:走 coding/token 套餐的模型照常统计 token,费用记 0。
|
|
17
|
+
- **余额查询**:模型计费明细按厂商显示「余额」列,DeepSeek 通过官方余额 API 实时查询;未配置 / Key 无效 / 服务不可达均有状态提示,扩展点可接更多厂商。
|
|
18
|
+
- **实时汇率与定价**:启动时自动拉取腾讯财经行情(USD→CNY)与 OpenRouter 官方模型价,失败自动降级内置默认值;此后每 6 小时自动刷新,单价表标注「今日汇率」与实时 / 内置徽标。
|
|
19
|
+
- **动态刷新**:侧边栏入口与仪表盘每 30 秒自动更新,无需重启或手动刷新。
|
|
20
|
+
- **更新时间**:模型计费明细表头显示最近一次统计的更新时间,精确到时分秒。
|
|
21
|
+
- **北京时间统一**:统计聚合与仪表盘日期一律按北京时间归天,跨零点不漂移。
|
|
17
22
|
- **离线自包含**:无图表库、无外部 CDN,全部使用设计令牌,适配深色/浅色主题。
|
|
18
23
|
|
|
19
24
|
## 截图
|
|
@@ -52,21 +57,23 @@ npm install @kenz1117/dsh-ui-usage-billing
|
|
|
52
57
|
├─ GET /api/billing/usage-stats ────────▶ ├─ sessionPersistence 遍历持久化会话日志
|
|
53
58
|
│ ├─ 按 request/header 归属模型
|
|
54
59
|
│ ├─ token 按缓存命中 / 未命中分桶
|
|
55
|
-
│ └─
|
|
60
|
+
│ └─ 按实时单价表估算费用(人民币)
|
|
61
|
+
├─ GET /api/billing/pricing ────────────▶ ├─ 腾讯财经 / OpenRouter 实时汇率与模型价
|
|
62
|
+
├─ GET /api/billing/balance ────────────▶ ├─ DeepSeek 官方余额 API(凭据 seam 取 key)
|
|
56
63
|
├─ llm.models 健康探测 ─────────────────▶ └─ 返回聚合统计 JSON
|
|
57
64
|
└─ 渲染仪表盘
|
|
58
65
|
```
|
|
59
66
|
|
|
60
|
-
- **服务端**(`src/index.ts`):注入 `webServer` 与 `
|
|
67
|
+
- **服务端**(`src/index.ts`):注入 `webServer`、`sessionPersistence` 与 `credentials`,注册 `GET /api/billing/usage-stats`、`/api/billing/pricing`、`/api/billing/balance`。每次调用折叠全部持久化日志:一次 LLM 调用归属到其前置 `request/header` 记录的模型,token 拆分到缓存命中 / 未命中桶,日期按本机时区归天。聚合逻辑见 `src/aggregate.ts`。
|
|
61
68
|
- **浏览器端**(`src/client/`):请求上述接口渲染仪表盘,通过 `llm.models` 探测各厂商连接状态。真实数据到达前显示全零空快照,不展示伪造样本。
|
|
62
69
|
|
|
63
70
|
## 计费引擎
|
|
64
71
|
|
|
65
|
-
单价表(`src/client/pricing.ts
|
|
72
|
+
单价表(`src/client/pricing.ts`)采用**原生币种**存储:国内厂商直接录入人民币价格,国外厂商录入美元价格。费用统一以人民币计算与展示——美元模型按**实时汇率**折算,国内模型全程不经过汇率换算。启动时服务端拉取实时汇率与模型价(`src/pricing-fetch.ts`):USD→CNY 优先腾讯财经行情(免 key、国内可达),失败依次降级 open.er-api 与内置默认值;之后每 6 小时后台刷新,单价表弹窗标注「今日汇率」与实时 / 内置徽标。
|
|
66
73
|
|
|
67
74
|
```
|
|
68
75
|
cost(CNY)= (missInput × p_input + cacheHit × p_cacheHit + output × p_output) / 10⁶
|
|
69
|
-
——
|
|
76
|
+
—— 价格为原生币种;美元模型按实时 USD → CNY 汇率折算
|
|
70
77
|
```
|
|
71
78
|
|
|
72
79
|
统计中的 `input` 为总输入(cacheHit + cacheMiss),估算按命中 / 未命中分拆计价,避免重复计费。支持双档计费的模型按 `DEFAULT_PEAK_SHARE`(默认 0.5)混合高峰与低谷档。
|
|
@@ -100,6 +107,45 @@ cost(CNY)= (missInput × p_input + cacheHit × p_cacheHit + output × p_outp
|
|
|
100
107
|
|
|
101
108
|
## HTTP API
|
|
102
109
|
|
|
110
|
+
### `GET /api/billing/pricing`
|
|
111
|
+
|
|
112
|
+
实时定价文档(汇率 + 模型价,6 小时后台刷新),浏览器端单价表数据源:
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"source": "live",
|
|
117
|
+
"rate": 7.11,
|
|
118
|
+
"rateTime": "2026-08-16T12:00:00+08:00",
|
|
119
|
+
"models": {
|
|
120
|
+
"deepseek-chat": { "input": 0.5, "output": 2, "cacheHit": 0.1 }
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
`source` 为 `live`(腾讯财经 / OpenRouter 拉到)或 `builtin`(全部降级内置默认值);`rate` 为 USD→CNY 实时汇率。
|
|
126
|
+
|
|
127
|
+
### `GET /api/billing/balance`
|
|
128
|
+
|
|
129
|
+
各接入厂商账户余额(凭据 seam 按 `balanceApiKeyEnv` 取 key):
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"balances": [
|
|
134
|
+
{
|
|
135
|
+
"provider": "deepseek",
|
|
136
|
+
"displayName": "DeepSeek",
|
|
137
|
+
"ok": true,
|
|
138
|
+
"currency": "CNY",
|
|
139
|
+
"total": 12.34,
|
|
140
|
+
"available": 10.56,
|
|
141
|
+
"granted": 1.78
|
|
142
|
+
}
|
|
143
|
+
]
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
查询失败时对应条目带 `error`(`unconfigured` / `unauthorized` / `unreachable`),表格按此渲染状态提示。
|
|
148
|
+
|
|
103
149
|
### `GET /api/billing/usage-stats`
|
|
104
150
|
|
|
105
151
|
聚合统计文档,浏览器端数据源:
|
|
@@ -135,6 +181,8 @@ cost(CNY)= (missInput × p_input + cacheHit × p_cacheHit + output × p_outp
|
|
|
135
181
|
| 字段 | 默认 | 说明 |
|
|
136
182
|
|---|---|---|
|
|
137
183
|
| `statsPath` | 未设置 | 回退统计文件 `.dsh-usage-stats.json` 的绝对路径(`sessionPersistence` 不可用时生效) |
|
|
184
|
+
| `balanceApiKeyEnv` | `DEEPSEEK_API_KEY` | 余额查询使用的 DeepSeek 凭据引用(环境变量名),经 `ctx.credentials` 解析 |
|
|
185
|
+
| `subscriptionProviders` | `kimi-coding`、`xiaomi-token-plan-cn` | 订阅制(coding / token 套餐)provider id 列表,照常统计 token、费用记 0 |
|
|
138
186
|
|
|
139
187
|
## 开发
|
|
140
188
|
|
package/lib/client.js
CHANGED
|
@@ -33,93 +33,93 @@ window.__ModuleLoader__.load({
|
|
|
33
33
|
document.head.appendChild(tag);
|
|
34
34
|
}
|
|
35
35
|
var UsageBilling_module_css_default = {
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
36
|
+
"triggerAmount": "VWh0dG_triggerAmount",
|
|
37
|
+
"bandPriceOff": "VWh0dG_bandPriceOff",
|
|
38
|
+
"chartBar": "VWh0dG_chartBar",
|
|
39
|
+
"bandTag": "VWh0dG_bandTag",
|
|
40
|
+
"triggerMonth": "VWh0dG_triggerMonth",
|
|
41
|
+
"chartTooltipSwatch": "VWh0dG_chartTooltipSwatch",
|
|
42
|
+
"deltaDown": "VWh0dG_deltaDown",
|
|
43
|
+
"modelTable": "VWh0dG_modelTable",
|
|
44
|
+
"pricingTable": "VWh0dG_pricingTable",
|
|
45
|
+
"heroSideItem": "VWh0dG_heroSideItem",
|
|
41
46
|
"modelDot": "VWh0dG_modelDot",
|
|
42
|
-
"
|
|
47
|
+
"pricingChevronOpen": "VWh0dG_pricingChevronOpen",
|
|
48
|
+
"kpiValue": "VWh0dG_kpiValue",
|
|
49
|
+
"flatTag": "VWh0dG_flatTag",
|
|
50
|
+
"panel": "VWh0dG_panel",
|
|
51
|
+
"triggerAmountSub": "VWh0dG_triggerAmountSub",
|
|
52
|
+
"dashboard": "VWh0dG_dashboard",
|
|
53
|
+
"kpiTile": "VWh0dG_kpiTile",
|
|
54
|
+
"heroMain": "VWh0dG_heroMain",
|
|
55
|
+
"healthBad": "VWh0dG_healthBad",
|
|
43
56
|
"rateBadge": "VWh0dG_rateBadge",
|
|
57
|
+
"heroMeta": "VWh0dG_heroMeta",
|
|
58
|
+
"triggerDivider": "VWh0dG_triggerDivider",
|
|
44
59
|
"bandTagOff": "VWh0dG_bandTagOff",
|
|
60
|
+
"triggerToday": "VWh0dG_triggerToday",
|
|
45
61
|
"heroSideLabel": "VWh0dG_heroSideLabel",
|
|
46
|
-
"triggerAmountSub": "VWh0dG_triggerAmountSub",
|
|
47
|
-
"healthIdle": "VWh0dG_healthIdle",
|
|
48
|
-
"chartGrid": "VWh0dG_chartGrid",
|
|
49
|
-
"deltaUp": "VWh0dG_deltaUp",
|
|
50
|
-
"healthBadgeOk": "VWh0dG_healthBadgeOk",
|
|
51
|
-
"kpiValue": "VWh0dG_kpiValue",
|
|
52
|
-
"kpiGrid": "VWh0dG_kpiGrid",
|
|
53
|
-
"heroSide": "VWh0dG_heroSide",
|
|
54
|
-
"chartDot": "VWh0dG_chartDot",
|
|
55
|
-
"heroSideItem": "VWh0dG_heroSideItem",
|
|
56
|
-
"chartTooltipSwatch": "VWh0dG_chartTooltipSwatch",
|
|
57
|
-
"deltaDown": "VWh0dG_deltaDown",
|
|
58
|
-
"rateBadgeLive": "VWh0dG_rateBadgeLive",
|
|
59
|
-
"pricingChevron": "VWh0dG_pricingChevron",
|
|
60
|
-
"heroLabel": "VWh0dG_heroLabel",
|
|
61
|
-
"trigger": "VWh0dG_trigger",
|
|
62
|
-
"chartAxisLabel": "VWh0dG_chartAxisLabel",
|
|
63
|
-
"modelTable": "VWh0dG_modelTable",
|
|
64
|
-
"chartBar": "VWh0dG_chartBar",
|
|
65
62
|
"modelProvider": "VWh0dG_modelProvider",
|
|
66
|
-
"
|
|
63
|
+
"chartEmpty": "VWh0dG_chartEmpty",
|
|
64
|
+
"heroLabel": "VWh0dG_heroLabel",
|
|
65
|
+
"deltaUp": "VWh0dG_deltaUp",
|
|
67
66
|
"chartCrosshair": "VWh0dG_chartCrosshair",
|
|
68
|
-
"
|
|
69
|
-
"
|
|
67
|
+
"chartTooltipRow": "VWh0dG_chartTooltipRow",
|
|
68
|
+
"chartLegendBar": "VWh0dG_chartLegendBar",
|
|
69
|
+
"triggerIcon": "VWh0dG_triggerIcon",
|
|
70
|
+
"railButton": "VWh0dG_railButton",
|
|
71
|
+
"chartStack": "VWh0dG_chartStack",
|
|
72
|
+
"panelTitle": "VWh0dG_panelTitle",
|
|
73
|
+
"modelName": "VWh0dG_modelName",
|
|
74
|
+
"chartAxisLabel": "VWh0dG_chartAxisLabel",
|
|
75
|
+
"heroSide": "VWh0dG_heroSide",
|
|
76
|
+
"healthOk": "VWh0dG_healthOk",
|
|
77
|
+
"heroSideValue": "VWh0dG_heroSideValue",
|
|
78
|
+
"kpiDetail": "VWh0dG_kpiDetail",
|
|
70
79
|
"pricingToggleText": "VWh0dG_pricingToggleText",
|
|
71
80
|
"closeButton": "VWh0dG_closeButton",
|
|
72
|
-
"costCol": "VWh0dG_costCol",
|
|
73
|
-
"bandPriceOff": "VWh0dG_bandPriceOff",
|
|
74
|
-
"hero": "VWh0dG_hero",
|
|
75
|
-
"heroMain": "VWh0dG_heroMain",
|
|
76
|
-
"chartEmpty": "VWh0dG_chartEmpty",
|
|
77
|
-
"triggerMeta": "VWh0dG_triggerMeta",
|
|
78
|
-
"kpiDetail": "VWh0dG_kpiDetail",
|
|
79
|
-
"pricingChevronOpen": "VWh0dG_pricingChevronOpen",
|
|
80
81
|
"dashboardTitle": "VWh0dG_dashboardTitle",
|
|
82
|
+
"dashboardBody": "VWh0dG_dashboardBody",
|
|
83
|
+
"chartLegendLine": "VWh0dG_chartLegendLine",
|
|
84
|
+
"chartLegend": "VWh0dG_chartLegend",
|
|
85
|
+
"numCol": "VWh0dG_numCol",
|
|
86
|
+
"healthBadgeBad": "VWh0dG_healthBadgeBad",
|
|
87
|
+
"kpiGrid": "VWh0dG_kpiGrid",
|
|
88
|
+
"chartDot": "VWh0dG_chartDot",
|
|
81
89
|
"healthDot": "VWh0dG_healthDot",
|
|
82
|
-
"
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"
|
|
86
|
-
"
|
|
87
|
-
"modelCell": "VWh0dG_modelCell",
|
|
88
|
-
"healthOk": "VWh0dG_healthOk",
|
|
89
|
-
"chartWrap": "VWh0dG_chartWrap",
|
|
90
|
-
"bandTag": "VWh0dG_bandTag",
|
|
91
|
-
"kpiGreen": "VWh0dG_kpiGreen",
|
|
92
|
-
"flatTag": "VWh0dG_flatTag",
|
|
90
|
+
"pricingChevron": "VWh0dG_pricingChevron",
|
|
91
|
+
"emptyRow": "VWh0dG_emptyRow",
|
|
92
|
+
"rateBadgeBuiltin": "VWh0dG_rateBadgeBuiltin",
|
|
93
|
+
"healthIdle": "VWh0dG_healthIdle",
|
|
94
|
+
"trigger": "VWh0dG_trigger",
|
|
93
95
|
"dashboardHead": "VWh0dG_dashboardHead",
|
|
94
|
-
"pricingTable": "VWh0dG_pricingTable",
|
|
95
|
-
"kpiLabel": "VWh0dG_kpiLabel",
|
|
96
|
-
"numCol": "VWh0dG_numCol",
|
|
97
96
|
"tableScroll": "VWh0dG_tableScroll",
|
|
98
|
-
"
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
97
|
+
"kpiGreen": "VWh0dG_kpiGreen",
|
|
98
|
+
"na": "VWh0dG_na",
|
|
99
|
+
"healthBadgeOk": "VWh0dG_healthBadgeOk",
|
|
100
|
+
"hero": "VWh0dG_hero",
|
|
101
|
+
"chartTooltipDate": "VWh0dG_chartTooltipDate",
|
|
102
|
+
"modelCell": "VWh0dG_modelCell",
|
|
103
|
+
"planTag": "VWh0dG_planTag",
|
|
104
|
+
"costCol": "VWh0dG_costCol",
|
|
105
|
+
"panelHead": "VWh0dG_panelHead",
|
|
106
|
+
"kpiLabel": "VWh0dG_kpiLabel",
|
|
107
|
+
"chartWrap": "VWh0dG_chartWrap",
|
|
108
|
+
"heroValue": "VWh0dG_heroValue",
|
|
109
|
+
"triggerMeta": "VWh0dG_triggerMeta",
|
|
110
|
+
"chartGrid": "VWh0dG_chartGrid",
|
|
107
111
|
"chartLine": "VWh0dG_chartLine",
|
|
108
|
-
"
|
|
109
|
-
"
|
|
110
|
-
"healthBadgeBad": "VWh0dG_healthBadgeBad",
|
|
111
|
-
"railButton": "VWh0dG_railButton",
|
|
112
|
-
"triggerIcon": "VWh0dG_triggerIcon",
|
|
112
|
+
"chartTooltip": "VWh0dG_chartTooltip",
|
|
113
|
+
"rateBadgeLive": "VWh0dG_rateBadgeLive",
|
|
113
114
|
"healthBadge": "VWh0dG_healthBadge",
|
|
114
|
-
"chartSvg": "VWh0dG_chartSvg",
|
|
115
|
-
"panelHead": "VWh0dG_panelHead",
|
|
116
|
-
"heroMeta": "VWh0dG_heroMeta",
|
|
117
|
-
"delta": "VWh0dG_delta",
|
|
118
|
-
"chartTooltipDate": "VWh0dG_chartTooltipDate",
|
|
119
|
-
"triggerToday": "VWh0dG_triggerToday",
|
|
120
115
|
"dashboardRight": "VWh0dG_dashboardRight",
|
|
121
116
|
"dashboardSubtitle": "VWh0dG_dashboardSubtitle",
|
|
122
|
-
"
|
|
117
|
+
"pricingToggle": "VWh0dG_pricingToggle",
|
|
118
|
+
"delta": "VWh0dG_delta",
|
|
119
|
+
"dashboardModal": "VWh0dG_dashboardModal",
|
|
120
|
+
"bandPrice": "VWh0dG_bandPrice",
|
|
121
|
+
"chartSvg": "VWh0dG_chartSvg",
|
|
122
|
+
"panelHint": "VWh0dG_panelHint"
|
|
123
123
|
};
|
|
124
124
|
/** 运行时实时覆盖:undefined = 用内置目录与内置汇率(默认值降级)。 */
|
|
125
125
|
let liveRate;
|
|
@@ -155,8 +155,9 @@ window.__ModuleLoader__.load({
|
|
|
155
155
|
* costs no tokens: the estimator treats them as ¥0 and the billing table
|
|
156
156
|
* labels them 订阅包含. Add any model key your deployment serves through a
|
|
157
157
|
* plan here; leave empty when every route is pay-as-you-go.
|
|
158
|
+
* kimi-k3: 月之暗面 coding plan 通道的调用按套餐计费,费用记 0。
|
|
158
159
|
*/
|
|
159
|
-
const SUBSCRIPTION_PLAN_KEYS = [];
|
|
160
|
+
const SUBSCRIPTION_PLAN_KEYS = ["kimi-k3"];
|
|
160
161
|
/** Whether one stats model key is billed through a subscription plan. */
|
|
161
162
|
function isSubscriptionPlan(key) {
|
|
162
163
|
return SUBSCRIPTION_PLAN_KEYS.includes(key);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kenz1117/dsh-ui-usage-billing",
|
|
3
3
|
"description": "Usage billing dashboard for DeepSeek Harness: sidebar cost metrics plus a full dashboard modal, priced from a current multi-provider catalog with real usage aggregated from session logs.",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.5",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|