@lakphy/local-router 0.5.1 → 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 +1184 -237
- package/dist/entry.js +1131 -184
- 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,8 +11166,8 @@ __export(exports_config_extra, {
|
|
|
11166
11166
|
configDiff: () => configDiff,
|
|
11167
11167
|
configBackupsList: () => configBackupsList
|
|
11168
11168
|
});
|
|
11169
|
-
import { existsSync as
|
|
11170
|
-
import { dirname as dirname4, join as
|
|
11169
|
+
import { existsSync as existsSync10, readdirSync, readFileSync as readFileSync9, statSync as statSync5 } from "fs";
|
|
11170
|
+
import { dirname as dirname4, join as join12 } from "path";
|
|
11171
11171
|
import { parseArgs as parseArgs2 } from "util";
|
|
11172
11172
|
function maskApiKey(k) {
|
|
11173
11173
|
if (k.length <= 8)
|
|
@@ -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
|
-
const backupDir =
|
|
11256
|
-
if (!
|
|
11255
|
+
const backupDir = join12(dirname4(path), ".backups");
|
|
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
|
});
|
|
@@ -11262,21 +11262,21 @@ async function configDiff(args, flags) {
|
|
|
11262
11262
|
if (files.length === 0) {
|
|
11263
11263
|
throw new CliError("CONFIG_NOT_FOUND", "\u5907\u4EFD\u76EE\u5F55\u4E3A\u7A7A");
|
|
11264
11264
|
}
|
|
11265
|
-
beforePath =
|
|
11266
|
-
} else if (
|
|
11265
|
+
beforePath = join12(backupDir, files[0]);
|
|
11266
|
+
} else if (existsSync10(against)) {
|
|
11267
11267
|
beforePath = against;
|
|
11268
11268
|
} else {
|
|
11269
|
-
const backupDir =
|
|
11270
|
-
const candidate =
|
|
11271
|
-
if (
|
|
11269
|
+
const backupDir = join12(dirname4(path), ".backups");
|
|
11270
|
+
const candidate = join12(backupDir, `config-${against}.json5`);
|
|
11271
|
+
if (existsSync10(candidate))
|
|
11272
11272
|
beforePath = candidate;
|
|
11273
|
-
else if (
|
|
11274
|
-
beforePath =
|
|
11273
|
+
else if (existsSync10(join12(backupDir, against)))
|
|
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 {
|
|
@@ -11426,12 +11426,12 @@ ${result.diff}`
|
|
|
11426
11426
|
});
|
|
11427
11427
|
}
|
|
11428
11428
|
function listBackups(configPath) {
|
|
11429
|
-
const dir =
|
|
11430
|
-
if (!
|
|
11429
|
+
const dir = join12(dirname4(configPath), ".backups");
|
|
11430
|
+
if (!existsSync10(dir))
|
|
11431
11431
|
return [];
|
|
11432
11432
|
return readdirSync(dir).filter((f) => f.startsWith("config-") && f.endsWith(".json5")).map((f) => {
|
|
11433
|
-
const full =
|
|
11434
|
-
const st =
|
|
11433
|
+
const full = join12(dir, f);
|
|
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
|
|
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;
|
|
@@ -55041,7 +55938,15 @@ function subscribeLogEvents(subscriber) {
|
|
|
55041
55938
|
}
|
|
55042
55939
|
|
|
55043
55940
|
// src/logger.ts
|
|
55044
|
-
import {
|
|
55941
|
+
import {
|
|
55942
|
+
appendFileSync,
|
|
55943
|
+
closeSync as closeSync2,
|
|
55944
|
+
existsSync as existsSync8,
|
|
55945
|
+
mkdirSync as mkdirSync4,
|
|
55946
|
+
openSync as openSync2,
|
|
55947
|
+
statSync as statSync3,
|
|
55948
|
+
writeSync
|
|
55949
|
+
} from "fs";
|
|
55045
55950
|
import { join as join9 } from "path";
|
|
55046
55951
|
class Logger {
|
|
55047
55952
|
baseDir;
|
|
@@ -55070,13 +55975,13 @@ class Logger {
|
|
|
55070
55975
|
}
|
|
55071
55976
|
ensureDirs() {
|
|
55072
55977
|
for (const dir of [this.baseDir, this.eventsDir, this.streamsDir]) {
|
|
55073
|
-
if (!
|
|
55978
|
+
if (!existsSync8(dir))
|
|
55074
55979
|
mkdirSync4(dir, { recursive: true });
|
|
55075
55980
|
}
|
|
55076
55981
|
}
|
|
55077
55982
|
ensureStreamDateDir(dateStr) {
|
|
55078
55983
|
const dir = join9(this.streamsDir, dateStr);
|
|
55079
|
-
if (!
|
|
55984
|
+
if (!existsSync8(dir))
|
|
55080
55985
|
mkdirSync4(dir, { recursive: true });
|
|
55081
55986
|
return dir;
|
|
55082
55987
|
}
|
|
@@ -55084,11 +55989,12 @@ class Logger {
|
|
|
55084
55989
|
if (!this._enabled)
|
|
55085
55990
|
return;
|
|
55086
55991
|
try {
|
|
55992
|
+
const enrichedEvent = enrichLogEventTokenUsage(event, { baseDir: this.baseDir });
|
|
55087
55993
|
this.ensureDirs();
|
|
55088
|
-
const dateStr =
|
|
55994
|
+
const dateStr = enrichedEvent.ts_start.slice(0, 10);
|
|
55089
55995
|
const filePath = join9(this.eventsDir, `${dateStr}.jsonl`);
|
|
55090
|
-
const offset =
|
|
55091
|
-
const line2 = `${JSON.stringify(
|
|
55996
|
+
const offset = existsSync8(filePath) ? statSync3(filePath).size : 0;
|
|
55997
|
+
const line2 = `${JSON.stringify(enrichedEvent)}
|
|
55092
55998
|
`;
|
|
55093
55999
|
appendFileSync(filePath, line2);
|
|
55094
56000
|
const id = encodeOffsetLogEventId(dateStr, offset);
|
|
@@ -55098,29 +56004,80 @@ class Logger {
|
|
|
55098
56004
|
date: dateStr,
|
|
55099
56005
|
offset,
|
|
55100
56006
|
byteLength: Buffer.byteLength(line2),
|
|
55101
|
-
event
|
|
56007
|
+
event: enrichedEvent
|
|
55102
56008
|
});
|
|
55103
|
-
publishLogEvent({ id, date: dateStr, filePath, offset, event });
|
|
56009
|
+
publishLogEvent({ id, date: dateStr, filePath, offset, event: enrichedEvent });
|
|
55104
56010
|
} catch (err) {
|
|
55105
56011
|
console.error("[logger] \u4E8B\u4EF6\u65E5\u5FD7\u5199\u5165\u5931\u8D25:", err);
|
|
55106
56012
|
}
|
|
55107
56013
|
}
|
|
55108
|
-
|
|
55109
|
-
if (!this._enabled || !this._streamsEnabled)
|
|
55110
|
-
return
|
|
56014
|
+
openStreamCapture(requestId, dateStr) {
|
|
56015
|
+
if (!this._enabled || !this._streamsEnabled) {
|
|
56016
|
+
return makeNoopStreamCaptureHandle();
|
|
56017
|
+
}
|
|
56018
|
+
const maxStreamBytes = this.maxStreamBytes;
|
|
56019
|
+
const truncationMarker = Buffer.from(`
|
|
56020
|
+
[TRUNCATED]`);
|
|
56021
|
+
let filePath;
|
|
56022
|
+
let fd;
|
|
55111
56023
|
try {
|
|
55112
56024
|
const dir = this.ensureStreamDateDir(dateStr);
|
|
55113
|
-
|
|
55114
|
-
|
|
55115
|
-
[TRUNCATED]` : content;
|
|
55116
|
-
writeFileSync4(filePath, toWrite);
|
|
55117
|
-
return filePath;
|
|
56025
|
+
filePath = join9(dir, `${requestId}.sse.raw`);
|
|
56026
|
+
fd = openSync2(filePath, "a");
|
|
55118
56027
|
} catch (err) {
|
|
55119
|
-
console.error("[logger] \u6D41\u5F0F\u65E5\u5FD7\
|
|
55120
|
-
return
|
|
56028
|
+
console.error("[logger] \u6D41\u5F0F\u65E5\u5FD7\u6253\u5F00\u5931\u8D25:", err);
|
|
56029
|
+
return makeNoopStreamCaptureHandle();
|
|
55121
56030
|
}
|
|
56031
|
+
let bytes = 0;
|
|
56032
|
+
let truncated = false;
|
|
56033
|
+
let finalized = false;
|
|
56034
|
+
return {
|
|
56035
|
+
filePath,
|
|
56036
|
+
write(chunk) {
|
|
56037
|
+
if (finalized || truncated || fd == null)
|
|
56038
|
+
return;
|
|
56039
|
+
try {
|
|
56040
|
+
if (bytes + chunk.byteLength > maxStreamBytes) {
|
|
56041
|
+
const remaining = Math.max(0, maxStreamBytes - bytes);
|
|
56042
|
+
if (remaining > 0) {
|
|
56043
|
+
writeSync(fd, chunk.subarray(0, remaining));
|
|
56044
|
+
bytes += remaining;
|
|
56045
|
+
}
|
|
56046
|
+
writeSync(fd, truncationMarker);
|
|
56047
|
+
truncated = true;
|
|
56048
|
+
return;
|
|
56049
|
+
}
|
|
56050
|
+
writeSync(fd, chunk);
|
|
56051
|
+
bytes += chunk.byteLength;
|
|
56052
|
+
} catch (err) {
|
|
56053
|
+
console.error("[logger] \u6D41\u5F0F\u65E5\u5FD7\u5199\u5165\u5931\u8D25:", err);
|
|
56054
|
+
truncated = true;
|
|
56055
|
+
}
|
|
56056
|
+
},
|
|
56057
|
+
finalize() {
|
|
56058
|
+
if (finalized)
|
|
56059
|
+
return { bytesWritten: bytes, truncated, filePath };
|
|
56060
|
+
finalized = true;
|
|
56061
|
+
if (fd != null) {
|
|
56062
|
+
try {
|
|
56063
|
+
closeSync2(fd);
|
|
56064
|
+
} catch {}
|
|
56065
|
+
fd = null;
|
|
56066
|
+
}
|
|
56067
|
+
return { bytesWritten: bytes, truncated, filePath };
|
|
56068
|
+
}
|
|
56069
|
+
};
|
|
55122
56070
|
}
|
|
55123
56071
|
}
|
|
56072
|
+
function makeNoopStreamCaptureHandle() {
|
|
56073
|
+
return {
|
|
56074
|
+
filePath: null,
|
|
56075
|
+
write() {},
|
|
56076
|
+
finalize() {
|
|
56077
|
+
return { bytesWritten: 0, truncated: false, filePath: null };
|
|
56078
|
+
}
|
|
56079
|
+
};
|
|
56080
|
+
}
|
|
55124
56081
|
var instance = null;
|
|
55125
56082
|
function initLogger(baseDir, config2) {
|
|
55126
56083
|
instance = new Logger(baseDir, config2);
|
|
@@ -56340,7 +57297,7 @@ var openAPISpec = {
|
|
|
56340
57297
|
// src/plugin-loader.ts
|
|
56341
57298
|
import { mkdtemp, rm, writeFile } from "fs/promises";
|
|
56342
57299
|
import { tmpdir } from "os";
|
|
56343
|
-
import { join as join10, resolve as
|
|
57300
|
+
import { join as join10, resolve as resolve6 } from "path";
|
|
56344
57301
|
function isLocalPath(pkg) {
|
|
56345
57302
|
return pkg.startsWith("./") || pkg.startsWith("../") || pkg.startsWith("/") || /^[A-Za-z]:[\\/]/.test(pkg);
|
|
56346
57303
|
}
|
|
@@ -56396,7 +57353,7 @@ async function importPlugin(pkg, configDir) {
|
|
|
56396
57353
|
const localPath = await fetchRemotePlugin(pkg);
|
|
56397
57354
|
modulePath = `${localPath}?t=${Date.now()}`;
|
|
56398
57355
|
} else if (isLocalPath(pkg)) {
|
|
56399
|
-
const absolutePath =
|
|
57356
|
+
const absolutePath = resolve6(configDir, pkg);
|
|
56400
57357
|
modulePath = `${absolutePath}?t=${Date.now()}`;
|
|
56401
57358
|
} else {
|
|
56402
57359
|
modulePath = pkg;
|
|
@@ -56512,11 +57469,6 @@ class PluginManager {
|
|
|
56512
57469
|
}
|
|
56513
57470
|
}
|
|
56514
57471
|
|
|
56515
|
-
// src/proxy.ts
|
|
56516
|
-
import { appendFile, readFile, unlink } from "fs/promises";
|
|
56517
|
-
import { tmpdir as tmpdir2 } from "os";
|
|
56518
|
-
import { join as join11 } from "path";
|
|
56519
|
-
|
|
56520
57472
|
// src/plugin-engine.ts
|
|
56521
57473
|
async function executeRequestPlugins(plugins, ctx, url2, headers, body) {
|
|
56522
57474
|
let currentUrl = url2;
|
|
@@ -56706,28 +57658,6 @@ function buildLogEvent(logMeta, targetUrl, proxyUrl, tsEnd, overrides) {
|
|
|
56706
57658
|
...overrides
|
|
56707
57659
|
};
|
|
56708
57660
|
}
|
|
56709
|
-
function createTempStreamCapturePath(requestId) {
|
|
56710
|
-
return join11(tmpdir2(), `local-router-stream-${requestId}-${Date.now()}.sse.raw`);
|
|
56711
|
-
}
|
|
56712
|
-
async function appendTempStreamCapture(filePath, chunk) {
|
|
56713
|
-
await appendFile(filePath, chunk);
|
|
56714
|
-
}
|
|
56715
|
-
async function flushTempCaptureToLogger(tempPath, requestId, dateStr, logger) {
|
|
56716
|
-
if (!logger)
|
|
56717
|
-
return null;
|
|
56718
|
-
try {
|
|
56719
|
-
const text2 = await readFile(tempPath, "utf-8").catch((err) => {
|
|
56720
|
-
if (err.code === "ENOENT")
|
|
56721
|
-
return "";
|
|
56722
|
-
throw err;
|
|
56723
|
-
});
|
|
56724
|
-
return logger.writeStreamFile(requestId, dateStr, text2);
|
|
56725
|
-
} finally {
|
|
56726
|
-
await unlink(tempPath).catch(() => {
|
|
56727
|
-
return;
|
|
56728
|
-
});
|
|
56729
|
-
}
|
|
56730
|
-
}
|
|
56731
57661
|
async function proxyRequest(c2, options) {
|
|
56732
57662
|
const { logMeta, plugins, pluginConfigs } = options;
|
|
56733
57663
|
const logger = getLogger();
|
|
@@ -56735,19 +57665,20 @@ async function proxyRequest(c2, options) {
|
|
|
56735
57665
|
const hasPlugins = plugins && plugins.length > 0;
|
|
56736
57666
|
let targetUrl = options.targetUrl;
|
|
56737
57667
|
let headers = buildUpstreamHeaders(c2.req.raw.headers, options.apiKey, options.authType);
|
|
56738
|
-
let
|
|
57668
|
+
let currentBody = options.body;
|
|
57669
|
+
const pluginCtx = Object.freeze({
|
|
57670
|
+
requestId: logMeta.requestId,
|
|
57671
|
+
provider: logMeta.provider,
|
|
57672
|
+
modelIn: logMeta.modelIn,
|
|
57673
|
+
modelOut: logMeta.modelOut,
|
|
57674
|
+
routeType: logMeta.routeType,
|
|
57675
|
+
isStream: logMeta.isStream
|
|
57676
|
+
});
|
|
57677
|
+
const wantsBodyLog = shouldLog && logger?.bodyPolicy !== "off";
|
|
57678
|
+
const requestBodySnapshot = wantsBodyLog ? JSON.parse(JSON.stringify(options.body)) : undefined;
|
|
56739
57679
|
const pluginLogOverrides = {};
|
|
56740
57680
|
if (hasPlugins) {
|
|
56741
|
-
const
|
|
56742
|
-
const ctx = {
|
|
56743
|
-
requestId: logMeta.requestId,
|
|
56744
|
-
provider: logMeta.provider,
|
|
56745
|
-
modelIn: logMeta.modelIn,
|
|
56746
|
-
modelOut: logMeta.modelOut,
|
|
56747
|
-
routeType: logMeta.routeType,
|
|
56748
|
-
isStream: logMeta.isStream
|
|
56749
|
-
};
|
|
56750
|
-
const result = await executeRequestPlugins(plugins, ctx, targetUrl, headers, bodyObj);
|
|
57681
|
+
const result = await executeRequestPlugins(plugins, pluginCtx, targetUrl, headers, currentBody);
|
|
56751
57682
|
if (pluginConfigs) {
|
|
56752
57683
|
pluginLogOverrides.plugins_request = pluginConfigs;
|
|
56753
57684
|
}
|
|
@@ -56756,20 +57687,20 @@ async function proxyRequest(c2, options) {
|
|
|
56756
57687
|
pluginLogOverrides.request_url_after_plugins = targetUrl;
|
|
56757
57688
|
}
|
|
56758
57689
|
headers = result.headers;
|
|
56759
|
-
|
|
56760
|
-
|
|
56761
|
-
bodyStr = newBodyStr;
|
|
57690
|
+
if (result.body !== currentBody) {
|
|
57691
|
+
currentBody = result.body;
|
|
56762
57692
|
pluginLogOverrides.request_body_after_plugins = result.body;
|
|
56763
57693
|
}
|
|
56764
57694
|
}
|
|
56765
|
-
const
|
|
57695
|
+
const wireBody = JSON.stringify(currentBody);
|
|
57696
|
+
const requestBody = requestBodySnapshot;
|
|
56766
57697
|
const proxy = options.proxy?.trim() ? options.proxy.trim() : undefined;
|
|
56767
57698
|
let upstreamRes;
|
|
56768
57699
|
try {
|
|
56769
57700
|
upstreamRes = await fetch(targetUrl, {
|
|
56770
57701
|
method: c2.req.method,
|
|
56771
57702
|
headers,
|
|
56772
|
-
body:
|
|
57703
|
+
body: wireBody,
|
|
56773
57704
|
...proxy ? { proxy } : {},
|
|
56774
57705
|
decompress: true
|
|
56775
57706
|
});
|
|
@@ -56799,15 +57730,7 @@ async function proxyRequest(c2, options) {
|
|
|
56799
57730
|
let sseHeaders = responseHeaders;
|
|
56800
57731
|
let sseTransform = null;
|
|
56801
57732
|
if (hasPlugins) {
|
|
56802
|
-
const
|
|
56803
|
-
requestId: logMeta.requestId,
|
|
56804
|
-
provider: logMeta.provider,
|
|
56805
|
-
modelIn: logMeta.modelIn,
|
|
56806
|
-
modelOut: logMeta.modelOut,
|
|
56807
|
-
routeType: logMeta.routeType,
|
|
56808
|
-
isStream: logMeta.isStream
|
|
56809
|
-
};
|
|
56810
|
-
const sseResult = await createSSEPluginTransform(plugins, ctx, upstreamRes.status, responseHeaders);
|
|
57733
|
+
const sseResult = await createSSEPluginTransform(plugins, pluginCtx, upstreamRes.status, responseHeaders);
|
|
56811
57734
|
sseStatus = sseResult.status;
|
|
56812
57735
|
sseHeaders = sseResult.headers;
|
|
56813
57736
|
sseTransform = sseResult.transform;
|
|
@@ -56816,47 +57739,75 @@ async function proxyRequest(c2, options) {
|
|
|
56816
57739
|
}
|
|
56817
57740
|
}
|
|
56818
57741
|
if (!shouldLog) {
|
|
56819
|
-
const
|
|
56820
|
-
return new Response(
|
|
57742
|
+
const outputBody = sseTransform ? upstreamRes.body.pipeThrough(sseTransform) : upstreamRes.body;
|
|
57743
|
+
return new Response(outputBody, {
|
|
56821
57744
|
status: sseStatus,
|
|
56822
57745
|
headers: sseHeaders
|
|
56823
57746
|
});
|
|
56824
57747
|
}
|
|
56825
|
-
const
|
|
56826
|
-
|
|
56827
|
-
|
|
56828
|
-
|
|
56829
|
-
|
|
56830
|
-
|
|
56831
|
-
|
|
56832
|
-
|
|
56833
|
-
|
|
56834
|
-
|
|
56835
|
-
|
|
56836
|
-
|
|
56837
|
-
|
|
57748
|
+
const capture = logger?.openStreamCapture(logMeta.requestId, dateStr) ?? null;
|
|
57749
|
+
const tokenUsageCollector = createTokenUsageStreamCollector(`${logMeta.provider} ${logMeta.routeType} ${logMeta.modelIn} ${logMeta.modelOut}`);
|
|
57750
|
+
let upstreamBytes = 0;
|
|
57751
|
+
let writeEventCalled = false;
|
|
57752
|
+
const finalizeAndWriteEvent = () => {
|
|
57753
|
+
if (writeEventCalled)
|
|
57754
|
+
return;
|
|
57755
|
+
writeEventCalled = true;
|
|
57756
|
+
const captureResult = capture?.finalize() ?? {
|
|
57757
|
+
bytesWritten: 0,
|
|
57758
|
+
truncated: false,
|
|
57759
|
+
filePath: null
|
|
57760
|
+
};
|
|
57761
|
+
const tokenUsage2 = tokenUsageCollector.getUsage();
|
|
57762
|
+
logger?.writeEvent(buildLogEvent(logMeta, targetUrl, proxy, Date.now(), {
|
|
57763
|
+
upstream_status: sseStatus,
|
|
57764
|
+
content_type_res: contentTypeRes,
|
|
57765
|
+
response_headers: sseHeaders,
|
|
57766
|
+
stream_bytes: upstreamBytes,
|
|
57767
|
+
provider_request_id: providerRequestId,
|
|
57768
|
+
...captureResult.filePath != null && { stream_file: captureResult.filePath },
|
|
57769
|
+
...captureResult.bytesWritten > 0 && {
|
|
57770
|
+
stream_file_bytes: captureResult.bytesWritten
|
|
57771
|
+
},
|
|
57772
|
+
...captureResult.truncated && { stream_file_truncated: true },
|
|
57773
|
+
...tokenUsage2 && { token_usage: tokenUsage2 },
|
|
57774
|
+
...requestBody !== undefined && { request_body: requestBody },
|
|
57775
|
+
...pluginLogOverrides
|
|
57776
|
+
}));
|
|
57777
|
+
};
|
|
57778
|
+
const upstreamReader = upstreamRes.body.getReader();
|
|
57779
|
+
const tappedStream = new ReadableStream({
|
|
57780
|
+
async pull(controller) {
|
|
57781
|
+
try {
|
|
57782
|
+
const { done, value } = await upstreamReader.read();
|
|
57783
|
+
if (done) {
|
|
57784
|
+
controller.close();
|
|
57785
|
+
finalizeAndWriteEvent();
|
|
57786
|
+
return;
|
|
57787
|
+
}
|
|
57788
|
+
upstreamBytes += value.byteLength;
|
|
57789
|
+
tokenUsageCollector.addChunk(value);
|
|
57790
|
+
capture?.write(value);
|
|
57791
|
+
controller.enqueue(value);
|
|
57792
|
+
} catch (err) {
|
|
57793
|
+
finalizeAndWriteEvent();
|
|
57794
|
+
controller.error(err);
|
|
57795
|
+
}
|
|
57796
|
+
},
|
|
57797
|
+
cancel(reason) {
|
|
57798
|
+
try {
|
|
57799
|
+
upstreamReader.cancel(reason).catch(() => {
|
|
57800
|
+
return;
|
|
57801
|
+
});
|
|
57802
|
+
} finally {
|
|
57803
|
+
finalizeAndWriteEvent();
|
|
56838
57804
|
}
|
|
56839
|
-
streamFile = await flushTempCaptureToLogger(tempPath, logMeta.requestId, dateStr, logger);
|
|
56840
|
-
} catch (err) {
|
|
56841
|
-
await unlink(tempPath).catch(() => {
|
|
56842
|
-
return;
|
|
56843
|
-
});
|
|
56844
|
-
console.error("[logger] \u6D41\u5F0F\u65E5\u5FD7\u5904\u7406\u5931\u8D25:", err);
|
|
56845
|
-
} finally {
|
|
56846
|
-
logger?.writeEvent(buildLogEvent(logMeta, targetUrl, proxy, Date.now(), {
|
|
56847
|
-
upstream_status: sseStatus,
|
|
56848
|
-
content_type_res: contentTypeRes,
|
|
56849
|
-
response_headers: sseHeaders,
|
|
56850
|
-
stream_bytes: streamBytes,
|
|
56851
|
-
provider_request_id: providerRequestId,
|
|
56852
|
-
...streamFile != null && { stream_file: streamFile },
|
|
56853
|
-
...requestBody !== undefined && { request_body: requestBody },
|
|
56854
|
-
...pluginLogOverrides
|
|
56855
|
-
}));
|
|
56856
57805
|
}
|
|
56857
|
-
})
|
|
56858
|
-
|
|
56859
|
-
|
|
57806
|
+
});
|
|
57807
|
+
let stream = tappedStream;
|
|
57808
|
+
if (sseTransform)
|
|
57809
|
+
stream = stream.pipeThrough(sseTransform);
|
|
57810
|
+
return new Response(stream, {
|
|
56860
57811
|
status: sseStatus,
|
|
56861
57812
|
headers: sseHeaders
|
|
56862
57813
|
});
|
|
@@ -56865,15 +57816,7 @@ async function proxyRequest(c2, options) {
|
|
|
56865
57816
|
let responseStatus = upstreamRes.status;
|
|
56866
57817
|
let finalResponseHeaders = responseHeaders;
|
|
56867
57818
|
if (hasPlugins) {
|
|
56868
|
-
const
|
|
56869
|
-
requestId: logMeta.requestId,
|
|
56870
|
-
provider: logMeta.provider,
|
|
56871
|
-
modelIn: logMeta.modelIn,
|
|
56872
|
-
modelOut: logMeta.modelOut,
|
|
56873
|
-
routeType: logMeta.routeType,
|
|
56874
|
-
isStream: logMeta.isStream
|
|
56875
|
-
};
|
|
56876
|
-
const result = await executeJsonResponsePlugins(plugins, ctx, upstreamRes.status, responseHeaders, responseText);
|
|
57819
|
+
const result = await executeJsonResponsePlugins(plugins, pluginCtx, upstreamRes.status, responseHeaders, responseText);
|
|
56877
57820
|
if (pluginConfigs) {
|
|
56878
57821
|
pluginLogOverrides.plugins_response = pluginConfigs;
|
|
56879
57822
|
}
|
|
@@ -56894,12 +57837,14 @@ async function proxyRequest(c2, options) {
|
|
|
56894
57837
|
});
|
|
56895
57838
|
}
|
|
56896
57839
|
const responseBytes = Buffer.byteLength(responseText, "utf-8");
|
|
57840
|
+
const tokenUsage = extractTokenUsageFromResponseText(responseText, "response_body", `${logMeta.provider} ${logMeta.routeType} ${logMeta.modelIn} ${logMeta.modelOut}`);
|
|
56897
57841
|
const eventOverrides = {
|
|
56898
57842
|
upstream_status: upstreamRes.status,
|
|
56899
57843
|
content_type_res: contentTypeRes,
|
|
56900
57844
|
response_headers: finalResponseHeaders,
|
|
56901
57845
|
response_bytes: responseBytes,
|
|
56902
57846
|
provider_request_id: providerRequestId,
|
|
57847
|
+
...tokenUsage && { token_usage: tokenUsage },
|
|
56903
57848
|
...pluginLogOverrides
|
|
56904
57849
|
};
|
|
56905
57850
|
if (requestBody !== undefined) {
|
|
@@ -56950,8 +57895,10 @@ function createModelRoutingHandler(options) {
|
|
|
56950
57895
|
return c2.json({ error: `provider "${target.provider}" \u672A\u5728\u914D\u7F6E\u4E2D\u5B9A\u4E49` }, 500);
|
|
56951
57896
|
}
|
|
56952
57897
|
payload.model = target.model;
|
|
56953
|
-
const body = JSON.stringify(payload);
|
|
56954
57898
|
const targetUrl = buildTargetUrl(provider.base);
|
|
57899
|
+
const contentLengthHeader = c2.req.header("content-length");
|
|
57900
|
+
const parsedContentLength = contentLengthHeader ? Number(contentLengthHeader) : NaN;
|
|
57901
|
+
const requestBytes = Number.isInteger(parsedContentLength) && parsedContentLength >= 0 ? parsedContentLength : Buffer.byteLength(JSON.stringify(payload), "utf-8");
|
|
56955
57902
|
const logMeta = {
|
|
56956
57903
|
requestId: crypto.randomUUID(),
|
|
56957
57904
|
tsStart: Date.now(),
|
|
@@ -56965,7 +57912,7 @@ function createModelRoutingHandler(options) {
|
|
|
56965
57912
|
path: c2.req.path,
|
|
56966
57913
|
contentTypeReq: c2.req.header("content-type") ?? null,
|
|
56967
57914
|
userAgent: c2.req.header("user-agent") ?? null,
|
|
56968
|
-
requestBytes
|
|
57915
|
+
requestBytes,
|
|
56969
57916
|
requestHeaders: collectHeaders(c2.req.raw.headers)
|
|
56970
57917
|
};
|
|
56971
57918
|
const plugins = pluginManager?.getPlugins(target.provider) ?? [];
|
|
@@ -56975,7 +57922,7 @@ function createModelRoutingHandler(options) {
|
|
|
56975
57922
|
apiKey: provider.apiKey,
|
|
56976
57923
|
proxy: provider.proxy,
|
|
56977
57924
|
authType,
|
|
56978
|
-
body,
|
|
57925
|
+
body: payload,
|
|
56979
57926
|
logMeta,
|
|
56980
57927
|
plugins: plugins.length > 0 ? plugins : undefined,
|
|
56981
57928
|
pluginConfigs: pluginConfigs.length > 0 ? pluginConfigs.map((lp) => ({
|
|
@@ -57183,7 +58130,7 @@ function createAdminApiRoutes(store, pluginManager, registerCleanup) {
|
|
|
57183
58130
|
const CRYPTO_SESSION_TTL_MS = 2 * 60 * 1000;
|
|
57184
58131
|
const CRYPTO_SESSION_MAX = 512;
|
|
57185
58132
|
const schemaPath = getBundledSchemaPath();
|
|
57186
|
-
const schemaJson = JSON.parse(
|
|
58133
|
+
const schemaJson = JSON.parse(readFileSync6(schemaPath, "utf-8"));
|
|
57187
58134
|
const pruneExpiredCryptoSessions = (now2 = Date.now()) => {
|
|
57188
58135
|
for (const [id, record2] of Array.from(cryptoSessions.entries())) {
|
|
57189
58136
|
if (now2 - record2.createdAt > CRYPTO_SESSION_TTL_MS) {
|
|
@@ -57788,7 +58735,7 @@ async function createApp(store, options) {
|
|
|
57788
58735
|
}
|
|
57789
58736
|
const stopLogStorageTask = startLogStorageBackgroundTask(config2.log);
|
|
57790
58737
|
options?.registerCleanup?.(stopLogStorageTask);
|
|
57791
|
-
const configDir = dirname3(
|
|
58738
|
+
const configDir = dirname3(resolve7(store.getPath()));
|
|
57792
58739
|
const pluginManager = new PluginManager(configDir);
|
|
57793
58740
|
const reloadResult = await pluginManager.reloadAll(config2.providers);
|
|
57794
58741
|
if (!reloadResult.ok) {
|
|
@@ -57904,24 +58851,24 @@ async function startServer(options) {
|
|
|
57904
58851
|
}
|
|
57905
58852
|
|
|
57906
58853
|
// src/cli/runtime.ts
|
|
57907
|
-
import { existsSync as
|
|
58854
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync7, rmSync, writeFileSync as writeFileSync4 } from "fs";
|
|
57908
58855
|
import { homedir as homedir2 } from "os";
|
|
57909
|
-
import { join as
|
|
58856
|
+
import { join as join11, resolve as resolve8 } from "path";
|
|
57910
58857
|
function getRuntimeDirs() {
|
|
57911
58858
|
const override = process.env.LOCAL_ROUTER_RUNTIME_DIR;
|
|
57912
|
-
const root2 = override?.trim() ? override.trim() :
|
|
58859
|
+
const root2 = override?.trim() ? override.trim() : join11(homedir2(), ".local-router");
|
|
57913
58860
|
return {
|
|
57914
58861
|
root: root2,
|
|
57915
|
-
run:
|
|
57916
|
-
logs:
|
|
58862
|
+
run: join11(root2, "run"),
|
|
58863
|
+
logs: join11(root2, "logs")
|
|
57917
58864
|
};
|
|
57918
58865
|
}
|
|
57919
58866
|
function getRuntimeFiles() {
|
|
57920
58867
|
const dirs = getRuntimeDirs();
|
|
57921
58868
|
return {
|
|
57922
|
-
pid:
|
|
57923
|
-
state:
|
|
57924
|
-
daemonLog:
|
|
58869
|
+
pid: join11(dirs.run, "local-router.pid"),
|
|
58870
|
+
state: join11(dirs.run, "status.json"),
|
|
58871
|
+
daemonLog: join11(dirs.logs, "daemon.log")
|
|
57925
58872
|
};
|
|
57926
58873
|
}
|
|
57927
58874
|
function ensureRuntimeDirs() {
|
|
@@ -57933,17 +58880,17 @@ function ensureRuntimeDirs() {
|
|
|
57933
58880
|
function writeRuntimeState(state) {
|
|
57934
58881
|
ensureRuntimeDirs();
|
|
57935
58882
|
const files = getRuntimeFiles();
|
|
57936
|
-
|
|
58883
|
+
writeFileSync4(files.pid, `${state.pid}
|
|
57937
58884
|
`, "utf-8");
|
|
57938
|
-
|
|
58885
|
+
writeFileSync4(files.state, JSON.stringify(state, null, 2), "utf-8");
|
|
57939
58886
|
}
|
|
57940
58887
|
function readRuntimeState() {
|
|
57941
58888
|
const files = getRuntimeFiles();
|
|
57942
|
-
if (!
|
|
58889
|
+
if (!existsSync9(files.state)) {
|
|
57943
58890
|
return null;
|
|
57944
58891
|
}
|
|
57945
58892
|
try {
|
|
57946
|
-
return JSON.parse(
|
|
58893
|
+
return JSON.parse(readFileSync7(files.state, "utf-8"));
|
|
57947
58894
|
} catch {
|
|
57948
58895
|
return null;
|
|
57949
58896
|
}
|
|
@@ -57954,7 +58901,7 @@ function clearRuntimeFiles() {
|
|
|
57954
58901
|
rmSync(files.state, { force: true });
|
|
57955
58902
|
}
|
|
57956
58903
|
function resolveConfigArgPath(pathValue) {
|
|
57957
|
-
return
|
|
58904
|
+
return resolve8(pathValue);
|
|
57958
58905
|
}
|
|
57959
58906
|
|
|
57960
58907
|
// src/cli/process.ts
|
|
@@ -58094,8 +59041,8 @@ async function startDaemon(flags) {
|
|
|
58094
59041
|
}
|
|
58095
59042
|
ensureRuntimeDirs();
|
|
58096
59043
|
const files = getRuntimeFiles();
|
|
58097
|
-
const stdoutFd =
|
|
58098
|
-
const stderrFd =
|
|
59044
|
+
const stdoutFd = openSync3(files.daemonLog, "a");
|
|
59045
|
+
const stderrFd = openSync3(files.daemonLog, "a");
|
|
58099
59046
|
const childArgs = [process.argv[1] ?? "src/cli.ts", "__run-server", "--mode", "daemon"];
|
|
58100
59047
|
if (flags.config) {
|
|
58101
59048
|
childArgs.push("--config", resolveConfigArgPath(flags.config));
|
|
@@ -58116,8 +59063,8 @@ async function startDaemon(flags) {
|
|
|
58116
59063
|
stderr: stderrFd,
|
|
58117
59064
|
detached: true
|
|
58118
59065
|
});
|
|
58119
|
-
|
|
58120
|
-
|
|
59066
|
+
closeSync3(stdoutFd);
|
|
59067
|
+
closeSync3(stderrFd);
|
|
58121
59068
|
child.unref();
|
|
58122
59069
|
for (let i = 0;i < 24; i += 1) {
|
|
58123
59070
|
await sleep(250);
|
|
@@ -58132,7 +59079,7 @@ async function startDaemon(flags) {
|
|
|
58132
59079
|
}
|
|
58133
59080
|
let tail = "";
|
|
58134
59081
|
try {
|
|
58135
|
-
const content =
|
|
59082
|
+
const content = readFileSync8(files.daemonLog, "utf-8");
|
|
58136
59083
|
tail = content.split(`
|
|
58137
59084
|
`).slice(-20).join(`
|
|
58138
59085
|
`);
|
|
@@ -58165,11 +59112,11 @@ async function stopProcess(graceMs = 8000) {
|
|
|
58165
59112
|
}
|
|
58166
59113
|
function readLogDelta(filePath, offset) {
|
|
58167
59114
|
try {
|
|
58168
|
-
const stats =
|
|
59115
|
+
const stats = statSync4(filePath);
|
|
58169
59116
|
if (stats.size <= offset) {
|
|
58170
59117
|
return { content: "", nextOffset: offset };
|
|
58171
59118
|
}
|
|
58172
|
-
const full =
|
|
59119
|
+
const full = readFileSync8(filePath, "utf-8");
|
|
58173
59120
|
const content = full.slice(offset);
|
|
58174
59121
|
return { content, nextOffset: full.length };
|
|
58175
59122
|
} catch {
|
|
@@ -59182,13 +60129,13 @@ init_config();
|
|
|
59182
60129
|
init_config_validate();
|
|
59183
60130
|
init_errors();
|
|
59184
60131
|
init_output();
|
|
59185
|
-
import { existsSync as
|
|
60132
|
+
import { existsSync as existsSync11 } from "fs";
|
|
59186
60133
|
import { parseArgs as parseArgs4 } from "util";
|
|
59187
60134
|
init_registry();
|
|
59188
60135
|
function tryRead(configArg) {
|
|
59189
60136
|
try {
|
|
59190
60137
|
const path = resolveConfigPath(configArg);
|
|
59191
|
-
if (!
|
|
60138
|
+
if (!existsSync11(path)) {
|
|
59192
60139
|
return { path, config: null, error: `\u914D\u7F6E\u6587\u4EF6\u4E0D\u5B58\u5728: ${path}` };
|
|
59193
60140
|
}
|
|
59194
60141
|
const config2 = loadConfig(path);
|
|
@@ -59623,7 +60570,7 @@ defineCommand({
|
|
|
59623
60570
|
});
|
|
59624
60571
|
|
|
59625
60572
|
// src/cli/handlers/introspection.ts
|
|
59626
|
-
import { readFileSync as
|
|
60573
|
+
import { readFileSync as readFileSync10 } from "fs";
|
|
59627
60574
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
59628
60575
|
import { parseArgs as parseArgs5 } from "util";
|
|
59629
60576
|
|
|
@@ -59899,7 +60846,7 @@ defineCommand({
|
|
|
59899
60846
|
hint: "\u8BF7\u786E\u8BA4 npm \u5305\u5B8C\u6574\u6027\uFF0C\u6216\u5F00\u53D1\u6A21\u5F0F\u4E0B\u4ECE\u6E90\u7801\u8FD0\u884C"
|
|
59900
60847
|
});
|
|
59901
60848
|
}
|
|
59902
|
-
const text2 =
|
|
60849
|
+
const text2 = readFileSync10(fileURLToPath2(schemaUrl), "utf-8");
|
|
59903
60850
|
const data2 = JSON.parse(text2);
|
|
59904
60851
|
emitResult(ctx, {
|
|
59905
60852
|
command: "schema.config",
|
|
@@ -60119,7 +61066,7 @@ defineCommand({
|
|
|
60119
61066
|
|
|
60120
61067
|
// src/cli/handlers/lifecycle.ts
|
|
60121
61068
|
init_config();
|
|
60122
|
-
import { existsSync as
|
|
61069
|
+
import { existsSync as existsSync12, readFileSync as readFileSync11 } from "fs";
|
|
60123
61070
|
import { setTimeout as sleep3 } from "timers/promises";
|
|
60124
61071
|
import { parseArgs as parseArgs6 } from "util";
|
|
60125
61072
|
init_errors();
|
|
@@ -60424,10 +61371,10 @@ defineCommand({
|
|
|
60424
61371
|
})
|
|
60425
61372
|
});
|
|
60426
61373
|
function readLastLines(filePath, lines) {
|
|
60427
|
-
if (!
|
|
61374
|
+
if (!existsSync12(filePath)) {
|
|
60428
61375
|
return { content: "", offset: 0 };
|
|
60429
61376
|
}
|
|
60430
|
-
const full =
|
|
61377
|
+
const full = readFileSync11(filePath, "utf-8");
|
|
60431
61378
|
const rendered = full.split(`
|
|
60432
61379
|
`).slice(-lines).join(`
|
|
60433
61380
|
`);
|
|
@@ -60458,7 +61405,7 @@ defineCommand({
|
|
|
60458
61405
|
const linesRaw = parsed.values.lines ?? "100";
|
|
60459
61406
|
const lines = Number.parseInt(linesRaw, 10);
|
|
60460
61407
|
const initial = readLastLines(files.daemonLog, Number.isFinite(lines) ? lines : 100);
|
|
60461
|
-
if (!
|
|
61408
|
+
if (!existsSync12(files.daemonLog)) {
|
|
60462
61409
|
emitResult(ctx, {
|
|
60463
61410
|
command: "logs.daemon",
|
|
60464
61411
|
data: { exists: false, path: files.daemonLog, lines: [] },
|