@kenz1117/dsh-ui-usage-billing 1.4.21 → 1.4.23
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/lib/client.js +2 -2
- package/lib/index.js +105 -12
- package/lib/types/aggregate.d.ts +14 -6
- package/lib/types/client/TrendChart.d.ts +1 -1
- package/lib/types/client/UsageBilling.d.ts +14 -1
- package/lib/types/client/locales.d.ts +1 -1
- package/lib/types/client/pricing.d.ts +4 -4
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -2,12 +2,12 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import { lstat, mkdir, readFile, rm, stat } from "node:fs/promises";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
|
-
import { SessionLogOffset } from "@deepseek-ai/dsh-session/types";
|
|
6
5
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
7
6
|
import { withFileLock, writeFileAtomic } from "@deepseek-ai/dsh-atomic-write";
|
|
8
7
|
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
9
8
|
import { resolveDshHome } from "@deepseek-ai/dsh-home-paths";
|
|
10
9
|
import z from "@deepseek-ai/schemastery";
|
|
10
|
+
import * as sessionTypes from "@deepseek-ai/dsh-session/types";
|
|
11
11
|
import { createHash, createHmac } from "node:crypto";
|
|
12
12
|
//#region lib/types/client/plan-knowledge.js
|
|
13
13
|
/**
|
|
@@ -585,15 +585,15 @@ function isPriced(key) {
|
|
|
585
585
|
return FALLBACK_RATES.some((rate) => rate.key.toLowerCase() === resolved.toLowerCase());
|
|
586
586
|
}
|
|
587
587
|
/**
|
|
588
|
-
* 促销在 nowMs 是否生效:factor 必须落在
|
|
589
|
-
* endsAtMs 缺省表示长期活动,在 factor 合法期间持续生效。
|
|
588
|
+
* 促销在 nowMs 是否生效:factor 必须落在 [0,1) 区间(0 = 活动期免费,issue #86),
|
|
589
|
+
* 截止时刻及之后视为过期;endsAtMs 缺省表示长期活动,在 factor 合法期间持续生效。
|
|
590
590
|
* 导出供测试:纯函数。
|
|
591
591
|
* @param promo - 待判定的促销窗口。
|
|
592
592
|
* @param nowMs - 判定时刻(epoch ms)。
|
|
593
593
|
*/
|
|
594
594
|
function isPromoActive(promo, nowMs) {
|
|
595
595
|
const expired = promo.endsAtMs !== void 0 && nowMs >= promo.endsAtMs;
|
|
596
|
-
return Number.isFinite(nowMs) && !expired && promo.factor
|
|
596
|
+
return Number.isFinite(nowMs) && !expired && promo.factor >= 0 && promo.factor < 1;
|
|
597
597
|
}
|
|
598
598
|
/**
|
|
599
599
|
* 把限时促销折入条目单价:生效期内返回 price 主档与 offPeak 逐档乘折扣系数的
|
|
@@ -606,7 +606,10 @@ function isPromoActive(promo, nowMs) {
|
|
|
606
606
|
function applyPromo(entry, nowMs) {
|
|
607
607
|
const { promo } = entry;
|
|
608
608
|
if (promo === void 0 || !isPromoActive(promo, nowMs)) return entry;
|
|
609
|
-
const factorOf = (field) =>
|
|
609
|
+
const factorOf = (field) => {
|
|
610
|
+
const perBand = promo.factors?.[field];
|
|
611
|
+
return perBand !== void 0 && perBand >= 0 && perBand < 1 ? perBand : promo.factor;
|
|
612
|
+
};
|
|
610
613
|
const scaled = (band) => ({
|
|
611
614
|
input: band.input * factorOf("input"),
|
|
612
615
|
cacheHit: band.cacheHit * factorOf("cacheHit"),
|
|
@@ -1308,6 +1311,59 @@ const BUILTIN_MODEL_CATALOG = [
|
|
|
1308
1311
|
output: 16.8
|
|
1309
1312
|
}
|
|
1310
1313
|
},
|
|
1314
|
+
{
|
|
1315
|
+
key: "minimax-m3.1-flash-preview",
|
|
1316
|
+
name: "MiniMax M3.1 Flash Preview",
|
|
1317
|
+
provider: "MiniMax",
|
|
1318
|
+
colorVar: "ds-amber",
|
|
1319
|
+
price: {
|
|
1320
|
+
currency: "CNY",
|
|
1321
|
+
input: 2.1,
|
|
1322
|
+
cacheHit: .42,
|
|
1323
|
+
output: 8.4
|
|
1324
|
+
}
|
|
1325
|
+
},
|
|
1326
|
+
{
|
|
1327
|
+
key: "u2-flash",
|
|
1328
|
+
name: "U2 Flash",
|
|
1329
|
+
provider: "云知声",
|
|
1330
|
+
colorVar: "dsw-static-violet-400",
|
|
1331
|
+
price: {
|
|
1332
|
+
currency: "CNY",
|
|
1333
|
+
input: 1,
|
|
1334
|
+
cacheHit: .2,
|
|
1335
|
+
output: 2
|
|
1336
|
+
},
|
|
1337
|
+
promo: {
|
|
1338
|
+
factor: 0,
|
|
1339
|
+
endsAtMs: Date.UTC(2026, 9, 31, 16, 0, 0),
|
|
1340
|
+
note: "限时免费至 2026-10-31"
|
|
1341
|
+
}
|
|
1342
|
+
},
|
|
1343
|
+
{
|
|
1344
|
+
key: "u2",
|
|
1345
|
+
name: "Unisound U2",
|
|
1346
|
+
provider: "云知声",
|
|
1347
|
+
colorVar: "dsw-static-violet-400",
|
|
1348
|
+
price: {
|
|
1349
|
+
currency: "CNY",
|
|
1350
|
+
input: 1,
|
|
1351
|
+
cacheHit: .2,
|
|
1352
|
+
output: 2
|
|
1353
|
+
}
|
|
1354
|
+
},
|
|
1355
|
+
{
|
|
1356
|
+
key: "u2-med",
|
|
1357
|
+
name: "U2-Med",
|
|
1358
|
+
provider: "云知声",
|
|
1359
|
+
colorVar: "dsw-static-violet-400",
|
|
1360
|
+
price: {
|
|
1361
|
+
currency: "CNY",
|
|
1362
|
+
input: 8,
|
|
1363
|
+
cacheHit: 2,
|
|
1364
|
+
output: 28
|
|
1365
|
+
}
|
|
1366
|
+
},
|
|
1311
1367
|
{
|
|
1312
1368
|
key: "ernie",
|
|
1313
1369
|
name: "ERNIE-5.1",
|
|
@@ -2244,6 +2300,11 @@ const BUILTIN_MODEL_KEY_ALIASES = {
|
|
|
2244
2300
|
"minimax-m2-7": "minimax-m2.7",
|
|
2245
2301
|
"minimax-m2-7-highspeed": "minimax-m2.7-highspeed",
|
|
2246
2302
|
"minimax-m2-7-high-speed": "minimax-m2.7-highspeed",
|
|
2303
|
+
"minimax-m3.1-flash-preview": "minimax-m3.1-flash-preview",
|
|
2304
|
+
"minimax-m3-1-flash-preview": "minimax-m3.1-flash-preview",
|
|
2305
|
+
"u2-flash": "u2-flash",
|
|
2306
|
+
"u2": "u2",
|
|
2307
|
+
"u2-med": "u2-med",
|
|
2247
2308
|
"gpt-6": "gpt-6-astra",
|
|
2248
2309
|
"gpt-6-astra": "gpt-6-astra",
|
|
2249
2310
|
"gpt-6-sol": "gpt-6-sol",
|
|
@@ -2269,6 +2330,11 @@ const BUILTIN_MODEL_KEY_ALIASES = {
|
|
|
2269
2330
|
* zero while their tokens still count. Pure functions only: the persistence
|
|
2270
2331
|
* handle is injected, so the fold is unit-testable without a host.
|
|
2271
2332
|
*/
|
|
2333
|
+
/** 会话日志偏移:宿主 0.1.2-alpha.4 前的 dsh-session/types 不导出 SessionLogOffset
|
|
2334
|
+
* 值,具名导入让这些宿主装载即崩(v1.4.19~v1.4.22 实测,alpha.3 真机复现);
|
|
2335
|
+
* namespace 导入不做具名导出校验,缺导出时退化为恒等——偏移类型是
|
|
2336
|
+
* BrandedNumber,值域与 number 同构,老宿主的 readFrom 原样收数字。 */
|
|
2337
|
+
const sessionLogOffsetOf = sessionTypes.SessionLogOffset ?? ((value) => value);
|
|
2272
2338
|
/**
|
|
2273
2339
|
* 走订阅套餐(coding / token plan / opencode 订阅)的 provider id:这些通道的
|
|
2274
2340
|
* 调用按套餐计费,不再按 token 计费,因此即使模型 id 与计费表撞名也一律豁免。
|
|
@@ -3192,6 +3258,13 @@ function configFingerprint(subscriptionMatcher, officialProviderIds, routeAliase
|
|
|
3192
3258
|
String(searchEstimate)
|
|
3193
3259
|
].join("|");
|
|
3194
3260
|
}
|
|
3261
|
+
/** 会话格式拒读(宿主 SessionFormatUnsupportedError):日志完好,仅本构建读不了。
|
|
3262
|
+
* 宿主错误类显式落 error.name;经包装/重建的路径只在 message 里保留类名,两处都认。
|
|
3263
|
+
* 据此把「格式不受支持」与「损坏/其他读取失败」分开计数,供面板按原因归因(issue #84)。 */
|
|
3264
|
+
function isFormatUnsupportedError(error) {
|
|
3265
|
+
if (!(error instanceof Error)) return false;
|
|
3266
|
+
return error.name === "SessionFormatUnsupportedError" || error.message.includes("SessionFormatUnsupportedError");
|
|
3267
|
+
}
|
|
3195
3268
|
/**
|
|
3196
3269
|
* Create the incremental usage aggregator.
|
|
3197
3270
|
* @param persistence - the session persistence service.
|
|
@@ -3301,12 +3374,22 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
3301
3374
|
const included = /* @__PURE__ */ new Set();
|
|
3302
3375
|
const folds = [];
|
|
3303
3376
|
const skipped = [];
|
|
3377
|
+
let carriedUnreadable = 0;
|
|
3378
|
+
let carriedFormatUnreadable = 0;
|
|
3379
|
+
let formatUnreadable = 0;
|
|
3380
|
+
let noLedgerUnreadable = 0;
|
|
3304
3381
|
let staleLedgerSessions = 0;
|
|
3305
3382
|
for (const meta of metas) {
|
|
3306
3383
|
const id = String(meta.id);
|
|
3307
3384
|
seen.add(id);
|
|
3308
3385
|
const stamp = await stampOf(meta);
|
|
3309
|
-
|
|
3386
|
+
const mark = unreadable.get(id);
|
|
3387
|
+
if (stamp !== null && mark !== void 0 && mark.stamp === stamp) {
|
|
3388
|
+
carriedUnreadable += 1;
|
|
3389
|
+
if (mark.format) carriedFormatUnreadable += 1;
|
|
3390
|
+
if (!ledger.has(id)) noLedgerUnreadable += 1;
|
|
3391
|
+
continue;
|
|
3392
|
+
}
|
|
3310
3393
|
const hit = cache.get(id);
|
|
3311
3394
|
if (hit !== void 0 && stamp !== null && hit.stamp === stamp) {
|
|
3312
3395
|
cache.delete(id);
|
|
@@ -3335,7 +3418,7 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
3335
3418
|
const previous = cache.get(id);
|
|
3336
3419
|
if (previous?.machine !== void 0 && previous.lastSeq !== void 0) {
|
|
3337
3420
|
const from = previous.lastSeq + 1;
|
|
3338
|
-
const { events, inheritedEventCount } = await persistence.readFrom(meta.id,
|
|
3421
|
+
const { events, inheritedEventCount } = await persistence.readFrom(meta.id, sessionLogOffsetOf(from));
|
|
3339
3422
|
const after = await stampOf(meta);
|
|
3340
3423
|
if (stamp !== null && after !== stamp) {
|
|
3341
3424
|
cache.delete(id);
|
|
@@ -3361,7 +3444,7 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
3361
3444
|
recordLedger(id, cwd, stamp, previous.fold);
|
|
3362
3445
|
continue;
|
|
3363
3446
|
}
|
|
3364
|
-
const { events, inheritedEventCount } = await persistence.readFrom(meta.id,
|
|
3447
|
+
const { events, inheritedEventCount } = await persistence.readFrom(meta.id, sessionLogOffsetOf(0));
|
|
3365
3448
|
const after = await stampOf(meta);
|
|
3366
3449
|
if (stamp !== null && after !== stamp) continue;
|
|
3367
3450
|
const fold = freshFold();
|
|
@@ -3389,7 +3472,13 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
3389
3472
|
recordLedger(id, cwd, stamp, fold);
|
|
3390
3473
|
} catch (error) {
|
|
3391
3474
|
skipped.push(id);
|
|
3392
|
-
|
|
3475
|
+
const format = isFormatUnsupportedError(error);
|
|
3476
|
+
if (format) formatUnreadable += 1;
|
|
3477
|
+
if (!ledger.has(id)) noLedgerUnreadable += 1;
|
|
3478
|
+
if (stamp !== null) unreadable.set(id, {
|
|
3479
|
+
stamp,
|
|
3480
|
+
format
|
|
3481
|
+
});
|
|
3393
3482
|
console.warn("[usage-billing] skip unreadable session", id, error);
|
|
3394
3483
|
}
|
|
3395
3484
|
}
|
|
@@ -3569,6 +3658,8 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
3569
3658
|
}).filter((entry) => entry !== null))];
|
|
3570
3659
|
}))
|
|
3571
3660
|
};
|
|
3661
|
+
const unreadableTotal = skipped.length + carriedUnreadable;
|
|
3662
|
+
const formatUnreadableTotal = formatUnreadable + carriedFormatUnreadable;
|
|
3572
3663
|
lastDoc = {
|
|
3573
3664
|
version: 4,
|
|
3574
3665
|
updatedAt: now,
|
|
@@ -3592,7 +3683,9 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
3592
3683
|
...searchEstimate > 0 ? { searchCallEstimateCny: searchEstimate } : {},
|
|
3593
3684
|
...perf === void 0 ? {} : { perf },
|
|
3594
3685
|
...staleLedgerSessions > 0 ? { staleLedgerSessions } : {},
|
|
3595
|
-
...
|
|
3686
|
+
...unreadableTotal > 0 ? { unreadableSessions: unreadableTotal } : {},
|
|
3687
|
+
...formatUnreadableTotal > 0 ? { unreadableFormatSessions: formatUnreadableTotal } : {},
|
|
3688
|
+
...noLedgerUnreadable > 0 ? { unreadableNoLedgerSessions: noLedgerUnreadable } : {},
|
|
3596
3689
|
byRole: (() => {
|
|
3597
3690
|
const chars = roles.userChars + roles.toolChars;
|
|
3598
3691
|
const userShare = chars > 0 ? roles.userChars / chars : .5;
|
|
@@ -6894,8 +6987,8 @@ function adaptSessionPersistence(raw) {
|
|
|
6894
6987
|
const events = Array.isArray(slice) ? slice : slice.events;
|
|
6895
6988
|
return {
|
|
6896
6989
|
meta: handle.header,
|
|
6897
|
-
fromSeq:
|
|
6898
|
-
inheritedEventCount:
|
|
6990
|
+
fromSeq: sessionLogOffsetOf(fromSeq),
|
|
6991
|
+
inheritedEventCount: sessionLogOffsetOf(handle.inheritedEventCount),
|
|
6899
6992
|
events
|
|
6900
6993
|
};
|
|
6901
6994
|
} finally {
|
package/lib/types/aggregate.d.ts
CHANGED
|
@@ -10,11 +10,15 @@
|
|
|
10
10
|
* zero while their tokens still count. Pure functions only: the persistence
|
|
11
11
|
* handle is injected, so the fold is unit-testable without a host.
|
|
12
12
|
*/
|
|
13
|
-
import { SessionLogOffset } from '@deepseek-ai/dsh-session/types';
|
|
14
|
-
import type { SessionEvent, SessionHeader, SessionId } from '@deepseek-ai/dsh-session/types';
|
|
13
|
+
import type { SessionEvent, SessionHeader, SessionId, SessionLogOffset as SessionLogOffsetT } from '@deepseek-ai/dsh-session/types';
|
|
15
14
|
import type { TokenUsage } from '@deepseek-ai/dsh-llm';
|
|
16
15
|
import { resolveCatalogKey } from './client/pricing.ts';
|
|
17
16
|
import { BUILTIN_MODEL_KEY_ALIASES } from './builtin-catalog.ts';
|
|
17
|
+
/** 会话日志偏移:宿主 0.1.2-alpha.4 前的 dsh-session/types 不导出 SessionLogOffset
|
|
18
|
+
* 值,具名导入让这些宿主装载即崩(v1.4.19~v1.4.22 实测,alpha.3 真机复现);
|
|
19
|
+
* namespace 导入不做具名导出校验,缺导出时退化为恒等——偏移类型是
|
|
20
|
+
* BrandedNumber,值域与 number 同构,老宿主的 readFrom 原样收数字。 */
|
|
21
|
+
export declare const sessionLogOffsetOf: (value: number) => SessionLogOffsetT;
|
|
18
22
|
export { BUILTIN_MODEL_KEY_ALIASES, resolveCatalogKey };
|
|
19
23
|
/**
|
|
20
24
|
* 走订阅套餐(coding / token plan / opencode 订阅)的 provider id:这些通道的
|
|
@@ -212,8 +216,8 @@ export declare function workspaceNameOf(cwd: string | undefined): string;
|
|
|
212
216
|
/** readFrom 返回的后缀切片:与宿主 0.1.2 的 SessionEventSuffix 同构,本地结构声明。 */
|
|
213
217
|
export interface UsageEventSuffix {
|
|
214
218
|
readonly meta: SessionHeader;
|
|
215
|
-
readonly inheritedEventCount:
|
|
216
|
-
readonly fromSeq:
|
|
219
|
+
readonly inheritedEventCount: SessionLogOffsetT;
|
|
220
|
+
readonly fromSeq: SessionLogOffsetT;
|
|
217
221
|
readonly events: readonly SessionEvent[];
|
|
218
222
|
}
|
|
219
223
|
/**
|
|
@@ -227,7 +231,7 @@ export interface UsageEventSuffix {
|
|
|
227
231
|
*/
|
|
228
232
|
export interface UsagePersistence {
|
|
229
233
|
list(): Promise<readonly SessionHeader[]>;
|
|
230
|
-
readFrom(id: SessionId, fromSeq:
|
|
234
|
+
readFrom(id: SessionId, fromSeq: SessionLogOffsetT): Promise<UsageEventSuffix>;
|
|
231
235
|
locate?(meta: SessionHeader): {
|
|
232
236
|
path: string;
|
|
233
237
|
} | undefined;
|
|
@@ -295,8 +299,12 @@ export interface UsageStatsDocument {
|
|
|
295
299
|
perf?: PerfStats;
|
|
296
300
|
/** 只存在于账本、且缺 foldVersion 的旧会话数;无旧行时省略。 */
|
|
297
301
|
staleLedgerSessions?: number;
|
|
298
|
-
/**
|
|
302
|
+
/** 本轮未完整计入的会话数(新失败 + 负缓存命中,跨轮次稳定;有账本旧行的按存档计入最近可用点);无失败时省略。 */
|
|
299
303
|
unreadableSessions?: number;
|
|
304
|
+
/** 未统计中因会话格式拒读(SessionFormatUnsupportedError)的子集;无格式拒读时省略。 */
|
|
305
|
+
unreadableFormatSessions?: number;
|
|
306
|
+
/** 未统计中无任何账本存档、完全未计入的子集(区别于有存档按最近可用点计入的);无存档缺失时省略。 */
|
|
307
|
+
unreadableNoLedgerSessions?: number;
|
|
300
308
|
}
|
|
301
309
|
/** 按角色费用归因:user / tool 为输入成本的启发式摊分,assistant 为输出成本实测。 */
|
|
302
310
|
export interface RoleCost {
|
|
@@ -26,7 +26,7 @@ export interface TrendPoint {
|
|
|
26
26
|
cost: number;
|
|
27
27
|
/** API calls that day (total across models). */
|
|
28
28
|
calls: number;
|
|
29
|
-
/** Total tokens that day (input+output
|
|
29
|
+
/** Total tokens that day (input+output — stats.input already includes cache hit/miss); used by the `tokens` metric. */
|
|
30
30
|
tokens?: number;
|
|
31
31
|
/** Per-model cost that day (stats key → CNY); absent entries plot zero. */
|
|
32
32
|
byModel?: Readonly<Record<string, number>>;
|
|
@@ -110,6 +110,15 @@ export declare function activeDaysOf(byDay: Record<string, {
|
|
|
110
110
|
export declare function streakDaysOf(byDay: Record<string, {
|
|
111
111
|
cost: number;
|
|
112
112
|
}>, now?: number): number;
|
|
113
|
+
/** 按日 Token 口径:输入 + 输出。stats.input 本身已含缓存命中与未命中
|
|
114
|
+
* (aggregate foldUsage:input = cacheHit + cacheMiss),再叠加缓存字段即重复
|
|
115
|
+
* 计数——趋势图/热力图/触发卡/峰值日的按日 Token 一律走本函数(issue #85);
|
|
116
|
+
* 跨日合计在各行之上累加(KPI 见 kpiAgg,触发卡见 month/weekTokens)。
|
|
117
|
+
* 导出供测试:纯函数。 */
|
|
118
|
+
export declare function dayTokensOf(day: {
|
|
119
|
+
input: number;
|
|
120
|
+
output: number;
|
|
121
|
+
}): number;
|
|
113
122
|
/**
|
|
114
123
|
* 近 7 天费用序列(含今天,缺日补 0):触发卡 hover 速览的迷你柱数据源。
|
|
115
124
|
* 导出供测试:纯函数(日期取本地时区)。
|
|
@@ -315,8 +324,12 @@ export interface UsageStats {
|
|
|
315
324
|
perf?: ClientPerf;
|
|
316
325
|
/** 旧版算法账本行兜底的会话数(模型归属可能失真);0 或缺省 = 全部数据可信。 */
|
|
317
326
|
staleLedgerSessions?: number;
|
|
318
|
-
/**
|
|
327
|
+
/** 本轮未完整计入的会话数(跨轮次稳定);0 或缺省 = 全部会话已统计。 */
|
|
319
328
|
unreadableSessions?: number;
|
|
329
|
+
/** 未统计中因会话格式拒读的子集(过新待宿主升级、过旧需迁移);0 或缺省 = 无格式拒读。 */
|
|
330
|
+
unreadableFormatSessions?: number;
|
|
331
|
+
/** 未统计中无账本存档、完全未计入的子集;0 或缺省 = 拒读会话均有存档兜底。 */
|
|
332
|
+
unreadableNoLedgerSessions?: number;
|
|
320
333
|
/** 插件版本号(服务端读自包 package.json;旧快照缺失)。 */
|
|
321
334
|
pluginVersion?: string;
|
|
322
335
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Locale dictionaries for the usage billing surface. */
|
|
2
|
-
export type UsageBillingKey = 'title' | 'cost' | 'todayCost' | 'monthCost' | 'yearCost' | 'monthProjected' | 'liveTurn' | 'liveSession' | 'calls' | 'cacheHitRate' | 'tokens' | 'inputTokens' | 'outputTokens' | 'avgCost' | 'kpiRange' | 'avgRangeToday' | 'avgRange7d' | 'avgRangeWeek' | 'avgRangeMonth' | 'avgRangeAll' | 'trend' | 'trend7d' | 'trend30d' | 'trendMetric' | 'trendMetricCost' | 'trendMetricTokens' | 'pricingSearch' | 'bandPinHint' | 'trendEmpty' | 'trendTotal' | 'roundsUnit' | 'healthModelsLive' | 'healthVendorsDown' | 'healthVendorsUnavailable' | 'budget' | 'budgetRemain' | 'budgetAmount' | 'budgetSummary' | 'sessions' | 'sessionTitle' | 'lastActive' | 'sessionOverflow' | 'budgetTierBody' | 'models' | 'providerBilling' | 'channelUnknown' | 'directTag' | 'relayTag' | 'unknownTag' | 'unknownChannelHint' | 'planCountUnit' | 'actual' | 'pricing' | 'input' | 'output' | 'cacheHit' | 'peak' | 'offPeak' | 'flat' | 'band' | 'close' | 'footer' | 'footerCredit' | 'lastUpdated' | 'noData' | 'todayRate' | 'rateLive' | 'rateBuiltin' | 'pricingSyncedAt' | 'pricingSyncNow' | 'pricingSyncing' | 'promoBadge' | 'promoUntil' | 'promoOpenEnded' | 'pricingTip' | 'pricingUnit' | 'pricingNotes' | 'ubPeak' | 'ubOff' | 'ubStd' | 'peakBand' | 'pricingSource' | 'noteCache' | 'noteBand' | 'noteSource' | 'balance' | 'recharge' | 'rechargeHint' | 'balanceUnconfigured' | 'balanceUnauthorized' | 'balanceUnreachable' | 'uncatalogued' | 'estimatedPricing' | 'balanceDays' | 'balanceLowBody' | 'reconcileDrift' | 'reconcileDismiss' | 'subscriptionNotConfigured' | 'subscriptionUnauthorized' | 'subscriptionUnavailable' | 'subscriptionInvalid' | 'subscriptionRateLimited' | 'subscriptionSession' | 'subscriptionWeekly' | 'subscriptionMonthly' | 'subscriptionBilling' | 'subscriptionRemaining' | 'subscriptionExhausted' | 'subscriptionReset' | 'subscriptionNoApi' | 'floatWindow' | 'floatModeCombined' | 'floatModeSubscription' | 'floatMode' | 'floatTargets' | 'floatWindowHint' | 'floatPrimaryToday' | 'floatPrimaryWeek' | 'floatPrimaryMonth' | 'floatNoTargets' | 'floatNoTargetsHint' | 'cardDisplay' | 'cardDisplayHint' | 'cardMetric' | 'cardMetricMoney' | 'cardMetricTokens' | 'cardSpan' | 'cardSpanDay' | 'cardSpanWeek' | 'cardSpanMonth' | 'triggerMonthTokens' | 'triggerTodayTokens' | 'triggerWeekTokens' | 'subscriptionsStale' | 'staleLedgerNotice' | 'unreadableNotice' | 'tokenCacheWrite' | 'toolRank' | 'toolName' | 'userPrices' | 'userPricesHint' | 'userPriceSave' | 'userPriceModel' | 'userPriceSource' | 'userPriceSourceHint' | 'userPriceOffPeak' | 'userPriceNormal' | 'userPriceCurrency' | 'userPriceAdd' | 'userPriceRemoveSelected' | 'userPriceSelect' | 'sessionStaleBadge' | 'sessionUntitled' | 'heatmapLess' | 'heatmapMore' | 'currency' | 'currencyCny' | 'currencyUsd' | 'currencyEur' | 'language' | 'heatmap' | 'rounds' | 'roundsHint' | 'anomaly' | 'model' | 'thModel' | 'thInputMiss' | 'thInputHit' | 'costAbbr' | 'tabOverview' | 'tabTrends' | 'tabProviders' | 'tabPricing' | 'tabSettings' | 'budgetHint' | 'peakAlertHint' | 'peakAlertDescPeak' | 'peakAlertDescOff' | 'peakAlertDescPeakZhipu' | 'peakAlertDescOffZhipu' | 'export' | 'exportCsvDay' | 'exportCsvSession' | 'exportJson' | 'peakShare' | 'peakSharePerCall' | 'offPeakSavings' | 'perfMax' | 'weekCost' | 'roleCost' | 'roleUser' | 'roleAssistant' | 'roleTool' | 'roleHint' | 'tierPeak' | 'tierOff' | 'tierToPeak' | 'tierToOff' | 'tierAlertEnterPeak' | 'tierAlertEnterOff' | 'tierAlertEnterPeakZhipu' | 'tierAlertEnterOffZhipu' | 'peakAlertTitlePeak' | 'peakAlertTitleOff' | 'peakAlert' | 'peakAlertLeadMin' | 'peakAlertPos' | 'peakAlertMode' | 'peakAlertPosCorner' | 'peakAlertPosCenter' | 'peakAlertModePeak' | 'peakAlertModeOff' | 'peakAlertModeBoth' | 'peakAlertWebNotify' | 'peakAlertPreview' | 'planTypeCode' | 'planTypeToken' | 'subscriptionFeePerMonth' | 'triggerToday' | 'triggerMonth' | 'subscriptionTag' | 'cellEstimateHint' | 'free' | 'perfSamples' | 'perfTtft' | 'perfP50' | 'perfP90' | 'perfTps' | 'perfLatency' | 'perfEstimated' | 'perfEmpty' | 'perfTpsUnit' | 'perfTitle' | 'perfHint' | 'perfAll' | 'perfChartEmpty' | 'heatmapYear' | 'heatmapHalf' | 'heatmapMonth' | 'metricCost' | 'metricTokens' | 'heatmapTotal' | 'activeDays' | 'streakDays' | 'subscriptionAutoDetect' | 'pluginAuthor' | 'pluginRepository' | 'pluginNpm' | 'pluginLicense' | 'tabToken' | 'tokenExport' | 'tokenExportCsv' | 'tokenReasoningShare' | 'tokenReasoningShort' | 'tokenIo' | 'tokenPeak' | 'tokenDaily' | 'tokenByModel' | 'tokenMiss' | 'tokenHit' | 'tokenOutput' | 'tokenViewStructure' | 'tokenViewModel' | 'tokenHitShort' | 'tokenMissShort' | 'tokenTotal' | 'tokenShare' | 'usageStatsTool' | 'usageStatsToolHint' | 'balanceGranted' | 'balanceTopped' | 'balanceDaily' | 'balanceDaysLong' | 'balanceDaysUnit' | 'popTodayModel' | 'popNoConsumption' | 'popTitle' | 'popDirectLead' | 'popSubLead' | 'unpricedHint' | 'searchEstimateHint' | 'siteListDisplay' | 'siteListDisplayHint' | 'liveCostBar' | 'liveCostBarHint' | 'liveCostBarPosition' | 'liveCostBarPosBelow' | 'liveCostBarPosAbove' | 'liveCostBarPosToolbar' | 'exportCsvSite' | 'panelRelayQuota' | 'relayBalance' | 'relayNoQuota' | 'relayWindowUsed' | 'relayKindNewApi' | 'relayKindSub2Api' | 'relayKindUnknown' | 'expandAll' | 'collapseAll' | 'todayOnly' | 'todayNoConsumption';
|
|
2
|
+
export type UsageBillingKey = 'title' | 'cost' | 'todayCost' | 'monthCost' | 'yearCost' | 'monthProjected' | 'liveTurn' | 'liveSession' | 'calls' | 'cacheHitRate' | 'tokens' | 'inputTokens' | 'outputTokens' | 'avgCost' | 'kpiRange' | 'avgRangeToday' | 'avgRange7d' | 'avgRangeWeek' | 'avgRangeMonth' | 'avgRangeAll' | 'trend' | 'trend7d' | 'trend30d' | 'trendMetric' | 'trendMetricCost' | 'trendMetricTokens' | 'pricingSearch' | 'bandPinHint' | 'trendEmpty' | 'trendTotal' | 'roundsUnit' | 'healthModelsLive' | 'healthVendorsDown' | 'healthVendorsUnavailable' | 'budget' | 'budgetRemain' | 'budgetAmount' | 'budgetSummary' | 'sessions' | 'sessionTitle' | 'lastActive' | 'sessionOverflow' | 'budgetTierBody' | 'models' | 'providerBilling' | 'channelUnknown' | 'directTag' | 'relayTag' | 'unknownTag' | 'unknownChannelHint' | 'planCountUnit' | 'actual' | 'pricing' | 'input' | 'output' | 'cacheHit' | 'peak' | 'offPeak' | 'flat' | 'band' | 'close' | 'footer' | 'footerCredit' | 'lastUpdated' | 'noData' | 'todayRate' | 'rateLive' | 'rateBuiltin' | 'pricingSyncedAt' | 'pricingSyncNow' | 'pricingSyncing' | 'promoBadge' | 'promoUntil' | 'promoOpenEnded' | 'pricingTip' | 'pricingUnit' | 'pricingNotes' | 'ubPeak' | 'ubOff' | 'ubStd' | 'peakBand' | 'pricingSource' | 'noteCache' | 'noteBand' | 'noteSource' | 'balance' | 'recharge' | 'rechargeHint' | 'balanceUnconfigured' | 'balanceUnauthorized' | 'balanceUnreachable' | 'uncatalogued' | 'estimatedPricing' | 'balanceDays' | 'balanceLowBody' | 'reconcileDrift' | 'reconcileDismiss' | 'subscriptionNotConfigured' | 'subscriptionUnauthorized' | 'subscriptionUnavailable' | 'subscriptionInvalid' | 'subscriptionRateLimited' | 'subscriptionSession' | 'subscriptionWeekly' | 'subscriptionMonthly' | 'subscriptionBilling' | 'subscriptionRemaining' | 'subscriptionExhausted' | 'subscriptionReset' | 'subscriptionNoApi' | 'floatWindow' | 'floatModeCombined' | 'floatModeSubscription' | 'floatMode' | 'floatTargets' | 'floatWindowHint' | 'floatPrimaryToday' | 'floatPrimaryWeek' | 'floatPrimaryMonth' | 'floatNoTargets' | 'floatNoTargetsHint' | 'cardDisplay' | 'cardDisplayHint' | 'cardMetric' | 'cardMetricMoney' | 'cardMetricTokens' | 'cardSpan' | 'cardSpanDay' | 'cardSpanWeek' | 'cardSpanMonth' | 'triggerMonthTokens' | 'triggerTodayTokens' | 'triggerWeekTokens' | 'subscriptionsStale' | 'staleLedgerNotice' | 'unreadableNotice' | 'unreadableFormatNotice' | 'unreadableNoLedgerNotice' | 'tokenCacheWrite' | 'toolRank' | 'toolName' | 'userPrices' | 'userPricesHint' | 'userPriceSave' | 'userPriceModel' | 'userPriceSource' | 'userPriceSourceHint' | 'userPriceOffPeak' | 'userPriceNormal' | 'userPriceCurrency' | 'userPriceAdd' | 'userPriceRemoveSelected' | 'userPriceSelect' | 'sessionStaleBadge' | 'sessionUntitled' | 'heatmapLess' | 'heatmapMore' | 'currency' | 'currencyCny' | 'currencyUsd' | 'currencyEur' | 'language' | 'heatmap' | 'rounds' | 'roundsHint' | 'anomaly' | 'model' | 'thModel' | 'thInputMiss' | 'thInputHit' | 'costAbbr' | 'tabOverview' | 'tabTrends' | 'tabProviders' | 'tabPricing' | 'tabSettings' | 'budgetHint' | 'peakAlertHint' | 'peakAlertDescPeak' | 'peakAlertDescOff' | 'peakAlertDescPeakZhipu' | 'peakAlertDescOffZhipu' | 'export' | 'exportCsvDay' | 'exportCsvSession' | 'exportJson' | 'peakShare' | 'peakSharePerCall' | 'offPeakSavings' | 'perfMax' | 'weekCost' | 'roleCost' | 'roleUser' | 'roleAssistant' | 'roleTool' | 'roleHint' | 'tierPeak' | 'tierOff' | 'tierToPeak' | 'tierToOff' | 'tierAlertEnterPeak' | 'tierAlertEnterOff' | 'tierAlertEnterPeakZhipu' | 'tierAlertEnterOffZhipu' | 'peakAlertTitlePeak' | 'peakAlertTitleOff' | 'peakAlert' | 'peakAlertLeadMin' | 'peakAlertPos' | 'peakAlertMode' | 'peakAlertPosCorner' | 'peakAlertPosCenter' | 'peakAlertModePeak' | 'peakAlertModeOff' | 'peakAlertModeBoth' | 'peakAlertWebNotify' | 'peakAlertPreview' | 'planTypeCode' | 'planTypeToken' | 'subscriptionFeePerMonth' | 'triggerToday' | 'triggerMonth' | 'subscriptionTag' | 'cellEstimateHint' | 'free' | 'perfSamples' | 'perfTtft' | 'perfP50' | 'perfP90' | 'perfTps' | 'perfLatency' | 'perfEstimated' | 'perfEmpty' | 'perfTpsUnit' | 'perfTitle' | 'perfHint' | 'perfAll' | 'perfChartEmpty' | 'heatmapYear' | 'heatmapHalf' | 'heatmapMonth' | 'metricCost' | 'metricTokens' | 'heatmapTotal' | 'activeDays' | 'streakDays' | 'subscriptionAutoDetect' | 'pluginAuthor' | 'pluginRepository' | 'pluginNpm' | 'pluginLicense' | 'tabToken' | 'tokenExport' | 'tokenExportCsv' | 'tokenReasoningShare' | 'tokenReasoningShort' | 'tokenIo' | 'tokenPeak' | 'tokenDaily' | 'tokenByModel' | 'tokenMiss' | 'tokenHit' | 'tokenOutput' | 'tokenViewStructure' | 'tokenViewModel' | 'tokenHitShort' | 'tokenMissShort' | 'tokenTotal' | 'tokenShare' | 'usageStatsTool' | 'usageStatsToolHint' | 'balanceGranted' | 'balanceTopped' | 'balanceDaily' | 'balanceDaysLong' | 'balanceDaysUnit' | 'popTodayModel' | 'popNoConsumption' | 'popTitle' | 'popDirectLead' | 'popSubLead' | 'unpricedHint' | 'searchEstimateHint' | 'siteListDisplay' | 'siteListDisplayHint' | 'liveCostBar' | 'liveCostBarHint' | 'liveCostBarPosition' | 'liveCostBarPosBelow' | 'liveCostBarPosAbove' | 'liveCostBarPosToolbar' | 'exportCsvSite' | 'panelRelayQuota' | 'relayBalance' | 'relayNoQuota' | 'relayWindowUsed' | 'relayKindNewApi' | 'relayKindSub2Api' | 'relayKindUnknown' | 'expandAll' | 'collapseAll' | 'todayOnly' | 'todayNoConsumption';
|
|
3
3
|
export declare const NS = "usageBilling";
|
|
4
4
|
export declare const zh: Record<UsageBillingKey, string>;
|
|
5
5
|
export declare const en: Record<UsageBillingKey, string>;
|
|
@@ -278,12 +278,12 @@ export interface ModelPrice extends PriceBand {
|
|
|
278
278
|
* (主档与 offPeak)单价按 factor 折扣计价与显示,截止时刻起自动恢复刊例价。
|
|
279
279
|
*/
|
|
280
280
|
export interface PricePromo {
|
|
281
|
-
/** 折扣系数(0.5 =
|
|
281
|
+
/** 折扣系数(0.5 = 五折,0 = 活动期免费);仅 [0,1) 区间有效,非法值视为无促销。 */
|
|
282
282
|
factor: number;
|
|
283
283
|
/**
|
|
284
284
|
* 分档折扣覆盖:厂商对不同档位给不同折扣时逐档指定(如 GPT-5.6 Sol 促销为
|
|
285
285
|
* 缓存 0.8 / 输入 0.8 / 输出 2/3)。缺省档位沿用 {@link factor};单档取值
|
|
286
|
-
* 仅
|
|
286
|
+
* 仅 [0,1) 区间有效(0 = 该档活动期免费),非法值回落 factor。
|
|
287
287
|
*/
|
|
288
288
|
factors?: Partial<Record<'input' | 'cacheHit' | 'cacheMiss' | 'output', number>>;
|
|
289
289
|
/**
|
|
@@ -382,8 +382,8 @@ export declare function modelOf(key: string): ModelEntry;
|
|
|
382
382
|
*/
|
|
383
383
|
export declare function isPriced(key: string): boolean;
|
|
384
384
|
/**
|
|
385
|
-
* 促销在 nowMs 是否生效:factor 必须落在
|
|
386
|
-
* endsAtMs 缺省表示长期活动,在 factor 合法期间持续生效。
|
|
385
|
+
* 促销在 nowMs 是否生效:factor 必须落在 [0,1) 区间(0 = 活动期免费,issue #86),
|
|
386
|
+
* 截止时刻及之后视为过期;endsAtMs 缺省表示长期活动,在 factor 合法期间持续生效。
|
|
387
387
|
* 导出供测试:纯函数。
|
|
388
388
|
* @param promo - 待判定的促销窗口。
|
|
389
389
|
* @param nowMs - 判定时刻(epoch ms)。
|
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": "1.4.
|
|
4
|
+
"version": "1.4.23",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
7
7
|
"deepseek",
|