@tonyclaw/llm-inspector 1.7.7 → 1.7.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, p as parseOpenAIResponse, I as InspectorResponseSchema } from "./router-CZSteFqT.mjs";
2
+ import { C as CapturedLogSchema, a as parseRequest, p as parseOpenAIResponse, I as InspectorResponseSchema } from "./router-CsCLdrXq.mjs";
3
3
  import { u as useVirtualizer } from "../_libs/tanstack__react-virtual.mjs";
4
4
  import { J as JSZip } from "../_libs/jszip.mjs";
5
5
  import { c as clsx } from "../_libs/clsx.mjs";
@@ -7,9 +7,9 @@ import { t as twMerge } from "../_libs/tailwind-merge.mjs";
7
7
  import { c as cva } from "../_libs/class-variance-authority.mjs";
8
8
  import { R as Root, T as Trigger$1, C as Content, a as Close, b as Title, P as Portal$1, O as Overlay } from "../_libs/radix-ui__react-dialog.mjs";
9
9
  import { R as Root2, T as Trigger, I as Icon, V as Value, P as Portal, C as Content2, 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";
10
- import { D as Download, L as LayoutGrid, a as List, S as Settings, C as ChevronDown, b as Check, R as RotateCcw, X, P as Plus, c as Copy, d as CircleAlert, e as ChevronUp, f as ChevronRight, g as Clock, M as MessageSquare, Z as Zap, h as LoaderCircle, W as Wrench, G as Globe, U as User, F as FileTerminal, i as Radio, E as ExternalLink, j as EyeOff, k as Eye, l as RotateCw, m as Pencil, T as Trash2, n as TriangleAlert, o as Minus, p as CircleCheckBig, q as CircleStop, r as CircleQuestionMark, s as Server, t as Gauge, u as Lock, v as Wifi, w as WifiOff, x as ChevronsUp, y as ChevronsDown, z as Terminal, B as Brain } from "../_libs/lucide-react.mjs";
10
+ import { D as Download, L as LayoutGrid, a as List, S as Settings, C as ChevronDown, b as Check, R as RotateCcw, X, U as Upload, P as Plus, c as Copy, d as CircleAlert, e as ChevronUp, f as ChevronRight, g as Clock, M as MessageSquare, Z as Zap, h as LoaderCircle, W as Wrench, G as Globe, i as User, F as FileTerminal, j as Radio, E as ExternalLink, k as EyeOff, l as Eye, m as RotateCw, n as Pencil, T as Trash2, o as TriangleAlert, p as Minus, q as CircleCheckBig, r as CircleStop, s as CircleQuestionMark, t as Server, u as Gauge, v as Lock, w as Wifi, x as WifiOff, y as ChevronsUp, z as ChevronsDown, A as Terminal, B as Brain } from "../_libs/lucide-react.mjs";
11
11
  import { M as Markdown } from "../_libs/react-markdown.mjs";
12
- import { a as array, s as string, u as union, o as object, l as literal, b as boolean, n as number } from "../_libs/zod.mjs";
12
+ import { a as array, s as string, u as union, o as object, l as literal, n as number, b as boolean } from "../_libs/zod.mjs";
13
13
  import { R as Root2$1, L as List$1, T as Trigger$2, C as Content$1 } from "../_libs/radix-ui__react-tabs.mjs";
14
14
  import { S as Slot } from "../_libs/radix-ui__react-slot.mjs";
15
15
  import { P as Provider, R as Root3, T as Trigger$3, a as Portal$2, C as Content2$1, A as Arrow2 } from "../_libs/radix-ui__react-tooltip.mjs";
@@ -946,7 +946,7 @@ const STATUS_BADGE_CLASSES = {
946
946
  server_error: "",
947
947
  pending: "bg-muted text-muted-foreground border-border"
948
948
  };
949
- function LogEntryHeader({
949
+ const LogEntryHeader = reactExports.memo(function LogEntryHeader2({
950
950
  log,
951
951
  parsedRequest,
952
952
  expanded,
@@ -1098,7 +1098,7 @@ function LogEntryHeader({
1098
1098
  ]
1099
1099
  }
1100
1100
  );
1101
- }
1101
+ });
1102
1102
  function Dialog({
1103
1103
  ...props
1104
1104
  }) {
@@ -1832,7 +1832,10 @@ function CopyButton({
1832
1832
  }
1833
1833
  );
1834
1834
  }
1835
- function LogEntry({ log, viewMode = "simple" }) {
1835
+ const LogEntry = reactExports.memo(function LogEntry2({
1836
+ log,
1837
+ viewMode = "simple"
1838
+ }) {
1836
1839
  const [expanded, setExpanded] = reactExports.useState(false);
1837
1840
  const [requestCopied, setRequestCopied] = reactExports.useState(false);
1838
1841
  const [responseCopied, setResponseCopied] = reactExports.useState(false);
@@ -1953,7 +1956,7 @@ function LogEntry({ log, viewMode = "simple" }) {
1953
1956
  ] }),
1954
1957
  /* @__PURE__ */ jsxRuntimeExports.jsx(ReplayDialog, { log, open: replayOpen, onOpenChange: setReplayOpen })
1955
1958
  ] });
