@tonyclaw/agent-inspector 3.0.4 → 3.0.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/{CompareDrawer-Bs2kzUSp.js → CompareDrawer-DsvO6QyJ.js} +1 -1
- package/.output/public/assets/ProxyViewerContainer-CfP-komD.js +106 -0
- package/.output/public/assets/{ReplayDialog-B6xy7pl5.js → ReplayDialog-CQlLX1Uo.js} +1 -1
- package/.output/public/assets/{RequestAnatomy-BSvSmpCO.js → RequestAnatomy-m1q-uVTl.js} +1 -1
- package/.output/public/assets/{ResponseView-BFnUva7Z.js → ResponseView-DuLLeYSh.js} +2 -2
- package/.output/public/assets/{StreamingChunkSequence-u7ZaJOqn.js → StreamingChunkSequence-CN4aRFBx.js} +1 -1
- package/.output/public/assets/{_sessionId-CH4nsMOA.js → _sessionId-CWP4pxOU.js} +1 -1
- package/.output/public/assets/index-0pt1umWP.css +1 -0
- package/.output/public/assets/index-DSUEOPbC.js +1 -0
- package/.output/public/assets/{index-B5HS6AxR.js → index-zfZUz67K.js} +1 -1
- package/.output/public/assets/{json-viewer-5Tj5vkqe.js → json-viewer-_4r1LBBE.js} +1 -1
- package/.output/public/assets/{main-ChxZWG8K.js → main-C_nEHNUM.js} +2 -2
- package/.output/server/_libs/lucide-react.mjs +292 -203
- package/.output/server/{_sessionId-B3r-VBdM.mjs → _sessionId-CTqCAmUF.mjs} +3 -3
- package/.output/server/_ssr/{CompareDrawer-DAYrKHKe.mjs → CompareDrawer-D6b6TZ-O.mjs} +3 -3
- package/.output/server/_ssr/{ProxyViewerContainer-CYmNmQK9.mjs → ProxyViewerContainer-Dp7Va9_T.mjs} +593 -148
- package/.output/server/_ssr/{ReplayDialog-CdKvdXxk.mjs → ReplayDialog-D_KWQ1GY.mjs} +4 -4
- package/.output/server/_ssr/{RequestAnatomy-BtmTUq7O.mjs → RequestAnatomy-CPubXjMX.mjs} +2 -2
- package/.output/server/_ssr/{ResponseView-DuD_z5hd.mjs → ResponseView-CsTiNoOE.mjs} +3 -3
- package/.output/server/_ssr/{StreamingChunkSequence-DpIV2wt_.mjs → StreamingChunkSequence-Bgbzt23U.mjs} +2 -2
- package/.output/server/_ssr/{index-B4_8vH8k.mjs → index-P1gSpuM5.mjs} +2 -2
- package/.output/server/_ssr/index.mjs +2 -2
- package/.output/server/_ssr/{json-viewer-gcNJEB7y.mjs → json-viewer-10ciVGnc.mjs} +3 -3
- package/.output/server/_ssr/{router-CYjIsElo.mjs → router-BJJ0VhsB.mjs} +362 -151
- package/.output/server/_tanstack-start-manifest_v-CltlYL3I.mjs +4 -0
- package/.output/server/index.mjs +75 -75
- package/package.json +1 -1
- package/src/components/ProxyViewer.tsx +19 -1
- package/src/components/ecosystem/AgentLabDialog.tsx +379 -0
- package/src/components/providers/ProviderCard.tsx +342 -206
- package/src/lib/ecosystemContract.ts +47 -0
- package/src/lib/stopReason.ts +4 -0
- package/src/routes/api/ecosystem.packages.ts +263 -0
- package/.output/public/assets/ProxyViewerContainer-BUnlrfiS.js +0 -106
- package/.output/public/assets/index-BqVL284D.js +0 -1
- package/.output/public/assets/index-DJW6qbnH.css +0 -1
- package/.output/server/_tanstack-start-manifest_v-BRdOxY6q.mjs +0 -4
|
@@ -319,6 +319,165 @@ function ProviderTestCoverage({ provider }: { provider: ProviderConfig }): JSX.E
|
|
|
319
319
|
);
|
|
320
320
|
}
|
|
321
321
|
|
|
322
|
+
type ProviderTestSummary = {
|
|
323
|
+
total: number;
|
|
324
|
+
passed: number;
|
|
325
|
+
failed: number;
|
|
326
|
+
testing: number;
|
|
327
|
+
};
|
|
328
|
+
|
|
329
|
+
function configuredFormatResults(
|
|
330
|
+
provider: ProviderConfig,
|
|
331
|
+
testResults: TestResults | undefined,
|
|
332
|
+
): ProviderFormatTestResults[] {
|
|
333
|
+
if (testResults === undefined) return [];
|
|
334
|
+
const results: ProviderFormatTestResults[] = [];
|
|
335
|
+
if (providerHasEndpoint(provider.anthropicBaseUrl)) {
|
|
336
|
+
results.push(testResults.anthropic);
|
|
337
|
+
}
|
|
338
|
+
if (providerHasEndpoint(provider.openaiBaseUrl)) {
|
|
339
|
+
results.push(testResults.openaiChat);
|
|
340
|
+
}
|
|
341
|
+
if (providerHasEndpoint(provider.openaiResponsesBaseUrl)) {
|
|
342
|
+
results.push(testResults.openaiResponses);
|
|
343
|
+
}
|
|
344
|
+
return results;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function summarizeTestResults(
|
|
348
|
+
provider: ProviderConfig,
|
|
349
|
+
testResults: TestResults | undefined,
|
|
350
|
+
): ProviderTestSummary {
|
|
351
|
+
const summary: ProviderTestSummary = { total: 0, passed: 0, failed: 0, testing: 0 };
|
|
352
|
+
for (const results of configuredFormatResults(provider, testResults)) {
|
|
353
|
+
for (const state of [results.nonStreaming, results.streaming]) {
|
|
354
|
+
if (isNotConfiguredState(state)) continue;
|
|
355
|
+
summary.total += 1;
|
|
356
|
+
if (hasSuccessField(state)) {
|
|
357
|
+
if (state.success) {
|
|
358
|
+
summary.passed += 1;
|
|
359
|
+
} else {
|
|
360
|
+
summary.failed += 1;
|
|
361
|
+
}
|
|
362
|
+
} else {
|
|
363
|
+
summary.testing += 1;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return summary;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function ProviderTestPill({
|
|
371
|
+
provider,
|
|
372
|
+
testResults,
|
|
373
|
+
isTesting,
|
|
374
|
+
}: {
|
|
375
|
+
provider: ProviderConfig;
|
|
376
|
+
testResults: TestResults | undefined;
|
|
377
|
+
isTesting: boolean;
|
|
378
|
+
}): JSX.Element {
|
|
379
|
+
const targets = providerTestTargets(provider);
|
|
380
|
+
const summary = summarizeTestResults(provider, testResults);
|
|
381
|
+
|
|
382
|
+
if (isTesting) {
|
|
383
|
+
return (
|
|
384
|
+
<span className="inline-flex items-center gap-1 rounded border border-cyan-300/25 bg-cyan-300/[0.08] px-2 py-1 text-xs text-cyan-100">
|
|
385
|
+
<RotateCw className="size-3 animate-spin" />
|
|
386
|
+
Testing
|
|
387
|
+
</span>
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (targets.length === 0) {
|
|
392
|
+
return (
|
|
393
|
+
<span className="rounded border border-border/70 bg-muted/20 px-2 py-1 text-xs text-muted-foreground">
|
|
394
|
+
No endpoint
|
|
395
|
+
</span>
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
if (testResults === undefined || summary.total === 0) {
|
|
400
|
+
return (
|
|
401
|
+
<span className="rounded border border-border/70 bg-muted/20 px-2 py-1 text-xs text-muted-foreground">
|
|
402
|
+
Not tested
|
|
403
|
+
</span>
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (summary.failed > 0) {
|
|
408
|
+
return (
|
|
409
|
+
<span className="inline-flex items-center gap-1 rounded border border-red-400/25 bg-red-500/[0.08] px-2 py-1 text-xs text-red-200">
|
|
410
|
+
<AlertCircle className="size-3" />
|
|
411
|
+
{summary.passed}/{summary.total} passed
|
|
412
|
+
</span>
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (summary.testing > 0 || summary.passed < summary.total) {
|
|
417
|
+
return (
|
|
418
|
+
<span className="rounded border border-amber-300/25 bg-amber-400/[0.08] px-2 py-1 text-xs text-amber-100">
|
|
419
|
+
Partial {summary.passed}/{summary.total}
|
|
420
|
+
</span>
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
return (
|
|
425
|
+
<span className="inline-flex items-center gap-1 rounded border border-cyan-300/25 bg-cyan-300/[0.08] px-2 py-1 text-xs text-cyan-100">
|
|
426
|
+
<CheckCircle className="size-3" />
|
|
427
|
+
OK {summary.passed}/{summary.total}
|
|
428
|
+
</span>
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function compactModelLabel(provider: ProviderConfig): string {
|
|
433
|
+
const models = provider.models ?? [];
|
|
434
|
+
if (models.length === 0) return "No models configured";
|
|
435
|
+
const visible = models.slice(0, 2).join(", ");
|
|
436
|
+
const hiddenCount = models.length - 2;
|
|
437
|
+
return hiddenCount > 0 ? `${visible} +${hiddenCount} more` : visible;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function ModelChip({ provider, model }: { provider: ProviderConfig; model: string }): JSX.Element {
|
|
441
|
+
const metadata = findProviderModelMetadata(provider, model);
|
|
442
|
+
const sourceLink = getModelSourceLink(metadata?.sourceUrl);
|
|
443
|
+
return (
|
|
444
|
+
<span className="inline-flex items-center gap-1 rounded bg-muted px-1.5 py-0.5 text-xs text-muted-foreground">
|
|
445
|
+
<span>{model}</span>
|
|
446
|
+
{metadata !== null && metadata.contextWindow !== undefined && (
|
|
447
|
+
<span className="font-mono text-foreground/80">
|
|
448
|
+
{formatContextWindowTokens(metadata.contextWindow)}
|
|
449
|
+
</span>
|
|
450
|
+
)}
|
|
451
|
+
{sourceLink !== null && (
|
|
452
|
+
<TooltipProvider>
|
|
453
|
+
<Tooltip>
|
|
454
|
+
<TooltipTrigger asChild>
|
|
455
|
+
<a
|
|
456
|
+
href={sourceLink.url}
|
|
457
|
+
target="_blank"
|
|
458
|
+
rel="noopener noreferrer"
|
|
459
|
+
className="inline-flex items-center gap-0.5 rounded border border-border/70 bg-background/70 px-1 font-mono text-[10px] leading-4 text-muted-foreground transition-colors hover:text-foreground"
|
|
460
|
+
aria-label={sourceLink.title}
|
|
461
|
+
>
|
|
462
|
+
{sourceLink.label}
|
|
463
|
+
</a>
|
|
464
|
+
</TooltipTrigger>
|
|
465
|
+
<TooltipContent>{sourceLink.title}</TooltipContent>
|
|
466
|
+
</Tooltip>
|
|
467
|
+
</TooltipProvider>
|
|
468
|
+
)}
|
|
469
|
+
</span>
|
|
470
|
+
);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function modelResultsLabel(testResults: TestResults | undefined): string | null {
|
|
474
|
+
const models = testResults?.models;
|
|
475
|
+
if (models === undefined) return null;
|
|
476
|
+
const count = Object.keys(models).length;
|
|
477
|
+
if (count === 0) return null;
|
|
478
|
+
return `${count} model${count === 1 ? "" : "s"} tested`;
|
|
479
|
+
}
|
|
480
|
+
|
|
322
481
|
function getErrorIcon(type: ErrorType): JSX.Element {
|
|
323
482
|
const iconProps = { className: "size-3", strokeWidth: 2 };
|
|
324
483
|
switch (type) {
|
|
@@ -520,6 +679,7 @@ export function ProviderCard({
|
|
|
520
679
|
}: ProviderCardProps): JSX.Element {
|
|
521
680
|
const [showApiKey, setShowApiKey] = useState(false);
|
|
522
681
|
const [copied, setCopied] = useState(false);
|
|
682
|
+
const [showDetails, setShowDetails] = useState(false);
|
|
523
683
|
const [showModelResults, setShowModelResults] = useState(false);
|
|
524
684
|
|
|
525
685
|
function handleCopy() {
|
|
@@ -530,7 +690,6 @@ export function ProviderCard({
|
|
|
530
690
|
});
|
|
531
691
|
}
|
|
532
692
|
|
|
533
|
-
// Get docs URL: use provider.apiDocsUrl if configured, otherwise check KNOWN_PROVIDER_DOCS
|
|
534
693
|
const docsUrl =
|
|
535
694
|
provider.apiDocsUrl ??
|
|
536
695
|
Object.entries(KNOWN_PROVIDER_DOCS).find(([keyword]) =>
|
|
@@ -540,6 +699,8 @@ export function ProviderCard({
|
|
|
540
699
|
const canRefreshMetadata =
|
|
541
700
|
provider.modelMetadataUrl !== undefined && provider.modelMetadataUrl.trim() !== "";
|
|
542
701
|
const metadataImportLabel = canRefreshMetadata ? "Refresh" : "Import defaults";
|
|
702
|
+
const isTestingValue = isTesting ?? false;
|
|
703
|
+
const testedModelsLabel = modelResultsLabel(testResults);
|
|
543
704
|
const providerSourceBadge =
|
|
544
705
|
provider.source === "company" ? (
|
|
545
706
|
<TooltipProvider>
|
|
@@ -567,90 +728,53 @@ export function ProviderCard({
|
|
|
567
728
|
|
|
568
729
|
return (
|
|
569
730
|
<div
|
|
570
|
-
className={`
|
|
731
|
+
className={`flex flex-col gap-3 rounded-[8px] border border-border bg-card p-4 shadow-sm transition-all duration-500 ${isHighlighted === true ? "ring-2 ring-primary shadow-md" : ""}`}
|
|
571
732
|
>
|
|
572
733
|
<div className="flex items-start justify-between gap-2">
|
|
573
|
-
<div className="
|
|
574
|
-
<
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
{
|
|
589
|
-
<
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
</
|
|
598
|
-
</
|
|
734
|
+
<div className="min-w-0 space-y-1">
|
|
735
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
736
|
+
<span className="truncate font-medium">{provider.name}</span>
|
|
737
|
+
{providerSourceBadge}
|
|
738
|
+
</div>
|
|
739
|
+
<div className="truncate text-xs text-muted-foreground">
|
|
740
|
+
{compactModelLabel(provider)}
|
|
741
|
+
</div>
|
|
742
|
+
</div>
|
|
743
|
+
<div className="flex shrink-0 items-center gap-2">
|
|
744
|
+
<ProviderTestPill
|
|
745
|
+
provider={provider}
|
|
746
|
+
testResults={testResults}
|
|
747
|
+
isTesting={isTestingValue}
|
|
748
|
+
/>
|
|
749
|
+
{docsUrl !== undefined && (
|
|
750
|
+
<a
|
|
751
|
+
href={docsUrl}
|
|
752
|
+
target="_blank"
|
|
753
|
+
rel="noopener noreferrer"
|
|
754
|
+
className="flex items-center gap-1 text-xs text-muted-foreground transition-colors hover:text-foreground"
|
|
755
|
+
title="View API documentation"
|
|
756
|
+
>
|
|
757
|
+
<ExternalLink className="size-3" />
|
|
758
|
+
<span className="sr-only">Docs</span>
|
|
759
|
+
</a>
|
|
599
760
|
)}
|
|
600
761
|
</div>
|
|
601
|
-
{docsUrl !== undefined && (
|
|
602
|
-
<a
|
|
603
|
-
href={docsUrl}
|
|
604
|
-
target="_blank"
|
|
605
|
-
rel="noopener noreferrer"
|
|
606
|
-
className="text-muted-foreground hover:text-foreground transition-colors flex items-center gap-1 text-xs"
|
|
607
|
-
title="View API documentation"
|
|
608
|
-
>
|
|
609
|
-
<ExternalLink className="size-3" />
|
|
610
|
-
<span className="sr-only">Docs</span>
|
|
611
|
-
</a>
|
|
612
|
-
)}
|
|
613
762
|
</div>
|
|
614
763
|
|
|
615
|
-
|
|
616
|
-
<
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
<span className="font-mono text-foreground/80">
|
|
628
|
-
{formatContextWindowTokens(metadata.contextWindow)}
|
|
629
|
-
</span>
|
|
630
|
-
)}
|
|
631
|
-
{sourceLink !== null && (
|
|
632
|
-
<TooltipProvider>
|
|
633
|
-
<Tooltip>
|
|
634
|
-
<TooltipTrigger asChild>
|
|
635
|
-
<a
|
|
636
|
-
href={sourceLink.url}
|
|
637
|
-
target="_blank"
|
|
638
|
-
rel="noopener noreferrer"
|
|
639
|
-
className="inline-flex items-center gap-0.5 rounded border border-border/70 bg-background/70 px-1 font-mono text-[10px] leading-4 text-muted-foreground transition-colors hover:text-foreground"
|
|
640
|
-
aria-label={sourceLink.title}
|
|
641
|
-
>
|
|
642
|
-
{sourceLink.label}
|
|
643
|
-
</a>
|
|
644
|
-
</TooltipTrigger>
|
|
645
|
-
<TooltipContent>{sourceLink.title}</TooltipContent>
|
|
646
|
-
</Tooltip>
|
|
647
|
-
</TooltipProvider>
|
|
648
|
-
)}
|
|
649
|
-
</span>
|
|
650
|
-
);
|
|
651
|
-
})}
|
|
764
|
+
<div className="flex flex-col gap-2 text-xs text-muted-foreground sm:flex-row sm:items-center sm:justify-between">
|
|
765
|
+
<ProviderTestCoverage provider={provider} />
|
|
766
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
767
|
+
<span>Key configured</span>
|
|
768
|
+
<span>{provider.authHeader === "x-api-key" ? "x-api-key" : "Bearer"}</span>
|
|
769
|
+
{testedModelsLabel !== null && <span>{testedModelsLabel}</span>}
|
|
770
|
+
{testResults?.testedAt !== undefined && (
|
|
771
|
+
<span className="inline-flex items-center gap-1">
|
|
772
|
+
<Clock className="size-3" />
|
|
773
|
+
{formatTimeAgo(testResults.testedAt)}
|
|
774
|
+
</span>
|
|
775
|
+
)}
|
|
652
776
|
</div>
|
|
653
|
-
|
|
777
|
+
</div>
|
|
654
778
|
|
|
655
779
|
{!hasContextMetadata && (
|
|
656
780
|
<div className="flex items-center justify-between gap-2 rounded-md border border-amber-500/25 bg-amber-500/6 px-2 py-1.5 text-xs text-muted-foreground">
|
|
@@ -682,144 +806,156 @@ export function ProviderCard({
|
|
|
682
806
|
</div>
|
|
683
807
|
)}
|
|
684
808
|
|
|
685
|
-
|
|
686
|
-
<
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
809
|
+
{showDetails && (
|
|
810
|
+
<div className="space-y-3 border-t border-border/60 pt-3">
|
|
811
|
+
<div className="flex items-center gap-2">
|
|
812
|
+
<code className="flex-1 truncate rounded bg-muted px-2 py-1 text-xs text-muted-foreground">
|
|
813
|
+
{showApiKey ? provider.apiKey : maskApiKey(provider.apiKey)}
|
|
814
|
+
</code>
|
|
815
|
+
<button
|
|
816
|
+
type="button"
|
|
817
|
+
onClick={() => setShowApiKey((s) => !s)}
|
|
818
|
+
className="p-1 text-muted-foreground transition-colors hover:text-foreground"
|
|
819
|
+
aria-label={showApiKey ? "Hide API key" : "Show API key"}
|
|
820
|
+
>
|
|
821
|
+
{showApiKey ? <EyeOff className="size-4" /> : <Eye className="size-4" />}
|
|
822
|
+
</button>
|
|
823
|
+
<button
|
|
824
|
+
type="button"
|
|
825
|
+
onClick={handleCopy}
|
|
826
|
+
className="p-1 text-muted-foreground transition-colors hover:text-foreground"
|
|
827
|
+
aria-label="Copy API key"
|
|
828
|
+
>
|
|
829
|
+
{copied ? <Check className="size-4 text-cyan-300" /> : <Copy className="size-4" />}
|
|
830
|
+
</button>
|
|
831
|
+
</div>
|
|
706
832
|
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
>
|
|
714
|
-
{testResults !== undefined && (
|
|
715
|
-
<ProviderFormatTestStatus results={testResults.anthropic} />
|
|
833
|
+
{provider.models !== undefined && provider.models.length > 0 && (
|
|
834
|
+
<div className="flex flex-wrap gap-1">
|
|
835
|
+
{provider.models.map((model) => (
|
|
836
|
+
<ModelChip key={model} provider={provider} model={model} />
|
|
837
|
+
))}
|
|
838
|
+
</div>
|
|
716
839
|
)}
|
|
717
|
-
</ProviderEndpointSummary>
|
|
718
|
-
)}
|
|
719
840
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
841
|
+
{providerHasEndpoint(provider.anthropicBaseUrl) && (
|
|
842
|
+
<ProviderEndpointSummary
|
|
843
|
+
label="Anthropic"
|
|
844
|
+
baseUrl={provider.anthropicBaseUrl}
|
|
845
|
+
endpoint={PATH_V1_MESSAGES}
|
|
846
|
+
providerName={provider.name}
|
|
847
|
+
>
|
|
848
|
+
{testResults !== undefined && (
|
|
849
|
+
<ProviderFormatTestStatus results={testResults.anthropic} />
|
|
850
|
+
)}
|
|
851
|
+
</ProviderEndpointSummary>
|
|
729
852
|
)}
|
|
730
|
-
</ProviderEndpointSummary>
|
|
731
|
-
)}
|
|
732
853
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
854
|
+
{providerHasEndpoint(provider.openaiBaseUrl) && (
|
|
855
|
+
<ProviderEndpointSummary
|
|
856
|
+
label="Chat"
|
|
857
|
+
baseUrl={provider.openaiBaseUrl}
|
|
858
|
+
endpoint={PATH_V1_CHAT_COMPLETIONS}
|
|
859
|
+
providerName={provider.name}
|
|
860
|
+
>
|
|
861
|
+
{testResults !== undefined && (
|
|
862
|
+
<ProviderFormatTestStatus results={testResults.openaiChat} />
|
|
863
|
+
)}
|
|
864
|
+
</ProviderEndpointSummary>
|
|
742
865
|
)}
|
|
743
|
-
</ProviderEndpointSummary>
|
|
744
|
-
)}
|
|
745
866
|
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
Chat
|
|
781
|
-
</th>
|
|
782
|
-
)}
|
|
783
|
-
{provider.openaiResponsesBaseUrl !== undefined &&
|
|
784
|
-
provider.openaiResponsesBaseUrl !== "" && (
|
|
785
|
-
<th className="text-left py-1 px-2 font-medium text-muted-foreground">
|
|
786
|
-
Responses
|
|
867
|
+
{providerHasEndpoint(provider.openaiResponsesBaseUrl) && (
|
|
868
|
+
<ProviderEndpointSummary
|
|
869
|
+
label="Responses"
|
|
870
|
+
baseUrl={provider.openaiResponsesBaseUrl}
|
|
871
|
+
endpoint={PATH_V1_RESPONSES}
|
|
872
|
+
providerName={provider.name}
|
|
873
|
+
>
|
|
874
|
+
{testResults !== undefined && (
|
|
875
|
+
<ProviderFormatTestStatus results={testResults.openaiResponses} />
|
|
876
|
+
)}
|
|
877
|
+
</ProviderEndpointSummary>
|
|
878
|
+
)}
|
|
879
|
+
|
|
880
|
+
{testResults?.models !== undefined && Object.keys(testResults.models).length > 0 && (
|
|
881
|
+
<div>
|
|
882
|
+
<button
|
|
883
|
+
type="button"
|
|
884
|
+
onClick={() => setShowModelResults((value) => !value)}
|
|
885
|
+
className="flex items-center gap-1 text-xs text-muted-foreground transition-colors hover:text-foreground"
|
|
886
|
+
>
|
|
887
|
+
{showModelResults ? (
|
|
888
|
+
<ChevronDown className="size-3" />
|
|
889
|
+
) : (
|
|
890
|
+
<ChevronRight className="size-3" />
|
|
891
|
+
)}
|
|
892
|
+
Model Test Results ({Object.keys(testResults.models).length})
|
|
893
|
+
</button>
|
|
894
|
+
{showModelResults && (
|
|
895
|
+
<div className="mt-2 overflow-x-auto">
|
|
896
|
+
<table className="w-full border-collapse text-xs">
|
|
897
|
+
<thead>
|
|
898
|
+
<tr className="border-b border-border">
|
|
899
|
+
<th className="px-2 py-1 text-left font-medium text-muted-foreground">
|
|
900
|
+
Model
|
|
787
901
|
</th>
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
<td className="py-1 px-2 align-top">
|
|
798
|
-
<ProviderFormatTestStatus results={modelResult.anthropic} />
|
|
799
|
-
</td>
|
|
902
|
+
{providerHasEndpoint(provider.anthropicBaseUrl) && (
|
|
903
|
+
<th className="px-2 py-1 text-left font-medium text-muted-foreground">
|
|
904
|
+
Anthropic
|
|
905
|
+
</th>
|
|
906
|
+
)}
|
|
907
|
+
{providerHasEndpoint(provider.openaiBaseUrl) && (
|
|
908
|
+
<th className="px-2 py-1 text-left font-medium text-muted-foreground">
|
|
909
|
+
Chat
|
|
910
|
+
</th>
|
|
800
911
|
)}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
)}
|
|
806
|
-
{provider.openaiResponsesBaseUrl !== undefined &&
|
|
807
|
-
provider.openaiResponsesBaseUrl !== "" && (
|
|
808
|
-
<td className="py-1 px-2 align-top">
|
|
809
|
-
<ProviderFormatTestStatus results={modelResult.openaiResponses} />
|
|
810
|
-
</td>
|
|
912
|
+
{providerHasEndpoint(provider.openaiResponsesBaseUrl) && (
|
|
913
|
+
<th className="px-2 py-1 text-left font-medium text-muted-foreground">
|
|
914
|
+
Responses
|
|
915
|
+
</th>
|
|
811
916
|
)}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
917
|
+
</tr>
|
|
918
|
+
</thead>
|
|
919
|
+
<tbody>
|
|
920
|
+
{Object.entries(testResults.models).map(([modelName, modelResult]) => (
|
|
921
|
+
<tr key={modelName} className="border-b border-border/50 last:border-0">
|
|
922
|
+
<td className="px-2 py-1 font-medium">{modelName}</td>
|
|
923
|
+
{providerHasEndpoint(provider.anthropicBaseUrl) && (
|
|
924
|
+
<td className="px-2 py-1 align-top">
|
|
925
|
+
<ProviderFormatTestStatus results={modelResult.anthropic} />
|
|
926
|
+
</td>
|
|
927
|
+
)}
|
|
928
|
+
{providerHasEndpoint(provider.openaiBaseUrl) && (
|
|
929
|
+
<td className="px-2 py-1 align-top">
|
|
930
|
+
<ProviderFormatTestStatus results={modelResult.openaiChat} />
|
|
931
|
+
</td>
|
|
932
|
+
)}
|
|
933
|
+
{providerHasEndpoint(provider.openaiResponsesBaseUrl) && (
|
|
934
|
+
<td className="px-2 py-1 align-top">
|
|
935
|
+
<ProviderFormatTestStatus results={modelResult.openaiResponses} />
|
|
936
|
+
</td>
|
|
937
|
+
)}
|
|
938
|
+
</tr>
|
|
939
|
+
))}
|
|
940
|
+
</tbody>
|
|
941
|
+
</table>
|
|
942
|
+
</div>
|
|
943
|
+
)}
|
|
816
944
|
</div>
|
|
817
945
|
)}
|
|
818
946
|
</div>
|
|
819
947
|
)}
|
|
820
948
|
|
|
821
|
-
<div className="flex flex-col gap-2 pt-1
|
|
822
|
-
<
|
|
949
|
+
<div className="flex flex-col gap-2 border-t pt-1 sm:flex-row sm:items-center sm:justify-between">
|
|
950
|
+
<button
|
|
951
|
+
type="button"
|
|
952
|
+
onClick={() => setShowDetails((value) => !value)}
|
|
953
|
+
className="inline-flex items-center gap-1 text-xs text-muted-foreground transition-colors hover:text-foreground"
|
|
954
|
+
aria-expanded={showDetails}
|
|
955
|
+
>
|
|
956
|
+
{showDetails ? <ChevronDown className="size-3" /> : <ChevronRight className="size-3" />}
|
|
957
|
+
Details
|
|
958
|
+
</button>
|
|
823
959
|
<div className="flex gap-2">
|
|
824
960
|
{onTest !== undefined && (
|
|
825
961
|
<Button
|
|
@@ -827,10 +963,10 @@ export function ProviderCard({
|
|
|
827
963
|
size="sm"
|
|
828
964
|
onClick={() => onTest(provider.id)}
|
|
829
965
|
className="text-xs h-7 gap-1"
|
|
830
|
-
disabled={
|
|
966
|
+
disabled={isTestingValue}
|
|
831
967
|
>
|
|
832
|
-
<RotateCw className={`size-3 ${
|
|
833
|
-
{
|
|
968
|
+
<RotateCw className={`size-3 ${isTestingValue ? "animate-spin" : ""}`} />
|
|
969
|
+
{isTestingValue
|
|
834
970
|
? testingTimeLeft !== undefined
|
|
835
971
|
? `Testing (${testingTimeLeft}s)`
|
|
836
972
|
: "Testing..."
|
|
@@ -842,7 +978,7 @@ export function ProviderCard({
|
|
|
842
978
|
size="sm"
|
|
843
979
|
onClick={() => onEdit(provider)}
|
|
844
980
|
className="text-xs h-7 gap-1"
|
|
845
|
-
disabled={
|
|
981
|
+
disabled={isTestingValue}
|
|
846
982
|
>
|
|
847
983
|
<Pencil className="size-3" />
|
|
848
984
|
Edit
|
|
@@ -852,7 +988,7 @@ export function ProviderCard({
|
|
|
852
988
|
size="sm"
|
|
853
989
|
onClick={() => onDelete(provider.id)}
|
|
854
990
|
className="text-xs h-7 gap-1 text-destructive hover:text-destructive"
|
|
855
|
-
disabled={
|
|
991
|
+
disabled={isTestingValue}
|
|
856
992
|
>
|
|
857
993
|
<Trash2 className="size-3" />
|
|
858
994
|
Delete
|