@kenz1117/dsh-ui-usage-billing 1.4.21 → 1.4.22

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 CHANGED
@@ -3192,6 +3192,13 @@ function configFingerprint(subscriptionMatcher, officialProviderIds, routeAliase
3192
3192
  String(searchEstimate)
3193
3193
  ].join("|");
3194
3194
  }
3195
+ /** 会话格式拒读(宿主 SessionFormatUnsupportedError):日志完好,仅本构建读不了。
3196
+ * 宿主错误类显式落 error.name;经包装/重建的路径只在 message 里保留类名,两处都认。
3197
+ * 据此把「格式不受支持」与「损坏/其他读取失败」分开计数,供面板按原因归因(issue #84)。 */
3198
+ function isFormatUnsupportedError(error) {
3199
+ if (!(error instanceof Error)) return false;
3200
+ return error.name === "SessionFormatUnsupportedError" || error.message.includes("SessionFormatUnsupportedError");
3201
+ }
3195
3202
  /**
3196
3203
  * Create the incremental usage aggregator.
3197
3204
  * @param persistence - the session persistence service.
@@ -3301,12 +3308,22 @@ function createUsageAggregator(persistence, options = {}) {
3301
3308
  const included = /* @__PURE__ */ new Set();
3302
3309
  const folds = [];
3303
3310
  const skipped = [];
3311
+ let carriedUnreadable = 0;
3312
+ let carriedFormatUnreadable = 0;
3313
+ let formatUnreadable = 0;
3314
+ let noLedgerUnreadable = 0;
3304
3315
  let staleLedgerSessions = 0;
3305
3316
  for (const meta of metas) {
3306
3317
  const id = String(meta.id);
3307
3318
  seen.add(id);
3308
3319
  const stamp = await stampOf(meta);
3309
- if (stamp !== null && unreadable.get(id) === stamp) continue;
3320
+ const mark = unreadable.get(id);
3321
+ if (stamp !== null && mark !== void 0 && mark.stamp === stamp) {
3322
+ carriedUnreadable += 1;
3323
+ if (mark.format) carriedFormatUnreadable += 1;
3324
+ if (!ledger.has(id)) noLedgerUnreadable += 1;
3325
+ continue;
3326
+ }
3310
3327
  const hit = cache.get(id);
3311
3328
  if (hit !== void 0 && stamp !== null && hit.stamp === stamp) {
3312
3329
  cache.delete(id);
@@ -3389,7 +3406,13 @@ function createUsageAggregator(persistence, options = {}) {
3389
3406
  recordLedger(id, cwd, stamp, fold);
3390
3407
  } catch (error) {
3391
3408
  skipped.push(id);
3392
- if (stamp !== null) unreadable.set(id, stamp);
3409
+ const format = isFormatUnsupportedError(error);
3410
+ if (format) formatUnreadable += 1;
3411
+ if (!ledger.has(id)) noLedgerUnreadable += 1;
3412
+ if (stamp !== null) unreadable.set(id, {
3413
+ stamp,
3414
+ format
3415
+ });
3393
3416
  console.warn("[usage-billing] skip unreadable session", id, error);
3394
3417
  }
3395
3418
  }
@@ -3569,6 +3592,8 @@ function createUsageAggregator(persistence, options = {}) {
3569
3592
  }).filter((entry) => entry !== null))];
3570
3593
  }))
3571
3594
  };
3595
+ const unreadableTotal = skipped.length + carriedUnreadable;
3596
+ const formatUnreadableTotal = formatUnreadable + carriedFormatUnreadable;
3572
3597
  lastDoc = {
3573
3598
  version: 4,
3574
3599
  updatedAt: now,
@@ -3592,7 +3617,9 @@ function createUsageAggregator(persistence, options = {}) {
3592
3617
  ...searchEstimate > 0 ? { searchCallEstimateCny: searchEstimate } : {},
3593
3618
  ...perf === void 0 ? {} : { perf },
3594
3619
  ...staleLedgerSessions > 0 ? { staleLedgerSessions } : {},
3595
- ...skipped.length > 0 ? { unreadableSessions: skipped.length } : {},
3620
+ ...unreadableTotal > 0 ? { unreadableSessions: unreadableTotal } : {},
3621
+ ...formatUnreadableTotal > 0 ? { unreadableFormatSessions: formatUnreadableTotal } : {},
3622
+ ...noLedgerUnreadable > 0 ? { unreadableNoLedgerSessions: noLedgerUnreadable } : {},
3596
3623
  byRole: (() => {
3597
3624
  const chars = roles.userChars + roles.toolChars;
3598
3625
  const userShare = chars > 0 ? roles.userChars / chars : .5;
@@ -295,8 +295,12 @@ export interface UsageStatsDocument {
295
295
  perf?: PerfStats;
296
296
  /** 只存在于账本、且缺 foldVersion 的旧会话数;无旧行时省略。 */
297
297
  staleLedgerSessions?: number;
298
- /** 读时迁移拒读而未统计的会话数(原始日志未动,上游修复后自动恢复);无拒读时省略。 */
298
+ /** 本轮未完整计入的会话数(新失败 + 负缓存命中,跨轮次稳定;有账本旧行的按存档计入最近可用点);无失败时省略。 */
299
299
  unreadableSessions?: number;
300
+ /** 未统计中因会话格式拒读(SessionFormatUnsupportedError)的子集;无格式拒读时省略。 */
301
+ unreadableFormatSessions?: number;
302
+ /** 未统计中无任何账本存档、完全未计入的子集(区别于有存档按最近可用点计入的);无存档缺失时省略。 */
303
+ unreadableNoLedgerSessions?: number;
300
304
  }
301
305
  /** 按角色费用归因:user / tool 为输入成本的启发式摊分,assistant 为输出成本实测。 */
302
306
  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+cache); used by the `tokens` metric. */
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
- /** 读时迁移拒读而未统计的会话数;0 或缺省 = 全部会话已统计。 */
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>;
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.21",
4
+ "version": "1.4.22",
5
5
  "keywords": [
6
6
  "deepseek-harness",
7
7
  "deepseek",