@kenz1117/dsh-ui-usage-billing 1.0.9 → 1.0.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.md +2 -0
- package/lib/client.js +5 -5
- package/lib/index.js +30 -6
- package/lib/types/aggregate.d.ts +13 -2
- package/lib/types/client/UsageBilling.d.ts +10 -0
- package/lib/types/client/locales.d.ts +1 -1
- package/lib/types/client/pricing.d.ts +21 -8
- package/lib/types/client/usage-billing-settings.d.ts +10 -14
- package/lib/types/client/vendor-logos.d.ts +7 -4
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -1866,6 +1866,7 @@ function serializeFold(fold) {
|
|
|
1866
1866
|
byModel: Object.fromEntries(fold.byModel),
|
|
1867
1867
|
byDay: Object.fromEntries(fold.byDay),
|
|
1868
1868
|
byDayModels: Object.fromEntries([...fold.byDayModels].map(([day, models]) => [day, Object.fromEntries(models)])),
|
|
1869
|
+
byDayModelsSite: Object.fromEntries([...fold.byDayModelsSite].map(([day, models]) => [day, Object.fromEntries([...models].map(([model, sites]) => [model, Object.fromEntries(sites)]))])),
|
|
1869
1870
|
byTier: Object.fromEntries(fold.byTier),
|
|
1870
1871
|
byTool: Object.fromEntries(fold.byTool),
|
|
1871
1872
|
bySite: Object.fromEntries(fold.bySite),
|
|
@@ -1884,6 +1885,7 @@ function deserializeFold(fold) {
|
|
|
1884
1885
|
byModel: new Map(Object.entries(fold.byModel)),
|
|
1885
1886
|
byDay: new Map(Object.entries(fold.byDay)),
|
|
1886
1887
|
byDayModels: new Map(Object.entries(fold.byDayModels).map(([day, models]) => [day, new Map(Object.entries(models))])),
|
|
1888
|
+
byDayModelsSite: new Map(Object.entries(fold.byDayModelsSite ?? {}).map(([day, models]) => [day, new Map(Object.entries(models).map(([model, sites]) => [model, new Map(Object.entries(sites))]))])),
|
|
1887
1889
|
byTier: new Map(Object.entries(fold.byTier ?? {})),
|
|
1888
1890
|
byTool: new Map(Object.entries(fold.byTool ?? {})),
|
|
1889
1891
|
bySite: new Map(Object.entries(fold.bySite)),
|
|
@@ -1941,6 +1943,20 @@ function modelDayCell(map, day, modelKey) {
|
|
|
1941
1943
|
}
|
|
1942
1944
|
return usageCell(models, modelKey);
|
|
1943
1945
|
}
|
|
1946
|
+
/** Get-or-create one day×model×site cell inside the three-dimensional map. */
|
|
1947
|
+
function modelDaySiteCell(map, day, modelKey, siteKey) {
|
|
1948
|
+
let models = map.get(day);
|
|
1949
|
+
if (models === void 0) {
|
|
1950
|
+
models = /* @__PURE__ */ new Map();
|
|
1951
|
+
map.set(day, models);
|
|
1952
|
+
}
|
|
1953
|
+
let sites = models.get(modelKey);
|
|
1954
|
+
if (sites === void 0) {
|
|
1955
|
+
sites = /* @__PURE__ */ new Map();
|
|
1956
|
+
models.set(modelKey, sites);
|
|
1957
|
+
}
|
|
1958
|
+
return usageCell(sites, siteKey);
|
|
1959
|
+
}
|
|
1944
1960
|
/** Get-or-create one turn's accumulation state. */
|
|
1945
1961
|
function turnState(turns, turn) {
|
|
1946
1962
|
const existing = turns.get(turn);
|
|
@@ -1983,6 +1999,7 @@ function foldSession(events, subscriptionProviders, officialProviderIds, routes
|
|
|
1983
1999
|
byModel: /* @__PURE__ */ new Map(),
|
|
1984
2000
|
byDay: /* @__PURE__ */ new Map(),
|
|
1985
2001
|
byDayModels: /* @__PURE__ */ new Map(),
|
|
2002
|
+
byDayModelsSite: /* @__PURE__ */ new Map(),
|
|
1986
2003
|
byTier: /* @__PURE__ */ new Map(),
|
|
1987
2004
|
byTool: /* @__PURE__ */ new Map(),
|
|
1988
2005
|
bySite: /* @__PURE__ */ new Map(),
|
|
@@ -2059,14 +2076,16 @@ function foldSession(events, subscriptionProviders, officialProviderIds, routes
|
|
|
2059
2076
|
const model = event.data.body?.model;
|
|
2060
2077
|
const modelKey = typeof model === "string" && model !== "" ? resolveCatalogKey(model) : "other";
|
|
2061
2078
|
const day = dayStamp(event.time);
|
|
2079
|
+
const siteKey = siteBucketKey({
|
|
2080
|
+
kind: "direct",
|
|
2081
|
+
provider: "deepseek"
|
|
2082
|
+
});
|
|
2062
2083
|
foldSearchCall(fold.total, searchCallEstimateCny);
|
|
2063
2084
|
foldSearchCall(usageCell(fold.byModel, modelKey), searchCallEstimateCny);
|
|
2064
2085
|
foldSearchCall(usageCell(fold.byDay, day), searchCallEstimateCny);
|
|
2065
2086
|
foldSearchCall(modelDayCell(fold.byDayModels, day, modelKey), searchCallEstimateCny);
|
|
2066
|
-
foldSearchCall(
|
|
2067
|
-
|
|
2068
|
-
provider: "deepseek"
|
|
2069
|
-
})), searchCallEstimateCny);
|
|
2087
|
+
foldSearchCall(modelDaySiteCell(fold.byDayModelsSite, day, modelKey, siteKey), searchCallEstimateCny);
|
|
2088
|
+
foldSearchCall(usageCell(fold.bySite, siteKey), searchCallEstimateCny);
|
|
2070
2089
|
foldSearchCall(usageCell(fold.byTier, tierAt(event.time)), searchCallEstimateCny);
|
|
2071
2090
|
continue;
|
|
2072
2091
|
}
|
|
@@ -2111,6 +2130,7 @@ function foldSession(events, subscriptionProviders, officialProviderIds, routes
|
|
|
2111
2130
|
foldUsage(usageCell(fold.byModel, modelKey), usage, modelKey, subscription, event.time, official);
|
|
2112
2131
|
foldUsage(usageCell(fold.byDay, day), usage, modelKey, subscription, event.time, official);
|
|
2113
2132
|
foldUsage(modelDayCell(fold.byDayModels, day, modelKey), usage, modelKey, subscription, event.time, official);
|
|
2133
|
+
foldUsage(modelDaySiteCell(fold.byDayModelsSite, day, modelKey, siteBucket), usage, modelKey, subscription, event.time, official);
|
|
2114
2134
|
foldUsage(usageCell(fold.bySite, siteBucket), usage, modelKey, subscription, event.time, official);
|
|
2115
2135
|
foldUsage(usageCell(fold.byTier, tierAt(event.time)), usage, modelKey, subscription, event.time, official);
|
|
2116
2136
|
if (subscription) fold.planCalls.set(modelKey, (fold.planCalls.get(modelKey) ?? 0) + 1);
|
|
@@ -2321,7 +2341,7 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
2321
2341
|
id,
|
|
2322
2342
|
...meta.cwd === void 0 ? {} : { cwd: meta.cwd },
|
|
2323
2343
|
...stamp === null ? {} : { stamp },
|
|
2324
|
-
foldVersion:
|
|
2344
|
+
foldVersion: 4,
|
|
2325
2345
|
fold: serializeFold(fold)
|
|
2326
2346
|
};
|
|
2327
2347
|
const previous = ledger.get(id);
|
|
@@ -2345,7 +2365,7 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
2345
2365
|
for (const entry of ledger.values()) {
|
|
2346
2366
|
if (included.has(entry.id)) continue;
|
|
2347
2367
|
try {
|
|
2348
|
-
const stale = (entry.foldVersion ?? 1) <
|
|
2368
|
+
const stale = (entry.foldVersion ?? 1) < 4;
|
|
2349
2369
|
folds.push({
|
|
2350
2370
|
id: entry.id,
|
|
2351
2371
|
...entry.cwd === void 0 ? {} : { cwd: entry.cwd },
|
|
@@ -2375,6 +2395,7 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
2375
2395
|
const byModel = /* @__PURE__ */ new Map();
|
|
2376
2396
|
const byDay = /* @__PURE__ */ new Map();
|
|
2377
2397
|
const byDayModels = /* @__PURE__ */ new Map();
|
|
2398
|
+
const byDayModelsSite = /* @__PURE__ */ new Map();
|
|
2378
2399
|
const byTier = /* @__PURE__ */ new Map();
|
|
2379
2400
|
const byTool = /* @__PURE__ */ new Map();
|
|
2380
2401
|
const bySite = /* @__PURE__ */ new Map();
|
|
@@ -2400,6 +2421,7 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
2400
2421
|
for (const [modelKey, cell] of fold.byModel) mergeUsageInto(usageCell(byModel, modelKey), cell);
|
|
2401
2422
|
for (const [day, cell] of fold.byDay) mergeUsageInto(usageCell(byDay, day), cell);
|
|
2402
2423
|
for (const [day, models] of fold.byDayModels) for (const [modelKey, cell] of models) mergeUsageInto(modelDayCell(byDayModels, day, modelKey), cell);
|
|
2424
|
+
for (const [day, models] of fold.byDayModelsSite) for (const [modelKey, sites] of models) for (const [siteKey, cell] of sites) mergeUsageInto(modelDaySiteCell(byDayModelsSite, day, modelKey, siteKey), cell);
|
|
2403
2425
|
for (const [siteKey, cell] of fold.bySite) mergeUsageInto(usageCell(bySite, siteKey), cell);
|
|
2404
2426
|
for (const [tierKey, cell] of fold.byTier) mergeUsageInto(usageCell(byTier, tierKey), cell);
|
|
2405
2427
|
for (const [toolName, count] of fold.byTool) byTool.set(toolName, (byTool.get(toolName) ?? 0) + count);
|
|
@@ -2473,6 +2495,7 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
2473
2495
|
return record;
|
|
2474
2496
|
};
|
|
2475
2497
|
const toModelDayRecord = (map) => Object.fromEntries([...map].map(([day, models]) => [day, Object.fromEntries(models)]));
|
|
2498
|
+
const toModelDaySiteRecord = (map) => Object.fromEntries([...map].map(([day, models]) => [day, Object.fromEntries([...models].map(([model, sites]) => [model, Object.fromEntries(sites)]))]));
|
|
2476
2499
|
const perf = perfModel.size === 0 ? void 0 : {
|
|
2477
2500
|
byModel: Object.fromEntries([...perfModel].map(([model, acc]) => [model, {
|
|
2478
2501
|
samples: acc.ttfts.length,
|
|
@@ -2500,6 +2523,7 @@ function createUsageAggregator(persistence, options = {}) {
|
|
|
2500
2523
|
byModel: toRecord(byModel),
|
|
2501
2524
|
byDay: toRecord(byDay),
|
|
2502
2525
|
byDayModels: toModelDayRecord(byDayModels),
|
|
2526
|
+
...byDayModelsSite.size === 0 ? {} : { byDayModelsSite: toModelDaySiteRecord(byDayModelsSite) },
|
|
2503
2527
|
bySession: sessionRows.slice(0, 100),
|
|
2504
2528
|
byTurn: turnRows.slice(0, 200),
|
|
2505
2529
|
byWorkspace: workspaces.slice(0, 100),
|
package/lib/types/aggregate.d.ts
CHANGED
|
@@ -175,6 +175,12 @@ export interface UsageStatsDocument {
|
|
|
175
175
|
byDay: Record<string, ModelUsage>;
|
|
176
176
|
/** 模型 × 日期 二维统计:趋势图按模型堆叠的输入([date][modelKey])。 */
|
|
177
177
|
byDayModels: Record<string, Record<string, ModelUsage>>;
|
|
178
|
+
/**
|
|
179
|
+
* 模型 × 日期 × 站点 三维统计([date][modelKey][siteKey]):供「按 origin 绑定
|
|
180
|
+
* 自定义价」的显示层重估(issue #16)——只有这个维度能知道「某模型从某中转站
|
|
181
|
+
* 消耗了多少 token」。旧算法快照缺失。
|
|
182
|
+
*/
|
|
183
|
+
byDayModelsSite?: Record<string, Record<string, Record<string, ModelUsage>>>;
|
|
178
184
|
/**
|
|
179
185
|
* 峰谷分桶(真实判档):折叠时逐调用按 `tierAt(event.time)` 归入高峰/低谷桶,
|
|
180
186
|
* 峰谷占比与「挪谷省钱」据此展示,不再按比例估算。旧快照可能缺失。
|
|
@@ -335,6 +341,8 @@ export interface SessionFold {
|
|
|
335
341
|
byModel: Map<string, ModelUsage>;
|
|
336
342
|
byDay: Map<string, ModelUsage>;
|
|
337
343
|
byDayModels: Map<string, Map<string, ModelUsage>>;
|
|
344
|
+
/** 模型×日期×站点三维(issue #16):供按 origin 绑定自定义价的显示层重估。 */
|
|
345
|
+
byDayModelsSite: Map<string, Map<string, Map<string, ModelUsage>>>;
|
|
338
346
|
/** 峰谷分桶:折叠时按调用时刻精确判档(tierAt),键 = 'peak' / 'offPeak'。 */
|
|
339
347
|
byTier: Map<string, ModelUsage>;
|
|
340
348
|
/** 工具调用次数(键 = 工具名;tool-call-delta 首见计数)。 */
|
|
@@ -371,6 +379,8 @@ export interface SerializedSessionFold {
|
|
|
371
379
|
byModel: Record<string, ModelUsage>;
|
|
372
380
|
byDay: Record<string, ModelUsage>;
|
|
373
381
|
byDayModels: Record<string, Record<string, ModelUsage>>;
|
|
382
|
+
/** 1.0.10(issue #16)新增;模型×日期×站点三维,旧账本行缺失(合并按空处理)。 */
|
|
383
|
+
byDayModelsSite?: Record<string, Record<string, Record<string, ModelUsage>>>;
|
|
374
384
|
/** 1.0.8 起新增;旧账本行缺失(合并时按空处理,不触发重折算)。 */
|
|
375
385
|
byTier?: Record<string, ModelUsage>;
|
|
376
386
|
byTool?: Record<string, number>;
|
|
@@ -407,10 +417,11 @@ export interface UsageLedgerDocument {
|
|
|
407
417
|
* 折叠算法版本:归账语义变化时递增。v1 = 按 request/header 归账(稀疏 header 把
|
|
408
418
|
* 两次 header 之间的用量串到上一个模型,订阅模型首当其冲,issue #14);v2 =
|
|
409
419
|
* `assistant/message` 自带 source 归账(1.0.7 起);v3 = 联网搜索请求按次估算
|
|
410
|
-
* 计费(issue #15,1.0.9
|
|
420
|
+
* 计费(issue #15,1.0.9 起);v4 = 模型×日期×站点三维桶(issue #16,按 origin
|
|
421
|
+
* 绑定自定义价的显示层重估)——旧行缺该维度,按无 origin 价处理。
|
|
411
422
|
* 持久账本行据此区分新旧算法:日志已删/不可读而只能沿用旧行时,UI 标注置信度提示。
|
|
412
423
|
*/
|
|
413
|
-
export declare const FOLD_VERSION =
|
|
424
|
+
export declare const FOLD_VERSION = 4;
|
|
414
425
|
/**
|
|
415
426
|
* 一次性账本迁移:id 唯一,apply 在加载边界对原始文档执行,已应用过的跳过。
|
|
416
427
|
* 未来账本/schema 字段变更(重命名、拆桶、语义调整)时,在此追加一条迁移并
|
|
@@ -179,6 +179,16 @@ export interface UsageStats {
|
|
|
179
179
|
cacheMiss: number;
|
|
180
180
|
cost: number;
|
|
181
181
|
}>>;
|
|
182
|
+
/** 模型 × 日期 × 站点 三维统计([date][modelKey][siteKey]):按 origin 绑定自定义价的
|
|
183
|
+
* 显示层重估数据源;旧快照可能缺失。 */
|
|
184
|
+
byDayModelsSite?: Record<string, Record<string, Record<string, {
|
|
185
|
+
calls: number;
|
|
186
|
+
input: number;
|
|
187
|
+
output: number;
|
|
188
|
+
cacheHit: number;
|
|
189
|
+
cacheMiss: number;
|
|
190
|
+
cost: number;
|
|
191
|
+
}>>>;
|
|
182
192
|
/**
|
|
183
193
|
* 峰谷分桶(全量逐调用真实判档):1.0.8 起服务端按调用时刻精确归桶,
|
|
184
194
|
* 峰谷占比条优先用它(覆盖全部历史调用);旧快照缺失时回退逐轮估算。
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Locale dictionaries for the usage billing surface. */
|
|
2
|
-
export type UsageBillingKey = 'billing.title' | 'billing.subtitle' | 'billing.cost' | 'billing.todayCost' | 'billing.monthCost' | 'billing.yearCost' | 'billing.monthProjected' | 'billing.liveTurn' | 'billing.liveSession' | 'billing.totalCost' | 'billing.calls' | 'billing.cacheHitRate' | 'billing.tokens' | 'billing.inputTokens' | 'billing.outputTokens' | 'billing.avgCost' | 'billing.trend' | 'billing.trend7d' | 'billing.trend30d' | 'billing.trendMetric' | 'billing.trendMetricCost' | 'billing.trendMetricTokens' | 'billing.trendEmpty' | 'billing.budget' | 'billing.budgetAmount' | 'billing.budgetSummary' | 'billing.sessions' | 'billing.sessionTitle' | 'billing.project' | 'billing.lastActive' | 'billing.sessionOverflow' | 'billing.budgetTierBody' | 'billing.models' | 'billing.providerBilling' | 'billing.estimated' | 'billing.actual' | 'billing.pricing' | 'billing.showPricing' | 'billing.hidePricing' | 'billing.pricePerM' | 'billing.input' | 'billing.output' | 'billing.cacheHit' | 'billing.peak' | 'billing.offPeak' | 'billing.flat' | 'billing.peakHours' | 'billing.band' | 'billing.openDashboard' | 'billing.close' | 'billing.footer' | 'billing.footerCredit' | 'billing.lastUpdated' | 'billing.noData' | 'billing.todayRate' | 'billing.rateLive' | 'billing.rateBuiltin' | 'billing.promoBadge' | 'billing.promoUntil' | 'billing.promoOpenEnded' | 'billing.pricingTip' | 'billing.pricingUnit' | 'billing.pricingNotes' | 'billing.ubPeak' | 'billing.ubOff' | 'billing.ubStd' | 'billing.peakBand' | 'billing.pricingSource' | 'billing.noteCache' | 'billing.noteBand' | 'billing.noteSource' | 'billing.balance' | 'billing.balanceUnconfigured' | 'billing.balanceUnauthorized' | 'billing.balanceUnreachable' | 'billing.uncatalogued' | 'billing.estimatedPricing' | 'billing.balanceDays' | 'billing.balanceLowBody' | 'billing.reconcileDrift' | 'billing.reconcileDismiss' | 'billing.subscriptions' | 'billing.subscriptionNotConfigured' | 'billing.subscriptionUnauthorized' | 'billing.subscriptionUnavailable' | 'billing.subscriptionInvalid' | 'billing.subscriptionRateLimited' | 'billing.subscriptionSession' | 'billing.subscriptionWeekly' | 'billing.subscriptionMonthly' | 'billing.subscriptionBilling' | 'billing.subscriptionRemaining' | 'billing.subscriptionExhausted' | 'billing.subscriptionReset' | 'billing.subscriptionNoApi' | 'billing.floatWindow' | 'billing.floatModeCombined' | 'billing.floatModeSubscription' | 'billing.floatMode' | 'billing.floatTargets' | 'billing.floatWindowHint' | 'billing.floatNoTargets' | 'billing.floatNoTargetsHint' | 'billing.cardDisplay' | 'billing.cardDisplayHint' | 'billing.cardMetric' | 'billing.cardMetricMoney' | 'billing.cardMetricTokens' | 'billing.triggerMonthTokens' | 'billing.floatPrev' | 'billing.floatNext' | 'billing.subscriptionsStale' | 'billing.staleLedgerNotice' | 'billing.tokenCacheWrite' | 'billing.toolRank' | 'billing.toolName' | 'billing.userPrices' | 'billing.userPricesHint' | 'billing.userPriceSave' | 'billing.sessionStaleBadge' | 'billing.heatmapLess' | 'billing.heatmapMore' | 'billing.currency' | 'billing.currencyCny' | 'billing.currencyUsd' | 'billing.heatmap' | 'billing.rounds' | 'billing.roundsHint' | 'billing.anomaly' | 'billing.workspaces' | 'billing.workspacesHint' | 'billing.plan' | 'billing.remaining' | 'billing.unknownModel' | 'billing.model' | 'billing.thModel' | 'billing.thInputMiss' | 'billing.thInputHit' | 'billing.currentRound' | 'billing.costAbbr' | 'billing.tabOverview' | 'billing.tabTrends' | 'billing.tabProviders' | 'billing.tabDetails' | 'billing.tabPricing' | 'billing.tabSettings' | 'billing.settingsHead' | 'billing.settingsHint' | 'billing.budgetHint' | 'billing.peakAlertHint' | 'billing.peakAlertDescPeak' | 'billing.peakAlertDescOff' | 'billing.export' | 'billing.exportCsvDay' | 'billing.exportCsvSession' | 'billing.exportJson' | 'billing.peakShare' | 'billing.peakSharePerCall' | 'billing.offPeakSavings' | 'billing.perfMax' | 'billing.weekCost' | 'billing.roleCost' | 'billing.roleUser' | 'billing.roleAssistant' | 'billing.roleTool' | 'billing.roleHint' | 'billing.tierPeak' | 'billing.tierOff' | 'billing.tierToPeak' | 'billing.tierToOff' | 'billing.tierAlertEnterPeak' | 'billing.tierAlertEnterOff' | 'billing.peakAlertTitlePeak' | 'billing.peakAlertTitleOff' | 'billing.peakAlert' | 'billing.peakAlertLeadMin' | 'billing.peakAlertPos' | 'billing.peakAlertMode' | 'billing.peakAlertPosCorner' | 'billing.peakAlertPosCenter' | 'billing.peakAlertModePeak' | 'billing.peakAlertModeOff' | 'billing.peakAlertModeBoth' | 'billing.peakAlertWebNotify' | 'billing.peakAlertPreview' | 'billing.planTypeCode' | 'billing.planTypeToken' | 'billing.subscriptionFeePerMonth' | 'billing.triggerToday' | 'billing.triggerMonth' | 'billing.subscriptionIncluded' | 'billing.free' | 'billing.official' | 'billing.thirdParty' | 'billing.officialCost' | 'billing.thirdPartyCost' | 'billing.perfSamples' | 'billing.perfTtft' | 'billing.perfP50' | 'billing.perfP90' | 'billing.perfTps' | 'billing.perfLatency' | 'billing.perfEstimated' | 'billing.perfEmpty' | 'billing.perfTpsUnit' | 'billing.perfTitle' | 'billing.perfHint' | 'billing.heatmapYear' | 'billing.heatmapMonth' | 'billing.activeDays' | 'billing.streakDays' | 'billing.subscriptionAutoDetect' | 'billing.pluginInfo' | 'billing.pluginName' | 'billing.pluginDescription' | 'billing.pluginVersion' | 'billing.pluginAuthor' | 'billing.pluginRepository' | 'billing.pluginNpm' | 'billing.pluginLicense' | 'billing.tabToken' | 'billing.tokenExport' | 'billing.tokenExportCsv' | 'billing.tokenCacheHitRate' | 'billing.tokenReasoningShare' | 'billing.tokenReasoningShort' | 'billing.tokenIo' | 'billing.tokenPeak' | 'billing.tokenDaily' | 'billing.tokenByModel' | 'billing.tokenMiss' | 'billing.tokenHit' | 'billing.tokenOutput' | 'billing.tokenTotal' | 'billing.tokenShare' | 'billing.usageStatsTool' | 'billing.usageStatsToolHint' | 'billing.balanceGranted' | 'billing.balanceTopped' | 'billing.balanceDaily' | 'billing.balanceDaysLong' | 'billing.balanceDaysUnit' | 'billing.popTodayModel' | 'billing.popNoConsumption' | 'billing.popQuotaAlert' | 'billing.popRiskNone' | 'billing.popTitle' | 'billing.popDirectLead' | 'billing.popSubLead' | 'billing.popBalanceNormal' | 'billing.popBalanceLow' | 'billing.popQuotaNormal' | 'billing.popQuotaLow' | 'billing.alertBalanceLow' | 'billing.alertQuotaLow' | 'billing.unpricedHint' | 'billing.searchEstimateHint' | 'billing.siteListDisplay' | 'billing.siteListDisplayHint' | 'billing.exportCsvSite' | 'billing.panelRelay' | 'billing.relaySite' | 'billing.relayDirect' | 'billing.relayUnknown' | 'billing.panelRelayQuota' | 'billing.relayBalance' | 'billing.relayNoQuota' | 'billing.relayWindowUsed' | 'billing.relayKindNewApi' | 'billing.relayKindSub2Api' | 'billing.relayKindUnknown' | 'billing.relayCalls';
|
|
2
|
+
export type UsageBillingKey = 'billing.title' | 'billing.subtitle' | 'billing.cost' | 'billing.todayCost' | 'billing.monthCost' | 'billing.yearCost' | 'billing.monthProjected' | 'billing.liveTurn' | 'billing.liveSession' | 'billing.totalCost' | 'billing.calls' | 'billing.cacheHitRate' | 'billing.tokens' | 'billing.inputTokens' | 'billing.outputTokens' | 'billing.avgCost' | 'billing.trend' | 'billing.trend7d' | 'billing.trend30d' | 'billing.trendMetric' | 'billing.trendMetricCost' | 'billing.trendMetricTokens' | 'billing.trendEmpty' | 'billing.budget' | 'billing.budgetAmount' | 'billing.budgetSummary' | 'billing.sessions' | 'billing.sessionTitle' | 'billing.project' | 'billing.lastActive' | 'billing.sessionOverflow' | 'billing.budgetTierBody' | 'billing.models' | 'billing.providerBilling' | 'billing.estimated' | 'billing.actual' | 'billing.pricing' | 'billing.showPricing' | 'billing.hidePricing' | 'billing.pricePerM' | 'billing.input' | 'billing.output' | 'billing.cacheHit' | 'billing.peak' | 'billing.offPeak' | 'billing.flat' | 'billing.peakHours' | 'billing.band' | 'billing.openDashboard' | 'billing.close' | 'billing.footer' | 'billing.footerCredit' | 'billing.lastUpdated' | 'billing.noData' | 'billing.todayRate' | 'billing.rateLive' | 'billing.rateBuiltin' | 'billing.promoBadge' | 'billing.promoUntil' | 'billing.promoOpenEnded' | 'billing.pricingTip' | 'billing.pricingUnit' | 'billing.pricingNotes' | 'billing.ubPeak' | 'billing.ubOff' | 'billing.ubStd' | 'billing.peakBand' | 'billing.pricingSource' | 'billing.noteCache' | 'billing.noteBand' | 'billing.noteSource' | 'billing.balance' | 'billing.balanceUnconfigured' | 'billing.balanceUnauthorized' | 'billing.balanceUnreachable' | 'billing.uncatalogued' | 'billing.estimatedPricing' | 'billing.balanceDays' | 'billing.balanceLowBody' | 'billing.reconcileDrift' | 'billing.reconcileDismiss' | 'billing.subscriptions' | 'billing.subscriptionNotConfigured' | 'billing.subscriptionUnauthorized' | 'billing.subscriptionUnavailable' | 'billing.subscriptionInvalid' | 'billing.subscriptionRateLimited' | 'billing.subscriptionSession' | 'billing.subscriptionWeekly' | 'billing.subscriptionMonthly' | 'billing.subscriptionBilling' | 'billing.subscriptionRemaining' | 'billing.subscriptionExhausted' | 'billing.subscriptionReset' | 'billing.subscriptionNoApi' | 'billing.floatWindow' | 'billing.floatModeCombined' | 'billing.floatModeSubscription' | 'billing.floatMode' | 'billing.floatTargets' | 'billing.floatWindowHint' | 'billing.floatNoTargets' | 'billing.floatNoTargetsHint' | 'billing.cardDisplay' | 'billing.cardDisplayHint' | 'billing.cardMetric' | 'billing.cardMetricMoney' | 'billing.cardMetricTokens' | 'billing.triggerMonthTokens' | 'billing.floatPrev' | 'billing.floatNext' | 'billing.subscriptionsStale' | 'billing.staleLedgerNotice' | 'billing.tokenCacheWrite' | 'billing.toolRank' | 'billing.toolName' | 'billing.userPrices' | 'billing.userPricesHint' | 'billing.userPriceSave' | 'billing.userPriceModel' | 'billing.userPriceSource' | 'billing.userPriceSourceHint' | 'billing.userPriceCurrency' | 'billing.userPriceAdd' | 'billing.userPriceRemove' | 'billing.sessionStaleBadge' | 'billing.heatmapLess' | 'billing.heatmapMore' | 'billing.currency' | 'billing.currencyCny' | 'billing.currencyUsd' | 'billing.heatmap' | 'billing.rounds' | 'billing.roundsHint' | 'billing.anomaly' | 'billing.workspaces' | 'billing.workspacesHint' | 'billing.plan' | 'billing.remaining' | 'billing.unknownModel' | 'billing.model' | 'billing.thModel' | 'billing.thInputMiss' | 'billing.thInputHit' | 'billing.currentRound' | 'billing.costAbbr' | 'billing.tabOverview' | 'billing.tabTrends' | 'billing.tabProviders' | 'billing.tabDetails' | 'billing.tabPricing' | 'billing.tabSettings' | 'billing.settingsHead' | 'billing.settingsHint' | 'billing.budgetHint' | 'billing.peakAlertHint' | 'billing.peakAlertDescPeak' | 'billing.peakAlertDescOff' | 'billing.export' | 'billing.exportCsvDay' | 'billing.exportCsvSession' | 'billing.exportJson' | 'billing.peakShare' | 'billing.peakSharePerCall' | 'billing.offPeakSavings' | 'billing.perfMax' | 'billing.weekCost' | 'billing.roleCost' | 'billing.roleUser' | 'billing.roleAssistant' | 'billing.roleTool' | 'billing.roleHint' | 'billing.tierPeak' | 'billing.tierOff' | 'billing.tierToPeak' | 'billing.tierToOff' | 'billing.tierAlertEnterPeak' | 'billing.tierAlertEnterOff' | 'billing.peakAlertTitlePeak' | 'billing.peakAlertTitleOff' | 'billing.peakAlert' | 'billing.peakAlertLeadMin' | 'billing.peakAlertPos' | 'billing.peakAlertMode' | 'billing.peakAlertPosCorner' | 'billing.peakAlertPosCenter' | 'billing.peakAlertModePeak' | 'billing.peakAlertModeOff' | 'billing.peakAlertModeBoth' | 'billing.peakAlertWebNotify' | 'billing.peakAlertPreview' | 'billing.planTypeCode' | 'billing.planTypeToken' | 'billing.subscriptionFeePerMonth' | 'billing.triggerToday' | 'billing.triggerMonth' | 'billing.subscriptionIncluded' | 'billing.free' | 'billing.official' | 'billing.thirdParty' | 'billing.officialCost' | 'billing.thirdPartyCost' | 'billing.perfSamples' | 'billing.perfTtft' | 'billing.perfP50' | 'billing.perfP90' | 'billing.perfTps' | 'billing.perfLatency' | 'billing.perfEstimated' | 'billing.perfEmpty' | 'billing.perfTpsUnit' | 'billing.perfTitle' | 'billing.perfHint' | 'billing.heatmapYear' | 'billing.heatmapMonth' | 'billing.activeDays' | 'billing.streakDays' | 'billing.subscriptionAutoDetect' | 'billing.pluginInfo' | 'billing.pluginName' | 'billing.pluginDescription' | 'billing.pluginVersion' | 'billing.pluginAuthor' | 'billing.pluginRepository' | 'billing.pluginNpm' | 'billing.pluginLicense' | 'billing.tabToken' | 'billing.tokenExport' | 'billing.tokenExportCsv' | 'billing.tokenCacheHitRate' | 'billing.tokenReasoningShare' | 'billing.tokenReasoningShort' | 'billing.tokenIo' | 'billing.tokenPeak' | 'billing.tokenDaily' | 'billing.tokenByModel' | 'billing.tokenMiss' | 'billing.tokenHit' | 'billing.tokenOutput' | 'billing.tokenTotal' | 'billing.tokenShare' | 'billing.usageStatsTool' | 'billing.usageStatsToolHint' | 'billing.balanceGranted' | 'billing.balanceTopped' | 'billing.balanceDaily' | 'billing.balanceDaysLong' | 'billing.balanceDaysUnit' | 'billing.popTodayModel' | 'billing.popNoConsumption' | 'billing.popQuotaAlert' | 'billing.popRiskNone' | 'billing.popTitle' | 'billing.popDirectLead' | 'billing.popSubLead' | 'billing.popBalanceNormal' | 'billing.popBalanceLow' | 'billing.popQuotaNormal' | 'billing.popQuotaLow' | 'billing.alertBalanceLow' | 'billing.alertQuotaLow' | 'billing.unpricedHint' | 'billing.searchEstimateHint' | 'billing.siteListDisplay' | 'billing.siteListDisplayHint' | 'billing.exportCsvSite' | 'billing.panelRelay' | 'billing.relaySite' | 'billing.relayDirect' | 'billing.relayUnknown' | 'billing.panelRelayQuota' | 'billing.relayBalance' | 'billing.relayNoQuota' | 'billing.relayWindowUsed' | 'billing.relayKindNewApi' | 'billing.relayKindSub2Api' | 'billing.relayKindUnknown' | 'billing.relayCalls';
|
|
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>;
|
|
@@ -42,16 +42,29 @@ export interface UserPrice {
|
|
|
42
42
|
/** 计价币种;缺省 CNY。 */
|
|
43
43
|
currency?: 'CNY' | 'USD';
|
|
44
44
|
}
|
|
45
|
+
/** 一条用户自定义价:绑定「模型(计费目录键)+ 可选来源(中转站 origin)」。
|
|
46
|
+
* origin 缺省 = 该模型的默认价;带 origin = 仅该中转站的同名模型用此价。 */
|
|
47
|
+
export interface UserPriceEntry extends UserPrice {
|
|
48
|
+
/** 计费目录键(如 `flash`、`minimax-m2.7`)。 */
|
|
49
|
+
key: string;
|
|
50
|
+
/** 绑定来源(中转站 origin,如 `https://api.my-relay.com`);缺省 = 默认价。 */
|
|
51
|
+
origin?: string;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 注入用户自定义单价列表。每条含模型目录键 + 可选来源(origin)。空数组 = 清除全部
|
|
55
|
+
* 自定义价,回退内置目录。
|
|
56
|
+
* @param entries - 用户自定义价条目(列表)。
|
|
57
|
+
*/
|
|
58
|
+
export declare function applyUserPrices(entries: Readonly<UserPriceEntry[]>): void;
|
|
59
|
+
/** 当前生效的用户自定义价条目(设置面板回显用);未设置时 undefined。 */
|
|
60
|
+
export declare function getUserPrices(): Readonly<UserPriceEntry[]> | undefined;
|
|
45
61
|
/**
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* @param
|
|
62
|
+
* 查一个模型(可选来源)的用户自定义价:优先「模型×来源」精确命中;无来源匹配时
|
|
63
|
+
* 回落该模型的无来源默认价;再无则 undefined(走内置目录)。
|
|
64
|
+
* @param key - 计费目录键。
|
|
65
|
+
* @param origin - 调用来源(中转站 origin);缺省仅查默认价。
|
|
49
66
|
*/
|
|
50
|
-
export declare function
|
|
51
|
-
/** 当前生效的用户自定义单价(设置面板回显用);未设置时 undefined。 */
|
|
52
|
-
export declare function getUserPrices(): Readonly<Record<string, UserPrice>> | undefined;
|
|
53
|
-
/** 查一个计费键的用户自定义价(精确键 → 归一化键两跳)。 */
|
|
54
|
-
export declare function userPriceOf(key: string): UserPrice | undefined;
|
|
67
|
+
export declare function userPriceOf(key: string, origin?: string): UserPrice | undefined;
|
|
55
68
|
/**
|
|
56
69
|
* Apply the node half's live pricing snapshot. Absent fields keep the
|
|
57
70
|
* built-in catalog and rate; callers never fabricate values.
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* client 半区在「设置」Tab 渲染开关并写入同一命名空间。缺省的默认行为是关闭——
|
|
7
7
|
* 避免该工具默认占用模型每次请求的上下文(coding 场景通常在仪表盘看用量)。
|
|
8
8
|
*/
|
|
9
|
+
import type { UserPriceEntry } from './pricing.ts';
|
|
9
10
|
/** 设置命名空间 id(小写 kebab-case)。 */
|
|
10
11
|
export declare const BILLING_SETTINGS_NAMESPACE = "ui-usage-billing";
|
|
11
12
|
/** 该命名空间下用户可编辑的字段名。 */
|
|
@@ -72,22 +73,17 @@ export declare const SITE_LIST_STORAGE_KEY = "dsh.ui-usage-billing.sites";
|
|
|
72
73
|
export declare function loadSiteListPrefs(): SiteListPrefs;
|
|
73
74
|
/** 写入站点列表偏好。失败静默(展示偏好非关键)。 */
|
|
74
75
|
export declare function saveSiteListPrefs(prefs: SiteListPrefs): void;
|
|
75
|
-
/** 用户自定义单价(与 client/pricing.ts 的 `
|
|
76
|
-
export
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
/** 缓存命中输入单价。 */
|
|
80
|
-
cacheHit: number;
|
|
81
|
-
/** 输出单价。 */
|
|
82
|
-
output: number;
|
|
83
|
-
/** 计价币种;缺省 CNY。 */
|
|
84
|
-
currency?: 'CNY' | 'USD';
|
|
85
|
-
}
|
|
86
|
-
/** 自定义价表:计费目录键 → 单价。 */
|
|
87
|
-
export type UserPriceMap = Record<string, StoredUserPrice>;
|
|
76
|
+
/** 用户自定义单价(与 client/pricing.ts 的 `UserPriceEntry` 同形;type import,无运行时依赖)。 */
|
|
77
|
+
export type StoredUserPrice = UserPriceEntry;
|
|
78
|
+
/** 自定义价表:模型(+可选来源)→ 单价,条目列表(支持同名模型绑定不同中转站 origin)。 */
|
|
79
|
+
export type UserPriceMap = UserPriceEntry[];
|
|
88
80
|
/** localStorage key(与其他 `dsh.ui-usage-billing.*` 偏好同命名空间)。 */
|
|
89
81
|
export declare const USER_PRICES_STORAGE_KEY = "dsh.ui-usage-billing.prices";
|
|
90
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* 读取用户自定义价(写入侧已校验,这里挡住手工改坏的非数字行 与 旧版单行对象格式)。
|
|
84
|
+
* 旧版(1.0.9 及更早)存的是 `Record<目录键, {input,cacheHit,output,currency?}>`,
|
|
85
|
+
* 迁移为「条目列表」(origin 缺省 = 该模型默认价)。仅在浏览器半区调用。
|
|
86
|
+
*/
|
|
91
87
|
export declare function loadUserPrices(): UserPriceMap;
|
|
92
88
|
/** 写入用户自定义价。失败静默(展示偏好非关键)。 */
|
|
93
89
|
export declare function saveUserPrices(prices: UserPriceMap): void;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Vendor logos: inlined data URIs fetched from models.dev /logos/{id}.svg.
|
|
3
|
+
* Kept to the highest-traffic providers only to hold the client bundle under the
|
|
4
|
+
* DSH Store 256 KiB single-file cap. Every other provider falls back to a
|
|
5
|
+
* brand-color letter badge in the UI (see VendorLogo in UsageBilling.tsx), so no
|
|
6
|
+
* external asset or copyright risk is introduced for unlisted vendors. 月之暗面 is
|
|
7
|
+
* a hand-redrawn minimal crescent — its official SVG is a multi-thousand-node path
|
|
8
|
+
* that alone cost ~6.8 KiB.
|
|
6
9
|
*/
|
|
7
10
|
export declare const VENDOR_LOGOS: Readonly<Record<string, string>>;
|
|
8
11
|
export declare function vendorLogoOf(provider: string): string | undefined;
|
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.0.
|
|
4
|
+
"version": "1.0.11",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|