@tonyclaw/llm-inspector 1.8.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.output/nitro.json +1 -1
- package/.output/public/assets/{index-DH3FOgcK.js → index-BmEH5jeO.js} +18 -18
- package/.output/public/assets/{index-BLVa7n9b.css → index-DdJSLfxK.css} +1 -1
- package/.output/public/assets/{main-Beo3LJDa.js → main-GVpFMVGE.js} +1 -1
- package/.output/server/_ssr/{index-HkueJ4Un.mjs → index-D0d6QxPt.mjs} +85 -31
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-DTswxb7l.mjs → router-D9uLXa9A.mjs} +287 -67
- package/.output/server/{_tanstack-start-manifest_v-DhUuivt-.mjs → _tanstack-start-manifest_v-ByfnNZV_.mjs} +1 -1
- package/.output/server/index.mjs +26 -26
- package/README.md +8 -209
- package/package.json +1 -1
- package/src/components/ProxyViewerContainer.tsx +10 -1
- package/src/components/providers/ProviderCard.tsx +19 -15
- package/src/components/providers/ProviderForm.tsx +21 -0
- package/src/components/proxy-viewer/LogEntry.tsx +7 -0
- package/src/components/proxy-viewer/ResponseView.tsx +32 -6
- package/src/components/proxy-viewer/StreamingChunkSequence.tsx +12 -3
- package/src/proxy/chunkStorage.ts +4 -6
- package/src/proxy/formats/anthropic/schemas.ts +9 -0
- package/src/proxy/formats/anthropic/stream.ts +11 -0
- package/src/proxy/formats/openai/stream.ts +15 -0
- package/src/proxy/handler.ts +44 -27
- package/src/proxy/logIndex.ts +73 -7
- package/src/proxy/logger.ts +62 -6
- package/src/proxy/providers.ts +5 -0
- package/src/proxy/schemas.ts +2 -0
- package/src/proxy/socketTracker.ts +90 -36
- package/src/proxy/store.ts +32 -18
- package/src/routes/api/providers.$providerId.ts +1 -0
- package/src/routes/api/providers.ts +2 -0
|
@@ -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-
|
|
2
|
+
import { C as CapturedLogSchema, a as parseRequest, p as parseOpenAIResponse, I as InspectorResponseSchema } from "./router-D9uLXa9A.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";
|
|
@@ -199,7 +199,7 @@ async function exportLogsAsZip(logs) {
|
|
|
199
199
|
document.body.removeChild(anchor);
|
|
200
200
|
URL.revokeObjectURL(url);
|
|
201
201
|
}
|
|
202
|
-
const version = "1.
|
|
202
|
+
const version = "1.9.1";
|
|
203
203
|
const packageJson = {
|
|
204
204
|
version
|
|
205
205
|
};
|
|
@@ -1509,22 +1509,37 @@ function ResponseView({
|
|
|
1509
1509
|
outputTokens,
|
|
1510
1510
|
cacheCreationInputTokens,
|
|
1511
1511
|
cacheReadInputTokens,
|
|
1512
|
-
apiFormat
|
|
1512
|
+
apiFormat,
|
|
1513
|
+
error
|
|
1513
1514
|
}) {
|
|
1514
|
-
if (responseText === null) {
|
|
1515
|
+
if (responseText === null && error === void 0) {
|
|
1515
1516
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2 py-3", children: [
|
|
1516
1517
|
/* @__PURE__ */ jsxRuntimeExports.jsx(StatusIndicator, { status: responseStatus }),
|
|
1517
1518
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-xs text-muted-foreground italic", children: "No response" })
|
|
1518
1519
|
] });
|
|
1519
1520
|
}
|
|
1520
|
-
const
|
|
1521
|
-
if (
|
|
1521
|
+
const isHttpError = responseStatus !== null && responseStatus >= 400;
|
|
1522
|
+
if (isHttpError) {
|
|
1522
1523
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1523
1524
|
/* @__PURE__ */ jsxRuntimeExports.jsx(StatusIndicator, { status: responseStatus }),
|
|
1524
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(ErrorResponseView, { text: responseText })
|
|
1525
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ErrorResponseView, { text: responseText ?? "" }),
|
|
1526
|
+
error !== void 0 && error !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded border border-destructive/50 bg-destructive/10 p-3 text-xs", children: [
|
|
1527
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-semibold text-destructive mb-1", children: "SSE Error" }),
|
|
1528
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground font-mono", children: error })
|
|
1529
|
+
] })
|
|
1530
|
+
] });
|
|
1531
|
+
}
|
|
1532
|
+
if (error !== void 0 && error !== null) {
|
|
1533
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1534
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(StatusIndicator, { status: responseStatus }),
|
|
1535
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded border border-destructive/50 bg-destructive/10 p-3 text-xs", children: [
|
|
1536
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-semibold text-destructive mb-1", children: "SSE Error" }),
|
|
1537
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground font-mono", children: error })
|
|
1538
|
+
] }),
|
|
1539
|
+
responseText !== null && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorResponseView, { text: responseText }) })
|
|
1525
1540
|
] });
|
|
1526
1541
|
}
|
|
1527
|
-
const parsed = parseResponse(responseText, apiFormat);
|
|
1542
|
+
const parsed = responseText !== null ? parseResponse(responseText, apiFormat) : null;
|
|
1528
1543
|
if (parsed !== null) {
|
|
1529
1544
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
1530
1545
|
/* @__PURE__ */ jsxRuntimeExports.jsx(StatusIndicator, { status: responseStatus }),
|
|
@@ -1553,7 +1568,7 @@ function ResponseView({
|
|
|
1553
1568
|
] })
|
|
1554
1569
|
] })
|
|
1555
1570
|
] }),
|
|
1556
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(MarkdownFallbackView, { text: responseText })
|
|
1571
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(MarkdownFallbackView, { text: responseText ?? "" })
|
|
1557
1572
|
] });
|
|
1558
1573
|
}
|
|
1559
1574
|
const ReplayResultSchema = object({
|
|
@@ -1711,6 +1726,7 @@ function StreamingChunkSequence({
|
|
|
1711
1726
|
const [expandedIndices, setExpandedIndices] = reactExports.useState(/* @__PURE__ */ new Set());
|
|
1712
1727
|
reactExports.useEffect(() => {
|
|
1713
1728
|
if (!containerExpanded || chunkState.status !== "idle") return;
|
|
1729
|
+
let cancelled = false;
|
|
1714
1730
|
setChunkState({ status: "loading" });
|
|
1715
1731
|
fetch(`/api/logs/${logId}/chunks`).then((res) => {
|
|
1716
1732
|
if (!res.ok) {
|
|
@@ -1718,11 +1734,18 @@ function StreamingChunkSequence({
|
|
|
1718
1734
|
}
|
|
1719
1735
|
return res.json();
|
|
1720
1736
|
}).then((data) => {
|
|
1721
|
-
|
|
1737
|
+
if (!cancelled) {
|
|
1738
|
+
setChunkState({ status: "success", chunks: data.chunks });
|
|
1739
|
+
}
|
|
1722
1740
|
}).catch(() => {
|
|
1723
|
-
|
|
1741
|
+
if (!cancelled) {
|
|
1742
|
+
setChunkState({ status: "error", message: "Chunk data unavailable" });
|
|
1743
|
+
}
|
|
1724
1744
|
});
|
|
1725
|
-
|
|
1745
|
+
return () => {
|
|
1746
|
+
cancelled = true;
|
|
1747
|
+
};
|
|
1748
|
+
}, [containerExpanded, logId]);
|
|
1726
1749
|
const groups = reactExports.useMemo(() => {
|
|
1727
1750
|
if (chunkState.status !== "success") return [];
|
|
1728
1751
|
const map = /* @__PURE__ */ new Map();
|
|
@@ -1936,6 +1959,10 @@ const LogEntry = reactExports.memo(function LogEntry2({
|
|
|
1936
1959
|
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "text-muted-foreground truncate", title: value, children: value })
|
|
1937
1960
|
] }, key)) }) : /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground italic", children: "No raw headers captured" }) }) }),
|
|
1938
1961
|
/* @__PURE__ */ jsxRuntimeExports.jsx(TabsContent, { value: "raw", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "px-4 py-3 space-y-3", children: [
|
|
1962
|
+
log.error !== void 0 && log.error !== null && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "rounded border border-destructive/50 bg-destructive/10 p-3 text-xs", children: [
|
|
1963
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "font-semibold text-destructive mb-1", children: "SSE Error" }),
|
|
1964
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "text-muted-foreground font-mono", children: log.error })
|
|
1965
|
+
] }),
|
|
1939
1966
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
1940
1967
|
CopyButton,
|
|
1941
1968
|
{
|
|
@@ -1964,7 +1991,8 @@ const LogEntry = reactExports.memo(function LogEntry2({
|
|
|
1964
1991
|
outputTokens: log.outputTokens,
|
|
1965
1992
|
cacheCreationInputTokens: log.cacheCreationInputTokens,
|
|
1966
1993
|
cacheReadInputTokens: log.cacheReadInputTokens,
|
|
1967
|
-
apiFormat: log.apiFormat
|
|
1994
|
+
apiFormat: log.apiFormat,
|
|
1995
|
+
error: log.error
|
|
1968
1996
|
}
|
|
1969
1997
|
) }) })
|
|
1970
1998
|
] }) })
|
|
@@ -2207,25 +2235,25 @@ function ProviderCard({
|
|
|
2207
2235
|
onTest
|
|
2208
2236
|
}) {
|
|
2209
2237
|
const [showApiKey, setShowApiKey] = reactExports.useState(false);
|
|
2238
|
+
const docsUrl = provider.apiDocsUrl ?? Object.entries(KNOWN_PROVIDER_DOCS).find(
|
|
2239
|
+
([keyword]) => provider.name.toLowerCase().includes(keyword)
|
|
2240
|
+
)?.[1];
|
|
2210
2241
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "border rounded-lg p-4 flex flex-col gap-3 bg-card", children: [
|
|
2211
2242
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
2212
2243
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "flex items-center gap-2 min-w-0", children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "font-medium truncate", children: provider.model !== void 0 && provider.model !== "" ? `${provider.model} (${provider.name})` : provider.name }) }),
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
},
|
|
2227
|
-
keyword
|
|
2228
|
-
) : null
|
|
2244
|
+
docsUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
2245
|
+
"a",
|
|
2246
|
+
{
|
|
2247
|
+
href: docsUrl,
|
|
2248
|
+
target: "_blank",
|
|
2249
|
+
rel: "noopener noreferrer",
|
|
2250
|
+
className: "text-muted-foreground hover:text-foreground transition-colors flex items-center gap-1 text-xs",
|
|
2251
|
+
title: "View API documentation",
|
|
2252
|
+
children: [
|
|
2253
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ExternalLink, { className: "size-3" }),
|
|
2254
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "sr-only", children: "Docs" })
|
|
2255
|
+
]
|
|
2256
|
+
}
|
|
2229
2257
|
)
|
|
2230
2258
|
] }),
|
|
2231
2259
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
@@ -2331,6 +2359,7 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
2331
2359
|
const [model, setModel] = reactExports.useState(provider?.model ?? "");
|
|
2332
2360
|
const [format, setFormat] = reactExports.useState(provider?.format ?? "anthropic");
|
|
2333
2361
|
const [baseUrl, setBaseUrl] = reactExports.useState(provider?.baseUrl ?? "");
|
|
2362
|
+
const [apiDocsUrl, setApiDocsUrl] = reactExports.useState(provider?.apiDocsUrl ?? "");
|
|
2334
2363
|
const [errors, setErrors] = reactExports.useState({});
|
|
2335
2364
|
const [isSubmitting, setIsSubmitting] = reactExports.useState(false);
|
|
2336
2365
|
const [manualBaseUrlOverride, setManualBaseUrlOverride] = reactExports.useState(false);
|
|
@@ -2343,6 +2372,7 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
2343
2372
|
setModel(provider.model ?? "");
|
|
2344
2373
|
setFormat(provider.format ?? "anthropic");
|
|
2345
2374
|
setBaseUrl(provider.baseUrl ?? "");
|
|
2375
|
+
setApiDocsUrl(provider.apiDocsUrl ?? "");
|
|
2346
2376
|
setManualBaseUrlOverride(false);
|
|
2347
2377
|
}
|
|
2348
2378
|
}, [provider]);
|
|
@@ -2401,7 +2431,8 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
2401
2431
|
apiKey: apiKey.trim(),
|
|
2402
2432
|
model: model.trim() || void 0,
|
|
2403
2433
|
format,
|
|
2404
|
-
baseUrl: baseUrl.trim() || void 0
|
|
2434
|
+
baseUrl: baseUrl.trim() || void 0,
|
|
2435
|
+
apiDocsUrl: apiDocsUrl.trim() || void 0
|
|
2405
2436
|
});
|
|
2406
2437
|
} finally {
|
|
2407
2438
|
setIsSubmitting(false);
|
|
@@ -2513,6 +2544,21 @@ function ProviderForm({ provider, onSubmit, onCancel }) {
|
|
|
2513
2544
|
errors.baseUrl !== void 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-destructive", children: errors.baseUrl }),
|
|
2514
2545
|
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Base URL for the provider API." })
|
|
2515
2546
|
] }),
|
|
2547
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "space-y-2", children: [
|
|
2548
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("label", { htmlFor: "provider-api-docs-url", className: "text-sm font-medium", children: "API Docs URL" }),
|
|
2549
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
2550
|
+
"input",
|
|
2551
|
+
{
|
|
2552
|
+
id: "provider-api-docs-url",
|
|
2553
|
+
type: "text",
|
|
2554
|
+
value: apiDocsUrl,
|
|
2555
|
+
onChange: (e) => setApiDocsUrl(e.target.value),
|
|
2556
|
+
placeholder: "https://api.example.com/docs",
|
|
2557
|
+
className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:border-ring focus-visible:outline-ring focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50"
|
|
2558
|
+
}
|
|
2559
|
+
),
|
|
2560
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { className: "text-xs text-muted-foreground", children: "Optional API documentation URL. If not set, uses known provider docs." })
|
|
2561
|
+
] }),
|
|
2516
2562
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex gap-2 justify-end pt-2", children: [
|
|
2517
2563
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Button, { type: "button", variant: "outline", onClick: onCancel, disabled: isSubmitting, children: "Cancel" }),
|
|
2518
2564
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Button, { type: "submit", disabled: isSubmitting, children: isSubmitting ? "Saving..." : provider ? "Update Provider" : "Add Provider" })
|
|
@@ -3291,6 +3337,7 @@ function ProxyViewerContainer() {
|
|
|
3291
3337
|
const [viewMode, setViewMode] = reactExports.useState("simple");
|
|
3292
3338
|
const [error, setError] = reactExports.useState(null);
|
|
3293
3339
|
const eventSourceRef = reactExports.useRef(null);
|
|
3340
|
+
const reconnectTimeoutRef = reactExports.useRef(null);
|
|
3294
3341
|
const fetchSessionsAndModels = reactExports.useCallback(async () => {
|
|
3295
3342
|
try {
|
|
3296
3343
|
const [sessionsRes, modelsRes] = await Promise.all([
|
|
@@ -3365,7 +3412,10 @@ function ProxyViewerContainer() {
|
|
|
3365
3412
|
es.onerror = () => {
|
|
3366
3413
|
setError("SSE connection lost, reconnecting...");
|
|
3367
3414
|
es.close();
|
|
3368
|
-
|
|
3415
|
+
if (reconnectTimeoutRef.current !== null) {
|
|
3416
|
+
clearTimeout(reconnectTimeoutRef.current);
|
|
3417
|
+
}
|
|
3418
|
+
reconnectTimeoutRef.current = setTimeout(connectSSE, 3e3);
|
|
3369
3419
|
};
|
|
3370
3420
|
void fetchSessionsAndModels();
|
|
3371
3421
|
}, [selectedSession, selectedModel, fetchSessionsAndModels]);
|
|
@@ -3376,6 +3426,10 @@ function ProxyViewerContainer() {
|
|
|
3376
3426
|
eventSourceRef.current.close();
|
|
3377
3427
|
eventSourceRef.current = null;
|
|
3378
3428
|
}
|
|
3429
|
+
if (reconnectTimeoutRef.current !== null) {
|
|
3430
|
+
clearTimeout(reconnectTimeoutRef.current);
|
|
3431
|
+
reconnectTimeoutRef.current = null;
|
|
3432
|
+
}
|
|
3379
3433
|
};
|
|
3380
3434
|
}, [connectSSE]);
|
|
3381
3435
|
const handleClearAll = reactExports.useCallback(() => {
|
|
@@ -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-
|
|
200
|
+
const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-ByfnNZV_.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-
|
|
769
|
+
const routerEntry = await import("./router-D9uLXa9A.mjs").then((n) => n.r);
|
|
770
770
|
const startEntry = await import("./start-HYkvq4Ni.mjs");
|
|
771
771
|
return { startEntry, routerEntry };
|
|
772
772
|
}
|