@tonyclaw/llm-inspector 1.14.8 → 1.14.9

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.
@@ -1,5 +1,5 @@
1
1
  import { r as reactExports, j as jsxRuntimeExports, a as React } from "../_libs/react.mjs";
2
- import { C as CapturedLogSchema, a as parseRequest, s as stripClaudeCodeBillingHeader, R as RuntimeConfigSchema, c as createPendingProviderTestResults, P as ProviderTestResultsSchema, b as createFailedProviderTestResults, d as ProviderConfigSchema, p as parseOpenAIResponse, I as InspectorResponseSchema } from "./router-BKnjB_zi.mjs";
2
+ import { C as CapturedLogSchema, a as parseRequest, O as OpenAIRequestSchema, p as parseOpenAIResponse, I as InspectorResponseSchema, s as stripClaudeCodeBillingHeader, R as RuntimeConfigSchema, c as createPendingProviderTestResults, P as ProviderTestResultsSchema, b as createFailedProviderTestResults, d as ProviderConfigSchema } from "./router-CmanwZJc.mjs";
3
3
  import { u as useSWR, a as useSWRConfig } from "../_libs/swr.mjs";
4
4
  import { u as useVirtualizer } from "../_libs/tanstack__react-virtual.mjs";
5
5
  import { c as clsx } from "../_libs/clsx.mjs";
@@ -10,7 +10,7 @@ import { d as diffLines, a as diffJson } from "../_libs/diff.mjs";
10
10
  import { R as Root, T as Trigger$2, C as Content, a as Close, b as Title, P as Portal$2, O as Overlay } from "../_libs/radix-ui__react-dialog.mjs";
11
11
  import { R as Root2, T as Trigger$1, I as Icon, V as Value, P as Portal$1, C as Content2$1, a as Viewport, b as Item, c as ItemIndicator, d as ItemText, S as ScrollUpButton, e as ScrollDownButton } from "../_libs/radix-ui__react-select.mjs";
12
12
  import "../_libs/modelcontextprotocol__server.mjs";
13
- import { D as Download, L as LayoutGrid, a as List, G as GitBranch, S as Settings, C as ChevronDown, b as Check, c as GitCompareArrows, R as RotateCcw, X, U as Upload, d as Scan, P as Plus, e as Copy, f as CircleAlert, g as ChevronUp, h as LoaderCircle, i as ChevronRight, j as Clock, M as MessageSquare, Z as Zap, W as Wrench, k as Globe, l as User, F as FileTerminal, m as Radio, n as Rows3, o as Columns2, p as Minus, q as Pencil, E as Equal, r as EyeOff, s as Eye, t as ExternalLink, u as RotateCw, T as Trash2, A as ArrowUp, v as ArrowDown, w as TriangleAlert, x as CircleCheckBig, y as CircleStop, z as CircleQuestionMark, B as Server, H as Gauge, I as Lock, J as Wifi, K as WifiOff, N as ChevronsUp, O as ChevronsDown, Q as Brain, V as Terminal } from "../_libs/lucide-react.mjs";
13
+ import { D as Download, L as LayoutGrid, a as List, G as GitBranch, S as Settings, C as ChevronDown, b as Check, c as GitCompareArrows, R as RotateCcw, X, U as Upload, d as Scan, P as Plus, e as Copy, f as CircleAlert, g as ChevronUp, h as LoaderCircle, i as ChevronRight, j as User, k as Clock, M as MessageSquare, Z as Zap, W as Wrench, l as Globe, F as FileTerminal, m as Radio, n as Rows3, o as Columns2, p as Minus, q as Pencil, E as Equal, r as EyeOff, s as Eye, t as ExternalLink, u as RotateCw, T as Trash2, A as ArrowUp, v as ArrowDown, w as TriangleAlert, x as CircleCheckBig, y as CircleStop, z as CircleQuestionMark, B as Server, H as Gauge, I as Lock, J as Wifi, K as WifiOff, N as ChevronsUp, O as ChevronsDown, Q as Brain, V as Terminal } from "../_libs/lucide-react.mjs";
14
14
  import { M as Markdown } from "../_libs/react-markdown.mjs";
15
15
  import { a as array, b as string, u as union, d as object, l as literal, n as number, c as boolean, _ as _enum } from "../_libs/zod.mjs";
16
16
  import { P as Provider, R as Root3, T as Trigger, a as Portal, C as Content2, A as Arrow2 } from "../_libs/radix-ui__react-tooltip.mjs";
