@kenz1117/dsh-ui-usage-billing 1.4.29 → 1.4.30
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/index.js +74 -19
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -2301,6 +2301,9 @@ const BUILTIN_MODEL_KEY_ALIASES = {
|
|
|
2301
2301
|
"deepseek-v4.1-flash": "flash",
|
|
2302
2302
|
"deepseek-v4.1-flash-expires-on-0910": "flash",
|
|
2303
2303
|
"deepseek-flash": "flash",
|
|
2304
|
+
"deepseek/deepseek-v4.1-flash": "flash",
|
|
2305
|
+
"deepseek/deepseek-flash": "flash",
|
|
2306
|
+
"deepseek/deepseek-v4-pro": "pro",
|
|
2304
2307
|
"deepseek-v4-pro": "pro",
|
|
2305
2308
|
"glm-5.2": "glm",
|
|
2306
2309
|
"glm-4.5-air": "glm-4.5-air",
|
|
@@ -2491,6 +2494,29 @@ const DEFAULT_SUBSCRIPTION_PROVIDERS = [
|
|
|
2491
2494
|
function isOfficialProvider(provider) {
|
|
2492
2495
|
return /^deepseek(?:-[a-z0-9-]+)?$/i.test(provider.trim());
|
|
2493
2496
|
}
|
|
2497
|
+
/**
|
|
2498
|
+
* 已知的「第三方插件注册 adapter」通道名单:这些 provider 由别的插件经
|
|
2499
|
+
* `ctx.llm.registerAdapter` 注册,**不经 llm-pi-ai 路由表**,因此
|
|
2500
|
+
* {@link siteRefOf} 的路由查询必然 miss。与订阅豁免(issue #37 的 grok
|
|
2501
|
+
* build)同构:显式列出、不做「猜通道」的通用发现——猜错会把来源不明的
|
|
2502
|
+
* 路由也归位成可计价通道,违背 issue #82「支出主体无法核实不计价」的初衷。
|
|
2503
|
+
*
|
|
2504
|
+
* 收录语义:这些通道的支出主体是可核实的(WorkBuddy = 腾讯 WorkBuddy
|
|
2505
|
+
* 桌面 App 账号),计费用模型官方目录价折算 Token 成本,与积分制解耦。
|
|
2506
|
+
* 它们**不是** DeepSeek 官方渠道——{@link isOfficialProvider} 与
|
|
2507
|
+
* {@link officialChannelOf} 都不会因本名单把调用算进官方口径。
|
|
2508
|
+
*/
|
|
2509
|
+
const KNOWN_ADAPTER_PROVIDERS = /* @__PURE__ */ new Set(["workbuddy", "workbuddy-ai"]);
|
|
2510
|
+
/**
|
|
2511
|
+
* 一个 provider 是否命中已知 adapter 通道名单。判定与 {@link isOfficialProvider}
|
|
2512
|
+
* 同姿态:trim + 大小写不敏感——dsh-workbuddy-connect 注册的是小写 id
|
|
2513
|
+
*(`workbuddy` / `workbuddy-ai`),宿主不强制 route 小写,按大小写归一化可避免
|
|
2514
|
+
* 日志里出现大小写变体时静默落回 unknown 桶。路由表命中仍优先于本名单
|
|
2515
|
+
*(见 {@link siteRefOf}),用户自配的大小写变体走配置路径。
|
|
2516
|
+
*/
|
|
2517
|
+
function isKnownAdapterProvider(provider) {
|
|
2518
|
+
return KNOWN_ADAPTER_PROVIDERS.has(provider.trim().toLowerCase());
|
|
2519
|
+
}
|
|
2494
2520
|
/** DeepSeek 官方直连端点的归一化 origin(`siteOriginOf` 口径):有 baseURL 的路由只有它算官方。 */
|
|
2495
2521
|
const OFFICIAL_DEEPSEEK_ORIGIN = "https://api.deepseek.com";
|
|
2496
2522
|
/**
|
|
@@ -2527,8 +2553,10 @@ function siteOriginOf(baseURL) {
|
|
|
2527
2553
|
* 网关)按 `direct` 归位——unknown 一刀切会把官方直连的费用堆进「未知路由」组、
|
|
2528
2554
|
* 且不算官方渠道(回归修复);订阅豁免命中的通道(显式 `subscriptionProviders`
|
|
2529
2555
|
* 配置或判定函数)同样按 `direct` 归位——订阅管理类插件注册的通道不经路由表,
|
|
2530
|
-
* unknown 桶既掩盖归属又按 token 误计费(issue #37 的 grok build
|
|
2531
|
-
*
|
|
2556
|
+
* unknown 桶既掩盖归属又按 token 误计费(issue #37 的 grok build);已知的第三方
|
|
2557
|
+
* 插件注册 adapter 通道({@link KNOWN_ADAPTER_PROVIDERS},如 workbuddy)同样按
|
|
2558
|
+
* `direct` 归位——支出主体可核实、按模型官方目录价计 Token 成本,unknown 桶会让
|
|
2559
|
+
* 费用恒为 0;其余配置外名称保留 unknown(无法核实通道)。
|
|
2532
2560
|
* @param provider - 会话日志里的 provider 路由名(request/header 的 `config.provider`)。
|
|
2533
2561
|
* @param routes - 当前 provider 路由视图(来自 llm-pi-ai providers)。
|
|
2534
2562
|
* @param opts - `subscription`:该调用是否已被订阅豁免判定命中。
|
|
@@ -2554,6 +2582,10 @@ function siteRefOf(provider, routes, opts = {}) {
|
|
|
2554
2582
|
kind: "direct",
|
|
2555
2583
|
provider
|
|
2556
2584
|
};
|
|
2585
|
+
if (isKnownAdapterProvider(provider)) return {
|
|
2586
|
+
kind: "direct",
|
|
2587
|
+
provider
|
|
2588
|
+
};
|
|
2557
2589
|
return {
|
|
2558
2590
|
kind: "unknown",
|
|
2559
2591
|
provider
|
|
@@ -3419,6 +3451,15 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
3419
3451
|
const searchEstimate = options.searchCallEstimateCny ?? .02;
|
|
3420
3452
|
const foldFingerprint = configFingerprint(subscriptionMatcher, officialProviderIds, routeAliases, searchEstimate);
|
|
3421
3453
|
const FOLD_CHUNK_EVENTS = 8e3;
|
|
3454
|
+
/** 惰性失效(价目表时序):账本/缓存行若以「当时不可计价」的模型落盘,
|
|
3455
|
+
* 而价目表现在能计价了,该行必须重折——否则显示层按实时价算出的
|
|
3456
|
+
* 「目录价预估」与服务端 cost=0 并存,出现「明细比总额高」的双口径
|
|
3457
|
+
* (真机复现:预热折叠早于 models.dev 价目表注入,step-5-preview
|
|
3458
|
+
* 折叠时刻 cost=0、渲染时刻 ≈¥38)。复用判定据此刻检查:不走配置指纹
|
|
3459
|
+
* (价目表不进指纹,否则 6h 一次的价格刷新会让全部历史行全量重折),
|
|
3460
|
+
* 也不 bump FOLD_VERSION(那是归账语义版本,价目表时序不是语义变更)。
|
|
3461
|
+
* 价目表变化后未受影响行(无 unpriced 模型)依旧零读取复用。 */
|
|
3462
|
+
const foldNeedsReprice = (fold) => fold.unpricedModels.some((key) => isPriced(key));
|
|
3422
3463
|
/** 把一份(全量或增量)折叠结果登记进 durable ledger;内容无变化时不落盘。 */
|
|
3423
3464
|
const recordLedger = (id, cwd, stamp, fold) => {
|
|
3424
3465
|
if (options.ledger === void 0) return;
|
|
@@ -3426,12 +3467,12 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
3426
3467
|
id,
|
|
3427
3468
|
...cwd === void 0 ? {} : { cwd },
|
|
3428
3469
|
...stamp === null ? {} : { stamp },
|
|
3429
|
-
foldVersion:
|
|
3470
|
+
foldVersion: 21,
|
|
3430
3471
|
fingerprint: foldFingerprint,
|
|
3431
3472
|
fold: serializeFold(fold)
|
|
3432
3473
|
};
|
|
3433
3474
|
const row = ledger.get(id);
|
|
3434
|
-
if (row === void 0 || row.stamp !== entry.stamp || row.cwd !== entry.cwd || row.foldVersion !== entry.foldVersion || row.fingerprint !== entry.fingerprint || stamp === null && JSON.stringify(row.fold) !== JSON.stringify(entry.fold)) {
|
|
3475
|
+
if (row === void 0 || row.stamp !== entry.stamp || row.cwd !== entry.cwd || row.foldVersion !== entry.foldVersion || row.fingerprint !== entry.fingerprint || stamp === null && JSON.stringify(row.fold) !== JSON.stringify(entry.fold) || stamp !== null && foldNeedsReprice(row.fold)) {
|
|
3435
3476
|
ledger.set(id, entry);
|
|
3436
3477
|
ledgerNeedsSave = true;
|
|
3437
3478
|
}
|
|
@@ -3518,22 +3559,10 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
3518
3559
|
if (!ledger.has(id)) noLedgerUnreadable += 1;
|
|
3519
3560
|
continue;
|
|
3520
3561
|
}
|
|
3521
|
-
const hit = cache.get(id);
|
|
3522
|
-
if (hit !== void 0 && stamp !== null && hit.stamp === stamp) {
|
|
3523
|
-
cache.delete(id);
|
|
3524
|
-
cache.set(id, hit);
|
|
3525
|
-
folds.push({
|
|
3526
|
-
id,
|
|
3527
|
-
...meta.cwd === void 0 ? {} : { cwd: meta.cwd },
|
|
3528
|
-
fold: hit.fold
|
|
3529
|
-
});
|
|
3530
|
-
included.add(id);
|
|
3531
|
-
continue;
|
|
3532
|
-
}
|
|
3533
3562
|
try {
|
|
3534
3563
|
const cwd = meta.cwd;
|
|
3535
3564
|
const ledgerRow = ledger.get(id);
|
|
3536
|
-
if (ledgerRow !== void 0 && ledgerRow.stamp !== void 0 && stamp !== null && ledgerRow.stamp === stamp && (ledgerRow.foldVersion ?? 1) ===
|
|
3565
|
+
if (ledgerRow !== void 0 && ledgerRow.stamp !== void 0 && stamp !== null && ledgerRow.stamp === stamp && (ledgerRow.foldVersion ?? 1) === 21 && ledgerRow.fingerprint === foldFingerprint && !foldNeedsReprice(ledgerRow.fold)) {
|
|
3537
3566
|
const fold = deserializeFold(ledgerRow.fold);
|
|
3538
3567
|
folds.push({
|
|
3539
3568
|
id,
|
|
@@ -3543,8 +3572,20 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
3543
3572
|
included.add(id);
|
|
3544
3573
|
continue;
|
|
3545
3574
|
}
|
|
3575
|
+
const hit = cache.get(id);
|
|
3576
|
+
if (hit !== void 0 && stamp !== null && hit.stamp === stamp && ![...hit.fold.unpricedModels].some((key) => isPriced(key))) {
|
|
3577
|
+
cache.delete(id);
|
|
3578
|
+
cache.set(id, hit);
|
|
3579
|
+
folds.push({
|
|
3580
|
+
id,
|
|
3581
|
+
...meta.cwd === void 0 ? {} : { cwd: meta.cwd },
|
|
3582
|
+
fold: hit.fold
|
|
3583
|
+
});
|
|
3584
|
+
included.add(id);
|
|
3585
|
+
continue;
|
|
3586
|
+
}
|
|
3546
3587
|
const previous = cache.get(id);
|
|
3547
|
-
if (previous?.machine !== void 0 && previous.lastSeq !== void 0) {
|
|
3588
|
+
if (previous?.machine !== void 0 && previous.lastSeq !== void 0 && ![...previous.fold.unpricedModels].some((key) => isPriced(key))) {
|
|
3548
3589
|
const from = previous.lastSeq + 1;
|
|
3549
3590
|
const { events, inheritedEventCount } = await persistence.readFrom(meta.id, sessionLogOffsetOf(from));
|
|
3550
3591
|
const after = await stampOf(meta);
|
|
@@ -3621,7 +3662,7 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
3621
3662
|
for (const entry of ledger.values()) {
|
|
3622
3663
|
if (included.has(entry.id)) continue;
|
|
3623
3664
|
try {
|
|
3624
|
-
const stale = (entry.foldVersion ?? 1) <
|
|
3665
|
+
const stale = (entry.foldVersion ?? 1) < 21 || entry.fingerprint !== foldFingerprint;
|
|
3625
3666
|
folds.push({
|
|
3626
3667
|
id: entry.id,
|
|
3627
3668
|
...entry.cwd === void 0 ? {} : { cwd: entry.cwd },
|
|
@@ -7456,11 +7497,25 @@ function apply(ctx, config = {}) {
|
|
|
7456
7497
|
});
|
|
7457
7498
|
let live = { source: "builtin" };
|
|
7458
7499
|
let pricingSyncedAt = 0;
|
|
7500
|
+
/** 价目表代际:extraModels 的 key 集合稳定序列化。fetchLivePricing 每次都重建
|
|
7501
|
+
* 数组(引用必不同),引用比较恒真会退化成「每次刷新都跑一轮 reprice」;
|
|
7502
|
+
* key 集合变化才代表真的出现了「此前未收录、现在可计价」的模型。 */
|
|
7503
|
+
const priceGenerationOf = (snapshot) => (snapshot.extraModels ?? []).map((model) => model.key).sort().join(",");
|
|
7504
|
+
let priceGeneration = priceGenerationOf(live);
|
|
7459
7505
|
const refreshPricing = async () => {
|
|
7460
7506
|
live = await fetchLivePricing();
|
|
7461
7507
|
pricingSyncedAt = Date.now();
|
|
7462
7508
|
applyLivePricing(live);
|
|
7463
7509
|
pricingBodyCache = void 0;
|
|
7510
|
+
const nextGeneration = priceGenerationOf(live);
|
|
7511
|
+
if (nextGeneration !== priceGeneration) {
|
|
7512
|
+
priceGeneration = nextGeneration;
|
|
7513
|
+
aggregator.aggregate().then(() => {
|
|
7514
|
+
console.info("[usage-billing] repriced folds after pricing refresh; ledger aligned with live catalog");
|
|
7515
|
+
}).catch((error) => {
|
|
7516
|
+
console.warn("[usage-billing] reprice fold after pricing refresh failed; next aggregate retries:", error);
|
|
7517
|
+
});
|
|
7518
|
+
}
|
|
7464
7519
|
};
|
|
7465
7520
|
let pricingBodyCache;
|
|
7466
7521
|
const pricingBody = () => {
|
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.30",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
7
7
|
"deepseek",
|