@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.
Files changed (30) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/assets/{index-DH3FOgcK.js → index-BmEH5jeO.js} +18 -18
  3. package/.output/public/assets/{index-BLVa7n9b.css → index-DdJSLfxK.css} +1 -1
  4. package/.output/public/assets/{main-Beo3LJDa.js → main-GVpFMVGE.js} +1 -1
  5. package/.output/server/_ssr/{index-HkueJ4Un.mjs → index-D0d6QxPt.mjs} +85 -31
  6. package/.output/server/_ssr/index.mjs +2 -2
  7. package/.output/server/_ssr/{router-DTswxb7l.mjs → router-D9uLXa9A.mjs} +287 -67
  8. package/.output/server/{_tanstack-start-manifest_v-DhUuivt-.mjs → _tanstack-start-manifest_v-ByfnNZV_.mjs} +1 -1
  9. package/.output/server/index.mjs +26 -26
  10. package/README.md +8 -209
  11. package/package.json +1 -1
  12. package/src/components/ProxyViewerContainer.tsx +10 -1
  13. package/src/components/providers/ProviderCard.tsx +19 -15
  14. package/src/components/providers/ProviderForm.tsx +21 -0
  15. package/src/components/proxy-viewer/LogEntry.tsx +7 -0
  16. package/src/components/proxy-viewer/ResponseView.tsx +32 -6
  17. package/src/components/proxy-viewer/StreamingChunkSequence.tsx +12 -3
  18. package/src/proxy/chunkStorage.ts +4 -6
  19. package/src/proxy/formats/anthropic/schemas.ts +9 -0
  20. package/src/proxy/formats/anthropic/stream.ts +11 -0
  21. package/src/proxy/formats/openai/stream.ts +15 -0
  22. package/src/proxy/handler.ts +44 -27
  23. package/src/proxy/logIndex.ts +73 -7
  24. package/src/proxy/logger.ts +62 -6
  25. package/src/proxy/providers.ts +5 -0
  26. package/src/proxy/schemas.ts +2 -0
  27. package/src/proxy/socketTracker.ts +90 -36
  28. package/src/proxy/store.ts +32 -18
  29. package/src/routes/api/providers.$providerId.ts +1 -0
  30. package/src/routes/api/providers.ts +2 -0
@@ -1,9 +1,9 @@
1
1
  import { z } from "zod";
2
- import { readFileSync, existsSync } from "node:fs";
3
- import { createReadStream } from "node:fs";
2
+ import { readFileSync, existsSync, createReadStream } from "node:fs";
3
+ import { readdir as readdirCallback } from "node:fs/promises";
4
4
  import { createInterface } from "node:readline";
5
5
  import { join } from "node:path";
6
- import { appendLogEntry, resolveLogDir } from "./logger";
6
+ import { appendLogEntry, resolveLogDir, logger } from "./logger";
7
7
  import {
8
8
  addToIndex,
9
9
  findInIndex,
@@ -258,18 +258,16 @@ export async function getLogById(id: number): Promise<CapturedLog | null> {
258
258
  // Fallback: return the last matching entry (incomplete is better than nothing)
259
259
  if (lastMatch !== null) return lastMatch;
260
260
  } catch (err) {
261
- // eslint-disable-next-line no-console
262
- console.error("[store] Failed to read log from disk:", err);
261
+ logger.error("[store] Failed to read log from disk:", String(err));
263
262
  }
264
263
 
265
264
  return null;
266
265
  }
267
266
 
