@tonyclaw/agent-inspector 4.0.1 → 4.0.3
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/backend/nitro.json +1 -1
- package/.output/cli.js +193 -110
- package/.output/server/_ssr/index.mjs +1 -1
- package/.output/server/_ssr/{router-CTHqmroT.mjs → router-ChILzhmz.mjs} +92 -4
- package/.output/server/index.mjs +1 -1
- package/.output/ui/assets/{CompareDrawer-L3n4J-Or.js → CompareDrawer-Co3cUF_I.js} +1 -1
- package/.output/ui/assets/{InspectorPet-D5o_3OZS.js → InspectorPet-jFcrL37X.js} +1 -1
- package/.output/ui/assets/{ProxyViewerContainer-Ocagx75S.js → ProxyViewerContainer-BPzt_VpM.js} +4 -4
- package/.output/ui/assets/{ReplayDialog-Bk7x1h-y.js → ReplayDialog-C6O_zuwI.js} +1 -1
- package/.output/ui/assets/{RequestAnatomy-U4lzL7Sc.js → RequestAnatomy-Ca6NvB7L.js} +1 -1
- package/.output/ui/assets/{ResponseView-LOWfvBLK.js → ResponseView-BzqKcveJ.js} +1 -1
- package/.output/ui/assets/{StreamingChunkSequence-DPfsrrXQ.js → StreamingChunkSequence-9pCZIsS_.js} +1 -1
- package/.output/ui/assets/{_sessionId-0MlWS-CO.js → _sessionId-BMP3KVGR.js} +1 -1
- package/.output/ui/assets/{_sessionId-OdX2Jvt1.js → _sessionId-y4eRoT1H.js} +1 -1
- package/.output/ui/assets/{index-Ea-c8hPg.js → index-CH5w-haG.js} +1 -1
- package/.output/ui/assets/{index-zVVS53Hd.js → index-CmocEhyU.js} +1 -1
- package/.output/ui/assets/{index-Du4qNOOt.js → index-DT8sMJ_n.js} +2 -2
- package/.output/ui/assets/{index-gvFj_9K6.js → index-bfsbIRZa.js} +1 -1
- package/.output/ui/assets/{json-viewer-C1WAMzg3.js → json-viewer-BfFOSDG5.js} +1 -1
- package/.output/ui/assets/{jszip.min-B8AKu2zm.js → jszip.min-BkbVkOsp.js} +1 -1
- package/.output/ui/index.html +1 -1
- package/package.json +5 -10
- package/src/assets/IDE/claudecode-color.svg +0 -1
- package/src/assets/IDE/codex-color.svg +0 -1
- package/src/assets/IDE/opencode.svg +0 -1
- package/src/assets/IDE/xiaomimimo.svg +0 -1
- package/src/assets/favicon.svg +0 -21
- package/src/assets/logos/alibaba.svg +0 -1
- package/src/assets/logos/anthropic.svg +0 -1
- package/src/assets/logos/claude-code.svg +0 -4
- package/src/assets/logos/deepseek.svg +0 -1
- package/src/assets/logos/minimax.jpeg +0 -0
- package/src/assets/logos/openai.svg +0 -1
- package/src/assets/logos/opencode.svg +0 -4
- package/src/assets/logos/qwen.webp +0 -0
- package/src/assets/logos/zhipuai.svg +0 -219
- package/src/backend/router.tsx +0 -36
- package/src/backend/routes/$.ts +0 -26
- package/src/backend/routes/__root.ts +0 -4
- package/src/backend/routes/api/-instances.ts +0 -7
- package/src/backend/routes/api/alerts.summary.ts +0 -28
- package/src/backend/routes/api/alerts.ts +0 -70
- package/src/backend/routes/api/config.paths.ts +0 -14
- package/src/backend/routes/api/config.ts +0 -73
- package/src/backend/routes/api/ecosystem.packages.$packageId.help.ts +0 -11
- package/src/backend/routes/api/ecosystem.packages.$packageId.install.ts +0 -11
- package/src/backend/routes/api/ecosystem.packages.$packageId.runner-presets.ts +0 -11
- package/src/backend/routes/api/ecosystem.packages.$packageId.upgrade.ts +0 -11
- package/src/backend/routes/api/ecosystem.packages.ts +0 -21
- package/src/backend/routes/api/ecosystem.recipes.$recipeId.run.ts +0 -11
- package/src/backend/routes/api/ecosystem.tasks.$taskId.ts +0 -16
- package/src/backend/routes/api/ecosystem.tasks.ts +0 -12
- package/src/backend/routes/api/groups.$groupId.evidence.ts +0 -55
- package/src/backend/routes/api/groups.$groupId.sessions.ts +0 -50
- package/src/backend/routes/api/groups.$groupId.ts +0 -93
- package/src/backend/routes/api/groups.ts +0 -79
- package/src/backend/routes/api/health.ts +0 -22
- package/src/backend/routes/api/instances.$name.connection.ts +0 -12
- package/src/backend/routes/api/instances.$name.restart.ts +0 -23
- package/src/backend/routes/api/instances.$name.start.ts +0 -18
- package/src/backend/routes/api/instances.$name.stop.ts +0 -23
- package/src/backend/routes/api/instances.$name.ts +0 -12
- package/src/backend/routes/api/instances.ts +0 -16
- package/src/backend/routes/api/knowledge.candidates.$candidateId.promote.ts +0 -32
- package/src/backend/routes/api/knowledge.candidates.$candidateId.ts +0 -50
- package/src/backend/routes/api/knowledge.candidates.ts +0 -10
- package/src/backend/routes/api/knowledge.project-context.ts +0 -29
- package/src/backend/routes/api/knowledge.search.ts +0 -62
- package/src/backend/routes/api/knowledge.sessions.$sessionId.candidates.ts +0 -26
- package/src/backend/routes/api/logs.$id.body.ts +0 -58
- package/src/backend/routes/api/logs.$id.chunks.ts +0 -37
- package/src/backend/routes/api/logs.$id.replay.ts +0 -297
- package/src/backend/routes/api/logs.$id.ts +0 -23
- package/src/backend/routes/api/logs.import.ts +0 -39
- package/src/backend/routes/api/logs.stream.ts +0 -174
- package/src/backend/routes/api/logs.ts +0 -214
- package/src/backend/routes/api/mcp.ts +0 -25
- package/src/backend/routes/api/models.ts +0 -10
- package/src/backend/routes/api/pi-agent.ts +0 -29
- package/src/backend/routes/api/providers.$providerId.model-metadata.ts +0 -173
- package/src/backend/routes/api/providers.$providerId.test.log.ts +0 -342
- package/src/backend/routes/api/providers.$providerId.ts +0 -111
- package/src/backend/routes/api/providers.export.ts +0 -44
- package/src/backend/routes/api/providers.import.ts +0 -81
- package/src/backend/routes/api/providers.scan.ts +0 -28
- package/src/backend/routes/api/providers.ts +0 -102
- package/src/backend/routes/api/runs.$runId.evidence.ts +0 -55
- package/src/backend/routes/api/runs.$runId.ts +0 -50
- package/src/backend/routes/api/runs.ts +0 -62
- package/src/backend/routes/api/runtime.ts +0 -46
- package/src/backend/routes/api/sessions.ts +0 -51
- package/src/backend/routes/api/storage.ts +0 -56
- package/src/backend/routes/livez.ts +0 -13
- package/src/backend/routes/metrics.ts +0 -14
- package/src/backend/routes/proxy/$.ts +0 -15
- package/src/backend/routes/readyz.ts +0 -18
- package/src/backend/start.ts +0 -10
- package/src/cli/alias.ts +0 -431
- package/src/cli/detect-tools.ts +0 -123
- package/src/cli/doctor.ts +0 -836
- package/src/cli/instance.ts +0 -1071
- package/src/cli/instanceApi.ts +0 -213
- package/src/cli/instanceArgs.ts +0 -301
- package/src/cli/instanceControl.ts +0 -191
- package/src/cli/instanceModel.ts +0 -276
- package/src/cli/instanceRegistry.ts +0 -731
- package/src/cli/networkHints.ts +0 -170
- package/src/cli/onboard.ts +0 -1668
- package/src/cli/onboardTarget.ts +0 -228
- package/src/cli/rustBackendPackage.ts +0 -458
- package/src/cli/startupOutput.ts +0 -16
- package/src/cli/templates/codex-skill-onboard.ts +0 -320
- package/src/cli/templates/command-onboard.ts +0 -25
- package/src/cli/templates/skill-onboard.ts +0 -625
- package/src/cli.ts +0 -1433
- package/src/components/OnboardingBanner.tsx +0 -115
- package/src/components/ProxyViewer.tsx +0 -2259
- package/src/components/ProxyViewerContainer.tsx +0 -1076
- package/src/components/alerts/AlertsDialog.tsx +0 -615
- package/src/components/clients/ClientLogo.tsx +0 -175
- package/src/components/ecosystem/AgentLabDialog.tsx +0 -947
- package/src/components/errors/SafeErrorBoundary.tsx +0 -201
- package/src/components/groups/GroupsDialog.tsx +0 -1304
- package/src/components/inspector-pet/InspectorPet.tsx +0 -398
- package/src/components/inspector-pet/InspectorPetCrab3D.tsx +0 -455
- package/src/components/inspector-pet/inspectorPetLogic.ts +0 -272
- package/src/components/pi-agent/PiAgentPanel.tsx +0 -446
- package/src/components/pi-agent/piAgentChatLogic.ts +0 -273
- package/src/components/providers/ImportWizardDialog.tsx +0 -281
- package/src/components/providers/ProviderCard.tsx +0 -347
- package/src/components/providers/ProviderForm.tsx +0 -1183
- package/src/components/providers/ProviderLogo.tsx +0 -122
- package/src/components/providers/ProviderTestResultsView.tsx +0 -734
- package/src/components/providers/ProvidersPanel.tsx +0 -714
- package/src/components/providers/SettingsDialog.tsx +0 -1042
- package/src/components/providers/providerFormCapabilities.ts +0 -36
- package/src/components/proxy-viewer/AgentTraceSummary.tsx +0 -902
- package/src/components/proxy-viewer/AnswerMarkdown.tsx +0 -51
- package/src/components/proxy-viewer/ApplicationBar.tsx +0 -123
- package/src/components/proxy-viewer/CompareDrawer.tsx +0 -984
- package/src/components/proxy-viewer/ConversationGroup.tsx +0 -208
- package/src/components/proxy-viewer/ConversationGroupList.tsx +0 -652
- package/src/components/proxy-viewer/ConversationHeader.tsx +0 -429
- package/src/components/proxy-viewer/LiveConnectionStatus.tsx +0 -186
- package/src/components/proxy-viewer/LogEntry.tsx +0 -1419
- package/src/components/proxy-viewer/LogEntryHeader.tsx +0 -596
- package/src/components/proxy-viewer/ProviderLogoStack.tsx +0 -57
- package/src/components/proxy-viewer/RawExportWarning.tsx +0 -77
- package/src/components/proxy-viewer/ReplayDialog.tsx +0 -636
- package/src/components/proxy-viewer/RequestToolsPanel.tsx +0 -299
- package/src/components/proxy-viewer/ResponseView.tsx +0 -202
- package/src/components/proxy-viewer/SessionLoadProgressBar.tsx +0 -61
- package/src/components/proxy-viewer/StreamingChunkSequence.tsx +0 -265
- package/src/components/proxy-viewer/ThreadConnector.tsx +0 -165
- package/src/components/proxy-viewer/ToolTraceEvents.tsx +0 -64
- package/src/components/proxy-viewer/TurnGroup.tsx +0 -383
- package/src/components/proxy-viewer/TurnGroupList.tsx +0 -156
- package/src/components/proxy-viewer/accessibility.ts +0 -8
- package/src/components/proxy-viewer/anatomy/RequestAnatomy.tsx +0 -887
- package/src/components/proxy-viewer/anatomy/SegmentBar.tsx +0 -222
- package/src/components/proxy-viewer/anatomy/contextIntelligence.ts +0 -737
- package/src/components/proxy-viewer/anatomy/sessionContextSummary.ts +0 -268
- package/src/components/proxy-viewer/anatomy/tokenEstimate.ts +0 -53
- package/src/components/proxy-viewer/anatomy/types.ts +0 -41
- package/src/components/proxy-viewer/anatomy/useAnatomyJump.ts +0 -114
- package/src/components/proxy-viewer/bodyHydration.ts +0 -123
- package/src/components/proxy-viewer/cacheTrend.ts +0 -50
- package/src/components/proxy-viewer/diff/DiffView.tsx +0 -321
- package/src/components/proxy-viewer/diff/computeDiff.ts +0 -178
- package/src/components/proxy-viewer/diff/index.ts +0 -3
- package/src/components/proxy-viewer/formats/anthropic/ContentBlocks.tsx +0 -194
- package/src/components/proxy-viewer/formats/anthropic/ResponseView.tsx +0 -81
- package/src/components/proxy-viewer/formats/anthropic/thinkingExtract.ts +0 -21
- package/src/components/proxy-viewer/formats/index.tsx +0 -47
- package/src/components/proxy-viewer/formats/openai/ResponseView.tsx +0 -442
- package/src/components/proxy-viewer/index.ts +0 -9
- package/src/components/proxy-viewer/lazy.ts +0 -41
- package/src/components/proxy-viewer/liveConnectionState.ts +0 -158
- package/src/components/proxy-viewer/log-formats/anthropic.ts +0 -191
- package/src/components/proxy-viewer/log-formats/index.ts +0 -23
- package/src/components/proxy-viewer/log-formats/openai.ts +0 -256
- package/src/components/proxy-viewer/log-formats/types.ts +0 -40
- package/src/components/proxy-viewer/log-formats/unknown.ts +0 -18
- package/src/components/proxy-viewer/logEntryVisibility.ts +0 -73
- package/src/components/proxy-viewer/logFocus.ts +0 -54
- package/src/components/proxy-viewer/proxyViewerContainerLogic.ts +0 -124
- package/src/components/proxy-viewer/proxyViewerLogic.ts +0 -488
- package/src/components/proxy-viewer/replayComparison.ts +0 -131
- package/src/components/proxy-viewer/requestDiff.ts +0 -277
- package/src/components/proxy-viewer/requestTools.ts +0 -220
- package/src/components/proxy-viewer/useCopyFeedback.ts +0 -38
- package/src/components/proxy-viewer/useKeyboardNavigation.ts +0 -232
- package/src/components/proxy-viewer/viewerState.ts +0 -176
- package/src/components/ui/badge.tsx +0 -47
- package/src/components/ui/button.tsx +0 -47
- package/src/components/ui/collapsible.tsx +0 -21
- package/src/components/ui/confirm-dialog.tsx +0 -51
- package/src/components/ui/crab-logo.tsx +0 -45
- package/src/components/ui/crab-variants.tsx +0 -467
- package/src/components/ui/dialog.tsx +0 -130
- package/src/components/ui/icon-trigger.ts +0 -2
- package/src/components/ui/json-viewer-bulk.ts +0 -132
- package/src/components/ui/json-viewer.tsx +0 -508
- package/src/components/ui/scroll-area.tsx +0 -54
- package/src/components/ui/select.tsx +0 -178
- package/src/components/ui/separator.tsx +0 -28
- package/src/components/ui/tabs.tsx +0 -88
- package/src/components/ui/tooltip.tsx +0 -51
- package/src/components/ui/transient-toast.tsx +0 -105
- package/src/contracts/anthropic.ts +0 -230
- package/src/contracts/index.ts +0 -59
- package/src/contracts/json.ts +0 -37
- package/src/contracts/log.ts +0 -139
- package/src/contracts/openai.ts +0 -267
- package/src/index.css +0 -11
- package/src/knowledge/candidateStore.ts +0 -94
- package/src/knowledge/distiller.ts +0 -98
- package/src/knowledge/openclawClient.ts +0 -161
- package/src/knowledge/openclawGatewayClient.ts +0 -249
- package/src/knowledge/openclawMarkdown.ts +0 -146
- package/src/knowledge/redactor.ts +0 -80
- package/src/knowledge/types.ts +0 -83
- package/src/lib/alertContract.ts +0 -76
- package/src/lib/apiClient.ts +0 -166
- package/src/lib/apiFormat.ts +0 -29
- package/src/lib/backendImplementation.ts +0 -16
- package/src/lib/basePath.ts +0 -107
- package/src/lib/browserRuntimeContract.ts +0 -124
- package/src/lib/browserRuntimeResolver.ts +0 -333
- package/src/lib/chunkLoadRecovery.ts +0 -83
- package/src/lib/claudeCodeStrip.ts +0 -98
- package/src/lib/clipboard.ts +0 -67
- package/src/lib/codingAgentEndpoints.ts +0 -54
- package/src/lib/ecosystemContract.ts +0 -130
- package/src/lib/export-logs.ts +0 -287
- package/src/lib/groupContract.ts +0 -166
- package/src/lib/instanceContract.ts +0 -239
- package/src/lib/jsonFile.ts +0 -46
- package/src/lib/logImportContract.ts +0 -12
- package/src/lib/managedInstance.ts +0 -112
- package/src/lib/mask.ts +0 -4
- package/src/lib/objectUtils.ts +0 -22
- package/src/lib/piAgentContract.ts +0 -102
- package/src/lib/providerContract.ts +0 -125
- package/src/lib/providerImportContract.ts +0 -27
- package/src/lib/providerModelMetadata.ts +0 -458
- package/src/lib/providerTestContract.ts +0 -170
- package/src/lib/providerTestPrompt.ts +0 -100
- package/src/lib/publicBasePathContext.ts +0 -137
- package/src/lib/resourceLimits.ts +0 -345
- package/src/lib/runContract.ts +0 -164
- package/src/lib/runtimeConfig.ts +0 -43
- package/src/lib/safeDiagnostic.ts +0 -38
- package/src/lib/safeFetch.ts +0 -508
- package/src/lib/serverPort.ts +0 -47
- package/src/lib/sessionInfoContract.ts +0 -78
- package/src/lib/sessionUrl.ts +0 -46
- package/src/lib/ssrfGuard.ts +0 -133
- package/src/lib/stopReason.ts +0 -130
- package/src/lib/themeMode.ts +0 -41
- package/src/lib/timeDisplay.ts +0 -22
- package/src/lib/toolTrace.ts +0 -290
- package/src/lib/uiScale.ts +0 -117
- package/src/lib/unknown.ts +0 -27
- package/src/lib/upstreamUrl.ts +0 -150
- package/src/lib/useAlerts.ts +0 -82
- package/src/lib/useGroupEvidence.ts +0 -37
- package/src/lib/useGroups.ts +0 -32
- package/src/lib/useOnboarding.ts +0 -93
- package/src/lib/useProviders.ts +0 -30
- package/src/lib/useStripConfig.ts +0 -154
- package/src/lib/useThemeMode.ts +0 -40
- package/src/lib/useUiScale.ts +0 -52
- package/src/lib/utils.ts +0 -78
- package/src/mcp/currentContext.ts +0 -65
- package/src/mcp/instanceHandlers.ts +0 -151
- package/src/mcp/loopback.ts +0 -88
- package/src/mcp/mode.ts +0 -75
- package/src/mcp/previewExtractor.ts +0 -233
- package/src/mcp/server.ts +0 -2274
- package/src/mcp/toolHandlers.ts +0 -1533
- package/src/proxy/alerts.ts +0 -664
- package/src/proxy/chunkStorage.ts +0 -123
- package/src/proxy/config.ts +0 -218
- package/src/proxy/constants.ts +0 -66
- package/src/proxy/dataDir.ts +0 -147
- package/src/proxy/ecosystemExecutionConfirmation.ts +0 -114
- package/src/proxy/ecosystemExecutionRoute.ts +0 -116
- package/src/proxy/ecosystemRegistry.ts +0 -296
- package/src/proxy/ecosystemTasks.ts +0 -415
- package/src/proxy/evidenceAnalysis.ts +0 -526
- package/src/proxy/evidenceExporter.ts +0 -235
- package/src/proxy/formats/anthropic/anthropicProvider.ts +0 -75
- package/src/proxy/formats/anthropic/handler.ts +0 -75
- package/src/proxy/formats/anthropic/index.ts +0 -13
- package/src/proxy/formats/anthropic/register.ts +0 -4
- package/src/proxy/formats/anthropic/schemas.ts +0 -6
- package/src/proxy/formats/anthropic/stream.ts +0 -205
- package/src/proxy/formats/handler.ts +0 -46
- package/src/proxy/formats/index.ts +0 -13
- package/src/proxy/formats/jsonSchema.ts +0 -2
- package/src/proxy/formats/openai/alibabaProvider.ts +0 -34
- package/src/proxy/formats/openai/codexMetadata.ts +0 -61
- package/src/proxy/formats/openai/handler.ts +0 -133
- package/src/proxy/formats/openai/index.ts +0 -33
- package/src/proxy/formats/openai/provider.ts +0 -50
- package/src/proxy/formats/openai/register.ts +0 -4
- package/src/proxy/formats/openai/schemas.ts +0 -25
- package/src/proxy/formats/openai/stream.ts +0 -600
- package/src/proxy/formats/openai/zhipuProvider.ts +0 -31
- package/src/proxy/formats/providerAdapter.ts +0 -50
- package/src/proxy/formats/providerRegistry.ts +0 -51
- package/src/proxy/formats/providers/index.ts +0 -3
- package/src/proxy/formats/registry.ts +0 -65
- package/src/proxy/groupEvidenceExporter.ts +0 -371
- package/src/proxy/groupStore.ts +0 -336
- package/src/proxy/handler.ts +0 -729
- package/src/proxy/identityProxy.ts +0 -1842
- package/src/proxy/jsonlRecovery.ts +0 -554
- package/src/proxy/logBodyChunks.ts +0 -91
- package/src/proxy/logFinalizer.ts +0 -777
- package/src/proxy/logFinalizer.worker.ts +0 -24
- package/src/proxy/logImportUpload.ts +0 -128
- package/src/proxy/logImporter.ts +0 -813
- package/src/proxy/logIndex.ts +0 -663
- package/src/proxy/logSearch.ts +0 -122
- package/src/proxy/logger.ts +0 -537
- package/src/proxy/openaiOrphanToolStrip.ts +0 -142
- package/src/proxy/platformCommands.ts +0 -258
- package/src/proxy/privateDataPath.ts +0 -419
- package/src/proxy/providerImporters.ts +0 -578
- package/src/proxy/providerScanStore.ts +0 -87
- package/src/proxy/providerSecretStore.ts +0 -216
- package/src/proxy/providers.ts +0 -998
- package/src/proxy/rawStreamCapture.ts +0 -342
- package/src/proxy/runFailures.ts +0 -94
- package/src/proxy/runStore.ts +0 -214
- package/src/proxy/runtimeAdmission.ts +0 -55
- package/src/proxy/runtimeHealth.ts +0 -221
- package/src/proxy/runtimeMetrics.ts +0 -149
- package/src/proxy/runtimeShutdown.ts +0 -157
- package/src/proxy/schemas.ts +0 -112
- package/src/proxy/sessionArchive.ts +0 -479
- package/src/proxy/sessionInfo.ts +0 -266
- package/src/proxy/sessionProcess.ts +0 -194
- package/src/proxy/sessionRuntime.ts +0 -121
- package/src/proxy/sessionSupervisor.ts +0 -305
- package/src/proxy/sessionWorkerEntry.ts +0 -26
- package/src/proxy/shutdownCoordinator.ts +0 -90
- package/src/proxy/socketTracker.ts +0 -639
- package/src/proxy/sqliteLogIndex.ts +0 -733
- package/src/proxy/storageLifecycle.ts +0 -432
- package/src/proxy/store.ts +0 -1773
- package/src/proxy/streamTiming.ts +0 -31
- package/src/proxy/toolSchemaWarnings.ts +0 -529
- package/src/proxy/uiAssetServer.ts +0 -106
- package/src/proxy/upstream.ts +0 -127
- package/src/services/alerts.ts +0 -12
- package/src/services/groups.ts +0 -119
- package/src/services/piAgent.ts +0 -421
- package/src/services/piAgentRetrieval.ts +0 -1180
- package/src/services/result.ts +0 -17
- package/src/services/runs.ts +0 -76
- package/src/ui/bootstrap.ts +0 -167
- package/src/ui/index.html +0 -38
- package/src/ui/logsStreamClient.ts +0 -20
- package/src/ui/main.tsx +0 -13
- package/src/ui/public/runtime-config.js +0 -2
- package/src/ui/router.tsx +0 -13
- package/src/ui/routes/__root.tsx +0 -69
- package/src/ui/routes/index.tsx +0 -35
- package/src/ui/routes/session/$sessionId.tsx +0 -50
- package/src/ui/runtimeDiscovery.ts +0 -50
|
@@ -1,734 +0,0 @@
|
|
|
1
|
-
import { type JSX, type ReactNode, useState } from "react";
|
|
2
|
-
import {
|
|
3
|
-
AlertCircle,
|
|
4
|
-
CheckCircle,
|
|
5
|
-
ChevronDown,
|
|
6
|
-
ChevronRight,
|
|
7
|
-
Clock,
|
|
8
|
-
Gauge,
|
|
9
|
-
HelpCircle,
|
|
10
|
-
Lock,
|
|
11
|
-
Minus,
|
|
12
|
-
RotateCw,
|
|
13
|
-
Server,
|
|
14
|
-
Wifi,
|
|
15
|
-
WifiOff,
|
|
16
|
-
} from "lucide-react";
|
|
17
|
-
import type {
|
|
18
|
-
ProviderFormatTestResults,
|
|
19
|
-
ProviderModelTestResults,
|
|
20
|
-
ProviderTestErrorType as ErrorType,
|
|
21
|
-
ProviderTestResult as TestResult,
|
|
22
|
-
ProviderTestResults as TestResults,
|
|
23
|
-
ProviderTestState,
|
|
24
|
-
} from "../../lib/providerTestContract";
|
|
25
|
-
import type { ProviderConfig } from "../../lib/providerContract";
|
|
26
|
-
import { cn } from "../../lib/utils";
|
|
27
|
-
import {
|
|
28
|
-
Dialog,
|
|
29
|
-
DialogContent,
|
|
30
|
-
DialogDescription,
|
|
31
|
-
DialogHeader,
|
|
32
|
-
DialogTitle,
|
|
33
|
-
DialogTrigger,
|
|
34
|
-
} from "../ui/dialog";
|
|
35
|
-
|
|
36
|
-
type ProviderTestResultSet = {
|
|
37
|
-
anthropic: ProviderFormatTestResults;
|
|
38
|
-
openaiChat: ProviderFormatTestResults;
|
|
39
|
-
openaiResponses: ProviderFormatTestResults;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
type ProbeTone = "success" | "failure" | "pending" | "quiet";
|
|
43
|
-
|
|
44
|
-
export type ProviderProbePresentation = {
|
|
45
|
-
tone: ProbeTone;
|
|
46
|
-
label: string;
|
|
47
|
-
metric: string | null;
|
|
48
|
-
hasDetails: boolean;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
export type ProviderTestSummary = {
|
|
52
|
-
total: number;
|
|
53
|
-
passed: number;
|
|
54
|
-
failed: number;
|
|
55
|
-
testing: number;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
function hasSuccessField(result: ProviderTestState): result is TestResult {
|
|
59
|
-
return Object.prototype.hasOwnProperty.call(result, "success");
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function isNotConfiguredState(result: ProviderTestState): result is { notConfigured: true } {
|
|
63
|
-
return Object.prototype.hasOwnProperty.call(result, "notConfigured");
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function providerHasEndpoint(value: string | undefined): value is string {
|
|
67
|
-
return value !== undefined && value !== "";
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function hasText(value: string | undefined): value is string {
|
|
71
|
-
return value !== undefined && value.trim() !== "";
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function hasNumber(value: number | null | undefined): value is number {
|
|
75
|
-
return value !== undefined && value !== null;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function formatElapsed(ms: number): string {
|
|
79
|
-
if (ms < 1000) return `${ms}ms`;
|
|
80
|
-
return `${(ms / 1000).toFixed(1)}s`;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function formatTokenRate(tokensPerSecond: number): string {
|
|
84
|
-
if (tokensPerSecond >= 10) return `${tokensPerSecond.toFixed(0)}/s`;
|
|
85
|
-
return `${tokensPerSecond.toFixed(1)}/s`;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function errorLabel(type: ErrorType): string {
|
|
89
|
-
switch (type) {
|
|
90
|
-
case "timeout":
|
|
91
|
-
return "Timeout";
|
|
92
|
-
case "network_unreachable":
|
|
93
|
-
return "Offline";
|
|
94
|
-
case "connection_refused":
|
|
95
|
-
return "Refused";
|
|
96
|
-
case "auth_failed":
|
|
97
|
-
return "Auth";
|
|
98
|
-
case "rate_limited":
|
|
99
|
-
return "Rate limit";
|
|
100
|
-
case "server_error":
|
|
101
|
-
return "Server";
|
|
102
|
-
case "invalid_response":
|
|
103
|
-
return "Invalid";
|
|
104
|
-
case "unknown":
|
|
105
|
-
return "Failed";
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function compactSuccessMetric(result: TestResult): string | null {
|
|
110
|
-
if (result.firstChunkMs !== undefined && result.firstChunkMs !== null) {
|
|
111
|
-
return formatElapsed(result.firstChunkMs);
|
|
112
|
-
}
|
|
113
|
-
if (result.latencyMs !== undefined) return formatElapsed(result.latencyMs);
|
|
114
|
-
if (result.totalStreamMs !== undefined && result.totalStreamMs !== null) {
|
|
115
|
-
return formatElapsed(result.totalStreamMs);
|
|
116
|
-
}
|
|
117
|
-
if (
|
|
118
|
-
result.tokensPerSecond !== undefined &&
|
|
119
|
-
result.tokensPerSecond !== null &&
|
|
120
|
-
result.tokensPerSecond > 0
|
|
121
|
-
) {
|
|
122
|
-
return formatTokenRate(result.tokensPerSecond);
|
|
123
|
-
}
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
export function presentProviderProbe(result: ProviderTestState): ProviderProbePresentation {
|
|
128
|
-
if (isNotConfiguredState(result)) {
|
|
129
|
-
return { tone: "quiet", label: "N/A", metric: null, hasDetails: false };
|
|
130
|
-
}
|
|
131
|
-
if (!hasSuccessField(result)) {
|
|
132
|
-
return { tone: "pending", label: "Running", metric: null, hasDetails: false };
|
|
133
|
-
}
|
|
134
|
-
if (result.success) {
|
|
135
|
-
return {
|
|
136
|
-
tone: "success",
|
|
137
|
-
label: "Pass",
|
|
138
|
-
metric: compactSuccessMetric(result),
|
|
139
|
-
hasDetails: true,
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
return {
|
|
143
|
-
tone: "failure",
|
|
144
|
-
label: errorLabel(result.error?.type ?? "unknown"),
|
|
145
|
-
metric: null,
|
|
146
|
-
hasDetails: true,
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function configuredFormatResults(
|
|
151
|
-
provider: ProviderConfig,
|
|
152
|
-
testResults: TestResults | undefined,
|
|
153
|
-
): ProviderFormatTestResults[] {
|
|
154
|
-
if (testResults === undefined) return [];
|
|
155
|
-
const results: ProviderFormatTestResults[] = [];
|
|
156
|
-
if (providerHasEndpoint(provider.anthropicBaseUrl)) results.push(testResults.anthropic);
|
|
157
|
-
if (providerHasEndpoint(provider.openaiBaseUrl)) results.push(testResults.openaiChat);
|
|
158
|
-
if (providerHasEndpoint(provider.openaiResponsesBaseUrl)) {
|
|
159
|
-
results.push(testResults.openaiResponses);
|
|
160
|
-
}
|
|
161
|
-
return results;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export function summarizeProviderTestResults(
|
|
165
|
-
provider: ProviderConfig,
|
|
166
|
-
testResults: TestResults | undefined,
|
|
167
|
-
): ProviderTestSummary {
|
|
168
|
-
const summary: ProviderTestSummary = { total: 0, passed: 0, failed: 0, testing: 0 };
|
|
169
|
-
for (const results of configuredFormatResults(provider, testResults)) {
|
|
170
|
-
for (const state of [results.nonStreaming, results.streaming]) {
|
|
171
|
-
if (isNotConfiguredState(state)) continue;
|
|
172
|
-
summary.total += 1;
|
|
173
|
-
if (!hasSuccessField(state)) {
|
|
174
|
-
summary.testing += 1;
|
|
175
|
-
} else if (state.success) {
|
|
176
|
-
summary.passed += 1;
|
|
177
|
-
} else {
|
|
178
|
-
summary.failed += 1;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
return summary;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function formatHeaders(headers: Record<string, string> | undefined): string | undefined {
|
|
186
|
-
if (headers === undefined) return undefined;
|
|
187
|
-
const lines = Object.entries(headers).map(([name, value]) => `${name}: ${value}`);
|
|
188
|
-
return lines.length === 0 ? undefined : lines.join("\n");
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
function DiagnosticSection({
|
|
192
|
-
title,
|
|
193
|
-
children,
|
|
194
|
-
}: {
|
|
195
|
-
title: string;
|
|
196
|
-
children: ReactNode;
|
|
197
|
-
}): JSX.Element {
|
|
198
|
-
return (
|
|
199
|
-
<section className="space-y-1.5">
|
|
200
|
-
<h4 className="text-xs font-medium text-foreground/80">{title}</h4>
|
|
201
|
-
{children}
|
|
202
|
-
</section>
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
function DiagnosticPre({ value }: { value: string }): JSX.Element {
|
|
207
|
-
return (
|
|
208
|
-
<pre className="max-h-48 overflow-auto whitespace-pre-wrap break-all rounded-md bg-muted/40 p-3 font-mono text-[11px] leading-relaxed text-foreground/80">
|
|
209
|
-
{value}
|
|
210
|
-
</pre>
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
function ResultMetric({ label, value }: { label: string; value: string }): JSX.Element {
|
|
215
|
-
return (
|
|
216
|
-
<div className="min-w-0 rounded-md bg-muted/35 px-3 py-2">
|
|
217
|
-
<dt className="text-[10px] text-muted-foreground">{label}</dt>
|
|
218
|
-
<dd className="mt-1 truncate font-mono text-xs font-semibold text-foreground" title={value}>
|
|
219
|
-
{value}
|
|
220
|
-
</dd>
|
|
221
|
-
</div>
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function ProviderTestMetrics({ result }: { result: TestResult }): JSX.Element | null {
|
|
226
|
-
const hasTokens = hasNumber(result.inputTokens) || hasNumber(result.outputTokens);
|
|
227
|
-
const hasCache =
|
|
228
|
-
hasNumber(result.cacheCreationInputTokens) || hasNumber(result.cacheReadInputTokens);
|
|
229
|
-
const hasMetrics =
|
|
230
|
-
hasText(result.model) ||
|
|
231
|
-
hasTokens ||
|
|
232
|
-
hasCache ||
|
|
233
|
-
result.latencyMs !== undefined ||
|
|
234
|
-
(result.firstChunkMs !== undefined && result.firstChunkMs !== null) ||
|
|
235
|
-
(result.totalStreamMs !== undefined && result.totalStreamMs !== null) ||
|
|
236
|
-
(result.tokensPerSecond !== undefined &&
|
|
237
|
-
result.tokensPerSecond !== null &&
|
|
238
|
-
result.tokensPerSecond > 0);
|
|
239
|
-
if (!hasMetrics) return null;
|
|
240
|
-
|
|
241
|
-
return (
|
|
242
|
-
<dl className="grid grid-cols-2 gap-2 sm:grid-cols-3 lg:grid-cols-4">
|
|
243
|
-
{hasText(result.model) && <ResultMetric label="Model" value={result.model} />}
|
|
244
|
-
{result.latencyMs !== undefined && (
|
|
245
|
-
<ResultMetric label="Latency" value={formatElapsed(result.latencyMs)} />
|
|
246
|
-
)}
|
|
247
|
-
{result.firstChunkMs !== undefined && result.firstChunkMs !== null && (
|
|
248
|
-
<ResultMetric label="First chunk" value={formatElapsed(result.firstChunkMs)} />
|
|
249
|
-
)}
|
|
250
|
-
{result.totalStreamMs !== undefined && result.totalStreamMs !== null && (
|
|
251
|
-
<ResultMetric label="Stream total" value={formatElapsed(result.totalStreamMs)} />
|
|
252
|
-
)}
|
|
253
|
-
{result.tokensPerSecond !== undefined &&
|
|
254
|
-
result.tokensPerSecond !== null &&
|
|
255
|
-
result.tokensPerSecond > 0 && (
|
|
256
|
-
<ResultMetric
|
|
257
|
-
label="Token rate"
|
|
258
|
-
value={`${formatTokenRate(result.tokensPerSecond)} tok`}
|
|
259
|
-
/>
|
|
260
|
-
)}
|
|
261
|
-
{hasTokens && (
|
|
262
|
-
<ResultMetric
|
|
263
|
-
label="Tokens in / out"
|
|
264
|
-
value={`${String(result.inputTokens ?? 0)} / ${String(result.outputTokens ?? 0)}`}
|
|
265
|
-
/>
|
|
266
|
-
)}
|
|
267
|
-
{hasCache && (
|
|
268
|
-
<ResultMetric
|
|
269
|
-
label="Cache create / read"
|
|
270
|
-
value={`${String(result.cacheCreationInputTokens ?? 0)} / ${String(result.cacheReadInputTokens ?? 0)}`}
|
|
271
|
-
/>
|
|
272
|
-
)}
|
|
273
|
-
</dl>
|
|
274
|
-
);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function ProviderTestDiagnostics({ result }: { result: TestResult }): JSX.Element | null {
|
|
278
|
-
const debug = result.debug;
|
|
279
|
-
const requestUrl = debug?.requestUrl;
|
|
280
|
-
const requestMethod = debug?.requestMethod ?? "POST";
|
|
281
|
-
const requestHeaders = formatHeaders(debug?.requestHeaders);
|
|
282
|
-
const responseHeaders = formatHeaders(debug?.responseHeaders);
|
|
283
|
-
const responseStatus =
|
|
284
|
-
debug?.responseStatus === undefined
|
|
285
|
-
? undefined
|
|
286
|
-
: `${String(debug.responseStatus)}${
|
|
287
|
-
hasText(debug.responseStatusText) ? ` ${debug.responseStatusText}` : ""
|
|
288
|
-
}`;
|
|
289
|
-
const responseBody = debug?.responseBody ?? result.rawResponse;
|
|
290
|
-
const hasDiagnostics =
|
|
291
|
-
hasText(result.error?.hint) ||
|
|
292
|
-
hasText(result.error?.details) ||
|
|
293
|
-
hasText(requestUrl) ||
|
|
294
|
-
requestHeaders !== undefined ||
|
|
295
|
-
hasText(debug?.requestBody) ||
|
|
296
|
-
hasText(responseStatus) ||
|
|
297
|
-
responseHeaders !== undefined ||
|
|
298
|
-
hasText(responseBody);
|
|
299
|
-
if (!hasDiagnostics) return null;
|
|
300
|
-
|
|
301
|
-
return (
|
|
302
|
-
<div className="space-y-4 border-t border-border/70 pt-4 text-xs text-muted-foreground">
|
|
303
|
-
{hasText(result.error?.hint) && (
|
|
304
|
-
<DiagnosticSection title="Hint">
|
|
305
|
-
<p className="break-words text-foreground/80">{result.error.hint}</p>
|
|
306
|
-
</DiagnosticSection>
|
|
307
|
-
)}
|
|
308
|
-
{hasText(result.error?.details) && (
|
|
309
|
-
<DiagnosticSection title="Error details">
|
|
310
|
-
<DiagnosticPre value={result.error.details} />
|
|
311
|
-
</DiagnosticSection>
|
|
312
|
-
)}
|
|
313
|
-
{hasText(requestUrl) && (
|
|
314
|
-
<DiagnosticSection title="Request">
|
|
315
|
-
<DiagnosticPre value={`${requestMethod} ${requestUrl}`} />
|
|
316
|
-
</DiagnosticSection>
|
|
317
|
-
)}
|
|
318
|
-
{requestHeaders !== undefined && (
|
|
319
|
-
<DiagnosticSection title="Request headers">
|
|
320
|
-
<DiagnosticPre value={requestHeaders} />
|
|
321
|
-
</DiagnosticSection>
|
|
322
|
-
)}
|
|
323
|
-
{hasText(debug?.requestBody) && (
|
|
324
|
-
<DiagnosticSection title="Request body">
|
|
325
|
-
<DiagnosticPre value={debug.requestBody} />
|
|
326
|
-
</DiagnosticSection>
|
|
327
|
-
)}
|
|
328
|
-
{hasText(responseStatus) && (
|
|
329
|
-
<DiagnosticSection title="Response status">
|
|
330
|
-
<DiagnosticPre value={responseStatus} />
|
|
331
|
-
</DiagnosticSection>
|
|
332
|
-
)}
|
|
333
|
-
{responseHeaders !== undefined && (
|
|
334
|
-
<DiagnosticSection title="Response headers">
|
|
335
|
-
<DiagnosticPre value={responseHeaders} />
|
|
336
|
-
</DiagnosticSection>
|
|
337
|
-
)}
|
|
338
|
-
{hasText(responseBody) && (
|
|
339
|
-
<DiagnosticSection title="Response body">
|
|
340
|
-
<DiagnosticPre value={responseBody} />
|
|
341
|
-
</DiagnosticSection>
|
|
342
|
-
)}
|
|
343
|
-
</div>
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
function ProviderTestResultDialog({
|
|
348
|
-
result,
|
|
349
|
-
probeLabel,
|
|
350
|
-
children,
|
|
351
|
-
}: {
|
|
352
|
-
result: TestResult;
|
|
353
|
-
probeLabel: string;
|
|
354
|
-
children: ReactNode;
|
|
355
|
-
}): JSX.Element {
|
|
356
|
-
const errorMessage = result.error?.message ?? "Connection failed";
|
|
357
|
-
return (
|
|
358
|
-
<Dialog>
|
|
359
|
-
<DialogTrigger asChild>{children}</DialogTrigger>
|
|
360
|
-
<DialogContent className="max-h-[82vh] max-w-2xl gap-0 overflow-hidden p-0">
|
|
361
|
-
<DialogHeader className="border-b border-border/70 px-5 py-4 pr-12">
|
|
362
|
-
<div className="flex items-center gap-2">
|
|
363
|
-
{result.success ? (
|
|
364
|
-
<CheckCircle className="size-4 text-status-success" />
|
|
365
|
-
) : (
|
|
366
|
-
<AlertCircle className="size-4 text-status-danger" />
|
|
367
|
-
)}
|
|
368
|
-
<DialogTitle className="text-base">{probeLabel}</DialogTitle>
|
|
369
|
-
</div>
|
|
370
|
-
<DialogDescription>
|
|
371
|
-
{result.success ? "Connection probe passed." : errorMessage}
|
|
372
|
-
</DialogDescription>
|
|
373
|
-
</DialogHeader>
|
|
374
|
-
<div className="space-y-4 overflow-y-auto p-5">
|
|
375
|
-
{!result.success && (
|
|
376
|
-
<div className="rounded-md border border-status-danger/30 bg-status-danger/10 px-3 py-2 text-sm text-status-danger">
|
|
377
|
-
<div className="font-medium">{errorLabel(result.error?.type ?? "unknown")}</div>
|
|
378
|
-
<div className="mt-1 break-words text-xs text-muted-foreground">{errorMessage}</div>
|
|
379
|
-
</div>
|
|
380
|
-
)}
|
|
381
|
-
<ProviderTestMetrics result={result} />
|
|
382
|
-
<ProviderTestDiagnostics result={result} />
|
|
383
|
-
</div>
|
|
384
|
-
</DialogContent>
|
|
385
|
-
</Dialog>
|
|
386
|
-
);
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
function errorIcon(type: ErrorType): JSX.Element {
|
|
390
|
-
const className = "size-3 shrink-0";
|
|
391
|
-
switch (type) {
|
|
392
|
-
case "timeout":
|
|
393
|
-
return <Clock className={className} />;
|
|
394
|
-
case "network_unreachable":
|
|
395
|
-
return <WifiOff className={className} />;
|
|
396
|
-
case "connection_refused":
|
|
397
|
-
return <Wifi className={className} />;
|
|
398
|
-
case "auth_failed":
|
|
399
|
-
return <Lock className={className} />;
|
|
400
|
-
case "rate_limited":
|
|
401
|
-
return <Gauge className={className} />;
|
|
402
|
-
case "server_error":
|
|
403
|
-
return <Server className={className} />;
|
|
404
|
-
case "invalid_response":
|
|
405
|
-
return <HelpCircle className={className} />;
|
|
406
|
-
case "unknown":
|
|
407
|
-
return <AlertCircle className={className} />;
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
const PROBE_TONE_CLASSES: Record<ProbeTone, string> = {
|
|
412
|
-
success: "bg-status-success/10 text-status-success hover:bg-status-success/15",
|
|
413
|
-
failure: "bg-status-danger/10 text-status-danger hover:bg-status-danger/15",
|
|
414
|
-
pending: "bg-muted/40 text-foreground/80",
|
|
415
|
-
quiet: "bg-transparent text-muted-foreground/70",
|
|
416
|
-
};
|
|
417
|
-
|
|
418
|
-
function ProviderProbeStatus({
|
|
419
|
-
result,
|
|
420
|
-
probeLabel,
|
|
421
|
-
}: {
|
|
422
|
-
result: ProviderTestState;
|
|
423
|
-
probeLabel: string;
|
|
424
|
-
}): JSX.Element {
|
|
425
|
-
const presentation = presentProviderProbe(result);
|
|
426
|
-
const icon = isNotConfiguredState(result) ? (
|
|
427
|
-
<Minus className="size-3 shrink-0" />
|
|
428
|
-
) : !hasSuccessField(result) ? (
|
|
429
|
-
<RotateCw className="size-3 shrink-0 animate-spin" />
|
|
430
|
-
) : result.success ? (
|
|
431
|
-
<CheckCircle className="size-3 shrink-0" />
|
|
432
|
-
) : (
|
|
433
|
-
errorIcon(result.error?.type ?? "unknown")
|
|
434
|
-
);
|
|
435
|
-
const content = (
|
|
436
|
-
<>
|
|
437
|
-
{icon}
|
|
438
|
-
<span className="min-w-0 truncate">{presentation.label}</span>
|
|
439
|
-
{presentation.metric !== null && (
|
|
440
|
-
<span className="ml-auto shrink-0 font-mono text-[10px] opacity-75">
|
|
441
|
-
{presentation.metric}
|
|
442
|
-
</span>
|
|
443
|
-
)}
|
|
444
|
-
</>
|
|
445
|
-
);
|
|
446
|
-
const className = cn(
|
|
447
|
-
"inline-flex h-7 w-full min-w-0 items-center gap-1.5 rounded-md px-2 text-left text-xs transition-colors",
|
|
448
|
-
PROBE_TONE_CLASSES[presentation.tone],
|
|
449
|
-
);
|
|
450
|
-
|
|
451
|
-
if (hasSuccessField(result) && presentation.hasDetails) {
|
|
452
|
-
return (
|
|
453
|
-
<ProviderTestResultDialog result={result} probeLabel={probeLabel}>
|
|
454
|
-
<button
|
|
455
|
-
type="button"
|
|
456
|
-
className={className}
|
|
457
|
-
aria-label={`Open ${probeLabel} result: ${presentation.label}`}
|
|
458
|
-
>
|
|
459
|
-
{content}
|
|
460
|
-
</button>
|
|
461
|
-
</ProviderTestResultDialog>
|
|
462
|
-
);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
return <div className={className}>{content}</div>;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
function ProbePlaceholder({ label }: { label: string }): JSX.Element {
|
|
469
|
-
return (
|
|
470
|
-
<div className="inline-flex h-7 w-full min-w-0 items-center gap-1.5 rounded-md px-2 text-xs text-muted-foreground/70">
|
|
471
|
-
<Minus className="size-3 shrink-0" />
|
|
472
|
-
<span className="truncate">{label}</span>
|
|
473
|
-
</div>
|
|
474
|
-
);
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
function ProviderFormatRow({
|
|
478
|
-
label,
|
|
479
|
-
results,
|
|
480
|
-
configured = true,
|
|
481
|
-
}: {
|
|
482
|
-
label: string;
|
|
483
|
-
results?: ProviderFormatTestResults;
|
|
484
|
-
configured?: boolean;
|
|
485
|
-
}): JSX.Element {
|
|
486
|
-
return (
|
|
487
|
-
<div className="grid min-w-0 grid-cols-[minmax(6.5rem,1fr)_minmax(6rem,0.9fr)_minmax(6rem,0.9fr)] items-center gap-2 border-t border-border/60 px-2 py-2">
|
|
488
|
-
<div className="min-w-0 truncate text-xs font-medium text-foreground/85" title={label}>
|
|
489
|
-
{label}
|
|
490
|
-
</div>
|
|
491
|
-
{configured && results !== undefined ? (
|
|
492
|
-
<ProviderProbeStatus result={results.nonStreaming} probeLabel={`${label} non-stream`} />
|
|
493
|
-
) : (
|
|
494
|
-
<ProbePlaceholder label={configured ? "Not tested" : "Off"} />
|
|
495
|
-
)}
|
|
496
|
-
{configured && results !== undefined ? (
|
|
497
|
-
<ProviderProbeStatus result={results.streaming} probeLabel={`${label} stream`} />
|
|
498
|
-
) : (
|
|
499
|
-
<ProbePlaceholder label={configured ? "Not tested" : "Off"} />
|
|
500
|
-
)}
|
|
501
|
-
</div>
|
|
502
|
-
);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
export function ProviderTestMatrix({
|
|
506
|
-
provider,
|
|
507
|
-
results,
|
|
508
|
-
}: {
|
|
509
|
-
provider: ProviderConfig;
|
|
510
|
-
results?: ProviderTestResultSet;
|
|
511
|
-
}): JSX.Element {
|
|
512
|
-
const hasAnthropic = providerHasEndpoint(provider.anthropicBaseUrl);
|
|
513
|
-
const hasChat = providerHasEndpoint(provider.openaiBaseUrl);
|
|
514
|
-
const hasResponses = providerHasEndpoint(provider.openaiResponsesBaseUrl);
|
|
515
|
-
if (!hasAnthropic && !hasChat && !hasResponses) {
|
|
516
|
-
return <div className="text-xs text-muted-foreground">No testable endpoint configured.</div>;
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
return (
|
|
520
|
-
<div className="min-w-0 overflow-x-auto rounded-md border border-border/70">
|
|
521
|
-
<div className="min-w-[20rem]">
|
|
522
|
-
<div className="grid grid-cols-[minmax(6.5rem,1fr)_minmax(6rem,0.9fr)_minmax(6rem,0.9fr)] gap-2 bg-muted/25 px-2 py-1.5 text-[10px] text-muted-foreground">
|
|
523
|
-
<span>Protocol</span>
|
|
524
|
-
<span>Non-stream</span>
|
|
525
|
-
<span>Stream</span>
|
|
526
|
-
</div>
|
|
527
|
-
{hasAnthropic && <ProviderFormatRow label="Anthropic" results={results?.anthropic} />}
|
|
528
|
-
{hasChat && <ProviderFormatRow label="OpenAI Chat" results={results?.openaiChat} />}
|
|
529
|
-
{hasResponses && (
|
|
530
|
-
<ProviderFormatRow label="OpenAI Responses" results={results?.openaiResponses} />
|
|
531
|
-
)}
|
|
532
|
-
</div>
|
|
533
|
-
</div>
|
|
534
|
-
);
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
function summarizeFormat(results: ProviderFormatTestResults): ProviderTestSummary {
|
|
538
|
-
const summary: ProviderTestSummary = { total: 0, passed: 0, failed: 0, testing: 0 };
|
|
539
|
-
for (const state of [results.nonStreaming, results.streaming]) {
|
|
540
|
-
if (isNotConfiguredState(state)) continue;
|
|
541
|
-
summary.total += 1;
|
|
542
|
-
if (!hasSuccessField(state)) summary.testing += 1;
|
|
543
|
-
else if (state.success) summary.passed += 1;
|
|
544
|
-
else summary.failed += 1;
|
|
545
|
-
}
|
|
546
|
-
return summary;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
function FormatSummaryBadge({
|
|
550
|
-
label,
|
|
551
|
-
results,
|
|
552
|
-
}: {
|
|
553
|
-
label: string;
|
|
554
|
-
results: ProviderFormatTestResults;
|
|
555
|
-
}): JSX.Element {
|
|
556
|
-
const summary = summarizeFormat(results);
|
|
557
|
-
const className =
|
|
558
|
-
summary.failed > 0
|
|
559
|
-
? "border-status-danger/30 bg-status-danger/10 text-status-danger"
|
|
560
|
-
: summary.testing > 0
|
|
561
|
-
? "border-status-watch/30 bg-status-watch/10 text-status-watch"
|
|
562
|
-
: summary.total > 0 && summary.passed === summary.total
|
|
563
|
-
? "border-status-success/30 bg-status-success/10 text-status-success"
|
|
564
|
-
: "border-border/60 bg-transparent text-muted-foreground";
|
|
565
|
-
const value =
|
|
566
|
-
summary.failed > 0
|
|
567
|
-
? `${summary.failed} fail`
|
|
568
|
-
: summary.testing > 0
|
|
569
|
-
? "running"
|
|
570
|
-
: summary.total === 0
|
|
571
|
-
? "off"
|
|
572
|
-
: `${summary.passed}/${summary.total}`;
|
|
573
|
-
return (
|
|
574
|
-
<span className={cn("rounded border px-1.5 py-0.5 text-[10px]", className)}>
|
|
575
|
-
{label} {value}
|
|
576
|
-
</span>
|
|
577
|
-
);
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
function ModelResultRow({
|
|
581
|
-
provider,
|
|
582
|
-
modelName,
|
|
583
|
-
results,
|
|
584
|
-
expanded,
|
|
585
|
-
onToggle,
|
|
586
|
-
}: {
|
|
587
|
-
provider: ProviderConfig;
|
|
588
|
-
modelName: string;
|
|
589
|
-
results: ProviderModelTestResults;
|
|
590
|
-
expanded: boolean;
|
|
591
|
-
onToggle: () => void;
|
|
592
|
-
}): JSX.Element {
|
|
593
|
-
return (
|
|
594
|
-
<div className="min-w-0">
|
|
595
|
-
<button
|
|
596
|
-
type="button"
|
|
597
|
-
onClick={onToggle}
|
|
598
|
-
className="flex w-full min-w-0 flex-wrap items-center gap-2 px-2 py-2 text-left transition-colors hover:bg-muted/20"
|
|
599
|
-
aria-expanded={expanded}
|
|
600
|
-
>
|
|
601
|
-
{expanded ? (
|
|
602
|
-
<ChevronDown className="size-3.5 shrink-0 text-muted-foreground" />
|
|
603
|
-
) : (
|
|
604
|
-
<ChevronRight className="size-3.5 shrink-0 text-muted-foreground" />
|
|
605
|
-
)}
|
|
606
|
-
<span className="min-w-[8rem] flex-1 truncate text-xs font-medium" title={modelName}>
|
|
607
|
-
{modelName}
|
|
608
|
-
</span>
|
|
609
|
-
<span className="flex flex-wrap items-center justify-end gap-1">
|
|
610
|
-
{providerHasEndpoint(provider.anthropicBaseUrl) && (
|
|
611
|
-
<FormatSummaryBadge label="A" results={results.anthropic} />
|
|
612
|
-
)}
|
|
613
|
-
{providerHasEndpoint(provider.openaiBaseUrl) && (
|
|
614
|
-
<FormatSummaryBadge label="Chat" results={results.openaiChat} />
|
|
615
|
-
)}
|
|
616
|
-
{providerHasEndpoint(provider.openaiResponsesBaseUrl) && (
|
|
617
|
-
<FormatSummaryBadge label="Resp" results={results.openaiResponses} />
|
|
618
|
-
)}
|
|
619
|
-
</span>
|
|
620
|
-
</button>
|
|
621
|
-
{expanded && (
|
|
622
|
-
<div className="px-2 pb-3">
|
|
623
|
-
<ProviderTestMatrix provider={provider} results={results} />
|
|
624
|
-
</div>
|
|
625
|
-
)}
|
|
626
|
-
</div>
|
|
627
|
-
);
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
export function ProviderModelTestResultsView({
|
|
631
|
-
provider,
|
|
632
|
-
testResults,
|
|
633
|
-
}: {
|
|
634
|
-
provider: ProviderConfig;
|
|
635
|
-
testResults: TestResults;
|
|
636
|
-
}): JSX.Element | null {
|
|
637
|
-
const entries = Object.entries(testResults.models ?? {});
|
|
638
|
-
const [showModels, setShowModels] = useState(false);
|
|
639
|
-
const [expandedModelName, setExpandedModelName] = useState<string | null>(null);
|
|
640
|
-
if (entries.length === 0) return null;
|
|
641
|
-
|
|
642
|
-
return (
|
|
643
|
-
<section className="space-y-2">
|
|
644
|
-
<button
|
|
645
|
-
type="button"
|
|
646
|
-
onClick={() => setShowModels((value) => !value)}
|
|
647
|
-
className="flex w-full items-center gap-1.5 text-left text-xs font-medium text-foreground/80 transition-colors hover:text-foreground"
|
|
648
|
-
aria-expanded={showModels}
|
|
649
|
-
>
|
|
650
|
-
{showModels ? <ChevronDown className="size-3.5" /> : <ChevronRight className="size-3.5" />}
|
|
651
|
-
Model probes
|
|
652
|
-
<span className="rounded bg-muted/50 px-1.5 py-0.5 font-mono text-[10px] text-muted-foreground">
|
|
653
|
-
{entries.length}
|
|
654
|
-
</span>
|
|
655
|
-
</button>
|
|
656
|
-
{showModels && (
|
|
657
|
-
<div className="min-w-0 divide-y divide-border/60 rounded-md border border-border/70">
|
|
658
|
-
{entries.map(([modelName, modelResults]) => (
|
|
659
|
-
<ModelResultRow
|
|
660
|
-
key={modelName}
|
|
661
|
-
provider={provider}
|
|
662
|
-
modelName={modelName}
|
|
663
|
-
results={modelResults}
|
|
664
|
-
expanded={expandedModelName === modelName}
|
|
665
|
-
onToggle={() =>
|
|
666
|
-
setExpandedModelName((current) => (current === modelName ? null : modelName))
|
|
667
|
-
}
|
|
668
|
-
/>
|
|
669
|
-
))}
|
|
670
|
-
</div>
|
|
671
|
-
)}
|
|
672
|
-
</section>
|
|
673
|
-
);
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
export function ProviderTestPill({
|
|
677
|
-
provider,
|
|
678
|
-
testResults,
|
|
679
|
-
isTesting,
|
|
680
|
-
}: {
|
|
681
|
-
provider: ProviderConfig;
|
|
682
|
-
testResults: TestResults | undefined;
|
|
683
|
-
isTesting: boolean;
|
|
684
|
-
}): JSX.Element {
|
|
685
|
-
const hasEndpoint =
|
|
686
|
-
providerHasEndpoint(provider.anthropicBaseUrl) ||
|
|
687
|
-
providerHasEndpoint(provider.openaiBaseUrl) ||
|
|
688
|
-
providerHasEndpoint(provider.openaiResponsesBaseUrl);
|
|
689
|
-
const summary = summarizeProviderTestResults(provider, testResults);
|
|
690
|
-
|
|
691
|
-
if (isTesting) {
|
|
692
|
-
return (
|
|
693
|
-
<span className="inline-flex items-center gap-1 rounded border border-status-cool/30 bg-status-cool/10 px-2 py-1 text-xs text-status-cool">
|
|
694
|
-
<RotateCw className="size-3 animate-spin" />
|
|
695
|
-
Testing
|
|
696
|
-
</span>
|
|
697
|
-
);
|
|
698
|
-
}
|
|
699
|
-
if (!hasEndpoint) {
|
|
700
|
-
return (
|
|
701
|
-
<span className="rounded border border-border/70 bg-muted/20 px-2 py-1 text-xs text-muted-foreground">
|
|
702
|
-
No endpoint
|
|
703
|
-
</span>
|
|
704
|
-
);
|
|
705
|
-
}
|
|
706
|
-
if (testResults === undefined || summary.total === 0) {
|
|
707
|
-
return (
|
|
708
|
-
<span className="rounded border border-border/70 bg-muted/20 px-2 py-1 text-xs text-muted-foreground">
|
|
709
|
-
Not tested
|
|
710
|
-
</span>
|
|
711
|
-
);
|
|
712
|
-
}
|
|
713
|
-
if (summary.failed > 0) {
|
|
714
|
-
return (
|
|
715
|
-
<span className="inline-flex items-center gap-1 rounded border border-status-danger/30 bg-status-danger/10 px-2 py-1 text-xs text-status-danger">
|
|
716
|
-
<AlertCircle className="size-3" />
|
|
717
|
-
{summary.failed} failed
|
|
718
|
-
</span>
|
|
719
|
-
);
|
|
720
|
-
}
|
|
721
|
-
if (summary.testing > 0 || summary.passed < summary.total) {
|
|
722
|
-
return (
|
|
723
|
-
<span className="rounded border border-status-watch/30 bg-status-watch/10 px-2 py-1 text-xs text-status-watch">
|
|
724
|
-
{summary.passed}/{summary.total}
|
|
725
|
-
</span>
|
|
726
|
-
);
|
|
727
|
-
}
|
|
728
|
-
return (
|
|
729
|
-
<span className="inline-flex items-center gap-1 rounded border border-status-success/30 bg-status-success/10 px-2 py-1 text-xs text-status-success">
|
|
730
|
-
<CheckCircle className="size-3" />
|
|
731
|
-
{summary.passed}/{summary.total}
|
|
732
|
-
</span>
|
|
733
|
-
);
|
|
734
|
-
}
|