@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.
Files changed (44) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/CompareDrawer-C-4ypEWs.js +1 -0
  3. package/.output/public/assets/ProxyViewerContainer-WRenRpeh.js +101 -0
  4. package/.output/public/assets/ReplayDialog-CyBKOgba.js +1 -0
  5. package/.output/public/assets/RequestAnatomy-C0IrVQ3q.js +1 -0
  6. package/.output/public/assets/ResponseView-MogToC4i.js +1 -0
  7. package/.output/public/assets/StreamingChunkSequence-ClhUhT-s.js +1 -0
  8. package/.output/public/assets/_sessionId-BO47oA3Z.js +1 -0
  9. package/.output/public/assets/index-BRvz6-L6.css +1 -0
  10. package/.output/public/assets/index-Btw8ec7-.js +1 -0
  11. package/.output/public/assets/{json-viewer-D-z1r1Pp.js → json-viewer-BicGakI5.js} +1 -1
  12. package/.output/public/assets/{main-CZJ63sQh.js → main-Be2qqUUW.js} +8 -7
  13. package/.output/server/_libs/lucide-react.mjs +23 -17
  14. package/.output/server/_sessionId-DhKJIdQC.mjs +122 -0
  15. package/.output/server/_ssr/{CompareDrawer-BJr-913n.mjs → CompareDrawer-BGUgukJ8.mjs} +57 -57
  16. package/.output/server/_ssr/{index-C7I_Qgt0.mjs → ProxyViewerContainer--3K3o3Sm.mjs} +277 -191
  17. package/.output/server/_ssr/{ReplayDialog-BwmToGuR.mjs → ReplayDialog-Bo86xZI4.mjs} +57 -57
  18. package/.output/server/_ssr/{RequestAnatomy-BmMiPRPB.mjs → RequestAnatomy-jRU5qgwB.mjs} +4 -4
  19. package/.output/server/_ssr/{ResponseView-ZB9-8Raw.mjs → ResponseView-DdO_-79a.mjs} +5 -5
  20. package/.output/server/_ssr/{StreamingChunkSequence-DWm4CQWC.mjs → StreamingChunkSequence-BigLwhh4.mjs} +57 -57
  21. package/.output/server/_ssr/index-BHG6vOnr.mjs +117 -0
  22. package/.output/server/_ssr/index.mjs +2 -2
  23. package/.output/server/_ssr/{json-viewer-D9XETzwp.mjs → json-viewer-B4c_WjXD.mjs} +4 -4
  24. package/.output/server/_ssr/{router-711KpGkz.mjs → router-DVixpJO-.mjs} +79 -22
  25. package/.output/server/_tanstack-start-manifest_v-BbvWUF4v.mjs +4 -0
  26. package/.output/server/index.mjs +70 -56
  27. package/README.md +109 -59
  28. package/package.json +1 -1
  29. package/src/assets/logos/mcp.png +0 -0
  30. package/src/components/ProxyViewer.tsx +240 -64
  31. package/src/components/ProxyViewerContainer.tsx +42 -11
  32. package/src/components/proxy-viewer/ConversationGroup.tsx +1 -8
  33. package/src/components/proxy-viewer/ConversationHeader.tsx +35 -7
  34. package/src/components/ui/mcp-logo.tsx +20 -0
  35. package/src/lib/sessionUrl.ts +44 -0
  36. package/src/routes/session/$sessionId.tsx +23 -0
  37. package/.output/public/assets/CompareDrawer-BpwZCB6M.js +0 -1
  38. package/.output/public/assets/ReplayDialog-Clratkzl.js +0 -1
  39. package/.output/public/assets/RequestAnatomy-EtiX0r_G.js +0 -1
  40. package/.output/public/assets/ResponseView-CJqxo-EN.js +0 -1
  41. package/.output/public/assets/StreamingChunkSequence-BIbRqQiV.js +0 -1
  42. package/.output/public/assets/index-B-0F9n1w.js +0 -101
  43. package/.output/public/assets/index-DoGvsnbA.css +0 -1
  44. 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-DoGvsnbA.css";
