dsh-cost-meter 1.6.9 → 1.6.11
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.en.md +5 -5
- package/README.md +5 -5
- package/lib/backfill.js +58 -10
- package/lib/client.js +2 -2
- package/lib/index.js +110 -20
- package/lib/pricing.js +37 -0
- package/lib/store.js +64 -0
- package/lib/typert.host.js +2 -0
- package/lib/usage-dedup.js +116 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
Per-conversation cost · daily totals · OpenCode Go subscription quota display · budget with usage percentage · official account balance · custom provider balance · balance progress bar · history · peak/off-peak pricing hours display (peak hours UTC 01:00–04:00, 06:00–10:00; from Aug 23, 2026 weekends are billed at off-peak prices all day, shown as “Weekend — all off-peak”) · pre-switch popup & system-notification alerts for peak/off-peak changes (position / lead time / alert type configurable) · one-click price sync from the official docs · Codex-style token usage heat grid · multi-vendor model pricing (built-in 90+ model price catalog with auto-matching) · mainstream Coding Plan quota queries & display (Anthropic / Z.ai / MiniMax / Kimi / OpenRouter / SiliconFlow / CommandCode / SCNet) plan/API dual-track billing (subscription quota vs pay-as-you-go money separated, per-1% & full-window token/equivalent-cost estimates with daily/weekly/monthly curves) · · quota strip above the input box (budget / Go / coding-plan usage in one row, toggleable)
|
|
8
8
|
|
|
9
|
-
[](https://github.com/Han-1413141/dsh-cost-meter)
|
|
10
10
|
[](https://www.npmjs.com/package/dsh-cost-meter)
|
|
11
11
|
[](LICENSE)
|
|
12
12
|
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
@@ -231,22 +231,22 @@ Real captures from an actual DSH sidebar of the period strip and collapsed verti
|
|
|
231
231
|
dsh plugin --profile web add dsh-cost-meter
|
|
232
232
|
```
|
|
233
233
|
|
|
234
|
-
**PowerShell one-click script** (copy the whole line, paste, press Enter; pnpm is provisioned automatically, git is auto-detected — no clone needed; the install chain is **pinned to the release tag `v1.6.
|
|
234
|
+
**PowerShell one-click script** (copy the whole line, paste, press Enter; pnpm is provisioned automatically, git is auto-detected — no clone needed; the install chain is **pinned to the release tag `v1.6.11`** — review the script before running):
|
|
235
235
|
|
|
236
236
|
```powershell
|
|
237
|
-
irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.6.
|
|
237
|
+
irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.6.11/install.ps1 | iex
|
|
238
238
|
```
|
|
239
239
|
|
|
240
240
|
**Or a plain command line** (the machine must already have pnpm and git; also pinned to the tag):
|
|
241
241
|
|
|
242
242
|
```sh
|
|
243
|
-
dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.6.
|
|
243
|
+
dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.6.11
|
|
244
244
|
```
|
|
245
245
|
|
|
246
246
|
Without git, use the GitHub tag archive:
|
|
247
247
|
|
|
248
248
|
```sh
|
|
249
|
-
dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.6.
|
|
249
|
+
dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.6.11.tar.gz
|
|
250
250
|
```
|
|
251
251
|
|
|
252
252
|
After installing, **restart** `dsh web` (plugin rows, the Typert manifest and the client bundle are all scanned at startup):
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
本会话费用 · 当日费用 · OpenCode Go 订阅额度显示 · 预算与已用百分比 · 官方账户余额 · 自定义 Provider 余额查询(可配任意 HTTP 端点) · 余额三段进度条 · 历史记录 · 峰谷计价时段显示(UTC 01:00–04:00、06:00–10:00 为峰时段;2026-08-23 起周末全天按谷价,显示「周末时段——全谷价」) · 峰/谷切换前弹窗与系统通知提醒(位置/提前量/提醒类型可配) · 官方价格一键同步 · 类 Codex Token 用量热图 · 多厂商多模型价格计费(内置 90+ 模型价格目录与自动匹配) · 主流 Coding Plan 额度查询与显示(Anthropic / Z.ai / MiniMax / Kimi / OpenRouter / SiliconFlow / CommandCode / SCNet / 火山方舟 九家,含 Volcano Ark AK/SK 签名) · Plan/API 双轨计费(订阅额度与按量金额分离统计,每 1% 额度与满窗的 token/等值金额估算及日/周/月曲线) · 输入框上方额度横条(预算/Go/Coding Plan 用量一条横排显示,可开关)
|
|
8
8
|
|
|
9
|
-
[](https://github.com/Han-1413141/dsh-cost-meter)
|
|
10
10
|
[](https://www.npmjs.com/package/dsh-cost-meter)
|
|
11
11
|
[](LICENSE)
|
|
12
12
|
[](https://github.com/deepseek-ai/deepseek-harness)
|
|
@@ -233,22 +233,22 @@
|
|
|
233
233
|
dsh plugin --profile web add dsh-cost-meter
|
|
234
234
|
```
|
|
235
235
|
|
|
236
|
-
**PowerShell 一键脚本**(复制整行粘贴回车;自动补齐 pnpm、自动探测 git,无需克隆仓库;安装链**固定到发布 tag `v1.6.
|
|
236
|
+
**PowerShell 一键脚本**(复制整行粘贴回车;自动补齐 pnpm、自动探测 git,无需克隆仓库;安装链**固定到发布 tag `v1.6.11`**,建议先下载审阅再运行):
|
|
237
237
|
|
|
238
238
|
```powershell
|
|
239
|
-
irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.6.
|
|
239
|
+
irm https://raw.githubusercontent.com/Han-1413141/dsh-cost-meter/v1.6.11/install.ps1 | iex
|
|
240
240
|
```
|
|
241
241
|
|
|
242
242
|
**或直接命令行**(机器上需已有 pnpm 与 git;同样固定到 tag):
|
|
243
243
|
|
|
244
244
|
```sh
|
|
245
|
-
dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.6.
|
|
245
|
+
dsh plugin --profile web add github:Han-1413141/dsh-cost-meter#v1.6.11
|
|
246
246
|
```
|
|
247
247
|
|
|
248
248
|
没有 git 时可用 GitHub tag 打包直链:
|
|
249
249
|
|
|
250
250
|
```sh
|
|
251
|
-
dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.6.
|
|
251
|
+
dsh plugin --profile web add https://github.com/Han-1413141/dsh-cost-meter/archive/refs/tags/v1.6.11.tar.gz
|
|
252
252
|
```
|
|
253
253
|
|
|
254
254
|
安装后**重启** `dsh web`(插件行、Typert 清单与客户端 bundle 均在启动时扫描):
|
package/lib/backfill.js
CHANGED
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
import { readdirSync, readFileSync, statSync } from 'node:fs'
|
|
14
14
|
import { join } from 'node:path'
|
|
15
15
|
import * as zlib from 'node:zlib'
|
|
16
|
-
import { costOf, providerPriceEntryFor, usdFromCost, isWrapperProviderId } from './pricing.js'
|
|
16
|
+
import { costOf, providerPriceEntryFor, usdFromCost, isWrapperProviderId, wrapperUpstreamProvider } from './pricing.js'
|
|
17
17
|
import { localDayKey, zeroDay, splitLedgerApiCost } from './store.js'
|
|
18
18
|
import { billingClassOf, enabledPlanSetOf } from './plan-billing.js'
|
|
19
|
+
import { USAGE_DEDUP_WINDOW_MS, usageFingerprint } from './usage-dedup.js'
|
|
19
20
|
|
|
20
21
|
const ZSTD_MAGIC = 4247762216
|
|
21
22
|
/** 打包行(文本/推理/工具调用增量游程)不含 header 与 usage,回放时跳过。 */
|
|
@@ -218,6 +219,42 @@ export function replaySessionRecords(records, config, wantDates = null) {
|
|
|
218
219
|
apiCost: (current.apiCost ?? 0) + sign * (sample.apiCost ?? sample.cost),
|
|
219
220
|
}
|
|
220
221
|
}
|
|
222
|
+
// 两遍扫预扫描(issue #76):按与主循环一致的 header 状态机,收集全部非包装
|
|
223
|
+
// provider 的 (model, 五桶) 指纹及其事件时刻。主循环遇到包装层 provider 的
|
|
224
|
+
// usage 时,仅当窗口内不存在相同指纹的非包装样本才改挂上游计入——上游真实
|
|
225
|
+
// 流无论出现在包装层事件之前还是之后,转发对都只计一次(与实时投影/账本
|
|
226
|
+
// 入账钩子的指纹窗口去重同语义,保证回放与实时逐位一致)。
|
|
227
|
+
const plainFingerprints = new Map()
|
|
228
|
+
let preProvider = 'deepseek'
|
|
229
|
+
let preModel = 'default'
|
|
230
|
+
for (const event of records) {
|
|
231
|
+
if (event === null || typeof event !== 'object') continue
|
|
232
|
+
if (event.type === 'session/title') continue
|
|
233
|
+
if (PACKED_ROW_TYPES.has(event.type)) continue
|
|
234
|
+
if (event.type === 'request/header') {
|
|
235
|
+
const m = event.data?.header?.config?.model
|
|
236
|
+
const p = event.data?.header?.config?.provider
|
|
237
|
+
preModel = typeof m === 'string' && m.length > 0 ? m : 'default'
|
|
238
|
+
preProvider = typeof p === 'string' && p.length > 0 ? p : 'deepseek'
|
|
239
|
+
continue
|
|
240
|
+
}
|
|
241
|
+
let u = null
|
|
242
|
+
if (event.type === 'assistant/chunk' && event.data?.chunk?.type === 'usage' && event.data.chunk.usage != null) u = event.data.chunk.usage
|
|
243
|
+
else if (event.type === 'assistant/message' && event.data?.usage != null) u = event.data.usage
|
|
244
|
+
else continue
|
|
245
|
+
if (isWrapperProviderId(preProvider)) continue
|
|
246
|
+
const at = Number(event.time)
|
|
247
|
+
if (!Number.isFinite(at) || at <= 0) continue
|
|
248
|
+
const fp = usageFingerprint(preModel, {
|
|
249
|
+
input: num(u.inputTokens), output: num(u.outputTokens), cacheRead: num(u.cacheReadTokens),
|
|
250
|
+
cacheWrite: num(u.cacheWriteTokens), reasoning: num(u.reasoningTokens),
|
|
251
|
+
})
|
|
252
|
+
const times = plainFingerprints.get(fp)
|
|
253
|
+
if (times === undefined) plainFingerprints.set(fp, [at])
|
|
254
|
+
else times.push(at)
|
|
255
|
+
}
|
|
256
|
+
// 本回放已计入的改挂样本(fp + 时刻):包装层重复分发(窗口内同指纹两次)只计一次。
|
|
257
|
+
let remappedAdmitted = []
|
|
221
258
|
for (const event of records) {
|
|
222
259
|
if (event === null || typeof event !== 'object') continue
|
|
223
260
|
if (event.type === 'session' && typeof event.id === 'string') {
|
|
@@ -266,11 +303,6 @@ export function replaySessionRecords(records, config, wantDates = null) {
|
|
|
266
303
|
if (!Number.isFinite(atMs) || atMs <= 0) continue
|
|
267
304
|
const date = localDayKey(atMs)
|
|
268
305
|
if (wantDates !== null && !wantDates.has(date)) continue
|
|
269
|
-
// modlens 视觉包装层(modlens-<upstream> / deepseek-modlens)把同一份 usage
|
|
270
|
-
// 经上游 llm.stream 再转发一次:日志里同一调用出现两套 header+usage 事件,
|
|
271
|
-
// 且因 provider 不同绕过 (turn, step) 键去重,回放会翻倍(issue #70)。与
|
|
272
|
-
// 实时钩子/投影一致,包装层 provider 状态下的 usage 事件跳过,只记上游真实流。
|
|
273
|
-
if (isWrapperProviderId(provider)) continue
|
|
274
306
|
const buckets = {
|
|
275
307
|
input: num(usage.inputTokens),
|
|
276
308
|
output: num(usage.outputTokens),
|
|
@@ -278,17 +310,33 @@ export function replaySessionRecords(records, config, wantDates = null) {
|
|
|
278
310
|
cacheWrite: num(usage.cacheWriteTokens),
|
|
279
311
|
reasoning: num(usage.reasoningTokens),
|
|
280
312
|
}
|
|
313
|
+
// modlens 视觉包装层转发对去重(issue #70/#76):包装层样本先改挂上游 id
|
|
314
|
+
// (与账本入账钩子/实时投影同口径),仅当窗口内既无相同 (model, 五桶) 的
|
|
315
|
+
// 非包装样本(预扫描集)、也无本回放已计入的改挂样本时才计入——上游真实流
|
|
316
|
+
// 先到或后到都只计一次。v1.6.10 及之前对包装层一律跳过,整条链路均为包装型
|
|
317
|
+
// id 的转售路由(如 modlens-go-ds4f)因此整单漏计(账本 sessions=0)。
|
|
318
|
+
let effectiveProvider = provider
|
|
319
|
+
if (isWrapperProviderId(provider)) {
|
|
320
|
+
effectiveProvider = wrapperUpstreamProvider(provider) ?? provider
|
|
321
|
+
const fp = usageFingerprint(model, buckets)
|
|
322
|
+
remappedAdmitted = remappedAdmitted.filter(entry => atMs - entry.at <= USAGE_DEDUP_WINDOW_MS)
|
|
323
|
+
const plainTimes = plainFingerprints.get(fp)
|
|
324
|
+
const plainHit = plainTimes !== undefined && plainTimes.some(t => Math.abs(atMs - t) <= USAGE_DEDUP_WINDOW_MS)
|
|
325
|
+
if (plainHit || remappedAdmitted.some(entry => entry.fp === fp)) continue
|
|
326
|
+
remappedAdmitted.push({ fp, at: atMs })
|
|
327
|
+
}
|
|
281
328
|
const key = `${turn}:${step}`
|
|
282
329
|
// 去重与旧版完全一致:单一 (turn,step) 状态机跨种子/own 段连续生效。
|
|
283
330
|
const prev = last !== null && last.key === key ? last : null
|
|
284
|
-
if (prev !== null && prev.providerKey === `${
|
|
331
|
+
if (prev !== null && prev.providerKey === `${effectiveProvider}:${model}`
|
|
285
332
|
&& prev.buckets.input === buckets.input && prev.buckets.output === buckets.output
|
|
286
333
|
&& prev.buckets.cacheRead === buckets.cacheRead && prev.buckets.cacheWrite === buckets.cacheWrite
|
|
287
334
|
&& prev.buckets.reasoning === buckets.reasoning) {
|
|
288
335
|
continue
|
|
289
336
|
}
|
|
290
337
|
// 按事件时刻计费(历史正确):峰谷时代前用 legacyBase,之后按峰谷两档。
|
|
291
|
-
|
|
338
|
+
// 计价/归类/归因一律用改挂后的 effectiveProvider(包装层转发对挂语义上游)。
|
|
339
|
+
const resolved = providerPriceEntryFor(effectiveProvider, model, config?.prices, {
|
|
292
340
|
mode: config?.priceMatch === 'exact' ? 'exact' : 'auto',
|
|
293
341
|
overrides: config?.priceOverrides,
|
|
294
342
|
})
|
|
@@ -305,8 +353,8 @@ export function replaySessionRecords(records, config, wantDates = null) {
|
|
|
305
353
|
config?.exchangeRate)
|
|
306
354
|
// Plan/API 双轨分类(issue #64):与 store.account() 同一分类器,回放条目
|
|
307
355
|
// 的 apiCost 只含真金白银部分(plan 类金额仅记等值);传 prices 供路由判定。
|
|
308
|
-
const apiCost = billingClassOf(
|
|
309
|
-
const providerKey = `${
|
|
356
|
+
const apiCost = billingClassOf(effectiveProvider, model, config?.planBilling, enabledPlanSetOf(config), config?.prices) === 'api' ? cost : 0
|
|
357
|
+
const providerKey = `${effectiveProvider}:${model}`
|
|
310
358
|
// 聚合目标按事件分段路由;被替换的旧样本从它当时所在的段扣回(与旧版
|
|
311
359
|
// 单流先减后加的净效果一致,仅种子段的量落到 seedDays 而非 days)。
|
|
312
360
|
if (prev !== null) shift(prev, -1, prev.seed ? seedDays : days)
|