@@ -333,7 +333,7 @@ async function exportLogsAsZip(logs) {
333
333
  document.body.removeChild(anchor);
334
334
  URL.revokeObjectURL(url);
335
335
  }
336
- const version = "1.14.8";
336
+ const version = "1.14.9";
337
337
  const packageJson = {
338
338
  version
339
339
  };
@@ -419,7 +419,8 @@ function ConversationHeader({
419
419
  hideApiFormat = false,
420
420
  isLoading = false,
421
421
  viewMode,
422
- onToggleViewMode
422
+ onToggleViewMode,
423
+ userAgent
423
424
  }) {
424
425
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
425
426
  "div",
@@ -465,6 +466,17 @@ function ConversationHeader({
465
466
  children: conversationId.length > 24 ? conversationId.slice(0, 12) + "…" + conversationId.slice(-12) : conversationId
466
467
  }
467
468
  ),
469
+ userAgent !== null && userAgent !== void 0 && userAgent !== "" && /* @__PURE__ */ jsxRuntimeExports.jsxs(
470
+ "span",
471
+ {
472
+ className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0",
473
+ title: userAgent,
474
+ children: [
475
+ /* @__PURE__ */ jsxRuntimeExports.jsx(User, { className: "size-3" }),
476
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums truncate max-w-[120px]", children: userAgent })
477
+ ]
478
+ }
479
+ ),
468
480
  !hideApiFormat && /* @__PURE__ */ jsxRuntimeExports.jsx(
469
481
  Badge,
470
482
  {
@@ -1455,16 +1467,15 @@ function CacheTrendIndicator({ trend }) {
1455
1467
  }
1456
1468
  const LogEntryHeader = reactExports.memo(function({
1457
1469
  log,
1458
- parsedRequest,
1470
+ messageCount = null,
1471
+ toolCount = null,
1459
1472
  expanded,
1460
1473
  onToggle,
1461
- suppressApiFormatBadge = false,
1474
+ responseToolNames = null,
1462
1475
  cacheTrend = null
1463
1476
  }) {
1464
1477
  const statusCategory = getStatusCategory(log.responseStatus);
1465
1478
  const hasTokens = log.inputTokens !== null || log.outputTokens !== null;
1466
- const messageCount = parsedRequest !== null ? parsedRequest.messages.length : null;
1467
- const toolCount = parsedRequest !== null && parsedRequest.tools !== void 0 && parsedRequest.tools.length > 0 ? parsedRequest.tools.length : null;
1468
1479
  return /* @__PURE__ */ jsxRuntimeExports.jsxs(
1469
1480
  "div",
1470
1481
  {
@@ -1491,20 +1502,14 @@ const LogEntryHeader = reactExports.memo(function({
1491
1502
  /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "shrink-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ProviderLogo, { provider: detectProvider(log.model), className: "size-4" }) }) }),
1492
1503
  /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: log.model })
1493
1504
  ] }) }),
1494
- !suppressApiFormatBadge && /* @__PURE__ */ jsxRuntimeExports.jsx(
1505
+ statusCategory !== "success" && /* @__PURE__ */ jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, { children: statusCategory === "server_error" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
1495
1506
  Badge,
1496
1507
  {
1497
- variant: "outline",
1498
- className: cn(
1499
- "text-[10px] px-1.5 py-0 h-5 font-mono shrink-0",
1500
- log.apiFormat === "openai" && "border-blue-500/40 text-blue-400",
1501
- log.apiFormat === "anthropic" && "border-orange-500/40 text-orange-400",
1502
- log.apiFormat === "unknown" && "border-muted text-muted-foreground"
1503
- ),
1504
- children: log.apiFormat === "anthropic" ? "Anthropic" : log.apiFormat === "openai" ? "OpenAI" : "Unknown"
1508
+ variant: "destructive",
1509
+ className: "text-[10px] px-1.5 py-0 h-5 font-mono tabular-nums",
1510
+ children: log.responseStatus
1505
1511
  }
1506
- ),
1507
- statusCategory === "server_error" ? /* @__PURE__ */ jsxRuntimeExports.jsx(Badge, { variant: "destructive", className: "text-[10px] px-1.5 py-0 h-5 font-mono tabular-nums", children: log.responseStatus }) : statusCategory === "pending" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
1512
+ ) : statusCategory === "pending" ? /* @__PURE__ */ jsxRuntimeExports.jsx(
1508
1513
  Badge,
1509
1514
  {
1510
1515
  variant: "outline",
@@ -1524,7 +1529,7 @@ const LogEntryHeader = reactExports.memo(function({
1524
1529
  ),
1525
1530
  children: log.responseStatus
1526
1531
  }
1527
- ),
1532
+ ) }),
1528
1533
  log.elapsedMs !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0", children: [
1529
1534
  /* @__PURE__ */ jsxRuntimeExports.jsx(Clock, { className: "size-3" }),
1530
1535
  /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums", children: formatElapsed(log.elapsedMs) })
@@ -1583,6 +1588,16 @@ const LogEntryHeader = reactExports.memo(function({
1583
1588
  ] }) }),
