@tonyclaw/llm-inspector 1.10.0 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/index-BIZeMR5N.js +105 -0
- package/.output/public/assets/index-SmtLx1lM.css +1 -0
- package/.output/public/assets/main-QfbQ5oBP.js +17 -0
- package/.output/server/_libs/@radix-ui/react-use-controllable-state+[...].mjs +1 -1
- package/.output/server/_libs/ajv-formats.mjs +18 -18
- package/.output/server/_libs/ajv.mjs +6306 -942
- package/.output/server/_libs/cookie-es.mjs +21 -7
- package/.output/server/_libs/diff.mjs +320 -0
- package/.output/server/_libs/floating-ui__core.mjs +17 -18
- package/.output/server/_libs/floating-ui__dom.mjs +20 -24
- package/.output/server/_libs/floating-ui__react-dom.mjs +28 -55
- package/.output/server/_libs/floating-ui__utils.mjs +36 -36
- package/.output/server/_libs/h3-v2.mjs +9 -20
- package/.output/server/_libs/isbot.mjs +2 -3
- package/.output/server/_libs/json-schema-traverse.mjs +90 -1
- package/.output/server/_libs/jszip.mjs +28 -28
- package/.output/server/_libs/lucide-react.mjs +103 -80
- package/.output/server/_libs/mdast-util-from-markdown.mjs +1 -1
- package/.output/server/_libs/pako.mjs +13 -13
- package/.output/server/_libs/property-information.mjs +1 -13
- package/.output/server/_libs/radix-ui__react-collection.mjs +1 -1
- package/.output/server/_libs/radix-ui__react-id.mjs +1 -1
- package/.output/server/_libs/react-dom.mjs +10 -10
- package/.output/server/_libs/react.mjs +44 -44
- package/.output/server/_libs/readable-stream.mjs +15 -15
- package/.output/server/_libs/rou3.mjs +7 -1
- package/.output/server/_libs/safe-buffer.mjs +3 -3
- package/.output/server/_libs/semver.mjs +10 -10
- package/.output/server/_libs/seroval-plugins.mjs +5 -5
- package/.output/server/_libs/seroval.mjs +596 -606
- package/.output/server/_libs/srvx.mjs +245 -10
- package/.output/server/_libs/swr.mjs +1 -1
- package/.output/server/_libs/tailwind-merge.mjs +41 -286
- package/.output/server/_libs/tanstack__history.mjs +44 -31
- package/.output/server/_libs/tanstack__react-router.mjs +1089 -780
- package/.output/server/_libs/tanstack__react-store.mjs +1 -1
- package/.output/server/_libs/tanstack__react-virtual.mjs +7 -82
- package/.output/server/_libs/tanstack__router-core.mjs +2331 -2226
- package/.output/server/_libs/tanstack__virtual-core.mjs +17 -58
- package/.output/server/_libs/tiny-invariant.mjs +12 -0
- package/.output/server/_libs/tiny-warning.mjs +5 -0
- package/.output/server/_libs/ungap__structured-clone.mjs +2 -14
- package/.output/server/_libs/use-sync-external-store.mjs +1 -1
- package/.output/server/_libs/zod.mjs +207 -505
- package/.output/server/_ssr/{index-XyU4WVWt.mjs → index-I_Nx8QEB.mjs} +532 -79
- package/.output/server/_ssr/index.mjs +776 -1098
- package/.output/server/_ssr/{router-BR4XO8XJ.mjs → router-BVxfVFsb.mjs} +98 -53
- package/.output/server/_tanstack-start-manifest_v-Cm2Owszu.mjs +4 -0
- package/.output/server/index.mjs +28 -28
- package/README.md +1 -1
- package/package.json +2 -1
- package/src/components/ProxyViewer.tsx +10 -3
- package/src/components/ProxyViewerContainer.tsx +6 -0
- package/src/components/proxy-viewer/ConversationGroup.tsx +10 -1
- package/src/components/proxy-viewer/LogEntry.tsx +114 -5
- package/src/components/proxy-viewer/diff/DiffView.tsx +319 -0
- package/src/components/proxy-viewer/diff/computeDiff.ts +178 -0
- package/src/components/proxy-viewer/diff/index.ts +3 -0
- package/src/components/ui/crab-logo.tsx +45 -0
- package/src/proxy/config.ts +3 -17
- package/src/proxy/dataDir.ts +56 -0
- package/src/proxy/formats/anthropic/stream.ts +5 -2
- package/src/proxy/formats/jsonSchema.ts +12 -0
- package/src/proxy/formats/openai/stream.ts +21 -16
- package/src/proxy/handler.ts +15 -5
- package/src/proxy/providers.ts +61 -1
- package/src/proxy/schemas.ts +42 -0
- package/src/proxy/store.ts +6 -23
- package/.output/public/assets/index-BFuUQkWu.js +0 -11
- package/.output/public/assets/index-F2ojFPJp.css +0 -1
- package/.output/public/assets/index-nKnhyW4Y.js +0 -122
- package/.output/server/_ssr/empty-plugin-adapters-BFgPZ6_d.mjs +0 -6
- package/.output/server/_tanstack-start-manifest_v-DFoSjBkb.mjs +0 -4
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import { c as createRouter, a as createRootRoute, b as createFileRoute, l as lazyRouteComponent, O as Outlet, H as HeadContent, S as Scripts } from "../_libs/tanstack__react-router.mjs";
|
|
2
2
|
import { j as jsxRuntimeExports } from "../_libs/react.mjs";
|
|
3
3
|
import { S as SWRConfig } from "../_libs/swr.mjs";
|
|
4
|
-
import { mkdirSync, writeFileSync, renameSync, copyFileSync, unlinkSync,
|
|
4
|
+
import { existsSync, mkdirSync, writeFileSync, renameSync, copyFileSync, unlinkSync, readFileSync } from "node:fs";
|
|
5
5
|
import path, { join, isAbsolute, dirname } from "node:path";
|
|
6
|
-
import { mkdir, appendFile, readdir, stat, unlink,
|
|
6
|
+
import { mkdir, appendFile, readFile, readdir, stat, unlink, writeFile } from "node:fs/promises";
|
|
7
7
|
import { C as Conf } from "../_libs/conf.mjs";
|
|
8
8
|
import { randomUUID } from "crypto";
|
|
9
9
|
import { exec } from "node:child_process";
|
|
10
10
|
import { promisify } from "node:util";
|
|
11
|
-
import { o as object, s as string, _ as _enum, u as union, a as array, b as boolean, n as number, d as discriminatedUnion, l as literal, r as record, c as
|
|
11
|
+
import { o as object, s as string, _ as _enum, u as union, a as array, b as boolean, n as number, d as discriminatedUnion, l as literal, r as record, c as _null, e as lazy, f as unknown } from "../_libs/zod.mjs";
|
|
12
|
+
import "../_libs/tiny-warning.mjs";
|
|
12
13
|
import "../_libs/tanstack__router-core.mjs";
|
|
13
|
-
import "../_libs/tanstack__history.mjs";
|
|
14
14
|
import "../_libs/cookie-es.mjs";
|
|
15
|
+
import "../_libs/tanstack__history.mjs";
|
|
16
|
+
import "../_libs/tiny-invariant.mjs";
|
|
15
17
|
import "../_libs/seroval.mjs";
|
|
16
18
|
import "../_libs/seroval-plugins.mjs";
|
|
17
19
|
import "node:stream/web";
|
|
@@ -42,7 +44,7 @@ import "../_libs/debounce-fn.mjs";
|
|
|
42
44
|
import "../_libs/mimic-function.mjs";
|
|
43
45
|
import "../_libs/semver.mjs";
|
|
44
46
|
import "../_libs/uint8array-extras.mjs";
|
|
45
|
-
const appCss = "/assets/index-
|
|
47
|
+
const appCss = "/assets/index-SmtLx1lM.css";
|
|
46
48
|
const Route$h = createRootRoute({
|
|
47
49
|
head: () => ({
|
|
48
50
|
meta: [
|
|
@@ -66,7 +68,7 @@ function RootDocument({ children }) {
|
|
|
66
68
|
] })
|
|
67
69
|
] });
|
|
68
70
|
}
|
|
69
|
-
const $$splitComponentImporter = () => import("./index-
|
|
71
|
+
const $$splitComponentImporter = () => import("./index-I_Nx8QEB.mjs");
|
|
70
72
|
const Route$g = createFileRoute("/")({
|
|
71
73
|
component: lazyRouteComponent($$splitComponentImporter, "component")
|
|
72
74
|
});
|
|
@@ -328,6 +330,9 @@ const JsonValueSchema = lazy(
|
|
|
328
330
|
record(string(), JsonValueSchema)
|
|
329
331
|
])
|
|
330
332
|
);
|
|
333
|
+
function trustAsJsonValue(value) {
|
|
334
|
+
return value;
|
|
335
|
+
}
|
|
331
336
|
const CacheControl = object({
|
|
332
337
|
type: string(),
|
|
333
338
|
ttl: string().optional(),
|
|
@@ -698,6 +703,25 @@ function extractModelFromBody(body) {
|
|
|
698
703
|
return null;
|
|
699
704
|
}
|
|
700
705
|
}
|
|
706
|
+
const LooseRequestSchema = object({
|
|
707
|
+
model: string().optional(),
|
|
708
|
+
metadata: object({ user_id: string().optional() }).passthrough().optional()
|
|
709
|
+
});
|
|
710
|
+
function extractRequestMetadata(body, headers) {
|
|
711
|
+
if (body === null) return { model: null, sessionId: null };
|
|
712
|
+
try {
|
|
713
|
+
const json = JSON.parse(body);
|
|
714
|
+
const loose = LooseRequestSchema.safeParse(json);
|
|
715
|
+
if (loose.success) {
|
|
716
|
+
return {
|
|
717
|
+
model: loose.data.model ?? null,
|
|
718
|
+
sessionId: loose.data.metadata?.user_id ?? headers.get("x-session-affinity") ?? null
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
} catch {
|
|
722
|
+
}
|
|
723
|
+
return { model: null, sessionId: null };
|
|
724
|
+
}
|
|
701
725
|
function parseRequest(rawBody) {
|
|
702
726
|
if (rawBody === null) return null;
|
|
703
727
|
try {
|
|
@@ -777,10 +801,6 @@ function readChunks(path2) {
|
|
|
777
801
|
return null;
|
|
778
802
|
}
|
|
779
803
|
}
|
|
780
|
-
const LooseRequestSchema = object({
|
|
781
|
-
model: string().optional(),
|
|
782
|
-
metadata: object({ user_id: string().optional() }).passthrough().optional()
|
|
783
|
-
});
|
|
784
804
|
const MAX_MEMORY_CACHE = 100;
|
|
785
805
|
const memoryCache = /* @__PURE__ */ new Map();
|
|
786
806
|
let headId = null;
|
|
@@ -840,23 +860,10 @@ async function addTestLogEntry(entry) {
|
|
|
840
860
|
emitLogUpdate(log);
|
|
841
861
|
return log;
|
|
842
862
|
}
|
|
843
|
-
async function createLog(method, path2, requestBody, headers, clientInfo, rawHeaders, upstreamHeaders, apiFormat = "unknown") {
|
|
844
|
-
let model = null;
|
|
845
|
-
let sessionId = null;
|
|
846
|
-
if (requestBody !== null) {
|
|
847
|
-
try {
|
|
848
|
-
const json = JSON.parse(requestBody);
|
|
849
|
-
const loose = LooseRequestSchema.safeParse(json);
|
|
850
|
-
if (loose.success) {
|
|
851
|
-
model = loose.data.model ?? null;
|
|
852
|
-
sessionId = loose.data.metadata?.user_id ?? headers.get("x-session-affinity") ?? null;
|
|
853
|
-
}
|
|
854
|
-
} catch {
|
|
855
|
-
}
|
|
856
|
-
}
|
|
863
|
+
async function createLog(method, path2, requestBody, headers, clientInfo, rawHeaders, upstreamHeaders, apiFormat = "unknown", model = null, sessionId = null, preAcquiredId) {
|
|
857
864
|
const userAgent = headers.get("user-agent");
|
|
858
865
|
const origin = headers.get("origin");
|
|
859
|
-
const id = await getNextLogId();
|
|
866
|
+
const id = preAcquiredId ?? await getNextLogId();
|
|
860
867
|
const log = {
|
|
861
868
|
id,
|
|
862
869
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -1098,7 +1105,10 @@ function extractAnthropicStream(raw, log, fallbackModel, collectChunks) {
|
|
|
1098
1105
|
index: chunkIndex,
|
|
1099
1106
|
timestamp: Date.now() - streamStartMs,
|
|
1100
1107
|
type: data.type,
|
|
1101
|
-
data
|
|
1108
|
+
// `data` has already been validated by SseEventSchema above; the
|
|
1109
|
+
// recursive JsonValue walk is a redundant full-tree zod parse on
|
|
1110
|
+
// every SSE event. See trustAsJsonValue for the rationale.
|
|
1111
|
+
data: trustAsJsonValue(data)
|
|
1102
1112
|
});
|
|
1103
1113
|
}
|
|
1104
1114
|
chunkIndex++;
|
|
@@ -1325,7 +1335,7 @@ function extractOpenAIStream(raw, log, fallbackModel, collectChunks = true) {
|
|
|
1325
1335
|
let completionTokens = 0;
|
|
1326
1336
|
let usageCaptured = false;
|
|
1327
1337
|
let started = false;
|
|
1328
|
-
const toolCalls =
|
|
1338
|
+
const toolCalls = /* @__PURE__ */ new Map();
|
|
1329
1339
|
const MAX_CHUNKS = 1e3;
|
|
1330
1340
|
let chunkIndex = 0;
|
|
1331
1341
|
let streamStartMs = 0;
|
|
@@ -1352,15 +1362,12 @@ function extractOpenAIStream(raw, log, fallbackModel, collectChunks = true) {
|
|
|
1352
1362
|
const chunk = chunkResult.data;
|
|
1353
1363
|
if (chunkIndex === 0) streamStartMs = Date.now();
|
|
1354
1364
|
if (collectChunks === true && chunks.length < MAX_CHUNKS) {
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
data: jsonResult.data
|
|
1362
|
-
});
|
|
1363
|
-
}
|
|
1365
|
+
chunks.push({
|
|
1366
|
+
index: chunkIndex,
|
|
1367
|
+
timestamp: Date.now() - streamStartMs,
|
|
1368
|
+
type: "chat.completion.chunk",
|
|
1369
|
+
data: trustAsJsonValue(chunk)
|
|
1370
|
+
});
|
|
1364
1371
|
}
|
|
1365
1372
|
chunkIndex++;
|
|
1366
1373
|
if (!started) {
|
|
@@ -1394,14 +1401,14 @@ function extractOpenAIStream(raw, log, fallbackModel, collectChunks = true) {
|
|
|
1394
1401
|
}
|
|
1395
1402
|
if (delta.tool_calls !== void 0 && delta.tool_calls !== null) {
|
|
1396
1403
|
for (const tc of delta.tool_calls) {
|
|
1397
|
-
let existing = toolCalls.
|
|
1398
|
-
if (
|
|
1404
|
+
let existing = toolCalls.get(tc.index);
|
|
1405
|
+
if (existing === void 0) {
|
|
1399
1406
|
existing = {
|
|
1400
1407
|
index: tc.index,
|
|
1401
1408
|
type: "function",
|
|
1402
1409
|
function: { name: "", arguments: "" }
|
|
1403
1410
|
};
|
|
1404
|
-
toolCalls.
|
|
1411
|
+
toolCalls.set(tc.index, existing);
|
|
1405
1412
|
}
|
|
1406
1413
|
if (tc.id !== void 0) existing.id = tc.id;
|
|
1407
1414
|
if (tc.function?.name !== void 0) existing.function.name += tc.function.name;
|
|
@@ -1428,7 +1435,7 @@ function extractOpenAIStream(raw, log, fallbackModel, collectChunks = true) {
|
|
|
1428
1435
|
content: completionContent
|
|
1429
1436
|
};
|
|
1430
1437
|
if (reasoningContent) message.reasoning_content = reasoningContent;
|
|
1431
|
-
if (toolCalls.
|
|
1438
|
+
if (toolCalls.size > 0) message.tool_calls = [...toolCalls.values()];
|
|
1432
1439
|
return JSON.stringify({
|
|
1433
1440
|
id,
|
|
1434
1441
|
object: "chat.completion",
|
|
@@ -1549,6 +1556,22 @@ const alibabaProvider = {
|
|
|
1549
1556
|
}
|
|
1550
1557
|
};
|
|
1551
1558
|
registry.register(alibabaProvider);
|
|
1559
|
+
function getDataDir() {
|
|
1560
|
+
const isWindows = process.platform === "win32";
|
|
1561
|
+
const base = isWindows ? process.env["APPDATA"] ?? join(process.env["USERPROFILE"] ?? "C:\\", ".llm-inspector") : process.env["HOME"] ?? "/tmp";
|
|
1562
|
+
const dirEnv = process.env["LLM_INSPECTOR_DATA_DIR"];
|
|
1563
|
+
if (dirEnv !== void 0 && dirEnv !== "") {
|
|
1564
|
+
return isAbsolute(dirEnv) ? dirEnv : join(base, dirEnv);
|
|
1565
|
+
}
|
|
1566
|
+
const legacyDirEnv = process.env["LLM_INSPECTOR_CONFIG_DIR"];
|
|
1567
|
+
if (legacyDirEnv !== void 0 && legacyDirEnv !== "") {
|
|
1568
|
+
return isAbsolute(legacyDirEnv) ? legacyDirEnv : join(base, legacyDirEnv);
|
|
1569
|
+
}
|
|
1570
|
+
return join(base, ".llm-inspector");
|
|
1571
|
+
}
|
|
1572
|
+
function hasExplicitDataDir() {
|
|
1573
|
+
return process.env["LLM_INSPECTOR_DATA_DIR"] !== void 0 && process.env["LLM_INSPECTOR_DATA_DIR"] !== "" || process.env["LLM_INSPECTOR_CONFIG_DIR"] !== void 0 && process.env["LLM_INSPECTOR_CONFIG_DIR"] !== "";
|
|
1574
|
+
}
|
|
1552
1575
|
const ProviderConfigSchema = object({
|
|
1553
1576
|
id: string(),
|
|
1554
1577
|
name: string(),
|
|
@@ -1572,14 +1595,42 @@ const ProviderConfigSchema = object({
|
|
|
1572
1595
|
object({
|
|
1573
1596
|
providers: array(ProviderConfigSchema)
|
|
1574
1597
|
});
|
|
1598
|
+
function migrateFromLegacyConfLocation(targetStore) {
|
|
1599
|
+
if (process.env["LLM_INSPECTOR_CONFIG_PATH"] !== void 0) return;
|
|
1600
|
+
if (hasExplicitDataDir()) return;
|
|
1601
|
+
if (targetStore.get("providers", []).length > 0) return;
|
|
1602
|
+
let legacyStore;
|
|
1603
|
+
try {
|
|
1604
|
+
legacyStore = new Conf({
|
|
1605
|
+
projectName: "llm-inspector",
|
|
1606
|
+
defaults: { providers: [] }
|
|
1607
|
+
});
|
|
1608
|
+
const legacyProviders = legacyStore.get("providers", []);
|
|
1609
|
+
if (legacyProviders.length === 0) return;
|
|
1610
|
+
targetStore.set("providers", legacyProviders);
|
|
1611
|
+
logger.info(
|
|
1612
|
+
`[providers] Migrated ${legacyProviders.length} provider(s) from ${legacyStore.path} to ${targetStore.path}`
|
|
1613
|
+
);
|
|
1614
|
+
} catch (err) {
|
|
1615
|
+
logger.warn(`[providers] Legacy migration failed: ${String(err)}`);
|
|
1616
|
+
} finally {
|
|
1617
|
+
legacyStore?._closeWatcher();
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1575
1620
|
const configPath = process.env["LLM_INSPECTOR_CONFIG_PATH"];
|
|
1621
|
+
const dataDir = getDataDir();
|
|
1622
|
+
if (configPath === void 0) {
|
|
1623
|
+
mkdirSync(dataDir, { recursive: true });
|
|
1624
|
+
}
|
|
1576
1625
|
const store = new Conf({
|
|
1577
|
-
|
|
1626
|
+
cwd: dataDir,
|
|
1627
|
+
configName: "providers",
|
|
1578
1628
|
defaults: {
|
|
1579
1629
|
providers: []
|
|
1580
1630
|
},
|
|
1581
1631
|
...configPath !== void 0 ? { path: configPath } : {}
|
|
1582
1632
|
});
|
|
1633
|
+
migrateFromLegacyConfLocation(store);
|
|
1583
1634
|
function migrateProvider(p) {
|
|
1584
1635
|
const currentAnthropicUrl = p.anthropicBaseUrl ?? "";
|
|
1585
1636
|
const currentOpenaiUrl = p.openaiBaseUrl ?? "";
|
|
@@ -1930,15 +1981,6 @@ async function getClientInfo(request) {
|
|
|
1930
1981
|
const RuntimeConfigSchema = object({
|
|
1931
1982
|
stripClaudeCodeBillingHeader: boolean()
|
|
1932
1983
|
});
|
|
1933
|
-
function getDataDir() {
|
|
1934
|
-
const isWindows = process.platform === "win32";
|
|
1935
|
-
const base = isWindows ? process.env["APPDATA"] ?? join(process.env["USERPROFILE"] ?? "C:\\", ".llm-inspector") : process.env["HOME"] ?? "/tmp";
|
|
1936
|
-
const configDirEnv = process.env["LLM_INSPECTOR_CONFIG_DIR"];
|
|
1937
|
-
if (configDirEnv !== void 0) {
|
|
1938
|
-
return isAbsolute(configDirEnv) ? configDirEnv : join(base, configDirEnv);
|
|
1939
|
-
}
|
|
1940
|
-
return join(base, ".llm-inspector");
|
|
1941
|
-
}
|
|
1942
1984
|
function getConfigFilePath() {
|
|
1943
1985
|
return join(getDataDir(), "config.json");
|
|
1944
1986
|
}
|
|
@@ -2265,7 +2307,7 @@ async function handleProxy(req) {
|
|
|
2265
2307
|
bodyToForward = stripped.body;
|
|
2266
2308
|
}
|
|
2267
2309
|
}
|
|
2268
|
-
const model
|
|
2310
|
+
const { model, sessionId } = extractRequestMetadata(requestBody, req.headers);
|
|
2269
2311
|
const matchedProviderConfig = model !== null ? findProviderByModel(model) : null;
|
|
2270
2312
|
const upstreamBase = selectUpstreamBase$1(parsed.isChatCompletions, matchedProviderConfig);
|
|
2271
2313
|
const upstreamUrl = buildUpstreamUrl$1(upstreamBase, parsed.normalizedPath);
|
|
@@ -2290,7 +2332,7 @@ async function handleProxy(req) {
|
|
|
2290
2332
|
if (formatHandler === null) {
|
|
2291
2333
|
return new Response("Forbidden: unsupported format", { status: STATUS_FORBIDDEN });
|
|
2292
2334
|
}
|
|
2293
|
-
const clientInfo = await getClientInfo(req);
|
|
2335
|
+
const [clientInfo, preAcquiredId] = await Promise.all([getClientInfo(req), getNextLogId()]);
|
|
2294
2336
|
const upstreamHeadersObj = {};
|
|
2295
2337
|
upstreamHeaders.forEach((value, key) => {
|
|
2296
2338
|
upstreamHeadersObj[key.toLowerCase()] = value;
|
|
@@ -2303,7 +2345,10 @@ async function handleProxy(req) {
|
|
|
2303
2345
|
clientInfo,
|
|
2304
2346
|
rawHeaders,
|
|
2305
2347
|
upstreamHeadersObj,
|
|
2306
|
-
formatHandler.format
|
|
2348
|
+
formatHandler.format,
|
|
2349
|
+
model,
|
|
2350
|
+
sessionId,
|
|
2351
|
+
preAcquiredId
|
|
2307
2352
|
);
|
|
2308
2353
|
let upstreamRes;
|
|
2309
2354
|
try {
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
const tsrStartManifest = () => ({ "routes": { "__root__": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/__root.tsx", "children": ["/", "/api/config", "/api/health", "/api/logs", "/api/models", "/api/providers", "/api/sessions", "/proxy/$"], "preloads": ["/assets/main-QfbQ5oBP.js"], "assets": [] }, "/": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-BIZeMR5N.js"] }, "/api/config": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/config.ts", "children": ["/api/config/paths"] }, "/api/health": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/health.ts" }, "/api/logs": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/logs.ts", "children": ["/api/logs/$id", "/api/logs/stream"] }, "/api/models": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/models.ts" }, "/api/providers": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/providers.ts", "children": ["/api/providers/$providerId", "/api/providers/export", "/api/providers/import"] }, "/api/sessions": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/sessions.ts" }, "/proxy/$": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/proxy/$.ts" }, "/api/config/paths": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/config.paths.ts" }, "/api/logs/$id": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/logs.$id.ts", "children": ["/api/logs/$id/chunks", "/api/logs/$id/replay"] }, "/api/logs/stream": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/logs.stream.ts" }, "/api/providers/$providerId": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/providers.$providerId.ts", "children": ["/api/providers/$providerId/test"] }, "/api/providers/export": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/providers.export.ts" }, "/api/providers/import": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/providers.import.ts" }, "/api/logs/$id/chunks": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/logs.$id.chunks.ts" }, "/api/logs/$id/replay": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/logs.$id.replay.ts" }, "/api/providers/$providerId/test": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/providers.$providerId.test.ts" } }, "clientEntry": "/assets/main-QfbQ5oBP.js" });
|
|
2
|
+
export {
|
|
3
|
+
tsrStartManifest
|
|
4
|
+
};
|
package/.output/server/index.mjs
CHANGED
|
@@ -38,51 +38,51 @@ const assets = {
|
|
|
38
38
|
"/assets/alibaba-TTwafVwX.svg": {
|
|
39
39
|
"type": "image/svg+xml",
|
|
40
40
|
"etag": '"171b-6dyV5K8QjiaY35sN9qNprh9zDIs"',
|
|
41
|
-
"mtime": "2026-06-
|
|
41
|
+
"mtime": "2026-06-06T08:18:21.509Z",
|
|
42
42
|
"size": 5915,
|
|
43
43
|
"path": "../public/assets/alibaba-TTwafVwX.svg"
|
|
44
44
|
},
|
|
45
|
-
"/assets/index-F2ojFPJp.css": {
|
|
46
|
-
"type": "text/css; charset=utf-8",
|
|
47
|
-
"etag": '"10ec0-6l9kmLR4Pe6YaaY4kOiwBp4tyLs"',
|
|
48
|
-
"mtime": "2026-06-06T06:10:03.368Z",
|
|
49
|
-
"size": 69312,
|
|
50
|
-
"path": "../public/assets/index-F2ojFPJp.css"
|
|
51
|
-
},
|
|
52
|
-
"/assets/index-BFuUQkWu.js": {
|
|
53
|
-
"type": "text/javascript; charset=utf-8",
|
|
54
|
-
"etag": '"5103c-l+8AHdGxgCZ2mb5rpeb9f++id64"',
|
|
55
|
-
"mtime": "2026-06-06T06:10:03.369Z",
|
|
56
|
-
"size": 331836,
|
|
57
|
-
"path": "../public/assets/index-BFuUQkWu.js"
|
|
58
|
-
},
|
|
59
45
|
"/assets/minimax-BPMzvuL-.jpeg": {
|
|
60
46
|
"type": "image/jpeg",
|
|
61
47
|
"etag": '"1b06-IwivU89ko5UTMUM1/t7hn4sQK9A"',
|
|
62
|
-
"mtime": "2026-06-
|
|
48
|
+
"mtime": "2026-06-06T08:18:21.509Z",
|
|
63
49
|
"size": 6918,
|
|
64
50
|
"path": "../public/assets/minimax-BPMzvuL-.jpeg"
|
|
65
51
|
},
|
|
66
|
-
"/assets/
|
|
67
|
-
"type": "
|
|
68
|
-
"etag": '"
|
|
69
|
-
"mtime": "2026-06-
|
|
70
|
-
"size":
|
|
71
|
-
"path": "../public/assets/
|
|
52
|
+
"/assets/index-SmtLx1lM.css": {
|
|
53
|
+
"type": "text/css; charset=utf-8",
|
|
54
|
+
"etag": '"11492-YrH3CdmyZtpurvkMiG+Z8YMKpbg"',
|
|
55
|
+
"mtime": "2026-06-06T08:18:21.509Z",
|
|
56
|
+
"size": 70802,
|
|
57
|
+
"path": "../public/assets/index-SmtLx1lM.css"
|
|
72
58
|
},
|
|
73
59
|
"/assets/zhipuai-BPNAnxo-.svg": {
|
|
74
60
|
"type": "image/svg+xml",
|
|
75
61
|
"etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
|
|
76
|
-
"mtime": "2026-06-
|
|
62
|
+
"mtime": "2026-06-06T08:18:21.506Z",
|
|
77
63
|
"size": 11256,
|
|
78
64
|
"path": "../public/assets/zhipuai-BPNAnxo-.svg"
|
|
79
65
|
},
|
|
80
|
-
"/assets/
|
|
66
|
+
"/assets/main-QfbQ5oBP.js": {
|
|
67
|
+
"type": "text/javascript; charset=utf-8",
|
|
68
|
+
"etag": '"50599-yvOA/5xDdT1OIDnPKqlDcpJMeK8"',
|
|
69
|
+
"mtime": "2026-06-06T08:18:21.511Z",
|
|
70
|
+
"size": 329113,
|
|
71
|
+
"path": "../public/assets/main-QfbQ5oBP.js"
|
|
72
|
+
},
|
|
73
|
+
"/assets/qwen-CONDcHqt.png": {
|
|
74
|
+
"type": "image/png",
|
|
75
|
+
"etag": '"572c3-cdJAPaHdOvFCGzuaQjagdgOu6XE"',
|
|
76
|
+
"mtime": "2026-06-06T08:18:21.509Z",
|
|
77
|
+
"size": 357059,
|
|
78
|
+
"path": "../public/assets/qwen-CONDcHqt.png"
|
|
79
|
+
},
|
|
80
|
+
"/assets/index-BIZeMR5N.js": {
|
|
81
81
|
"type": "text/javascript; charset=utf-8",
|
|
82
|
-
"etag": '"
|
|
83
|
-
"mtime": "2026-06-
|
|
84
|
-
"size":
|
|
85
|
-
"path": "../public/assets/index-
|
|
82
|
+
"etag": '"899b2-Z4qeBA7/hHMhVcDcD+o7Iw8hgNw"',
|
|
83
|
+
"mtime": "2026-06-06T08:18:21.511Z",
|
|
84
|
+
"size": 563634,
|
|
85
|
+
"path": "../public/assets/index-BIZeMR5N.js"
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
88
|
function readAsset(id) {
|
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tonyclaw/llm-inspector",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "LLM API proxy inspector — captures and displays requests/responses from AI coding tools in a web UI",
|
|
6
6
|
"license": "MIT",
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
"class-variance-authority": "^0.7.1",
|
|
56
56
|
"clsx": "^2.1.1",
|
|
57
57
|
"conf": "^15.1.0",
|
|
58
|
+
"diff": "^9.0.0",
|
|
58
59
|
"jszip": "^3.10.1",
|
|
59
60
|
"lucide-react": "^0.563.0",
|
|
60
61
|
"radix-ui": "^1.4.3",
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
LogEntry,
|
|
11
11
|
type ConversationGroupData,
|
|
12
12
|
} from "./proxy-viewer";
|
|
13
|
+
import { CrabLogo } from "./ui/crab-logo";
|
|
13
14
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select";
|
|
14
15
|
import { SettingsDialog } from "./providers/SettingsDialog";
|
|
15
16
|
|
|
@@ -91,6 +92,8 @@ export type ProxyViewerProps = {
|
|
|
91
92
|
onClearAll: () => void;
|
|
92
93
|
viewMode: "simple" | "full";
|
|
93
94
|
onViewModeChange: (mode: "simple" | "full") => void;
|
|
95
|
+
/** Live strip-Claude-Code-billing-header flag, sourced once at the container. */
|
|
96
|
+
strip: boolean;
|
|
94
97
|
};
|
|
95
98
|
|
|
96
99
|
export function ProxyViewer({
|
|
@@ -104,6 +107,7 @@ export function ProxyViewer({
|
|
|
104
107
|
onClearAll,
|
|
105
108
|
viewMode,
|
|
106
109
|
onViewModeChange,
|
|
110
|
+
strip,
|
|
107
111
|
}: ProxyViewerProps): JSX.Element {
|
|
108
112
|
const { totalIn, totalOut } = computeTokenSummary(logs);
|
|
109
113
|
const [groupedView, setGroupedView] = useState(true);
|
|
@@ -142,7 +146,10 @@ export function ProxyViewer({
|
|
|
142
146
|
<div className="max-w-[1200px] mx-auto flex flex-col h-screen" style={{ maxHeight: "100vh" }}>
|
|
143
147
|
{/* Header */}
|
|
144
148
|
<div className="flex items-center gap-4 mb-4 px-6 pt-6">
|
|
145
|
-
<h1 className="text-lg font-bold flex-1
|
|
149
|
+
<h1 className="text-lg font-bold flex-1 flex items-center gap-2">
|
|
150
|
+
<CrabLogo className="size-10 text-amber-500" />
|
|
151
|
+
LLM Inspector
|
|
152
|
+
</h1>
|
|
146
153
|
<span className="text-muted-foreground text-xs font-mono">v{packageJson.version}</span>
|
|
147
154
|
<div className="flex items-center border border-border rounded-md overflow-hidden">
|
|
148
155
|
<button
|
|
@@ -294,7 +301,7 @@ export function ProxyViewer({
|
|
|
294
301
|
transform: `translateY(${virtualRow.start}px)`,
|
|
295
302
|
}}
|
|
296
303
|
>
|
|
297
|
-
<ConversationGroup group={group} viewMode={viewMode} />
|
|
304
|
+
<ConversationGroup group={group} viewMode={viewMode} strip={strip} />
|
|
298
305
|
</div>
|
|
299
306
|
);
|
|
300
307
|
} else {
|
|
@@ -313,7 +320,7 @@ export function ProxyViewer({
|
|
|
313
320
|
transform: `translateY(${virtualRow.start}px)`,
|
|
314
321
|
}}
|
|
315
322
|
>
|
|
316
|
-
<LogEntry log={log} viewMode={viewMode} />
|
|
323
|
+
<LogEntry log={log} viewMode={viewMode} strip={strip} />
|
|
317
324
|
</div>
|
|
318
325
|
);
|
|
319
326
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useState, useEffect, useCallback, useRef, type JSX } from "react";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { CapturedLogSchema, type CapturedLog } from "../proxy/schemas";
|
|
4
|
+
import { useStripConfig } from "../lib/useStripConfig";
|
|
4
5
|
import { ProxyViewer } from "./ProxyViewer";
|
|
5
6
|
|
|
6
7
|
type logsResponse = {
|
|
@@ -196,6 +197,10 @@ export function ProxyViewerContainer(): JSX.Element {
|
|
|
196
197
|
})();
|
|
197
198
|
}, []);
|
|
198
199
|
|
|
200
|
+
// Read the strip config once at the container so the virtualized list does
|
|
201
|
+
// not need N independent SWR subscriptions per row.
|
|
202
|
+
const { strip } = useStripConfig();
|
|
203
|
+
|
|
199
204
|
return (
|
|
200
205
|
<>
|
|
201
206
|
{error !== null && (
|
|
@@ -214,6 +219,7 @@ export function ProxyViewerContainer(): JSX.Element {
|
|
|
214
219
|
onClearAll={handleClearAll}
|
|
215
220
|
viewMode={viewMode}
|
|
216
221
|
onViewModeChange={setViewMode}
|
|
222
|
+
strip={strip}
|
|
217
223
|
/>
|
|
218
224
|
</>
|
|
219
225
|
);
|
|
@@ -13,6 +13,8 @@ import { LogEntry } from "./LogEntry";
|
|
|
13
13
|
export type ConversationGroupProps = {
|
|
14
14
|
group: ConversationGroupData;
|
|
15
15
|
viewMode?: "simple" | "full";
|
|
16
|
+
/** Live strip-Claude-Code-billing-header flag from the viewer container. */
|
|
17
|
+
strip: boolean;
|
|
16
18
|
};
|
|
17
19
|
|
|
18
20
|
function computeStats(logs: CapturedLog[]): {
|
|
@@ -31,6 +33,7 @@ function computeStats(logs: CapturedLog[]): {
|
|
|
31
33
|
export const ConversationGroup = memo(function ({
|
|
32
34
|
group,
|
|
33
35
|
viewMode = "simple",
|
|
36
|
+
strip,
|
|
34
37
|
}: ConversationGroupProps): JSX.Element {
|
|
35
38
|
const [expanded, setExpanded] = useState(false);
|
|
36
39
|
|
|
@@ -64,7 +67,13 @@ export const ConversationGroup = memo(function ({
|
|
|
64
67
|
{expanded && (
|
|
65
68
|
<div className="pl-4 border-l-2 border-muted ml-3">
|
|
66
69
|
{group.logs.map((log) => (
|
|
67
|
-
<LogEntry
|
|
70
|
+
<LogEntry
|
|
71
|
+
key={log.id}
|
|
72
|
+
log={log}
|
|
73
|
+
viewMode={viewMode}
|
|
74
|
+
suppressApiFormatBadge={!mixed}
|
|
75
|
+
strip={strip}
|
|
76
|
+
/>
|
|
68
77
|
))}
|
|
69
78
|
</div>
|
|
70
79
|
)}
|