@tonyclaw/llm-inspector 1.15.1 → 1.16.0
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/cli.js +1 -0
- package/.output/nitro.json +1 -1
- package/.output/public/assets/index-BmkN9DxE.js +107 -0
- package/.output/public/assets/index-DPe3eOih.css +1 -0
- package/.output/public/assets/{main-C2-qvdhC.js → main-BjnjXVBU.js} +1 -1
- package/.output/server/_ssr/{index-C001qcnM.mjs → index-BIOEVAzU.mjs} +216 -180
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{router-D7aEu4dR.mjs → router-THS9ptvu.mjs} +392 -170
- package/.output/server/{_tanstack-start-manifest_v-BXMwlSXD.mjs → _tanstack-start-manifest_v-BYhN7q_z.mjs} +1 -1
- package/.output/server/index.mjs +27 -27
- package/package.json +1 -1
- package/src/cli.ts +1 -0
- package/src/components/ProxyViewer.tsx +16 -54
- package/src/components/ProxyViewerContainer.tsx +121 -77
- package/src/components/providers/ImportWizardDialog.tsx +27 -3
- package/src/components/proxy-viewer/ConversationGroup.tsx +3 -3
- package/src/components/proxy-viewer/ConversationHeader.tsx +1 -1
- package/src/components/proxy-viewer/LogEntry.tsx +184 -171
- package/src/components/proxy-viewer/LogEntryHeader.tsx +126 -137
- package/src/components/proxy-viewer/ResponseView.tsx +3 -2
- package/src/components/proxy-viewer/StreamingChunkSequence.tsx +3 -3
- package/src/components/proxy-viewer/TurnGroup.tsx +4 -4
- package/src/components/proxy-viewer/diff/DiffView.tsx +5 -3
- package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +13 -9
- package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +3 -3
- package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +7 -3
- package/src/components/ui/json-viewer.tsx +3 -3
- package/src/lib/objectUtils.ts +22 -0
- package/src/proxy/claudeCodeStrip.ts +5 -8
- package/src/proxy/logIndex.ts +58 -43
- package/src/proxy/logger.ts +51 -27
- package/src/proxy/openaiOrphanToolStrip.ts +11 -17
- package/src/proxy/providerImporters.ts +245 -19
- package/src/proxy/providers.ts +20 -7
- package/src/proxy/schemas.ts +5 -9
- package/src/proxy/socketTracker.ts +109 -78
- package/src/proxy/store.ts +52 -83
- package/.output/public/assets/index-BIUbzgJN.css +0 -1
- package/.output/public/assets/index-TwYgzIL4.js +0 -107
|
@@ -209,6 +209,7 @@ export const LogEntry = memo(function ({
|
|
|
209
209
|
const [replayOpen, setReplayOpen] = useState<boolean>(false);
|
|
210
210
|
const [headersDiff, setHeadersDiff] = useState<boolean>(false);
|
|
211
211
|
const [requestDiff, setRequestDiff] = useState<boolean>(false);
|
|
212
|
+
const [activeTab, setActiveTab] = useState("request");
|
|
212
213
|
const resolvedFormat = resolveLogFormat(log);
|
|
213
214
|
const adapter = getLogFormatAdapter(resolvedFormat);
|
|
214
215
|
const requestAnalysis = useMemo(
|
|
@@ -232,7 +233,7 @@ export const LogEntry = memo(function ({
|
|
|
232
233
|
|
|
233
234
|
return (
|
|
234
235
|
<TooltipProvider>
|
|
235
|
-
<div className="border border-border rounded-lg mb-1
|
|
236
|
+
<div className="border border-border rounded-lg mb-1 overflow-hidden">
|
|
236
237
|
<LogEntryHeader
|
|
237
238
|
log={log}
|
|
238
239
|
messageCount={requestAnalysis.messageCount}
|
|
@@ -245,7 +246,7 @@ export const LogEntry = memo(function ({
|
|
|
245
246
|
|
|
246
247
|
{expanded && (
|
|
247
248
|
<div onClick={(e) => e.stopPropagation()} onKeyDown={(e) => e.stopPropagation()}>
|
|
248
|
-
<Tabs
|
|
249
|
+
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
|
249
250
|
<TabsList className="mx-4 mt-2">
|
|
250
251
|
{viewMode === "full" && (
|
|
251
252
|
<Tooltip>
|
|
@@ -289,42 +290,65 @@ export const LogEntry = memo(function ({
|
|
|
289
290
|
|
|
290
291
|
{shouldShowRawRequestTab(resolvedFormat, viewMode, strip) && (
|
|
291
292
|
<TabsContent value="raw-request">
|
|
292
|
-
|
|
293
|
-
<div className="
|
|
294
|
-
<
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
293
|
+
{activeTab === "raw-request" && (
|
|
294
|
+
<div className="px-4 py-3">
|
|
295
|
+
<div className="flex justify-end mb-2">
|
|
296
|
+
<CopyButton
|
|
297
|
+
text={log.rawRequestBody}
|
|
298
|
+
label="Copy Raw Request"
|
|
299
|
+
copied={rawRequestCopy.copied}
|
|
300
|
+
onCopy={rawRequestCopy.copy}
|
|
301
|
+
/>
|
|
302
|
+
</div>
|
|
303
|
+
{log.rawRequestBody !== null ? (
|
|
304
|
+
<JsonViewerFromString text={log.rawRequestBody} defaultExpandDepth={1} />
|
|
305
|
+
) : (
|
|
306
|
+
<p className="text-xs text-muted-foreground italic">No request body</p>
|
|
307
|
+
)}
|
|
300
308
|
</div>
|
|
301
|
-
|
|
302
|
-
<JsonViewerFromString text={log.rawRequestBody} defaultExpandDepth={1} />
|
|
303
|
-
) : (
|
|
304
|
-
<p className="text-xs text-muted-foreground italic">No request body</p>
|
|
305
|
-
)}
|
|
306
|
-
</div>
|
|
309
|
+
)}
|
|
307
310
|
</TabsContent>
|
|
308
311
|
)}
|
|
309
312
|
|
|
310
313
|
<TabsContent value="request">
|
|
311
|
-
|
|
312
|
-
<div className="
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
e
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
314
|
+
{activeTab === "request" && (
|
|
315
|
+
<div className="px-4 py-3">
|
|
316
|
+
<div className="flex justify-end gap-2 mb-2">
|
|
317
|
+
{shouldShowRequestDiffButton(
|
|
318
|
+
resolvedFormat,
|
|
319
|
+
viewMode,
|
|
320
|
+
strip,
|
|
321
|
+
log.rawRequestBody !== null,
|
|
322
|
+
) && (
|
|
323
|
+
<DiffToggleButton
|
|
324
|
+
active={requestDiff}
|
|
325
|
+
onClick={(e) => {
|
|
326
|
+
e.stopPropagation();
|
|
327
|
+
setRequestDiff(!requestDiff);
|
|
328
|
+
}}
|
|
329
|
+
/>
|
|
330
|
+
)}
|
|
331
|
+
{onCompareWithPrevious !== undefined && (
|
|
332
|
+
<Tooltip>
|
|
333
|
+
<TooltipTrigger asChild>
|
|
334
|
+
<Button
|
|
335
|
+
variant="outline"
|
|
336
|
+
size="sm"
|
|
337
|
+
className="h-7 text-xs"
|
|
338
|
+
onClick={(e) => {
|
|
339
|
+
e.stopPropagation();
|
|
340
|
+
onCompareWithPrevious(log);
|
|
341
|
+
}}
|
|
342
|
+
>
|
|
343
|
+
<GitCompareArrows className="size-3 mr-1" />
|
|
344
|
+
Diff with Previous
|
|
345
|
+
</Button>
|
|
346
|
+
</TooltipTrigger>
|
|
347
|
+
<TooltipContent>
|
|
348
|
+
Compare this request with the immediately preceding one
|
|
349
|
+
</TooltipContent>
|
|
350
|
+
</Tooltip>
|
|
351
|
+
)}
|
|
328
352
|
<Tooltip>
|
|
329
353
|
<TooltipTrigger asChild>
|
|
330
354
|
<Button
|
|
@@ -333,172 +357,161 @@ export const LogEntry = memo(function ({
|
|
|
333
357
|
className="h-7 text-xs"
|
|
334
358
|
onClick={(e) => {
|
|
335
359
|
e.stopPropagation();
|
|
336
|
-
|
|
360
|
+
setReplayOpen(true);
|
|
337
361
|
}}
|
|
338
362
|
>
|
|
339
|
-
<
|
|
340
|
-
|
|
363
|
+
<RotateCcw className="size-3 mr-1" />
|
|
364
|
+
Replay
|
|
341
365
|
</Button>
|
|
342
366
|
</TooltipTrigger>
|
|
343
|
-
<TooltipContent>
|
|
344
|
-
Compare this request with the immediately preceding one
|
|
345
|
-
</TooltipContent>
|
|
367
|
+
<TooltipContent>Re-send this request to the provider</TooltipContent>
|
|
346
368
|
</Tooltip>
|
|
369
|
+
<CopyButton
|
|
370
|
+
text={displayedRequestBody}
|
|
371
|
+
label="Copy Request"
|
|
372
|
+
copied={requestCopy.copied}
|
|
373
|
+
onCopy={requestCopy.copy}
|
|
374
|
+
/>
|
|
375
|
+
</div>
|
|
376
|
+
{requestDiff ? (
|
|
377
|
+
<RequestDiffContent
|
|
378
|
+
rawBody={log.rawRequestBody}
|
|
379
|
+
displayedBody={displayedRequestBody}
|
|
380
|
+
emptyLabel="No transformation applied — raw and sent request bodies are identical."
|
|
381
|
+
/>
|
|
382
|
+
) : displayedRequestBody !== null ? (
|
|
383
|
+
<JsonViewerFromString text={displayedRequestBody} defaultExpandDepth={1} />
|
|
384
|
+
) : (
|
|
385
|
+
<p className="text-xs text-muted-foreground italic">No request body</p>
|
|
347
386
|
)}
|
|
348
|
-
<Tooltip>
|
|
349
|
-
<TooltipTrigger asChild>
|
|
350
|
-
<Button
|
|
351
|
-
variant="outline"
|
|
352
|
-
size="sm"
|
|
353
|
-
className="h-7 text-xs"
|
|
354
|
-
onClick={(e) => {
|
|
355
|
-
e.stopPropagation();
|
|
356
|
-
setReplayOpen(true);
|
|
357
|
-
}}
|
|
358
|
-
>
|
|
359
|
-
<RotateCcw className="size-3 mr-1" />
|
|
360
|
-
Replay
|
|
361
|
-
</Button>
|
|
362
|
-
</TooltipTrigger>
|
|
363
|
-
<TooltipContent>Re-send this request to the provider</TooltipContent>
|
|
364
|
-
</Tooltip>
|
|
365
|
-
<CopyButton
|
|
366
|
-
text={displayedRequestBody}
|
|
367
|
-
label="Copy Request"
|
|
368
|
-
copied={requestCopy.copied}
|
|
369
|
-
onCopy={requestCopy.copy}
|
|
370
|
-
/>
|
|
371
387
|
</div>
|
|
372
|
-
|
|
373
|
-
<RequestDiffContent
|
|
374
|
-
rawBody={log.rawRequestBody}
|
|
375
|
-
displayedBody={displayedRequestBody}
|
|
376
|
-
emptyLabel="No transformation applied — raw and sent request bodies are identical."
|
|
377
|
-
/>
|
|
378
|
-
) : displayedRequestBody !== null ? (
|
|
379
|
-
<JsonViewerFromString text={displayedRequestBody} defaultExpandDepth={1} />
|
|
380
|
-
) : (
|
|
381
|
-
<p className="text-xs text-muted-foreground italic">No request body</p>
|
|
382
|
-
)}
|
|
383
|
-
</div>
|
|
388
|
+
)}
|
|
384
389
|
</TabsContent>
|
|
385
390
|
|
|
386
391
|
{viewMode === "full" && (
|
|
387
392
|
<TabsContent value="headers">
|
|
388
|
-
|
|
389
|
-
<div className="
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
e
|
|
398
|
-
|
|
399
|
-
|
|
393
|
+
{activeTab === "headers" && (
|
|
394
|
+
<div className="px-4 py-3">
|
|
395
|
+
<div className="flex justify-end gap-2 mb-2">
|
|
396
|
+
{shouldShowHeadersDiffButton(
|
|
397
|
+
viewMode,
|
|
398
|
+
log.rawHeaders !== undefined && Object.keys(log.rawHeaders).length > 0,
|
|
399
|
+
) && (
|
|
400
|
+
<DiffToggleButton
|
|
401
|
+
active={headersDiff}
|
|
402
|
+
onClick={(e) => {
|
|
403
|
+
e.stopPropagation();
|
|
404
|
+
setHeadersDiff(!headersDiff);
|
|
405
|
+
}}
|
|
406
|
+
/>
|
|
407
|
+
)}
|
|
408
|
+
</div>
|
|
409
|
+
{headersDiff ? (
|
|
410
|
+
<HeadersDiffContent
|
|
411
|
+
rawHeaders={log.rawHeaders}
|
|
412
|
+
headers={log.headers}
|
|
413
|
+
emptyLabel="No transformation applied — raw and processed headers are identical."
|
|
400
414
|
/>
|
|
415
|
+
) : log.headers && Object.keys(log.headers).length > 0 ? (
|
|
416
|
+
<div className="space-y-1 font-mono text-xs">
|
|
417
|
+
{Object.entries(log.headers)
|
|
418
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
419
|
+
.map(([key, value]) => (
|
|
420
|
+
<div key={key} className="flex gap-2">
|
|
421
|
+
<span className="text-blue-600 dark:text-blue-400 font-semibold shrink-0">
|
|
422
|
+
{key}:
|
|
423
|
+
</span>
|
|
424
|
+
<span className="text-muted-foreground truncate" title={value}>
|
|
425
|
+
{value}
|
|
426
|
+
</span>
|
|
427
|
+
</div>
|
|
428
|
+
))}
|
|
429
|
+
</div>
|
|
430
|
+
) : (
|
|
431
|
+
<p className="text-xs text-muted-foreground italic">No headers captured</p>
|
|
401
432
|
)}
|
|
402
433
|
</div>
|
|
403
|
-
|
|
404
|
-
<HeadersDiffContent
|
|
405
|
-
rawHeaders={log.rawHeaders}
|
|
406
|
-
headers={log.headers}
|
|
407
|
-
emptyLabel="No transformation applied — raw and processed headers are identical."
|
|
408
|
-
/>
|
|
409
|
-
) : log.headers && Object.keys(log.headers).length > 0 ? (
|
|
410
|
-
<div className="space-y-1 font-mono text-xs">
|
|
411
|
-
{Object.entries(log.headers)
|
|
412
|
-
.sort(([a], [b]) => a.localeCompare(b))
|
|
413
|
-
.map(([key, value]) => (
|
|
414
|
-
<div key={key} className="flex gap-2">
|
|
415
|
-
<span className="text-blue-600 dark:text-blue-400 font-semibold shrink-0">
|
|
416
|
-
{key}:
|
|
417
|
-
</span>
|
|
418
|
-
<span className="text-muted-foreground truncate" title={value}>
|
|
419
|
-
{value}
|
|
420
|
-
</span>
|
|
421
|
-
</div>
|
|
422
|
-
))}
|
|
423
|
-
</div>
|
|
424
|
-
) : (
|
|
425
|
-
<p className="text-xs text-muted-foreground italic">No headers captured</p>
|
|
426
|
-
)}
|
|
427
|
-
</div>
|
|
434
|
+
)}
|
|
428
435
|
</TabsContent>
|
|
429
436
|
)}
|
|
430
437
|
|
|
431
438
|
{viewMode === "full" && (
|
|
432
439
|
<TabsContent value="raw-headers">
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
.
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
<
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
{value}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
440
|
+
{activeTab === "raw-headers" && (
|
|
441
|
+
<div className="px-4 py-3">
|
|
442
|
+
{log.rawHeaders && Object.keys(log.rawHeaders).length > 0 ? (
|
|
443
|
+
<div className="space-y-1 font-mono text-xs">
|
|
444
|
+
{Object.entries(log.rawHeaders)
|
|
445
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
446
|
+
.map(([key, value]) => (
|
|
447
|
+
<div key={key} className="flex gap-2">
|
|
448
|
+
<span className="text-blue-600 dark:text-blue-400 font-semibold shrink-0">
|
|
449
|
+
{key}:
|
|
450
|
+
</span>
|
|
451
|
+
<span className="text-muted-foreground truncate" title={value}>
|
|
452
|
+
{value}
|
|
453
|
+
</span>
|
|
454
|
+
</div>
|
|
455
|
+
))}
|
|
456
|
+
</div>
|
|
457
|
+
) : (
|
|
458
|
+
<p className="text-xs text-muted-foreground italic">
|
|
459
|
+
No raw headers captured
|
|
460
|
+
</p>
|
|
461
|
+
)}
|
|
462
|
+
</div>
|
|
463
|
+
)}
|
|
455
464
|
</TabsContent>
|
|
456
465
|
)}
|
|
457
466
|
|
|
458
467
|
<TabsContent value="raw">
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
<div className="
|
|
463
|
-
|
|
468
|
+
{activeTab === "raw" && (
|
|
469
|
+
<div className="px-4 py-3 space-y-3">
|
|
470
|
+
{log.error !== undefined && log.error !== null && (
|
|
471
|
+
<div className="rounded border border-destructive/50 bg-destructive/10 p-3 text-xs">
|
|
472
|
+
<div className="font-semibold text-destructive mb-1">SSE Error</div>
|
|
473
|
+
<div className="text-muted-foreground font-mono">{log.error}</div>
|
|
474
|
+
</div>
|
|
475
|
+
)}
|
|
476
|
+
<div className="flex justify-end">
|
|
477
|
+
<CopyButton
|
|
478
|
+
text={log.responseText}
|
|
479
|
+
label="Copy Response"
|
|
480
|
+
copied={responseCopy.copied}
|
|
481
|
+
onCopy={responseCopy.copy}
|
|
482
|
+
/>
|
|
464
483
|
</div>
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
text
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
484
|
+
{log.responseText !== null ? (
|
|
485
|
+
<JsonViewerFromString text={log.responseText} defaultExpandDepth={1} />
|
|
486
|
+
) : (
|
|
487
|
+
<p className="text-xs text-muted-foreground italic">No response</p>
|
|
488
|
+
)}
|
|
489
|
+
{log.streaming === true && (
|
|
490
|
+
<StreamingChunkSequence
|
|
491
|
+
logId={log.id}
|
|
492
|
+
truncated={log.streamingChunksPath !== null}
|
|
493
|
+
/>
|
|
494
|
+
)}
|
|
473
495
|
</div>
|
|
474
|
-
|
|
475
|
-
<JsonViewerFromString text={log.responseText} defaultExpandDepth={1} />
|
|
476
|
-
) : (
|
|
477
|
-
<p className="text-xs text-muted-foreground italic">No response</p>
|
|
478
|
-
)}
|
|
479
|
-
{log.streaming === true && (
|
|
480
|
-
<StreamingChunkSequence
|
|
481
|
-
logId={log.id}
|
|
482
|
-
truncated={log.streamingChunksPath !== null}
|
|
483
|
-
/>
|
|
484
|
-
)}
|
|
485
|
-
</div>
|
|
496
|
+
)}
|
|
486
497
|
</TabsContent>
|
|
487
498
|
|
|
488
499
|
<TabsContent value="parsed">
|
|
489
|
-
|
|
490
|
-
<
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
500
|
+
{activeTab === "parsed" && (
|
|
501
|
+
<div className="px-4 py-3">
|
|
502
|
+
<ResponseView
|
|
503
|
+
responseText={log.responseText}
|
|
504
|
+
responseStatus={log.responseStatus}
|
|
505
|
+
streaming={log.streaming}
|
|
506
|
+
inputTokens={log.inputTokens}
|
|
507
|
+
outputTokens={log.outputTokens}
|
|
508
|
+
cacheCreationInputTokens={log.cacheCreationInputTokens}
|
|
509
|
+
cacheReadInputTokens={log.cacheReadInputTokens}
|
|
510
|
+
apiFormat={resolvedFormat}
|
|
511
|
+
error={log.error}
|
|
512
|
+
/>
|
|
513
|
+
</div>
|
|
514
|
+
)}
|
|
502
515
|
</TabsContent>
|
|
503
516
|
</Tabs>
|
|
504
517
|
</div>
|