1584
1589
  /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipContent, { children: "Number of tools defined in the request" })
1585
1590
  ] }) }),
1591
+ responseToolNames !== null && responseToolNames.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
1592
+ /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-amber-400/80 text-xs shrink-0", children: [
1593
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Wrench, { className: "size-3" }),
1594
+ /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums truncate max-w-[160px]", children: responseToolNames.join(", ") })
1595
+ ] }) }),
1596
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(TooltipContent, { children: [
1597
+ "Tools called by model: ",
1598
+ responseToolNames.join(", ")
1599
+ ] })
1600
+ ] }) }),
1586
1601
  log.origin !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1587
1602
  "span",
1588
1603
  {
@@ -1594,17 +1609,6 @@ const LogEntryHeader = reactExports.memo(function({
1594
1609
  ]
1595
1610
  }
1596
1611
  ),
1597
- log.userAgent !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs(
1598
- "span",
1599
- {
1600
- className: "flex items-center gap-1 text-muted-foreground text-xs shrink-0",
1601
- title: `User-Agent: ${log.userAgent}`,
1602
- children: [
1603
- /* @__PURE__ */ jsxRuntimeExports.jsx(User, { className: "size-3" }),
1604
- /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-mono tabular-nums truncate max-w-[150px]", title: log.userAgent, children: log.userAgent })
1605
- ]
1606
- }
1607
- ),
1608
1612
  (log.clientPid !== null || log.clientProjectFolder !== null) && /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Tooltip, { children: [
1609
1613
  /* @__PURE__ */ jsxRuntimeExports.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-center gap-1 text-purple-400/80 text-xs shrink-0", children: [
1610
1614
  /* @__PURE__ */ jsxRuntimeExports.jsx(FileTerminal, { className: "size-3" }),
@@ -2479,7 +2483,6 @@ function DiffToggleButton({
2479
2483
  const LogEntry = reactExports.memo(function({
2480
2484
  log,
2481
2485
  viewMode = "simple",
2482
- suppressApiFormatBadge = false,
2483
2486
  strip,
2484
2487
  cacheTrend = null,
2485
2488
  onCompareWithPrevious
@@ -2491,7 +2494,60 @@ const LogEntry = reactExports.memo(function({
2491
2494
  const [replayOpen, setReplayOpen] = reactExports.useState(false);
2492
2495
  const [headersDiff, setHeadersDiff] = reactExports.useState(false);
2493
2496
  const [requestDiff, setRequestDiff] = reactExports.useState(false);
2494
- const parsedRequest = reactExports.useMemo(() => parseRequest(log.rawRequestBody), [log.rawRequestBody]);
2497
+ const messageCount = reactExports.useMemo(() => {
2498
+ if (log.rawRequestBody === null) return null;
2499
+ if (log.apiFormat === "anthropic") {
2500
+ const parsed = parseRequest(log.rawRequestBody);
2501
+ if (parsed !== null) return parsed.messages.length;
2502
+ } else if (log.apiFormat === "openai") {
2503
+ try {
2504
+ const result = OpenAIRequestSchema.safeParse(JSON.parse(log.rawRequestBody));
2505
+ if (result.success) return result.data.messages.length;
2506
+ } catch {
2507
+ }
2508
+ }
2509
+ return null;
2510
+ }, [log.rawRequestBody, log.apiFormat]);
2511
+ const toolCount = reactExports.useMemo(() => {
2512
+ if (log.rawRequestBody === null) return null;
2513
+ if (log.apiFormat === "anthropic") {
2514
+ const parsed = parseRequest(log.rawRequestBody);
2515
+ if (parsed !== null && parsed.tools !== void 0 && parsed.tools.length > 0) {
2516
+ return parsed.tools.length;
2517
+ }
2518
+ } else if (log.apiFormat === "openai") {
2519
+ try {
2520
+ const result = OpenAIRequestSchema.safeParse(JSON.parse(log.rawRequestBody));
2521
+ if (result.success && result.data.tools !== void 0 && result.data.tools.length > 0) {
2522
+ return result.data.tools.length;
2523
+ }
2524
+ } catch {
2525
+ }
2526
+ }
2527
+ return null;
2528
+ }, [log.rawRequestBody, log.apiFormat]);
2529
+ const responseToolNames = reactExports.useMemo(() => {
2530
+ if (log.responseText === null) return null;
2531
+ if (log.apiFormat === "openai") {
2532
+ const parsed = parseOpenAIResponse(log.responseText);
2533
+ if (parsed !== null) {
2534
+ const toolCalls = parsed.choices[0]?.message?.tool_calls;
2535
+ if (toolCalls !== void 0 && toolCalls !== null && toolCalls.length > 0) {
2536
+ return toolCalls.map((tc) => tc.function?.name ?? "?").filter((n) => n !== "");
2537
+ }
2538
+ }
2539
+ } else if (log.apiFormat === "anthropic") {
2540
+ try {
2541
+ const result = InspectorResponseSchema.safeParse(JSON.parse(log.responseText));
2542
+ if (result.success) {
2543
+ const names = result.data.content.filter((c) => c.type === "tool_use").map((c) => c.name);
2544
+ if (names.length > 0) return names;
2545
+ }
2546
+ } catch {
2547
+ }
2548
+ }
2549
+ return null;
2550
+ }, [log.responseText, log.apiFormat]);
2495
2551
  const strippedRequestBody = reactExports.useMemo(() => {
2496
2552
  if (!strip || log.apiFormat !== "anthropic" || log.rawRequestBody === null) {
2497
2553
  return null;
@@ -2537,10 +2593,11 @@ const LogEntry = reactExports.memo(function({
2537
2593
  LogEntryHeader,
2538
2594
  {
2539
2595
  log,
2540
- parsedRequest,
2596
+ messageCount,
2597
+ toolCount,
2541
2598
  expanded,
2542
2599
  onToggle: () => setExpanded(!expanded),
2543
- suppressApiFormatBadge,
2600
+ responseToolNames,
2544
2601
  cacheTrend
2545
2602
  }
2546
2603
  ),
@@ -2731,26 +2788,14 @@ function ThreadConnector({
2731
2788
  stopReason,
2732
2789
  isPending,
2733
2790
  isFirst,
2734
- isLast
2791
+ isLast: _isLast,
2792
+ isTurnStart
2735
2793
  }) {
2736
2794
  const isBoundary = stopReason === "end_turn" || stopReason === "stop";
2737
2795
  const isToolUse = stopReason === "tool_use";
2738
- cn(
2739
- "w-0.5 bg-muted-foreground/30",
2740
- isPending && "border-dashed bg-transparent border-l-2 border-muted-foreground/20"
2741
- );
2742
- return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-stretch h-full w-6 shrink-0 relative", children: [
2743
- /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "absolute left-1/2 -translate-x-1/2 w-0.5 top-0 flex flex-col items-center", children: [
2744
- !isFirst && /* @__PURE__ */ jsxRuntimeExports.jsx(
2745
- "div",
2746
- {
2747
- className: cn("w-0.5 grow", "bg-muted-foreground/30", "h-4"),
2748
- style: { minHeight: "0.5rem" }
2749
- }
2750
- ),
2751
- isFirst && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "h-4" })
2752
- ] }),
2753
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "absolute left-1/2 -translate-x-1/2 top-4 flex items-center justify-center z-10", children: isBoundary ? /* @__PURE__ */ jsxRuntimeExports.jsx(
2796
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex flex-col items-center w-6 shrink-0", children: [
2797
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-center h-4", children: !isFirst && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-0.5 bg-muted-foreground/30" }) }),
2798
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center py-0.5", children: isBoundary ? /* @__PURE__ */ jsxRuntimeExports.jsx(
2754
2799
  "div",
2755
2800
  {
2756
2801
  className: cn(
@@ -2763,8 +2808,11 @@ function ThreadConnector({
2763
2808
  ) : isToolUse ? /* @__PURE__ */ jsxRuntimeExports.jsx(
2764
2809
  "div",
2765
2810
  {
2766
- className: "size-2 rounded-full bg-muted-foreground/25",
2767
- title: "Tool Use — turn continues"
2811
+ className: cn(
2812
+ "size-2 rounded-full",
2813
+ isTurnStart ? "bg-emerald-400 shadow-[0_0_6px_rgba(52,211,153,0.5)]" : "bg-muted-foreground/25"
2814
+ ),
2815
+ title: isTurnStart ? "Tool Use — start of turn" : "Tool Use — turn continues"
2768
2816
  }
2769
2817
  ) : isPending ? /* @__PURE__ */ jsxRuntimeExports.jsx(
2770
2818
  "div",
@@ -2772,26 +2820,24 @@ function ThreadConnector({
2772
2820
  className: "size-2.5 rounded-full border-2 border-dashed border-muted-foreground/30 animate-pulse",
2773
2821
  title: "Response pending"
2774
2822
  }
2775
- ) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "size-1.5 rounded-full bg-muted-foreground/30" }) }),
2776
- /* @__PURE__ */ jsxRuntimeExports.jsxs(
2823
+ ) : /* @__PURE__ */ jsxRuntimeExports.jsx(
2777
2824
  "div",
2778
2825
  {
2779
- className: "absolute left-1/2 -translate-x-1/2 w-0.5 top-4 flex flex-col items-center",
2780
- style: { bottom: 0 },
2781
- children: [
2782
- !isBoundary && /* @__PURE__ */ jsxRuntimeExports.jsx(
2783
- "div",
2784
- {
2785
- className: cn(
2786
- "w-0.5 flex-1",
2787
- isPending ? "border-dashed bg-transparent border-l-2 border-muted-foreground/20" : "bg-muted-foreground/30"
2788
- )
2789
- }
2790
- ),
2791
- isBoundary && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-0.5 h-4 bg-muted-foreground/10" })
2792
- ]
2826
+ className: cn(
2827
+ "size-1.5 rounded-full",
2828
+ isTurnStart ? "bg-emerald-400 shadow-[0_0_6px_rgba(52,211,153,0.5)]" : "bg-muted-foreground/30"
2829
+ )
2793
2830
  }
2794
- )
2831
+ ) }),
2832
+ /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 flex justify-center min-h-1", children: isBoundary ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "w-0.5 bg-muted-foreground/10 h-4" }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
2833
+ "div",
2834
+ {
2835
+ className: cn(
2836
+ "w-0.5 h-full",
2837
+ isPending ? "border-dashed bg-transparent border-l-2 border-muted-foreground/20" : "bg-muted-foreground/30"
2838
+ )
2839
+ }
2840
+ ) })
2795
2841
  ] });
2796
2842
  }
