tokentracker-cli 0.98.0 → 1.0.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/README.de.md +6 -5
- package/README.ja.md +6 -5
- package/README.ko.md +6 -5
- package/README.md +7 -5
- package/README.zh-CN.md +6 -5
- package/dashboard/dist/assets/{AchievementBadge-DlZklawB.js → AchievementBadge-Dpf3VBcS.js} +1 -1
- package/dashboard/dist/assets/{AchievementsPage-CprbOKnt.js → AchievementsPage-BSGbLI5V.js} +2 -2
- package/dashboard/dist/assets/{AchievementsSection-BKLJBRKw.js → AchievementsSection-C10HXoq1.js} +1 -1
- package/dashboard/dist/assets/{BadgeDetailModal-CfVXLh_t.js → BadgeDetailModal-DajrEc3e.js} +1 -1
- package/dashboard/dist/assets/{BotAnimated-wzqeGbEY.js → BotAnimated-BpbTCJML.js} +1 -1
- package/dashboard/dist/assets/{Card-B-gcOAn7.js → Card-CDCj927L.js} +1 -1
- package/dashboard/dist/assets/{ClawdAnimated-1c5ezBBN.js → ClawdAnimated-CiTkm04x.js} +2 -2
- package/dashboard/dist/assets/{CommandPalette-CD8ggEF6.js → CommandPalette-CYHUUX7M.js} +1 -1
- package/dashboard/dist/assets/{CommunityStatsModal-b2fPhi1E.js → CommunityStatsModal-BHZehk5P.js} +1 -1
- package/dashboard/dist/assets/{ConfirmModal-ARBkT8AC.js → ConfirmModal-DZlT_xe_.js} +1 -1
- package/dashboard/dist/assets/{Controls-WFXDjIYG.js → Controls-Bcijikco.js} +1 -1
- package/dashboard/dist/assets/{DashboardPage-XtiZa2JN.js → DashboardPage-yREqx-tx.js} +1 -1
- package/dashboard/dist/assets/DevicePage-DzisRlts.js +1 -0
- package/dashboard/dist/assets/{DialogClose-D2PE0TAc.js → DialogClose-CQ9hFUNv.js} +1 -1
- package/dashboard/dist/assets/DialogDescription-CLXWH3lG.js +1 -0
- package/dashboard/dist/assets/{DialogTitle-Bby0zSCw.js → DialogTitle--bItxq3K.js} +1 -1
- package/dashboard/dist/assets/{FadeIn-Cx1hl4so.js → FadeIn-v5sAHZBu.js} +1 -1
- package/dashboard/dist/assets/{HeaderGithubStar-my4zR6Pp.js → HeaderGithubStar-Bg8R51fN.js} +1 -1
- package/dashboard/dist/assets/{HoverTooltip-DOiGAPLE.js → HoverTooltip-DToNFKkJ.js} +1 -1
- package/dashboard/dist/assets/{IpCheckPage-4SdHlFuA.js → IpCheckPage-BmFXYXZm.js} +1 -1
- package/dashboard/dist/assets/{LandingPage-bSWc9xiS.js → LandingPage-COpRacX0.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardAvatar-EN4117E5.js → LeaderboardAvatar-D0l8_L_d.js} +1 -1
- package/dashboard/dist/assets/{LeaderboardPage-Bwjf-xZl.js → LeaderboardPage-BJHS44Fq.js} +2 -2
- package/dashboard/dist/assets/{LeaderboardProfileModal-B1mgVtfP.js → LeaderboardProfileModal-C1K9rSGn.js} +3 -3
- package/dashboard/dist/assets/{LeaderboardProfilePage-DVt5S3lY.js → LeaderboardProfilePage-CvR48D_y.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-BCXjJE1B.js → LimitsPage-BN79yMkb.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-CzeH4OoJ.js → LocalOnlyNotice-BwTJpkDW.js} +1 -1
- package/dashboard/dist/assets/{LoginCard-aiqSQInP.js → LoginCard-BMXAg1rZ.js} +1 -1
- package/dashboard/dist/assets/{LoginModal-DdGKIi8h.js → LoginModal-Bf0F_UPK.js} +1 -1
- package/dashboard/dist/assets/{LoginPage-D7xGSoMn.js → LoginPage-BylH2waa.js} +1 -1
- package/dashboard/dist/assets/{PetPage-Dlb6rNpF.js → PetPage-X4v_J-Uj.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-Xbruj7Zi.js → PopoverPopup-yEQVU5C_.js} +1 -1
- package/dashboard/dist/assets/{ProviderIcon-BPxacBzj.js → ProviderIcon-KRXIghW0.js} +1 -1
- package/dashboard/dist/assets/{ResetPasswordPage-9wgPnLRj.js → ResetPasswordPage-DQQF2rsl.js} +1 -1
- package/dashboard/dist/assets/{SegmentedControl-CYFcZFNE.js → SegmentedControl-VkVwcoXk.js} +1 -1
- package/dashboard/dist/assets/{Select-zpFEMqpk.js → Select-CxW2C8OG.js} +1 -1
- package/dashboard/dist/assets/{SelectItemText-Bldm5Y0C.js → SelectItemText-D6vhaI0Q.js} +1 -1
- package/dashboard/dist/assets/{ServiceStatusPage-dgtwFazV.js → ServiceStatusPage-CCKqUZhI.js} +1 -1
- package/dashboard/dist/assets/{SessionsPage-DHfpv53M.js → SessionsPage-Huqt77Qt.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-DcGToOT7.js → SettingsPage-CpX8GYce.js} +2 -2
- package/dashboard/dist/assets/{SkillsPage-DiQDLw2l.js → SkillsPage-S1dtu_Cd.js} +1 -1
- package/dashboard/dist/assets/{TokenGalaxy-GDx7ftMU.js → TokenGalaxy-BayM0FOG.js} +161 -161
- package/dashboard/dist/assets/{ToolbarRootContext-PoCcP2AV.js → ToolbarRootContext-B1KG8B5q.js} +1 -1
- package/dashboard/dist/assets/{TrendMonitor-CIfq_yk8.js → TrendMonitor-DYqz9nLw.js} +1 -1
- package/dashboard/dist/assets/{WidgetsPage-Dwy-E3Lf.js → WidgetsPage-WuzVeCBG.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-Ce7NV6Ij.js → WrappedPage-Bpn9GYHz.js} +1 -1
- package/dashboard/dist/assets/{__vite-browser-external-D-WLjvZW.js → __vite-browser-external-B1XxVtUQ.js} +3 -3
- package/dashboard/dist/assets/{agent-logos-BY8n7b9g.js → agent-logos-DVE956SY.js} +1 -1
- package/dashboard/dist/assets/{arrow-up-right-BDiV6Wlk.js → arrow-up-right-B7aZy0wJ.js} +1 -1
- package/dashboard/dist/assets/{check-Chyo6ujc.js → check-55YI8PGo.js} +1 -1
- package/dashboard/dist/assets/{composite-DgBqMs_Z.js → composite--O2XKSlW.js} +1 -1
- package/dashboard/dist/assets/{copy-ENRv4pAy.js → copy-DDVFmqF5.js} +1 -1
- package/dashboard/dist/assets/{download-D3y3gd-z.js → download-DmIFrL1S.js} +1 -1
- package/dashboard/dist/assets/{external-link-VxSAxWnH.js → external-link-DwG_AAOo.js} +1 -1
- package/dashboard/dist/assets/{flame-CPdTWeRo.js → flame-ANwvantJ.js} +1 -1
- package/dashboard/dist/assets/{format-tokens-BF2G89Q7.js → format-tokens-BzZ_DfX2.js} +1 -1
- package/dashboard/dist/assets/{icons-D6rfdVyV.js → icons-CdyVgzlu.js} +1 -1
- package/dashboard/dist/assets/{index-mzMCUT_4.js → index-B-1uJU_R.js} +1 -1
- package/dashboard/dist/assets/{index-CgmpxGym.js → index-D0VhCSlC.js} +1 -1
- package/dashboard/dist/assets/{index-4bHcp2Ep.js → index-D8g1p_Ee.js} +1 -1
- package/dashboard/dist/assets/{info-BMso9PcY.js → info-n2XpcOlm.js} +1 -1
- package/dashboard/dist/assets/{link-2-Bg34XOcd.js → link-2-Bq9GUX22.js} +1 -1
- package/dashboard/dist/assets/{loader-circle-C4Vul1Yx.js → loader-circle-fkYFxXuJ.js} +1 -1
- package/dashboard/dist/assets/{maximize-2-BBqcwFFo.js → maximize-2-C5v08iPD.js} +1 -1
- package/dashboard/dist/assets/{modifiers.esm-B2F4Iiey.js → modifiers.esm-AG_bzlWZ.js} +1 -1
- package/dashboard/dist/assets/{monitor-smartphone-k1Ja3ngF.js → monitor-smartphone-BEjlJhuw.js} +1 -1
- package/dashboard/dist/assets/{plus-BaCIejxc.js → plus-BfdtMxnD.js} +1 -1
- package/dashboard/dist/assets/{provider-display-BYxmO9Bu.js → provider-display-BvjuoQty.js} +2 -2
- package/dashboard/dist/assets/{react-xGx5RIqz.js → react-f77D-LSn.js} +1 -1
- package/dashboard/dist/assets/{search-LZQC7j6a.js → search-Dmczow8i.js} +1 -1
- package/dashboard/dist/assets/{skills-api-yLfQkApo.js → skills-api-BuqPQei_.js} +1 -1
- package/dashboard/dist/assets/{sortable.esm-B8XRHrmA.js → sortable.esm-CDAVQaFQ.js} +1 -1
- package/dashboard/dist/assets/{store-DfUIKvz3.js → store-C2inKcM-.js} +1 -1
- package/dashboard/dist/assets/{terminal-CsFh6eZS.js → terminal-C1O422UZ.js} +1 -1
- package/dashboard/dist/assets/{trash-CVk10mDa.js → trash-CXOKPZfQ.js} +1 -1
- package/dashboard/dist/assets/{use-community-stats-DP7lJyDV.js → use-community-stats-CKfyxLvl.js} +1 -1
- package/dashboard/dist/assets/{use-latest-request-guard-B7uLArxV.js → use-latest-request-guard-C3rQR4BN.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-DG97cWcv.js → use-limits-display-prefs-Ic4GUPRY.js} +1 -1
- package/dashboard/dist/assets/{use-ordered-list-ChQc2v-Q.js → use-ordered-list-Cvbz_wcj.js} +1 -1
- package/dashboard/dist/assets/use-reduced-motion-DewlF5tk.js +1 -0
- package/dashboard/dist/assets/{use-session-efficiency-pref-CtF9JAlF.js → use-session-efficiency-pref-u_qW99gD.js} +1 -1
- package/dashboard/dist/assets/{use-transform-DVIBk7FO.js → use-transform-CVGiBxJK.js} +1 -1
- package/dashboard/dist/assets/{use-usage-limits-BYQuJ9Ud.js → use-usage-limits-MPu696v0.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-CNkzJCP9.js → useCurrency-BOBz18If.js} +1 -1
- package/dashboard/dist/assets/{useTokenFormat-CU3MtXtS.js → useTokenFormat-BooCzJJZ.js} +1 -1
- package/dashboard/dist/assets/{zap-D5k595AR.js → zap-M_x9dAwo.js} +1 -1
- package/dashboard/dist/index.html +13 -11
- package/dashboard/dist/ip-check.html +1 -1
- package/dashboard/dist/leaderboard.html +1 -1
- package/dashboard/dist/llms.txt +3 -2
- package/dashboard/dist/share.html +1 -1
- package/package.json +2 -2
- package/src/commands/init.js +10 -0
- package/src/commands/status.js +13 -0
- package/src/commands/sync.js +33 -0
- package/src/lib/cloud-account.js +12 -1
- package/src/lib/heatmap-compact.js +114 -0
- package/src/lib/local-api.js +11 -10
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/src/lib/rollout.js +148 -20
- package/src/lib/skills-manager.js +2 -2
- package/dashboard/dist/assets/DevicePage-BCwSpNXa.js +0 -1
- package/dashboard/dist/assets/DialogDescription-5LunWukT.js +0 -1
- package/dashboard/dist/assets/use-reduced-motion-BoFF1l9U.js +0 -1
package/src/lib/cloud-account.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
// schema exactly, so the popover renders the cloud payload unchanged.
|
|
12
12
|
|
|
13
13
|
const { DEFAULT_BASE_URL, DEFAULT_ANON_KEY } = require("./runtime-config");
|
|
14
|
+
const { expandHeatmapCompact } = require("./heatmap-compact");
|
|
14
15
|
|
|
15
16
|
// usage-* (local CLI) → account-* (cloud) slug map. Only these have a
|
|
16
17
|
// cross-device cloud equivalent; project-usage / usage-limits / category
|
|
@@ -28,6 +29,14 @@ function accountSlugFor(usageSlug) {
|
|
|
28
29
|
return USAGE_TO_ACCOUNT_SLUG[usageSlug] || null;
|
|
29
30
|
}
|
|
30
31
|
|
|
32
|
+
// The heatmap renders a 52-week grid out of a handful of active days, so the
|
|
33
|
+
// dense form is mostly zero-filled cells and repeated keys: ~42 KB on the wire
|
|
34
|
+
// for ~12 KB of data, and this proxy accounts for 94% of those reads. Asking
|
|
35
|
+
// for the sparse rows and rebuilding the grid in `expandHeatmapCompact` gives
|
|
36
|
+
// the caller a byte-identical payload. An edge that predates the compact branch
|
|
37
|
+
// ignores the param and answers densely, which that helper passes through.
|
|
38
|
+
const HEATMAP_ACCOUNT_SLUG = "tokentracker-account-heatmap";
|
|
39
|
+
|
|
31
40
|
// Mirror of dashboard/src/contexts/InsforgeAuthContext.jsx
|
|
32
41
|
// `accessTokenFromRefreshPayload`: the refresh response may put the token at the
|
|
33
42
|
// top level or nested under `session`, in camelCase or snake_case.
|
|
@@ -340,6 +349,7 @@ async function fetchAccountFunction({
|
|
|
340
349
|
if (value != null && value !== "") url.searchParams.set(key, String(value));
|
|
341
350
|
}
|
|
342
351
|
}
|
|
352
|
+
if (slug === HEATMAP_ACCOUNT_SLUG) url.searchParams.set("format", "compact");
|
|
343
353
|
const headers = { Accept: "application/json", Authorization: `Bearer ${accessToken}` };
|
|
344
354
|
if (anonKey) headers.apikey = anonKey;
|
|
345
355
|
|
|
@@ -361,7 +371,7 @@ async function fetchAccountFunction({
|
|
|
361
371
|
throw err;
|
|
362
372
|
}
|
|
363
373
|
const data = await res.json();
|
|
364
|
-
return data;
|
|
374
|
+
return slug === HEATMAP_ACCOUNT_SLUG ? expandHeatmapCompact(data) : data;
|
|
365
375
|
} finally {
|
|
366
376
|
if (timeoutId) {
|
|
367
377
|
clearTimeout(timeoutId);
|
|
@@ -450,6 +460,7 @@ module.exports = {
|
|
|
450
460
|
AccountAuthError,
|
|
451
461
|
USAGE_TO_ACCOUNT_SLUG,
|
|
452
462
|
PAYLOAD_TTL_MS,
|
|
463
|
+
HEATMAP_ACCOUNT_SLUG,
|
|
453
464
|
accountSlugFor,
|
|
454
465
|
accessTokenFromRefreshPayload,
|
|
455
466
|
refreshTokenFromRefreshPayload,
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// Expand the `format=compact` activity-heatmap payload back into the schema the
|
|
4
|
+
// dashboard, popover and widget already render.
|
|
5
|
+
//
|
|
6
|
+
// The heatmap is the single largest cloud read we serve: 2.55 GB/day on
|
|
7
|
+
// 2026-09-21, 31% of all egress, and 94% of it proxied through this CLI. The
|
|
8
|
+
// bytes were never the data — `account_heatmap_compact` hands the edge ~12 KB
|
|
9
|
+
// of sparse rows, and the edge blew that up to ~42 KB by materialising a
|
|
10
|
+
// 52-week × 7-day grid of five-key objects, where `billable_total_tokens`
|
|
11
|
+
// simply repeats `total_tokens` and `level` is derived from the window maximum.
|
|
12
|
+
//
|
|
13
|
+
// So the grid gets rebuilt here instead, one hop later, off the same sparse
|
|
14
|
+
// rows. The rendered payload is identical; it just stops crossing the network.
|
|
15
|
+
//
|
|
16
|
+
// Model names are the other half of it. A year of daily rows repeats the same
|
|
17
|
+
// ~160 names ~1900 times, which was 57% of even the sparse payload, so they are
|
|
18
|
+
// sent once in `model_names` and referenced by index from flat
|
|
19
|
+
// [index, tokens, index, tokens, ...] pairs.
|
|
20
|
+
//
|
|
21
|
+
// This mirrors the tail of dashboard/edge-patches/tokentracker-account-heatmap.ts
|
|
22
|
+
// step for step, including the details that look incidental but are not:
|
|
23
|
+
// coercion through Number(), last-row-wins on a repeated day, and `{}` (not
|
|
24
|
+
// null) for a present row whose models map is empty.
|
|
25
|
+
|
|
26
|
+
/** Next YYYY-MM-DD, treating the key as a UTC calendar day (same as the edge). */
|
|
27
|
+
function nextDayKey(dayKey) {
|
|
28
|
+
const d = new Date(`${dayKey}T00:00:00Z`);
|
|
29
|
+
d.setUTCDate(d.getUTCDate() + 1);
|
|
30
|
+
return d.toISOString().slice(0, 10);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const DAY_KEY_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
34
|
+
|
|
35
|
+
// 104 weeks is the edge's own `weeks` clamp; the guard is here so a malformed
|
|
36
|
+
// `to` cannot spin the grid loop forever.
|
|
37
|
+
const MAX_GRID_DAYS = 104 * 7 + 7;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* @param {unknown} payload a `tokentracker-account-heatmap` response.
|
|
41
|
+
* @returns {unknown} the dense payload, or `payload` itself when it is not a
|
|
42
|
+
* well-formed compact response — which is also how a new CLI stays correct
|
|
43
|
+
* against an edge that predates the compact branch.
|
|
44
|
+
*/
|
|
45
|
+
function expandHeatmapCompact(payload) {
|
|
46
|
+
if (!payload || typeof payload !== "object") return payload;
|
|
47
|
+
if (payload.format !== "compact") return payload;
|
|
48
|
+
if (!Array.isArray(payload.days)) return payload;
|
|
49
|
+
// Without the dictionary the indices resolve to nothing and every model
|
|
50
|
+
// breakdown would render empty, so refuse the payload instead of silently
|
|
51
|
+
// dropping data.
|
|
52
|
+
if (!Array.isArray(payload.model_names)) return payload;
|
|
53
|
+
if (!DAY_KEY_RE.test(String(payload.from)) || !DAY_KEY_RE.test(String(payload.to))) return payload;
|
|
54
|
+
|
|
55
|
+
const names = payload.model_names;
|
|
56
|
+
const byDay = new Map();
|
|
57
|
+
for (const row of payload.days) {
|
|
58
|
+
if (!Array.isArray(row)) continue;
|
|
59
|
+
const [day, total, pairs] = row;
|
|
60
|
+
if (!DAY_KEY_RE.test(String(day))) continue;
|
|
61
|
+
const mdl = {};
|
|
62
|
+
if (Array.isArray(pairs)) {
|
|
63
|
+
// `i + 1 < length` drops a trailing index with no value rather than
|
|
64
|
+
// recording it as 0.
|
|
65
|
+
for (let i = 0; i + 1 < pairs.length; i += 2) {
|
|
66
|
+
const name = names[pairs[i]];
|
|
67
|
+
if (typeof name === "string") mdl[name] = Number(pairs[i + 1]) || 0;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Last row wins, as Map.set did on the edge.
|
|
71
|
+
byDay.set(String(day), { total: Number(total) || 0, models: mdl });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const maxValue = Number(payload.max_value) || 0;
|
|
75
|
+
const levelOf = (v) => {
|
|
76
|
+
if (v <= 0) return 0;
|
|
77
|
+
if (maxValue === 0) return 1;
|
|
78
|
+
const r = v / maxValue;
|
|
79
|
+
if (r <= 0.25) return 1;
|
|
80
|
+
if (r <= 0.5) return 2;
|
|
81
|
+
if (r <= 0.75) return 3;
|
|
82
|
+
return 4;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const cells = [];
|
|
86
|
+
const to = String(payload.to);
|
|
87
|
+
let cursor = String(payload.from);
|
|
88
|
+
while (cursor <= to && cells.length < MAX_GRID_DAYS) {
|
|
89
|
+
const hit = byDay.get(cursor);
|
|
90
|
+
const total = hit ? hit.total : 0;
|
|
91
|
+
cells.push({
|
|
92
|
+
day: cursor,
|
|
93
|
+
total_tokens: total,
|
|
94
|
+
billable_total_tokens: total,
|
|
95
|
+
level: levelOf(total),
|
|
96
|
+
models: hit ? hit.models : null,
|
|
97
|
+
});
|
|
98
|
+
cursor = nextDayKey(cursor);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const weeks = [];
|
|
102
|
+
for (let i = 0; i < cells.length; i += 7) weeks.push(cells.slice(i, i + 7));
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
from: payload.from,
|
|
106
|
+
to: payload.to,
|
|
107
|
+
week_starts_on: payload.week_starts_on,
|
|
108
|
+
active_days: payload.active_days,
|
|
109
|
+
streak_days: payload.streak_days,
|
|
110
|
+
weeks,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
module.exports = { expandHeatmapCompact };
|
package/src/lib/local-api.js
CHANGED
|
@@ -2380,20 +2380,21 @@ function createLocalApiHandler({ queuePath }) {
|
|
|
2380
2380
|
ma.totals.cached_input_tokens += row.cached_input_tokens || 0;
|
|
2381
2381
|
ma.totals.cache_creation_input_tokens += row.cache_creation_input_tokens || 0;
|
|
2382
2382
|
ma.totals.reasoning_output_tokens += row.reasoning_output_tokens || 0;
|
|
2383
|
-
|
|
2384
|
-
|
|
2383
|
+
// Price each row, exactly as aggregateByDay does for the hero (#653).
|
|
2384
|
+
// computeRowCost is not linear for models with a Fast tier or
|
|
2385
|
+
// long-context rates: priority_* / long_context_* mark a subset of the
|
|
2386
|
+
// base columns rather than adding volume, so they survive only while the
|
|
2387
|
+
// row is intact. Summing the base columns first and pricing once silently
|
|
2388
|
+
// dropped every premium — same tokens, fewer dollars than the hero.
|
|
2389
|
+
ma.totals.total_cost_usd = Number(ma.totals.total_cost_usd || 0) + computeRowCost(row);
|
|
2385
2390
|
}
|
|
2386
2391
|
|
|
2387
2392
|
const sources = Array.from(bySource.values()).map((s) => {
|
|
2388
2393
|
s.models = Array.from(s.models.values())
|
|
2389
|
-
.map((m) => {
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
source: s.source,
|
|
2394
|
-
});
|
|
2395
|
-
return { ...m, totals: { ...m.totals, total_cost_usd: cost.toFixed(6) } };
|
|
2396
|
-
})
|
|
2394
|
+
.map((m) => ({
|
|
2395
|
+
...m,
|
|
2396
|
+
totals: { ...m.totals, total_cost_usd: Number(m.totals.total_cost_usd || 0).toFixed(6) },
|
|
2397
|
+
}))
|
|
2397
2398
|
.sort((a, b) => b.totals.total_tokens - a.totals.total_tokens);
|
|
2398
2399
|
const sourceCost = s.models.reduce((sum, m) => sum + Number(m.totals.total_cost_usd), 0);
|
|
2399
2400
|
s.totals.total_cost_usd = sourceCost.toFixed(6);
|