51
- const Route$k = createRootRoute({
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-C7I_Qgt0.mjs").then((n) => n.t);
75
- const Route$j = createFileRoute("/")({
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$j.update({
5464
+ const IndexRoute = Route$k.update({
5416
5465
  id: "/",
5417
5466
  path: "/",
5418
- getParentRoute: () => Route$k
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$k
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$k
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$k
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$k
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$k
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$k
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$k
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$k
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$k._addFileChildren(rootRouteChildren)._addFileTypes();
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
- RuntimeConfigSchema as R,
5585
- AnthropicRequestSchema as a,
5586
- createFailedProviderTestResults as b,
5639
+ Route$j as R,
5640
+ RuntimeConfigSchema as a,
5641
+ AnthropicRequestSchema as b,
5587
5642
  createPendingProviderTestResults as c,
5588
- ProviderConfigSchema as d,
5589
- router as e,
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
+ };
@@ -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-17T03:16:09.841Z",
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-DoGvsnbA.css": {
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": '"16d26-qw65JIM4oxztXa/jhWYD9PPuvfA"',
48
- "mtime": "2026-06-17T03:16:09.841Z",
49
- "size": 93478,
50
- "path": "../public/assets/index-DoGvsnbA.css"
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/json-viewer-D-z1r1Pp.js": {
59
+ "/assets/CompareDrawer-C-4ypEWs.js": {
53
60
  "type": "text/javascript; charset=utf-8",
54
- "etag": '"1e60c-b+Su3KxY/1YyCIOXy/kCh7EVJWA"',
55
- "mtime": "2026-06-17T03:16:09.843Z",
56
- "size": 124428,
57
- "path": "../public/assets/json-viewer-D-z1r1Pp.js"
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/CompareDrawer-BpwZCB6M.js": {
60
- "type": "text/javascript; charset=utf-8",
61
- "etag": '"4a10-Mv9zkKfYXObP08//mt6T/CvprxM"',
62
- "mtime": "2026-06-17T03:16:09.843Z",
63
- "size": 18960,
64
- "path": "../public/assets/CompareDrawer-BpwZCB6M.js"
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/ResponseView-CJqxo-EN.js": {
73
+ "/assets/json-viewer-BicGakI5.js": {
67
74
  "type": "text/javascript; charset=utf-8",
68
- "etag": '"6e82-6eo0MjJytLP7tyoCDUCMVX+Imqs"',
69
- "mtime": "2026-06-17T03:16:09.843Z",
70
- "size": 28290,
71
- "path": "../public/assets/ResponseView-CJqxo-EN.js"
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/RequestAnatomy-EtiX0r_G.js": {
80
+ "/assets/ReplayDialog-CyBKOgba.js": {
74
81
  "type": "text/javascript; charset=utf-8",
75
- "etag": '"141b-XyuVMPaOmzNpTd/Yosdaset1y/Q"',
76
- "mtime": "2026-06-17T03:16:09.843Z",
77
- "size": 5147,
78
- "path": "../public/assets/RequestAnatomy-EtiX0r_G.js"
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/ReplayDialog-Clratkzl.js": {
87
+ "/assets/ResponseView-MogToC4i.js": {
81
88
  "type": "text/javascript; charset=utf-8",
82
- "etag": '"11b1-d+cNxLktG4hY5CiGmJvG2zg1hgE"',
83
- "mtime": "2026-06-17T03:16:09.843Z",
84
- "size": 4529,
85
- "path": "../public/assets/ReplayDialog-Clratkzl.js"
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/StreamingChunkSequence-BIbRqQiV.js": {
94
+ "/assets/RequestAnatomy-C0IrVQ3q.js": {
88
95
  "type": "text/javascript; charset=utf-8",
89
- "etag": '"d72-t3qm896NP0azRIGukgZdUpqJo4U"',
90
- "mtime": "2026-06-17T03:16:09.843Z",
91
- "size": 3442,
92
- "path": "../public/assets/StreamingChunkSequence-BIbRqQiV.js"
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/index-B-0F9n1w.js": {
101
+ "/assets/StreamingChunkSequence-ClhUhT-s.js": {
102
102
  "type": "text/javascript; charset=utf-8",
103
- "etag": '"743e9-3qHa5OB4CA7Rxh5qKe3GN7HIf8w"',
104
- "mtime": "2026-06-17T03:16:09.843Z",
105
- "size": 476137,
106
- "path": "../public/assets/index-B-0F9n1w.js"
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-17T03:16:09.841Z",
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-17T03:16:09.843Z",
132
+ "mtime": "2026-06-17T09:08:04.357Z",
119
133
  "size": 357059,
120
134
  "path": "../public/assets/qwen-CONDcHqt.png"
121
135
  },
122
- "/assets/main-CZJ63sQh.js": {
136
+ "/assets/ProxyViewerContainer-WRenRpeh.js": {
123
137
  "type": "text/javascript; charset=utf-8",
124
- "etag": '"505ae-5KYVtgQMJj49Xmb8K+9UosNpz6U"',
125
- "mtime": "2026-06-17T03:16:09.843Z",
126
- "size": 329134,
127
- "path": "../public/assets/main-CZJ63sQh.js"
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
- 透明捕获、解析和重放 AI 编码工具的 LLM API 流量。
3
+ 透明捕获、解析、重放和治理 AI 编码工具的 LLM API 流量。
4
4
 
5
5
  ![LLM Inspector](docs/封面v1.15.0.png)
6
6
 
7
- `llm-inspector` 是一个运行在本机的 HTTP 代理和 Web 调试器。将 Claude Code、OpenCode、Cursor、Cody 或其他兼容客户端的 API Base URL 指向它,即可实时查看请求、响应、工具调用、Thinking、SSE chunks Token 用量。
7
+ `llm-inspector` 是一个运行在本机的 LLM API 代理调试台,同时提供 **Web UI + Proxy + MCP Server**。把 Claude Code、OpenCode、Cursor、Cody、MiMo Code 或其他兼容客户端的 API Base URL 指向它,就可以在 Web UI 中实时观察请求、响应、SSE 流式事件、工具调用、Thinking/ReasoningToken 用量、缓存 Token、请求来源和提供商路由结果;也可以让 Coding Agent 通过 MCP 主动读取日志、复盘会话和重放请求。
8
8
 
9
- 默认地址:
9
+ 它的定位不是一个单纯的抓包工具,而是面向 AI 编码场景的“可观察代理层”:
10
10
 
11
- - Web UI:`http://localhost:25947`
12
- - 代理入口:`http://localhost:25947/proxy`
13
- - MCP Server:`http://localhost:25947/api/mcp`
11
+ - 调试 Agent:看清系统提示词、工具定义、消息、响应、错误和慢请求。
12
+ - 治理 Provider:按模型路由到不同 LLM 提供商,集中管理 API Key、模型、协议 URL 和连接测试。
13
+ - 复现问题:重放历史请求,对比代理处理前后的请求差异,排查客户端、代理和上游之间的问题。
14
+ - 辅助 Agent 自诊断:内置 MCP Server,让 Coding Agent 可以直接查询最近日志、会话、模型、Provider 和重放结果。
14
15
 
15
- ## 功能
16
+ 默认入口:
16
17
 
17
- - 同时支持 Anthropic Messages API 和 OpenAI Chat Completions API
18
- - 分开解析两种协议的请求、响应、Thinking、Answer 和 Tool Call
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
- - 实时捕获普通响应和 SSE 流式响应
23
- - 查看原始/处理后的 Headers Request Diff
24
- - 比较同一会话、同一协议的相邻请求
25
- - 请求 Replay、日志 ZIP 导出和按会话/模型筛选
26
- - 会话分组、Turn 边界、缓存 Token 趋势和 Token 汇总
27
- - 提供商管理、双协议 URL、连接测试、导入与导出
28
- - 记录客户端 PID、工作目录和项目目录,便于定位请求来源
29
- - 内置 MCP Server,让 Coding Agent 直接查询日志和管理提供商
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
  ![Proxy Overview](docs/Proxy.png)
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. 填写提供商名称和 API Key。
71
- 2. 添加一个或多个模型名称。代理使用模型名称匹配提供商。
72
- 3. 按实际能力填写协议 URL:
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
- 同一个提供商可以同时配置 Anthropic 和 OpenAI URL。代理会根据客户端请求路径选择协议与上游地址,而不是根据请求 body 猜测格式。
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
- ```text
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
- LLM_BASE_URL=http://localhost:25947/proxy opencode
129
+ OPENAI_BASE_URL=http://localhost:25947/proxy mimo
115
130
  ```
116
131
 
117
- 对于 Cursor、Cody 等工具,请在其设置中修改 API Base URL
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**:聚焦 Request 和解析后的 Response
172
+ - **Simple**:聚焦请求、解析后的响应、工具调用、Thinking/Reasoning Token
154
173
  - **Full**:额外显示 Raw Headers、Headers、Raw Request、Raw Response 和 SSE chunks。
155
174
 
156
- ### Thinking Answer
175
+ ### Thinking / Reasoning / Answer
157
176
 
158
177
  查看器按协议分别解析响应:
159
178
 
160
- - Anthropic:`content[].type === "thinking"` 与 `content[].type === "text"`
161
- - OpenAI:`reasoning_content`、`thinking`、`think` 与 `message.content`
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
- Thinking 和最终 Answer 会分区显示。如果响应因 `max_tokens` 提前结束且只返回 Thinking,界面只展示真实存在的 Thinking,不会生成不存在的 Answer。
186
+ 日志会记录:
164
187
 
165
- ### Diff 与 Replay
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
- | 提供商查询 | `inspector_list_providers`, `inspector_get_provider` |
239
+ | Provider 查询 | `inspector_list_providers`, `inspector_get_provider` |
200
240
  | 操作 | `inspector_replay_log`, `inspector_test_provider` |
201
- | 提供商写入 | `inspector_add_provider`, `inspector_update_provider` |
241
+ | Provider 写入 | `inspector_add_provider`, `inspector_update_provider` |
202
242
 
203
243
  完整说明见 [MCP Server 文档](docs/MCP-Server.md)。
204
244
 
205
- > MCP 接口仅应在可信本机环境使用。提供商查询工具会返回明文 API Key,请勿将服务暴露到不可信网络。
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
- 提供商 API Key 以明文 JSON 保存在本机数据目录中。请保护该目录,并避免在共享或不可信主机上运行服务。
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/logs` | GET / DELETE | 查询或清空日志 |
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
- npm run typecheck
248
- npm run lint
249
- npm run format:check
250
- npm run build
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tonyclaw/llm-inspector",
3
- "version": "1.18.0",
3
+ "version": "1.18.2",
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",