2797
2843
  function computeStats(logs) {
@@ -2822,6 +2868,17 @@ const ConversationGroup = reactExports.memo(function({
2822
2868
  const mixed = hasMixedApiFormat(group.logs);
2823
2869
  const isLoading = group.logs.some((log) => log.responseStatus === null);
2824
2870
  const stopReasons = reactExports.useMemo(() => group.logs.map((log) => extractStopReason(log)), [group.logs]);
2871
+ const turnIndices = reactExports.useMemo(() => {
2872
+ const indices = [];
2873
+ let turn = 0;
2874
+ for (let i = 0; i < stopReasons.length; i++) {
2875
+ if (i > 0 && (stopReasons[i - 1] === "end_turn" || stopReasons[i - 1] === "stop")) {
2876
+ turn++;
2877
+ }
2878
+ indices.push(turn);
2879
+ }
2880
+ return indices;
2881
+ }, [stopReasons]);
2825
2882
  const displayId = group.conversationId.startsWith("PID:") || group.conversationId.includes("|") ? group.conversationId : group.conversationId.length > 24 ? group.conversationId.slice(0, 12) + "…" + group.conversationId.slice(-12) : group.conversationId;
2826
2883
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "mb-4", children: [
2827
2884
  /* @__PURE__ */ jsxRuntimeExports.jsx(
@@ -2838,6 +2895,7 @@ const ConversationGroup = reactExports.memo(function({
2838
2895
  onToggle: () => setExpanded(!expanded),
2839
2896
  hideApiFormat: mixed,
2840
2897
  isLoading,
2898
+ userAgent: group.logs[0]?.userAgent ?? null,
2841
2899
  viewMode: groupViewMode,
2842
2900
  onToggleViewMode: () => setGroupViewMode((prev) => prev === "thread" ? "flat" : "thread")
2843
2901
  }
@@ -2847,35 +2905,46 @@ const ConversationGroup = reactExports.memo(function({
2847
2905
  {
2848
2906
  log,
2849
2907
  viewMode,
2850
- suppressApiFormatBadge: !mixed,
2851
2908
  strip,
2852
2909
  cacheTrend: cacheTrends?.get(log.id) ?? null,
2853
2910
  onCompareWithPrevious: () => onCompareWithPrevious(log)
2854
2911
  },
2855
2912
  log.id
2856
2913
  )) }),
2857
- expanded && groupViewMode === "thread" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ml-3", children: group.logs.map((log, idx) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-stretch", children: [
2858
- /* @__PURE__ */ jsxRuntimeExports.jsx(
2859
- ThreadConnector,
2860
- {
2861
- stopReason: stopReasons[idx] ?? null,
2862
- isPending: log.responseStatus === null,
2863
- isFirst: idx === 0,
2864
- isLast: idx === group.logs.length - 1
2865
- }
2866
- ),
2867
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-w-0 mb-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
2868
- LogEntry,
2869
- {
2870
- log,
2871
- viewMode,
2872
- suppressApiFormatBadge: !mixed,
2873
- strip,
2874
- cacheTrend: cacheTrends?.get(log.id) ?? null,
2875
- onCompareWithPrevious: () => onCompareWithPrevious(log)
2876
- }
2877
- ) })
2878
- ] }, log.id)) })
2914
+ expanded && groupViewMode === "thread" && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "ml-3", children: group.logs.map((log, idx) => {
2915
+ const isTurnStart = idx === 0 || stopReasons[idx - 1] === "end_turn" || stopReasons[idx - 1] === "stop";
2916
+ return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-stretch", children: [
2917
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2918
+ ThreadConnector,
2919
+ {
2920
+ stopReason: stopReasons[idx] ?? null,
2921
+ isPending: log.responseStatus === null,
2922
+ isFirst: idx === 0,
2923
+ isLast: idx === group.logs.length - 1,
2924
+ isTurnStart
2925
+ }
2926
+ ),
2927
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2928
+ "div",
2929
+ {
2930
+ className: cn(
2931
+ "flex-1 min-w-0 mb-2 rounded-lg",
2932
+ (turnIndices[idx] ?? 0) % 2 === 0 ? "bg-muted/10" : "bg-muted/25"
2933
+ ),
2934
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
2935
+ LogEntry,
2936
+ {
2937
+ log,
2938
+ viewMode,
2939
+ strip,
2940
+ cacheTrend: cacheTrends?.get(log.id) ?? null,
2941
+ onCompareWithPrevious: () => onCompareWithPrevious(log)
2942
+ }
2943
+ )
2944
+ }
2945
+ )
2946
+ ] }, log.id);
2947
+ }) })
2879
2948
  ] });
