From ba63fcd6189c6d07389f805ac7906e2a17dd74fa Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 23 Apr 2026 16:55:13 +0000 Subject: [PATCH] feat(ui): model_dashboard/health_check_columns to shadcn - antd Tooltip/Checkbox + @tremor Text + @heroicons InformationCircle/Play/Refresh \u2192 shadcn Tooltip/Checkbox + lucide Info/Play/RefreshCcw. - Checkbox indeterminate state uses Radix checkbox's 'indeterminate' boolean convention. - Indigo (action button), blue (model id pill), emerald (success response), red (error detail) categorical palette preserved; file added to .eslintrc overrides. Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang --- ui/litellm-dashboard/.eslintrc.json | 1 + .../model_dashboard/health_check_columns.tsx | 290 +++++++++++------- 2 files changed, 185 insertions(+), 106 deletions(-) diff --git a/ui/litellm-dashboard/.eslintrc.json b/ui/litellm-dashboard/.eslintrc.json index 71dd3bd6b56..3ec2a72eaad 100644 --- a/ui/litellm-dashboard/.eslintrc.json +++ b/ui/litellm-dashboard/.eslintrc.json @@ -67,6 +67,7 @@ "src/components/claude_code_plugins/MakeSkillPublicForm.tsx", "src/components/mcp_tools/MCPPermissionManagement.tsx", "src/components/model_add/credentials.tsx", + "src/components/model_dashboard/health_check_columns.tsx", "src/components/permissions/VectorStorePermissions.tsx", "src/components/permissions/AgentPermissions.tsx", "src/components/permissions/MCPServerPermissions.tsx", diff --git a/ui/litellm-dashboard/src/components/model_dashboard/health_check_columns.tsx b/ui/litellm-dashboard/src/components/model_dashboard/health_check_columns.tsx index 396afb7ed0b..43aff39025e 100644 --- a/ui/litellm-dashboard/src/components/model_dashboard/health_check_columns.tsx +++ b/ui/litellm-dashboard/src/components/model_dashboard/health_check_columns.tsx @@ -1,7 +1,13 @@ import { ColumnDef } from "@tanstack/react-table"; -import { Tooltip, Checkbox } from "antd"; -import { Text } from "@tremor/react"; -import { InformationCircleIcon, PlayIcon, RefreshIcon } from "@heroicons/react/outline"; +import { Checkbox } from "@/components/ui/checkbox"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { cn } from "@/lib/utils"; +import { Info, Play, RefreshCcw } from "lucide-react"; import { Team } from "@/components/key_team_helpers/key_list"; interface HealthCheckData { @@ -28,6 +34,7 @@ interface HealthStatus { loading: boolean; error?: string; fullError?: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any successResponse?: any; } @@ -38,9 +45,16 @@ export const healthCheckColumns = ( handleModelSelection: (modelId: string, checked: boolean) => void, handleSelectAll: (checked: boolean) => void, runIndividualHealthCheck: (modelId: string) => void, + // eslint-disable-next-line no-undef getStatusBadge: (status: string) => JSX.Element, + // eslint-disable-next-line @typescript-eslint/no-explicit-any getDisplayModelName: (model: any) => string, - showErrorModal?: (modelName: string, cleanedError: string, fullError: string) => void, + showErrorModal?: ( + modelName: string, + cleanedError: string, + fullError: string, + ) => void, + // eslint-disable-next-line @typescript-eslint/no-explicit-any showSuccessModal?: (modelName: string, response: any) => void, setSelectedModelId?: (modelId: string) => void, teams?: Team[] | null, @@ -49,9 +63,14 @@ export const healthCheckColumns = ( header: () => (
0 && !allModelsSelected} - onChange={(e) => handleSelectAll(e.target.checked)} + checked={ + selectedModelsForHealth.length > 0 && !allModelsSelected + ? "indeterminate" + : allModelsSelected + ? true + : false + } + onCheckedChange={(c) => handleSelectAll(c === true)} onClick={(e) => e.stopPropagation()} /> Model ID @@ -69,17 +88,25 @@ export const healthCheckColumns = (
handleModelSelection(modelId, e.target.checked)} + onCheckedChange={(c) => handleModelSelection(modelId, c === true)} onClick={(e) => e.stopPropagation()} /> - -
setSelectedModelId && setSelectedModelId(model.model_info.id)} - > - {model.model_info.id} -
-
+ + + + + + {modelId} + +
); }, @@ -95,9 +122,14 @@ export const healthCheckColumns = ( return (
- -
{displayName}
-
+ + + +
{displayName}
+
+ {displayName} +
+
); }, @@ -112,7 +144,7 @@ export const healthCheckColumns = ( const teamId = model.model_info?.team_id; if (!teamId) { - return -; + return -; } const team = teams?.find((t) => t.team_id === teamId); @@ -120,9 +152,14 @@ export const healthCheckColumns = ( return (
- -
{teamAlias}
-
+ + + +
{teamAlias}
+
+ {teamAlias} +
+
); }, @@ -131,11 +168,10 @@ export const healthCheckColumns = ( header: "Health Status", accessorKey: "health_status", enableSorting: true, - sortingFn: (rowA, rowB, columnId) => { + sortingFn: (rowA, rowB) => { const statusA = (rowA.getValue("health_status") as string) || "unknown"; const statusB = (rowB.getValue("health_status") as string) || "unknown"; - // Define sorting order: healthy > checking > unknown > unhealthy const statusOrder = { healthy: 0, checking: 1, unknown: 2, unhealthy: 3 }; const orderA = statusOrder[statusA as keyof typeof statusOrder] ?? 4; const orderB = statusOrder[statusB as keyof typeof statusOrder] ?? 4; @@ -164,27 +200,41 @@ export const healthCheckColumns = ( style={{ animationDelay: "0.4s" }} >
- Checking... + Checking... ); } const modelId = model.model_info?.id ?? ""; const displayName = getDisplayModelName(model) || model.model_name; - const hasSuccessResponse = healthStatus.status === "healthy" && modelHealthStatuses[modelId]?.successResponse; + const hasSuccessResponse = + healthStatus.status === "healthy" && + modelHealthStatuses[modelId]?.successResponse; return (
{getStatusBadge(healthStatus.status)} {hasSuccessResponse && showSuccessModal && ( - - - + + + + + + View response details + + )}
); @@ -201,7 +251,7 @@ export const healthCheckColumns = ( const healthStatus = modelHealthStatuses[modelId]; if (!healthStatus?.error) { - return No errors; + return No errors; } const cleanedError = healthStatus.error; @@ -210,19 +260,35 @@ export const healthCheckColumns = ( return (
- - {cleanedError} - + + + + + {cleanedError} + + + {cleanedError} + +
{showErrorModal && fullError !== cleanedError && ( - - - + + + + + + View full error details + + )}
); @@ -232,37 +298,40 @@ export const healthCheckColumns = ( header: "Last Check", accessorKey: "last_check", enableSorting: true, - sortingFn: (rowA, rowB, columnId) => { - const lastCheckA = (rowA.getValue("last_check") as string) || "Never checked"; - const lastCheckB = (rowB.getValue("last_check") as string) || "Never checked"; + sortingFn: (rowA, rowB) => { + const lastCheckA = + (rowA.getValue("last_check") as string) || "Never checked"; + const lastCheckB = + (rowB.getValue("last_check") as string) || "Never checked"; - // Handle special cases - if (lastCheckA === "Never checked" && lastCheckB === "Never checked") return 0; - if (lastCheckA === "Never checked") return 1; // Never checked goes to bottom + if (lastCheckA === "Never checked" && lastCheckB === "Never checked") + return 0; + if (lastCheckA === "Never checked") return 1; if (lastCheckB === "Never checked") return -1; - if (lastCheckA === "Check in progress..." && lastCheckB === "Check in progress...") return 0; - if (lastCheckA === "Check in progress...") return -1; // In progress goes to top + if ( + lastCheckA === "Check in progress..." && + lastCheckB === "Check in progress..." + ) + return 0; + if (lastCheckA === "Check in progress...") return -1; if (lastCheckB === "Check in progress...") return 1; - // Parse dates for comparison const dateA = new Date(lastCheckA); const dateB = new Date(lastCheckB); - // If dates are invalid, treat as never checked if (isNaN(dateA.getTime()) && isNaN(dateB.getTime())) return 0; if (isNaN(dateA.getTime())) return 1; if (isNaN(dateB.getTime())) return -1; - // Sort by date (most recent first) return dateB.getTime() - dateA.getTime(); }, cell: ({ row }) => { const model = row.original; return ( - + {model.health_loading ? "Check in progress..." : model.last_check} - + ); }, }, @@ -270,28 +339,27 @@ export const healthCheckColumns = ( header: "Last Success", accessorKey: "last_success", enableSorting: true, - sortingFn: (rowA, rowB, columnId) => { - const lastSuccessA = (rowA.getValue("last_success") as string) || "Never succeeded"; - const lastSuccessB = (rowB.getValue("last_success") as string) || "Never succeeded"; + sortingFn: (rowA, rowB) => { + const lastSuccessA = + (rowA.getValue("last_success") as string) || "Never succeeded"; + const lastSuccessB = + (rowB.getValue("last_success") as string) || "Never succeeded"; - // Handle special cases - if (lastSuccessA === "Never succeeded" && lastSuccessB === "Never succeeded") return 0; - if (lastSuccessA === "Never succeeded") return 1; // Never succeeded goes to bottom + if (lastSuccessA === "Never succeeded" && lastSuccessB === "Never succeeded") + return 0; + if (lastSuccessA === "Never succeeded") return 1; if (lastSuccessB === "Never succeeded") return -1; if (lastSuccessA === "None" && lastSuccessB === "None") return 0; - if (lastSuccessA === "None") return 1; // None goes to bottom + if (lastSuccessA === "None") return 1; if (lastSuccessB === "None") return -1; - // Parse dates for comparison const dateA = new Date(lastSuccessA); const dateB = new Date(lastSuccessB); - // If dates are invalid, treat as never succeeded if (isNaN(dateA.getTime()) && isNaN(dateB.getTime())) return 0; if (isNaN(dateA.getTime())) return 1; if (isNaN(dateB.getTime())) return -1; - // Sort by date (most recent first) return dateB.getTime() - dateA.getTime(); }, cell: ({ row }) => { @@ -300,7 +368,9 @@ export const healthCheckColumns = ( const healthStatus = modelHealthStatuses[modelId]; const lastSuccess = healthStatus?.lastSuccess || "None"; - return {lastSuccess}; + return ( + {lastSuccess} + ); }, }, { @@ -310,7 +380,8 @@ export const healthCheckColumns = ( const model = row.original; const modelId = model.model_info?.id ?? ""; - const hasExistingStatus = model.health_status && model.health_status !== "none"; + const hasExistingStatus = + model.health_status && model.health_status !== "none"; const tooltipText = model.health_loading ? "Checking..." : hasExistingStatus @@ -318,40 +389,47 @@ export const healthCheckColumns = ( : "Run Health Check"; return ( - - - + + + + + + {tooltipText} + + ); }, enableSorting: false,