@lakphy/local-router 0.5.2 → 0.5.3
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/cli.js +1013 -109
- package/dist/entry.js +980 -76
- package/dist/web/assets/index-CQ4HNKVY.css +2 -0
- package/dist/web/assets/index-HmBORmZo.js +190 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/index-BprGtkte.js +0 -191
- package/dist/web/assets/index-OkpSAlwA.css +0 -2
package/dist/cli.js
CHANGED
|
@@ -11166,7 +11166,7 @@ __export(exports_config_extra, {
|
|
|
11166
11166
|
configDiff: () => configDiff,
|
|
11167
11167
|
configBackupsList: () => configBackupsList
|
|
11168
11168
|
});
|
|
11169
|
-
import { existsSync as
|
|
11169
|
+
import { existsSync as existsSync10, readdirSync, readFileSync as readFileSync9, statSync as statSync5 } from "fs";
|
|
11170
11170
|
import { dirname as dirname4, join as join12 } from "path";
|
|
11171
11171
|
import { parseArgs as parseArgs2 } from "util";
|
|
11172
11172
|
function maskApiKey(k) {
|
|
@@ -11245,15 +11245,15 @@ async function configDiff(args, flags) {
|
|
|
11245
11245
|
strict: false
|
|
11246
11246
|
});
|
|
11247
11247
|
const path = resolveConfigPath(parsed.values.config);
|
|
11248
|
-
if (!
|
|
11248
|
+
if (!existsSync10(path)) {
|
|
11249
11249
|
throw new CliError("CONFIG_NOT_FOUND", `\u914D\u7F6E\u4E0D\u5B58\u5728: ${path}`);
|
|
11250
11250
|
}
|
|
11251
|
-
const after =
|
|
11251
|
+
const after = readFileSync9(path, "utf-8");
|
|
11252
11252
|
const against = parsed.values.against;
|
|
11253
11253
|
let beforePath;
|
|
11254
11254
|
if (!against) {
|
|
11255
11255
|
const backupDir = join12(dirname4(path), ".backups");
|
|
11256
|
-
if (!
|
|
11256
|
+
if (!existsSync10(backupDir)) {
|
|
11257
11257
|
throw new CliError("CONFIG_NOT_FOUND", "\u6CA1\u6709\u5907\u4EFD\u53EF\u5BF9\u6BD4", {
|
|
11258
11258
|
hint: "\u6307\u5B9A --against <path|backup-id>"
|
|
11259
11259
|
});
|
|
@@ -11263,20 +11263,20 @@ async function configDiff(args, flags) {
|
|
|
11263
11263
|
throw new CliError("CONFIG_NOT_FOUND", "\u5907\u4EFD\u76EE\u5F55\u4E3A\u7A7A");
|
|
11264
11264
|
}
|
|
11265
11265
|
beforePath = join12(backupDir, files[0]);
|
|
11266
|
-
} else if (
|
|
11266
|
+
} else if (existsSync10(against)) {
|
|
11267
11267
|
beforePath = against;
|
|
11268
11268
|
} else {
|
|
11269
11269
|
const backupDir = join12(dirname4(path), ".backups");
|
|
11270
11270
|
const candidate = join12(backupDir, `config-${against}.json5`);
|
|
11271
|
-
if (
|
|
11271
|
+
if (existsSync10(candidate))
|
|
11272
11272
|
beforePath = candidate;
|
|
11273
|
-
else if (
|
|
11273
|
+
else if (existsSync10(join12(backupDir, against)))
|
|
11274
11274
|
beforePath = join12(backupDir, against);
|
|
11275
11275
|
else {
|
|
11276
11276
|
throw new CliError("CONFIG_NOT_FOUND", `--against \u4E0D\u5B58\u5728: ${against}`);
|
|
11277
11277
|
}
|
|
11278
11278
|
}
|
|
11279
|
-
const before =
|
|
11279
|
+
const before = readFileSync9(beforePath, "utf-8");
|
|
11280
11280
|
const diff = computeLineDiff(before, after);
|
|
11281
11281
|
const { added, removed } = summarizeDiff(diff);
|
|
11282
11282
|
emitResult(ctx, {
|
|
@@ -11320,10 +11320,10 @@ async function configImport(args, flags) {
|
|
|
11320
11320
|
if (!file2 || file2 === "-") {
|
|
11321
11321
|
raw2 = await readStdin();
|
|
11322
11322
|
} else {
|
|
11323
|
-
if (!
|
|
11323
|
+
if (!existsSync10(file2)) {
|
|
11324
11324
|
throw new CliError("CONFIG_NOT_FOUND", `\u8F93\u5165\u6587\u4EF6\u4E0D\u5B58\u5728: ${file2}`);
|
|
11325
11325
|
}
|
|
11326
|
-
raw2 =
|
|
11326
|
+
raw2 = readFileSync9(file2, "utf-8");
|
|
11327
11327
|
}
|
|
11328
11328
|
let parsedInput;
|
|
11329
11329
|
try {
|
|
@@ -11332,7 +11332,7 @@ async function configImport(args, flags) {
|
|
|
11332
11332
|
throw new CliError("CONFIG_INVALID", `\u8F93\u5165\u4E0D\u662F\u5408\u6CD5 JSON5: ${err instanceof Error ? err.message : err}`);
|
|
11333
11333
|
}
|
|
11334
11334
|
const path = resolveConfigPath(parsed.values.config);
|
|
11335
|
-
const current =
|
|
11335
|
+
const current = existsSync10(path) ? loadConfig(path) : null;
|
|
11336
11336
|
let next;
|
|
11337
11337
|
if (parsed.values.replace || !current) {
|
|
11338
11338
|
next = parsedInput;
|
|
@@ -11380,10 +11380,10 @@ async function configPatch(args, flags) {
|
|
|
11380
11380
|
if (!file2 || file2 === "-") {
|
|
11381
11381
|
raw2 = await readStdin();
|
|
11382
11382
|
} else {
|
|
11383
|
-
if (!
|
|
11383
|
+
if (!existsSync10(file2)) {
|
|
11384
11384
|
throw new CliError("CONFIG_NOT_FOUND", `patch \u6587\u4EF6\u4E0D\u5B58\u5728: ${file2}`);
|
|
11385
11385
|
}
|
|
11386
|
-
raw2 =
|
|
11386
|
+
raw2 = readFileSync9(file2, "utf-8");
|
|
11387
11387
|
}
|
|
11388
11388
|
let ops;
|
|
11389
11389
|
try {
|
|
@@ -11427,11 +11427,11 @@ ${result.diff}`
|
|
|
11427
11427
|
}
|
|
11428
11428
|
function listBackups(configPath) {
|
|
11429
11429
|
const dir = join12(dirname4(configPath), ".backups");
|
|
11430
|
-
if (!
|
|
11430
|
+
if (!existsSync10(dir))
|
|
11431
11431
|
return [];
|
|
11432
11432
|
return readdirSync(dir).filter((f) => f.startsWith("config-") && f.endsWith(".json5")).map((f) => {
|
|
11433
11433
|
const full = join12(dir, f);
|
|
11434
|
-
const st =
|
|
11434
|
+
const st = statSync5(full);
|
|
11435
11435
|
return {
|
|
11436
11436
|
id: f.replace(/^config-/, "").replace(/\.json5$/, ""),
|
|
11437
11437
|
path: full,
|
|
@@ -11490,7 +11490,7 @@ async function configRollback(args, flags) {
|
|
|
11490
11490
|
details: { available: backups.map((b) => b.id) }
|
|
11491
11491
|
});
|
|
11492
11492
|
}
|
|
11493
|
-
const text2 =
|
|
11493
|
+
const text2 = readFileSync9(target.path, "utf-8");
|
|
11494
11494
|
let parsedConfig;
|
|
11495
11495
|
try {
|
|
11496
11496
|
parsedConfig = dist_default.parse(text2);
|
|
@@ -11677,13 +11677,13 @@ import { parseArgs as parseArgs3 } from "util";
|
|
|
11677
11677
|
|
|
11678
11678
|
// src/cli/process.ts
|
|
11679
11679
|
init_config();
|
|
11680
|
-
import { closeSync as closeSync3, openSync as openSync3, readFileSync as
|
|
11680
|
+
import { closeSync as closeSync3, openSync as openSync3, readFileSync as readFileSync8, statSync as statSync4 } from "fs";
|
|
11681
11681
|
import { setTimeout as sleep } from "timers/promises";
|
|
11682
11682
|
import { parseArgs } from "util";
|
|
11683
11683
|
|
|
11684
11684
|
// src/index.ts
|
|
11685
|
-
import { readFileSync as
|
|
11686
|
-
import { dirname as dirname3, resolve as
|
|
11685
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
11686
|
+
import { dirname as dirname3, resolve as resolve7 } from "path";
|
|
11687
11687
|
|
|
11688
11688
|
// node_modules/.bun/@ai-sdk+provider@3.0.8/node_modules/@ai-sdk/provider/dist/index.mjs
|
|
11689
11689
|
var marker = "vercel.ai.error";
|
|
@@ -52928,8 +52928,8 @@ async function getLogMetrics(options) {
|
|
|
52928
52928
|
|
|
52929
52929
|
// src/log-query.ts
|
|
52930
52930
|
init_config();
|
|
52931
|
-
import { createReadStream as createReadStream3, existsSync as
|
|
52932
|
-
import { join as join6, resolve as
|
|
52931
|
+
import { createReadStream as createReadStream3, existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
|
|
52932
|
+
import { join as join6, resolve as resolve5 } from "path";
|
|
52933
52933
|
import { createInterface as createInterface2 } from "readline";
|
|
52934
52934
|
|
|
52935
52935
|
// src/log-index.ts
|
|
@@ -52938,11 +52938,11 @@ import { Database } from "bun:sqlite";
|
|
|
52938
52938
|
import {
|
|
52939
52939
|
closeSync,
|
|
52940
52940
|
createReadStream as createReadStream2,
|
|
52941
|
-
existsSync as
|
|
52941
|
+
existsSync as existsSync4,
|
|
52942
52942
|
mkdirSync as mkdirSync3,
|
|
52943
52943
|
openSync,
|
|
52944
52944
|
readSync,
|
|
52945
|
-
statSync
|
|
52945
|
+
statSync as statSync2
|
|
52946
52946
|
} from "fs";
|
|
52947
52947
|
import { join as join5 } from "path";
|
|
52948
52948
|
|
|
@@ -53022,8 +53022,588 @@ function resolveLogSessionIdentity(requestBody) {
|
|
|
53022
53022
|
};
|
|
53023
53023
|
}
|
|
53024
53024
|
|
|
53025
|
+
// src/token-usage.ts
|
|
53026
|
+
import { existsSync as existsSync3, readFileSync as readFileSync4, statSync } from "fs";
|
|
53027
|
+
import { resolve as resolve4 } from "path";
|
|
53028
|
+
var MAX_STREAM_USAGE_BYTES = 25 * 1024 * 1024;
|
|
53029
|
+
function asRecord(value) {
|
|
53030
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
53031
|
+
return null;
|
|
53032
|
+
return value;
|
|
53033
|
+
}
|
|
53034
|
+
function numeric(value) {
|
|
53035
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
53036
|
+
return value;
|
|
53037
|
+
if (typeof value === "string" && value.trim()) {
|
|
53038
|
+
const parsed = Number(value);
|
|
53039
|
+
if (Number.isFinite(parsed))
|
|
53040
|
+
return parsed;
|
|
53041
|
+
}
|
|
53042
|
+
return null;
|
|
53043
|
+
}
|
|
53044
|
+
function numberAt(value, path) {
|
|
53045
|
+
let current = value;
|
|
53046
|
+
for (const key2 of path) {
|
|
53047
|
+
const record2 = asRecord(current);
|
|
53048
|
+
if (!record2 || !(key2 in record2))
|
|
53049
|
+
return null;
|
|
53050
|
+
current = record2[key2];
|
|
53051
|
+
}
|
|
53052
|
+
return numeric(current);
|
|
53053
|
+
}
|
|
53054
|
+
function firstNumber(value, paths) {
|
|
53055
|
+
for (const path of paths) {
|
|
53056
|
+
const found = numberAt(value, path);
|
|
53057
|
+
if (found !== null)
|
|
53058
|
+
return found;
|
|
53059
|
+
}
|
|
53060
|
+
return null;
|
|
53061
|
+
}
|
|
53062
|
+
function maxNullable(...values) {
|
|
53063
|
+
const numbers = values.filter((value) => value !== null && value !== undefined);
|
|
53064
|
+
if (numbers.length === 0)
|
|
53065
|
+
return null;
|
|
53066
|
+
return Math.max(...numbers);
|
|
53067
|
+
}
|
|
53068
|
+
function sumNullable(...values) {
|
|
53069
|
+
let total = 0;
|
|
53070
|
+
let hasValue = false;
|
|
53071
|
+
for (const value of values) {
|
|
53072
|
+
if (value === null || value === undefined)
|
|
53073
|
+
continue;
|
|
53074
|
+
total += value;
|
|
53075
|
+
hasValue = true;
|
|
53076
|
+
}
|
|
53077
|
+
return hasValue ? total : null;
|
|
53078
|
+
}
|
|
53079
|
+
function roundPercent(numerator, denominator) {
|
|
53080
|
+
if (!Number.isFinite(numerator) || !Number.isFinite(denominator) || denominator <= 0)
|
|
53081
|
+
return null;
|
|
53082
|
+
return Number((numerator / denominator * 100).toFixed(2));
|
|
53083
|
+
}
|
|
53084
|
+
function inferProviderStyle(usage, providerHint) {
|
|
53085
|
+
const hint = providerHint?.toLowerCase() ?? "";
|
|
53086
|
+
if (hint.includes("anthropic") || hint.includes("claude"))
|
|
53087
|
+
return "anthropic";
|
|
53088
|
+
if (hint.includes("gemini") || hint.includes("google"))
|
|
53089
|
+
return "gemini";
|
|
53090
|
+
if (hint.includes("deepseek"))
|
|
53091
|
+
return "deepseek";
|
|
53092
|
+
if (hint.includes("cohere"))
|
|
53093
|
+
return "cohere";
|
|
53094
|
+
if (hint.includes("mistral"))
|
|
53095
|
+
return "mistral";
|
|
53096
|
+
if (hint.includes("openrouter"))
|
|
53097
|
+
return "openrouter";
|
|
53098
|
+
if (hint.includes("openai") || hint.includes("gpt-"))
|
|
53099
|
+
return "openai";
|
|
53100
|
+
if ("cache_read_input_tokens" in usage || "cache_creation_input_tokens" in usage) {
|
|
53101
|
+
return "anthropic";
|
|
53102
|
+
}
|
|
53103
|
+
if ("prompt_cache_hit_tokens" in usage || "prompt_cache_miss_tokens" in usage) {
|
|
53104
|
+
return "deepseek";
|
|
53105
|
+
}
|
|
53106
|
+
if ("promptTokenCount" in usage || "usageMetadata" in usage || "cachedContentTokenCount" in usage) {
|
|
53107
|
+
return "gemini";
|
|
53108
|
+
}
|
|
53109
|
+
if ("billed_units" in usage || "tokens" in usage) {
|
|
53110
|
+
return "cohere";
|
|
53111
|
+
}
|
|
53112
|
+
if ("prompt_tokens" in usage || "completion_tokens" in usage) {
|
|
53113
|
+
return "openai";
|
|
53114
|
+
}
|
|
53115
|
+
if ("input_tokens" in usage || "output_tokens" in usage) {
|
|
53116
|
+
return "openai";
|
|
53117
|
+
}
|
|
53118
|
+
return "unknown";
|
|
53119
|
+
}
|
|
53120
|
+
function createEmptyMetrics2(input) {
|
|
53121
|
+
return {
|
|
53122
|
+
schemaVersion: 1,
|
|
53123
|
+
source: input.source,
|
|
53124
|
+
providerStyle: input.providerStyle,
|
|
53125
|
+
inputTokens: null,
|
|
53126
|
+
outputTokens: null,
|
|
53127
|
+
totalTokens: null,
|
|
53128
|
+
cachedInputTokens: null,
|
|
53129
|
+
cacheHitInputTokens: null,
|
|
53130
|
+
cacheHitRate: null,
|
|
53131
|
+
cacheHitRateDenominatorTokens: null,
|
|
53132
|
+
cacheHitRateFormula: null,
|
|
53133
|
+
cacheReadInputTokens: null,
|
|
53134
|
+
cacheCreationInputTokens: null,
|
|
53135
|
+
cacheCreationInputTokens5m: null,
|
|
53136
|
+
cacheCreationInputTokens1h: null,
|
|
53137
|
+
cacheWriteInputTokens: null,
|
|
53138
|
+
cacheMissInputTokens: null,
|
|
53139
|
+
reasoningTokens: null,
|
|
53140
|
+
audioInputTokens: null,
|
|
53141
|
+
audioOutputTokens: null,
|
|
53142
|
+
textInputTokens: null,
|
|
53143
|
+
textOutputTokens: null,
|
|
53144
|
+
acceptedPredictionTokens: null,
|
|
53145
|
+
rejectedPredictionTokens: null,
|
|
53146
|
+
toolUsePromptTokens: null,
|
|
53147
|
+
billableInputTokens: null,
|
|
53148
|
+
billableOutputTokens: null,
|
|
53149
|
+
creditUsage: null,
|
|
53150
|
+
cost: null,
|
|
53151
|
+
rawUsage: input.rawUsage,
|
|
53152
|
+
rawUsagePath: input.rawUsagePath,
|
|
53153
|
+
warnings: []
|
|
53154
|
+
};
|
|
53155
|
+
}
|
|
53156
|
+
function hasAnyTokenSignal(metrics) {
|
|
53157
|
+
return [
|
|
53158
|
+
metrics.inputTokens,
|
|
53159
|
+
metrics.outputTokens,
|
|
53160
|
+
metrics.totalTokens,
|
|
53161
|
+
metrics.cachedInputTokens,
|
|
53162
|
+
metrics.cacheHitInputTokens,
|
|
53163
|
+
metrics.cacheReadInputTokens,
|
|
53164
|
+
metrics.cacheCreationInputTokens,
|
|
53165
|
+
metrics.cacheMissInputTokens,
|
|
53166
|
+
metrics.reasoningTokens,
|
|
53167
|
+
metrics.billableInputTokens,
|
|
53168
|
+
metrics.billableOutputTokens,
|
|
53169
|
+
metrics.creditUsage,
|
|
53170
|
+
metrics.cost
|
|
53171
|
+
].some((value) => value !== null);
|
|
53172
|
+
}
|
|
53173
|
+
function normalizeUsageObject(input) {
|
|
53174
|
+
const { usage, source: source2, rawUsagePath, providerHint } = input;
|
|
53175
|
+
const providerStyle = inferProviderStyle(usage, providerHint);
|
|
53176
|
+
const metrics = createEmptyMetrics2({
|
|
53177
|
+
source: source2,
|
|
53178
|
+
providerStyle,
|
|
53179
|
+
rawUsage: usage,
|
|
53180
|
+
rawUsagePath
|
|
53181
|
+
});
|
|
53182
|
+
const usageBody = asRecord(usage.usageMetadata) ?? usage;
|
|
53183
|
+
metrics.inputTokens = firstNumber(usageBody, [
|
|
53184
|
+
["input_tokens"],
|
|
53185
|
+
["prompt_tokens"],
|
|
53186
|
+
["promptTokenCount"],
|
|
53187
|
+
["tokens", "input_tokens"],
|
|
53188
|
+
["billed_units", "input_tokens"]
|
|
53189
|
+
]);
|
|
53190
|
+
metrics.outputTokens = firstNumber(usageBody, [
|
|
53191
|
+
["output_tokens"],
|
|
53192
|
+
["completion_tokens"],
|
|
53193
|
+
["candidatesTokenCount"],
|
|
53194
|
+
["tokens", "output_tokens"],
|
|
53195
|
+
["billed_units", "output_tokens"]
|
|
53196
|
+
]);
|
|
53197
|
+
const explicitTotalTokens = firstNumber(usageBody, [
|
|
53198
|
+
["total_tokens"],
|
|
53199
|
+
["totalTokenCount"],
|
|
53200
|
+
["tokens", "total_tokens"]
|
|
53201
|
+
]);
|
|
53202
|
+
metrics.totalTokens = explicitTotalTokens;
|
|
53203
|
+
const cachedTokens = firstNumber(usageBody, [
|
|
53204
|
+
["input_tokens_details", "cached_tokens"],
|
|
53205
|
+
["prompt_tokens_details", "cached_tokens"],
|
|
53206
|
+
["cached_tokens"],
|
|
53207
|
+
["cachedContentTokenCount"]
|
|
53208
|
+
]);
|
|
53209
|
+
const cacheReadTokens = firstNumber(usageBody, [
|
|
53210
|
+
["cache_read_input_tokens"],
|
|
53211
|
+
["cacheReadInputTokens"],
|
|
53212
|
+
["claude_cache_read_input_tokens"]
|
|
53213
|
+
]);
|
|
53214
|
+
const promptCacheHitTokens = firstNumber(usageBody, [["prompt_cache_hit_tokens"]]);
|
|
53215
|
+
const promptCacheMissTokens = firstNumber(usageBody, [["prompt_cache_miss_tokens"]]);
|
|
53216
|
+
const cacheCreationTokens = firstNumber(usageBody, [
|
|
53217
|
+
["cache_creation_input_tokens"],
|
|
53218
|
+
["cacheCreationInputTokens"],
|
|
53219
|
+
["cache_creation", "input_tokens"],
|
|
53220
|
+
["claude_cache_creation_input_tokens"]
|
|
53221
|
+
]);
|
|
53222
|
+
metrics.cacheCreationInputTokens5m = firstNumber(usageBody, [
|
|
53223
|
+
["cache_creation", "ephemeral_5m_input_tokens"],
|
|
53224
|
+
["cache_creation", "ephemeral5mInputTokens"],
|
|
53225
|
+
["cache_creation_ephemeral_5m_input_tokens"],
|
|
53226
|
+
["claude_cache_creation_5_m_tokens"]
|
|
53227
|
+
]);
|
|
53228
|
+
metrics.cacheCreationInputTokens1h = firstNumber(usageBody, [
|
|
53229
|
+
["cache_creation", "ephemeral_1h_input_tokens"],
|
|
53230
|
+
["cache_creation", "ephemeral1hInputTokens"],
|
|
53231
|
+
["cache_creation_ephemeral_1h_input_tokens"],
|
|
53232
|
+
["claude_cache_creation_1_h_tokens"]
|
|
53233
|
+
]);
|
|
53234
|
+
metrics.cacheCreationInputTokens = maxNullable(cacheCreationTokens, sumNullable(metrics.cacheCreationInputTokens5m, metrics.cacheCreationInputTokens1h));
|
|
53235
|
+
metrics.cacheReadInputTokens = cacheReadTokens;
|
|
53236
|
+
metrics.cacheHitInputTokens = maxNullable(cachedTokens, cacheReadTokens, promptCacheHitTokens);
|
|
53237
|
+
metrics.cachedInputTokens = metrics.cacheHitInputTokens;
|
|
53238
|
+
metrics.cacheMissInputTokens = promptCacheMissTokens;
|
|
53239
|
+
metrics.cacheWriteInputTokens = firstNumber(usageBody, [
|
|
53240
|
+
["cache_write_input_tokens"],
|
|
53241
|
+
["cacheWriteInputTokens"]
|
|
53242
|
+
]);
|
|
53243
|
+
if (metrics.cacheWriteInputTokens === null && providerStyle === "anthropic") {
|
|
53244
|
+
metrics.cacheWriteInputTokens = metrics.cacheCreationInputTokens;
|
|
53245
|
+
}
|
|
53246
|
+
metrics.reasoningTokens = firstNumber(usageBody, [
|
|
53247
|
+
["output_tokens_details", "reasoning_tokens"],
|
|
53248
|
+
["completion_tokens_details", "reasoning_tokens"],
|
|
53249
|
+
["reasoning_tokens"],
|
|
53250
|
+
["thoughtsTokenCount"]
|
|
53251
|
+
]);
|
|
53252
|
+
metrics.audioInputTokens = firstNumber(usageBody, [
|
|
53253
|
+
["input_tokens_details", "audio_tokens"],
|
|
53254
|
+
["prompt_tokens_details", "audio_tokens"],
|
|
53255
|
+
["audio_input_tokens"]
|
|
53256
|
+
]);
|
|
53257
|
+
metrics.audioOutputTokens = firstNumber(usageBody, [
|
|
53258
|
+
["output_tokens_details", "audio_tokens"],
|
|
53259
|
+
["completion_tokens_details", "audio_tokens"],
|
|
53260
|
+
["audio_output_tokens"]
|
|
53261
|
+
]);
|
|
53262
|
+
metrics.textInputTokens = firstNumber(usageBody, [
|
|
53263
|
+
["input_tokens_details", "text_tokens"],
|
|
53264
|
+
["prompt_tokens_details", "text_tokens"],
|
|
53265
|
+
["text_input_tokens"]
|
|
53266
|
+
]);
|
|
53267
|
+
metrics.textOutputTokens = firstNumber(usageBody, [
|
|
53268
|
+
["output_tokens_details", "text_tokens"],
|
|
53269
|
+
["completion_tokens_details", "text_tokens"],
|
|
53270
|
+
["text_output_tokens"]
|
|
53271
|
+
]);
|
|
53272
|
+
metrics.acceptedPredictionTokens = firstNumber(usageBody, [
|
|
53273
|
+
["output_tokens_details", "accepted_prediction_tokens"],
|
|
53274
|
+
["completion_tokens_details", "accepted_prediction_tokens"],
|
|
53275
|
+
["accepted_prediction_tokens"]
|
|
53276
|
+
]);
|
|
53277
|
+
metrics.rejectedPredictionTokens = firstNumber(usageBody, [
|
|
53278
|
+
["output_tokens_details", "rejected_prediction_tokens"],
|
|
53279
|
+
["completion_tokens_details", "rejected_prediction_tokens"],
|
|
53280
|
+
["rejected_prediction_tokens"]
|
|
53281
|
+
]);
|
|
53282
|
+
metrics.toolUsePromptTokens = firstNumber(usageBody, [
|
|
53283
|
+
["toolUsePromptTokenCount"],
|
|
53284
|
+
["tool_use_prompt_tokens"]
|
|
53285
|
+
]);
|
|
53286
|
+
metrics.billableInputTokens = firstNumber(usageBody, [
|
|
53287
|
+
["billed_units", "input_tokens"],
|
|
53288
|
+
["billable_input_tokens"]
|
|
53289
|
+
]);
|
|
53290
|
+
metrics.billableOutputTokens = firstNumber(usageBody, [
|
|
53291
|
+
["billed_units", "output_tokens"],
|
|
53292
|
+
["billable_output_tokens"]
|
|
53293
|
+
]);
|
|
53294
|
+
metrics.creditUsage = firstNumber(usageBody, [["credit_usage"], ["creditUsage"]]);
|
|
53295
|
+
metrics.cost = firstNumber(usageBody, [["cost"], ["total_cost"], ["totalCost"]]);
|
|
53296
|
+
let cacheDenominator = null;
|
|
53297
|
+
let cacheFormula = null;
|
|
53298
|
+
if (providerStyle === "anthropic") {
|
|
53299
|
+
cacheDenominator = sumNullable(metrics.inputTokens, metrics.cacheReadInputTokens, metrics.cacheCreationInputTokens);
|
|
53300
|
+
cacheFormula = "cache_read_input_tokens / (input_tokens + cache_read_input_tokens + cache_creation_input_tokens)";
|
|
53301
|
+
} else if (providerStyle === "deepseek") {
|
|
53302
|
+
cacheDenominator = metrics.inputTokens ?? sumNullable(metrics.cacheHitInputTokens, metrics.cacheMissInputTokens);
|
|
53303
|
+
cacheFormula = "prompt_cache_hit_tokens / prompt_tokens";
|
|
53304
|
+
} else if (providerStyle === "gemini") {
|
|
53305
|
+
cacheDenominator = metrics.inputTokens;
|
|
53306
|
+
cacheFormula = "cachedContentTokenCount / promptTokenCount";
|
|
53307
|
+
} else if (providerStyle === "openai" || providerStyle === "mistral" || providerStyle === "openrouter" || providerStyle === "unknown") {
|
|
53308
|
+
cacheDenominator = metrics.inputTokens;
|
|
53309
|
+
cacheFormula = "cached_tokens / input_tokens";
|
|
53310
|
+
}
|
|
53311
|
+
if (metrics.cacheHitInputTokens !== null && cacheDenominator !== null && cacheDenominator > 0) {
|
|
53312
|
+
metrics.cacheHitRateDenominatorTokens = cacheDenominator;
|
|
53313
|
+
metrics.cacheHitRateFormula = cacheFormula;
|
|
53314
|
+
metrics.cacheHitRate = roundPercent(metrics.cacheHitInputTokens, cacheDenominator);
|
|
53315
|
+
if (metrics.cacheMissInputTokens === null) {
|
|
53316
|
+
metrics.cacheMissInputTokens = Math.max(0, cacheDenominator - metrics.cacheHitInputTokens);
|
|
53317
|
+
}
|
|
53318
|
+
}
|
|
53319
|
+
if (metrics.totalTokens === null && metrics.outputTokens !== null) {
|
|
53320
|
+
const effectiveInputTokens = metrics.cacheHitRateDenominatorTokens ?? metrics.inputTokens;
|
|
53321
|
+
if (effectiveInputTokens !== null) {
|
|
53322
|
+
metrics.totalTokens = effectiveInputTokens + metrics.outputTokens;
|
|
53323
|
+
metrics.warnings.push(metrics.cacheHitRateDenominatorTokens !== null ? "totalTokens \u7531 cacheHitRateDenominatorTokens + outputTokens \u63A8\u5BFC" : "totalTokens \u7531 inputTokens + outputTokens \u63A8\u5BFC");
|
|
53324
|
+
}
|
|
53325
|
+
}
|
|
53326
|
+
return hasAnyTokenSignal(metrics) ? metrics : null;
|
|
53327
|
+
}
|
|
53328
|
+
function collectUsageCandidates(value, prefix = "") {
|
|
53329
|
+
const record2 = asRecord(value);
|
|
53330
|
+
if (!record2)
|
|
53331
|
+
return [];
|
|
53332
|
+
const candidates = [];
|
|
53333
|
+
const candidateKeys = [
|
|
53334
|
+
"usage",
|
|
53335
|
+
"usageMetadata",
|
|
53336
|
+
"message.usage",
|
|
53337
|
+
"response.usage",
|
|
53338
|
+
"body.usage",
|
|
53339
|
+
"data.usage",
|
|
53340
|
+
"event.usage"
|
|
53341
|
+
];
|
|
53342
|
+
for (const key2 of candidateKeys) {
|
|
53343
|
+
const path = key2.split(".");
|
|
53344
|
+
let current = record2;
|
|
53345
|
+
for (const part of path) {
|
|
53346
|
+
const currentRecord = asRecord(current);
|
|
53347
|
+
current = currentRecord?.[part];
|
|
53348
|
+
}
|
|
53349
|
+
const usage = asRecord(current);
|
|
53350
|
+
if (usage) {
|
|
53351
|
+
candidates.push({ usage, path: prefix ? `${prefix}.${key2}` : key2 });
|
|
53352
|
+
}
|
|
53353
|
+
}
|
|
53354
|
+
const direct = normalizeUsageObject({
|
|
53355
|
+
usage: record2,
|
|
53356
|
+
source: "response_body",
|
|
53357
|
+
rawUsagePath: prefix || null
|
|
53358
|
+
});
|
|
53359
|
+
if (direct) {
|
|
53360
|
+
candidates.push({ usage: record2, path: prefix || "$" });
|
|
53361
|
+
}
|
|
53362
|
+
return candidates;
|
|
53363
|
+
}
|
|
53364
|
+
function mergeNumber(current, incoming, strategy = "max") {
|
|
53365
|
+
if (incoming === null)
|
|
53366
|
+
return current;
|
|
53367
|
+
if (current === null)
|
|
53368
|
+
return incoming;
|
|
53369
|
+
return strategy === "latest" ? incoming : Math.max(current, incoming);
|
|
53370
|
+
}
|
|
53371
|
+
function mergeTokenUsageMetrics(current, incoming) {
|
|
53372
|
+
if (!current)
|
|
53373
|
+
return incoming;
|
|
53374
|
+
if (!incoming)
|
|
53375
|
+
return current;
|
|
53376
|
+
const merged = {
|
|
53377
|
+
...current,
|
|
53378
|
+
source: incoming.source,
|
|
53379
|
+
providerStyle: current.providerStyle === "unknown" ? incoming.providerStyle : current.providerStyle,
|
|
53380
|
+
rawUsage: incoming.rawUsage ?? current.rawUsage,
|
|
53381
|
+
rawUsagePath: incoming.rawUsagePath ?? current.rawUsagePath,
|
|
53382
|
+
warnings: Array.from(new Set([...current.warnings, ...incoming.warnings]))
|
|
53383
|
+
};
|
|
53384
|
+
const numericKeys = [
|
|
53385
|
+
"inputTokens",
|
|
53386
|
+
"outputTokens",
|
|
53387
|
+
"totalTokens",
|
|
53388
|
+
"cachedInputTokens",
|
|
53389
|
+
"cacheHitInputTokens",
|
|
53390
|
+
"cacheHitRateDenominatorTokens",
|
|
53391
|
+
"cacheReadInputTokens",
|
|
53392
|
+
"cacheCreationInputTokens",
|
|
53393
|
+
"cacheCreationInputTokens5m",
|
|
53394
|
+
"cacheCreationInputTokens1h",
|
|
53395
|
+
"cacheWriteInputTokens",
|
|
53396
|
+
"cacheMissInputTokens",
|
|
53397
|
+
"reasoningTokens",
|
|
53398
|
+
"audioInputTokens",
|
|
53399
|
+
"audioOutputTokens",
|
|
53400
|
+
"textInputTokens",
|
|
53401
|
+
"textOutputTokens",
|
|
53402
|
+
"acceptedPredictionTokens",
|
|
53403
|
+
"rejectedPredictionTokens",
|
|
53404
|
+
"toolUsePromptTokens",
|
|
53405
|
+
"billableInputTokens",
|
|
53406
|
+
"billableOutputTokens",
|
|
53407
|
+
"creditUsage",
|
|
53408
|
+
"cost"
|
|
53409
|
+
];
|
|
53410
|
+
for (const key2 of numericKeys) {
|
|
53411
|
+
const value = mergeNumber(current[key2], incoming[key2], key2 === "cost" || key2 === "creditUsage" ? "latest" : "max");
|
|
53412
|
+
merged[key2] = value;
|
|
53413
|
+
}
|
|
53414
|
+
if (incoming.cacheHitRate !== null && (current.cacheHitRate === null || (incoming.cacheHitRateDenominatorTokens ?? 0) >= (current.cacheHitRateDenominatorTokens ?? 0))) {
|
|
53415
|
+
merged.cacheHitRate = incoming.cacheHitRate;
|
|
53416
|
+
merged.cacheHitRateFormula = incoming.cacheHitRateFormula;
|
|
53417
|
+
}
|
|
53418
|
+
if (merged.cacheHitInputTokens !== null && merged.cacheHitRateDenominatorTokens !== null) {
|
|
53419
|
+
merged.cacheHitRate = roundPercent(merged.cacheHitInputTokens, merged.cacheHitRateDenominatorTokens);
|
|
53420
|
+
}
|
|
53421
|
+
if (merged.totalTokens === null && merged.outputTokens !== null) {
|
|
53422
|
+
const effectiveInputTokens = merged.cacheHitRateDenominatorTokens ?? merged.inputTokens;
|
|
53423
|
+
if (effectiveInputTokens !== null) {
|
|
53424
|
+
merged.totalTokens = effectiveInputTokens + merged.outputTokens;
|
|
53425
|
+
merged.warnings.push(merged.cacheHitRateDenominatorTokens !== null ? "totalTokens \u7531 cacheHitRateDenominatorTokens + outputTokens \u63A8\u5BFC" : "totalTokens \u7531 inputTokens + outputTokens \u63A8\u5BFC");
|
|
53426
|
+
merged.warnings = Array.from(new Set(merged.warnings));
|
|
53427
|
+
}
|
|
53428
|
+
}
|
|
53429
|
+
return merged;
|
|
53430
|
+
}
|
|
53431
|
+
function toTokenUsageSummary(metrics) {
|
|
53432
|
+
const { rawUsage: _rawUsage, ...summary } = metrics;
|
|
53433
|
+
return summary;
|
|
53434
|
+
}
|
|
53435
|
+
function extractTokenUsageFromJson(value, options) {
|
|
53436
|
+
let merged = null;
|
|
53437
|
+
const candidates = collectUsageCandidates(value, options.rawUsagePathPrefix ?? "");
|
|
53438
|
+
for (const candidate of candidates) {
|
|
53439
|
+
const metrics = normalizeUsageObject({
|
|
53440
|
+
usage: candidate.usage,
|
|
53441
|
+
source: options.source,
|
|
53442
|
+
rawUsagePath: candidate.path,
|
|
53443
|
+
providerHint: options.providerHint
|
|
53444
|
+
});
|
|
53445
|
+
merged = mergeTokenUsageMetrics(merged, metrics);
|
|
53446
|
+
}
|
|
53447
|
+
return merged;
|
|
53448
|
+
}
|
|
53449
|
+
function extractTokenUsageFromResponseText(text2, source2 = "response_body", providerHint) {
|
|
53450
|
+
if (!text2?.trim())
|
|
53451
|
+
return null;
|
|
53452
|
+
try {
|
|
53453
|
+
return extractTokenUsageFromJson(JSON.parse(text2), { source: source2, providerHint });
|
|
53454
|
+
} catch {
|
|
53455
|
+
return null;
|
|
53456
|
+
}
|
|
53457
|
+
}
|
|
53458
|
+
function processSseMessage(dataLines, source2, providerHint) {
|
|
53459
|
+
if (dataLines.length === 0)
|
|
53460
|
+
return null;
|
|
53461
|
+
const data = dataLines.join(`
|
|
53462
|
+
`).trim();
|
|
53463
|
+
if (!data || data === "[DONE]")
|
|
53464
|
+
return null;
|
|
53465
|
+
try {
|
|
53466
|
+
return extractTokenUsageFromJson(JSON.parse(data), {
|
|
53467
|
+
source: source2,
|
|
53468
|
+
providerHint,
|
|
53469
|
+
rawUsagePathPrefix: source2 === "stream_file" ? "stream" : "stream"
|
|
53470
|
+
});
|
|
53471
|
+
} catch {
|
|
53472
|
+
return null;
|
|
53473
|
+
}
|
|
53474
|
+
}
|
|
53475
|
+
function extractTokenUsageFromSseText(text2, source2 = "stream_file", providerHint) {
|
|
53476
|
+
if (!text2?.trim())
|
|
53477
|
+
return null;
|
|
53478
|
+
let merged = null;
|
|
53479
|
+
let dataLines = [];
|
|
53480
|
+
const flush = () => {
|
|
53481
|
+
merged = mergeTokenUsageMetrics(merged, processSseMessage(dataLines, source2, providerHint));
|
|
53482
|
+
dataLines = [];
|
|
53483
|
+
};
|
|
53484
|
+
for (const rawLine of text2.split(/\r?\n/)) {
|
|
53485
|
+
if (rawLine === "") {
|
|
53486
|
+
flush();
|
|
53487
|
+
continue;
|
|
53488
|
+
}
|
|
53489
|
+
if (rawLine.startsWith("data:")) {
|
|
53490
|
+
dataLines.push(rawLine.slice(5).trimStart());
|
|
53491
|
+
}
|
|
53492
|
+
}
|
|
53493
|
+
flush();
|
|
53494
|
+
return merged;
|
|
53495
|
+
}
|
|
53496
|
+
function createTokenUsageStreamCollector(providerHint) {
|
|
53497
|
+
const decoder = new TextDecoder;
|
|
53498
|
+
let buffer2 = "";
|
|
53499
|
+
let dataLines = [];
|
|
53500
|
+
let latest = null;
|
|
53501
|
+
const flushMessage = () => {
|
|
53502
|
+
latest = mergeTokenUsageMetrics(latest, processSseMessage(dataLines, "stream_chunk", providerHint));
|
|
53503
|
+
dataLines = [];
|
|
53504
|
+
};
|
|
53505
|
+
const processLine = (rawLine) => {
|
|
53506
|
+
const line2 = rawLine.replace(/\r$/, "");
|
|
53507
|
+
if (line2 === "") {
|
|
53508
|
+
flushMessage();
|
|
53509
|
+
return;
|
|
53510
|
+
}
|
|
53511
|
+
if (line2.startsWith("data:")) {
|
|
53512
|
+
dataLines.push(line2.slice(5).trimStart());
|
|
53513
|
+
}
|
|
53514
|
+
};
|
|
53515
|
+
return {
|
|
53516
|
+
addChunk(chunk) {
|
|
53517
|
+
buffer2 += decoder.decode(chunk, { stream: true });
|
|
53518
|
+
let newlineIndex = buffer2.indexOf(`
|
|
53519
|
+
`);
|
|
53520
|
+
while (newlineIndex >= 0) {
|
|
53521
|
+
processLine(buffer2.slice(0, newlineIndex));
|
|
53522
|
+
buffer2 = buffer2.slice(newlineIndex + 1);
|
|
53523
|
+
newlineIndex = buffer2.indexOf(`
|
|
53524
|
+
`);
|
|
53525
|
+
}
|
|
53526
|
+
},
|
|
53527
|
+
getUsage() {
|
|
53528
|
+
buffer2 += decoder.decode();
|
|
53529
|
+
if (buffer2) {
|
|
53530
|
+
processLine(buffer2);
|
|
53531
|
+
buffer2 = "";
|
|
53532
|
+
}
|
|
53533
|
+
flushMessage();
|
|
53534
|
+
return latest;
|
|
53535
|
+
}
|
|
53536
|
+
};
|
|
53537
|
+
}
|
|
53538
|
+
function safeReadStreamFile(streamFile, baseDir) {
|
|
53539
|
+
if (!streamFile)
|
|
53540
|
+
return { content: null, warning: null };
|
|
53541
|
+
try {
|
|
53542
|
+
const candidates = [streamFile];
|
|
53543
|
+
if (baseDir)
|
|
53544
|
+
candidates.push(resolve4(baseDir, streamFile));
|
|
53545
|
+
for (const candidate of candidates) {
|
|
53546
|
+
const resolved = resolve4(candidate);
|
|
53547
|
+
if (!resolved.endsWith(".sse.raw"))
|
|
53548
|
+
continue;
|
|
53549
|
+
if (!existsSync3(resolved))
|
|
53550
|
+
continue;
|
|
53551
|
+
const stats = statSync(resolved);
|
|
53552
|
+
if (stats.size > MAX_STREAM_USAGE_BYTES) {
|
|
53553
|
+
return {
|
|
53554
|
+
content: null,
|
|
53555
|
+
warning: `stream_file \u8D85\u8FC7 ${MAX_STREAM_USAGE_BYTES} \u5B57\u8282\uFF0C\u5DF2\u8DF3\u8FC7 token usage \u56DE\u586B`
|
|
53556
|
+
};
|
|
53557
|
+
}
|
|
53558
|
+
return { content: readFileSync4(resolved, "utf-8"), warning: null };
|
|
53559
|
+
}
|
|
53560
|
+
} catch (err) {
|
|
53561
|
+
return {
|
|
53562
|
+
content: null,
|
|
53563
|
+
warning: `stream_file token usage \u8BFB\u53D6\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
|
|
53564
|
+
};
|
|
53565
|
+
}
|
|
53566
|
+
return { content: null, warning: null };
|
|
53567
|
+
}
|
|
53568
|
+
function extractTokenUsageFromLogEvent(event, options = {}) {
|
|
53569
|
+
if (event.token_usage) {
|
|
53570
|
+
return {
|
|
53571
|
+
rawUsage: null,
|
|
53572
|
+
...event.token_usage,
|
|
53573
|
+
source: event.token_usage.source ?? "explicit"
|
|
53574
|
+
};
|
|
53575
|
+
}
|
|
53576
|
+
const providerHint = [event.provider, event.route_type, event.model_in, event.model_out].filter(Boolean).join(" ");
|
|
53577
|
+
const responseBodyUsage = extractTokenUsageFromResponseText(event.response_body, "response_body", providerHint);
|
|
53578
|
+
if (responseBodyUsage)
|
|
53579
|
+
return responseBodyUsage;
|
|
53580
|
+
const responseAfterPluginsUsage = extractTokenUsageFromResponseText(event.response_body_after_plugins, "response_body_after_plugins", providerHint);
|
|
53581
|
+
if (responseAfterPluginsUsage)
|
|
53582
|
+
return responseAfterPluginsUsage;
|
|
53583
|
+
const responseBeforePluginsUsage = extractTokenUsageFromResponseText(event.response_body_before_plugins, "response_body_before_plugins", providerHint);
|
|
53584
|
+
if (responseBeforePluginsUsage)
|
|
53585
|
+
return responseBeforePluginsUsage;
|
|
53586
|
+
const streamContent = options.streamContent ?? safeReadStreamFile(event.stream_file, options.baseDir).content;
|
|
53587
|
+
return extractTokenUsageFromSseText(streamContent ?? undefined, "stream_file", providerHint);
|
|
53588
|
+
}
|
|
53589
|
+
function extractTokenUsageSummaryFromLogEvent(event, options = {}) {
|
|
53590
|
+
const metrics = extractTokenUsageFromLogEvent(event, options);
|
|
53591
|
+
return metrics ? toTokenUsageSummary(metrics) : null;
|
|
53592
|
+
}
|
|
53593
|
+
function enrichLogEventTokenUsage(event, options = {}) {
|
|
53594
|
+
if (event.token_usage)
|
|
53595
|
+
return event;
|
|
53596
|
+
const tokenUsage = extractTokenUsageFromLogEvent(event, options);
|
|
53597
|
+
if (!tokenUsage)
|
|
53598
|
+
return event;
|
|
53599
|
+
return {
|
|
53600
|
+
...event,
|
|
53601
|
+
token_usage: tokenUsage
|
|
53602
|
+
};
|
|
53603
|
+
}
|
|
53604
|
+
|
|
53025
53605
|
// src/log-index.ts
|
|
53026
|
-
var SCHEMA_VERSION2 =
|
|
53606
|
+
var SCHEMA_VERSION2 = 3;
|
|
53027
53607
|
var MAX_INDEX_QUEUE = 20000;
|
|
53028
53608
|
var INDEX_BATCH_SIZE = 250;
|
|
53029
53609
|
var INDEX_FLUSH_DELAY_MS = 50;
|
|
@@ -53167,6 +53747,7 @@ function eventToRow(input) {
|
|
|
53167
53747
|
const statusClass = getStatusClass2(event);
|
|
53168
53748
|
const latencyMs = Math.max(0, event.latency_ms ?? 0);
|
|
53169
53749
|
const model = event.model_out || event.model_in;
|
|
53750
|
+
const tokenUsage = extractTokenUsageSummaryFromLogEvent(event, { baseDir: input.baseDir });
|
|
53170
53751
|
return {
|
|
53171
53752
|
id: input.id,
|
|
53172
53753
|
ts_ms: tsMs,
|
|
@@ -53194,8 +53775,46 @@ function eventToRow(input) {
|
|
|
53194
53775
|
line_number: input.lineNumber,
|
|
53195
53776
|
byte_offset: input.offset,
|
|
53196
53777
|
byte_length: input.byteLength,
|
|
53197
|
-
search_text: buildSearchText(event)
|
|
53198
|
-
|
|
53778
|
+
search_text: buildSearchText(event),
|
|
53779
|
+
token_input: tokenUsage?.inputTokens ?? null,
|
|
53780
|
+
token_output: tokenUsage?.outputTokens ?? null,
|
|
53781
|
+
token_total: tokenUsage?.totalTokens ?? null,
|
|
53782
|
+
token_cached_input: tokenUsage?.cachedInputTokens ?? null,
|
|
53783
|
+
token_cache_hit_input: tokenUsage?.cacheHitInputTokens ?? null,
|
|
53784
|
+
token_cache_hit_rate: tokenUsage?.cacheHitRate ?? null,
|
|
53785
|
+
token_cache_hit_rate_denominator: tokenUsage?.cacheHitRateDenominatorTokens ?? null,
|
|
53786
|
+
token_cache_read_input: tokenUsage?.cacheReadInputTokens ?? null,
|
|
53787
|
+
token_cache_creation_input: tokenUsage?.cacheCreationInputTokens ?? null,
|
|
53788
|
+
token_cache_creation_input_5m: tokenUsage?.cacheCreationInputTokens5m ?? null,
|
|
53789
|
+
token_cache_creation_input_1h: tokenUsage?.cacheCreationInputTokens1h ?? null,
|
|
53790
|
+
token_cache_write_input: tokenUsage?.cacheWriteInputTokens ?? null,
|
|
53791
|
+
token_cache_miss_input: tokenUsage?.cacheMissInputTokens ?? null,
|
|
53792
|
+
token_reasoning: tokenUsage?.reasoningTokens ?? null,
|
|
53793
|
+
token_audio_input: tokenUsage?.audioInputTokens ?? null,
|
|
53794
|
+
token_audio_output: tokenUsage?.audioOutputTokens ?? null,
|
|
53795
|
+
token_text_input: tokenUsage?.textInputTokens ?? null,
|
|
53796
|
+
token_text_output: tokenUsage?.textOutputTokens ?? null,
|
|
53797
|
+
token_accepted_prediction: tokenUsage?.acceptedPredictionTokens ?? null,
|
|
53798
|
+
token_rejected_prediction: tokenUsage?.rejectedPredictionTokens ?? null,
|
|
53799
|
+
token_tool_use_prompt: tokenUsage?.toolUsePromptTokens ?? null,
|
|
53800
|
+
token_billable_input: tokenUsage?.billableInputTokens ?? null,
|
|
53801
|
+
token_billable_output: tokenUsage?.billableOutputTokens ?? null,
|
|
53802
|
+
token_credit_usage: tokenUsage?.creditUsage ?? null,
|
|
53803
|
+
token_cost: tokenUsage?.cost ?? null,
|
|
53804
|
+
token_source: tokenUsage?.source ?? null,
|
|
53805
|
+
token_provider_style: tokenUsage?.providerStyle ?? null,
|
|
53806
|
+
token_raw_usage_path: tokenUsage?.rawUsagePath ?? null,
|
|
53807
|
+
token_usage_json: tokenUsage ? JSON.stringify(tokenUsage) : null
|
|
53808
|
+
};
|
|
53809
|
+
}
|
|
53810
|
+
function parseTokenUsageSummary(value) {
|
|
53811
|
+
if (!value)
|
|
53812
|
+
return null;
|
|
53813
|
+
try {
|
|
53814
|
+
return JSON.parse(value);
|
|
53815
|
+
} catch {
|
|
53816
|
+
return null;
|
|
53817
|
+
}
|
|
53199
53818
|
}
|
|
53200
53819
|
function rowToSummary(row) {
|
|
53201
53820
|
return {
|
|
@@ -53218,7 +53837,8 @@ function rowToSummary(row) {
|
|
|
53218
53837
|
hasMetadata: row.has_metadata === 1,
|
|
53219
53838
|
userIdRaw: row.user_id_raw,
|
|
53220
53839
|
userKey: row.user_key,
|
|
53221
|
-
sessionId: row.session_id
|
|
53840
|
+
sessionId: row.session_id,
|
|
53841
|
+
tokenUsage: parseTokenUsageSummary(row.token_usage_json)
|
|
53222
53842
|
};
|
|
53223
53843
|
}
|
|
53224
53844
|
async function* readJsonlLinesWithOffsets(filePath) {
|
|
@@ -53288,7 +53908,22 @@ function createEmptyStats() {
|
|
|
53288
53908
|
errorCount: 0,
|
|
53289
53909
|
errorRate: 0,
|
|
53290
53910
|
avgLatencyMs: 0,
|
|
53291
|
-
p95LatencyMs: 0
|
|
53911
|
+
p95LatencyMs: 0,
|
|
53912
|
+
tokenUsageCount: 0,
|
|
53913
|
+
inputTokens: 0,
|
|
53914
|
+
outputTokens: 0,
|
|
53915
|
+
totalTokens: 0,
|
|
53916
|
+
cachedInputTokens: 0,
|
|
53917
|
+
cacheHitInputTokens: 0,
|
|
53918
|
+
cacheHitRate: 0,
|
|
53919
|
+
cacheHitRateDenominatorTokens: 0,
|
|
53920
|
+
cacheReadInputTokens: 0,
|
|
53921
|
+
cacheCreationInputTokens: 0,
|
|
53922
|
+
cacheWriteInputTokens: 0,
|
|
53923
|
+
cacheMissInputTokens: 0,
|
|
53924
|
+
reasoningTokens: 0,
|
|
53925
|
+
billableInputTokens: 0,
|
|
53926
|
+
billableOutputTokens: 0
|
|
53292
53927
|
};
|
|
53293
53928
|
}
|
|
53294
53929
|
function createEmptyQueryResult(query, meta3 = {}) {
|
|
@@ -53381,12 +54016,28 @@ class LogIndex {
|
|
|
53381
54016
|
id, ts_ms, ts_start, level, provider, route_type, model, model_in, model_out,
|
|
53382
54017
|
path, request_id, latency_ms, upstream_status, status_class, has_error,
|
|
53383
54018
|
message, error_type, has_metadata, user_id_raw, user_key, session_id,
|
|
53384
|
-
source_date, source_file, line_number, byte_offset, byte_length, search_text
|
|
54019
|
+
source_date, source_file, line_number, byte_offset, byte_length, search_text,
|
|
54020
|
+
token_input, token_output, token_total, token_cached_input, token_cache_hit_input,
|
|
54021
|
+
token_cache_hit_rate, token_cache_hit_rate_denominator, token_cache_read_input,
|
|
54022
|
+
token_cache_creation_input, token_cache_creation_input_5m, token_cache_creation_input_1h,
|
|
54023
|
+
token_cache_write_input, token_cache_miss_input, token_reasoning, token_audio_input,
|
|
54024
|
+
token_audio_output, token_text_input, token_text_output, token_accepted_prediction,
|
|
54025
|
+
token_rejected_prediction, token_tool_use_prompt, token_billable_input,
|
|
54026
|
+
token_billable_output, token_credit_usage, token_cost, token_source,
|
|
54027
|
+
token_provider_style, token_raw_usage_path, token_usage_json
|
|
53385
54028
|
) VALUES (
|
|
53386
54029
|
$id, $ts_ms, $ts_start, $level, $provider, $route_type, $model, $model_in, $model_out,
|
|
53387
54030
|
$path, $request_id, $latency_ms, $upstream_status, $status_class, $has_error,
|
|
53388
54031
|
$message, $error_type, $has_metadata, $user_id_raw, $user_key, $session_id,
|
|
53389
|
-
$source_date, $source_file, $line_number, $byte_offset, $byte_length, $search_text
|
|
54032
|
+
$source_date, $source_file, $line_number, $byte_offset, $byte_length, $search_text,
|
|
54033
|
+
$token_input, $token_output, $token_total, $token_cached_input, $token_cache_hit_input,
|
|
54034
|
+
$token_cache_hit_rate, $token_cache_hit_rate_denominator, $token_cache_read_input,
|
|
54035
|
+
$token_cache_creation_input, $token_cache_creation_input_5m, $token_cache_creation_input_1h,
|
|
54036
|
+
$token_cache_write_input, $token_cache_miss_input, $token_reasoning, $token_audio_input,
|
|
54037
|
+
$token_audio_output, $token_text_input, $token_text_output, $token_accepted_prediction,
|
|
54038
|
+
$token_rejected_prediction, $token_tool_use_prompt, $token_billable_input,
|
|
54039
|
+
$token_billable_output, $token_credit_usage, $token_cost, $token_source,
|
|
54040
|
+
$token_provider_style, $token_raw_usage_path, $token_usage_json
|
|
53390
54041
|
)
|
|
53391
54042
|
`);
|
|
53392
54043
|
this.deleteFtsStmt = this.db.prepare("DELETE FROM log_events_fts WHERE event_id = ?");
|
|
@@ -53436,9 +54087,9 @@ class LogIndex {
|
|
|
53436
54087
|
const dates = listDateStrings2(fromMs, toMs);
|
|
53437
54088
|
for (const date5 of dates) {
|
|
53438
54089
|
const filePath = join5(eventsDir, `${date5}.jsonl`);
|
|
53439
|
-
if (!
|
|
54090
|
+
if (!existsSync4(filePath))
|
|
53440
54091
|
continue;
|
|
53441
|
-
const stats =
|
|
54092
|
+
const stats = statSync2(filePath);
|
|
53442
54093
|
const fileRow = this.db.query("SELECT size_bytes, mtime_ms FROM log_index_files WHERE file_path = ?").get(filePath);
|
|
53443
54094
|
const sizeBytes = stats.size;
|
|
53444
54095
|
const mtimeMs = Math.trunc(stats.mtimeMs);
|
|
@@ -53473,7 +54124,7 @@ class LogIndex {
|
|
|
53473
54124
|
e.id, e.ts_start, e.level, e.provider, e.route_type, e.model, e.model_in,
|
|
53474
54125
|
e.model_out, e.path, e.request_id, e.latency_ms, e.upstream_status,
|
|
53475
54126
|
e.status_class, e.has_error, e.message, e.error_type, e.has_metadata,
|
|
53476
|
-
e.user_id_raw, e.user_key, e.session_id, e.ts_ms
|
|
54127
|
+
e.user_id_raw, e.user_key, e.session_id, e.ts_ms, e.token_usage_json
|
|
53477
54128
|
FROM log_events e
|
|
53478
54129
|
${whereSql}
|
|
53479
54130
|
${cursorClause}
|
|
@@ -53528,7 +54179,7 @@ class LogIndex {
|
|
|
53528
54179
|
return null;
|
|
53529
54180
|
const row = this.db.query("SELECT source_date, source_file, line_number, byte_offset FROM log_events WHERE id = ?").get(id);
|
|
53530
54181
|
const filePath = row?.source_file ?? join5(this.baseDir, "events", `${parsedId.date}.jsonl`);
|
|
53531
|
-
if (!
|
|
54182
|
+
if (!existsSync4(filePath))
|
|
53532
54183
|
return null;
|
|
53533
54184
|
const line2 = readLineAtOffset(filePath, row?.byte_offset ?? parsedId.offset);
|
|
53534
54185
|
if (!line2?.trim())
|
|
@@ -53596,7 +54247,36 @@ class LogIndex {
|
|
|
53596
54247
|
line_number INTEGER,
|
|
53597
54248
|
byte_offset INTEGER NOT NULL,
|
|
53598
54249
|
byte_length INTEGER NOT NULL,
|
|
53599
|
-
search_text TEXT NOT NULL
|
|
54250
|
+
search_text TEXT NOT NULL,
|
|
54251
|
+
token_input INTEGER,
|
|
54252
|
+
token_output INTEGER,
|
|
54253
|
+
token_total INTEGER,
|
|
54254
|
+
token_cached_input INTEGER,
|
|
54255
|
+
token_cache_hit_input INTEGER,
|
|
54256
|
+
token_cache_hit_rate REAL,
|
|
54257
|
+
token_cache_hit_rate_denominator INTEGER,
|
|
54258
|
+
token_cache_read_input INTEGER,
|
|
54259
|
+
token_cache_creation_input INTEGER,
|
|
54260
|
+
token_cache_creation_input_5m INTEGER,
|
|
54261
|
+
token_cache_creation_input_1h INTEGER,
|
|
54262
|
+
token_cache_write_input INTEGER,
|
|
54263
|
+
token_cache_miss_input INTEGER,
|
|
54264
|
+
token_reasoning INTEGER,
|
|
54265
|
+
token_audio_input INTEGER,
|
|
54266
|
+
token_audio_output INTEGER,
|
|
54267
|
+
token_text_input INTEGER,
|
|
54268
|
+
token_text_output INTEGER,
|
|
54269
|
+
token_accepted_prediction INTEGER,
|
|
54270
|
+
token_rejected_prediction INTEGER,
|
|
54271
|
+
token_tool_use_prompt INTEGER,
|
|
54272
|
+
token_billable_input INTEGER,
|
|
54273
|
+
token_billable_output INTEGER,
|
|
54274
|
+
token_credit_usage REAL,
|
|
54275
|
+
token_cost REAL,
|
|
54276
|
+
token_source TEXT,
|
|
54277
|
+
token_provider_style TEXT,
|
|
54278
|
+
token_raw_usage_path TEXT,
|
|
54279
|
+
token_usage_json TEXT
|
|
53600
54280
|
);
|
|
53601
54281
|
|
|
53602
54282
|
CREATE VIRTUAL TABLE IF NOT EXISTS log_events_fts
|
|
@@ -53614,12 +54294,67 @@ class LogIndex {
|
|
|
53614
54294
|
CREATE INDEX IF NOT EXISTS idx_log_events_session_time ON log_events(session_id, ts_ms DESC);
|
|
53615
54295
|
CREATE INDEX IF NOT EXISTS idx_log_events_file ON log_events(source_file);
|
|
53616
54296
|
`);
|
|
54297
|
+
this.ensureTokenColumns();
|
|
54298
|
+
this.db.exec(`
|
|
54299
|
+
CREATE INDEX IF NOT EXISTS idx_log_events_token_total_time ON log_events(token_total, ts_ms DESC);
|
|
54300
|
+
CREATE INDEX IF NOT EXISTS idx_log_events_token_input_time ON log_events(token_input, ts_ms DESC);
|
|
54301
|
+
CREATE INDEX IF NOT EXISTS idx_log_events_token_cache_hit_rate_time ON log_events(token_cache_hit_rate, ts_ms DESC);
|
|
54302
|
+
`);
|
|
54303
|
+
const versionRow = this.db.query("SELECT value FROM log_index_meta WHERE key = 'schema_version'").get();
|
|
54304
|
+
const previousVersion = Number.parseInt(versionRow?.value ?? "0", 10) || 0;
|
|
54305
|
+
if (previousVersion > 0 && previousVersion < SCHEMA_VERSION2) {
|
|
54306
|
+
this.db.exec(`
|
|
54307
|
+
DELETE FROM log_events_fts;
|
|
54308
|
+
DELETE FROM log_events;
|
|
54309
|
+
DELETE FROM log_index_files;
|
|
54310
|
+
`);
|
|
54311
|
+
}
|
|
53617
54312
|
this.db.prepare(`
|
|
53618
54313
|
INSERT INTO log_index_meta(key, value)
|
|
53619
54314
|
VALUES ('schema_version', ?)
|
|
53620
54315
|
ON CONFLICT(key) DO UPDATE SET value = excluded.value
|
|
53621
54316
|
`).run(String(SCHEMA_VERSION2));
|
|
53622
54317
|
}
|
|
54318
|
+
ensureTokenColumns() {
|
|
54319
|
+
const rows = this.db.query("PRAGMA table_info(log_events)").all();
|
|
54320
|
+
const existing = new Set(rows.map((row) => row.name));
|
|
54321
|
+
const columns = [
|
|
54322
|
+
{ name: "token_input", type: "INTEGER" },
|
|
54323
|
+
{ name: "token_output", type: "INTEGER" },
|
|
54324
|
+
{ name: "token_total", type: "INTEGER" },
|
|
54325
|
+
{ name: "token_cached_input", type: "INTEGER" },
|
|
54326
|
+
{ name: "token_cache_hit_input", type: "INTEGER" },
|
|
54327
|
+
{ name: "token_cache_hit_rate", type: "REAL" },
|
|
54328
|
+
{ name: "token_cache_hit_rate_denominator", type: "INTEGER" },
|
|
54329
|
+
{ name: "token_cache_read_input", type: "INTEGER" },
|
|
54330
|
+
{ name: "token_cache_creation_input", type: "INTEGER" },
|
|
54331
|
+
{ name: "token_cache_creation_input_5m", type: "INTEGER" },
|
|
54332
|
+
{ name: "token_cache_creation_input_1h", type: "INTEGER" },
|
|
54333
|
+
{ name: "token_cache_write_input", type: "INTEGER" },
|
|
54334
|
+
{ name: "token_cache_miss_input", type: "INTEGER" },
|
|
54335
|
+
{ name: "token_reasoning", type: "INTEGER" },
|
|
54336
|
+
{ name: "token_audio_input", type: "INTEGER" },
|
|
54337
|
+
{ name: "token_audio_output", type: "INTEGER" },
|
|
54338
|
+
{ name: "token_text_input", type: "INTEGER" },
|
|
54339
|
+
{ name: "token_text_output", type: "INTEGER" },
|
|
54340
|
+
{ name: "token_accepted_prediction", type: "INTEGER" },
|
|
54341
|
+
{ name: "token_rejected_prediction", type: "INTEGER" },
|
|
54342
|
+
{ name: "token_tool_use_prompt", type: "INTEGER" },
|
|
54343
|
+
{ name: "token_billable_input", type: "INTEGER" },
|
|
54344
|
+
{ name: "token_billable_output", type: "INTEGER" },
|
|
54345
|
+
{ name: "token_credit_usage", type: "REAL" },
|
|
54346
|
+
{ name: "token_cost", type: "REAL" },
|
|
54347
|
+
{ name: "token_source", type: "TEXT" },
|
|
54348
|
+
{ name: "token_provider_style", type: "TEXT" },
|
|
54349
|
+
{ name: "token_raw_usage_path", type: "TEXT" },
|
|
54350
|
+
{ name: "token_usage_json", type: "TEXT" }
|
|
54351
|
+
];
|
|
54352
|
+
for (const column2 of columns) {
|
|
54353
|
+
if (existing.has(column2.name))
|
|
54354
|
+
continue;
|
|
54355
|
+
this.db.exec(`ALTER TABLE log_events ADD COLUMN ${column2.name} ${column2.type}`);
|
|
54356
|
+
}
|
|
54357
|
+
}
|
|
53623
54358
|
flushQueue() {
|
|
53624
54359
|
if (this.queue.length === 0 || this.disposed)
|
|
53625
54360
|
return;
|
|
@@ -53647,6 +54382,7 @@ class LogIndex {
|
|
|
53647
54382
|
return;
|
|
53648
54383
|
const id = encodeOffsetLogEventId(item.date, item.offset);
|
|
53649
54384
|
const row = eventToRow({
|
|
54385
|
+
baseDir: this.baseDir,
|
|
53650
54386
|
id,
|
|
53651
54387
|
date: item.date,
|
|
53652
54388
|
filePath: item.filePath,
|
|
@@ -53662,7 +54398,7 @@ class LogIndex {
|
|
|
53662
54398
|
this.insertFtsStmt.run(id, row.search_text);
|
|
53663
54399
|
if (!this.dirtyFiles.has(item.filePath)) {
|
|
53664
54400
|
try {
|
|
53665
|
-
const stats =
|
|
54401
|
+
const stats = statSync2(item.filePath);
|
|
53666
54402
|
const indexedThrough = item.offset + item.byteLength;
|
|
53667
54403
|
this.upsertFileStmt.run(item.filePath, item.date, Math.min(indexedThrough, stats.size), Math.trunc(stats.mtimeMs), Date.now());
|
|
53668
54404
|
} catch {}
|
|
@@ -53689,6 +54425,7 @@ class LogIndex {
|
|
|
53689
54425
|
continue;
|
|
53690
54426
|
}
|
|
53691
54427
|
const row = eventToRow({
|
|
54428
|
+
baseDir: this.baseDir,
|
|
53692
54429
|
id: encodeOffsetLogEventId(date5, item.offset),
|
|
53693
54430
|
date: date5,
|
|
53694
54431
|
filePath,
|
|
@@ -53721,7 +54458,21 @@ class LogIndex {
|
|
|
53721
54458
|
SELECT
|
|
53722
54459
|
COUNT(*) AS total,
|
|
53723
54460
|
COALESCE(SUM(has_error), 0) AS errorCount,
|
|
53724
|
-
COALESCE(AVG(latency_ms), 0) AS avgLatencyMs
|
|
54461
|
+
COALESCE(AVG(latency_ms), 0) AS avgLatencyMs,
|
|
54462
|
+
COALESCE(SUM(CASE WHEN token_usage_json IS NOT NULL THEN 1 ELSE 0 END), 0) AS tokenUsageCount,
|
|
54463
|
+
COALESCE(SUM(token_input), 0) AS inputTokens,
|
|
54464
|
+
COALESCE(SUM(token_output), 0) AS outputTokens,
|
|
54465
|
+
COALESCE(SUM(token_total), 0) AS totalTokens,
|
|
54466
|
+
COALESCE(SUM(token_cached_input), 0) AS cachedInputTokens,
|
|
54467
|
+
COALESCE(SUM(token_cache_hit_input), 0) AS cacheHitInputTokens,
|
|
54468
|
+
COALESCE(SUM(token_cache_hit_rate_denominator), 0) AS cacheHitRateDenominatorTokens,
|
|
54469
|
+
COALESCE(SUM(token_cache_read_input), 0) AS cacheReadInputTokens,
|
|
54470
|
+
COALESCE(SUM(token_cache_creation_input), 0) AS cacheCreationInputTokens,
|
|
54471
|
+
COALESCE(SUM(token_cache_write_input), 0) AS cacheWriteInputTokens,
|
|
54472
|
+
COALESCE(SUM(token_cache_miss_input), 0) AS cacheMissInputTokens,
|
|
54473
|
+
COALESCE(SUM(token_reasoning), 0) AS reasoningTokens,
|
|
54474
|
+
COALESCE(SUM(token_billable_input), 0) AS billableInputTokens,
|
|
54475
|
+
COALESCE(SUM(token_billable_output), 0) AS billableOutputTokens
|
|
53725
54476
|
FROM log_events e
|
|
53726
54477
|
${whereSql}
|
|
53727
54478
|
`).get(...params);
|
|
@@ -53742,7 +54493,22 @@ class LogIndex {
|
|
|
53742
54493
|
errorCount,
|
|
53743
54494
|
errorRate: toPercent2(errorCount, total),
|
|
53744
54495
|
avgLatencyMs: Math.round(Number(aggregate.avgLatencyMs) || 0),
|
|
53745
|
-
p95LatencyMs: Math.round(p95Row?.latency_ms ?? 0)
|
|
54496
|
+
p95LatencyMs: Math.round(p95Row?.latency_ms ?? 0),
|
|
54497
|
+
tokenUsageCount: Number(aggregate.tokenUsageCount) || 0,
|
|
54498
|
+
inputTokens: Number(aggregate.inputTokens) || 0,
|
|
54499
|
+
outputTokens: Number(aggregate.outputTokens) || 0,
|
|
54500
|
+
totalTokens: Number(aggregate.totalTokens) || 0,
|
|
54501
|
+
cachedInputTokens: Number(aggregate.cachedInputTokens) || 0,
|
|
54502
|
+
cacheHitInputTokens: Number(aggregate.cacheHitInputTokens) || 0,
|
|
54503
|
+
cacheHitRate: toPercent2(Number(aggregate.cacheHitInputTokens) || 0, Number(aggregate.cacheHitRateDenominatorTokens) || 0),
|
|
54504
|
+
cacheHitRateDenominatorTokens: Number(aggregate.cacheHitRateDenominatorTokens) || 0,
|
|
54505
|
+
cacheReadInputTokens: Number(aggregate.cacheReadInputTokens) || 0,
|
|
54506
|
+
cacheCreationInputTokens: Number(aggregate.cacheCreationInputTokens) || 0,
|
|
54507
|
+
cacheWriteInputTokens: Number(aggregate.cacheWriteInputTokens) || 0,
|
|
54508
|
+
cacheMissInputTokens: Number(aggregate.cacheMissInputTokens) || 0,
|
|
54509
|
+
reasoningTokens: Number(aggregate.reasoningTokens) || 0,
|
|
54510
|
+
billableInputTokens: Number(aggregate.billableInputTokens) || 0,
|
|
54511
|
+
billableOutputTokens: Number(aggregate.billableOutputTokens) || 0
|
|
53746
54512
|
};
|
|
53747
54513
|
}
|
|
53748
54514
|
}
|
|
@@ -53823,7 +54589,7 @@ function getIndexedLogEventDetail(logConfig, id) {
|
|
|
53823
54589
|
if (!parsedId)
|
|
53824
54590
|
return null;
|
|
53825
54591
|
const filePath = join5(baseDir, "events", `${parsedId.date}.jsonl`);
|
|
53826
|
-
if (!
|
|
54592
|
+
if (!existsSync4(filePath))
|
|
53827
54593
|
return null;
|
|
53828
54594
|
const line2 = readLineAtOffset(filePath, parsedId.offset);
|
|
53829
54595
|
if (!line2?.trim())
|
|
@@ -53956,8 +54722,25 @@ function createRunningStats() {
|
|
|
53956
54722
|
total: 0,
|
|
53957
54723
|
errorCount: 0,
|
|
53958
54724
|
latencySum: 0,
|
|
53959
|
-
latencyCounts: new Map
|
|
53960
|
-
|
|
54725
|
+
latencyCounts: new Map,
|
|
54726
|
+
tokenUsageCount: 0,
|
|
54727
|
+
inputTokens: 0,
|
|
54728
|
+
outputTokens: 0,
|
|
54729
|
+
totalTokens: 0,
|
|
54730
|
+
cachedInputTokens: 0,
|
|
54731
|
+
cacheHitInputTokens: 0,
|
|
54732
|
+
cacheHitRateDenominatorTokens: 0,
|
|
54733
|
+
cacheReadInputTokens: 0,
|
|
54734
|
+
cacheCreationInputTokens: 0,
|
|
54735
|
+
cacheWriteInputTokens: 0,
|
|
54736
|
+
cacheMissInputTokens: 0,
|
|
54737
|
+
reasoningTokens: 0,
|
|
54738
|
+
billableInputTokens: 0,
|
|
54739
|
+
billableOutputTokens: 0
|
|
54740
|
+
};
|
|
54741
|
+
}
|
|
54742
|
+
function addNullable(total, value) {
|
|
54743
|
+
return total + (value ?? 0);
|
|
53961
54744
|
}
|
|
53962
54745
|
function updateRunningStats(stats, item) {
|
|
53963
54746
|
stats.total += 1;
|
|
@@ -53968,6 +54751,23 @@ function updateRunningStats(stats, item) {
|
|
|
53968
54751
|
stats.latencySum += latency;
|
|
53969
54752
|
const roundedLatency = Math.round(latency);
|
|
53970
54753
|
stats.latencyCounts.set(roundedLatency, (stats.latencyCounts.get(roundedLatency) ?? 0) + 1);
|
|
54754
|
+
const usage = item.tokenUsage;
|
|
54755
|
+
if (!usage)
|
|
54756
|
+
return;
|
|
54757
|
+
stats.tokenUsageCount += 1;
|
|
54758
|
+
stats.inputTokens = addNullable(stats.inputTokens, usage.inputTokens);
|
|
54759
|
+
stats.outputTokens = addNullable(stats.outputTokens, usage.outputTokens);
|
|
54760
|
+
stats.totalTokens = addNullable(stats.totalTokens, usage.totalTokens);
|
|
54761
|
+
stats.cachedInputTokens = addNullable(stats.cachedInputTokens, usage.cachedInputTokens);
|
|
54762
|
+
stats.cacheHitInputTokens = addNullable(stats.cacheHitInputTokens, usage.cacheHitInputTokens);
|
|
54763
|
+
stats.cacheHitRateDenominatorTokens = addNullable(stats.cacheHitRateDenominatorTokens, usage.cacheHitRateDenominatorTokens);
|
|
54764
|
+
stats.cacheReadInputTokens = addNullable(stats.cacheReadInputTokens, usage.cacheReadInputTokens);
|
|
54765
|
+
stats.cacheCreationInputTokens = addNullable(stats.cacheCreationInputTokens, usage.cacheCreationInputTokens);
|
|
54766
|
+
stats.cacheWriteInputTokens = addNullable(stats.cacheWriteInputTokens, usage.cacheWriteInputTokens);
|
|
54767
|
+
stats.cacheMissInputTokens = addNullable(stats.cacheMissInputTokens, usage.cacheMissInputTokens);
|
|
54768
|
+
stats.reasoningTokens = addNullable(stats.reasoningTokens, usage.reasoningTokens);
|
|
54769
|
+
stats.billableInputTokens = addNullable(stats.billableInputTokens, usage.billableInputTokens);
|
|
54770
|
+
stats.billableOutputTokens = addNullable(stats.billableOutputTokens, usage.billableOutputTokens);
|
|
53971
54771
|
}
|
|
53972
54772
|
function percentileFromCounts(latencyCounts, total, ratio) {
|
|
53973
54773
|
if (total <= 0 || latencyCounts.size === 0)
|
|
@@ -53990,7 +54790,22 @@ function finalizeStats(stats) {
|
|
|
53990
54790
|
errorCount: 0,
|
|
53991
54791
|
errorRate: 0,
|
|
53992
54792
|
avgLatencyMs: 0,
|
|
53993
|
-
p95LatencyMs: 0
|
|
54793
|
+
p95LatencyMs: 0,
|
|
54794
|
+
tokenUsageCount: 0,
|
|
54795
|
+
inputTokens: 0,
|
|
54796
|
+
outputTokens: 0,
|
|
54797
|
+
totalTokens: 0,
|
|
54798
|
+
cachedInputTokens: 0,
|
|
54799
|
+
cacheHitInputTokens: 0,
|
|
54800
|
+
cacheHitRate: 0,
|
|
54801
|
+
cacheHitRateDenominatorTokens: 0,
|
|
54802
|
+
cacheReadInputTokens: 0,
|
|
54803
|
+
cacheCreationInputTokens: 0,
|
|
54804
|
+
cacheWriteInputTokens: 0,
|
|
54805
|
+
cacheMissInputTokens: 0,
|
|
54806
|
+
reasoningTokens: 0,
|
|
54807
|
+
billableInputTokens: 0,
|
|
54808
|
+
billableOutputTokens: 0
|
|
53994
54809
|
};
|
|
53995
54810
|
}
|
|
53996
54811
|
return {
|
|
@@ -53998,8 +54813,26 @@ function finalizeStats(stats) {
|
|
|
53998
54813
|
errorCount: stats.errorCount,
|
|
53999
54814
|
errorRate: toPercent3(stats.errorCount, stats.total),
|
|
54000
54815
|
avgLatencyMs: Math.round(stats.latencySum / stats.total),
|
|
54001
|
-
p95LatencyMs: percentileFromCounts(stats.latencyCounts, stats.total, 0.95)
|
|
54002
|
-
|
|
54816
|
+
p95LatencyMs: percentileFromCounts(stats.latencyCounts, stats.total, 0.95),
|
|
54817
|
+
tokenUsageCount: stats.tokenUsageCount,
|
|
54818
|
+
inputTokens: stats.inputTokens,
|
|
54819
|
+
outputTokens: stats.outputTokens,
|
|
54820
|
+
totalTokens: stats.totalTokens,
|
|
54821
|
+
cachedInputTokens: stats.cachedInputTokens,
|
|
54822
|
+
cacheHitInputTokens: stats.cacheHitInputTokens,
|
|
54823
|
+
cacheHitRate: toPercent3(stats.cacheHitInputTokens, stats.cacheHitRateDenominatorTokens),
|
|
54824
|
+
cacheHitRateDenominatorTokens: stats.cacheHitRateDenominatorTokens,
|
|
54825
|
+
cacheReadInputTokens: stats.cacheReadInputTokens,
|
|
54826
|
+
cacheCreationInputTokens: stats.cacheCreationInputTokens,
|
|
54827
|
+
cacheWriteInputTokens: stats.cacheWriteInputTokens,
|
|
54828
|
+
cacheMissInputTokens: stats.cacheMissInputTokens,
|
|
54829
|
+
reasoningTokens: stats.reasoningTokens,
|
|
54830
|
+
billableInputTokens: stats.billableInputTokens,
|
|
54831
|
+
billableOutputTokens: stats.billableOutputTokens
|
|
54832
|
+
};
|
|
54833
|
+
}
|
|
54834
|
+
function createEmptyLogQueryStats() {
|
|
54835
|
+
return finalizeStats(createRunningStats());
|
|
54003
54836
|
}
|
|
54004
54837
|
function compareLocatedEvents(a, b, sort) {
|
|
54005
54838
|
if (a.ts !== b.ts) {
|
|
@@ -54071,7 +54904,8 @@ function eventToSummary(item) {
|
|
|
54071
54904
|
hasMetadata: identity.hasMetadata,
|
|
54072
54905
|
userIdRaw: identity.userIdRaw,
|
|
54073
54906
|
userKey: identity.userKey,
|
|
54074
|
-
sessionId: identity.sessionId
|
|
54907
|
+
sessionId: identity.sessionId,
|
|
54908
|
+
tokenUsage: item.tokenUsage
|
|
54075
54909
|
};
|
|
54076
54910
|
}
|
|
54077
54911
|
function createLogEventSummaryFromEvent(event, location) {
|
|
@@ -54083,7 +54917,8 @@ function createLogEventSummaryFromEvent(event, location) {
|
|
|
54083
54917
|
ts: Number.isFinite(ts) ? ts : 0,
|
|
54084
54918
|
level: getLevel2(event),
|
|
54085
54919
|
statusClass: getStatusClass3(event),
|
|
54086
|
-
event
|
|
54920
|
+
event,
|
|
54921
|
+
tokenUsage: extractTokenUsageSummaryFromLogEvent(event)
|
|
54087
54922
|
});
|
|
54088
54923
|
}
|
|
54089
54924
|
function logEventMatchesQuery(event, query) {
|
|
@@ -54172,23 +55007,23 @@ function readStreamContent(baseDir, streamFile) {
|
|
|
54172
55007
|
if (!streamFile)
|
|
54173
55008
|
return { content: null, warning: null };
|
|
54174
55009
|
try {
|
|
54175
|
-
const resolvedBase =
|
|
54176
|
-
const resolvedFromFile =
|
|
55010
|
+
const resolvedBase = resolve5(baseDir);
|
|
55011
|
+
const resolvedFromFile = resolve5(streamFile);
|
|
54177
55012
|
const looksLikeStreamFile = resolvedFromFile.endsWith(".sse.raw");
|
|
54178
55013
|
if (!looksLikeStreamFile) {
|
|
54179
55014
|
return { content: null, warning: "stream_file \u4E0D\u662F .sse.raw \u6587\u4EF6\uFF0C\u5DF2\u8DF3\u8FC7\u8BFB\u53D6\u3002" };
|
|
54180
55015
|
}
|
|
54181
|
-
if (
|
|
54182
|
-
return { content:
|
|
55016
|
+
if (existsSync5(resolvedFromFile)) {
|
|
55017
|
+
return { content: readFileSync5(resolvedFromFile, "utf-8"), warning: null };
|
|
54183
55018
|
}
|
|
54184
|
-
const fallbackPath =
|
|
55019
|
+
const fallbackPath = resolve5(resolvedBase, streamFile);
|
|
54185
55020
|
if (!fallbackPath.startsWith(`${resolvedBase}/`) && fallbackPath !== resolvedBase) {
|
|
54186
55021
|
return { content: null, warning: "stream_file \u8DEF\u5F84\u975E\u6CD5\uFF0C\u5DF2\u62D2\u7EDD\u8BFB\u53D6\u3002" };
|
|
54187
55022
|
}
|
|
54188
|
-
if (!
|
|
55023
|
+
if (!existsSync5(fallbackPath)) {
|
|
54189
55024
|
return { content: null, warning: "stream_file \u4E0D\u5B58\u5728\uFF0C\u53EF\u80FD\u5DF2\u88AB\u6E05\u7406\u3002" };
|
|
54190
55025
|
}
|
|
54191
|
-
return { content:
|
|
55026
|
+
return { content: readFileSync5(fallbackPath, "utf-8"), warning: null };
|
|
54192
55027
|
} catch (err) {
|
|
54193
55028
|
return {
|
|
54194
55029
|
content: null,
|
|
@@ -54205,6 +55040,10 @@ async function buildLogEventDetail(id, parsed, location, context2) {
|
|
|
54205
55040
|
const responseBodyAvailable = event.response_body !== undefined;
|
|
54206
55041
|
const streamCaptured = Boolean(event.stream_file);
|
|
54207
55042
|
const { content: streamContent, warning: streamWarning } = readStreamContent(resolveLogBaseDir(context2.logConfig), event.stream_file);
|
|
55043
|
+
const tokenUsage = extractTokenUsageSummaryFromLogEvent(event, {
|
|
55044
|
+
baseDir: resolveLogBaseDir(context2.logConfig),
|
|
55045
|
+
streamContent: streamContent ?? undefined
|
|
55046
|
+
}) ?? null;
|
|
54208
55047
|
const hasPluginData = event.plugins_request || event.plugins_response || event.request_body_after_plugins !== undefined || event.request_url_after_plugins !== undefined || event.response_body_before_plugins !== undefined || event.response_body_after_plugins !== undefined;
|
|
54209
55048
|
const pluginsSection = hasPluginData ? {
|
|
54210
55049
|
request: event.plugins_request,
|
|
@@ -54230,7 +55069,16 @@ async function buildLogEventDetail(id, parsed, location, context2) {
|
|
|
54230
55069
|
hasError: level === "error",
|
|
54231
55070
|
model: event.model_out || event.model_in,
|
|
54232
55071
|
modelIn: event.model_in,
|
|
54233
|
-
modelOut: event.model_out
|
|
55072
|
+
modelOut: event.model_out,
|
|
55073
|
+
tokenUsage
|
|
55074
|
+
},
|
|
55075
|
+
usage: {
|
|
55076
|
+
tokenUsage,
|
|
55077
|
+
requestBytes: event.request_bytes ?? 0,
|
|
55078
|
+
responseBytes: event.response_bytes ?? null,
|
|
55079
|
+
streamBytes: event.stream_bytes ?? null,
|
|
55080
|
+
streamFileBytes: event.stream_file_bytes ?? null,
|
|
55081
|
+
streamFileTruncated: event.stream_file_truncated === true
|
|
54234
55082
|
},
|
|
54235
55083
|
request: {
|
|
54236
55084
|
method: event.method,
|
|
@@ -54272,16 +55120,10 @@ async function buildLogEventDetail(id, parsed, location, context2) {
|
|
|
54272
55120
|
}
|
|
54273
55121
|
async function scanEvents(baseDir, query) {
|
|
54274
55122
|
const eventsDir = join6(baseDir, "events");
|
|
54275
|
-
if (!
|
|
55123
|
+
if (!existsSync5(eventsDir)) {
|
|
54276
55124
|
return {
|
|
54277
55125
|
items: [],
|
|
54278
|
-
stats:
|
|
54279
|
-
total: 0,
|
|
54280
|
-
errorCount: 0,
|
|
54281
|
-
errorRate: 0,
|
|
54282
|
-
avgLatencyMs: 0,
|
|
54283
|
-
p95LatencyMs: 0
|
|
54284
|
-
},
|
|
55126
|
+
stats: createEmptyLogQueryStats(),
|
|
54285
55127
|
meta: {
|
|
54286
55128
|
scannedFiles: 0,
|
|
54287
55129
|
scannedLines: 0,
|
|
@@ -54305,7 +55147,7 @@ async function scanEvents(baseDir, query) {
|
|
|
54305
55147
|
break;
|
|
54306
55148
|
}
|
|
54307
55149
|
const filePath = join6(eventsDir, `${date5}.jsonl`);
|
|
54308
|
-
if (!
|
|
55150
|
+
if (!existsSync5(filePath))
|
|
54309
55151
|
continue;
|
|
54310
55152
|
scannedFiles += 1;
|
|
54311
55153
|
const stream = createReadStream3(filePath, { encoding: "utf-8" });
|
|
@@ -54367,6 +55209,7 @@ async function scanEvents(baseDir, query) {
|
|
|
54367
55209
|
continue;
|
|
54368
55210
|
if (!containsKeyword(event, query.q))
|
|
54369
55211
|
continue;
|
|
55212
|
+
const tokenUsage = extractTokenUsageSummaryFromLogEvent(event, { baseDir });
|
|
54370
55213
|
const located = {
|
|
54371
55214
|
id: encodeEventId(date5, lineNumber),
|
|
54372
55215
|
date: date5,
|
|
@@ -54374,7 +55217,8 @@ async function scanEvents(baseDir, query) {
|
|
|
54374
55217
|
ts,
|
|
54375
55218
|
level,
|
|
54376
55219
|
statusClass,
|
|
54377
|
-
event
|
|
55220
|
+
event,
|
|
55221
|
+
tokenUsage
|
|
54378
55222
|
};
|
|
54379
55223
|
updateRunningStats(runningStats, located);
|
|
54380
55224
|
insertBoundedEvent(items, located, query.sort, maxKeep);
|
|
@@ -54434,13 +55278,7 @@ async function queryLogEventsInternal(context2, input, maxLimit) {
|
|
|
54434
55278
|
items: [],
|
|
54435
55279
|
nextCursor: null,
|
|
54436
55280
|
hasMore: false,
|
|
54437
|
-
stats:
|
|
54438
|
-
total: 0,
|
|
54439
|
-
errorCount: 0,
|
|
54440
|
-
errorRate: 0,
|
|
54441
|
-
avgLatencyMs: 0,
|
|
54442
|
-
p95LatencyMs: 0
|
|
54443
|
-
},
|
|
55281
|
+
stats: createEmptyLogQueryStats(),
|
|
54444
55282
|
meta: {
|
|
54445
55283
|
scannedFiles: 0,
|
|
54446
55284
|
scannedLines: 0,
|
|
@@ -54492,7 +55330,7 @@ async function getLogEventDetailById(context2, id) {
|
|
|
54492
55330
|
const { date: date5, line: line2 } = decodeEventId(id);
|
|
54493
55331
|
const baseDir = resolveLogBaseDir(context2.logConfig);
|
|
54494
55332
|
const filePath = join6(baseDir, "events", `${date5}.jsonl`);
|
|
54495
|
-
if (!
|
|
55333
|
+
if (!existsSync5(filePath))
|
|
54496
55334
|
return null;
|
|
54497
55335
|
const stream = createReadStream3(filePath, { encoding: "utf-8" });
|
|
54498
55336
|
const rl = createInterface2({ input: stream, crlfDelay: Number.POSITIVE_INFINITY });
|
|
@@ -54523,6 +55361,7 @@ function escapeCsvValue(value) {
|
|
|
54523
55361
|
return text2;
|
|
54524
55362
|
}
|
|
54525
55363
|
function toCsvRow(item) {
|
|
55364
|
+
const usage = item.tokenUsage;
|
|
54526
55365
|
return [
|
|
54527
55366
|
item.id,
|
|
54528
55367
|
item.ts,
|
|
@@ -54543,7 +55382,36 @@ function toCsvRow(item) {
|
|
|
54543
55382
|
item.userKey ?? "",
|
|
54544
55383
|
item.sessionId ?? "",
|
|
54545
55384
|
item.message,
|
|
54546
|
-
item.errorType ?? ""
|
|
55385
|
+
item.errorType ?? "",
|
|
55386
|
+
usage?.inputTokens ?? "",
|
|
55387
|
+
usage?.outputTokens ?? "",
|
|
55388
|
+
usage?.totalTokens ?? "",
|
|
55389
|
+
usage?.cachedInputTokens ?? "",
|
|
55390
|
+
usage?.cacheHitInputTokens ?? "",
|
|
55391
|
+
usage?.cacheHitRate ?? "",
|
|
55392
|
+
usage?.cacheHitRateDenominatorTokens ?? "",
|
|
55393
|
+
usage?.cacheReadInputTokens ?? "",
|
|
55394
|
+
usage?.cacheCreationInputTokens ?? "",
|
|
55395
|
+
usage?.cacheCreationInputTokens5m ?? "",
|
|
55396
|
+
usage?.cacheCreationInputTokens1h ?? "",
|
|
55397
|
+
usage?.cacheWriteInputTokens ?? "",
|
|
55398
|
+
usage?.cacheMissInputTokens ?? "",
|
|
55399
|
+
usage?.reasoningTokens ?? "",
|
|
55400
|
+
usage?.audioInputTokens ?? "",
|
|
55401
|
+
usage?.audioOutputTokens ?? "",
|
|
55402
|
+
usage?.textInputTokens ?? "",
|
|
55403
|
+
usage?.textOutputTokens ?? "",
|
|
55404
|
+
usage?.acceptedPredictionTokens ?? "",
|
|
55405
|
+
usage?.rejectedPredictionTokens ?? "",
|
|
55406
|
+
usage?.toolUsePromptTokens ?? "",
|
|
55407
|
+
usage?.billableInputTokens ?? "",
|
|
55408
|
+
usage?.billableOutputTokens ?? "",
|
|
55409
|
+
usage?.creditUsage ?? "",
|
|
55410
|
+
usage?.cost ?? "",
|
|
55411
|
+
usage?.providerStyle ?? "",
|
|
55412
|
+
usage?.source ?? "",
|
|
55413
|
+
usage?.rawUsagePath ?? "",
|
|
55414
|
+
usage?.cacheHitRateFormula ?? ""
|
|
54547
55415
|
].map(escapeCsvValue).join(",");
|
|
54548
55416
|
}
|
|
54549
55417
|
function createCsvExportStream(items) {
|
|
@@ -54568,7 +55436,36 @@ function createCsvExportStream(items) {
|
|
|
54568
55436
|
"userKey",
|
|
54569
55437
|
"sessionId",
|
|
54570
55438
|
"message",
|
|
54571
|
-
"errorType"
|
|
55439
|
+
"errorType",
|
|
55440
|
+
"usage.inputTokens",
|
|
55441
|
+
"usage.outputTokens",
|
|
55442
|
+
"usage.totalTokens",
|
|
55443
|
+
"usage.cachedInputTokens",
|
|
55444
|
+
"usage.cacheHitInputTokens",
|
|
55445
|
+
"usage.cacheHitRate",
|
|
55446
|
+
"usage.cacheHitRateDenominatorTokens",
|
|
55447
|
+
"usage.cacheReadInputTokens",
|
|
55448
|
+
"usage.cacheCreationInputTokens",
|
|
55449
|
+
"usage.cacheCreationInputTokens5m",
|
|
55450
|
+
"usage.cacheCreationInputTokens1h",
|
|
55451
|
+
"usage.cacheWriteInputTokens",
|
|
55452
|
+
"usage.cacheMissInputTokens",
|
|
55453
|
+
"usage.reasoningTokens",
|
|
55454
|
+
"usage.audioInputTokens",
|
|
55455
|
+
"usage.audioOutputTokens",
|
|
55456
|
+
"usage.textInputTokens",
|
|
55457
|
+
"usage.textOutputTokens",
|
|
55458
|
+
"usage.acceptedPredictionTokens",
|
|
55459
|
+
"usage.rejectedPredictionTokens",
|
|
55460
|
+
"usage.toolUsePromptTokens",
|
|
55461
|
+
"usage.billableInputTokens",
|
|
55462
|
+
"usage.billableOutputTokens",
|
|
55463
|
+
"usage.creditUsage",
|
|
55464
|
+
"usage.cost",
|
|
55465
|
+
"usage.providerStyle",
|
|
55466
|
+
"usage.source",
|
|
55467
|
+
"usage.rawUsagePath",
|
|
55468
|
+
"usage.cacheHitRateFormula"
|
|
54572
55469
|
];
|
|
54573
55470
|
return new ReadableStream({
|
|
54574
55471
|
start(controller) {
|
|
@@ -54648,7 +55545,7 @@ function parseBooleanFlag(value) {
|
|
|
54648
55545
|
|
|
54649
55546
|
// src/log-sessions.ts
|
|
54650
55547
|
init_config();
|
|
54651
|
-
import { createReadStream as createReadStream4, existsSync as
|
|
55548
|
+
import { createReadStream as createReadStream4, existsSync as existsSync6 } from "fs";
|
|
54652
55549
|
import { join as join7 } from "path";
|
|
54653
55550
|
import { createInterface as createInterface3 } from "readline";
|
|
54654
55551
|
var MAX_LINES_SCANNED3 = 250000;
|
|
@@ -54771,7 +55668,7 @@ async function queryLogSessions(context2, input) {
|
|
|
54771
55668
|
}
|
|
54772
55669
|
const baseDir = resolveLogBaseDir(context2.logConfig);
|
|
54773
55670
|
const eventsDir = join7(baseDir, "events");
|
|
54774
|
-
if (!
|
|
55671
|
+
if (!existsSync6(eventsDir)) {
|
|
54775
55672
|
return createEmptyResult(normalized.fromMs, normalized.toMs);
|
|
54776
55673
|
}
|
|
54777
55674
|
const usersMap = new Map;
|
|
@@ -54790,7 +55687,7 @@ async function queryLogSessions(context2, input) {
|
|
|
54790
55687
|
break;
|
|
54791
55688
|
}
|
|
54792
55689
|
const filePath = join7(eventsDir, `${date5}.jsonl`);
|
|
54793
|
-
if (!
|
|
55690
|
+
if (!existsSync6(filePath))
|
|
54794
55691
|
continue;
|
|
54795
55692
|
scannedFiles += 1;
|
|
54796
55693
|
const stream = createReadStream4(filePath, { encoding: "utf-8" });
|
|
@@ -54899,7 +55796,7 @@ async function queryLogSessions(context2, input) {
|
|
|
54899
55796
|
|
|
54900
55797
|
// src/log-storage.ts
|
|
54901
55798
|
init_config();
|
|
54902
|
-
import { existsSync as
|
|
55799
|
+
import { existsSync as existsSync7, promises as fsPromises } from "fs";
|
|
54903
55800
|
import { join as join8 } from "path";
|
|
54904
55801
|
var cachedStorage = null;
|
|
54905
55802
|
var calculationPromise = null;
|
|
@@ -54908,7 +55805,7 @@ var CACHE_TTL_MS2 = 60 * 60 * 1000;
|
|
|
54908
55805
|
var CALCULATION_INTERVAL_MS = 60 * 60 * 1000;
|
|
54909
55806
|
var MIN_CALCULATION_INTERVAL_MS = 5 * 60 * 1000;
|
|
54910
55807
|
async function calculateDirSize(dirPath) {
|
|
54911
|
-
if (!
|
|
55808
|
+
if (!existsSync7(dirPath)) {
|
|
54912
55809
|
return { bytes: 0, fileCount: 0 };
|
|
54913
55810
|
}
|
|
54914
55811
|
let bytes = 0;
|
|
@@ -54963,7 +55860,7 @@ async function doCalculateStorage(logConfig) {
|
|
|
54963
55860
|
};
|
|
54964
55861
|
}
|
|
54965
55862
|
async function calculateIndexSize(baseDir) {
|
|
54966
|
-
if (!
|
|
55863
|
+
if (!existsSync7(baseDir)) {
|
|
54967
55864
|
return { bytes: 0, fileCount: 0 };
|
|
54968
55865
|
}
|
|
54969
55866
|
let bytes = 0;
|
|
@@ -55044,10 +55941,10 @@ function subscribeLogEvents(subscriber) {
|
|
|
55044
55941
|
import {
|
|
55045
55942
|
appendFileSync,
|
|
55046
55943
|
closeSync as closeSync2,
|
|
55047
|
-
existsSync as
|
|
55944
|
+
existsSync as existsSync8,
|
|
55048
55945
|
mkdirSync as mkdirSync4,
|
|
55049
55946
|
openSync as openSync2,
|
|
55050
|
-
statSync as
|
|
55947
|
+
statSync as statSync3,
|
|
55051
55948
|
writeSync
|
|
55052
55949
|
} from "fs";
|
|
55053
55950
|
import { join as join9 } from "path";
|
|
@@ -55078,13 +55975,13 @@ class Logger {
|
|
|
55078
55975
|
}
|
|
55079
55976
|
ensureDirs() {
|
|
55080
55977
|
for (const dir of [this.baseDir, this.eventsDir, this.streamsDir]) {
|
|
55081
|
-
if (!
|
|
55978
|
+
if (!existsSync8(dir))
|
|
55082
55979
|
mkdirSync4(dir, { recursive: true });
|
|
55083
55980
|
}
|
|
55084
55981
|
}
|
|
55085
55982
|
ensureStreamDateDir(dateStr) {
|
|
55086
55983
|
const dir = join9(this.streamsDir, dateStr);
|
|
55087
|
-
if (!
|
|
55984
|
+
if (!existsSync8(dir))
|
|
55088
55985
|
mkdirSync4(dir, { recursive: true });
|
|
55089
55986
|
return dir;
|
|
55090
55987
|
}
|
|
@@ -55092,11 +55989,12 @@ class Logger {
|
|
|
55092
55989
|
if (!this._enabled)
|
|
55093
55990
|
return;
|
|
55094
55991
|
try {
|
|
55992
|
+
const enrichedEvent = enrichLogEventTokenUsage(event, { baseDir: this.baseDir });
|
|
55095
55993
|
this.ensureDirs();
|
|
55096
|
-
const dateStr =
|
|
55994
|
+
const dateStr = enrichedEvent.ts_start.slice(0, 10);
|
|
55097
55995
|
const filePath = join9(this.eventsDir, `${dateStr}.jsonl`);
|
|
55098
|
-
const offset =
|
|
55099
|
-
const line2 = `${JSON.stringify(
|
|
55996
|
+
const offset = existsSync8(filePath) ? statSync3(filePath).size : 0;
|
|
55997
|
+
const line2 = `${JSON.stringify(enrichedEvent)}
|
|
55100
55998
|
`;
|
|
55101
55999
|
appendFileSync(filePath, line2);
|
|
55102
56000
|
const id = encodeOffsetLogEventId(dateStr, offset);
|
|
@@ -55106,9 +56004,9 @@ class Logger {
|
|
|
55106
56004
|
date: dateStr,
|
|
55107
56005
|
offset,
|
|
55108
56006
|
byteLength: Buffer.byteLength(line2),
|
|
55109
|
-
event
|
|
56007
|
+
event: enrichedEvent
|
|
55110
56008
|
});
|
|
55111
|
-
publishLogEvent({ id, date: dateStr, filePath, offset, event });
|
|
56009
|
+
publishLogEvent({ id, date: dateStr, filePath, offset, event: enrichedEvent });
|
|
55112
56010
|
} catch (err) {
|
|
55113
56011
|
console.error("[logger] \u4E8B\u4EF6\u65E5\u5FD7\u5199\u5165\u5931\u8D25:", err);
|
|
55114
56012
|
}
|
|
@@ -56399,7 +57297,7 @@ var openAPISpec = {
|
|
|
56399
57297
|
// src/plugin-loader.ts
|
|
56400
57298
|
import { mkdtemp, rm, writeFile } from "fs/promises";
|
|
56401
57299
|
import { tmpdir } from "os";
|
|
56402
|
-
import { join as join10, resolve as
|
|
57300
|
+
import { join as join10, resolve as resolve6 } from "path";
|
|
56403
57301
|
function isLocalPath(pkg) {
|
|
56404
57302
|
return pkg.startsWith("./") || pkg.startsWith("../") || pkg.startsWith("/") || /^[A-Za-z]:[\\/]/.test(pkg);
|
|
56405
57303
|
}
|
|
@@ -56455,7 +57353,7 @@ async function importPlugin(pkg, configDir) {
|
|
|
56455
57353
|
const localPath = await fetchRemotePlugin(pkg);
|
|
56456
57354
|
modulePath = `${localPath}?t=${Date.now()}`;
|
|
56457
57355
|
} else if (isLocalPath(pkg)) {
|
|
56458
|
-
const absolutePath =
|
|
57356
|
+
const absolutePath = resolve6(configDir, pkg);
|
|
56459
57357
|
modulePath = `${absolutePath}?t=${Date.now()}`;
|
|
56460
57358
|
} else {
|
|
56461
57359
|
modulePath = pkg;
|
|
@@ -56848,6 +57746,7 @@ async function proxyRequest(c2, options) {
|
|
|
56848
57746
|
});
|
|
56849
57747
|
}
|
|
56850
57748
|
const capture = logger?.openStreamCapture(logMeta.requestId, dateStr) ?? null;
|
|
57749
|
+
const tokenUsageCollector = createTokenUsageStreamCollector(`${logMeta.provider} ${logMeta.routeType} ${logMeta.modelIn} ${logMeta.modelOut}`);
|
|
56851
57750
|
let upstreamBytes = 0;
|
|
56852
57751
|
let writeEventCalled = false;
|
|
56853
57752
|
const finalizeAndWriteEvent = () => {
|
|
@@ -56859,6 +57758,7 @@ async function proxyRequest(c2, options) {
|
|
|
56859
57758
|
truncated: false,
|
|
56860
57759
|
filePath: null
|
|
56861
57760
|
};
|
|
57761
|
+
const tokenUsage2 = tokenUsageCollector.getUsage();
|
|
56862
57762
|
logger?.writeEvent(buildLogEvent(logMeta, targetUrl, proxy, Date.now(), {
|
|
56863
57763
|
upstream_status: sseStatus,
|
|
56864
57764
|
content_type_res: contentTypeRes,
|
|
@@ -56870,6 +57770,7 @@ async function proxyRequest(c2, options) {
|
|
|
56870
57770
|
stream_file_bytes: captureResult.bytesWritten
|
|
56871
57771
|
},
|
|
56872
57772
|
...captureResult.truncated && { stream_file_truncated: true },
|
|
57773
|
+
...tokenUsage2 && { token_usage: tokenUsage2 },
|
|
56873
57774
|
...requestBody !== undefined && { request_body: requestBody },
|
|
56874
57775
|
...pluginLogOverrides
|
|
56875
57776
|
}));
|
|
@@ -56885,6 +57786,7 @@ async function proxyRequest(c2, options) {
|
|
|
56885
57786
|
return;
|
|
56886
57787
|
}
|
|
56887
57788
|
upstreamBytes += value.byteLength;
|
|
57789
|
+
tokenUsageCollector.addChunk(value);
|
|
56888
57790
|
capture?.write(value);
|
|
56889
57791
|
controller.enqueue(value);
|
|
56890
57792
|
} catch (err) {
|
|
@@ -56935,12 +57837,14 @@ async function proxyRequest(c2, options) {
|
|
|
56935
57837
|
});
|
|
56936
57838
|
}
|
|
56937
57839
|
const responseBytes = Buffer.byteLength(responseText, "utf-8");
|
|
57840
|
+
const tokenUsage = extractTokenUsageFromResponseText(responseText, "response_body", `${logMeta.provider} ${logMeta.routeType} ${logMeta.modelIn} ${logMeta.modelOut}`);
|
|
56938
57841
|
const eventOverrides = {
|
|
56939
57842
|
upstream_status: upstreamRes.status,
|
|
56940
57843
|
content_type_res: contentTypeRes,
|
|
56941
57844
|
response_headers: finalResponseHeaders,
|
|
56942
57845
|
response_bytes: responseBytes,
|
|
56943
57846
|
provider_request_id: providerRequestId,
|
|
57847
|
+
...tokenUsage && { token_usage: tokenUsage },
|
|
56944
57848
|
...pluginLogOverrides
|
|
56945
57849
|
};
|
|
56946
57850
|
if (requestBody !== undefined) {
|
|
@@ -57226,7 +58130,7 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
57226
58130
|
const CRYPTO_SESSION_TTL_MS = 2 * 60 * 1000;
|
|
57227
58131
|
const CRYPTO_SESSION_MAX = 512;
|
|
57228
58132
|
const schemaPath = getBundledSchemaPath();
|
|
57229
|
-
const schemaJson = JSON.parse(
|
|
58133
|
+
const schemaJson = JSON.parse(readFileSync6(schemaPath, "utf-8"));
|
|
57230
58134
|
const pruneExpiredCryptoSessions = (now2 = Date.now()) => {
|
|
57231
58135
|
for (const [id, record2] of Array.from(cryptoSessions.entries())) {
|
|
57232
58136
|
if (now2 - record2.createdAt > CRYPTO_SESSION_TTL_MS) {
|
|
@@ -57831,7 +58735,7 @@ async function createApp(store, options) {
|
|
|
57831
58735
|
}
|
|
57832
58736
|
const stopLogStorageTask = startLogStorageBackgroundTask(config2.log);
|
|
57833
58737
|
options?.registerCleanup?.(stopLogStorageTask);
|
|
57834
|
-
const configDir = dirname3(
|
|
58738
|
+
const configDir = dirname3(resolve7(store.getPath()));
|
|
57835
58739
|
const pluginManager = new PluginManager(configDir);
|
|
57836
58740
|
const reloadResult = await pluginManager.reloadAll(config2.providers);
|
|
57837
58741
|
if (!reloadResult.ok) {
|
|
@@ -57947,9 +58851,9 @@ async function startServer(options) {
|
|
|
57947
58851
|
}
|
|
57948
58852
|
|
|
57949
58853
|
// src/cli/runtime.ts
|
|
57950
|
-
import { existsSync as
|
|
58854
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync7, rmSync, writeFileSync as writeFileSync4 } from "fs";
|
|
57951
58855
|
import { homedir as homedir2 } from "os";
|
|
57952
|
-
import { join as join11, resolve as
|
|
58856
|
+
import { join as join11, resolve as resolve8 } from "path";
|
|
57953
58857
|
function getRuntimeDirs() {
|
|
57954
58858
|
const override = process.env.LOCAL_ROUTER_RUNTIME_DIR;
|
|
57955
58859
|
const root2 = override?.trim() ? override.trim() : join11(homedir2(), ".local-router");
|
|
@@ -57982,11 +58886,11 @@ function writeRuntimeState(state) {
|
|
|
57982
58886
|
}
|
|
57983
58887
|
function readRuntimeState() {
|
|
57984
58888
|
const files = getRuntimeFiles();
|
|
57985
|
-
if (!
|
|
58889
|
+
if (!existsSync9(files.state)) {
|
|
57986
58890
|
return null;
|
|
57987
58891
|
}
|
|
57988
58892
|
try {
|
|
57989
|
-
return JSON.parse(
|
|
58893
|
+
return JSON.parse(readFileSync7(files.state, "utf-8"));
|
|
57990
58894
|
} catch {
|
|
57991
58895
|
return null;
|
|
57992
58896
|
}
|
|
@@ -57997,7 +58901,7 @@ function clearRuntimeFiles() {
|
|
|
57997
58901
|
rmSync(files.state, { force: true });
|
|
57998
58902
|
}
|
|
57999
58903
|
function resolveConfigArgPath(pathValue) {
|
|
58000
|
-
return
|
|
58904
|
+
return resolve8(pathValue);
|
|
58001
58905
|
}
|
|
58002
58906
|
|
|
58003
58907
|
// src/cli/process.ts
|
|
@@ -58175,7 +59079,7 @@ async function startDaemon(flags) {
|
|
|
58175
59079
|
}
|
|
58176
59080
|
let tail = "";
|
|
58177
59081
|
try {
|
|
58178
|
-
const content =
|
|
59082
|
+
const content = readFileSync8(files.daemonLog, "utf-8");
|
|
58179
59083
|
tail = content.split(`
|
|
58180
59084
|
`).slice(-20).join(`
|
|
58181
59085
|
`);
|
|
@@ -58208,11 +59112,11 @@ async function stopProcess(graceMs = 8000) {
|
|
|
58208
59112
|
}
|
|
58209
59113
|
function readLogDelta(filePath, offset) {
|
|
58210
59114
|
try {
|
|
58211
|
-
const stats =
|
|
59115
|
+
const stats = statSync4(filePath);
|
|
58212
59116
|
if (stats.size <= offset) {
|
|
58213
59117
|
return { content: "", nextOffset: offset };
|
|
58214
59118
|
}
|
|
58215
|
-
const full =
|
|
59119
|
+
const full = readFileSync8(filePath, "utf-8");
|
|
58216
59120
|
const content = full.slice(offset);
|
|
58217
59121
|
return { content, nextOffset: full.length };
|
|
58218
59122
|
} catch {
|
|
@@ -59225,13 +60129,13 @@ init_config();
|
|
|
59225
60129
|
init_config_validate();
|
|
59226
60130
|
init_errors();
|
|
59227
60131
|
init_output();
|
|
59228
|
-
import { existsSync as
|
|
60132
|
+
import { existsSync as existsSync11 } from "fs";
|
|
59229
60133
|
import { parseArgs as parseArgs4 } from "util";
|
|
59230
60134
|
init_registry();
|
|
59231
60135
|
function tryRead(configArg) {
|
|
59232
60136
|
try {
|
|
59233
60137
|
const path = resolveConfigPath(configArg);
|
|
59234
|
-
if (!
|
|
60138
|
+
if (!existsSync11(path)) {
|
|
59235
60139
|
return { path, config: null, error: `\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728: ${path}` };
|
|
59236
60140
|
}
|
|
59237
60141
|
const config2 = loadConfig(path);
|
|
@@ -59666,7 +60570,7 @@ defineCommand({
|
|
|
59666
60570
|
});
|
|
59667
60571
|
|
|
59668
60572
|
// src/cli/handlers/introspection.ts
|
|
59669
|
-
import { readFileSync as
|
|
60573
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
59670
60574
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
59671
60575
|
import { parseArgs as parseArgs5 } from "util";
|
|
59672
60576
|
|
|
@@ -59942,7 +60846,7 @@ defineCommand({
|
|
|
59942
60846
|
hint: "\u8BF7\u786E\u8BA4 npm \u5305\u5B8C\u6574\u6027\uFF0C\u6216\u5F00\u53D1\u6A21\u5F0F\u4E0B\u4ECE\u6E90\u7801\u8FD0\u884C"
|
|
59943
60847
|
});
|
|
59944
60848
|
}
|
|
59945
|
-
const text2 =
|
|
60849
|
+
const text2 = readFileSync10(fileURLToPath2(schemaUrl), "utf-8");
|
|
59946
60850
|
const data2 = JSON.parse(text2);
|
|
59947
60851
|
emitResult(ctx, {
|
|
59948
60852
|
command: "schema.config",
|
|
@@ -60162,7 +61066,7 @@ defineCommand({
|
|
|
60162
61066
|
|
|
60163
61067
|
// src/cli/handlers/lifecycle.ts
|
|
60164
61068
|
init_config();
|
|
60165
|
-
import { existsSync as
|
|
61069
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
60166
61070
|
import { setTimeout as sleep3 } from "timers/promises";
|
|
60167
61071
|
import { parseArgs as parseArgs6 } from "util";
|
|
60168
61072
|
init_errors();
|
|
@@ -60467,10 +61371,10 @@ defineCommand({
|
|
|
60467
61371
|
})
|
|
60468
61372
|
});
|
|
60469
61373
|
function readLastLines(filePath, lines) {
|
|
60470
|
-
if (!
|
|
61374
|
+
if (!existsSync12(filePath)) {
|
|
60471
61375
|
return { content: "", offset: 0 };
|
|
60472
61376
|
}
|
|
60473
|
-
const full =
|
|
61377
|
+
const full = readFileSync11(filePath, "utf-8");
|
|
60474
61378
|
const rendered = full.split(`
|
|
60475
61379
|
`).slice(-lines).join(`
|
|
60476
61380
|
`);
|
|
@@ -60501,7 +61405,7 @@ defineCommand({
|
|
|
60501
61405
|
const linesRaw = parsed.values.lines ?? "100";
|
|
60502
61406
|
const lines = Number.parseInt(linesRaw, 10);
|
|
60503
61407
|
const initial = readLastLines(files.daemonLog, Number.isFinite(lines) ? lines : 100);
|
|
60504
|
-
if (!
|
|
61408
|
+
if (!existsSync12(files.daemonLog)) {
|
|
60505
61409
|
emitResult(ctx, {
|
|
60506
61410
|
command: "logs.daemon",
|
|
60507
61411
|
data: { exists: false, path: files.daemonLog, lines: [] },
|