2880
2949
  });
2881
2950
  function CrabLogo({ className }) {
@@ -5706,6 +5775,17 @@ function ProxyViewer({
5706
5775
  const groups = reactExports.useMemo(() => groupLogsByConversation(logs), [logs]);
5707
5776
  const cacheTrends = reactExports.useMemo(() => computeCacheTrends(groups), [groups]);
5708
5777
  const stopReasons = reactExports.useMemo(() => logs.map((log) => extractStopReason(log)), [logs]);
5778
+ const turnIndices = reactExports.useMemo(() => {
5779
+ const indices = [];
5780
+ let turn = 0;
5781
+ for (let i = 0; i < stopReasons.length; i++) {
5782
+ if (i > 0 && (stopReasons[i - 1] === "end_turn" || stopReasons[i - 1] === "stop")) {
5783
+ turn++;
5784
+ }
5785
+ indices.push(turn);
5786
+ }
5787
+ return indices;
5788
+ }, [stopReasons]);
5709
5789
  const renderGroups = logs.length > 0 && groupedView && groups.length > 1;
5710
5790
  const rowVirtualizer = useVirtualizer({
5711
5791
  count: renderGroups ? groups.length : logs.length,
@@ -5908,19 +5988,29 @@ function ProxyViewer({
5908
5988
  stopReason: stopReasons[idx] ?? null,
5909
5989
  isPending: log.responseStatus === null,
5910
5990
  isFirst: idx === 0,
5911
- isLast: idx === logs.length - 1
5991
+ isLast: idx === logs.length - 1,
5992
+ isTurnStart: idx === 0 || stopReasons[idx - 1] === "end_turn" || stopReasons[idx - 1] === "stop"
5912
5993
  }
5913
5994
  ),
5914
- /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex-1 min-w-0 mb-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
5915
- LogEntry,
5995
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
5996
+ "div",
5916
5997
  {
5917
- log,
5918
- viewMode,
5919
- strip,
5920
- cacheTrend: cacheTrends.get(log.id) ?? null,
5921
- onCompareWithPrevious: () => handleCompareWithPrevious(log)
5998
+ className: cn(
5999
+ "flex-1 min-w-0 mb-2 rounded-lg",
6000
+ (turnIndices[idx] ?? 0) % 2 === 0 ? "bg-muted/10" : "bg-muted/25"
6001
+ ),
6002
+ children: /* @__PURE__ */ jsxRuntimeExports.jsx(
6003
+ LogEntry,
6004
+ {
6005
+ log,
6006
+ viewMode,
6007
+ strip,
6008
+ cacheTrend: cacheTrends.get(log.id) ?? null,
6009
+ onCompareWithPrevious: () => handleCompareWithPrevious(log)
6010
+ }
6011
+ )
5922
6012
  }
5923
- ) })
6013
+ )
5924
6014
  ] }) : /* @__PURE__ */ jsxRuntimeExports.jsx(
5925
6015
  LogEntry,
5926
6016
  {
@@ -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-IsglLVKy.mjs");
201
+ const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-BVIiyDeJ.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-BKnjB_zi.mjs").then((n) => n.r);
770
+ const routerEntry = await import("./router-CmanwZJc.mjs").then((n) => n.r);
771
771
  const startEntry = await import("./start-HYkvq4Ni.mjs");
772
772
  return { startEntry, routerEntry };
773
773
  }
@@ -45,7 +45,7 @@ import "../_libs/debounce-fn.mjs";
45
45
  import "../_libs/mimic-function.mjs";
46
46
  import "../_libs/semver.mjs";
47
47
  import "../_libs/uint8array-extras.mjs";
48
- const appCss = "/assets/index-vP91146S.css";
48
+ const appCss = "/assets/index-bqeypwJB.css";
49
49
  const Route$k = createRootRoute({
50
50
  head: () => ({
51
51
  meta: [
@@ -69,7 +69,7 @@ function RootDocument({ children }) {
69
69
  ] })
70
70
  ] });
71
71
  }