1956
- }
1959
+ });
1957
1960
  function computeStats(logs) {
1958
1961
  let totalInput = 0;
1959
1962
  let totalOutput = 0;
@@ -1963,7 +1966,7 @@ function computeStats(logs) {
1963
1966
  }
1964
1967
  return { totalInputTokens: totalInput, totalOutputTokens: totalOutput };
1965
1968
  }
1966
- function ConversationGroup({
1969
+ const ConversationGroup = reactExports.memo(function ConversationGroup2({
1967
1970
  group,
1968
1971
  viewMode = "simple"
1969
1972
  }) {
@@ -1988,7 +1991,7 @@ function ConversationGroup({
1988
1991
  ),
1989
1992
  expanded && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "pl-4 border-l-2 border-muted ml-3", children: group.logs.map((log) => /* @__PURE__ */ jsxRuntimeExports.jsx(LogEntry, { log, viewMode }, log.id)) })
1990
1993
  ] });
1991
- }
1994
+ });
1992
1995
  function Select({
1993
1996
  ...props
1994
1997
  }) {
@@ -2562,11 +2565,21 @@ function ProvidersPanel({
2562
2565
  const TEST_TIMEOUT_SECONDS = 30;
2563
2566
  const runTest = reactExports.useCallback(
2564
2567
  async (providerId) => {
2568
+ const resetResults = {
2569
+ anthropic: { nonStreaming: { notConfigured: true }, streaming: { notConfigured: true } },
2570
+ openai: { nonStreaming: { notConfigured: true }, streaming: { notConfigured: true } }
2571
+ };
2572
+ if (onTestResultsChange) {
2573
+ onTestResultsChange(providerId, resetResults);
2574
+ } else {
2575
+ setInternalTestResults((prev) => ({ ...prev, [providerId]: resetResults }));
2576
+ }
2565
2577
  if (onTestingProvidersChange) {
2566
2578
  onTestingProvidersChange(providerId, true);
2567
2579
  } else {
2568
2580
  setInternalTestingProviders((prev) => new Set(prev).add(providerId));
2569
2581
  }
2582
+ const controller = new AbortController();
2570
2583
  let remaining = TEST_TIMEOUT_SECONDS;
2571
2584
  setTestingTimeLeft(providerId, remaining);
2572
2585
  const intervalId = setInterval(() => {
@@ -2574,10 +2587,14 @@ function ProvidersPanel({
2574
2587
  setTestingTimeLeft(providerId, remaining);
2575
2588
  if (remaining <= 0) {
2576
2589
  clearInterval(intervalId);
2590
+ controller.abort();
2577
2591
  }
2578
2592
  }, 1e3);
2579
2593
  try {
2580
- const res = await fetch(`/api/providers/${providerId}/test`, { method: "POST" });
2594
+ const res = await fetch(`/api/providers/${providerId}/test`, {
2595
+ method: "POST",
2596
+ signal: controller.signal
2597
+ });
2581
2598
  if (res.ok) {
2582
2599
  const results = await res.json();
2583
2600
  if (onTestResultsChange) {
@@ -2585,18 +2602,68 @@ function ProvidersPanel({
2585
2602
  } else {
2586
2603
  setInternalTestResults((prev) => ({ ...prev, [providerId]: results }));
2587
2604
  }
2605
+ } else {
2606
+ const errorResult = {
2607
+ anthropic: {
2608
+ nonStreaming: {
2609
+ success: false,
2610
+ error: { message: `HTTP ${res.status}: ${res.statusText}`, type: "server_error" }
2611
+ },
2612
+ streaming: { notConfigured: true }
2613
+ },
2614
+ openai: {
2615
+ nonStreaming: {
2616
+ success: false,
2617
+ error: { message: `HTTP ${res.status}: ${res.statusText}`, type: "server_error" }
2618
+ },
2619
+ streaming: { notConfigured: true }
2620
+ }
2621
+ };
2622
+ if (onTestResultsChange) {
2623
+ onTestResultsChange(providerId, errorResult);
2624
+ } else {
2625
+ setInternalTestResults((prev) => ({ ...prev, [providerId]: errorResult }));
2626
+ }
2627
+ }
2628
+ } catch (err) {
2629
+ const isAbort = err instanceof Error && err.name === "AbortError";
2630
+ if (isAbort) {
2631
+ const timeoutResult = {
2632
+ anthropic: {
2633
+ nonStreaming: {
2634
+ success: false,
2635
+ error: { message: "Request timed out", type: "timeout" }
2636
+ },
2637
+ streaming: { notConfigured: true }
2638
+ },
2639
+ openai: {
2640
+ nonStreaming: {
2641
+ success: false,
2642
+ error: { message: "Request timed out", type: "timeout" }
2643
+ },
2644
+ streaming: { notConfigured: true }
2645
+ }
2646
+ };
2647
+ if (onTestResultsChange) {
2648
+ onTestResultsChange(providerId, timeoutResult);
2649
+ } else {
2650
+ setInternalTestResults((prev) => ({ ...prev, [providerId]: timeoutResult }));
2651
+ }
2588
2652
  }
2589
2653
  } finally {
2590
2654
  clearInterval(intervalId);
2591
2655
  setTestingTimeLeft(providerId, void 0);
2592
- if (onTestingProvidersChange) {
2593
- onTestingProvidersChange(providerId, false);
2594
- } else {
2595
- setInternalTestingProviders((prev) => {
2596
- const next = new Set(prev);
2597
- next.delete(providerId);
2598
- return next;
2599
- });
2656
+ try {
2657
+ if (onTestingProvidersChange) {
2658
+ onTestingProvidersChange(providerId, false);
2659
+ } else {
2660
+ setInternalTestingProviders((prev) => {
2661
+ const next = new Set(prev);
2662
+ next.delete(providerId);
2663
+ return next;
2664
+ });
2665
+ }
2666
+ } catch {
2600
2667
  }
2601
2668
  }
2602
2669
  },
@@ -2669,6 +2736,65 @@ function ProvidersPanel({
2669
2736
  await fetchProviders();
2670
2737
  })();
2671
2738
  }
2739
+ const fileInputRef = reactExports.useRef(null);
2740
+ function handleExport(includeKeys) {
2741
+ const url = `/api/providers/export${""}`;
2742
+ void (async () => {
2743
+ try {
2744
+ const res = await fetch(url);
2745
+ if (!res.ok) {
2746
+ setError("Failed to export providers");
2747
+ return;
2748
+ }
2749
+ const blob = await res.blob();
2750
+ const downloadUrl = URL.createObjectURL(blob);
2751
+ const a = document.createElement("a");
2752
+ a.href = downloadUrl;
2753
+ a.download = res.headers.get("Content-Disposition")?.match(/filename="(.+)"/)?.[1] ?? "providers.json";
2754
+ document.body.appendChild(a);
2755
+ a.click();
2756
+ document.body.removeChild(a);
2757
+ URL.revokeObjectURL(downloadUrl);
2758
+ } catch {
2759
+ setError("Failed to export providers");
2760
+ }
2761
+ })();
2762
+ }
2763
+ function handleImportClick() {
2764
+ fileInputRef.current?.click();
2765
+ }
2766
+ function handleFileChange(e) {
2767
+ const file = e.target.files?.[0];
2768
+ if (!file) return;
2769
+ void (async () => {
2770
+ try {
2771
+ const text = await file.text();
2772
+ const res = await fetch("/api/providers/import", {
2773
+ method: "POST",
2774
+ headers: { "Content-Type": "application/json" },
2775
+ body: JSON.stringify(text)
2776
+ });
2777
+ const ImportResponseSchema = object({
2778
+ success: boolean().optional(),
2779
+ imported: number().optional(),
2780
+ message: string().optional(),
2781
+ errors: array(string()).optional()
2782
+ });
2783
+ const data = ImportResponseSchema.parse(await res.json());
2784
+ if (res.ok && data.imported !== void 0 && data.imported > 0) {
2785
+ await fetchProviders();
2786
+ setError(null);
2787
+ } else if (data.errors && data.errors.length > 0) {
2788
+ setError(data.errors.join("; "));
2789
+ } else {
2790
+ setError(data.message ?? "Import failed");
2791
+ }
2792
+ } catch {
2793
+ setError("Failed to import providers");
2794
+ }
2795
+ e.target.value = "";
2796
+ })();
2797
+ }
2672
2798
  if (isLoading && providers.length === 0) {
2673
2799
  return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center justify-center py-8", children: /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-sm text-muted-foreground", children: "Loading providers..." }) });
2674
2800
  }
@@ -2691,9 +2817,29 @@ function ProvidersPanel({
2691
2817
  return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-4", children: [
2692
2818
  /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center justify-between", children: [
2693
2819
  /* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-medium", children: "Providers" }),
2694
- /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { onClick: () => setShowForm(true), size: "sm", className: "gap-1", children: [
2695
- /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-4" }),
2696
- "Add Provider"
2820
+ /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
2821
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { variant: "outline", size: "sm", onClick: () => handleExport(), className: "gap-1", children: [
2822
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Download, { className: "size-3" }),
2823
+ "Export"
2824
+ ] }),
2825
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { variant: "outline", size: "sm", onClick: handleImportClick, className: "gap-1", children: [
2826
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Upload, { className: "size-3" }),
2827
+ "Import"
2828
+ ] }),
2829
+ /* @__PURE__ */ jsxRuntimeExports.jsx(
2830
+ "input",
2831
+ {
2832
+ type: "file",
2833
+ ref: fileInputRef,
2834
+ accept: ".json",
2835
+ onChange: handleFileChange,
2836
+ style: { display: "none" }
2837
+ }
2838
+ ),
2839
+ /* @__PURE__ */ jsxRuntimeExports.jsxs(Button, { onClick: () => setShowForm(true), size: "sm", className: "gap-1", children: [
2840
+ /* @__PURE__ */ jsxRuntimeExports.jsx(Plus, { className: "size-4" }),
2841
+ "Add Provider"
2842
+ ] })
2697
2843
  ] })
2698
2844
  ] }),
2699
2845
  configPath !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground bg-muted/30 rounded-md px-3 py-2", children: [
@@ -197,7 +197,7 @@ function getResponse() {
197
197
  return event.res;
198
198
  }
199
199
  async function getStartManifest(matchedRoutes) {
200
- const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-DgsS3z4y.mjs");
200
+ const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-BF6ge6dS.mjs");
201
201
  const startManifest = tsrStartManifest();
202
202
  const rootRoute = startManifest.routes[rootRouteId] = startManifest.routes[rootRouteId] || {};
203
203
  rootRoute.assets = rootRoute.assets || [];
@@ -766,7 +766,7 @@ let entriesPromise;
766
766
  let baseManifestPromise;
767
767
  let cachedFinalManifestPromise;
768
768
  async function loadEntries() {
769
- const routerEntry = await import("./router-CZSteFqT.mjs").then((n) => n.r);
769
+ const routerEntry = await import("./router-CsCLdrXq.mjs").then((n) => n.r);
770
770
  const startEntry = await import("./start-HYkvq4Ni.mjs");
771
771
  return { startEntry, routerEntry };
772
772
  }