tokenmaxx 0.0.3 → 0.0.4
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/dist/index.js +70 -43
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -14800,6 +14800,7 @@ var init_domain = __esm(() => {
|
|
|
14800
14800
|
TokenEventSchema = exports_external.object({
|
|
14801
14801
|
accountId: exports_external.uuid().nullable(),
|
|
14802
14802
|
at: exports_external.number().int().nonnegative(),
|
|
14803
|
+
cacheReadTokens: exports_external.number().int().nonnegative(),
|
|
14803
14804
|
inputTokens: exports_external.number().int().nonnegative(),
|
|
14804
14805
|
model: exports_external.string().min(1).nullable(),
|
|
14805
14806
|
outputTokens: exports_external.number().int().nonnegative(),
|
|
@@ -14814,6 +14815,7 @@ var init_domain = __esm(() => {
|
|
|
14814
14815
|
key: exports_external.string(),
|
|
14815
14816
|
peakPerHour: exports_external.number().nonnegative(),
|
|
14816
14817
|
topModel: exports_external.string().nullable(),
|
|
14818
|
+
totalCached: exports_external.number().nonnegative(),
|
|
14817
14819
|
totalInput: exports_external.number().nonnegative(),
|
|
14818
14820
|
totalOutput: exports_external.number().nonnegative(),
|
|
14819
14821
|
totalTokens: exports_external.number().nonnegative()
|
|
@@ -15087,21 +15089,21 @@ function priceFor(model) {
|
|
|
15087
15089
|
const key = (model ?? "").toLowerCase();
|
|
15088
15090
|
return PRICES.find((entry) => key.includes(entry.match))?.price ?? DEFAULT_PRICE;
|
|
15089
15091
|
}
|
|
15090
|
-
function costUsd(model, inputTokens, outputTokens) {
|
|
15092
|
+
function costUsd(model, inputTokens, outputTokens, cacheReadTokens = 0) {
|
|
15091
15093
|
const price = priceFor(model);
|
|
15092
|
-
return inputTokens / 1e6 * price.inputPerMTok + outputTokens / 1e6 * price.outputPerMTok;
|
|
15094
|
+
return inputTokens / 1e6 * price.inputPerMTok + outputTokens / 1e6 * price.outputPerMTok + cacheReadTokens / 1e6 * price.cacheReadPerMTok;
|
|
15093
15095
|
}
|
|
15094
15096
|
var PRICES, DEFAULT_PRICE;
|
|
15095
15097
|
var init_pricing = __esm(() => {
|
|
15096
15098
|
PRICES = [
|
|
15097
|
-
{ match: "claude-opus", price: { inputPerMTok: 15, outputPerMTok: 75 } },
|
|
15098
|
-
{ match: "claude-sonnet", price: { inputPerMTok: 3, outputPerMTok: 15 } },
|
|
15099
|
-
{ match: "claude-haiku", price: { inputPerMTok: 0.8, outputPerMTok: 4 } },
|
|
15100
|
-
{ match: "claude-fable", price: { inputPerMTok: 1, outputPerMTok: 5 } },
|
|
15101
|
-
{ match: "gpt-5", price: { inputPerMTok: 1.25, outputPerMTok: 10 } },
|
|
15102
|
-
{ match: "o4", price: { inputPerMTok: 1.1, outputPerMTok: 4.4 } }
|
|
15099
|
+
{ match: "claude-opus", price: { cacheReadPerMTok: 1.5, inputPerMTok: 15, outputPerMTok: 75 } },
|
|
15100
|
+
{ match: "claude-sonnet", price: { cacheReadPerMTok: 0.3, inputPerMTok: 3, outputPerMTok: 15 } },
|
|
15101
|
+
{ match: "claude-haiku", price: { cacheReadPerMTok: 0.08, inputPerMTok: 0.8, outputPerMTok: 4 } },
|
|
15102
|
+
{ match: "claude-fable", price: { cacheReadPerMTok: 0.1, inputPerMTok: 1, outputPerMTok: 5 } },
|
|
15103
|
+
{ match: "gpt-5", price: { cacheReadPerMTok: 0.125, inputPerMTok: 1.25, outputPerMTok: 10 } },
|
|
15104
|
+
{ match: "o4", price: { cacheReadPerMTok: 0.28, inputPerMTok: 1.1, outputPerMTok: 4.4 } }
|
|
15103
15105
|
];
|
|
15104
|
-
DEFAULT_PRICE = { inputPerMTok: 3, outputPerMTok: 15 };
|
|
15106
|
+
DEFAULT_PRICE = { cacheReadPerMTok: 0.3, inputPerMTok: 3, outputPerMTok: 15 };
|
|
15105
15107
|
});
|
|
15106
15108
|
|
|
15107
15109
|
// src/tui/format.ts
|
|
@@ -15141,32 +15143,24 @@ function meter(usedPercent, width = 14) {
|
|
|
15141
15143
|
function percentLabel(usedPercent) {
|
|
15142
15144
|
return usedPercent === null ? " ?%" : `${Math.round(clamp(usedPercent))}%`.padStart(4);
|
|
15143
15145
|
}
|
|
15144
|
-
function
|
|
15146
|
+
function brailleArea(columns, width, height, max = 100) {
|
|
15145
15147
|
const dotRows = height * 4;
|
|
15146
15148
|
const dotCols = width * 2;
|
|
15147
15149
|
const scale = max <= 0 ? 1 : max;
|
|
15148
15150
|
const grid = Array.from({ length: dotCols }, () => new Array(dotRows).fill(false));
|
|
15149
|
-
const toY = (value) => Math.max(0, Math.min(dotRows - 1, Math.round(value / scale * (dotRows - 1))));
|
|
15150
|
-
let previousY = -1;
|
|
15151
15151
|
for (let x = 0;x < dotCols; x += 1) {
|
|
15152
15152
|
const value = columns[x];
|
|
15153
|
-
if (value === null || value === undefined) {
|
|
15154
|
-
previousY = -1;
|
|
15153
|
+
if (value === null || value === undefined || value <= 0) {
|
|
15155
15154
|
continue;
|
|
15156
15155
|
}
|
|
15157
|
-
const
|
|
15156
|
+
const top = Math.max(1, Math.min(dotRows, Math.round(value / scale * dotRows)));
|
|
15158
15157
|
const column = grid[x];
|
|
15159
15158
|
if (column === undefined) {
|
|
15160
15159
|
continue;
|
|
15161
15160
|
}
|
|
15162
|
-
|
|
15163
|
-
for (let fill = Math.min(previousY, y);fill <= Math.max(previousY, y); fill += 1) {
|
|
15164
|
-
column[fill] = true;
|
|
15165
|
-
}
|
|
15166
|
-
} else {
|
|
15161
|
+
for (let y = 0;y < top; y += 1) {
|
|
15167
15162
|
column[y] = true;
|
|
15168
15163
|
}
|
|
15169
|
-
previousY = y;
|
|
15170
15164
|
}
|
|
15171
15165
|
const rows = [];
|
|
15172
15166
|
for (let charRow = 0;charRow < height; charRow += 1) {
|
|
@@ -15366,7 +15360,8 @@ function buildTokens(scale) {
|
|
|
15366
15360
|
const target = Math.round(150000 * hours * 0.5 * scale);
|
|
15367
15361
|
const buckets = raw.map((value) => Math.round(value / rawSum * target));
|
|
15368
15362
|
const totalTokens = buckets.reduce((sum, value) => sum + value, 0);
|
|
15369
|
-
const
|
|
15363
|
+
const totalCached = Math.round(totalTokens * 0.62);
|
|
15364
|
+
const totalInput = Math.round(totalTokens * 0.28);
|
|
15370
15365
|
const codexTokens = Math.round(totalTokens * 0.55);
|
|
15371
15366
|
const claudeTokens = totalTokens - codexTokens;
|
|
15372
15367
|
const codexCost = costUsd("gpt-5.6-sol", Math.round(codexTokens * 0.7), Math.round(codexTokens * 0.3));
|
|
@@ -15384,8 +15379,9 @@ function buildTokens(scale) {
|
|
|
15384
15379
|
key: timeframe.key,
|
|
15385
15380
|
peakPerHour: Math.round(peakBucket * (3600000 / bucketMs)),
|
|
15386
15381
|
topModel: totalTokens === 0 ? null : "claude-opus-4-8",
|
|
15382
|
+
totalCached,
|
|
15387
15383
|
totalInput,
|
|
15388
|
-
totalOutput: totalTokens - totalInput,
|
|
15384
|
+
totalOutput: totalTokens - totalInput - totalCached,
|
|
15389
15385
|
totalTokens
|
|
15390
15386
|
};
|
|
15391
15387
|
});
|
|
@@ -15808,28 +15804,43 @@ function timeframeBar(ctx, timeframe) {
|
|
|
15808
15804
|
function throughputCard(ctx, tokens, timeframe, height, width) {
|
|
15809
15805
|
const body = [];
|
|
15810
15806
|
if (tokens === undefined || tokens.totalTokens === 0) {
|
|
15811
|
-
for (let index = 0;index < Math.max(1, height
|
|
15807
|
+
for (let index = 0;index < Math.max(1, Math.floor(height / 2)); index += 1) {
|
|
15812
15808
|
body.push(Box({ flexDirection: "row" }, Text({ content: " ", fg: rgb(ctx.theme.bg) })));
|
|
15813
15809
|
}
|
|
15814
|
-
body.push(Box({ flexDirection: "row" }, Text({ content: "
|
|
15810
|
+
body.push(Box({ flexDirection: "row", justifyContent: "center", width: "100%" }, Text({ content: "no token usage yet \u2014 run ", fg: rgb(ctx.theme.dim) }), Text({ content: "codex", fg: rgb(ctx.theme.fg) }), Text({ content: " or ", fg: rgb(ctx.theme.dim) }), Text({ content: "claude", fg: rgb(ctx.theme.fg) }), Text({ content: " and it fills in live", fg: rgb(ctx.theme.dim) })));
|
|
15815
15811
|
} else {
|
|
15816
|
-
const
|
|
15812
|
+
const axisTop = `${compactNumber(tokens.peakPerHour)}/h`;
|
|
15813
|
+
const gutter = Math.max(6, axisTop.length);
|
|
15814
|
+
const chartWidth = Math.max(16, width - gutter - 1);
|
|
15815
|
+
const columns = throughputColumns(tokens.buckets, chartWidth * 2);
|
|
15817
15816
|
const peak = Math.max(...columns, 1);
|
|
15818
|
-
const chart =
|
|
15819
|
-
const axisTop = `${compactNumber(tokens.peakPerHour)}/h`.padStart(6);
|
|
15817
|
+
const chart = brailleArea(columns, chartWidth, height, peak);
|
|
15820
15818
|
chart.forEach((line, index) => {
|
|
15821
|
-
const
|
|
15822
|
-
body.push(Box({ flexDirection: "row" }, Text({ content: `${
|
|
15819
|
+
const label = index === 0 ? axisTop : index === chart.length - 1 ? "0" : "";
|
|
15820
|
+
body.push(Box({ flexDirection: "row" }, Text({ content: `${label.padStart(gutter)} `, fg: rgb(ctx.theme.faint) }), Text({ content: line, fg: rgb(ctx.theme.accent) })));
|
|
15823
15821
|
});
|
|
15824
|
-
body.push(Box({ flexDirection: "row" }, Text({ content: "
|
|
15825
|
-
content: `${timeframe.label} ago`.padEnd(Math.max(0,
|
|
15822
|
+
body.push(Box({ flexDirection: "row" }, Text({ content: " ".repeat(gutter + 1), fg: rgb(ctx.theme.bg) }), Text({
|
|
15823
|
+
content: `${timeframe.label} ago`.padEnd(Math.max(0, chartWidth - 3)),
|
|
15826
15824
|
fg: rgb(ctx.theme.faint)
|
|
15827
15825
|
}), Text({ content: "now", fg: rgb(ctx.theme.faint) })));
|
|
15826
|
+
const spanHours = tokens.bucketMs * tokens.buckets.length / 3600000;
|
|
15827
|
+
const averagePerHour = spanHours > 0 ? tokens.totalTokens / spanHours : 0;
|
|
15828
|
+
const recent = tokens.buckets.slice(-2);
|
|
15829
|
+
const nowPerHour = Math.max(...recent, 0) * (3600000 / tokens.bucketMs);
|
|
15830
|
+
const cachedShare = tokens.totalTokens > 0 ? Math.round(tokens.totalCached / tokens.totalTokens * 100) : 0;
|
|
15828
15831
|
body.push(Box({ flexDirection: "row", paddingLeft: 1 }, Text({ content: "\u03A3 ", fg: rgb(ctx.theme.dim) }), Text({
|
|
15829
15832
|
attributes: 1,
|
|
15830
15833
|
content: `${compactNumber(tokens.totalTokens)} tokens`,
|
|
15831
15834
|
fg: rgb(ctx.theme.fg)
|
|
15832
|
-
}), Text({ content: " \u2248 ", fg: rgb(ctx.theme.dim) }), Text({ attributes: 1, content: compactUsd(tokens.costUsd), fg: rgb(ctx.theme.good) }), Text({ content: "
|
|
15835
|
+
}), Text({ content: " \u2248 ", fg: rgb(ctx.theme.dim) }), Text({ attributes: 1, content: compactUsd(tokens.costUsd), fg: rgb(ctx.theme.good) }), Text({ content: " API value", fg: rgb(ctx.theme.dim) }), Text({
|
|
15836
|
+
content: cachedShare > 0 ? ` ${cachedShare}% cached` : "",
|
|
15837
|
+
fg: rgb(ctx.theme.dim)
|
|
15838
|
+
})));
|
|
15839
|
+
body.push(Box({ flexDirection: "row", paddingLeft: 1 }, Text({ content: "now ", fg: rgb(ctx.theme.dim) }), Text({
|
|
15840
|
+
attributes: 1,
|
|
15841
|
+
content: `${compactNumber(nowPerHour)}/h`,
|
|
15842
|
+
fg: rgb(nowPerHour > 0 ? ctx.theme.accent : ctx.theme.faint)
|
|
15843
|
+
}), Text({ content: " peak ", fg: rgb(ctx.theme.dim) }), Text({ content: `${compactNumber(tokens.peakPerHour)}/h`, fg: rgb(ctx.theme.fg) }), Text({ content: " avg ", fg: rgb(ctx.theme.dim) }), Text({ content: `${compactNumber(averagePerHour)}/h`, fg: rgb(ctx.theme.fg) }), Text({
|
|
15833
15844
|
content: tokens.topModel === null ? "" : ` top ${tokens.topModel}`,
|
|
15834
15845
|
fg: rgb(ctx.theme.faint)
|
|
15835
15846
|
})));
|
|
@@ -15859,7 +15870,7 @@ function analyticsBody(ctx, analytics, timeframe) {
|
|
|
15859
15870
|
const cols = process.stdout.columns ?? 80;
|
|
15860
15871
|
const rows = process.stdout.rows ?? 24;
|
|
15861
15872
|
const width = Math.max(24, Math.min(160, cols - 12));
|
|
15862
|
-
const height = Math.max(4, Math.min(10, rows -
|
|
15873
|
+
const height = Math.max(4, Math.min(10, rows - 17));
|
|
15863
15874
|
const tokens = analytics.tokens?.timeframes.find((entry) => entry.key === timeframe.key);
|
|
15864
15875
|
return [timeframeBar(ctx, timeframe), throughputCard(ctx, tokens, timeframe, height, width)];
|
|
15865
15876
|
}
|
|
@@ -17229,6 +17240,7 @@ function createUsageObserver(provider, contentType, onUsage) {
|
|
|
17229
17240
|
let model = null;
|
|
17230
17241
|
let input = 0;
|
|
17231
17242
|
let output = 0;
|
|
17243
|
+
let cacheRead = 0;
|
|
17232
17244
|
let saw = false;
|
|
17233
17245
|
const maxJson = 4000000;
|
|
17234
17246
|
const consume = (text) => {
|
|
@@ -17242,7 +17254,8 @@ function createUsageObserver(provider, contentType, onUsage) {
|
|
|
17242
17254
|
if (event.type === "message_start" && event.message) {
|
|
17243
17255
|
model = event.message.model ?? model;
|
|
17244
17256
|
const usage2 = event.message.usage ?? {};
|
|
17245
|
-
input += (usage2.input_tokens ?? 0) + (usage2.
|
|
17257
|
+
input += (usage2.input_tokens ?? 0) + (usage2.cache_creation_input_tokens ?? 0);
|
|
17258
|
+
cacheRead += usage2.cache_read_input_tokens ?? 0;
|
|
17246
17259
|
saw = true;
|
|
17247
17260
|
} else if (event.type === "message_delta" && event.usage) {
|
|
17248
17261
|
output = event.usage.output_tokens ?? output;
|
|
@@ -17252,7 +17265,9 @@ function createUsageObserver(provider, contentType, onUsage) {
|
|
|
17252
17265
|
}
|
|
17253
17266
|
const usage = event.response?.usage ?? event.usage;
|
|
17254
17267
|
if (usage) {
|
|
17255
|
-
|
|
17268
|
+
const cached2 = usage.input_tokens_details?.cached_tokens ?? 0;
|
|
17269
|
+
input = Math.max(0, (usage.input_tokens ?? usage.prompt_tokens ?? input) - cached2);
|
|
17270
|
+
cacheRead = cached2;
|
|
17256
17271
|
output = usage.output_tokens ?? usage.completion_tokens ?? output;
|
|
17257
17272
|
model = event.response?.model ?? event.model ?? model;
|
|
17258
17273
|
saw = true;
|
|
@@ -17263,8 +17278,9 @@ function createUsageObserver(provider, contentType, onUsage) {
|
|
|
17263
17278
|
if (!isSse && jsonBuffer.length > 0) {
|
|
17264
17279
|
consume(jsonBuffer);
|
|
17265
17280
|
}
|
|
17266
|
-
if (saw && input + output > 0) {
|
|
17281
|
+
if (saw && input + output + cacheRead > 0) {
|
|
17267
17282
|
onUsage({
|
|
17283
|
+
cacheReadTokens: cacheRead,
|
|
17268
17284
|
inputTokens: input,
|
|
17269
17285
|
model: model && model.length > 0 ? model : null,
|
|
17270
17286
|
outputTokens: output
|
|
@@ -17410,6 +17426,7 @@ function createProxyHandler(options) {
|
|
|
17410
17426
|
if (options.record !== undefined && response.ok && forwarded.body !== null) {
|
|
17411
17427
|
const observer = createUsageObserver(route.provider, response.headers.get("content-type") ?? "", (usage) => options.record?.({
|
|
17412
17428
|
at: Date.now(),
|
|
17429
|
+
cacheReadTokens: usage.cacheReadTokens,
|
|
17413
17430
|
inputTokens: usage.inputTokens,
|
|
17414
17431
|
model: usage.model,
|
|
17415
17432
|
outputTokens: usage.outputTokens,
|
|
@@ -17635,6 +17652,7 @@ function selectRotation(input) {
|
|
|
17635
17652
|
function priceTokenTimeframe(aggregate) {
|
|
17636
17653
|
let totalInput = 0;
|
|
17637
17654
|
let totalOutput = 0;
|
|
17655
|
+
let totalCached = 0;
|
|
17638
17656
|
let costTotal = 0;
|
|
17639
17657
|
const byProvider = {
|
|
17640
17658
|
anthropic: { costUsd: 0, tokens: 0 },
|
|
@@ -17642,10 +17660,11 @@ function priceTokenTimeframe(aggregate) {
|
|
|
17642
17660
|
};
|
|
17643
17661
|
let top = null;
|
|
17644
17662
|
for (const entry of aggregate.byModel) {
|
|
17645
|
-
const entryCost = costUsd(entry.model, entry.input, entry.output);
|
|
17646
|
-
const entryTokens = entry.input + entry.output;
|
|
17663
|
+
const entryCost = costUsd(entry.model, entry.input, entry.output, entry.cached);
|
|
17664
|
+
const entryTokens = entry.input + entry.output + entry.cached;
|
|
17647
17665
|
totalInput += entry.input;
|
|
17648
17666
|
totalOutput += entry.output;
|
|
17667
|
+
totalCached += entry.cached;
|
|
17649
17668
|
costTotal += entryCost;
|
|
17650
17669
|
const bucket = entry.provider === "anthropic" ? byProvider.anthropic : byProvider.openai;
|
|
17651
17670
|
bucket.tokens += entryTokens;
|
|
@@ -17663,9 +17682,10 @@ function priceTokenTimeframe(aggregate) {
|
|
|
17663
17682
|
key: aggregate.key,
|
|
17664
17683
|
peakPerHour: peakBucket * (3600000 / aggregate.bucketMs),
|
|
17665
17684
|
topModel: top?.model ?? null,
|
|
17685
|
+
totalCached,
|
|
17666
17686
|
totalInput,
|
|
17667
17687
|
totalOutput,
|
|
17668
|
-
totalTokens: totalInput + totalOutput
|
|
17688
|
+
totalTokens: totalInput + totalOutput + totalCached
|
|
17669
17689
|
};
|
|
17670
17690
|
}
|
|
17671
17691
|
var defaultDependencies = {
|
|
@@ -18246,6 +18266,7 @@ function migrate(database) {
|
|
|
18246
18266
|
account_id TEXT,
|
|
18247
18267
|
model TEXT,
|
|
18248
18268
|
input_tokens INTEGER NOT NULL DEFAULT 0,
|
|
18269
|
+
cache_read_tokens INTEGER NOT NULL DEFAULT 0,
|
|
18249
18270
|
output_tokens INTEGER NOT NULL DEFAULT 0
|
|
18250
18271
|
);
|
|
18251
18272
|
CREATE INDEX IF NOT EXISTS token_events_at ON token_events(at);
|
|
@@ -18253,6 +18274,11 @@ function migrate(database) {
|
|
|
18253
18274
|
DROP TABLE IF EXISTS runtime_sessions;
|
|
18254
18275
|
`);
|
|
18255
18276
|
const accountColumns = new Set(database.query("PRAGMA table_info(accounts)").all().map((column) => column.name));
|
|
18277
|
+
const tokenColumns = new Set(database.query("PRAGMA table_info(token_events)").all().map((column) => column.name));
|
|
18278
|
+
if (!tokenColumns.has("cache_read_tokens")) {
|
|
18279
|
+
database.exec("ALTER TABLE token_events ADD COLUMN cache_read_tokens INTEGER NOT NULL DEFAULT 0");
|
|
18280
|
+
database.exec("DELETE FROM token_events");
|
|
18281
|
+
}
|
|
18256
18282
|
const requiresLabelMigration = !accountColumns.has("label");
|
|
18257
18283
|
const requiresExternalAccountIdMigration = !accountColumns.has("external_account_id");
|
|
18258
18284
|
const requiresExternalUserIdMigration = !accountColumns.has("external_user_id");
|
|
@@ -18440,12 +18466,12 @@ function createStateStore(databasePath) {
|
|
|
18440
18466
|
}
|
|
18441
18467
|
function recordTokenEvent(event) {
|
|
18442
18468
|
const parsed = TokenEventSchema.parse(event);
|
|
18443
|
-
database.query("INSERT INTO token_events (at, provider, account_id, model, input_tokens, output_tokens) VALUES (?, ?, ?, ?, ?, ?)").run(parsed.at, parsed.provider, parsed.accountId, parsed.model, parsed.inputTokens, parsed.outputTokens);
|
|
18469
|
+
database.query("INSERT INTO token_events (at, provider, account_id, model, input_tokens, output_tokens, cache_read_tokens) VALUES (?, ?, ?, ?, ?, ?, ?)").run(parsed.at, parsed.provider, parsed.accountId, parsed.model, parsed.inputTokens, parsed.outputTokens, parsed.cacheReadTokens);
|
|
18444
18470
|
database.query("DELETE FROM token_events WHERE at < ?").run(parsed.at - maxTokenEventAgeMs);
|
|
18445
18471
|
}
|
|
18446
18472
|
function tokenAnalytics(nowMillis, timeframes) {
|
|
18447
|
-
const bucketQuery = database.query("SELECT CAST((at - ?) / ? AS INTEGER) AS b, SUM(input_tokens + output_tokens) AS tokens FROM token_events WHERE at >= ? AND at <= ? GROUP BY b");
|
|
18448
|
-
const modelQuery = database.query("SELECT model, provider, SUM(input_tokens) AS input, SUM(output_tokens) AS output FROM token_events WHERE at >= ? AND at <= ? GROUP BY model, provider");
|
|
18473
|
+
const bucketQuery = database.query("SELECT CAST((at - ?) / ? AS INTEGER) AS b, SUM(input_tokens + output_tokens + cache_read_tokens) AS tokens FROM token_events WHERE at >= ? AND at <= ? GROUP BY b");
|
|
18474
|
+
const modelQuery = database.query("SELECT model, provider, SUM(input_tokens) AS input, SUM(output_tokens) AS output, SUM(cache_read_tokens) AS cached FROM token_events WHERE at >= ? AND at <= ? GROUP BY model, provider");
|
|
18449
18475
|
return timeframes.map(({ key, ms }) => {
|
|
18450
18476
|
const start = nowMillis - ms;
|
|
18451
18477
|
const bucketMs = Math.max(1, Math.round(ms / tokenBucketCount));
|
|
@@ -18455,6 +18481,7 @@ function createStateStore(databasePath) {
|
|
|
18455
18481
|
buckets[index] = (buckets[index] ?? 0) + row.tokens;
|
|
18456
18482
|
}
|
|
18457
18483
|
const byModel = modelQuery.all(start, nowMillis).map((row) => ({
|
|
18484
|
+
cached: row.cached,
|
|
18458
18485
|
input: row.input,
|
|
18459
18486
|
model: row.model ?? "unknown",
|
|
18460
18487
|
output: row.output,
|
package/package.json
CHANGED