268
267
  export function getFilteredLogs(sessionId?: string, model?: string): CapturedLog[] {
269
- // Get all logs from memory cache sorted by ID
270
- const cachedLogs = Array.from(memoryCache.values()).sort((a, b) => a.id - b.id);
271
-
272
- return cachedLogs.filter((l) => {
268
+ // Cache maintains insertion order (sorted by ID since logs are added in ID order)
269
+ // Use spread instead of Array.from for slightly better performance
270
+ return [...memoryCache.values()].filter((l) => {
273
271
  if (sessionId !== undefined && l.sessionId !== sessionId) return false;
274
272
  if (model !== undefined && l.model !== model) return false;
275
273
  return true;
@@ -293,20 +291,32 @@ export function getModels(): string[] {
293
291
  }
294
292
 
295
293
  /**
296
- * Load recent completed log entries from today's log file into the memory cache.
294
+ * Load recent completed log entries from all log files into the memory cache.
297
295
  * Called on server startup so the frontend shows existing logs.
298
296
  */
299
297
  export async function loadLogsIntoMemory(): Promise<void> {
300
298
  const logDir = resolveLogDir();
301
299
  if (!existsSync(logDir)) return;
302
300
 
303
- const today = getCurrentLogFile();
304
- const filePath = join(logDir, today);
305
- if (!existsSync(filePath)) return;
306
-
307
301
  // Keep track of IDs seen so we only load the completed entry for each
308
302
  const seenComplete = new Set<number>();
309
303
 
304
+ // Get all .jsonl files sorted by name (date)
305
+ try {
306
+ const entries = await readdirCallback(logDir);
307
+ const filesWithExt = entries.filter((f: string) => f.endsWith(".jsonl")).sort();
308
+ for (const file of filesWithExt) {
309
+ const filePath = join(logDir, file);
310
+ await loadLogFile(filePath, seenComplete);
311
+ }
312
+ } catch (err) {
313
+ logger.error("[store] Failed to read log directory:", String(err));
314
+ }
315
+ }
316
+
317
+ async function loadLogFile(filePath: string, seenComplete: Set<number>): Promise<void> {
318
+ if (!existsSync(filePath)) return;
319
+
310
320
  try {
311
321
  const fileStream = createInterface({
312
322
  input: createReadStream(filePath),
@@ -348,8 +358,7 @@ export async function loadLogsIntoMemory(): Promise<void> {
348
358
  }
349
359
  }
350
360
  } catch (err) {
351
- // eslint-disable-next-line no-console
352
- console.error("[store] Failed to load logs into memory:", err);
361
+ logger.error("[store] Failed to load logs into memory:", String(err));
353
362
  }
354
363
  }
355
364
 
@@ -375,12 +384,17 @@ export function onLogUpdate(handler: LogUpdateHandler): () => void {
375
384
  }
376
385
 
377
386
  export function emitLogUpdate(log: CapturedLog): void {
387
+ const failedHandlers: LogUpdateHandler[] = [];
378
388
  for (const handler of sseHandlers) {
379
389
  try {
380
390
  handler(log);
381
391
  } catch {
382
- // Remove handler that threw
383
- sseHandlers.delete(handler);
392
+ // Collect failed handlers to remove after iteration
393
+ failedHandlers.push(handler);
384
394
  }
385
395
  }
396
+ // Remove failed handlers after iteration to avoid Set mutation during for...of
397
+ for (const handler of failedHandlers) {
398
+ sseHandlers.delete(handler);
399
+ }
386
400
  }
@@ -11,6 +11,7 @@ const ProviderUpdateSchema = z.object({
11
11
  authHeader: z.enum(["bearer", "x-api-key"]).optional(),
12
12
  anthropicBaseUrl: z.string().optional(),
13
13
  openaiBaseUrl: z.string().optional(),
14
+ apiDocsUrl: z.string().optional(),
14
15
  });
15
16
 
16
17
  export const Route = createFileRoute("/api/providers/$providerId")({
@@ -9,6 +9,7 @@ const ProviderInputSchema = z.object({
9
9
  baseUrl: z.string().min(1, "Base URL is required"),
10
10
  model: z.string().min(1, "Model is required"),
11
11
  authHeader: z.enum(["bearer", "x-api-key"]).optional().default("bearer"),
12
+ apiDocsUrl: z.string().optional(),
12
13
  });
13
14
 
14
15
  export const Route = createFileRoute("/api/providers")({
@@ -29,6 +30,7 @@ export const Route = createFileRoute("/api/providers")({
29
30
  parsed.data.baseUrl,
30
31
  parsed.data.model,
31
32
  parsed.data.authHeader,
33
+ parsed.data.apiDocsUrl,
32
34
  );
33
35
  return Response.json(newProvider, { status: 201 });
34
36
  },