72
- const $$splitComponentImporter = () => import("./index-9uTJ4xYR.mjs");
72
+ const $$splitComponentImporter = () => import("./index-_9xcAkkw.mjs");
73
73
  const Route$j = createFileRoute("/")({
74
74
  component: lazyRouteComponent($$splitComponentImporter, "component")
75
75
  });
@@ -577,6 +577,7 @@ const OpenAIRequestSchema = object({
577
577
  stream: boolean().optional(),
578
578
  tools: array(OpenAIToolDefinition).optional(),
579
579
  tool_choice: union([
580
+ _enum(["auto", "none", "required"]),
580
581
  object({ type: literal("auto") }),
581
582
  object({ type: literal("none") }),
582
583
  object({ type: literal("function"), function: object({ name: string() }) })
@@ -1276,11 +1277,7 @@ const AnthropicFormatHandler = {
1276
1277
  const json = JSON.parse(rawBody);
1277
1278
  if (typeof json === "object" && json !== null && !Array.isArray(json)) {
1278
1279
  const keys = Object.keys(json);
1279
- if (keys.includes("model") && keys.includes("messages")) {
1280
- if (keys.includes("system") || keys.includes("tools")) {
1281
- return true;
1282
- }
1283
- }
1280
+ return keys.includes("model") && keys.includes("messages") && keys.includes("system");
1284
1281
  }
1285
1282
  return false;
1286
1283
  } catch {
@@ -1410,6 +1407,21 @@ function extractOpenAIStream(raw, log, fallbackModel, collectChunks = true) {
1410
1407
  promptTokens = chunk.usage.prompt_tokens ?? 0;
1411
1408
  completionTokens = chunk.usage.completion_tokens ?? 0;
1412
1409
  log.inputTokens = promptTokens;
1410
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
1411
+ const usageDesc = Object.getOwnPropertyDescriptor(parsed, "usage");
1412
+ if (usageDesc !== void 0 && typeof usageDesc.value === "object" && usageDesc.value !== null) {
1413
+ const detailsDesc = Object.getOwnPropertyDescriptor(
1414
+ usageDesc.value,
1415
+ "prompt_tokens_details"
1416
+ );
1417
+ if (detailsDesc !== void 0 && typeof detailsDesc.value === "object" && detailsDesc.value !== null) {
1418
+ const cacheDesc = Object.getOwnPropertyDescriptor(detailsDesc.value, "cached_tokens");
1419
+ if (cacheDesc !== void 0 && typeof cacheDesc.value === "number") {
1420
+ log.cacheReadInputTokens = cacheDesc.value;
1421
+ }
1422
+ }
1423
+ }
1424
+ }
1413
1425
  usageCaptured = true;
1414
1426
  }
1415
1427
  for (const choice of chunk.choices) {
@@ -1506,11 +1518,31 @@ const OpenAIFormatHandler = {
1506
1518
  extractTokens(responseBody) {
1507
1519
  const parsed = parseOpenAIResponse(responseBody);
1508
1520
  if (parsed) {
1521
+ let cacheReadInputTokens = null;
1522
+ try {
1523
+ const raw = JSON.parse(responseBody);
1524
+ if (raw !== null && typeof raw === "object" && !Array.isArray(raw)) {
1525
+ const usageDesc = Object.getOwnPropertyDescriptor(raw, "usage");
1526
+ if (usageDesc !== void 0 && typeof usageDesc.value === "object" && usageDesc.value !== null) {
1527
+ const detailsDesc = Object.getOwnPropertyDescriptor(
1528
+ usageDesc.value,
1529
+ "prompt_tokens_details"
1530
+ );
1531
+ if (detailsDesc !== void 0 && typeof detailsDesc.value === "object" && detailsDesc.value !== null) {
1532
+ const cacheDesc = Object.getOwnPropertyDescriptor(detailsDesc.value, "cached_tokens");
1533
+ if (cacheDesc !== void 0 && typeof cacheDesc.value === "number") {
1534
+ cacheReadInputTokens = cacheDesc.value;
1535
+ }
1536
+ }
1537
+ }
1538
+ }
1539
+ } catch {
1540
+ }
1509
1541
  return {
1510
1542
  inputTokens: parsed.usage.prompt_tokens ?? null,
1511
1543
  outputTokens: parsed.usage.completion_tokens ?? null,
1512
1544
  cacheCreationInputTokens: null,
1513
- cacheReadInputTokens: null
1545
+ cacheReadInputTokens
1514
1546
  };
1515
1547
  }
1516
1548
  return {
@@ -1529,11 +1561,7 @@ const OpenAIFormatHandler = {
1529
1561
  const json = JSON.parse(rawBody);
1530
1562
  if (typeof json === "object" && json !== null && !Array.isArray(json)) {
1531
1563
  const keys = Object.keys(json);
1532
- if (keys.includes("model") && keys.includes("messages")) {
1533
- if (!keys.includes("system") && !keys.includes("tools")) {
1534
- return true;
1535
- }
1536
- }
1564
+ return keys.includes("model") && keys.includes("messages") && !keys.includes("system");
1537
1565
  }
1538
1566
  return false;
1539
1567
  } catch {
@@ -2504,6 +2532,8 @@ async function handleProxy(req) {
2504
2532
  upstreamHeaders.forEach((value, key) => {
2505
2533
  upstreamHeadersObj[key.toLowerCase()] = value;
2506
2534
  });
2535
+ const bodyFormat = formatRegistry.detectFormat(requestBody);
2536
+ const displayApiFormat = bodyFormat !== "unknown" ? bodyFormat : formatHandler.format;
2507
2537
  const log = await createLog(
2508
2538
  req.method,
2509
2539
  parsed.apiPath,
@@ -2512,7 +2542,7 @@ async function handleProxy(req) {
2512
2542
  clientInfo,
2513
2543
  rawHeaders,
2514
2544
  upstreamHeadersObj,
2515
- formatHandler.format,
2545
+ displayApiFormat,
2516
2546
  model,
2517
2547
  sessionId,
2518
2548
  preAcquiredId
@@ -4713,6 +4743,7 @@ const router = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
4713
4743
  export {
4714
4744
  CapturedLogSchema as C,
4715
4745
  InspectorResponseSchema as I,
4746
+ OpenAIRequestSchema as O,
4716
4747
  ProviderTestResultsSchema as P,
4717
4748
  RuntimeConfigSchema as R,
4718
4749
  parseRequest as a,
@@ -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-CJ4MreBr.js"], "assets": [] }, "/": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-CdnotuLh.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-CJ4MreBr.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-C8OUJKbz.js"], "assets": [] }, "/": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-Dv-dj1xH.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-C8OUJKbz.js" });
2
2
  export {
3
3
  tsrStartManifest
4
4
  };