balance-dsh 1.0.9 → 1.1.0
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 +12 -8
- package/lib/index.js +116 -33
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -111,12 +111,13 @@ window.__ModuleLoader__.load({
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
// ─── 价格(与服务端默认保持一致;服务端 /balance-api 返回后会覆盖) ───
|
|
114
|
-
// 2026-
|
|
114
|
+
// 北京时间 2026-09-10 12:00 起生效的 DeepSeek V4-Flash 空闲时段价(元/百万 tokens)
|
|
115
|
+
// 注:历史消耗由服务端按当时价格回传 cost;这里的默认值只用于拉不到服务端分桶时的本地估算。
|
|
115
116
|
const DEFAULT_PRICING = {
|
|
116
|
-
inputCnyPerMillion: 1
|
|
117
|
-
cacheReadCnyPerMillion: 0.
|
|
117
|
+
inputCnyPerMillion: 1, // 输入(缓存未命中)
|
|
118
|
+
cacheReadCnyPerMillion: 0.02, // 输入(缓存命中)
|
|
118
119
|
cacheWriteCnyPerMillion: 0, // DeepSeek 无缓存写入计费
|
|
119
|
-
outputCnyPerMillion: 4
|
|
120
|
+
outputCnyPerMillion: 4, // 输出
|
|
120
121
|
};
|
|
121
122
|
|
|
122
123
|
let pricing = DEFAULT_PRICING;
|
|
@@ -808,9 +809,12 @@ function UsageCostLine({ useProjection, sessionId }) {
|
|
|
808
809
|
|
|
809
810
|
// 金额:优先用服务端分时段 usage(高峰桶×2+空闲桶);未就绪时按当前时段价估算
|
|
810
811
|
const split = splitReady && splitUsage ? splitUsage : null;
|
|
811
|
-
|
|
812
|
-
const
|
|
813
|
-
|
|
812
|
+
// 服务端分桶可能"非 null 但为空"(拿不到该会话事件),此时必须回退本地投影估算,否则金额会显示 ¥0.0000
|
|
813
|
+
const splitUsable =
|
|
814
|
+
split != null && ((Array.isArray(split.byModel) && split.byModel.length > 0) || (split.totalTokens ?? 0) > 0);
|
|
815
|
+
const cost = splitUsable ? computeSplitCost(split, p) : computeCost(usage, periodAdjustedPricing(p));
|
|
816
|
+
const peakTokens = splitUsable ? split.peak.inputTokens + split.peak.cacheReadTokens + split.peak.cacheWriteTokens + split.peak.outputTokens : 0;
|
|
817
|
+
const offpeakTokens = splitUsable ? split.offpeak.inputTokens + split.offpeak.cacheReadTokens + split.offpeak.cacheWriteTokens + split.offpeak.outputTokens : 0;
|
|
814
818
|
|
|
815
819
|
// 子代理消耗(与主会话分开,同样跨时段精确计费)
|
|
816
820
|
const hasSubagent = splitReady && subagentUsage && subagentUsage.totalTokens > 0;
|
|
@@ -825,7 +829,7 @@ function UsageCostLine({ useProjection, sessionId }) {
|
|
|
825
829
|
"新增:" + formatTokens(newTokens) + " tokens(未缓存输入 " + formatTokens(uncachedInput) + " + 输出 " + formatTokens(output) + ")",
|
|
826
830
|
"计费:" + formatTokens(billedTotal) + " tokens(含缓存重读 " + formatTokens(cacheRead) + "、缓存写入 " + formatTokens(cacheWrite) + ")",
|
|
827
831
|
];
|
|
828
|
-
if (
|
|
832
|
+
if (splitUsable) {
|
|
829
833
|
detailLines.push(
|
|
830
834
|
"时段拆分:高峰 " + formatTokens(peakTokens) + " tokens + 空闲 " + formatTokens(offpeakTokens) + " tokens",
|
|
831
835
|
"估算金额:¥" + cost.toFixed(4) + "(高峰桶按目录价×2、空闲桶按目录价)",
|
package/lib/index.js
CHANGED
|
@@ -55,10 +55,10 @@ const DEEPSEEK_BALANCE_URL = "https://api.deepseek.com/user/balance";
|
|
|
55
55
|
// DeepSeek 目录价(人民币/百万 tokens),按模型区分。默认取 2026-08-17 生效的
|
|
56
56
|
// V4 峰谷定价之空闲时段价;高峰时段(每日 9:00 / 14:00)价格为下表 2 倍,
|
|
57
57
|
// 可在 config.peak: true 时统一翻倍,或在 config.price 中逐项覆盖。
|
|
58
|
-
// V4-Flash 空闲:输入未命中 1
|
|
59
|
-
// V4-Pro 空闲:输入未命中 4.5 / 命中 0.15 / 输出 13.5
|
|
58
|
+
// V4-Flash 空闲:输入未命中 1 / 命中 0.02 / 输出 4(北京时间 2026-09-10 12:00 起的新价)
|
|
59
|
+
// V4-Pro 空闲:输入未命中 4.5 / 命中 0.15 / 输出 13.5(官方 2026-09-14 后继续提供,计费不变)
|
|
60
60
|
const MODEL_PRICING = {
|
|
61
|
-
"deepseek-v4-flash": { inputCnyPerMillion: 1
|
|
61
|
+
"deepseek-v4-flash": { inputCnyPerMillion: 1, cacheReadCnyPerMillion: 0.02, cacheWriteCnyPerMillion: 0, outputCnyPerMillion: 4 },
|
|
62
62
|
"deepseek-v4-pro": { inputCnyPerMillion: 4.5, cacheReadCnyPerMillion: 0.15, cacheWriteCnyPerMillion: 0, outputCnyPerMillion: 13.5 },
|
|
63
63
|
// 旧模型兜底(V3.2 时代目录价)
|
|
64
64
|
"deepseek-chat": { inputCnyPerMillion: 2, cacheReadCnyPerMillion: 0.5, cacheWriteCnyPerMillion: 0, outputCnyPerMillion: 3 },
|
|
@@ -66,6 +66,18 @@ const MODEL_PRICING = {
|
|
|
66
66
|
};
|
|
67
67
|
const FALLBACK_PRICING = MODEL_PRICING["deepseek-v4-flash"];
|
|
68
68
|
|
|
69
|
+
// 定价历史:官方自北京时间 2026-09-10 12:00 起下调 flash 系列价格。
|
|
70
|
+
// 调价前产生的 token 仍按当时旧价计算(历史消耗金额不因后续调价而改变),
|
|
71
|
+
// 之后的消耗用 MODEL_PRICING(新价)。按每条 usage 的事件时间戳自动选择。
|
|
72
|
+
const FLASH_REPRICED_AT = Date.parse("2026-09-10T12:00:00+08:00");
|
|
73
|
+
const LEGACY_MODEL_PRICING = {
|
|
74
|
+
"deepseek-v4-flash": { inputCnyPerMillion: 1.5, cacheReadCnyPerMillion: 0.05, cacheWriteCnyPerMillion: 0, outputCnyPerMillion: 4.5 },
|
|
75
|
+
};
|
|
76
|
+
/** 事件时间在调价前返回 "legacy"(旧价),之后返回 "current"(新价)。 */
|
|
77
|
+
function pricingEraAt(time) {
|
|
78
|
+
return Number(time) < FLASH_REPRICED_AT ? "legacy" : "current";
|
|
79
|
+
}
|
|
80
|
+
|
|
69
81
|
// 高峰/空闲时段(V4 峰谷定价,2026-08-17 生效):高峰价 = 空闲价 × 2。
|
|
70
82
|
// 官方高峰窗口(北京时间)为 周一至周五 09:00-12:00、14:00-18:00;周末全天空闲。
|
|
71
83
|
// 可用 config.peakWindows 覆盖小时窗口(如 [{start:9,end:12},{start:14,end:18}]),
|
|
@@ -91,19 +103,23 @@ function isPeakAt(timeMs, config) {
|
|
|
91
103
|
return weekdays.includes(weekday) && windows.some((w) => hour >= w.start && hour < w.end);
|
|
92
104
|
}
|
|
93
105
|
|
|
94
|
-
/** 把模型字符串规整到定价表键;未识别返回 ""(用 Flash 价兜底)。
|
|
106
|
+
/** 把模型字符串规整到定价表键;未识别返回 ""(用 Flash 价兜底)。
|
|
107
|
+
* 注意:官方已把 Flash 主名改为 `deepseek-flash`,旧名 `deepseek-v4-flash` /
|
|
108
|
+
* `deepseek-v4-flash-vision-exp` 虽已退役但仍按 Flash 价计费,因此统一按 "flash" 匹配。 */
|
|
95
109
|
function modelKey(model) {
|
|
96
110
|
const m = String(model || "").toLowerCase();
|
|
97
|
-
if (m.includes("v4-pro") || m.includes("v4pro")) return "deepseek-v4-pro";
|
|
98
|
-
if (m.includes("
|
|
111
|
+
if (m.includes("v4-pro") || m.includes("v4pro") || m.includes("-pro")) return "deepseek-v4-pro";
|
|
112
|
+
if (m.includes("flash")) return "deepseek-v4-flash";
|
|
99
113
|
if (m.includes("reasoner")) return "deepseek-reasoner";
|
|
100
114
|
if (m.includes("chat")) return "deepseek-chat";
|
|
101
115
|
return "";
|
|
102
116
|
}
|
|
103
117
|
|
|
104
|
-
/** 解析价格:模型基价 + config.price 覆盖,config.peak: true 时按高峰价翻倍。
|
|
105
|
-
|
|
106
|
-
|
|
118
|
+
/** 解析价格:模型基价 + config.price 覆盖,config.peak: true 时按高峰价翻倍。
|
|
119
|
+
* era = "legacy" 时取调价前的历史价(目前仅 flash 系列与现价不同)。 */
|
|
120
|
+
function resolvePricing(config, model = "", era = "current") {
|
|
121
|
+
const key = modelKey(model);
|
|
122
|
+
const base = (era === "legacy" && LEGACY_MODEL_PRICING[key]) || MODEL_PRICING[key] || FALLBACK_PRICING;
|
|
107
123
|
const price = config?.price ?? {};
|
|
108
124
|
const multiplier = config?.peak === true ? 2 : 1;
|
|
109
125
|
const pick = (name, fallback) => Number(price[name] ?? fallback) * multiplier;
|
|
@@ -115,9 +131,25 @@ function resolvePricing(config, model = "") {
|
|
|
115
131
|
};
|
|
116
132
|
}
|
|
117
133
|
|
|
134
|
+
/** 取会话事件数组。DSH 2.0.4 起 live session **不再有 `.events` 属性**,改用 `snapshotEvents()` 方法
|
|
135
|
+
* (签名 `snapshotEvents(fromSeq = 0, toSeqExclusive = seq)`,可无参调用取全部事件);
|
|
136
|
+
* 旧版仍读 `.events` 属性。两者都拿不到时返回空数组(由调用方降级)。 */
|
|
137
|
+
function sessionEvents(session) {
|
|
138
|
+
if (!session) return [];
|
|
139
|
+
if (typeof session.snapshotEvents === "function") {
|
|
140
|
+
try {
|
|
141
|
+
const events = session.snapshotEvents();
|
|
142
|
+
if (Array.isArray(events)) return events;
|
|
143
|
+
} catch {
|
|
144
|
+
// 落到旧属性兜底
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return Array.isArray(session.events) ? session.events : [];
|
|
148
|
+
}
|
|
149
|
+
|
|
118
150
|
/** 从会话中识别实际使用的模型。
|
|
119
151
|
* 优先用 DSH 会话的 requestHeader()/requestContext() 折叠结果(即官方 session.models 取 current 的同一来源),
|
|
120
|
-
*
|
|
152
|
+
* 取不到再回退遍历会话事件找 request/header / request/context 事件。 */
|
|
121
153
|
function detectModel(session) {
|
|
122
154
|
if (!session) return "";
|
|
123
155
|
// 1) DSH 官方折叠 API:requestHeader().config.model 或 requestContext().model
|
|
@@ -133,7 +165,7 @@ function detectModel(session) {
|
|
|
133
165
|
}
|
|
134
166
|
// 2) 兜底:遍历事件
|
|
135
167
|
let model = "";
|
|
136
|
-
for (const event of session
|
|
168
|
+
for (const event of sessionEvents(session)) {
|
|
137
169
|
if (event.type === "request/header") {
|
|
138
170
|
const m = event.data?.header?.config?.model;
|
|
139
171
|
if (typeof m === "string" && m !== "") model = m;
|
|
@@ -272,11 +304,14 @@ function sumEventsUsage(events, config, fallbackModel = "") {
|
|
|
272
304
|
bucket.cacheWriteTokens += usage.cacheWriteTokens ?? 0;
|
|
273
305
|
};
|
|
274
306
|
const empty = () => ({ inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheWriteTokens: 0 });
|
|
275
|
-
const byModel = new Map(); // model -> { model, peak, offpeak }
|
|
307
|
+
const byModel = new Map(); // `${model}\0${era}` -> { model, era, peak, offpeak }
|
|
276
308
|
for (const { usage, time } of latestByStep.values()) {
|
|
277
309
|
const model = modelAtTime(switches, time) || fallback;
|
|
278
|
-
|
|
279
|
-
|
|
310
|
+
// 同一模型在调价前后的 token 分开分桶,各自用当时的价格计算(历史金额不变)。
|
|
311
|
+
const era = pricingEraAt(time);
|
|
312
|
+
const key = `${model}\u0000${era}`;
|
|
313
|
+
let e = byModel.get(key);
|
|
314
|
+
if (!e) { e = { model, era, peak: empty(), offpeak: empty() }; byModel.set(key, e); }
|
|
280
315
|
add(isPeakAt(time, config) ? e.peak : e.offpeak, usage);
|
|
281
316
|
}
|
|
282
317
|
// 聚合 + 转换 byModel(按金额未在前面算,这里只分桶 + 汇总字段)
|
|
@@ -284,7 +319,7 @@ function sumEventsUsage(events, config, fallbackModel = "") {
|
|
|
284
319
|
const offpeak = empty();
|
|
285
320
|
const modelList = [];
|
|
286
321
|
for (const e of byModel.values()) {
|
|
287
|
-
modelList.push({ model: e.model, peak: e.peak, offpeak: e.offpeak });
|
|
322
|
+
modelList.push({ model: e.model, era: e.era, peak: e.peak, offpeak: e.offpeak, cost: estimateCost(e, resolvePricing(config, e.model, e.era)) });
|
|
288
323
|
peak.inputTokens += e.peak.inputTokens;
|
|
289
324
|
peak.outputTokens += e.peak.outputTokens;
|
|
290
325
|
peak.cacheReadTokens += e.peak.cacheReadTokens;
|
|
@@ -315,7 +350,7 @@ function sumEventsUsage(events, config, fallbackModel = "") {
|
|
|
315
350
|
function sumSessionUsage(session, config) {
|
|
316
351
|
if (session == null) return null;
|
|
317
352
|
// fallbackModel = detectModel(session):当 message 早于所有模型切换点时兜底,避免落到默认 flash 价。
|
|
318
|
-
return sumEventsUsage(session
|
|
353
|
+
return sumEventsUsage(sessionEvents(session), config, detectModel(session));
|
|
319
354
|
}
|
|
320
355
|
|
|
321
356
|
function estimateCost(usage, pricing) {
|
|
@@ -385,7 +420,7 @@ function collectDailyUsage(events, config, fallbackModel, cutoffMs, days, sessio
|
|
|
385
420
|
const model = modelAtTime(switches, time) || fallback;
|
|
386
421
|
const bucket = { peak: emptyTokenBucket(), offpeak: emptyTokenBucket() };
|
|
387
422
|
addTokenUsage(isPeakAt(time, config) ? bucket.peak : bucket.offpeak, usage);
|
|
388
|
-
day.cost += estimateCost(bucket, resolvePricing(config, model));
|
|
423
|
+
day.cost += estimateCost(bucket, resolvePricing(config, model, pricingEraAt(time)));
|
|
389
424
|
}
|
|
390
425
|
}
|
|
391
426
|
|
|
@@ -401,16 +436,35 @@ function recentDayKeys(days, now = Date.now()) {
|
|
|
401
436
|
}
|
|
402
437
|
|
|
403
438
|
async function buildUsageStats(ctx, config) {
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
}
|
|
439
|
+
const getService = typeof ctx.get === "function" ? ctx.get.bind(ctx) : null;
|
|
440
|
+
const controller = getService?.("sessionController");
|
|
441
|
+
const persistence = getService?.("sessionPersistence");
|
|
408
442
|
const now = Date.now();
|
|
409
443
|
const cutoffMs = now - (STATS_DAYS - 1) * 24 * 60 * 60 * 1000;
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
444
|
+
// 列举会话要跨 DSH 版本:新版 sessionController.list() 返回 { items:[{ sessionId, updatedAt }] }
|
|
445
|
+
// (已按活动降序,字段叫 sessionId);旧版 sessionPersistence.list() 返回 [{ id, createdAt }]
|
|
446
|
+
// (字段叫 id)。旧版 persistence 还提供 inspect(),新版改成 handle-based 的 open(),
|
|
447
|
+
// 所以读取一律走已做三级兼容的 inspectPersistedSession,不再直接调 persistence.inspect。
|
|
448
|
+
let sessionIds = [];
|
|
449
|
+
let availableSessions = 0;
|
|
450
|
+
if (typeof controller?.list === "function") {
|
|
451
|
+
const listed = await controller.list({});
|
|
452
|
+
const items = Array.isArray(listed) ? listed : (listed?.items ?? []);
|
|
453
|
+
sessionIds = items.map((item) => (item && typeof item === "object" ? item.sessionId ?? item.id : void 0));
|
|
454
|
+
availableSessions = sessionIds.length;
|
|
455
|
+
} else if (typeof persistence?.list === "function") {
|
|
456
|
+
const headers = await persistence.list();
|
|
457
|
+
const list = Array.isArray(headers) ? headers : [];
|
|
458
|
+
availableSessions = list.length;
|
|
459
|
+
sessionIds = [...list]
|
|
460
|
+
.filter((header) => header && typeof header.id === "string")
|
|
461
|
+
.sort((a, b) => Number(b.createdAt ?? 0) - Number(a.createdAt ?? 0))
|
|
462
|
+
.map((header) => header.id);
|
|
463
|
+
} else {
|
|
464
|
+
throw new Error("会话持久化服务不可用");
|
|
465
|
+
}
|
|
466
|
+
const selected = sessionIds
|
|
467
|
+
.filter((id) => typeof id === "string" && id !== "")
|
|
414
468
|
.slice(0, STATS_MAX_SESSIONS);
|
|
415
469
|
const daily = new Map();
|
|
416
470
|
let cursor = 0;
|
|
@@ -418,16 +472,16 @@ async function buildUsageStats(ctx, config) {
|
|
|
418
472
|
let failedSessions = 0;
|
|
419
473
|
const worker = async () => {
|
|
420
474
|
while (cursor < selected.length) {
|
|
421
|
-
const
|
|
475
|
+
const sessionId = selected[cursor++];
|
|
422
476
|
try {
|
|
423
|
-
const inspected = await
|
|
477
|
+
const inspected = await inspectPersistedSession(ctx, sessionId);
|
|
424
478
|
collectDailyUsage(
|
|
425
479
|
inspected?.events ?? [],
|
|
426
480
|
config,
|
|
427
481
|
detectModel({ requestHeader: () => void 0, requestContext: () => void 0, events: inspected?.events ?? [] }),
|
|
428
482
|
cutoffMs,
|
|
429
483
|
daily,
|
|
430
|
-
|
|
484
|
+
sessionId,
|
|
431
485
|
);
|
|
432
486
|
scannedSessions++;
|
|
433
487
|
} catch {
|
|
@@ -468,8 +522,8 @@ async function buildUsageStats(ctx, config) {
|
|
|
468
522
|
rangeDays: STATS_DAYS,
|
|
469
523
|
scannedSessions,
|
|
470
524
|
failedSessions,
|
|
471
|
-
availableSessions
|
|
472
|
-
limited:
|
|
525
|
+
availableSessions,
|
|
526
|
+
limited: availableSessions > selected.length,
|
|
473
527
|
generatedAt: now,
|
|
474
528
|
};
|
|
475
529
|
}
|
|
@@ -564,7 +618,15 @@ async function sumSubagentUsage(ctx, sessionId, config) {
|
|
|
564
618
|
}
|
|
565
619
|
|
|
566
620
|
function formatUsage(usage, pricing, model = "") {
|
|
567
|
-
|
|
621
|
+
// 优先按 byModel 逐段累加(每段用各自模型 + 价格时代的价格,历史消耗不因调价而变);
|
|
622
|
+
// 无 byModel 时回退单一价格估算(兼容旧数据)。
|
|
623
|
+
const cost =
|
|
624
|
+
Array.isArray(usage.byModel) && usage.byModel.length > 0
|
|
625
|
+
? usage.byModel.reduce(
|
|
626
|
+
(sum, b) => sum + (typeof b.cost === "number" && Number.isFinite(b.cost) ? b.cost : estimateCost(b, pricing)),
|
|
627
|
+
0,
|
|
628
|
+
)
|
|
629
|
+
: estimateCost(usage, pricing);
|
|
568
630
|
const newTokens = usage.inputTokens + usage.outputTokens;
|
|
569
631
|
const peakTokens = usage.peak.inputTokens + usage.peak.cacheReadTokens + usage.peak.cacheWriteTokens + usage.peak.outputTokens;
|
|
570
632
|
const offpeakTokens = usage.offpeak.inputTokens + usage.offpeak.cacheReadTokens + usage.offpeak.cacheWriteTokens + usage.offpeak.outputTokens;
|
|
@@ -710,6 +772,27 @@ export function apply(ctx, config = {}) {
|
|
|
710
772
|
usage = sumSessionUsage(session, config);
|
|
711
773
|
detectedModel = detectModel(session);
|
|
712
774
|
}
|
|
775
|
+
// DSH 2.0.4 起 live session(sessions.get)可能拿不到 events,导致 usage 全 0。
|
|
776
|
+
// 此时回退从持久化读事件,避免客户端拿到"空但非 null"的 usage 而把金额显示成 ¥0.0000。
|
|
777
|
+
if (usage == null || usage.totalTokens === 0) {
|
|
778
|
+
try {
|
|
779
|
+
const persisted = await inspectPersistedSession(ctx, sessionParam);
|
|
780
|
+
if (Array.isArray(persisted?.events) && persisted.events.length > 0) {
|
|
781
|
+
const persistedUsage = sumEventsUsage(persisted.events, config, detectedModel);
|
|
782
|
+
if (persistedUsage.totalTokens > 0) {
|
|
783
|
+
usage = persistedUsage;
|
|
784
|
+
if (detectedModel === "") {
|
|
785
|
+
const switches = collectModelSwitches(persisted.events);
|
|
786
|
+
if (switches.length > 0) detectedModel = switches[switches.length - 1].model;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
} catch {
|
|
791
|
+
// 持久化读取不可用:忽略,走下方统一降级
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
// 仍无任何 token 就返回 null,让客户端回退本地投影估算(而不是当成 0 消耗显示 ¥0.0000)
|
|
795
|
+
if (usage != null && usage.totalTokens === 0) usage = null;
|
|
713
796
|
// 子代理枚举走持久化目录(不依赖 live 会话),即使父会话不在内存也能拿到
|
|
714
797
|
const subRes = await sumSubagentUsage(httpCtx, sessionParam, config);
|
|
715
798
|
subagentUsage = subRes.usage;
|
|
@@ -722,10 +805,10 @@ export function apply(ctx, config = {}) {
|
|
|
722
805
|
const effectiveModel = model || detectedModel;
|
|
723
806
|
// 逐模型标注 cost(高峰期×2、空闲期×1),客户端直接按各模型 cost 累加,无需自己定价。
|
|
724
807
|
if (usage && Array.isArray(usage.byModel)) {
|
|
725
|
-
usage.byModel = usage.byModel.map((b) => ({ ...b, cost: estimateCost(b, resolvePricing(config, b.model)) }));
|
|
808
|
+
usage.byModel = usage.byModel.map((b) => ({ ...b, cost: estimateCost(b, resolvePricing(config, b.model, b.era)) }));
|
|
726
809
|
}
|
|
727
810
|
if (subagentUsage && Array.isArray(subagentUsage.byModel)) {
|
|
728
|
-
subagentUsage.byModel = subagentUsage.byModel.map((b) => ({ ...b, cost: estimateCost(b, resolvePricing(config, b.model)) }));
|
|
811
|
+
subagentUsage.byModel = subagentUsage.byModel.map((b) => ({ ...b, cost: estimateCost(b, resolvePricing(config, b.model, b.era)) }));
|
|
729
812
|
}
|
|
730
813
|
try {
|
|
731
814
|
const data = await fetchBalance(apiKey);
|