codexmeter 1.0.26 → 1.0.27
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/bin/codexmeter.js +0 -38
- package/dist/assets/DailyUsage-DF-0V3BS.js +1 -0
- package/dist/assets/Models-CDwrE2OB.js +1 -0
- package/dist/assets/Overview-2lHDJwEq.js +2 -0
- package/dist/assets/OverviewVideoExport-C2TRW_hp.js +1 -0
- package/dist/assets/Repos-EbgAq5IT.js +1 -0
- package/dist/assets/Sessions-C2r1YNHt.js +4 -0
- package/dist/assets/animationsDefault-C_urdfl6.js +17 -0
- package/dist/assets/formatters-lxlIPan_.js +1 -0
- package/dist/assets/index-Bxd44dxc.js +10 -0
- package/dist/assets/subcharts-CA6MYRo3.js +1 -0
- package/dist/assets/useDailyStackPresentationTween-BWv0dKwE.js +3 -0
- package/dist/index.html +1 -1
- package/package.json +1 -1
- package/server/aggregator.js +1 -0
- package/server/cost-catalog.js +5 -2
- package/server/export-replay.js +2 -2
- package/server/index.js +2 -17
- package/server/ingest.js +50 -176
- package/server/live-state.js +16 -64
- package/server/normalize.js +9 -0
- package/server/pricing-fetch.js +7 -1
- package/server/rollout-reader.js +41 -19
- package/server/rollout-worker-pool.js +3 -71
- package/dist/assets/index-B2TwQuNm.js +0 -31
package/server/live-state.js
CHANGED
|
@@ -8,6 +8,7 @@ function normalizeEffortKey(effort) {
|
|
|
8
8
|
if (k === 'medium') return 'medium';
|
|
9
9
|
if (k === 'high') return 'high';
|
|
10
10
|
if (k === 'xhigh') return 'xhigh';
|
|
11
|
+
if (k === 'max') return 'max';
|
|
11
12
|
return k || 'unknown';
|
|
12
13
|
}
|
|
13
14
|
|
|
@@ -49,33 +50,20 @@ export function createLiveAggregateState(tz) {
|
|
|
49
50
|
};
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
export function
|
|
53
|
-
return {
|
|
54
|
-
overview: new Set(),
|
|
55
|
-
repos: { total: new Set(), d7: new Set(), d30: new Set() },
|
|
56
|
-
models: { total: new Set(), d7: new Set(), d30: new Set() },
|
|
57
|
-
families: { total: new Set(), d7: new Set(), d30: new Set() },
|
|
58
|
-
daily: new Set(),
|
|
59
|
-
heatmap: new Set(),
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export function applySessionToLiveState(live, session, patch) {
|
|
53
|
+
export function applySessionToLiveState(live, session) {
|
|
64
54
|
const rootId = session.root_thread_id || session.thread_id;
|
|
65
55
|
|
|
66
56
|
for (const rangeKey of ['total', 'd7', 'd30']) {
|
|
67
57
|
if (!overlapsLowerBound(session, live.lowerBounds[rangeKey])) continue;
|
|
68
58
|
|
|
69
59
|
applyOverviewBucket(live.overview[rangeKey], session, rootId);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
applyModelBucket(live.models[rangeKey], live.modelTopKeys[rangeKey], session, patch.models[rangeKey]);
|
|
74
|
-
applyFamilyBucket(live.families[rangeKey], live.familyTopKeys[rangeKey], session, patch.families[rangeKey]);
|
|
60
|
+
applyRepoBucket(live.repos[rangeKey], live.repoTopKeys[rangeKey], session);
|
|
61
|
+
applyModelBucket(live.models[rangeKey], live.modelTopKeys[rangeKey], session);
|
|
62
|
+
applyFamilyBucket(live.families[rangeKey], live.familyTopKeys[rangeKey], session);
|
|
75
63
|
}
|
|
76
64
|
|
|
77
|
-
applyDailyBucket(live.daily, session, live.tz, live.toDayKey
|
|
78
|
-
applyHeatmapBucket(live.heatmap, session, live.tz, live.toDayKey
|
|
65
|
+
applyDailyBucket(live.daily, session, live.tz, live.toDayKey);
|
|
66
|
+
applyHeatmapBucket(live.heatmap, session, live.tz, live.toDayKey);
|
|
79
67
|
}
|
|
80
68
|
|
|
81
69
|
export function buildLiveBootstrap(live) {
|
|
@@ -100,19 +88,6 @@ export function buildLiveSnapshot(live) {
|
|
|
100
88
|
};
|
|
101
89
|
}
|
|
102
90
|
|
|
103
|
-
export function buildLivePatch(live, patch) {
|
|
104
|
-
return {
|
|
105
|
-
overview: Object.fromEntries(
|
|
106
|
-
[...patch.overview].map((rangeKey) => [rangeKey, serializeOverviewBucket(live.overview[rangeKey], live.lowerBounds[rangeKey])])
|
|
107
|
-
),
|
|
108
|
-
repos: serializePatchedTopRanges(live.repos, live.repoTopKeys, patch.repos, serializeRepoSummary),
|
|
109
|
-
models: serializePatchedTopRanges(live.models, live.modelTopKeys, patch.models, serializeModelSummary),
|
|
110
|
-
families: serializePatchedTopRanges(live.families, live.familyTopKeys, patch.families, serializeFamilySummary),
|
|
111
|
-
daily: Object.fromEntries([...patch.daily].map((dayKey) => [dayKey, serializeDailyEntry(live.daily.get(dayKey))])),
|
|
112
|
-
heatmap: Object.fromEntries([...patch.heatmap].map((dayKey) => [dayKey, serializeHeatmapEntry(live.heatmap.get(dayKey))])),
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
91
|
function createTopKeyRanges() {
|
|
117
92
|
return {
|
|
118
93
|
total: [],
|
|
@@ -164,7 +139,7 @@ function applyOverviewBucket(bucket, session, rootId) {
|
|
|
164
139
|
if (session.ended_at && session.ended_at > bucket.latest) bucket.latest = session.ended_at;
|
|
165
140
|
}
|
|
166
141
|
|
|
167
|
-
function applyRepoBucket(repoMap, topKeys, session
|
|
142
|
+
function applyRepoBucket(repoMap, topKeys, session) {
|
|
168
143
|
const key = session.repo_label || 'unknown';
|
|
169
144
|
if (!repoMap.has(key)) {
|
|
170
145
|
repoMap.set(key, {
|
|
@@ -193,10 +168,9 @@ function applyRepoBucket(repoMap, topKeys, session, dirtySet) {
|
|
|
193
168
|
addBreakdown(repo.by_family, session.agent_family || 'generic', session);
|
|
194
169
|
|
|
195
170
|
updateTopKeys(repoMap, topKeys, key);
|
|
196
|
-
dirtySet.add(key);
|
|
197
171
|
}
|
|
198
172
|
|
|
199
|
-
function applyModelBucket(modelMap, topKeys, session
|
|
173
|
+
function applyModelBucket(modelMap, topKeys, session) {
|
|
200
174
|
const key = session.model_name || 'unknown';
|
|
201
175
|
if (!modelMap.has(key)) {
|
|
202
176
|
modelMap.set(key, { model_name: key, tokens: 0, cost: 0, cost_known: 0, exact_priced: 0, heuristic_priced: 0, sessions: 0, by_effort: {} });
|
|
@@ -213,7 +187,6 @@ function applyModelBucket(modelMap, topKeys, session, dirtySet) {
|
|
|
213
187
|
addBreakdown(model.by_effort, normalizeEffortKey(session.reasoning_effort), session);
|
|
214
188
|
|
|
215
189
|
updateTopKeys(modelMap, topKeys, key);
|
|
216
|
-
dirtySet.add(key);
|
|
217
190
|
}
|
|
218
191
|
|
|
219
192
|
function addBreakdown(target, key, session) {
|
|
@@ -228,7 +201,7 @@ function addBreakdown(target, key, session) {
|
|
|
228
201
|
}
|
|
229
202
|
}
|
|
230
203
|
|
|
231
|
-
function applyFamilyBucket(familyMap, topKeys, session
|
|
204
|
+
function applyFamilyBucket(familyMap, topKeys, session) {
|
|
232
205
|
const key = session.agent_family || 'generic';
|
|
233
206
|
if (!familyMap.has(key)) {
|
|
234
207
|
familyMap.set(key, { family: key, tokens: 0, cost: 0, exact_priced: 0, heuristic_priced: 0, sessions: 0 });
|
|
@@ -242,10 +215,9 @@ function applyFamilyBucket(familyMap, topKeys, session, dirtySet) {
|
|
|
242
215
|
if (session.cost_source === 'heuristic') family.heuristic_priced += 1;
|
|
243
216
|
}
|
|
244
217
|
updateTopKeys(familyMap, topKeys, key);
|
|
245
|
-
dirtySet.add(key);
|
|
246
218
|
}
|
|
247
219
|
|
|
248
|
-
function applyDailyBucket(dayMap, session, tz, toDayKey
|
|
220
|
+
function applyDailyBucket(dayMap, session, tz, toDayKey) {
|
|
249
221
|
if (!session.started_at || !session.ended_at) return;
|
|
250
222
|
|
|
251
223
|
const startMs = session.started_at * 1000;
|
|
@@ -253,12 +225,11 @@ function applyDailyBucket(dayMap, session, tz, toDayKey, dirtySet) {
|
|
|
253
225
|
const totalDur = endMs - startMs;
|
|
254
226
|
if (totalDur > 0) {
|
|
255
227
|
if (session.has_usage_by_day) {
|
|
256
|
-
addSessionPresence(dayMap, startMs, endMs, totalDur, tz, session
|
|
257
|
-
addUsageByDay(dayMap, session
|
|
228
|
+
addSessionPresence(dayMap, startMs, endMs, totalDur, tz, session);
|
|
229
|
+
addUsageByDay(dayMap, session);
|
|
258
230
|
} else {
|
|
259
231
|
for (const { dayKey, overlapMs } of splitIntervalByDay(startMs, endMs, tz)) {
|
|
260
232
|
addToDay(dayMap, dayKey, session, overlapMs / totalDur);
|
|
261
|
-
dirtySet.add(dayKey);
|
|
262
233
|
}
|
|
263
234
|
}
|
|
264
235
|
}
|
|
@@ -266,12 +237,11 @@ function applyDailyBucket(dayMap, session, tz, toDayKey, dirtySet) {
|
|
|
266
237
|
if (session.active_by_day) {
|
|
267
238
|
for (const [dayKey, seconds] of Object.entries(session.active_by_day)) {
|
|
268
239
|
addElapsedToDay(dayMap, dayKey, session, seconds || 0);
|
|
269
|
-
dirtySet.add(dayKey);
|
|
270
240
|
}
|
|
271
241
|
}
|
|
272
242
|
}
|
|
273
243
|
|
|
274
|
-
function applyHeatmapBucket(dayMap, session, tz, toDayKey
|
|
244
|
+
function applyHeatmapBucket(dayMap, session, tz, toDayKey) {
|
|
275
245
|
const startMs = session.started_at ? session.started_at * 1000 : null;
|
|
276
246
|
const endMs = (session.ended_at || session.started_at) ? (session.ended_at || session.started_at) * 1000 : null;
|
|
277
247
|
const totalDur = endMs - startMs;
|
|
@@ -282,20 +252,17 @@ function applyHeatmapBucket(dayMap, session, tz, toDayKey, dirtySet) {
|
|
|
282
252
|
const day = ensureHeatmapDay(dayMap, dayKey);
|
|
283
253
|
day.tokens += usageDay.tokens || 0;
|
|
284
254
|
if (usageDay.cost !== null) day.cost += usageDay.cost;
|
|
285
|
-
dirtySet.add(dayKey);
|
|
286
255
|
}
|
|
287
256
|
if (startMs !== null) {
|
|
288
257
|
if (totalDur > 0) {
|
|
289
258
|
for (const { dayKey, overlapMs } of splitIntervalByDay(startMs, endMs, tz)) {
|
|
290
259
|
const day = ensureHeatmapDay(dayMap, dayKey);
|
|
291
260
|
if ((overlapMs / totalDur) > 0.001) day.sessions += 1;
|
|
292
|
-
dirtySet.add(dayKey);
|
|
293
261
|
}
|
|
294
262
|
} else {
|
|
295
263
|
const startDay = toDayKey(startMs);
|
|
296
264
|
const day = ensureHeatmapDay(dayMap, startDay);
|
|
297
265
|
day.sessions += 1;
|
|
298
|
-
dirtySet.add(startDay);
|
|
299
266
|
}
|
|
300
267
|
}
|
|
301
268
|
} else if (startMs !== null) {
|
|
@@ -306,7 +273,6 @@ function applyHeatmapBucket(dayMap, session, tz, toDayKey, dirtySet) {
|
|
|
306
273
|
day.tokens += (session.tokens_used || 0) * fraction;
|
|
307
274
|
if (session.cost !== null) day.cost += session.cost * fraction;
|
|
308
275
|
if (fraction > 0.001) day.sessions += 1;
|
|
309
|
-
dirtySet.add(dayKey);
|
|
310
276
|
}
|
|
311
277
|
} else {
|
|
312
278
|
const dayKey = toDayKey(startMs);
|
|
@@ -314,7 +280,6 @@ function applyHeatmapBucket(dayMap, session, tz, toDayKey, dirtySet) {
|
|
|
314
280
|
day.tokens += session.tokens_used || 0;
|
|
315
281
|
if (session.cost !== null) day.cost += session.cost;
|
|
316
282
|
day.sessions += 1;
|
|
317
|
-
dirtySet.add(dayKey);
|
|
318
283
|
}
|
|
319
284
|
}
|
|
320
285
|
|
|
@@ -322,15 +287,13 @@ function applyHeatmapBucket(dayMap, session, tz, toDayKey, dirtySet) {
|
|
|
322
287
|
for (const [dayKey, seconds] of Object.entries(session.active_by_day)) {
|
|
323
288
|
const day = ensureHeatmapDay(dayMap, dayKey);
|
|
324
289
|
day.elapsed += seconds || 0;
|
|
325
|
-
dirtySet.add(dayKey);
|
|
326
290
|
}
|
|
327
291
|
}
|
|
328
292
|
}
|
|
329
293
|
|
|
330
|
-
function addSessionPresence(dayMap, startMs, endMs, totalDur, tz, session
|
|
294
|
+
function addSessionPresence(dayMap, startMs, endMs, totalDur, tz, session) {
|
|
331
295
|
for (const { dayKey, overlapMs } of splitIntervalByDay(startMs, endMs, tz)) {
|
|
332
296
|
addPresenceToDay(dayMap, dayKey, session, overlapMs / totalDur);
|
|
333
|
-
dirtySet.add(dayKey);
|
|
334
297
|
}
|
|
335
298
|
}
|
|
336
299
|
|
|
@@ -347,7 +310,7 @@ function addPresenceToDay(dayMap, dayKey, session, fraction) {
|
|
|
347
310
|
if (fraction > 0.001) day.by_repo[repoKey].sessions += 1;
|
|
348
311
|
}
|
|
349
312
|
|
|
350
|
-
function addUsageByDay(dayMap, session
|
|
313
|
+
function addUsageByDay(dayMap, session) {
|
|
351
314
|
for (const usageDay of session.usage_by_day || []) {
|
|
352
315
|
const dayKey = usageDay.day;
|
|
353
316
|
const day = ensureDay(dayMap, dayKey);
|
|
@@ -368,7 +331,6 @@ function addUsageByDay(dayMap, session, dirtySet) {
|
|
|
368
331
|
if (!day.by_repo[repoKey]) day.by_repo[repoKey] = { tokens: 0, cost: 0, elapsed_seconds: 0, sessions: 0 };
|
|
369
332
|
day.by_repo[repoKey].tokens += usageDay.tokens || 0;
|
|
370
333
|
if (usageDay.cost !== null) day.by_repo[repoKey].cost += usageDay.cost;
|
|
371
|
-
dirtySet.add(dayKey);
|
|
372
334
|
}
|
|
373
335
|
}
|
|
374
336
|
|
|
@@ -474,16 +436,6 @@ function serializeTopRanges(rangeMaps, topKeyRanges, projector) {
|
|
|
474
436
|
};
|
|
475
437
|
}
|
|
476
438
|
|
|
477
|
-
function serializePatchedTopRanges(rangeMaps, topKeyRanges, dirtyRanges, projector) {
|
|
478
|
-
const out = {};
|
|
479
|
-
for (const rangeKey of ['total', 'd7', 'd30']) {
|
|
480
|
-
if (dirtyRanges[rangeKey].size > 0) {
|
|
481
|
-
out[rangeKey] = serializeTopRange(rangeMaps[rangeKey], topKeyRanges[rangeKey], projector);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
return out;
|
|
485
|
-
}
|
|
486
|
-
|
|
487
439
|
function serializeTopRange(map, topKeys, projector) {
|
|
488
440
|
return topKeys
|
|
489
441
|
.map((key) => map.get(key))
|
package/server/normalize.js
CHANGED
|
@@ -124,6 +124,15 @@ function isLauncherSourceKind(sourceKind) {
|
|
|
124
124
|
|
|
125
125
|
const MODEL_ALIASES = {
|
|
126
126
|
'codex-mini-latest': 'o4-mini',
|
|
127
|
+
'gpt 5.6 sol': 'gpt-5.6-sol',
|
|
128
|
+
'gpt-5.6 sol': 'gpt-5.6-sol',
|
|
129
|
+
'openai/gpt-5.6-sol': 'gpt-5.6-sol',
|
|
130
|
+
'gpt 5.6 terra': 'gpt-5.6-terra',
|
|
131
|
+
'gpt-5.6 terra': 'gpt-5.6-terra',
|
|
132
|
+
'openai/gpt-5.6-terra': 'gpt-5.6-terra',
|
|
133
|
+
'gpt 5.6 luna': 'gpt-5.6-luna',
|
|
134
|
+
'gpt-5.6 luna': 'gpt-5.6-luna',
|
|
135
|
+
'openai/gpt-5.6-luna': 'gpt-5.6-luna',
|
|
127
136
|
'gpt 5.5': 'gpt-5.5',
|
|
128
137
|
'gpt-5.5': 'gpt-5.5',
|
|
129
138
|
'gpt 5 mini': 'gpt-5-mini',
|
package/server/pricing-fetch.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// Local pricing catalog for models used in codex-cli/codex app lifetime.
|
|
2
2
|
const FALLBACK = {
|
|
3
|
+
'gpt-5.6-sol': { input: 5.00, output: 30.00, cached_input: 0.50, cache_write: 6.25 },
|
|
4
|
+
'gpt-5.6-terra': { input: 2.50, output: 15.00, cached_input: 0.25, cache_write: 3.125 },
|
|
5
|
+
'gpt-5.6-luna': { input: 1.00, output: 6.00, cached_input: 0.10, cache_write: 1.25 },
|
|
3
6
|
'gpt-5.5': { input: 5.00, output: 30.00, cached_input: 0.50 },
|
|
4
7
|
'gpt-5.4': { input: 2.50, output: 15.00, cached_input: 0.25 },
|
|
5
8
|
'gpt-5-mini': { input: 0.75, output: 4.50, cached_input: 0.075},
|
|
@@ -29,7 +32,10 @@ const PRICING_URL = process.env.CODEXMETER_PRICING_URL || null;
|
|
|
29
32
|
function normalizeEntry(entry) {
|
|
30
33
|
if (!entry || typeof entry !== 'object' || entry.input == null || entry.output == null) return null;
|
|
31
34
|
const cached = entry.cached_input ?? entry.cachedInput ?? entry.input * 0.1;
|
|
32
|
-
|
|
35
|
+
const normalized = { input: entry.input, output: entry.output, cached_input: cached };
|
|
36
|
+
const cacheWrite = entry.cache_write ?? entry.cacheWrite ?? entry.cache_write_input;
|
|
37
|
+
if (cacheWrite != null) normalized.cache_write = cacheWrite;
|
|
38
|
+
return normalized;
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
function normalizePricing(raw) {
|
package/server/rollout-reader.js
CHANGED
|
@@ -65,6 +65,7 @@ export async function enrichFromRollout(rolloutPath, opts = {}) {
|
|
|
65
65
|
const usageByDay = new Map();
|
|
66
66
|
let lastInputTokens = 0;
|
|
67
67
|
let lastCachedInputTokens = 0;
|
|
68
|
+
let lastCacheWriteInputTokens = 0;
|
|
68
69
|
let lastOutputTokens = 0;
|
|
69
70
|
let lastReasoningOutputTokens = 0;
|
|
70
71
|
let lastTotalTokens = 0;
|
|
@@ -113,19 +114,21 @@ export async function enrichFromRollout(rolloutPath, opts = {}) {
|
|
|
113
114
|
if (obj.type === 'event_msg' && obj.payload?.type === 'token_count') {
|
|
114
115
|
const usage = obj.payload.info?.total_token_usage;
|
|
115
116
|
if (usage) {
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
const normalizedUsage = normalizeUsageTotals(usage);
|
|
118
|
+
const inputTokens = normalizedUsage.input_tokens;
|
|
119
|
+
const cachedInputTokens = normalizedUsage.cached_input_tokens;
|
|
120
|
+
const cacheWriteInputTokens = normalizedUsage.cache_write_input_tokens || 0;
|
|
121
|
+
const outputTokens = normalizedUsage.output_tokens;
|
|
122
|
+
const reasoningOutputTokens = normalizedUsage.reasoning_output_tokens;
|
|
123
|
+
const totalTokens = normalizedUsage.total_tokens;
|
|
124
|
+
|
|
125
|
+
result.usage_total = withCacheWrite({
|
|
123
126
|
input_tokens: inputTokens,
|
|
124
127
|
cached_input_tokens: cachedInputTokens,
|
|
125
128
|
output_tokens: outputTokens,
|
|
126
129
|
reasoning_output_tokens: reasoningOutputTokens,
|
|
127
130
|
total_tokens: totalTokens,
|
|
128
|
-
};
|
|
131
|
+
}, cacheWriteInputTokens);
|
|
129
132
|
|
|
130
133
|
const hasReset =
|
|
131
134
|
hasSeenUsage &&
|
|
@@ -138,30 +141,34 @@ export async function enrichFromRollout(rolloutPath, opts = {}) {
|
|
|
138
141
|
hasSeenUsage = false;
|
|
139
142
|
lastInputTokens = 0;
|
|
140
143
|
lastCachedInputTokens = 0;
|
|
144
|
+
lastCacheWriteInputTokens = 0;
|
|
141
145
|
lastOutputTokens = 0;
|
|
142
146
|
lastReasoningOutputTokens = 0;
|
|
143
147
|
lastTotalTokens = 0;
|
|
144
148
|
}
|
|
145
149
|
|
|
146
150
|
const usageDelta = hasSeenUsage
|
|
147
|
-
? {
|
|
151
|
+
? withCacheWrite({
|
|
148
152
|
input_tokens: Math.max(inputTokens - lastInputTokens, 0),
|
|
149
153
|
cached_input_tokens: Math.max(cachedInputTokens - lastCachedInputTokens, 0),
|
|
154
|
+
cache_write_input_tokens: Math.max(cacheWriteInputTokens - lastCacheWriteInputTokens, 0),
|
|
150
155
|
output_tokens: Math.max(outputTokens - lastOutputTokens, 0),
|
|
151
156
|
reasoning_output_tokens: Math.max(reasoningOutputTokens - lastReasoningOutputTokens, 0),
|
|
152
157
|
total_tokens: Math.max(totalTokens - lastTotalTokens, 0),
|
|
153
|
-
}
|
|
154
|
-
: {
|
|
158
|
+
}, Math.max(cacheWriteInputTokens - lastCacheWriteInputTokens, 0))
|
|
159
|
+
: withCacheWrite({
|
|
155
160
|
input_tokens: inputTokens,
|
|
156
161
|
cached_input_tokens: cachedInputTokens,
|
|
162
|
+
cache_write_input_tokens: cacheWriteInputTokens,
|
|
157
163
|
output_tokens: outputTokens,
|
|
158
164
|
reasoning_output_tokens: reasoningOutputTokens,
|
|
159
165
|
total_tokens: totalTokens,
|
|
160
|
-
};
|
|
166
|
+
}, cacheWriteInputTokens);
|
|
161
167
|
|
|
162
168
|
hasSeenUsage = true;
|
|
163
169
|
lastInputTokens = inputTokens;
|
|
164
170
|
lastCachedInputTokens = cachedInputTokens;
|
|
171
|
+
lastCacheWriteInputTokens = cacheWriteInputTokens;
|
|
165
172
|
lastOutputTokens = outputTokens;
|
|
166
173
|
lastReasoningOutputTokens = reasoningOutputTokens;
|
|
167
174
|
lastTotalTokens = totalTokens;
|
|
@@ -448,36 +455,39 @@ function isTokenCountEventLine(line) {
|
|
|
448
455
|
}
|
|
449
456
|
|
|
450
457
|
function normalizeUsageTotals(usage) {
|
|
451
|
-
return {
|
|
458
|
+
return withCacheWrite({
|
|
452
459
|
input_tokens: usage.input_tokens || 0,
|
|
453
|
-
cached_input_tokens: usage.cached_input_tokens
|
|
460
|
+
cached_input_tokens: usage.cached_input_tokens ?? usage.input_tokens_details?.cached_tokens ?? 0,
|
|
454
461
|
output_tokens: usage.output_tokens || 0,
|
|
455
462
|
reasoning_output_tokens: usage.reasoning_output_tokens || 0,
|
|
456
463
|
total_tokens: usage.total_tokens || 0,
|
|
457
|
-
};
|
|
464
|
+
}, usage.cache_write_input_tokens ?? usage.cache_write_tokens ?? usage.input_tokens_details?.cache_write_tokens ?? 0);
|
|
458
465
|
}
|
|
459
466
|
|
|
460
467
|
function splitUsageTotals(usage) {
|
|
461
468
|
const normalized = normalizeUsageTotals(usage || {});
|
|
462
469
|
const cachedInputTokens = Math.min(normalized.cached_input_tokens, normalized.input_tokens);
|
|
470
|
+
const cacheWriteInputTokens = Math.min(normalized.cache_write_input_tokens || 0, Math.max(normalized.input_tokens - cachedInputTokens, 0));
|
|
463
471
|
return {
|
|
464
|
-
uncached_input_tokens: Math.max(normalized.input_tokens - cachedInputTokens, 0),
|
|
472
|
+
uncached_input_tokens: Math.max(normalized.input_tokens - cachedInputTokens - cacheWriteInputTokens, 0),
|
|
465
473
|
cached_input_tokens: cachedInputTokens,
|
|
474
|
+
cache_write_input_tokens: cacheWriteInputTokens,
|
|
466
475
|
output_tokens: normalized.output_tokens,
|
|
467
476
|
reasoning_output_tokens: normalized.reasoning_output_tokens,
|
|
468
477
|
};
|
|
469
478
|
}
|
|
470
479
|
|
|
471
480
|
function combineUsageTotals(parts, currentTotalTokens = 0, previousTotalTokens = 0) {
|
|
472
|
-
const
|
|
481
|
+
const cacheWriteInputTokens = parts.cache_write_input_tokens || 0;
|
|
482
|
+
const inputTokens = (parts.uncached_input_tokens || 0) + (parts.cached_input_tokens || 0) + cacheWriteInputTokens;
|
|
473
483
|
const outputTokens = parts.output_tokens || 0;
|
|
474
|
-
return {
|
|
484
|
+
return withCacheWrite({
|
|
475
485
|
input_tokens: inputTokens,
|
|
476
486
|
cached_input_tokens: parts.cached_input_tokens || 0,
|
|
477
487
|
output_tokens: outputTokens,
|
|
478
488
|
reasoning_output_tokens: parts.reasoning_output_tokens || 0,
|
|
479
489
|
total_tokens: Math.max(inputTokens + outputTokens, Math.max(currentTotalTokens - previousTotalTokens, 0)),
|
|
480
|
-
};
|
|
490
|
+
}, cacheWriteInputTokens);
|
|
481
491
|
}
|
|
482
492
|
|
|
483
493
|
export function subtractUsageTotals(current, previous) {
|
|
@@ -486,6 +496,7 @@ export function subtractUsageTotals(current, previous) {
|
|
|
486
496
|
return combineUsageTotals({
|
|
487
497
|
uncached_input_tokens: Math.max(currentParts.uncached_input_tokens - previousParts.uncached_input_tokens, 0),
|
|
488
498
|
cached_input_tokens: Math.max(currentParts.cached_input_tokens - previousParts.cached_input_tokens, 0),
|
|
499
|
+
cache_write_input_tokens: Math.max(currentParts.cache_write_input_tokens - previousParts.cache_write_input_tokens, 0),
|
|
489
500
|
output_tokens: Math.max(currentParts.output_tokens - previousParts.output_tokens, 0),
|
|
490
501
|
reasoning_output_tokens: Math.max(currentParts.reasoning_output_tokens - previousParts.reasoning_output_tokens, 0),
|
|
491
502
|
}, current?.total_tokens || 0, previous?.total_tokens || 0);
|
|
@@ -495,6 +506,7 @@ export function hasUsageTotals(usage) {
|
|
|
495
506
|
return !!usage && (
|
|
496
507
|
(usage.input_tokens || 0) > 0 ||
|
|
497
508
|
(usage.cached_input_tokens || 0) > 0 ||
|
|
509
|
+
(usage.cache_write_input_tokens || 0) > 0 ||
|
|
498
510
|
(usage.output_tokens || 0) > 0 ||
|
|
499
511
|
(usage.reasoning_output_tokens || 0) > 0 ||
|
|
500
512
|
(usage.total_tokens || 0) > 0
|
|
@@ -505,6 +517,7 @@ function hasUsage(usage) {
|
|
|
505
517
|
return !!usage && (
|
|
506
518
|
usage.input_tokens > 0 ||
|
|
507
519
|
usage.cached_input_tokens > 0 ||
|
|
520
|
+
(usage.cache_write_input_tokens || 0) > 0 ||
|
|
508
521
|
usage.output_tokens > 0
|
|
509
522
|
);
|
|
510
523
|
}
|
|
@@ -521,5 +534,14 @@ function mergeUsageTotals(target, dayKey, usage) {
|
|
|
521
534
|
}
|
|
522
535
|
previous.input_tokens += usage.input_tokens || 0;
|
|
523
536
|
previous.cached_input_tokens += usage.cached_input_tokens || 0;
|
|
537
|
+
if (usage.cache_write_input_tokens) {
|
|
538
|
+
previous.cache_write_input_tokens = (previous.cache_write_input_tokens || 0) + usage.cache_write_input_tokens;
|
|
539
|
+
}
|
|
524
540
|
previous.output_tokens += usage.output_tokens || 0;
|
|
525
541
|
}
|
|
542
|
+
|
|
543
|
+
function withCacheWrite(usage, cacheWriteInputTokens) {
|
|
544
|
+
const value = Math.max(0, cacheWriteInputTokens || 0);
|
|
545
|
+
if (value > 0) usage.cache_write_input_tokens = value;
|
|
546
|
+
return usage;
|
|
547
|
+
}
|
|
@@ -2,11 +2,8 @@ import os from 'os';
|
|
|
2
2
|
import { Worker } from 'worker_threads';
|
|
3
3
|
|
|
4
4
|
export function createRolloutWorkerPool(opts = {}) {
|
|
5
|
-
const size = normalizePoolSize(
|
|
5
|
+
const size = normalizePoolSize();
|
|
6
6
|
const readerOptions = opts.readerOptions || {};
|
|
7
|
-
if (size <= 1) {
|
|
8
|
-
return createInlinePool({ readerOptions });
|
|
9
|
-
}
|
|
10
7
|
|
|
11
8
|
const workers = new Set();
|
|
12
9
|
const idleWorkers = [];
|
|
@@ -105,10 +102,6 @@ export function createRolloutWorkerPool(opts = {}) {
|
|
|
105
102
|
});
|
|
106
103
|
}
|
|
107
104
|
|
|
108
|
-
async function mapRollouts(rolloutPaths, timezone) {
|
|
109
|
-
return Promise.all(rolloutPaths.map((rolloutPath) => runTask(rolloutPath, timezone)));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
105
|
async function mapRolloutsInChunks(rolloutPaths, timezone, { chunkSize = 100, onChunk } = {}) {
|
|
113
106
|
const results = new Array(rolloutPaths.length);
|
|
114
107
|
const completed = new Array(rolloutPaths.length).fill(false);
|
|
@@ -188,71 +181,10 @@ export function createRolloutWorkerPool(opts = {}) {
|
|
|
188
181
|
idleWorkers.length = 0;
|
|
189
182
|
}
|
|
190
183
|
|
|
191
|
-
return {
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function createInlinePool({ readerOptions = {} } = {}) {
|
|
195
|
-
return {
|
|
196
|
-
size: 1,
|
|
197
|
-
async mapRollouts(rolloutPaths, timezone) {
|
|
198
|
-
const { enrichFromRollout } = await import('./rollout-reader.js');
|
|
199
|
-
return Promise.all(
|
|
200
|
-
rolloutPaths.map(async (rolloutPath) => {
|
|
201
|
-
try {
|
|
202
|
-
const data = await enrichFromRollout(rolloutPath, { timezone, ...readerOptions });
|
|
203
|
-
return { ok: true, data, error: null };
|
|
204
|
-
} catch (error) {
|
|
205
|
-
return {
|
|
206
|
-
ok: false,
|
|
207
|
-
data: null,
|
|
208
|
-
error: error instanceof Error ? error.message : String(error),
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
})
|
|
212
|
-
);
|
|
213
|
-
},
|
|
214
|
-
async mapRolloutsInChunks(rolloutPaths, timezone, { chunkSize = 100, onChunk } = {}) {
|
|
215
|
-
const { enrichFromRollout } = await import('./rollout-reader.js');
|
|
216
|
-
const results = [];
|
|
217
|
-
const completed = [];
|
|
218
|
-
const safeChunkSize = Math.max(1, Number(chunkSize) || 1);
|
|
219
|
-
|
|
220
|
-
for (let index = 0; index < rolloutPaths.length; index += 1) {
|
|
221
|
-
try {
|
|
222
|
-
const data = await enrichFromRollout(rolloutPaths[index], { timezone, ...readerOptions });
|
|
223
|
-
const result = { ok: true, data, error: null };
|
|
224
|
-
results[index] = result;
|
|
225
|
-
completed.push({ index, result });
|
|
226
|
-
} catch (error) {
|
|
227
|
-
const result = {
|
|
228
|
-
ok: false,
|
|
229
|
-
data: null,
|
|
230
|
-
error: error instanceof Error ? error.message : String(error),
|
|
231
|
-
};
|
|
232
|
-
results[index] = result;
|
|
233
|
-
completed.push({ index, result });
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
if (typeof onChunk === 'function' && completed.length >= safeChunkSize) {
|
|
237
|
-
await onChunk(completed.splice(0, completed.length));
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
if (typeof onChunk === 'function' && completed.length) {
|
|
242
|
-
await onChunk(completed.splice(0, completed.length));
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return results;
|
|
246
|
-
},
|
|
247
|
-
async close() {},
|
|
248
|
-
};
|
|
184
|
+
return { mapRolloutsInChunks, close, size };
|
|
249
185
|
}
|
|
250
186
|
|
|
251
|
-
function normalizePoolSize(
|
|
252
|
-
if (size != null) {
|
|
253
|
-
return Math.max(1, Number(size) || 1);
|
|
254
|
-
}
|
|
255
|
-
|
|
187
|
+
function normalizePoolSize() {
|
|
256
188
|
const cpuCount = os.cpus()?.length || 4;
|
|
257
189
|
return Math.max(2, Math.min(cpuCount - 1, 8));
|
|
258
190
|
}
|