@tonyclaw/llm-inspector 1.16.1 → 1.16.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.
@@ -198,7 +198,7 @@ function getResponse() {
198
198
  return event.res;
199
199
  }
200
200
  async function getStartManifest(matchedRoutes) {
201
- const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-CXomfWbm.mjs");
201
+ const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-nEbvsqrj.mjs");
202
202
  const startManifest = tsrStartManifest();
203
203
  const rootRoute = startManifest.routes[rootRouteId] = startManifest.routes[rootRouteId] || {};
204
204
  rootRoute.assets = rootRoute.assets || [];
@@ -767,7 +767,7 @@ let entriesPromise;
767
767
  let baseManifestPromise;
768
768
  let cachedFinalManifestPromise;
769
769
  async function loadEntries() {
770
- const routerEntry = await import("./router-DxmdQgBG.mjs").then((n) => n.e);
770
+ const routerEntry = await import("./router-DEx3DlVG.mjs").then((n) => n.e);
771
771
  const startEntry = await import("./start-HYkvq4Ni.mjs");
772
772
  return { startEntry, routerEntry };
773
773
  }
@@ -46,7 +46,7 @@ import "../_libs/debounce-fn.mjs";
46
46
  import "../_libs/mimic-function.mjs";
47
47
  import "../_libs/semver.mjs";
48
48
  import "../_libs/uint8array-extras.mjs";
49
- const appCss = "/assets/index-DPe3eOih.css";
49
+ const appCss = "/assets/index-DRRCmu5p.css";
50
50
  const Route$k = createRootRoute({
51
51
  head: () => ({
52
52
  meta: [
@@ -70,7 +70,7 @@ function RootDocument({ children }) {
70
70
  ] })
71
71
  ] });
72
72
  }
73
- const $$splitComponentImporter = () => import("./index-quoKBRYG.mjs");
73
+ const $$splitComponentImporter = () => import("./index-BVUWpOQi.mjs");
74
74
  const Route$j = createFileRoute("/")({
75
75
  component: lazyRouteComponent($$splitComponentImporter, "component")
76
76
  });
@@ -2159,7 +2159,8 @@ async function getClientInfo(request) {
2159
2159
  return promise;
2160
2160
  }
2161
2161
  const RuntimeConfigSchema = object({
2162
- stripClaudeCodeBillingHeader: boolean()
2162
+ stripClaudeCodeBillingHeader: boolean(),
2163
+ hasSeenOnboarding: boolean().default(false)
2163
2164
  });
2164
2165
  function getConfigFilePath() {
2165
2166
  return join(getDataDir(), "config.json");
@@ -2189,9 +2190,9 @@ function resolveInitialConfig() {
2189
2190
  }
2190
2191
  }
2191
2192
  if (process.env["LLM_INSPECTOR_STRIP_CLAUDE_CODE_BILLING_HEADER"] === "1") {
2192
- return { stripClaudeCodeBillingHeader: true };
2193
+ return { stripClaudeCodeBillingHeader: true, hasSeenOnboarding: false };
2193
2194
  }
2194
- return { stripClaudeCodeBillingHeader: false };
2195
+ return { stripClaudeCodeBillingHeader: false, hasSeenOnboarding: false };
2195
2196
  }
2196
2197
  function getConfig() {
2197
2198
  return currentConfig;
@@ -3277,7 +3278,8 @@ const Route$c = createFileRoute("/api/health")({
3277
3278
  }
3278
3279
  });
3279
3280
  const RuntimeConfigPatchSchema = object({
3280
- stripClaudeCodeBillingHeader: boolean().optional()
3281
+ stripClaudeCodeBillingHeader: boolean().optional(),
3282
+ hasSeenOnboarding: boolean().optional()
3281
3283
  }).strict().refine((v) => Object.keys(v).length > 0, {
3282
3284
  message: "At least one field must be provided"
3283
3285
  });
@@ -1,4 +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/$"], "preloads": ["/assets/main-BA7dEkGs.js"], "assets": [] }, "/": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-BhFaDZUL.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" }, "/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-BA7dEkGs.js" });
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/$"], "preloads": ["/assets/main-pT0WLAFQ.js"], "assets": [] }, "/": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-BdXOO2hn.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" }, "/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-pT0WLAFQ.js" });
2
2
  export {
3
3
  tsrStartManifest
4
4
  };
@@ -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-13T09:20:36.489Z",
41
+ "mtime": "2026-06-13T12:44:57.474Z",
42
42
  "size": 5915,
