@tonyclaw/llm-inspector 1.7.3 → 1.7.5

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.
@@ -14,4 +14,4 @@ Error generating stack: `+l.message+`
14
14
  `)}else{const p=o.indexOf(`
15
15
  `);if(p>=0){const S=o.slice(0,p).trim();o=o.slice(p+1),S.length>0&&(m=JSON.parse(S),f=!0)}}}return(async()=>{try{for(;;){const{value:y,done:h}=await r.read();y&&(o+=y);const p=o.lastIndexOf(`
16
16
  `);if(p>=0){const S=o.slice(0,p);o=o.slice(p+1);const b=S.split(`
17
- `).filter(Boolean);for(const _ of b)try{u(JSON.parse(_))}catch(R){i?.(`Invalid JSON line: ${_}`,R)}}if(h)break}}catch(y){i?.("Stream processing error:",y)}})(),u(m)}async function _E({jsonStream:a,onMessage:u,onError:i}){const r=a.getReader(),{value:o,done:f}=await r.read();if(f||!o)throw new Error("Stream ended before first object");const m=JSON.parse(o);return(async()=>{try{for(;;){const{value:y,done:h}=await r.read();if(h)break;if(y)try{u(JSON.parse(y))}catch(p){i?.(`Invalid JSON: ${y}`,p)}}}catch(y){i?.("Stream processing error:",y)}})(),u(m)}function EE(a){const u="/_serverFn/"+a;return Object.assign((...o)=>{const f=Lp()?.serverFns?.fetch;return gE(u,o,f??fetch)},{url:u,serverFnMeta:{id:a},[Ao]:!0})}const RE={key:"$TSS/serverfn",test:a=>typeof a!="function"||!(Ao in a)?!1:!!a[Ao],toSerializable:({serverFnMeta:a})=>({functionId:a.id}),fromSerializable:({functionId:a})=>EE(a)},TE="/assets/index-B3RwBPLW.css",Hp=G_({head:()=>({meta:[{charSet:"utf-8"},{name:"viewport",content:"width=device-width, initial-scale=1"},{title:"llm-inspector"}],links:[{rel:"stylesheet",href:TE}]}),component:AE});function AE(){return k.jsx(OE,{children:k.jsx(Cp,{})})}function OE({children:a}){return k.jsxs("html",{lang:"en",className:"dark",children:[k.jsx("head",{children:k.jsx(aE,{})}),k.jsxs("body",{children:[a,k.jsx(lE,{})]})]})}const ME="modulepreload",zE=function(a){return"/"+a},zy={},xE=function(u,i,r){let o=Promise.resolve();if(i&&i.length>0){let h=function(p){return Promise.all(p.map(S=>Promise.resolve(S).then(b=>({status:"fulfilled",value:b}),b=>({status:"rejected",reason:b}))))};document.getElementsByTagName("link");const m=document.querySelector("meta[property=csp-nonce]"),y=m?.nonce||m?.getAttribute("nonce");o=h(i.map(p=>{if(p=zE(p),p in zy)return;zy[p]=!0;const S=p.endsWith(".css"),b=S?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${p}"]${b}`))return;const _=document.createElement("link");if(_.rel=S?"stylesheet":ME,S||(_.as="script"),_.crossOrigin="",_.href=p,y&&_.setAttribute("nonce",y),document.head.appendChild(_),S)return new Promise((R,D)=>{_.addEventListener("load",R),_.addEventListener("error",()=>D(new Error(`Unable to preload CSS for ${p}`)))})}))}function f(m){const y=new Event("vite:preloadError",{cancelable:!0});if(y.payload=m,window.dispatchEvent(y),!y.defaultPrevented)throw m}return o.then(m=>{for(const y of m||[])y.status==="rejected"&&f(y.reason);return u().catch(f)})},wE=()=>xE(()=>import("./index-Bf_WGooQ.js"),[]),CE=To("/")({component:V_(wE,"component")}),DE=CE.update({id:"/",path:"/",getParentRoute:()=>Hp}),UE={IndexRoute:DE},LE=Hp._addFileChildren(UE);function NE(){return P_({routeTree:LE,scrollRestoration:!1})}async function BE(){const a=await NE();let u;return u=[],window.__TSS_START_OPTIONS__={serializationAdapters:u},u.push(RE),a.options.serializationAdapters&&u.push(...a.options.serializationAdapters),a.update({basepath:"",serializationAdapters:u}),a.state.matches.length||await uE(a),a}async function jE(){const a=await BE();return window.$_TSR?.h(),a}let So;function HE(){return So||(So=jE()),k.jsx(p_,{promise:So,children:a=>k.jsx(I_,{router:a})})}nt.startTransition(()=>{h0.hydrateRoot(document,k.jsx(nt.StrictMode,{children:k.jsx(HE,{})}))});export{Ul as R,Mp as a,s0 as b,qE as c,XE as d,xy as g,k as j,nt as r};
17
+ `).filter(Boolean);for(const _ of b)try{u(JSON.parse(_))}catch(R){i?.(`Invalid JSON line: ${_}`,R)}}if(h)break}}catch(y){i?.("Stream processing error:",y)}})(),u(m)}async function _E({jsonStream:a,onMessage:u,onError:i}){const r=a.getReader(),{value:o,done:f}=await r.read();if(f||!o)throw new Error("Stream ended before first object");const m=JSON.parse(o);return(async()=>{try{for(;;){const{value:y,done:h}=await r.read();if(h)break;if(y)try{u(JSON.parse(y))}catch(p){i?.(`Invalid JSON: ${y}`,p)}}}catch(y){i?.("Stream processing error:",y)}})(),u(m)}function EE(a){const u="/_serverFn/"+a;return Object.assign((...o)=>{const f=Lp()?.serverFns?.fetch;return gE(u,o,f??fetch)},{url:u,serverFnMeta:{id:a},[Ao]:!0})}const RE={key:"$TSS/serverfn",test:a=>typeof a!="function"||!(Ao in a)?!1:!!a[Ao],toSerializable:({serverFnMeta:a})=>({functionId:a.id}),fromSerializable:({functionId:a})=>EE(a)},TE="/assets/index-B3RwBPLW.css",Hp=G_({head:()=>({meta:[{charSet:"utf-8"},{name:"viewport",content:"width=device-width, initial-scale=1"},{title:"llm-inspector"}],links:[{rel:"stylesheet",href:TE}]}),component:AE});function AE(){return k.jsx(OE,{children:k.jsx(Cp,{})})}function OE({children:a}){return k.jsxs("html",{lang:"en",className:"dark",children:[k.jsx("head",{children:k.jsx(aE,{})}),k.jsxs("body",{children:[a,k.jsx(lE,{})]})]})}const ME="modulepreload",zE=function(a){return"/"+a},zy={},xE=function(u,i,r){let o=Promise.resolve();if(i&&i.length>0){let h=function(p){return Promise.all(p.map(S=>Promise.resolve(S).then(b=>({status:"fulfilled",value:b}),b=>({status:"rejected",reason:b}))))};document.getElementsByTagName("link");const m=document.querySelector("meta[property=csp-nonce]"),y=m?.nonce||m?.getAttribute("nonce");o=h(i.map(p=>{if(p=zE(p),p in zy)return;zy[p]=!0;const S=p.endsWith(".css"),b=S?'[rel="stylesheet"]':"";if(document.querySelector(`link[href="${p}"]${b}`))return;const _=document.createElement("link");if(_.rel=S?"stylesheet":ME,S||(_.as="script"),_.crossOrigin="",_.href=p,y&&_.setAttribute("nonce",y),document.head.appendChild(_),S)return new Promise((R,D)=>{_.addEventListener("load",R),_.addEventListener("error",()=>D(new Error(`Unable to preload CSS for ${p}`)))})}))}function f(m){const y=new Event("vite:preloadError",{cancelable:!0});if(y.payload=m,window.dispatchEvent(y),!y.defaultPrevented)throw m}return o.then(m=>{for(const y of m||[])y.status==="rejected"&&f(y.reason);return u().catch(f)})},wE=()=>xE(()=>import("./index-BkNsdDqV.js"),[]),CE=To("/")({component:V_(wE,"component")}),DE=CE.update({id:"/",path:"/",getParentRoute:()=>Hp}),UE={IndexRoute:DE},LE=Hp._addFileChildren(UE);function NE(){return P_({routeTree:LE,scrollRestoration:!1})}async function BE(){const a=await NE();let u;return u=[],window.__TSS_START_OPTIONS__={serializationAdapters:u},u.push(RE),a.options.serializationAdapters&&u.push(...a.options.serializationAdapters),a.update({basepath:"",serializationAdapters:u}),a.state.matches.length||await uE(a),a}async function jE(){const a=await BE();return window.$_TSR?.h(),a}let So;function HE(){return So||(So=jE()),k.jsx(p_,{promise:So,children:a=>k.jsx(I_,{router:a})})}nt.startTransition(()=>{h0.hydrateRoot(document,k.jsx(nt.StrictMode,{children:k.jsx(HE,{})}))});export{Ul as R,Mp as a,s0 as b,qE as c,XE as d,xy as g,k as j,nt as r};
@@ -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-BWDeDWmr.mjs";
2
+ import { C as CapturedLogSchema, a as parseRequest, p as parseOpenAIResponse, I as InspectorResponseSchema } from "./router-4NfH7bm_.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";
@@ -878,8 +878,9 @@ const PROVIDER_MAP = {
878
878
  };
879
879
  function detectProvider(model) {
880
880
  if (model === null) return "unknown";
881
+ const modelLower = model.toLowerCase();
881
882
  for (const [prefix, provider] of Object.entries(PROVIDER_MAP)) {
882
- if (model.startsWith(prefix)) {
883
+ if (modelLower.startsWith(prefix.toLowerCase())) {
883
884
  return provider;
884
885
  }
885
886
  }
@@ -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-b6u6g-Cr.mjs");
200
+ const { tsrStartManifest } = await import("../_tanstack-start-manifest_v-CdS0WV2N.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-BWDeDWmr.mjs").then((n) => n.r);
769
+ const routerEntry = await import("./router-4NfH7bm_.mjs").then((n) => n.r);
770
770
  const startEntry = await import("./start-HYkvq4Ni.mjs");
771
771
  return { startEntry, routerEntry };
772
772
  }
@@ -65,7 +65,7 @@ function RootDocument({ children }) {
65
65
  ] })
66
66
  ] });
67
67
  }
68
- const $$splitComponentImporter = () => import("./index-D0iGQ1Zd.mjs");
68
+ const $$splitComponentImporter = () => import("./index-DosJndBx.mjs");
69
69
  const Route$d = createFileRoute("/")({
70
70
  component: lazyRouteComponent($$splitComponentImporter, "component")
71
71
  });
@@ -1396,68 +1396,68 @@ const ProviderConfigSchema = object({
1396
1396
  object({
1397
1397
  providers: array(ProviderConfigSchema)
1398
1398
  });
1399
+ const configPath = process.env["LLM_INSPECTOR_CONFIG_PATH"];
1399
1400
  const store = new Conf({
1400
1401
  projectName: "llm-inspector",
1401
1402
  defaults: {
1402
1403
  providers: []
1404
+ },
1405
+ ...configPath !== void 0 ? { path: configPath } : {}
1406
+ });
1407
+ function migrateProvider(p) {
1408
+ const currentAnthropicUrl = p.anthropicBaseUrl ?? "";
1409
+ const currentOpenaiUrl = p.openaiBaseUrl ?? "";
1410
+ if (currentAnthropicUrl !== "" || currentOpenaiUrl !== "") {
1411
+ return p;
1412
+ }
1413
+ let format;
1414
+ let baseUrl;
1415
+ if (currentAnthropicUrl !== "" && currentOpenaiUrl !== "") {
1416
+ format = p.format ?? "anthropic";
1417
+ baseUrl = p.baseUrl !== void 0 && p.baseUrl !== "" ? p.baseUrl : currentAnthropicUrl;
1418
+ } else if (currentOpenaiUrl !== "") {
1419
+ format = "openai";
1420
+ baseUrl = currentOpenaiUrl;
1421
+ } else if (currentAnthropicUrl !== "") {
1422
+ format = "anthropic";
1423
+ baseUrl = currentAnthropicUrl;
1424
+ } else if (p.format !== void 0 && p.baseUrl !== void 0 && p.baseUrl !== "") {
1425
+ format = p.format;
1426
+ baseUrl = p.baseUrl;
1427
+ if (format === "openai") {
1428
+ return { ...p, format, baseUrl, anthropicBaseUrl: "", openaiBaseUrl: p.baseUrl };
1429
+ } else {
1430
+ return { ...p, format, baseUrl, anthropicBaseUrl: p.baseUrl, openaiBaseUrl: "" };
1431
+ }
1403
1432
  }
1404
- });
1433
+ return {
1434
+ ...p,
1435
+ format,
1436
+ baseUrl,
1437
+ anthropicBaseUrl: currentAnthropicUrl,
1438
+ openaiBaseUrl: currentOpenaiUrl
1439
+ };
1440
+ }
1405
1441
  function migrateProviders() {
1406
1442
  const providers = store.get("providers", []);
1407
- let migrated = false;
1408
- const updated = providers.map((p) => {
1409
- function getOldUrl(obj, key) {
1410
- if (obj !== null && typeof obj === "object") {
1411
- const desc = Object.getOwnPropertyDescriptor(obj, key);
1412
- if (desc !== void 0 && typeof desc.value === "string") {
1413
- return desc.value;
1414
- }
1415
- }
1416
- return "";
1417
- }
1418
- const oldAnthropicBaseUrl = getOldUrl(p, "anthropicBaseUrl");
1419
- const oldOpenaiBaseUrl = getOldUrl(p, "openaiBaseUrl");
1420
- const currentAnthropicUrl = p.anthropicBaseUrl ?? "";
1421
- const currentOpenaiUrl = p.openaiBaseUrl ?? "";
1422
- if (p.format !== void 0 && (currentAnthropicUrl !== "" || currentOpenaiUrl !== "")) {
1423
- return p;
1424
- }
1425
- const newAnthropicUrl = oldAnthropicBaseUrl !== "" ? oldAnthropicBaseUrl : currentAnthropicUrl;
1426
- const newOpenaiUrl = oldOpenaiBaseUrl !== "" ? oldOpenaiBaseUrl : currentOpenaiUrl;
1427
- let format;
1428
- let baseUrl;
1429
- if (newAnthropicUrl !== "" && newOpenaiUrl !== "") {
1430
- format = p.format ?? "anthropic";
1431
- baseUrl = p.baseUrl !== void 0 && p.baseUrl !== "" ? p.baseUrl : newAnthropicUrl;
1432
- } else if (newOpenaiUrl !== "") {
1433
- format = "openai";
1434
- baseUrl = newOpenaiUrl;
1435
- } else if (newAnthropicUrl !== "") {
1436
- format = "anthropic";
1437
- baseUrl = newAnthropicUrl;
1438
- } else if (p.format !== void 0 && p.baseUrl !== void 0 && p.baseUrl !== "") {
1439
- format = p.format;
1440
- baseUrl = p.baseUrl;
1441
- if (format === "openai") {
1442
- return { ...p, format, baseUrl, anthropicBaseUrl: "", openaiBaseUrl: p.baseUrl };
1443
- } else {
1444
- return { ...p, format, baseUrl, anthropicBaseUrl: p.baseUrl, openaiBaseUrl: "" };
1445
- }
1446
- }
1447
- migrated = true;
1448
- return {
1449
- ...p,
1450
- format,
1451
- baseUrl,
1452
- anthropicBaseUrl: newAnthropicUrl,
1453
- openaiBaseUrl: newOpenaiUrl
1454
- };
1455
- });
1443
+ const updated = providers.map(migrateProvider);
1444
+ const migrated = updated.some((p, i) => JSON.stringify(p) !== JSON.stringify(providers[i]));
1456
1445
  if (migrated) {
1457
1446
  store.set("providers", updated);
1458
1447
  }
1459
1448
  }
1460
1449
  migrateProviders();
1450
+ const providersJson = process.env["LLM_INSPECTOR_PROVIDERS_JSON"];
1451
+ if (providersJson !== void 0) {
1452
+ try {
1453
+ const parsed = ProviderConfigSchema.array().safeParse(JSON.parse(providersJson));
1454
+ if (parsed.success) {
1455
+ const migrated = parsed.data.map(migrateProvider);
1456
+ store.set("providers", migrated);
1457
+ }
1458
+ } catch {
1459
+ }
1460
+ }
1461
1461
  function getProviders() {
1462
1462
  return store.get("providers", []);
1463
1463
  }
@@ -1480,7 +1480,9 @@ function addProvider(name, apiKey, format, baseUrl, model, authHeader) {
1480
1480
  model,
1481
1481
  authHeader: authHeader ?? "bearer",
1482
1482
  createdAt: now,
1483
- updatedAt: now
1483
+ updatedAt: now,
1484
+ anthropicBaseUrl: format === "anthropic" && baseUrl !== void 0 ? baseUrl : "",
1485
+ openaiBaseUrl: format === "openai" && baseUrl !== void 0 ? baseUrl : ""
1484
1486
  };
1485
1487
  providers.push(newProvider);
1486
1488
  store.set("providers", providers);
@@ -1499,7 +1501,10 @@ function updateProvider(id, updates) {
1499
1501
  baseUrl: updates.baseUrl !== void 0 ? updates.baseUrl : existing.baseUrl,
1500
1502
  authHeader: updates.authHeader ?? existing.authHeader,
1501
1503
  createdAt: existing.createdAt,
1502
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1504
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
1505
+ // Handle format-specific URLs
1506
+ anthropicBaseUrl: updates.anthropicBaseUrl !== void 0 ? updates.anthropicBaseUrl : existing.anthropicBaseUrl,
1507
+ openaiBaseUrl: updates.openaiBaseUrl !== void 0 ? updates.openaiBaseUrl : existing.openaiBaseUrl
1503
1508
  };
1504
1509
  const index = providers.findIndex((p) => p.id === id);
1505
1510
  providers[index] = updated;
@@ -1968,7 +1973,9 @@ const ProviderUpdateSchema = object({
1968
1973
  format: _enum(["anthropic", "openai"]).optional(),
1969
1974
  baseUrl: string().min(1, "Base URL is required").optional(),
1970
1975
  model: string().min(1, "Model is required").optional(),
1971
- authHeader: _enum(["bearer", "x-api-key"]).optional()
1976
+ authHeader: _enum(["bearer", "x-api-key"]).optional(),
1977
+ anthropicBaseUrl: string().optional(),
1978
+ openaiBaseUrl: string().optional()
1972
1979
  });
1973
1980
  const Route$6 = createFileRoute("/api/providers/$providerId")({
1974
1981
  server: {
@@ -2195,16 +2202,17 @@ async function testEndpoint(baseUrl, apiKey, path2, model, isOpenAI) {
2195
2202
  messages: [{ role: "user", content: "say hello and briefly introduce yourself" }],
2196
2203
  max_tokens: 1024
2197
2204
  });
2205
+ const requestHeaders = {
2206
+ "Content-Type": "application/json",
2207
+ Authorization: `Bearer ${apiKey}`
2208
+ };
2198
2209
  const controller = new AbortController();
2199
2210
  const timeoutId = setTimeout(() => controller.abort(), TEST_TIMEOUT_MS);
2200
2211
  try {
2201
2212
  const url = `${baseUrl}${path2}`;
2202
2213
  const response = await fetch(url, {
2203
2214
  method: "POST",
2204
- headers: {
2205
- "Content-Type": "application/json",
2206
- Authorization: `Bearer ${apiKey}`
2207
- },
2215
+ headers: requestHeaders,
2208
2216
  body,
2209
2217
  signal: controller.signal
2210
2218
  });
@@ -2233,7 +2241,8 @@ async function testEndpoint(baseUrl, apiKey, path2, model, isOpenAI) {
2233
2241
  outputTokens,
2234
2242
  latencyMs,
2235
2243
  content: content !== null && content !== "" ? [{ type: "text", text: content }] : void 0,
2236
- rawResponse: responseText
2244
+ rawResponse: responseText,
2245
+ requestHeaders
2237
2246
  };
2238
2247
  } else {
2239
2248
  const json = AnthropicResponseSchema.parse(JSON.parse(responseText));
@@ -2255,7 +2264,8 @@ async function testEndpoint(baseUrl, apiKey, path2, model, isOpenAI) {
2255
2264
  outputTokens,
2256
2265
  latencyMs,
2257
2266
  content: content.length > 0 ? content : void 0,
2258
- rawResponse: responseText
2267
+ rawResponse: responseText,
2268
+ requestHeaders
2259
2269
  };
2260
2270
  }
2261
2271
  } catch (parseErr) {
@@ -2264,7 +2274,8 @@ async function testEndpoint(baseUrl, apiKey, path2, model, isOpenAI) {
2264
2274
  model,
2265
2275
  latencyMs,
2266
2276
  content: [{ type: "text", text: responseText.slice(0, 2e3) }],
2267
- rawResponse: responseText
2277
+ rawResponse: responseText,
2278
+ requestHeaders
2268
2279
  };
2269
2280
  }
2270
2281
  } catch (err) {
@@ -2280,16 +2291,17 @@ async function testStreamingEndpoint(baseUrl, apiKey, path2, model, isOpenAI) {
2280
2291
  max_tokens: 256,
2281
2292
  stream: true
2282
2293
  });
2294
+ const requestHeaders = {
2295
+ "Content-Type": "application/json",
2296
+ Authorization: `Bearer ${apiKey}`
2297
+ };
2283
2298
  const controller = new AbortController();
2284
2299
  const timeoutId = setTimeout(() => controller.abort(), TEST_TIMEOUT_MS);
2285
2300
  try {
2286
2301
  const url = `${baseUrl}${path2}`;
2287
2302
  const response = await fetch(url, {
2288
2303
  method: "POST",
2289
- headers: {
2290
- "Content-Type": "application/json",
2291
- Authorization: `Bearer ${apiKey}`
2292
- },
2304
+ headers: requestHeaders,
2293
2305
  body,
2294
2306
  signal: controller.signal
2295
2307
  });
@@ -2392,7 +2404,8 @@ async function testStreamingEndpoint(baseUrl, apiKey, path2, model, isOpenAI) {
2392
2404
  content: content.length > 0 ? content : void 0,
2393
2405
  rawResponse: reconstructedJson,
2394
2406
  streaming: true,
2395
- streamingChunks
2407
+ streamingChunks,
2408
+ requestHeaders
2396
2409
  };
2397
2410
  } catch {
2398
2411
  return {
@@ -2402,7 +2415,8 @@ async function testStreamingEndpoint(baseUrl, apiKey, path2, model, isOpenAI) {
2402
2415
  content: [{ type: "text", text: reconstructedJson.slice(0, 2e3) }],
2403
2416
  rawResponse: reconstructedJson,
2404
2417
  streaming: true,
2405
- streamingChunks
2418
+ streamingChunks,
2419
+ requestHeaders
2406
2420
  };
2407
2421
  }
2408
2422
  } catch (err) {
@@ -2430,7 +2444,8 @@ function createTestLogEntry(providerName, path2, body, upstreamUrl, result, isTe
2430
2444
  upstreamUrl,
2431
2445
  error: result.success ? null : result.error?.message ?? String(result.error),
2432
2446
  isTest: true,
2433
- providerName
2447
+ providerName,
2448
+ headers: result.requestHeaders ?? {}
2434
2449
  };
2435
2450
  }
2436
2451
  const Route$2 = createFileRoute("/api/providers/$providerId/test")({
@@ -2487,7 +2502,8 @@ const Route$2 = createFileRoute("/api/providers/$providerId/test")({
2487
2502
  origin: null,
2488
2503
  apiFormat: "anthropic",
2489
2504
  isTest: true,
2490
- providerName: provider.name
2505
+ providerName: provider.name,
2506
+ headers: nonStreamingResult.requestHeaders ?? {}
2491
2507
  });
2492
2508
  appendLogEntry(
2493
2509
  createTestLogEntry(
@@ -2533,7 +2549,8 @@ const Route$2 = createFileRoute("/api/providers/$providerId/test")({
2533
2549
  origin: null,
2534
2550
  apiFormat: "anthropic",
2535
2551
  isTest: true,
2536
- providerName: provider.name
2552
+ providerName: provider.name,
2553
+ headers: streamingResult.requestHeaders ?? {}
2537
2554
  });
2538
2555
  appendLogEntry(
2539
2556
  createTestLogEntry(
@@ -2580,7 +2597,8 @@ const Route$2 = createFileRoute("/api/providers/$providerId/test")({
2580
2597
  origin: null,
2581
2598
  apiFormat: "openai",
2582
2599
  isTest: true,
2583
- providerName: provider.name
2600
+ providerName: provider.name,
2601
+ headers: nonStreamingResult.requestHeaders ?? {}
2584
2602
  });
2585
2603
  appendLogEntry(
2586
2604
  createTestLogEntry(
@@ -2626,7 +2644,8 @@ const Route$2 = createFileRoute("/api/providers/$providerId/test")({
2626
2644
  origin: null,
2627
2645
  apiFormat: "openai",
2628
2646
  isTest: true,
2629
- providerName: provider.name
2647
+ providerName: provider.name,
2648
+ headers: streamingResult.requestHeaders ?? {}
2630
2649
  });
2631
2650
  appendLogEntry(
2632
2651
  createTestLogEntry(
@@ -1,4 +1,4 @@
1
- const tsrStartManifest = () => ({ "routes": { "__root__": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/__root.tsx", "children": ["/", "/api/health", "/api/logs", "/api/models", "/api/providers", "/api/sessions", "/proxy/$", "/api/config/paths"], "preloads": ["/assets/main-CpIX1ZHy.js"], "assets": [] }, "/": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-Bf_WGooQ.js"] }, "/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/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/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/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" } }, "clientEntry": "/assets/main-CpIX1ZHy.js" });
1
+ const tsrStartManifest = () => ({ "routes": { "__root__": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/__root.tsx", "children": ["/", "/api/health", "/api/logs", "/api/models", "/api/providers", "/api/sessions", "/proxy/$", "/api/config/paths"], "preloads": ["/assets/main-CC-HN8LQ.js"], "assets": [] }, "/": { "filePath": "C:/Users/claw/workspace/llm-inspector/src/routes/index.tsx", "assets": [], "preloads": ["/assets/index-BkNsdDqV.js"] }, "/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/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/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/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" } }, "clientEntry": "/assets/main-CC-HN8LQ.js" });
2
2
  export {
3
3
  tsrStartManifest
4
4
  };
@@ -100,51 +100,51 @@ const assets = {
100
100
  "/assets/alibaba-TTwafVwX.svg": {
101
101
  "type": "image/svg+xml",
102
102
  "etag": '"171b-6dyV5K8QjiaY35sN9qNprh9zDIs"',
103
- "mtime": "2026-06-03T09:45:53.063Z",
103
+ "mtime": "2026-06-03T11:03:37.187Z",
104
104
  "size": 5915,
105
105
  "path": "../public/assets/alibaba-TTwafVwX.svg"
106
106
  },
107
+ "/assets/zhipuai-BPNAnxo-.svg": {
108
+ "type": "image/svg+xml",
109
+ "etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
110
+ "mtime": "2026-06-03T11:03:37.189Z",
111
+ "size": 11256,
112
+ "path": "../public/assets/zhipuai-BPNAnxo-.svg"
113
+ },
107
114
  "/assets/minimax-BPMzvuL-.jpeg": {
108
115
  "type": "image/jpeg",
109
116
  "etag": '"1b06-IwivU89ko5UTMUM1/t7hn4sQK9A"',
110
- "mtime": "2026-06-03T09:45:53.065Z",
117
+ "mtime": "2026-06-03T11:03:37.187Z",
111
118
  "size": 6918,
112
119
  "path": "../public/assets/minimax-BPMzvuL-.jpeg"
113
120
  },
121
+ "/assets/main-CC-HN8LQ.js": {
122
+ "type": "text/javascript; charset=utf-8",
123
+ "etag": '"4db57-dDRKN7pAYlQliYE7orCiiIlfW1M"',
124
+ "mtime": "2026-06-03T11:03:37.189Z",
125
+ "size": 318295,
126
+ "path": "../public/assets/main-CC-HN8LQ.js"
127
+ },
114
128
  "/assets/index-B3RwBPLW.css": {
115
129
  "type": "text/css; charset=utf-8",
116
130
  "etag": '"10c74-aXacU4DRFVsUwcC5jHnjoPRSlTA"',
117
- "mtime": "2026-06-03T09:45:53.063Z",
131
+ "mtime": "2026-06-03T11:03:37.189Z",
118
132
  "size": 68724,
119
133
  "path": "../public/assets/index-B3RwBPLW.css"
120
134
  },
121
- "/assets/zhipuai-BPNAnxo-.svg": {
122
- "type": "image/svg+xml",
123
- "etag": '"2bf8-hNaLCTi89nOFCsIIfWpP/jrfo0s"',
124
- "mtime": "2026-06-03T09:45:53.063Z",
125
- "size": 11256,
126
- "path": "../public/assets/zhipuai-BPNAnxo-.svg"
127
- },
128
135
  "/assets/qwen-CONDcHqt.png": {
129
136
  "type": "image/png",
130
137
  "etag": '"572c3-cdJAPaHdOvFCGzuaQjagdgOu6XE"',
131
- "mtime": "2026-06-03T09:45:53.065Z",
138
+ "mtime": "2026-06-03T11:03:37.189Z",
132
139
  "size": 357059,
133
140
  "path": "../public/assets/qwen-CONDcHqt.png"
134
141
  },
135
- "/assets/main-CpIX1ZHy.js": {
136
- "type": "text/javascript; charset=utf-8",
137
- "etag": '"4db57-PIyiLXQGvlFJuizUFXRhGOYXJwY"',
138
- "mtime": "2026-06-03T09:45:53.065Z",
139
- "size": 318295,
140
- "path": "../public/assets/main-CpIX1ZHy.js"
141
- },
142
- "/assets/index-Bf_WGooQ.js": {
142
+ "/assets/index-BkNsdDqV.js": {
143
143
  "type": "text/javascript; charset=utf-8",
144
- "etag": '"831df-gmdpd1CCnM4IdaxHIs9uyMgWFaY"',
145
- "mtime": "2026-06-03T09:45:53.065Z",
146
- "size": 537055,
147
- "path": "../public/assets/index-Bf_WGooQ.js"
144
+ "etag": '"83205-a3v/UM6yJRUCSO9lDIeVoj7vJXg"',
145
+ "mtime": "2026-06-03T11:03:37.189Z",
146
+ "size": 537093,
147
+ "path": "../public/assets/index-BkNsdDqV.js"
148
148
  }
149
149
  };
150
150
  function readAsset(id) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tonyclaw/llm-inspector",
3
- "version": "1.7.3",
3
+ "version": "1.7.5",
4
4
  "type": "module",
5
5
  "description": "LLM API proxy inspector — captures and displays requests/responses from AI coding tools in a web UI",
6
6
  "license": "MIT",
package/src/cli.ts CHANGED
@@ -16,6 +16,8 @@ const portDefault = envPort !== undefined ? Number(envPort) : DEFAULT_PORT;
16
16
  const args = process.argv.slice(2);
17
17
  let port = portDefault;
18
18
  let open = true;
19
+ let configDir: string | undefined;
20
+ let providersJson: string | undefined;
19
21
 
20
22
  for (let i = 0; i < args.length; i++) {
21
23
  const arg = args[i] ?? "";
@@ -31,6 +33,14 @@ for (let i = 0; i < args.length; i++) {
31
33
  case "--open":
32
34
  open = true;
33
35
  break;
36
+ case "--config-dir":
37
+ configDir = args[i + 1];
38
+ i++;
39
+ break;
40
+ case "--providers":
41
+ providersJson = args[i + 1];
42
+ i++;
43
+ break;
34
44
  default:
35
45
  break;
36
46
  }
@@ -157,10 +167,23 @@ const outputDir = __dirname;
157
167
  const serverPath = join(outputDir, "../.output/server/index.mjs");
158
168
 
159
169
  // Start server with node
170
+ const serverEnv = { ...process.env };
171
+ if (configDir !== undefined) {
172
+ // Convert MSYS/Git Bash path like /c/Users/... to Windows absolute path
173
+ let resolvedPath = join(configDir, "config.json");
174
+ // Convert /c/... to C:\... format
175
+ if (resolvedPath.startsWith("\\c\\")) {
176
+ resolvedPath = "C:" + resolvedPath;
177
+ }
178
+ serverEnv["LLM_INSPECTOR_CONFIG_PATH"] = resolvedPath;
179
+ }
180
+ if (providersJson !== undefined) {
181
+ serverEnv["LLM_INSPECTOR_PROVIDERS_JSON"] = providersJson;
182
+ }
160
183
  const serverProcess = spawn(process.execPath, [serverPath], {
161
184
  stdio: ["ignore", "inherit", "inherit"],
162
185
  detached: true,
163
- env: { ...process.env },
186
+ env: serverEnv,
164
187
  });
165
188
 
166
189
  serverProcess.unref();
@@ -31,8 +31,9 @@ const PROVIDER_MAP: Record<string, Provider> = {
31
31
 
32
32
  export function detectProvider(model: string | null): Provider {
33
33
  if (model === null) return "unknown";
34
+ const modelLower = model.toLowerCase();
34
35
  for (const [prefix, provider] of Object.entries(PROVIDER_MAP)) {
35
- if (model.startsWith(prefix)) {
36
+ if (modelLower.startsWith(prefix.toLowerCase())) {
36
37
  return provider;
37
38
  }
38
39
  }