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,