43
43
  "path": "../public/assets/alibaba-TTwafVwX.svg"
44
44
  },
45
45
  "/assets/minimax-BPMzvuL-.jpeg": {
46
46
  "type": "image/jpeg",
47
47
  "etag": '"1b06-IwivU89ko5UTMUM1/t7hn4sQK9A"',
48
- "mtime": "2026-06-13T09:20:36.492Z",
48
+ "mtime": "2026-06-13T12:44:57.474Z",
49
49
  "size": 6918,
50
50
  "path": "../public/assets/minimax-BPMzvuL-.jpeg"
51
51
  },
52
- "/assets/index-DPe3eOih.css": {
52
+ "/assets/index-DRRCmu5p.css": {
53
53
  "type": "text/css; charset=utf-8",
54
- "etag": '"15a32-YUlUFJPXe5WrOzMkLVz9gOyERE4"',
55
- "mtime": "2026-06-13T09:20:36.492Z",
56
- "size": 88626,
57
- "path": "../public/assets/index-DPe3eOih.css"
54
+ "etag": '"15b6a-YPwntwMQpIU2AWIj8lgGWZpCwQw"',
55
+ "mtime": "2026-06-13T12:44:57.477Z",
56
+ "size": 88938,
57
+ "path": "../public/assets/index-DRRCmu5p.css"
58
58
  },
59
- "/assets/main-BA7dEkGs.js": {
59
+ "/assets/qwen-CONDcHqt.png": {
60
+ "type": "image/png",
61
+ "etag": '"572c3-cdJAPaHdOvFCGzuaQjagdgOu6XE"',
62
+ "mtime": "2026-06-13T12:44:57.474Z",
63
+ "size": 357059,
64
+ "path": "../public/assets/qwen-CONDcHqt.png"
65
+ },
66
+ "/assets/main-pT0WLAFQ.js": {
60
67
  "type": "text/javascript; charset=utf-8",
61
- "etag": '"50599-d29kGf+jbMQM5/PtEY4m3//tgSI"',
62
- "mtime": "2026-06-13T09:20:36.492Z",
68
+ "etag": '"50599-82xbDaMtgdQpwvS5w+mS0iJpU3M"',
69
+ "mtime": "2026-06-13T12:44:57.477Z",
63
70
  "size": 329113,
64
- "path": "../public/assets/main-BA7dEkGs.js"
71
+ "path": "../public/assets/main-pT0WLAFQ.js"
65
72
  },
66
73
  "/assets/zhipuai-BPNAnxo-.svg": {
67
74
  "type": "image/svg+xml",
68
75
  "etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
69
- "mtime": "2026-06-13T09:20:36.492Z",
76
+ "mtime": "2026-06-13T12:44:57.475Z",
70
77
  "size": 11256,
71
78
  "path": "../public/assets/zhipuai-BPNAnxo-.svg"
72
79
  },
73
- "/assets/qwen-CONDcHqt.png": {
74
- "type": "image/png",
75
- "etag": '"572c3-cdJAPaHdOvFCGzuaQjagdgOu6XE"',
76
- "mtime": "2026-06-13T09:20:36.492Z",
77
- "size": 357059,
78
- "path": "../public/assets/qwen-CONDcHqt.png"
79
- },
80
- "/assets/index-BhFaDZUL.js": {
80
+ "/assets/index-BdXOO2hn.js": {
81
81
  "type": "text/javascript; charset=utf-8",
82
- "etag": '"9ae0c-5fGvcIIENZG+TrgDz75NbqUsbWg"',
83
- "mtime": "2026-06-13T09:20:36.492Z",
84
- "size": 634380,
85
- "path": "../public/assets/index-BhFaDZUL.js"
82
+ "etag": '"9be56-XbSfCtl5VqxwdRzwEGzflxc3heE"',
83
+ "mtime": "2026-06-13T12:44:57.477Z",
84
+ "size": 638550,
85
+ "path": "../public/assets/index-BdXOO2hn.js"
86
86
  }
87
87
  };
88
88
  function readAsset(id) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tonyclaw/llm-inspector",
3
- "version": "1.16.1",
3
+ "version": "1.16.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",
@@ -0,0 +1,67 @@
1
+ import { Check, X } from "lucide-react";
2
+ import type { JSX } from "react";
3
+ import { useOnboarding } from "../lib/useOnboarding";
4
+
5
+ /**
6
+ * First-launch onboarding banner. Shows once on a fresh install (or
7
+ * any user who hasn't yet dismissed it), explains the per-tab data
8
+ * shapes the proxy captures, and disappears forever on dismissal.
9
+ *
10
+ * The "seen" state lives in the server's runtime config — see
11
+ * `useOnboarding` — so the dismissal persists across browser sessions
12
+ * and devices for the same install.
13
+ */
14
+ export function OnboardingBanner(): JSX.Element | null {
15
+ const { hasSeenOnboarding, isLoading, markSeen } = useOnboarding();
16
+
17
+ if (isLoading || hasSeenOnboarding) return null;
18
+
19
+ return (
20
+ <div
21
+ role="note"
22
+ aria-label="Onboarding tip"
23
+ className="mx-4 mt-2 mb-1 flex items-start gap-3 rounded-md border border-amber-500/30 bg-amber-500/5 px-4 py-3 text-sm"
24
+ >
25
+ <div className="flex-1 min-w-0">
26
+ <div className="font-medium text-amber-600 dark:text-amber-400 mb-1">
27
+ Quick tour of the log tabs
28
+ </div>
29
+ <ul className="space-y-0.5 text-muted-foreground text-xs leading-relaxed">
30
+ <li>
31
+ <strong>Request</strong> / <strong>Response</strong> — structured views of what the
32
+ proxy sent and received.
33
+ </li>
34
+ <li>
35
+ <strong>Headers</strong> — request and response headers after proxy processing.
36
+ </li>
37
+ <li>
38
+ <strong>Raw Headers</strong> / <strong>Raw Request</strong> /{" "}
39
+ <strong>Raw Response</strong> — exact bytes from the upstream provider (visible in Full
40
+ mode).
41
+ </li>
42
+ </ul>
43
+ </div>
44
+ <button
45
+ type="button"
46
+ onClick={() => {
47
+ void markSeen();
48
+ }}
49
+ className="inline-flex items-center gap-1.5 text-xs px-2.5 py-1.5 rounded-md border border-amber-500/40 text-amber-700 dark:text-amber-300 hover:bg-amber-500/10 transition-colors shrink-0"
50
+ aria-label="Dismiss onboarding tip"
51
+ >
52
+ <Check className="size-3" />
53
+ Got it
54
+ </button>
55
+ <button
56
+ type="button"
57
+ onClick={() => {
58
+ void markSeen();
59
+ }}
60
+ className="text-muted-foreground hover:text-foreground transition-colors shrink-0 p-1 -m-1"
61
+ aria-label="Dismiss"
62
+ >
63
+ <X className="size-3.5" />
64
+ </button>
65
+ </div>
66
+ );
67
+ }
@@ -1,7 +1,6 @@
1
1
  import { type JSX, useCallback, useEffect, useMemo, useState } from "react";
2
2
  import { Download } from "lucide-react";
3
3
 
4
- import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "./ui/tooltip";
5
4
  import type { CapturedLog } from "../proxy/schemas";
6
5
  import { exportLogsAsZip } from "../lib/export-logs";
7
6
  import packageJson from "../../package.json";
@@ -245,39 +244,30 @@ export function ProxyViewer({
245
244
  ))}
246
245
  </SelectContent>
247
246
  </Select>
248
- <TooltipProvider>
249
- <Tooltip>
250
- <TooltipTrigger asChild>
251
- <div className="flex items-center border border-border rounded-md overflow-hidden">
252
- <button
253
- type="button"
254
- onClick={() => onViewModeChange("simple")}
255
- className={`px-2 py-1 cursor-pointer transition-colors text-xs ${
256
- viewMode === "simple"
257
- ? "bg-muted text-foreground"
258
- : "text-muted-foreground hover:bg-muted/50"
259
- }`}
260
- >
261
- Simple
262
- </button>
263
- <button
264
- type="button"
265
- onClick={() => onViewModeChange("full")}
266
- className={`px-2 py-1 cursor-pointer transition-colors text-xs ${
267
- viewMode === "full"
268
- ? "bg-muted text-foreground"
269
- : "text-muted-foreground hover:bg-muted/50"
270
- }`}
271
- >
272
- Full
273
- </button>
274
- </div>
275
- </TooltipTrigger>
276
- <TooltipContent>
277
- Simple shows parsed output; Full adds raw headers and tokens
278
- </TooltipContent>
279
- </Tooltip>
280
- </TooltipProvider>
247
+ <div className="flex items-center border border-border rounded-md overflow-hidden">
248
+ <button
249
+ type="button"
250
+ onClick={() => onViewModeChange("simple")}
251
+ className={`px-2 py-1 cursor-pointer transition-colors text-xs ${
252
+ viewMode === "simple"
253
+ ? "bg-muted text-foreground"
254
+ : "text-muted-foreground hover:bg-muted/50"
255
+ }`}
256
+ >
257
+ Simple
258
+ </button>
259
+ <button
260
+ type="button"
261
+ onClick={() => onViewModeChange("full")}
262
+ className={`px-2 py-1 cursor-pointer transition-colors text-xs ${
263
+ viewMode === "full"
264
+ ? "bg-muted text-foreground"
265
+ : "text-muted-foreground hover:bg-muted/50"
266
+ }`}
267
+ >
268
+ Full
269
+ </button>
270
+ </div>
281
271
  <div className="flex-1" />
282
272
  <span className="text-muted-foreground text-xs font-mono">
283
273
  {logs.length} request{logs.length !== 1 ? "s" : ""}
@@ -2,6 +2,7 @@ import { useState, useEffect, useCallback, useRef, useMemo, type JSX } from "rea
2
2
  import { z } from "zod";
3
3
  import { CapturedLogSchema, type CapturedLog } from "../proxy/schemas";
4
4
  import { useStripConfig } from "../lib/useStripConfig";
5
+ import { OnboardingBanner } from "./OnboardingBanner";
5
6
  import { ProxyViewer } from "./ProxyViewer";
6
7
 
7
8
  type SSEUpdate =
@@ -279,6 +280,7 @@ export function ProxyViewerContainer(): JSX.Element {
279
280
  {error}
280
281
  </div>
281
282
  )}
