@tonyclaw/llm-inspector 1.18.0 → 1.18.2
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/CompareDrawer-C-4ypEWs.js +1 -0
- package/.output/public/assets/ProxyViewerContainer-WRenRpeh.js +101 -0
- package/.output/public/assets/ReplayDialog-CyBKOgba.js +1 -0
- package/.output/public/assets/RequestAnatomy-C0IrVQ3q.js +1 -0
- package/.output/public/assets/ResponseView-MogToC4i.js +1 -0
- package/.output/public/assets/StreamingChunkSequence-ClhUhT-s.js +1 -0
- package/.output/public/assets/_sessionId-BO47oA3Z.js +1 -0
- package/.output/public/assets/index-BRvz6-L6.css +1 -0
- package/.output/public/assets/index-Btw8ec7-.js +1 -0
- package/.output/public/assets/{json-viewer-D-z1r1Pp.js → json-viewer-BicGakI5.js} +1 -1
- package/.output/public/assets/{main-CZJ63sQh.js → main-Be2qqUUW.js} +8 -7
- package/.output/server/_libs/lucide-react.mjs +23 -17
- package/.output/server/_sessionId-DhKJIdQC.mjs +122 -0
- package/.output/server/_ssr/{CompareDrawer-BJr-913n.mjs → CompareDrawer-BGUgukJ8.mjs} +57 -57
- package/.output/server/_ssr/{index-C7I_Qgt0.mjs → ProxyViewerContainer--3K3o3Sm.mjs} +277 -191
- package/.output/server/_ssr/{ReplayDialog-BwmToGuR.mjs → ReplayDialog-Bo86xZI4.mjs} +57 -57
- package/.output/server/_ssr/{RequestAnatomy-BmMiPRPB.mjs → RequestAnatomy-jRU5qgwB.mjs} +4 -4
- package/.output/server/_ssr/{ResponseView-ZB9-8Raw.mjs → ResponseView-DdO_-79a.mjs} +5 -5
- package/.output/server/_ssr/{StreamingChunkSequence-DWm4CQWC.mjs → StreamingChunkSequence-BigLwhh4.mjs} +57 -57
- package/.output/server/_ssr/index-BHG6vOnr.mjs +117 -0
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-D9XETzwp.mjs → json-viewer-B4c_WjXD.mjs} +4 -4
- package/.output/server/_ssr/{router-711KpGkz.mjs → router-DVixpJO-.mjs} +79 -22
- package/.output/server/_tanstack-start-manifest_v-BbvWUF4v.mjs +4 -0
- package/.output/server/index.mjs +70 -56
- package/README.md +109 -59
- package/package.json +1 -1
- package/src/assets/logos/mcp.png +0 -0
- package/src/components/ProxyViewer.tsx +240 -64
- package/src/components/ProxyViewerContainer.tsx +42 -11
- package/src/components/proxy-viewer/ConversationGroup.tsx +1 -8
- package/src/components/proxy-viewer/ConversationHeader.tsx +35 -7
- package/src/components/ui/mcp-logo.tsx +20 -0
- package/src/lib/sessionUrl.ts +44 -0
- package/src/routes/session/$sessionId.tsx +23 -0
- package/.output/public/assets/CompareDrawer-BpwZCB6M.js +0 -1
- package/.output/public/assets/ReplayDialog-Clratkzl.js +0 -1
- package/.output/public/assets/RequestAnatomy-EtiX0r_G.js +0 -1
- package/.output/public/assets/ResponseView-CJqxo-EN.js +0 -1
- package/.output/public/assets/StreamingChunkSequence-BIbRqQiV.js +0 -1
- package/.output/public/assets/index-B-0F9n1w.js +0 -101
- package/.output/public/assets/index-DoGvsnbA.css +0 -1
- package/.output/server/_tanstack-start-manifest_v-noQw0Vmw.mjs +0 -4
|
@@ -47,8 +47,8 @@ import "../_libs/debounce-fn.mjs";
|
|
|
47
47
|
import "../_libs/mimic-function.mjs";
|
|
48
48
|
import "../_libs/semver.mjs";
|
|
49
49
|
import "../_libs/uint8array-extras.mjs";
|
|
50
|
-
const appCss = "/assets/index-
|
|
51
|
-
const Route$
|
|
50
|
+
const appCss = "/assets/index-BRvz6-L6.css";
|
|
51
|
+
const Route$l = createRootRoute({
|
|
52
52
|
head: () => ({
|
|
53
53
|
meta: [
|
|
54
54
|
{ charSet: "utf-8" },
|
|
@@ -71,9 +71,58 @@ function RootDocument({ children }) {
|
|
|
71
71
|
] })
|
|
72
72
|
] });
|
|
73
73
|
}
|
|
74
|
-
const $$splitComponentImporter = () => import("./index-
|
|
75
|
-
const Route$
|
|
76
|
-
component: lazyRouteComponent($$splitComponentImporter, "component")
|
|
74
|
+
const $$splitComponentImporter$1 = () => import("./index-BHG6vOnr.mjs");
|
|
75
|
+
const Route$k = createFileRoute("/")({
|
|
76
|
+
component: lazyRouteComponent($$splitComponentImporter$1, "component")
|
|
77
|
+
});
|
|
78
|
+
const B64URL_RE = /^[A-Za-z0-9_-]+$/;
|
|
79
|
+
function bytesToBinary(bytes) {
|
|
80
|
+
let binary = "";
|
|
81
|
+
for (const byte of bytes) {
|
|
82
|
+
binary += String.fromCharCode(byte);
|
|
83
|
+
}
|
|
84
|
+
return binary;
|
|
85
|
+
}
|
|
86
|
+
function binaryToBytes(binary) {
|
|
87
|
+
const bytes = new Uint8Array(binary.length);
|
|
88
|
+
for (let i = 0; i < binary.length; i++) {
|
|
89
|
+
bytes[i] = binary.charCodeAt(i);
|
|
90
|
+
}
|
|
91
|
+
return bytes;
|
|
92
|
+
}
|
|
93
|
+
function encodeSessionIdForPath(sessionId) {
|
|
94
|
+
const bytes = new TextEncoder().encode(sessionId);
|
|
95
|
+
const base64 = btoa(bytesToBinary(bytes));
|
|
96
|
+
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
97
|
+
}
|
|
98
|
+
function decodeSessionIdFromPath(encoded) {
|
|
99
|
+
if (encoded.startsWith("%")) {
|
|
100
|
+
return decodeURIComponent(encoded);
|
|
101
|
+
}
|
|
102
|
+
if (!B64URL_RE.test(encoded)) {
|
|
103
|
+
return encoded;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const padded = encoded.padEnd(Math.ceil(encoded.length / 4) * 4, "=");
|
|
107
|
+
const base64 = padded.replace(/-/g, "+").replace(/_/g, "/");
|
|
108
|
+
const binary = atob(base64);
|
|
109
|
+
return new TextDecoder().decode(binaryToBytes(binary));
|
|
110
|
+
} catch {
|
|
111
|
+
return encoded;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
function getSessionPath(sessionId) {
|
|
115
|
+
return `/session/${encodeSessionIdForPath(sessionId)}`;
|
|
116
|
+
}
|
|
117
|
+
const $$splitComponentImporter = () => import("../_sessionId-DhKJIdQC.mjs");
|
|
118
|
+
const Route$j = createFileRoute("/session/$sessionId")({
|
|
119
|
+
component: lazyRouteComponent($$splitComponentImporter, "component"),
|
|
120
|
+
parseParams: (params) => ({
|
|
121
|
+
sessionId: decodeSessionIdFromPath(params.sessionId)
|
|
122
|
+
}),
|
|
123
|
+
stringifyParams: (params) => ({
|
|
124
|
+
sessionId: encodeSessionIdForPath(params.sessionId)
|
|
125
|
+
})
|
|
77
126
|
});
|
|
78
127
|
const LOG_DIR_ENV = process.env["LOG_DIR"];
|
|
79
128
|
const RETENTION_DAYS = Number(process.env["LOG_RETENTION_DAYS"] ?? "7");
|
|
@@ -5412,50 +5461,55 @@ const Route = createFileRoute("/api/providers/$providerId/test/log")({
|
|
|
5412
5461
|
}
|
|
5413
5462
|
}
|
|
5414
5463
|
});
|
|
5415
|
-
const IndexRoute = Route$
|
|
5464
|
+
const IndexRoute = Route$k.update({
|
|
5416
5465
|
id: "/",
|
|
5417
5466
|
path: "/",
|
|
5418
|
-
getParentRoute: () => Route$
|
|
5467
|
+
getParentRoute: () => Route$l
|
|
5468
|
+
});
|
|
5469
|
+
const SessionSessionIdRoute = Route$j.update({
|
|
5470
|
+
id: "/session/$sessionId",
|
|
5471
|
+
path: "/session/$sessionId",
|
|
5472
|
+
getParentRoute: () => Route$l
|
|
5419
5473
|
});
|
|
5420
5474
|
const ProxySplatRoute = Route$i.update({
|
|
5421
5475
|
id: "/proxy/$",
|
|
5422
5476
|
path: "/proxy/$",
|
|
5423
|
-
getParentRoute: () => Route$
|
|
5477
|
+
getParentRoute: () => Route$l
|
|
5424
5478
|
});
|
|
5425
5479
|
const ApiSessionsRoute = Route$h.update({
|
|
5426
5480
|
id: "/api/sessions",
|
|
5427
5481
|
path: "/api/sessions",
|
|
5428
|
-
getParentRoute: () => Route$
|
|
5482
|
+
getParentRoute: () => Route$l
|
|
5429
5483
|
});
|
|
5430
5484
|
const ApiProvidersRoute = Route$g.update({
|
|
5431
5485
|
id: "/api/providers",
|
|
5432
5486
|
path: "/api/providers",
|
|
5433
|
-
getParentRoute: () => Route$
|
|
5487
|
+
getParentRoute: () => Route$l
|
|
5434
5488
|
});
|
|
5435
5489
|
const ApiModelsRoute = Route$f.update({
|
|
5436
5490
|
id: "/api/models",
|
|
5437
5491
|
path: "/api/models",
|
|
5438
|
-
getParentRoute: () => Route$
|
|
5492
|
+
getParentRoute: () => Route$l
|
|
5439
5493
|
});
|
|
5440
5494
|
const ApiMcpRoute = Route$e.update({
|
|
5441
5495
|
id: "/api/mcp",
|
|
5442
5496
|
path: "/api/mcp",
|
|
5443
|
-
getParentRoute: () => Route$
|
|
5497
|
+
getParentRoute: () => Route$l
|
|
5444
5498
|
});
|
|
5445
5499
|
const ApiLogsRoute = Route$d.update({
|
|
5446
5500
|
id: "/api/logs",
|
|
5447
5501
|
path: "/api/logs",
|
|
5448
|
-
getParentRoute: () => Route$
|
|
5502
|
+
getParentRoute: () => Route$l
|
|
5449
5503
|
});
|
|
5450
5504
|
const ApiHealthRoute = Route$c.update({
|
|
5451
5505
|
id: "/api/health",
|
|
5452
5506
|
path: "/api/health",
|
|
5453
|
-
getParentRoute: () => Route$
|
|
5507
|
+
getParentRoute: () => Route$l
|
|
5454
5508
|
});
|
|
5455
5509
|
const ApiConfigRoute = Route$b.update({
|
|
5456
5510
|
id: "/api/config",
|
|
5457
5511
|
path: "/api/config",
|
|
5458
|
-
getParentRoute: () => Route$
|
|
5512
|
+
getParentRoute: () => Route$l
|
|
5459
5513
|
});
|
|
5460
5514
|
const ApiProvidersScanRoute = Route$a.update({
|
|
5461
5515
|
id: "/scan",
|
|
@@ -5560,9 +5614,10 @@ const rootRouteChildren = {
|
|
|
5560
5614
|
ApiModelsRoute,
|
|
5561
5615
|
ApiProvidersRoute: ApiProvidersRouteWithChildren,
|
|
5562
5616
|
ApiSessionsRoute,
|
|
5563
|
-
ProxySplatRoute
|
|
5617
|
+
ProxySplatRoute,
|
|
5618
|
+
SessionSessionIdRoute
|
|
5564
5619
|
};
|
|
5565
|
-
const routeTree = Route$
|
|
5620
|
+
const routeTree = Route$l._addFileChildren(rootRouteChildren)._addFileTypes();
|
|
5566
5621
|
function getRouter() {
|
|
5567
5622
|
const router2 = createRouter({
|
|
5568
5623
|
routeTree,
|
|
@@ -5581,12 +5636,14 @@ export {
|
|
|
5581
5636
|
MAX_SLOW_RESPONSE_THRESHOLD_SECONDS as M,
|
|
5582
5637
|
OpenAIRequestSchema as O,
|
|
5583
5638
|
ProviderTestResultsSchema as P,
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5639
|
+
Route$j as R,
|
|
5640
|
+
RuntimeConfigSchema as a,
|
|
5641
|
+
AnthropicRequestSchema as b,
|
|
5587
5642
|
createPendingProviderTestResults as c,
|
|
5588
|
-
|
|
5589
|
-
|
|
5643
|
+
createFailedProviderTestResults as d,
|
|
5644
|
+
ProviderConfigSchema as e,
|
|
5645
|
+
router as f,
|
|
5646
|
+
getSessionPath as g,
|
|
5590
5647
|
parseOpenAIResponse as p,
|
|
5591
5648
|
requestFormatForPath as r,
|
|
5592
5649
|
stripClaudeCodeBillingHeader as s
|
|
@@ -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/mcp", "/api/models", "/api/providers", "/api/sessions", "/proxy/$", "/session/$sessionId"], "preloads": ["/assets/main-Be2qqUUW.js"], "assets": [] }, "/": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-Btw8ec7-.js", "/assets/ProxyViewerContainer-WRenRpeh.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/mcp": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/mcp.ts" }, "/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/providers/scan"] }, "/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" }, "/session/$sessionId": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/session/$sessionId.tsx", "assets": [], "preloads": ["/assets/_sessionId-BO47oA3Z.js", "/assets/ProxyViewerContainer-WRenRpeh.js"] }, "/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/providers/scan": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/providers.scan.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", "children": ["/api/providers/$providerId/test/log"] }, "/api/providers/$providerId/test/log": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/api/providers.$providerId.test.log.ts" } }, "clientEntry": "/assets/main-Be2qqUUW.js" });
|
|
2
|
+
export {
|
|
3
|
+
tsrStartManifest
|
|
4
|
+
};
|
package/.output/server/index.mjs
CHANGED
|
@@ -38,93 +38,107 @@ 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-17T09:08:04.357Z",
|
|
42
42
|
"size": 5915,
|
|
43
43
|
"path": "../public/assets/alibaba-TTwafVwX.svg"
|
|
44
44
|
},
|
|
45
|
-
"/assets/index-
|
|
45
|
+
"/assets/index-Btw8ec7-.js": {
|
|
46
|
+
"type": "text/javascript; charset=utf-8",
|
|
47
|
+
"etag": '"74-Si+LSldyx0r6eyhCKLBrfEnEPe8"',
|
|
48
|
+
"mtime": "2026-06-17T09:08:04.357Z",
|
|
49
|
+
"size": 116,
|
|
50
|
+
"path": "../public/assets/index-Btw8ec7-.js"
|
|
51
|
+
},
|
|
52
|
+
"/assets/index-BRvz6-L6.css": {
|
|
46
53
|
"type": "text/css; charset=utf-8",
|
|
47
|
-
"etag": '"
|
|
48
|
-
"mtime": "2026-06-
|
|
49
|
-
"size":
|
|
50
|
-
"path": "../public/assets/index-
|
|
54
|
+
"etag": '"17492-JL6LQ7LktE+Z13FtWdf220LM1h4"',
|
|
55
|
+
"mtime": "2026-06-17T09:08:04.357Z",
|
|
56
|
+
"size": 95378,
|
|
57
|
+
"path": "../public/assets/index-BRvz6-L6.css"
|
|
51
58
|
},
|
|
52
|
-
"/assets/
|
|
59
|
+
"/assets/CompareDrawer-C-4ypEWs.js": {
|
|
53
60
|
"type": "text/javascript; charset=utf-8",
|
|
54
|
-
"etag": '"
|
|
55
|
-
"mtime": "2026-06-
|
|
56
|
-
"size":
|
|
57
|
-
"path": "../public/assets/
|
|
61
|
+
"etag": '"4a1f-MtCtIuSEWViO+k9O7NiG+WdWW/s"',
|
|
62
|
+
"mtime": "2026-06-17T09:08:04.357Z",
|
|
63
|
+
"size": 18975,
|
|
64
|
+
"path": "../public/assets/CompareDrawer-C-4ypEWs.js"
|
|
58
65
|
},
|
|
59
|
-
"/assets/
|
|
60
|
-
"type": "
|
|
61
|
-
"etag": '"
|
|
62
|
-
"mtime": "2026-06-
|
|
63
|
-
"size":
|
|
64
|
-
"path": "../public/assets/
|
|
66
|
+
"/assets/minimax-BPMzvuL-.jpeg": {
|
|
67
|
+
"type": "image/jpeg",
|
|
68
|
+
"etag": '"1b06-IwivU89ko5UTMUM1/t7hn4sQK9A"',
|
|
69
|
+
"mtime": "2026-06-17T09:08:04.357Z",
|
|
70
|
+
"size": 6918,
|
|
71
|
+
"path": "../public/assets/minimax-BPMzvuL-.jpeg"
|
|
65
72
|
},
|
|
66
|
-
"/assets/
|
|
73
|
+
"/assets/json-viewer-BicGakI5.js": {
|
|
67
74
|
"type": "text/javascript; charset=utf-8",
|
|
68
|
-
"etag": '"
|
|
69
|
-
"mtime": "2026-06-
|
|
70
|
-
"size":
|
|
71
|
-
"path": "../public/assets/
|
|
75
|
+
"etag": '"1e61b-F0iLQvxKPSiAcJXtFh3kbxWdyCs"',
|
|
76
|
+
"mtime": "2026-06-17T09:08:04.359Z",
|
|
77
|
+
"size": 124443,
|
|
78
|
+
"path": "../public/assets/json-viewer-BicGakI5.js"
|
|
72
79
|
},
|
|
73
|
-
"/assets/
|
|
80
|
+
"/assets/ReplayDialog-CyBKOgba.js": {
|
|
74
81
|
"type": "text/javascript; charset=utf-8",
|
|
75
|
-
"etag": '"
|
|
76
|
-
"mtime": "2026-06-
|
|
77
|
-
"size":
|
|
78
|
-
"path": "../public/assets/
|
|
82
|
+
"etag": '"11c0-aqeGa8euE3ZZm68Zocdr8a5lyog"',
|
|
83
|
+
"mtime": "2026-06-17T09:08:04.357Z",
|
|
84
|
+
"size": 4544,
|
|
85
|
+
"path": "../public/assets/ReplayDialog-CyBKOgba.js"
|
|
79
86
|
},
|
|
80
|
-
"/assets/
|
|
87
|
+
"/assets/ResponseView-MogToC4i.js": {
|
|
81
88
|
"type": "text/javascript; charset=utf-8",
|
|
82
|
-
"etag": '"
|
|
83
|
-
"mtime": "2026-06-
|
|
84
|
-
"size":
|
|
85
|
-
"path": "../public/assets/
|
|
89
|
+
"etag": '"6e91-aH2PTafY1YSqsEz1haJsISbe08w"',
|
|
90
|
+
"mtime": "2026-06-17T09:08:04.359Z",
|
|
91
|
+
"size": 28305,
|
|
92
|
+
"path": "../public/assets/ResponseView-MogToC4i.js"
|
|
86
93
|
},
|
|
87
|
-
"/assets/
|
|
94
|
+
"/assets/RequestAnatomy-C0IrVQ3q.js": {
|
|
88
95
|
"type": "text/javascript; charset=utf-8",
|
|
89
|
-
"etag": '"
|
|
90
|
-
"mtime": "2026-06-
|
|
91
|
-
"size":
|
|
92
|
-
"path": "../public/assets/
|
|
93
|
-
},
|
|
94
|
-
"/assets/minimax-BPMzvuL-.jpeg": {
|
|
95
|
-
"type": "image/jpeg",
|
|
96
|
-
"etag": '"1b06-IwivU89ko5UTMUM1/t7hn4sQK9A"',
|
|
97
|
-
"mtime": "2026-06-17T03:16:09.841Z",
|
|
98
|
-
"size": 6918,
|
|
99
|
-
"path": "../public/assets/minimax-BPMzvuL-.jpeg"
|
|
96
|
+
"etag": '"142a-4J4inWZKnQA9eVxJUqhgY7bNixY"',
|
|
97
|
+
"mtime": "2026-06-17T09:08:04.357Z",
|
|
98
|
+
"size": 5162,
|
|
99
|
+
"path": "../public/assets/RequestAnatomy-C0IrVQ3q.js"
|
|
100
100
|
},
|
|
101
|
-
"/assets/
|
|
101
|
+
"/assets/StreamingChunkSequence-ClhUhT-s.js": {
|
|
102
102
|
"type": "text/javascript; charset=utf-8",
|
|
103
|
-
"etag": '"
|
|
104
|
-
"mtime": "2026-06-
|
|
105
|
-
"size":
|
|
106
|
-
"path": "../public/assets/
|
|
103
|
+
"etag": '"d81-H1su5BwFCKxXCFEbJtAP8Dh2syA"',
|
|
104
|
+
"mtime": "2026-06-17T09:08:04.359Z",
|
|
105
|
+
"size": 3457,
|
|
106
|
+
"path": "../public/assets/StreamingChunkSequence-ClhUhT-s.js"
|
|
107
107
|
},
|
|
108
108
|
"/assets/zhipuai-BPNAnxo-.svg": {
|
|
109
109
|
"type": "image/svg+xml",
|
|
110
110
|
"etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
|
|
111
|
-
"mtime": "2026-06-
|
|
111
|
+
"mtime": "2026-06-17T09:08:04.357Z",
|
|
112
112
|
"size": 11256,
|
|
113
113
|
"path": "../public/assets/zhipuai-BPNAnxo-.svg"
|
|
114
114
|
},
|
|
115
|
+
"/assets/_sessionId-BO47oA3Z.js": {
|
|
116
|
+
"type": "text/javascript; charset=utf-8",
|
|
117
|
+
"etag": '"d2-LQ4ORv9iRuNjtfE30EnrZcfuGdI"',
|
|
118
|
+
"mtime": "2026-06-17T09:08:04.357Z",
|
|
119
|
+
"size": 210,
|
|
120
|
+
"path": "../public/assets/_sessionId-BO47oA3Z.js"
|
|
121
|
+
},
|
|
122
|
+
"/assets/main-Be2qqUUW.js": {
|
|
123
|
+
"type": "text/javascript; charset=utf-8",
|
|
124
|
+
"etag": '"50a37-B3xzwOmQqzg0FvrYhReSzxCAqgg"',
|
|
125
|
+
"mtime": "2026-06-17T09:08:04.357Z",
|
|
126
|
+
"size": 330295,
|
|
127
|
+
"path": "../public/assets/main-Be2qqUUW.js"
|
|
128
|
+
},
|
|
115
129
|
"/assets/qwen-CONDcHqt.png": {
|
|
116
130
|
"type": "image/png",
|
|
117
131
|
"etag": '"572c3-cdJAPaHdOvFCGzuaQjagdgOu6XE"',
|
|
118
|
-
"mtime": "2026-06-
|
|
132
|
+
"mtime": "2026-06-17T09:08:04.357Z",
|
|
119
133
|
"size": 357059,
|
|
120
134
|
"path": "../public/assets/qwen-CONDcHqt.png"
|
|
121
135
|
},
|
|
122
|
-
"/assets/
|
|
136
|
+
"/assets/ProxyViewerContainer-WRenRpeh.js": {
|
|
123
137
|
"type": "text/javascript; charset=utf-8",
|
|
124
|
-
"etag": '"
|
|
125
|
-
"mtime": "2026-06-
|
|
126
|
-
"size":
|
|
127
|
-
"path": "../public/assets/
|
|
138
|
+
"etag": '"769e0-3XxRCK3xMutpa2X2uOf3cazcy88"',
|
|
139
|
+
"mtime": "2026-06-17T09:08:04.357Z",
|
|
140
|
+
"size": 485856,
|
|
141
|
+
"path": "../public/assets/ProxyViewerContainer-WRenRpeh.js"
|
|
128
142
|
}
|
|
129
143
|
};
|
|
130
144
|
function readAsset(id) {
|
package/README.md
CHANGED
|
@@ -1,32 +1,41 @@
|
|
|
1
1
|
# llm-inspector
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
透明捕获、解析、重放和治理 AI 编码工具的 LLM API 流量。
|
|
4
4
|
|
|
5
5
|

|
|
6
6
|
|
|
7
|
-
`llm-inspector` 是一个运行在本机的
|
|
7
|
+
`llm-inspector` 是一个运行在本机的 LLM API 代理调试台,同时提供 **Web UI + Proxy + MCP Server**。把 Claude Code、OpenCode、Cursor、Cody、MiMo Code 或其他兼容客户端的 API Base URL 指向它,就可以在 Web UI 中实时观察请求、响应、SSE 流式事件、工具调用、Thinking/Reasoning、Token 用量、缓存 Token、请求来源和提供商路由结果;也可以让 Coding Agent 通过 MCP 主动读取日志、复盘会话和重放请求。
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
它的定位不是一个单纯的抓包工具,而是面向 AI 编码场景的“可观察代理层”:
|
|
10
10
|
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
11
|
+
- 调试 Agent:看清系统提示词、工具定义、消息、响应、错误和慢请求。
|
|
12
|
+
- 治理 Provider:按模型路由到不同 LLM 提供商,集中管理 API Key、模型、协议 URL 和连接测试。
|
|
13
|
+
- 复现问题:重放历史请求,对比代理处理前后的请求差异,排查客户端、代理和上游之间的问题。
|
|
14
|
+
- 辅助 Agent 自诊断:内置 MCP Server,让 Coding Agent 可以直接查询最近日志、会话、模型、Provider 和重放结果。
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
默认入口:
|
|
16
17
|
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
18
|
+
- **Web UI**:`http://localhost:25947`
|
|
19
|
+
- **Proxy**:`http://localhost:25947/proxy`
|
|
20
|
+
- **MCP Server**:`http://localhost:25947/api/mcp`
|
|
21
|
+
|
|
22
|
+
## 核心能力
|
|
23
|
+
|
|
24
|
+
- **双协议代理**:支持 Anthropic Messages API 与 OpenAI Chat Completions API。
|
|
25
|
+
- **路径识别协议**:根据 endpoint 选择协议,而不是猜测 body。
|
|
20
26
|
- `/proxy/v1/messages` -> Anthropic
|
|
21
27
|
- `/proxy/v1/chat/completions` -> OpenAI
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
28
|
+
- `/proxy/chat/completions` -> OpenAI 兼容路径
|
|
29
|
+
- **多模型 Provider 路由**:一个 Provider 可以配置多个模型,同时保存 Anthropic/OpenAI 两套 Base URL。
|
|
30
|
+
- **AI 编码工具适配**:覆盖 Claude Code、OpenCode、Cursor、Cody、MiMo Code 等可配置 Base URL 的工具。
|
|
31
|
+
- **实时日志与 SSE 捕获**:普通响应和流式响应都会被记录,SSE chunks 支持按需加载。
|
|
32
|
+
- **结构化查看器**:按协议解析 Request、Response、Thinking/Reasoning、Answer、Tool Call、Token 和 Cache Token。
|
|
33
|
+
- **会话与 Turn 视图**:按 session 分组,展示多轮对话边界、模型、协议、状态、耗时和 Token 汇总。
|
|
34
|
+
- **请求 Diff 与 Replay**:比较 Raw/Processed Request,也可以比较同会话相邻请求,并用当前 Provider 配置重放。
|
|
35
|
+
- **Provider 迁移与导入**:支持导入/导出 Provider,并可扫描外部工具配置辅助迁移。
|
|
36
|
+
- **运行时代理开关**:通过 Settings 或 `/api/config` 调整代理行为,无需重启。
|
|
37
|
+
- **MCP 原生调试入口**:提供 `inspector_*` 工具,让 Agent 直接读取日志、重放请求、测试 Provider 和管理 Provider。
|
|
38
|
+
- **本地数据持久化**:Provider、运行时配置、日志与 SSE chunks 存在本机数据目录,适合离线排查。
|
|
30
39
|
|
|
31
40
|

|
|
32
41
|
|
|
@@ -39,12 +48,13 @@ npm install -g @tonyclaw/llm-inspector
|
|
|
39
48
|
llm-inspector
|
|
40
49
|
```
|
|
41
50
|
|
|
42
|
-
|
|
51
|
+
启动后会自动打开浏览器。常用参数:
|
|
43
52
|
|
|
44
53
|
```bash
|
|
45
54
|
llm-inspector --no-open
|
|
46
55
|
llm-inspector --port 3000
|
|
47
56
|
llm-inspector --config-dir ./local-config
|
|
57
|
+
llm-inspector --providers '[{"name":"OpenAI","apiKey":"sk-...","models":["gpt-4o-mini"],"openaiBaseUrl":"https://api.openai.com/v1","authHeader":"bearer","createdAt":"2026-01-01T00:00:00.000Z","updatedAt":"2026-01-01T00:00:00.000Z","id":"demo"}]'
|
|
48
58
|
```
|
|
49
59
|
|
|
50
60
|
### Docker
|
|
@@ -63,19 +73,26 @@ bun install
|
|
|
63
73
|
bun run dev
|
|
64
74
|
```
|
|
65
75
|
|
|
66
|
-
##
|
|
76
|
+
## 配置 Provider
|
|
67
77
|
|
|
68
|
-
打开 Web UI,进入 **Settings**
|
|
78
|
+
打开 Web UI,进入 **Settings** 添加 Provider:
|
|
69
79
|
|
|
70
|
-
1.
|
|
71
|
-
2.
|
|
72
|
-
3.
|
|
80
|
+
1. 填写 Provider 名称和 API Key。
|
|
81
|
+
2. 添加一个或多个模型名称。代理优先用请求体中的 `model` 匹配 Provider。
|
|
82
|
+
3. 按 Provider 能力填写协议 URL:
|
|
73
83
|
- **Anthropic Base URL**:例如 `https://api.anthropic.com`
|
|
74
|
-
- **OpenAI Base URL**:例如 `https://api.openai.com`
|
|
84
|
+
- **OpenAI Base URL**:例如 `https://api.openai.com/v1`
|
|
75
85
|
4. 选择认证方式:`Bearer` 或 `x-api-key`。
|
|
76
|
-
5. 使用 Provider Test
|
|
86
|
+
5. 使用 Provider Test 验证非流式和流式请求。
|
|
87
|
+
|
|
88
|
+
同一个 Provider 可以同时配置 Anthropic 和 OpenAI URL。代理会根据客户端请求路径选择协议和上游地址;模型匹配用于决定走哪个 Provider。
|
|
77
89
|
|
|
78
|
-
|
|
90
|
+
Provider 配置支持:
|
|
91
|
+
|
|
92
|
+
- 多模型合并:同一 API Key 和 URL 组合会合并模型列表。
|
|
93
|
+
- 导入/导出:可备份和迁移配置,导出时可选择是否包含 API Key。
|
|
94
|
+
- 外部扫描:从支持的外部工具配置中扫描可迁移的 Provider。
|
|
95
|
+
- 来源标记:区分个人或公司 Provider,便于列表管理。
|
|
79
96
|
|
|
80
97
|
## 接入 AI 编码工具
|
|
81
98
|
|
|
@@ -94,31 +111,33 @@ $env:ANTHROPIC_BASE_URL = "http://localhost:25947/proxy"
|
|
|
94
111
|
claude
|
|
95
112
|
```
|
|
96
113
|
|
|
97
|
-
如果 API Key 已保存在 llm-inspector
|
|
114
|
+
如果 API Key 已保存在 llm-inspector 的 Provider 配置中,客户端令牌可以留空:
|
|
98
115
|
|
|
99
116
|
```powershell
|
|
100
117
|
$env:ANTHROPIC_AUTH_TOKEN = ""
|
|
101
118
|
```
|
|
102
119
|
|
|
103
|
-
### OpenCode
|
|
104
|
-
|
|
105
|
-
不同客户端使用的环境变量名称不同。将其 Anthropic 或 OpenAI Base URL 设置为:
|
|
120
|
+
### OpenCode
|
|
106
121
|
|
|
107
|
-
```
|
|
108
|
-
http://localhost:25947/proxy
|
|
122
|
+
```bash
|
|
123
|
+
LLM_BASE_URL=http://localhost:25947/proxy opencode
|
|
109
124
|
```
|
|
110
125
|
|
|
111
|
-
|
|
126
|
+
### MiMo Code / OpenAI 兼容客户端
|
|
112
127
|
|
|
113
128
|
```bash
|
|
114
|
-
|
|
129
|
+
OPENAI_BASE_URL=http://localhost:25947/proxy mimo
|
|
115
130
|
```
|
|
116
131
|
|
|
117
|
-
对于 Cursor、Cody
|
|
132
|
+
对于 Cursor、Cody 等工具,请在其设置中将 Anthropic 或 OpenAI Base URL 改为:
|
|
133
|
+
|
|
134
|
+
```text
|
|
135
|
+
http://localhost:25947/proxy
|
|
136
|
+
```
|
|
118
137
|
|
|
119
138
|
## 直接发送请求
|
|
120
139
|
|
|
121
|
-
请先在 Settings 中配置与 `model`
|
|
140
|
+
请先在 Settings 中配置与 `model` 匹配的 Provider。
|
|
122
141
|
|
|
123
142
|
### Anthropic 格式
|
|
124
143
|
|
|
@@ -150,27 +169,48 @@ curl http://localhost:25947/proxy/v1/chat/completions \
|
|
|
150
169
|
|
|
151
170
|
### Simple / Full
|
|
152
171
|
|
|
153
|
-
- **Simple
|
|
172
|
+
- **Simple**:聚焦请求、解析后的响应、工具调用、Thinking/Reasoning 和 Token。
|
|
154
173
|
- **Full**:额外显示 Raw Headers、Headers、Raw Request、Raw Response 和 SSE chunks。
|
|
155
174
|
|
|
156
|
-
### Thinking
|
|
175
|
+
### Thinking / Reasoning / Answer
|
|
157
176
|
|
|
158
177
|
查看器按协议分别解析响应:
|
|
159
178
|
|
|
160
|
-
- Anthropic
|
|
161
|
-
- OpenAI
|
|
179
|
+
- Anthropic:解析 `content[].type === "thinking"`、`text`、`tool_use` 等内容块。
|
|
180
|
+
- OpenAI:解析 `reasoning_content`、`thinking`、`think`、`message.content` 和函数/工具调用。
|
|
181
|
+
|
|
182
|
+
如果响应因为 `max_tokens` 提前结束且只返回 Thinking,界面只展示真实存在的内容,不会生成不存在的 Answer。
|
|
183
|
+
|
|
184
|
+
### 会话、Turn 与来源
|
|
162
185
|
|
|
163
|
-
|
|
186
|
+
日志会记录:
|
|
164
187
|
|
|
165
|
-
|
|
188
|
+
- `sessionId`、模型、Provider、API format、状态码和耗时。
|
|
189
|
+
- 输入/输出 Token、cache creation/read Token。
|
|
190
|
+
- 客户端端口、PID、工作目录和项目目录。
|
|
191
|
+
- 流式响应的 SSE chunk 索引、类型和数据。
|
|
192
|
+
|
|
193
|
+
这些信息用于定位“哪一个工具、哪个项目、哪一轮对话、哪个 Provider”产生了问题。
|
|
194
|
+
|
|
195
|
+
### Diff、Replay 与 Export
|
|
166
196
|
|
|
167
197
|
- **Diff with Raw**:比较代理处理前后的 Request 或 Headers。
|
|
168
198
|
- **Diff with Previous**:比较同一会话、同一协议中的相邻请求。
|
|
169
|
-
- **Replay
|
|
170
|
-
- **Export**:将捕获日志导出为 ZIP
|
|
199
|
+
- **Replay**:使用当前 Provider 配置重新发送历史请求。
|
|
200
|
+
- **Export**:将捕获日志导出为 ZIP,供离线分析或问题复盘。
|
|
171
201
|
|
|
172
202
|
## MCP Server
|
|
173
203
|
|
|
204
|
+
MCP 是 llm-inspector 的一等能力。它让 Agent 不只是在 UI 外部“被观察”,还可以主动查询自己刚刚产生的请求链路,完成自诊断和复盘。
|
|
205
|
+
|
|
206
|
+
典型场景:
|
|
207
|
+
|
|
208
|
+
- **最近请求自查**:Agent 可以读取最近几条日志,确认模型、Provider、状态码、Token、SSE chunks 和错误信息。
|
|
209
|
+
- **会话复盘**:按 `sessionId` 找到一次编码任务的请求序列,定位是哪一轮、哪个工具调用或哪个上游响应异常。
|
|
210
|
+
- **Provider 调试**:列出 Provider、查看配置、触发连接测试,并把测试结果写回日志视图。
|
|
211
|
+
- **请求重放**:对历史请求执行 replay,验证问题是否来自客户端、代理配置或上游 Provider。
|
|
212
|
+
- **上下文压缩排查**:读取 last user message preview、response preview、cache token 等摘要,辅助判断 Agent 行为变化。
|
|
213
|
+
|
|
174
214
|
内置 MCP Server 使用 HTTP Streamable transport:
|
|
175
215
|
|
|
176
216
|
```text
|
|
@@ -196,13 +236,13 @@ Claude Code `.mcp.json` 示例:
|
|
|
196
236
|
| --- | --- |
|
|
197
237
|
| 日志查询 | `inspector_list_logs`, `inspector_get_log`, `inspector_get_log_chunks` |
|
|
198
238
|
| 索引查询 | `inspector_list_sessions`, `inspector_list_models` |
|
|
199
|
-
|
|
|
239
|
+
| Provider 查询 | `inspector_list_providers`, `inspector_get_provider` |
|
|
200
240
|
| 操作 | `inspector_replay_log`, `inspector_test_provider` |
|
|
201
|
-
|
|
|
241
|
+
| Provider 写入 | `inspector_add_provider`, `inspector_update_provider` |
|
|
202
242
|
|
|
203
243
|
完整说明见 [MCP Server 文档](docs/MCP-Server.md)。
|
|
204
244
|
|
|
205
|
-
> MCP
|
|
245
|
+
> MCP 接口仅应在可信本机环境使用。Provider 查询工具会返回明文 API Key,请勿将服务暴露到不可信网络。
|
|
206
246
|
|
|
207
247
|
## 数据目录与环境变量
|
|
208
248
|
|
|
@@ -215,39 +255,49 @@ Claude Code `.mcp.json` 示例:
|
|
|
215
255
|
|
|
216
256
|
| 变量 | 默认值 | 说明 |
|
|
217
257
|
| --- | --- | --- |
|
|
218
|
-
| `PORT` | `25947` | Web UI
|
|
219
|
-
| `LLM_INSPECTOR_DATA_DIR` | 系统默认目录 | providers、runtime config 等数据目录 |
|
|
258
|
+
| `PORT` | `25947` | Web UI、代理和 MCP 端口 |
|
|
259
|
+
| `LLM_INSPECTOR_DATA_DIR` | 系统默认目录 | providers、runtime config、logs、chunks 等数据目录 |
|
|
260
|
+
| `LLM_INSPECTOR_CONFIG_PATH` | `<dataDir>/providers.json` | Provider 配置文件路径 |
|
|
261
|
+
| `LLM_INSPECTOR_PROVIDERS_JSON` | 无 | 启动时注入 Provider JSON |
|
|
262
|
+
| `LLM_INSPECTOR_STRIP_CLAUDE_CODE_BILLING_HEADER` | `0` | 初始是否移除 Claude Code billing header 文本块 |
|
|
220
263
|
| `LOG_DIR` | `<dataDir>/logs` | 捕获日志和运行日志目录 |
|
|
221
264
|
| `LOG_RETENTION_DAYS` | `7` | 日志保留天数 |
|
|
222
265
|
| `CHUNKS_DIR` | `<dataDir>/chunks` | SSE chunks 存储目录 |
|
|
266
|
+
| `PROXY_IDENTITY` | `inspector9527@Tony` | 转发请求使用的代理身份标识 |
|
|
223
267
|
|
|
224
|
-
|
|
268
|
+
Provider API Key 以明文 JSON 保存在本机数据目录中。请保护该目录,并避免在共享或不可信主机上运行服务。
|
|
225
269
|
|
|
226
270
|
## HTTP API
|
|
227
271
|
|
|
228
272
|
| Endpoint | Method | 说明 |
|
|
229
273
|
| --- | --- | --- |
|
|
230
274
|
| `/api/health` | GET | 健康检查 |
|
|
231
|
-
| `/api/
|
|
275
|
+
| `/api/config` | GET / PATCH | 查询或更新运行时代理配置 |
|
|
276
|
+
| `/api/config/paths` | GET | 查看配置文件路径 |
|
|
277
|
+
| `/api/logs` | GET / DELETE | 查询、分页筛选或清空日志;DELETE 可按 id 清理 |
|
|
232
278
|
| `/api/logs/:id` | GET | 获取完整日志 |
|
|
233
279
|
| `/api/logs/:id/chunks` | GET | 获取 SSE chunks |
|
|
234
280
|
| `/api/logs/:id/replay` | POST | 重放请求 |
|
|
235
281
|
| `/api/logs/stream` | GET | 实时日志更新 |
|
|
236
282
|
| `/api/sessions` | GET | 会话列表 |
|
|
237
283
|
| `/api/models` | GET | 模型列表 |
|
|
238
|
-
| `/api/providers` | GET / POST |
|
|
239
|
-
| `/api/providers/:id` | GET / PUT / DELETE |
|
|
240
|
-
| `/api/providers/:id/test` | POST |
|
|
284
|
+
| `/api/providers` | GET / POST | 查询或添加 Provider |
|
|
285
|
+
| `/api/providers/:id` | GET / PUT / DELETE | 管理单个 Provider |
|
|
286
|
+
| `/api/providers/:id/test` | POST | 测试 Provider 连接 |
|
|
287
|
+
| `/api/providers/:id/test/log` | POST | 写入 Provider 测试日志 |
|
|
288
|
+
| `/api/providers/export` | GET | 导出 Provider 配置 |
|
|
289
|
+
| `/api/providers/import` | POST | 导入 Provider 配置 |
|
|
290
|
+
| `/api/providers/scan` | GET | 扫描外部 Provider 配置 |
|
|
241
291
|
| `/api/mcp` | POST | MCP HTTP Streamable endpoint |
|
|
242
292
|
|
|
243
293
|
## 开发与验证
|
|
244
294
|
|
|
245
295
|
```bash
|
|
246
296
|
bun test
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
297
|
+
bun run typecheck
|
|
298
|
+
bun run lint
|
|
299
|
+
bun run format:check
|
|
300
|
+
bun run build
|
|
251
301
|
```
|
|
252
302
|
|
|
253
303
|
涉及代理或 UI 的改动还应启动开发服务器,并使用真实 AI 编码工具通过代理完成端到端验证:
|
package/package.json
CHANGED