@tonyclaw/llm-inspector 1.11.4 → 1.11.6
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-BGxRftQy.css +1 -0
- package/.output/public/assets/{index-CjjXIYIt.js → index-BHjxAEHw.js} +14 -14
- package/.output/public/assets/{index-Do7wdaYZ.js → index-BLz26_QE.js} +1 -1
- package/.output/server/_ssr/{index-BWMVJy33.mjs → index-BzG8P6B8.mjs} +52 -9
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-DRA0j7Zv.mjs → router-BOZDxiEC.mjs} +99 -159
- package/.output/server/{_tanstack-start-manifest_v-DKfW4gRz.mjs → _tanstack-start-manifest_v-C_jssHYZ.mjs} +1 -1
- package/.output/server/index.mjs +24 -24
- package/package.json +1 -1
- package/src/components/ProxyViewer.tsx +5 -3
- package/src/components/providers/ProviderCard.tsx +57 -1
- package/src/components/providers/ProvidersPanel.tsx +29 -0
- package/src/proxy/formats/registry.ts +2 -6
- package/src/proxy/handler.ts +25 -119
- package/src/proxy/providers.ts +22 -39
- package/src/proxy/upstream.ts +90 -0
- package/src/routes/api/logs.$id.replay.ts +17 -90
- package/.output/public/assets/index-BpKPXEcb.css +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { r as reactExports, j as jsxRuntimeExports, R as React } from "../_libs/react.mjs";
|
|
2
|
-
import { C as CapturedLogSchema, R as RuntimeConfigSchema, a as parseRequest, s as stripClaudeCodeBillingHeader, p as parseOpenAIResponse, I as InspectorResponseSchema } from "./router-
|
|
2
|
+
import { C as CapturedLogSchema, R as RuntimeConfigSchema, a as parseRequest, s as stripClaudeCodeBillingHeader, p as parseOpenAIResponse, I as InspectorResponseSchema } from "./router-BOZDxiEC.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 { J as JSZip } from "../_libs/jszip.mjs";
|
|
@@ -255,7 +255,7 @@ async function exportLogsAsZip(logs) {
|
|
|
255
255
|
document.body.removeChild(anchor);
|
|
256
256
|
URL.revokeObjectURL(url);
|
|
257
257
|
}
|
|
258
|
-
const version = "1.11.
|
|
258
|
+
const version = "1.11.6";
|
|
259
259
|
const packageJson = {
|
|
260
260
|
version
|
|
261
261
|
};
|
|
@@ -2799,9 +2799,50 @@ function TestStatus({ result }) {
|
|
|
2799
2799
|
] });
|
|
2800
2800
|
}
|
|
2801
2801
|
if (result.success) {
|
|
2802
|
+
const tokenParts = [];
|
|
2803
|
+
if (result.inputTokens !== void 0) {
|
|
2804
|
+
tokenParts.push(/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
|
|
2805
|
+
result.inputTokens,
|
|
2806
|
+
" in"
|
|
2807
|
+
] }, "in"));
|
|
2808
|
+
}
|
|
2809
|
+
if (result.outputTokens !== void 0) {
|
|
2810
|
+
tokenParts.push(/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { children: [
|
|
2811
|
+
result.outputTokens,
|
|
2812
|
+
" out"
|
|
2813
|
+
] }, "out"));
|
|
2814
|
+
}
|
|
2815
|
+
if (result.cacheCreationInputTokens !== void 0 && result.cacheCreationInputTokens > 0) {
|
|
2816
|
+
tokenParts.push(
|
|
2817
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-emerald-400", children: [
|
|
2818
|
+
"+",
|
|
2819
|
+
result.cacheCreationInputTokens,
|
|
2820
|
+
" cache"
|
|
2821
|
+
] }, "cache-create")
|
|
2822
|
+
);
|
|
2823
|
+
}
|
|
2824
|
+
if (result.cacheReadInputTokens !== void 0 && result.cacheReadInputTokens > 0) {
|
|
2825
|
+
tokenParts.push(
|
|
2826
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "font-mono tabular-nums text-purple-400", children: [
|
|
2827
|
+
"~",
|
|
2828
|
+
result.cacheReadInputTokens,
|
|
2829
|
+
" cached"
|
|
2830
|
+
] }, "cache-read")
|
|
2831
|
+
);
|
|
2832
|
+
}
|
|
2833
|
+
const displayTokens = [];
|
|
2834
|
+
for (let i = 0; i < tokenParts.length; i++) {
|
|
2835
|
+
if (i > 0) displayTokens.push(", ");
|
|
2836
|
+
displayTokens.push(tokenParts[i]);
|
|
2837
|
+
}
|
|
2802
2838
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-1 text-xs text-green-600 shrink-0", children: [
|
|
2803
2839
|
/* @__PURE__ */ jsxRuntimeExports.jsx(CircleCheckBig, { className: "size-3" }),
|
|
2804
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Connected" })
|
|
2840
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: "Connected" }),
|
|
2841
|
+
tokenParts.length > 0 && /* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-muted-foreground", children: [
|
|
2842
|
+
"(",
|
|
2843
|
+
displayTokens,
|
|
2844
|
+
")"
|
|
2845
|
+
] })
|
|
2805
2846
|
] });
|
|
2806
2847
|
}
|
|
2807
2848
|
const error = result.error;
|
|
@@ -3835,12 +3876,14 @@ function ProxyViewer({
|
|
|
3835
3876
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "max-w-[1200px] mx-auto flex flex-col h-screen", style: { maxHeight: "100vh" }, children: [
|
|
3836
3877
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center gap-4 mb-4 px-6 pt-6", children: [
|
|
3837
3878
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("h1", { className: "text-lg font-bold flex-1 flex items-center gap-2", children: [
|
|
3838
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "size-10 text-amber-500" }),
|
|
3839
|
-
"
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3879
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(CrabLogo, { className: "size-10 text-amber-500 self-center" }),
|
|
3880
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "flex items-baseline gap-2", children: [
|
|
3881
|
+
"LLM Inspector",
|
|
3882
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("span", { className: "text-xs text-muted-foreground font-mono", children: [
|
|
3883
|
+
"v",
|
|
3884
|
+
packageJson.version
|
|
3885
|
+
] })
|
|
3886
|
+
] })
|
|
3844
3887
|
] }),
|
|
3845
3888
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "flex items-center border border-border rounded-md overflow-hidden", children: [
|
|
3846
3889
|
/* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -85,7 +85,7 @@ function getResponse() {
|
|
|
85
85
|
}
|
|
86
86
|
var HEADERS = { TSS_SHELL: "X-TSS_SHELL" };
|
|
87
87
|
async function getStartManifest(matchedRoutes) {
|
|
88
|
-
const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-
|
|
88
|
+
const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-C_jssHYZ.mjs");
|
|
89
89
|
const startManifest = tsrStartManifest();
|
|
90
90
|
let routes = startManifest.routes;
|
|
91
91
|
routes[rootRouteId];
|
|
@@ -1157,7 +1157,7 @@ var getBaseManifest = getProdBaseManifest;
|
|
|
1157
1157
|
var createEarlyHintsForRequest = createEarlyHintsCollector;
|
|
1158
1158
|
async function loadEntries() {
|
|
1159
1159
|
const [routerEntry, startEntry, pluginAdapters] = await Promise.all([
|
|
1160
|
-
import("./router-
|
|
1160
|
+
import("./router-BOZDxiEC.mjs").then((n) => n.r),
|
|
1161
1161
|
import("./start-HYkvq4Ni.mjs"),
|
|
1162
1162
|
import("./empty-plugin-adapters-BFgPZ6_d.mjs")
|
|
1163
1163
|
]);
|
|
@@ -42,7 +42,7 @@ import "../_libs/debounce-fn.mjs";
|
|
|
42
42
|
import "../_libs/mimic-function.mjs";
|
|
43
43
|
import "../_libs/semver.mjs";
|
|
44
44
|
import "../_libs/uint8array-extras.mjs";
|
|
45
|
-
const appCss = "/assets/index-
|
|
45
|
+
const appCss = "/assets/index-BGxRftQy.css";
|
|
46
46
|
const Route$h = createRootRoute({
|
|
47
47
|
head: () => ({
|
|
48
48
|
meta: [
|
|
@@ -66,7 +66,7 @@ function RootDocument({ children }) {
|
|
|
66
66
|
] })
|
|
67
67
|
] });
|
|
68
68
|
}
|
|
69
|
-
const $$splitComponentImporter = () => import("./index-
|
|
69
|
+
const $$splitComponentImporter = () => import("./index-BzG8P6B8.mjs");
|
|
70
70
|
const Route$g = createFileRoute("/")({
|
|
71
71
|
component: lazyRouteComponent($$splitComponentImporter, "component")
|
|
72
72
|
});
|
|
@@ -1031,11 +1031,8 @@ class FormatRegistryImpl {
|
|
|
1031
1031
|
/** Get handler matching a request path */
|
|
1032
1032
|
getByPath(path2) {
|
|
1033
1033
|
const messagesPath = path2.split("?")[0] ?? "";
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
return this.handlers.get("openai");
|
|
1037
|
-
}
|
|
1038
|
-
return void 0;
|
|
1034
|
+
const format = this.pathMap.get(messagesPath);
|
|
1035
|
+
return format === void 0 ? void 0 : this.handlers.get(format);
|
|
1039
1036
|
}
|
|
1040
1037
|
/** Detect format from request body content */
|
|
1041
1038
|
detectFormat(rawBody) {
|
|
@@ -1652,38 +1649,13 @@ const store = new Conf({
|
|
|
1652
1649
|
});
|
|
1653
1650
|
migrateFromLegacyConfLocation(store);
|
|
1654
1651
|
function migrateProvider(p) {
|
|
1655
|
-
|
|
1656
|
-
const currentOpenaiUrl = p.openaiBaseUrl ?? "";
|
|
1657
|
-
if (currentAnthropicUrl !== "" || currentOpenaiUrl !== "") {
|
|
1652
|
+
if (p.anthropicBaseUrl !== void 0 || p.openaiBaseUrl !== void 0) {
|
|
1658
1653
|
return p;
|
|
1659
1654
|
}
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
if (currentAnthropicUrl !== "" && currentOpenaiUrl !== "") {
|
|
1663
|
-
format = p.format ?? "anthropic";
|
|
1664
|
-
baseUrl = p.baseUrl !== void 0 && p.baseUrl !== "" ? p.baseUrl : currentAnthropicUrl;
|
|
1665
|
-
} else if (currentOpenaiUrl !== "") {
|
|
1666
|
-
format = "openai";
|
|
1667
|
-
baseUrl = currentOpenaiUrl;
|
|
1668
|
-
} else if (currentAnthropicUrl !== "") {
|
|
1669
|
-
format = "anthropic";
|
|
1670
|
-
baseUrl = currentAnthropicUrl;
|
|
1671
|
-
} else if (p.format !== void 0 && p.baseUrl !== void 0 && p.baseUrl !== "") {
|
|
1672
|
-
format = p.format;
|
|
1673
|
-
baseUrl = p.baseUrl;
|
|
1674
|
-
if (format === "openai") {
|
|
1675
|
-
return { ...p, format, baseUrl, anthropicBaseUrl: "", openaiBaseUrl: p.baseUrl };
|
|
1676
|
-
} else {
|
|
1677
|
-
return { ...p, format, baseUrl, anthropicBaseUrl: p.baseUrl, openaiBaseUrl: "" };
|
|
1678
|
-
}
|
|
1655
|
+
if (p.format === void 0 || p.baseUrl === void 0 || p.baseUrl === "") {
|
|
1656
|
+
return p;
|
|
1679
1657
|
}
|
|
1680
|
-
return {
|
|
1681
|
-
...p,
|
|
1682
|
-
format,
|
|
1683
|
-
baseUrl,
|
|
1684
|
-
anthropicBaseUrl: currentAnthropicUrl,
|
|
1685
|
-
openaiBaseUrl: currentOpenaiUrl
|
|
1686
|
-
};
|
|
1658
|
+
return p.format === "openai" ? { ...p, anthropicBaseUrl: "", openaiBaseUrl: p.baseUrl } : { ...p, anthropicBaseUrl: p.baseUrl, openaiBaseUrl: "" };
|
|
1687
1659
|
}
|
|
1688
1660
|
function migrateProviders() {
|
|
1689
1661
|
const providers = store.get("providers", []);
|
|
@@ -1856,6 +1828,13 @@ function findProviderByModel(model) {
|
|
|
1856
1828
|
if (provider.model !== void 0 && provider.model !== "" && modelNormalized === normalizeModelName(provider.model)) {
|
|
1857
1829
|
return provider;
|
|
1858
1830
|
}
|
|
1831
|
+
if (provider.model !== void 0 && provider.model !== "") {
|
|
1832
|
+
const modelPart = modelNormalized.replace(normalizeModelName(provider.name), "");
|
|
1833
|
+
const concatenated = normalizeModelName(provider.name) + modelPart;
|
|
1834
|
+
if (modelNormalized === concatenated) {
|
|
1835
|
+
return provider;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1859
1838
|
}
|
|
1860
1839
|
return null;
|
|
1861
1840
|
}
|
|
@@ -2130,6 +2109,52 @@ function stripClaudeCodeBillingHeader(rawBody) {
|
|
|
2130
2109
|
}
|
|
2131
2110
|
return { body: JSON.stringify(parsed), removed };
|
|
2132
2111
|
}
|
|
2112
|
+
function describeApiRoute(apiPath) {
|
|
2113
|
+
const endpointPath = apiPath.split("?")[0] ?? "";
|
|
2114
|
+
const isChatCompletions = endpointPath === PATH_CHAT_COMPLETIONS || endpointPath === PATH_V1_CHAT_COMPLETIONS;
|
|
2115
|
+
const normalizedPath = isChatCompletions && !apiPath.startsWith("/v1/") ? `/v1${apiPath}` : apiPath;
|
|
2116
|
+
return { apiPath, endpointPath, isChatCompletions, normalizedPath };
|
|
2117
|
+
}
|
|
2118
|
+
function getProxyApiPath(url) {
|
|
2119
|
+
return url.pathname.replace(/^\/proxy/, "") + url.search;
|
|
2120
|
+
}
|
|
2121
|
+
function selectUpstreamBase(route, provider) {
|
|
2122
|
+
if (provider === null) {
|
|
2123
|
+
return route.isChatCompletions ? DEFAULT_OPENAI_UPSTREAM$1 : DEFAULT_UPSTREAM$1;
|
|
2124
|
+
}
|
|
2125
|
+
const formatSpecificUrl = route.isChatCompletions ? provider.openaiBaseUrl : provider.anthropicBaseUrl;
|
|
2126
|
+
if (formatSpecificUrl !== void 0 && formatSpecificUrl !== "") {
|
|
2127
|
+
return formatSpecificUrl;
|
|
2128
|
+
}
|
|
2129
|
+
if (provider.baseUrl !== void 0 && provider.baseUrl !== "") {
|
|
2130
|
+
return provider.baseUrl;
|
|
2131
|
+
}
|
|
2132
|
+
return provider.format === "openai" ? DEFAULT_OPENAI_UPSTREAM$1 : DEFAULT_UPSTREAM$1;
|
|
2133
|
+
}
|
|
2134
|
+
function buildUpstreamUrl(upstreamBase, normalizedPath) {
|
|
2135
|
+
const base = upstreamBase.endsWith("/") ? upstreamBase.slice(0, -1) : upstreamBase;
|
|
2136
|
+
if (base.endsWith("/v1") && normalizedPath.startsWith("/v1/")) {
|
|
2137
|
+
return base + normalizedPath.slice(3);
|
|
2138
|
+
}
|
|
2139
|
+
return base + normalizedPath;
|
|
2140
|
+
}
|
|
2141
|
+
function setUpstreamHost(headers, upstreamBase) {
|
|
2142
|
+
try {
|
|
2143
|
+
headers.set(HEADER_HOST, new URL(upstreamBase).host);
|
|
2144
|
+
} catch {
|
|
2145
|
+
headers.set(HEADER_HOST, "api.anthropic.com");
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
function injectProviderAuth(headers, provider) {
|
|
2149
|
+
if (provider === null) return;
|
|
2150
|
+
const apiKey = provider.apiKey.replace(/^Bearer\s+/i, "").trim();
|
|
2151
|
+
if (provider.authHeader === AUTH_HEADER_X_API_KEY) {
|
|
2152
|
+
headers.set(HEADER_X_API_KEY, apiKey);
|
|
2153
|
+
headers.delete(HEADER_AUTHORIZATION);
|
|
2154
|
+
return;
|
|
2155
|
+
}
|
|
2156
|
+
headers.set(HEADER_AUTHORIZATION, `Bearer ${apiKey}`);
|
|
2157
|
+
}
|
|
2133
2158
|
function buildProxyHeaders(originalHeaders) {
|
|
2134
2159
|
const rawHeaders = {};
|
|
2135
2160
|
const headers = new Headers();
|
|
@@ -2146,14 +2171,6 @@ function buildProxyHeaders(originalHeaders) {
|
|
|
2146
2171
|
}
|
|
2147
2172
|
return { headers, rawHeaders };
|
|
2148
2173
|
}
|
|
2149
|
-
function getHostFromUrl$1(urlStr) {
|
|
2150
|
-
try {
|
|
2151
|
-
const url = new URL(urlStr);
|
|
2152
|
-
return url.host;
|
|
2153
|
-
} catch {
|
|
2154
|
-
return "api.anthropic.com";
|
|
2155
|
-
}
|
|
2156
|
-
}
|
|
2157
2174
|
function buildFileLogEntry(log, upstreamUrl) {
|
|
2158
2175
|
return {
|
|
2159
2176
|
timestamp: log.timestamp,
|
|
@@ -2182,55 +2199,14 @@ function buildFileLogEntry(log, upstreamUrl) {
|
|
|
2182
2199
|
};
|
|
2183
2200
|
}
|
|
2184
2201
|
function parseRequestPath(req, url) {
|
|
2185
|
-
const
|
|
2186
|
-
const
|
|
2187
|
-
const isChatCompletionsV1 = messagesPath === PATH_V1_CHAT_COMPLETIONS;
|
|
2188
|
-
const isChatCompletions = messagesPath === PATH_CHAT_COMPLETIONS || isChatCompletionsV1;
|
|
2189
|
-
const isMessages = req.method === "POST" && (messagesPath === PATH_V1_MESSAGES || messagesPath === PATH_V1_CHAT_COMPLETIONS || messagesPath === PATH_CHAT_COMPLETIONS);
|
|
2190
|
-
const normalizedPath = isChatCompletions && !apiPath.startsWith("/v1/") ? "/v1" + apiPath : apiPath;
|
|
2202
|
+
const route = describeApiRoute(getProxyApiPath(url));
|
|
2203
|
+
const isMessages = req.method === "POST" && (route.endpointPath === PATH_V1_MESSAGES || route.isChatCompletions);
|
|
2191
2204
|
return {
|
|
2192
|
-
apiPath,
|
|
2193
|
-
messagesPath,
|
|
2194
|
-
isChatCompletionsV1,
|
|
2195
|
-
isChatCompletions,
|
|
2205
|
+
apiPath: route.apiPath,
|
|
2196
2206
|
isMessages,
|
|
2197
|
-
normalizedPath
|
|
2207
|
+
normalizedPath: route.normalizedPath
|
|
2198
2208
|
};
|
|
2199
2209
|
}
|
|
2200
|
-
function buildUpstreamUrl$1(upstreamBase, normalizedPath) {
|
|
2201
|
-
const base = upstreamBase.endsWith("/") ? upstreamBase.slice(0, -1) : upstreamBase;
|
|
2202
|
-
if (base.endsWith("/v1") && normalizedPath.startsWith("/v1/")) {
|
|
2203
|
-
return base + normalizedPath.slice(3);
|
|
2204
|
-
}
|
|
2205
|
-
return base + normalizedPath;
|
|
2206
|
-
}
|
|
2207
|
-
function selectUpstreamBase$1(isChatCompletions, matchedProviderConfig) {
|
|
2208
|
-
let upstreamBase;
|
|
2209
|
-
if (matchedProviderConfig) {
|
|
2210
|
-
if (isChatCompletions && matchedProviderConfig.openaiBaseUrl !== void 0 && matchedProviderConfig.openaiBaseUrl !== "") {
|
|
2211
|
-
upstreamBase = matchedProviderConfig.openaiBaseUrl;
|
|
2212
|
-
} else if (!isChatCompletions && matchedProviderConfig.anthropicBaseUrl !== void 0 && matchedProviderConfig.anthropicBaseUrl !== "") {
|
|
2213
|
-
upstreamBase = matchedProviderConfig.anthropicBaseUrl;
|
|
2214
|
-
} else if (matchedProviderConfig.baseUrl !== void 0 && matchedProviderConfig.baseUrl !== "") {
|
|
2215
|
-
upstreamBase = matchedProviderConfig.baseUrl;
|
|
2216
|
-
} else {
|
|
2217
|
-
upstreamBase = matchedProviderConfig.format === "openai" ? DEFAULT_OPENAI_UPSTREAM$1 : DEFAULT_UPSTREAM$1;
|
|
2218
|
-
}
|
|
2219
|
-
} else {
|
|
2220
|
-
upstreamBase = isChatCompletions ? DEFAULT_OPENAI_UPSTREAM$1 : DEFAULT_UPSTREAM$1;
|
|
2221
|
-
}
|
|
2222
|
-
return upstreamBase;
|
|
2223
|
-
}
|
|
2224
|
-
function injectAuthHeaders$1(upstreamHeaders, matchedProviderConfig) {
|
|
2225
|
-
if (!matchedProviderConfig) return;
|
|
2226
|
-
const apiKey = matchedProviderConfig.apiKey.replace(/^Bearer\s+/i, "").trim();
|
|
2227
|
-
if (matchedProviderConfig.authHeader === AUTH_HEADER_X_API_KEY) {
|
|
2228
|
-
upstreamHeaders.set(HEADER_X_API_KEY, apiKey);
|
|
2229
|
-
upstreamHeaders.delete(HEADER_AUTHORIZATION);
|
|
2230
|
-
} else {
|
|
2231
|
-
upstreamHeaders.set(HEADER_AUTHORIZATION, `Bearer ${apiKey}`);
|
|
2232
|
-
}
|
|
2233
|
-
}
|
|
2234
2210
|
function handleNonStreamingResponse(upstreamRes, responseBody, startTime, formatHandler, upstreamUrl, log) {
|
|
2235
2211
|
const elapsedMs = Date.now() - startTime;
|
|
2236
2212
|
const tokens = formatHandler.extractTokens(responseBody);
|
|
@@ -2329,26 +2305,19 @@ async function handleProxy(req) {
|
|
|
2329
2305
|
}
|
|
2330
2306
|
const { model, sessionId } = extractRequestMetadata(requestBody, req.headers);
|
|
2331
2307
|
const matchedProviderConfig = model !== null ? findProviderByModel(model) : null;
|
|
2332
|
-
const
|
|
2333
|
-
const
|
|
2334
|
-
const
|
|
2308
|
+
const route = describeApiRoute(parsed.apiPath);
|
|
2309
|
+
const upstreamBase = selectUpstreamBase(route, matchedProviderConfig);
|
|
2310
|
+
const upstreamUrl = buildUpstreamUrl(upstreamBase, parsed.normalizedPath);
|
|
2335
2311
|
const startTime = Date.now();
|
|
2336
2312
|
const { headers: upstreamHeaders, rawHeaders } = buildProxyHeaders(req.headers);
|
|
2337
|
-
upstreamHeaders
|
|
2338
|
-
|
|
2313
|
+
setUpstreamHost(upstreamHeaders, upstreamBase);
|
|
2314
|
+
injectProviderAuth(upstreamHeaders, matchedProviderConfig);
|
|
2339
2315
|
const provider = model !== null ? registry.findProvider(model) : null;
|
|
2340
2316
|
if (model === null || provider === null) {
|
|
2341
2317
|
logger.warn(`[handler] Unsupported provider: model=${model}`);
|
|
2342
2318
|
return new Response("Forbidden: unsupported provider", { status: STATUS_FORBIDDEN });
|
|
2343
2319
|
}
|
|
2344
|
-
|
|
2345
|
-
if (parsed.isChatCompletions) {
|
|
2346
|
-
formatHandler = formatRegistry.get("openai") ?? null;
|
|
2347
|
-
} else if (matchedProviderConfig?.format) {
|
|
2348
|
-
formatHandler = formatRegistry.get(matchedProviderConfig.format) ?? null;
|
|
2349
|
-
} else {
|
|
2350
|
-
formatHandler = formatForPath(parsed.apiPath);
|
|
2351
|
-
}
|
|
2320
|
+
const formatHandler = formatForPath(parsed.apiPath);
|
|
2352
2321
|
if (formatHandler === null) {
|
|
2353
2322
|
return new Response("Forbidden: unsupported format", { status: STATUS_FORBIDDEN });
|
|
2354
2323
|
}
|
|
@@ -2837,12 +2806,15 @@ const AnthropicResponseSchema = object({
|
|
|
2837
2806
|
model: string().optional(),
|
|
2838
2807
|
usage: object({
|
|
2839
2808
|
input_tokens: number().optional(),
|
|
2840
|
-
output_tokens: number().optional()
|
|
2809
|
+
output_tokens: number().optional(),
|
|
2810
|
+
cache_creation_input_tokens: number().optional(),
|
|
2811
|
+
cache_read_input_tokens: number().optional()
|
|
2841
2812
|
}).optional(),
|
|
2842
2813
|
content: array(
|
|
2843
2814
|
discriminatedUnion("type", [
|
|
2844
2815
|
object({ type: literal("text"), text: string() }),
|
|
2845
|
-
object({ type: literal("thinking"), thinking: string() })
|
|
2816
|
+
object({ type: literal("thinking"), thinking: string() }),
|
|
2817
|
+
object({ type: literal("think"), thinking: string() })
|
|
2846
2818
|
])
|
|
2847
2819
|
)
|
|
2848
2820
|
});
|
|
@@ -2918,11 +2890,13 @@ async function testEndpoint(baseUrl, apiKey, path2, model, isOpenAI) {
|
|
|
2918
2890
|
const responseModel = json.model ?? model;
|
|
2919
2891
|
const inputTokens = json.usage?.input_tokens;
|
|
2920
2892
|
const outputTokens = json.usage?.output_tokens;
|
|
2893
|
+
const cacheCreationInputTokens = json.usage?.cache_creation_input_tokens;
|
|
2894
|
+
const cacheReadInputTokens = json.usage?.cache_read_input_tokens;
|
|
2921
2895
|
const content = [];
|
|
2922
2896
|
for (const block of json.content ?? []) {
|
|
2923
2897
|
if (block.type === "text" && block.text) {
|
|
2924
2898
|
content.push({ type: "text", text: block.text });
|
|
2925
|
-
} else if (block.type === "thinking" && block.thinking) {
|
|
2899
|
+
} else if ((block.type === "thinking" || block.type === "think") && block.thinking) {
|
|
2926
2900
|
content.push({ type: "thinking", thinking: block.thinking });
|
|
2927
2901
|
}
|
|
2928
2902
|
}
|
|
@@ -2931,6 +2905,8 @@ async function testEndpoint(baseUrl, apiKey, path2, model, isOpenAI) {
|
|
|
2931
2905
|
model: responseModel,
|
|
2932
2906
|
inputTokens,
|
|
2933
2907
|
outputTokens,
|
|
2908
|
+
cacheCreationInputTokens,
|
|
2909
|
+
cacheReadInputTokens,
|
|
2934
2910
|
latencyMs,
|
|
2935
2911
|
content: content.length > 0 ? content : void 0,
|
|
2936
2912
|
rawResponse: responseText,
|
|
@@ -3056,6 +3032,8 @@ async function testStreamingEndpoint(baseUrl, apiKey, path2, model, isOpenAI) {
|
|
|
3056
3032
|
let content = [];
|
|
3057
3033
|
let inputTokens;
|
|
3058
3034
|
let outputTokens;
|
|
3035
|
+
let cacheCreationInputTokens;
|
|
3036
|
+
let cacheReadInputTokens;
|
|
3059
3037
|
let responseModel;
|
|
3060
3038
|
if (isOpenAI) {
|
|
3061
3039
|
const parsed = OpenAIResponseSchema.parse(JSON.parse(reconstructedJson));
|
|
@@ -3071,10 +3049,12 @@ async function testStreamingEndpoint(baseUrl, apiKey, path2, model, isOpenAI) {
|
|
|
3071
3049
|
responseModel = typeof parsed.model === "string" && parsed.model !== "" ? parsed.model : void 0;
|
|
3072
3050
|
inputTokens = parsed.usage?.input_tokens;
|
|
3073
3051
|
outputTokens = parsed.usage?.output_tokens;
|
|
3052
|
+
cacheCreationInputTokens = parsed.usage?.cache_creation_input_tokens;
|
|
3053
|
+
cacheReadInputTokens = parsed.usage?.cache_read_input_tokens;
|
|
3074
3054
|
for (const block of parsed.content ?? []) {
|
|
3075
3055
|
if (block.type === "text" && block.text) {
|
|
3076
3056
|
content.push({ type: "text", text: block.text });
|
|
3077
|
-
} else if (block.type === "thinking" && block.thinking) {
|
|
3057
|
+
} else if ((block.type === "thinking" || block.type === "think") && block.thinking) {
|
|
3078
3058
|
content.push({ type: "thinking", thinking: block.thinking });
|
|
3079
3059
|
}
|
|
3080
3060
|
}
|
|
@@ -3084,6 +3064,8 @@ async function testStreamingEndpoint(baseUrl, apiKey, path2, model, isOpenAI) {
|
|
|
3084
3064
|
model: responseModel ?? model,
|
|
3085
3065
|
inputTokens,
|
|
3086
3066
|
outputTokens,
|
|
3067
|
+
cacheCreationInputTokens,
|
|
3068
|
+
cacheReadInputTokens,
|
|
3087
3069
|
latencyMs,
|
|
3088
3070
|
content: content.length > 0 ? content : void 0,
|
|
3089
3071
|
rawResponse: reconstructedJson,
|
|
@@ -3178,8 +3160,8 @@ const Route$2 = createFileRoute("/api/providers/$providerId/test")({
|
|
|
3178
3160
|
responseText: nonStreamingResult.rawResponse ?? JSON.stringify(nonStreamingResult),
|
|
3179
3161
|
inputTokens: nonStreamingResult.inputTokens ?? null,
|
|
3180
3162
|
outputTokens: nonStreamingResult.outputTokens ?? null,
|
|
3181
|
-
cacheCreationInputTokens: null,
|
|
3182
|
-
cacheReadInputTokens: null,
|
|
3163
|
+
cacheCreationInputTokens: nonStreamingResult.cacheCreationInputTokens ?? null,
|
|
3164
|
+
cacheReadInputTokens: nonStreamingResult.cacheReadInputTokens ?? null,
|
|
3183
3165
|
elapsedMs: nonStreamingResult.latencyMs ?? 0,
|
|
3184
3166
|
streaming: false,
|
|
3185
3167
|
userAgent: "provider-test",
|
|
@@ -3224,8 +3206,8 @@ const Route$2 = createFileRoute("/api/providers/$providerId/test")({
|
|
|
3224
3206
|
responseText: streamingResult.rawResponse ?? JSON.stringify(streamingResult),
|
|
3225
3207
|
inputTokens: streamingResult.inputTokens ?? null,
|
|
3226
3208
|
outputTokens: streamingResult.outputTokens ?? null,
|
|
3227
|
-
cacheCreationInputTokens: null,
|
|
3228
|
-
cacheReadInputTokens: null,
|
|
3209
|
+
cacheCreationInputTokens: streamingResult.cacheCreationInputTokens ?? null,
|
|
3210
|
+
cacheReadInputTokens: streamingResult.cacheReadInputTokens ?? null,
|
|
3229
3211
|
elapsedMs: streamingResult.latencyMs ?? 0,
|
|
3230
3212
|
streaming: true,
|
|
3231
3213
|
streamingChunks: streamingResult.streamingChunks,
|
|
@@ -3350,45 +3332,6 @@ const Route$2 = createFileRoute("/api/providers/$providerId/test")({
|
|
|
3350
3332
|
const ReplayRequestSchema = object({
|
|
3351
3333
|
modifiedBody: string()
|
|
3352
3334
|
});
|
|
3353
|
-
function getHostFromUrl(urlStr) {
|
|
3354
|
-
try {
|
|
3355
|
-
const url = new URL(urlStr);
|
|
3356
|
-
return url.host;
|
|
3357
|
-
} catch {
|
|
3358
|
-
return "api.anthropic.com";
|
|
3359
|
-
}
|
|
3360
|
-
}
|
|
3361
|
-
function buildUpstreamUrl(upstreamBase, apiPath) {
|
|
3362
|
-
const base = upstreamBase.endsWith("/") ? upstreamBase.slice(0, -1) : upstreamBase;
|
|
3363
|
-
return base + apiPath;
|
|
3364
|
-
}
|
|
3365
|
-
function selectUpstreamBase(isChatCompletions, matchedProviderConfig) {
|
|
3366
|
-
let upstreamBase;
|
|
3367
|
-
if (matchedProviderConfig) {
|
|
3368
|
-
if (isChatCompletions && matchedProviderConfig.openaiBaseUrl !== void 0 && matchedProviderConfig.openaiBaseUrl !== "") {
|
|
3369
|
-
upstreamBase = matchedProviderConfig.openaiBaseUrl;
|
|
3370
|
-
} else if (!isChatCompletions && matchedProviderConfig.anthropicBaseUrl !== void 0 && matchedProviderConfig.anthropicBaseUrl !== "") {
|
|
3371
|
-
upstreamBase = matchedProviderConfig.anthropicBaseUrl;
|
|
3372
|
-
} else if (matchedProviderConfig.baseUrl !== void 0 && matchedProviderConfig.baseUrl !== "") {
|
|
3373
|
-
upstreamBase = matchedProviderConfig.baseUrl;
|
|
3374
|
-
} else {
|
|
3375
|
-
upstreamBase = matchedProviderConfig.format === "openai" ? DEFAULT_OPENAI_UPSTREAM$1 : DEFAULT_UPSTREAM$1;
|
|
3376
|
-
}
|
|
3377
|
-
} else {
|
|
3378
|
-
upstreamBase = isChatCompletions ? DEFAULT_OPENAI_UPSTREAM$1 : DEFAULT_UPSTREAM$1;
|
|
3379
|
-
}
|
|
3380
|
-
return upstreamBase;
|
|
3381
|
-
}
|
|
3382
|
-
function injectAuthHeaders(upstreamHeaders, matchedProviderConfig) {
|
|
3383
|
-
if (!matchedProviderConfig) return;
|
|
3384
|
-
const apiKey = matchedProviderConfig.apiKey.replace(/^Bearer\s+/i, "").trim();
|
|
3385
|
-
if (matchedProviderConfig.authHeader === AUTH_HEADER_X_API_KEY) {
|
|
3386
|
-
upstreamHeaders.set(HEADER_X_API_KEY, apiKey);
|
|
3387
|
-
upstreamHeaders.delete(HEADER_AUTHORIZATION);
|
|
3388
|
-
} else {
|
|
3389
|
-
upstreamHeaders.set(HEADER_AUTHORIZATION, `Bearer ${apiKey}`);
|
|
3390
|
-
}
|
|
3391
|
-
}
|
|
3392
3335
|
const Route$1 = createFileRoute("/api/logs/$id/replay")({
|
|
3393
3336
|
server: {
|
|
3394
3337
|
handlers: {
|
|
@@ -3428,16 +3371,13 @@ const Route$1 = createFileRoute("/api/logs/$id/replay")({
|
|
|
3428
3371
|
{ status: STATUS_FORBIDDEN }
|
|
3429
3372
|
);
|
|
3430
3373
|
}
|
|
3431
|
-
const
|
|
3432
|
-
const
|
|
3433
|
-
const normalizedPath = isChatCompletions && !apiPath.startsWith("/v1/") ? "/v1" + apiPath : apiPath;
|
|
3434
|
-
const formatHandler = formatForPath(apiPath);
|
|
3374
|
+
const route = describeApiRoute(log.path);
|
|
3375
|
+
const formatHandler = formatForPath(route.apiPath);
|
|
3435
3376
|
if (formatHandler === null) {
|
|
3436
3377
|
return Response.json({ error: "Unsupported path" }, { status: STATUS_FORBIDDEN });
|
|
3437
3378
|
}
|
|
3438
|
-
const upstreamBase = selectUpstreamBase(
|
|
3439
|
-
const upstreamUrl = buildUpstreamUrl(upstreamBase, normalizedPath);
|
|
3440
|
-
const upstreamHost = getHostFromUrl(upstreamBase);
|
|
3379
|
+
const upstreamBase = selectUpstreamBase(route, matchedProviderConfig);
|
|
3380
|
+
const upstreamUrl = buildUpstreamUrl(upstreamBase, route.normalizedPath);
|
|
3441
3381
|
const headers = new Headers();
|
|
3442
3382
|
headers.set(HEADER_USER_AGENT, PROXY_IDENTITY);
|
|
3443
3383
|
headers.set(HEADER_X_PROXY_IDENTITY, PROXY_IDENTITY);
|
|
@@ -3448,8 +3388,8 @@ const Route$1 = createFileRoute("/api/logs/$id/replay")({
|
|
|
3448
3388
|
headers.set(name, value);
|
|
3449
3389
|
}
|
|
3450
3390
|
}
|
|
3451
|
-
headers
|
|
3452
|
-
|
|
3391
|
+
setUpstreamHost(headers, upstreamBase);
|
|
3392
|
+
injectProviderAuth(headers, matchedProviderConfig);
|
|
3453
3393
|
const startTime = Date.now();
|
|
3454
3394
|
let upstreamRes;
|
|
3455
3395
|
try {
|
|
@@ -3465,7 +3405,7 @@ const Route$1 = createFileRoute("/api/logs/$id/replay")({
|
|
|
3465
3405
|
});
|
|
3466
3406
|
}
|
|
3467
3407
|
const elapsedMs = Date.now() - startTime;
|
|
3468
|
-
const isStream = upstreamRes.headers.get(HEADER_CONTENT_TYPE)?.includes(
|
|
3408
|
+
const isStream = upstreamRes.headers.get(HEADER_CONTENT_TYPE)?.includes(CONTENT_TYPE_EVENT_STREAM) ?? false;
|
|
3469
3409
|
if (isStream) {
|
|
3470
3410
|
const chunks = [];
|
|
3471
3411
|
const decoder = new TextDecoder();
|
|
@@ -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/models", "/api/providers", "/api/sessions", "/proxy/$"], preloads: ["/assets/index-
|
|
1
|
+
const tsrStartManifest = () => ({ routes: { __root__: { filePath: "C:/Users/claw/workspace/llm-inspector/src/routes/__root.tsx", children: ["/", "/api/config", "/api/health", "/api/logs", "/api/models", "/api/providers", "/api/sessions", "/proxy/$"], preloads: ["/assets/index-BLz26_QE.js"], scripts: [{ attrs: { type: "module", async: true, src: "/assets/index-BLz26_QE.js" } }] }, "/": { filePath: "C:/Users/claw/workspace/llm-inspector/src/routes/index.tsx", children: void 0, preloads: ["/assets/index-BHjxAEHw.js"] } } });
|
|
2
2
|
export {
|
|
3
3
|
tsrStartManifest
|
|
4
4
|
};
|
package/.output/server/index.mjs
CHANGED
|
@@ -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-
|
|
41
|
+
"mtime": "2026-06-08T07:43:02.084Z",
|
|
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-
|
|
48
|
+
"mtime": "2026-06-08T07:43:02.087Z",
|
|
49
49
|
"size": 6918,
|
|
50
50
|
"path": "../public/assets/minimax-BPMzvuL-.jpeg"
|
|
51
51
|
},
|
|
52
|
-
"/assets/index-
|
|
52
|
+
"/assets/index-BGxRftQy.css": {
|
|
53
53
|
"type": "text/css; charset=utf-8",
|
|
54
|
-
"etag": '"
|
|
55
|
-
"mtime": "2026-06-
|
|
56
|
-
"size":
|
|
57
|
-
"path": "../public/assets/index-
|
|
54
|
+
"etag": '"1166c-gg/EqTkMlOVIH7gFJHkZ+FF8BFg"',
|
|
55
|
+
"mtime": "2026-06-08T07:43:02.087Z",
|
|
56
|
+
"size": 71276,
|
|
57
|
+
"path": "../public/assets/index-BGxRftQy.css"
|
|
58
58
|
},
|
|
59
|
-
"/assets/
|
|
60
|
-
"type": "image/svg+xml",
|
|
61
|
-
"etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
|
|
62
|
-
"mtime": "2026-06-08T00:49:07.051Z",
|
|
63
|
-
"size": 11256,
|
|
64
|
-
"path": "../public/assets/zhipuai-BPNAnxo-.svg"
|
|
65
|
-
},
|
|
66
|
-
"/assets/index-Do7wdaYZ.js": {
|
|
59
|
+
"/assets/index-BLz26_QE.js": {
|
|
67
60
|
"type": "text/javascript; charset=utf-8",
|
|
68
|
-
"etag": '"5103c
|
|
69
|
-
"mtime": "2026-06-
|
|
61
|
+
"etag": '"5103c-+JM4xmfobHv5x3cvc23X5jJh1dQ"',
|
|
62
|
+
"mtime": "2026-06-08T07:43:02.087Z",
|
|
70
63
|
"size": 331836,
|
|
71
|
-
"path": "../public/assets/index-
|
|
64
|
+
"path": "../public/assets/index-BLz26_QE.js"
|
|
72
65
|
},
|
|
73
66
|
"/assets/qwen-CONDcHqt.png": {
|
|
74
67
|
"type": "image/png",
|
|
75
68
|
"etag": '"572c3-cdJAPaHdOvFCGzuaQjagdgOu6XE"',
|
|
76
|
-
"mtime": "2026-06-
|
|
69
|
+
"mtime": "2026-06-08T07:43:02.087Z",
|
|
77
70
|
"size": 357059,
|
|
78
71
|
"path": "../public/assets/qwen-CONDcHqt.png"
|
|
79
72
|
},
|
|
80
|
-
"/assets/
|
|
73
|
+
"/assets/zhipuai-BPNAnxo-.svg": {
|
|
74
|
+
"type": "image/svg+xml",
|
|
75
|
+
"etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
|
|
76
|
+
"mtime": "2026-06-08T07:43:02.087Z",
|
|
77
|
+
"size": 11256,
|
|
78
|
+
"path": "../public/assets/zhipuai-BPNAnxo-.svg"
|
|
79
|
+
},
|
|
80
|
+
"/assets/index-BHjxAEHw.js": {
|
|
81
81
|
"type": "text/javascript; charset=utf-8",
|
|
82
|
-
"etag": '"
|
|
83
|
-
"mtime": "2026-06-
|
|
84
|
-
"size":
|
|
85
|
-
"path": "../public/assets/index-
|
|
82
|
+
"etag": '"8bc3c-HIRAa9iMtFjtXPHh5YlOmbmCEoQ"',
|
|
83
|
+
"mtime": "2026-06-08T07:43:02.087Z",
|
|
84
|
+
"size": 572476,
|
|
85
|
+
"path": "../public/assets/index-BHjxAEHw.js"
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
88
|
function readAsset(id) {
|
package/package.json
CHANGED
|
@@ -147,10 +147,12 @@ export function ProxyViewer({
|
|
|
147
147
|
{/* Header */}
|
|
148
148
|
<div className="flex items-center gap-4 mb-4 px-6 pt-6">
|
|
149
149
|
<h1 className="text-lg font-bold flex-1 flex items-center gap-2">
|
|
150
|
-
<CrabLogo className="size-10 text-amber-500" />
|
|
151
|
-
|
|
150
|
+
<CrabLogo className="size-10 text-amber-500 self-center" />
|
|
151
|
+
<span className="flex items-baseline gap-2">
|
|
152
|
+
LLM Inspector
|
|
153
|
+
<span className="text-xs text-muted-foreground font-mono">v{packageJson.version}</span>
|
|
154
|
+
</span>
|
|
152
155
|
</h1>
|
|
153
|
-
<span className="text-muted-foreground text-xs font-mono">v{packageJson.version}</span>
|
|
154
156
|
<div className="flex items-center border border-border rounded-md overflow-hidden">
|
|
155
157
|
<button
|
|
156
158
|
type="button"
|