283
+ <OnboardingBanner />
282
284
  <ProxyViewer
283
285
  logs={logs}
284
286
  sessions={sessions}
@@ -3,7 +3,6 @@ import { Settings } from "lucide-react";
3
3
  import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "../ui/dialog";
4
4
  import { Tabs, TabsList, TabsTrigger, TabsContent } from "../ui/tabs";
5
5
  import { Button } from "../ui/button";
6
- import { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from "../ui/tooltip";
7
6
  import { ProvidersPanel } from "./ProvidersPanel";
8
7
  import { useProviders } from "../../lib/useProviders";
9
8
  import { useStripConfig } from "../../lib/useStripConfig";
@@ -130,30 +129,21 @@ function ProxySettingsTab(): JSX.Element {
130
129
  </p>
131
130
  </div>
132
131
 
133
- <TooltipProvider>
134
- <Tooltip>
135
- <TooltipTrigger asChild>
136
- <label className="flex items-center gap-3">
137
- <input
138
- type="checkbox"
139
- role="switch"
140
- checked={strip}
141
- disabled={isLoading || pending}
142
- onChange={(e) => {
143
- void handleToggle(e.currentTarget.checked);
144
- }}
145
- className="size-4 cursor-pointer disabled:cursor-not-allowed disabled:opacity-50"
146
- />
147
- <span className="text-sm">
148
- {isLoading ? "Loading…" : strip ? "Stripping enabled" : "Stripping disabled"}
149
- </span>
150
- </label>
151
- </TooltipTrigger>
152
- <TooltipContent>
153
- Strip Claude Code billing header to improve cache hit rates
154
- </TooltipContent>
155
- </Tooltip>
156
- </TooltipProvider>
132
+ <label className="flex items-center gap-3">
133
+ <input
134
+ type="checkbox"
135
+ role="switch"
136
+ checked={strip}
137
+ disabled={isLoading || pending}
138
+ onChange={(e) => {
139
+ void handleToggle(e.currentTarget.checked);
140
+ }}
141
+ className="size-4 cursor-pointer disabled:cursor-not-allowed disabled:opacity-50"
142
+ />
143
+ <span className="text-sm">
144
+ {isLoading ? "Loading…" : strip ? "Stripping enabled" : "Stripping disabled"}
145
+ </span>
146
+ </label>
157
147
 
158
148
  {error !== null && <p className="text-xs text-destructive">Failed to save: {error}</p>}
159
149
  </div>
@@ -248,43 +248,13 @@ export const LogEntry = memo(function ({
248
248
  <div onClick={(e) => e.stopPropagation()} onKeyDown={(e) => e.stopPropagation()}>
249
249
  <Tabs value={activeTab} onValueChange={setActiveTab}>
250
250
  <TabsList className="mx-4 mt-2">
251
- {viewMode === "full" && (
252
- <Tooltip>
253
- <TooltipTrigger asChild>
254
- <TabsTrigger value="raw-headers">Raw Headers</TabsTrigger>
255
- </TooltipTrigger>
256
- <TooltipContent>
257
- HTTP headers received from the upstream provider
258
- </TooltipContent>
259
- </Tooltip>
260
- )}
261
- {viewMode === "full" && (
262
- <Tooltip>
263
- <TooltipTrigger asChild>
264
- <TabsTrigger value="headers">Headers</TabsTrigger>
265
- </TooltipTrigger>
266
- <TooltipContent>Request and response headers sent and received</TooltipContent>
267
- </Tooltip>
268
- )}
251
+ {viewMode === "full" && <TabsTrigger value="raw-headers">Raw Headers</TabsTrigger>}
252
+ {viewMode === "full" && <TabsTrigger value="headers">Headers</TabsTrigger>}
269
253
  {shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && (
270
- <Tooltip>
271
- <TooltipTrigger asChild>
272
- <TabsTrigger value="raw-request">Raw Request</TabsTrigger>
273
- </TooltipTrigger>
274
- <TooltipContent>
275
- Exact HTTP request sent to the upstream provider
276
- </TooltipContent>
277
- </Tooltip>
254
+ <TabsTrigger value="raw-request">Raw Request</TabsTrigger>
278
255
  )}
279
256
  <TabsTrigger value="request">Request</TabsTrigger>
280
- {viewMode === "full" && (
281
- <Tooltip>
282
- <TooltipTrigger asChild>
283
- <TabsTrigger value="raw">Raw Response</TabsTrigger>
284
- </TooltipTrigger>
285
- <TooltipContent>Exact HTTP response from the upstream provider</TooltipContent>
286
- </Tooltip>
287
- )}
257
+ {viewMode === "full" && <TabsTrigger value="raw">Raw Response</TabsTrigger>}
288
258
  <TabsTrigger value="parsed">Response</TabsTrigger>
289
259
  </TabsList>
290
260
 
@@ -210,43 +210,26 @@ export const LogEntryHeader = memo(function ({
210
210
 
211
211
  {/* Message count */}
212
212
  {messageCount !== null && (
213
- <Tooltip>
214
- <TooltipTrigger asChild>
215
- <span className="flex items-center gap-1 text-muted-foreground text-xs shrink-0">
216
- <MessageSquare className="size-3" />
217
- <span className="font-mono tabular-nums">{messageCount}</span>
218
- </span>
219
- </TooltipTrigger>
220
- <TooltipContent>Number of messages in the conversation</TooltipContent>
221
- </Tooltip>
213
+ <span className="flex items-center gap-1 text-muted-foreground text-xs shrink-0">
214
+ <MessageSquare className="size-3" />
215
+ <span className="font-mono tabular-nums">{messageCount}</span>
216
+ </span>
222
217
  )}
223
218
 
224
219
  {/* Tool count */}
225
220
  {toolCount !== null && (
226
- <Tooltip>
227
- <TooltipTrigger asChild>
228
- <span className="flex items-center gap-1 text-muted-foreground text-xs shrink-0">
229
- <Wrench className="size-3" />
230
- <span className="font-mono tabular-nums">{toolCount}</span>
231
- </span>
232
- </TooltipTrigger>
233
- <TooltipContent>Number of tools defined in the request</TooltipContent>
234
- </Tooltip>
221
+ <span className="flex items-center gap-1 text-muted-foreground text-xs shrink-0">
222
+ <Wrench className="size-3" />
223
+ <span className="font-mono tabular-nums">{toolCount}</span>
224
+ </span>
235
225
  )}
236
226
 
237
227
  {/* Response tool calls — tool names the model requested to invoke */}
238
228
  {responseToolNames !== null && responseToolNames.length > 0 && (
239
- <Tooltip>
240
- <TooltipTrigger asChild>
241
- <span className="flex items-center gap-1 text-amber-400/80 text-xs shrink-0">
242
- <Wrench className="size-3" />
243
- <span className="font-mono tabular-nums truncate max-w-[160px]">
244
- {toolNamesJoined}
245
- </span>
246
- </span>
247
- </TooltipTrigger>
248
- <TooltipContent>Tools called by model: {toolNamesJoined}</TooltipContent>
249
- </Tooltip>
229
+ <span className="flex items-center gap-1 text-amber-400/80 text-xs shrink-0">
230
+ <Wrench className="size-3" />
231
+ <span className="font-mono tabular-nums truncate max-w-[160px]">{toolNamesJoined}</span>
232
+ </span>
250
233
  )}
251
234
 
252
235
  {/* Origin */}