From 6fa4d35d7d5c0c9f8a68c2c1646995359c1cb24a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 23 Apr 2026 12:18:00 +0000 Subject: [PATCH] feat(ui): ModelsCell + ResponseMetrics to shadcn - ModelsCell: @tremor Badge/Icon/TableCell/Text + @heroicons Chevron \u2192 shadcn Badge + preserved tremor TableCell (whitelisted) + lucide ChevronDown/ChevronRight. Categorical red/emerald/blue palettes for all-proxy / access_group / direct models; file added to overrides. - ResponseMetrics: antd Tooltip + @ant-design icons (Clock/Number/ Import/Export/Bulb/Tool/Dollar) \u2192 shadcn Tooltip + lucide equivalents wrapped in a TooltipIconStat helper. Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang --- ui/litellm-dashboard/.eslintrc.json | 1 + .../components/TeamsTable/ModelsCell.tsx | 84 ++++++---- .../playground/chat_ui/ResponseMetrics.tsx | 146 +++++++++++------- 3 files changed, 144 insertions(+), 87 deletions(-) diff --git a/ui/litellm-dashboard/.eslintrc.json b/ui/litellm-dashboard/.eslintrc.json index 5d78bd2fec6..86e171d8933 100644 --- a/ui/litellm-dashboard/.eslintrc.json +++ b/ui/litellm-dashboard/.eslintrc.json @@ -59,6 +59,7 @@ "src/components/guardrails/content_filter/PatternTable.tsx", "src/components/guardrails/content_filter/ContentFilterDisplay.tsx", "src/components/prompts/prompt_editor_view/PromptEditorHeader.tsx", + "src/app/(dashboard)/teams/components/TeamsTable/ModelsCell.tsx", "src/components/policies/impact_preview_alert.tsx", "src/components/policies/impact_popover.tsx", "src/components/DeletedKeysPage/DeletedKeysPage.tsx", diff --git a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/ModelsCell.tsx b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/ModelsCell.tsx index 62a7fdb783f..11c8ed7c4d8 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/ModelsCell.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/teams/components/TeamsTable/ModelsCell.tsx @@ -1,5 +1,8 @@ -import { Badge, Icon, TableCell, Text } from "@tremor/react"; -import { ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/outline"; +import { Badge } from "@/components/ui/badge"; +// eslint-disable-next-line litellm-ui/no-banned-ui-imports +import { TableCell } from "@tremor/react"; +import { ChevronDown, ChevronRight } from "lucide-react"; +import { cn } from "@/lib/utils"; import { getModelDisplayName } from "@/components/key_team_helpers/fetch_available_models_team_key"; import React, { useMemo, useState } from "react"; import { Team } from "@/components/key_team_helpers/key_list"; @@ -16,7 +19,10 @@ interface ModelEntry { const ModelsCell = ({ team }: ModelsCellProps) => { const [expandedAccordion, setExpandedAccordion] = useState(false); - const isAllModels = !team.models || team.models.length === 0 || team.models.includes("all-proxy-models"); + const isAllModels = + !team.models || + team.models.length === 0 || + team.models.includes("all-proxy-models"); const modelEntries: ModelEntry[] = useMemo(() => { if (isAllModels) return []; @@ -33,21 +39,33 @@ const ModelsCell = ({ team }: ModelsCellProps) => { const renderBadge = (entry: ModelEntry, index: number) => { if (entry.name === "all-proxy-models") { return ( - - All Proxy Models + + All Proxy Models ); } const displayName = getModelDisplayName(entry.name); - const truncated = displayName.length > 30 ? `${displayName.slice(0, 30)}...` : displayName; + const truncated = + displayName.length > 30 ? `${displayName.slice(0, 30)}...` : displayName; return ( - {truncated} + {truncated} ); }; @@ -63,36 +81,46 @@ const ModelsCell = ({ team }: ModelsCellProps) => { >
{modelEntries.length === 0 ? ( - - All Proxy Models + + All Proxy Models ) : (
{modelEntries.length > 3 && ( -
- { - setExpandedAccordion((prev) => !prev); - }} - /> -
+ )}
- {modelEntries.slice(0, 3).map((entry, index) => renderBadge(entry, index))} + {modelEntries + .slice(0, 3) + .map((entry, index) => renderBadge(entry, index))} {modelEntries.length > 3 && !expandedAccordion && ( - - - +{modelEntries.length - 3} {modelEntries.length - 3 === 1 ? "more model" : "more models"} - + + +{modelEntries.length - 3}{" "} + {modelEntries.length - 3 === 1 + ? "more model" + : "more models"} )} {expandedAccordion && (
- {modelEntries.slice(3).map((entry, index) => renderBadge(entry, index + 3))} + {modelEntries + .slice(3) + .map((entry, index) => renderBadge(entry, index + 3))}
)}
diff --git a/ui/litellm-dashboard/src/components/playground/chat_ui/ResponseMetrics.tsx b/ui/litellm-dashboard/src/components/playground/chat_ui/ResponseMetrics.tsx index 01eafa4d534..c7fce8dfc77 100644 --- a/ui/litellm-dashboard/src/components/playground/chat_ui/ResponseMetrics.tsx +++ b/ui/litellm-dashboard/src/components/playground/chat_ui/ResponseMetrics.tsx @@ -1,14 +1,19 @@ import React from "react"; -import { Tooltip } from "antd"; import { - ClockCircleOutlined, - NumberOutlined, - ImportOutlined, - ExportOutlined, - BulbOutlined, - ToolOutlined, - DollarOutlined, -} from "@ant-design/icons"; + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { + Clock, + DollarSign, + Hash, + LogIn, + LogOut, + Lightbulb, + Wrench, +} from "lucide-react"; export interface TokenUsage { completionTokens?: number; @@ -25,81 +30,104 @@ interface ResponseMetricsProps { toolName?: string; } -const ResponseMetrics: React.FC = ({ timeToFirstToken, totalLatency, usage, toolName }) => { +const TooltipIconStat: React.FC<{ + tooltip: string; + icon: React.ReactNode; + children: React.ReactNode; +}> = ({ tooltip, icon, children }) => ( + + + +
+ {icon} + {children} +
+
+ {tooltip} +
+
+); + +const ResponseMetrics: React.FC = ({ + timeToFirstToken, + totalLatency, + usage, + toolName, +}) => { if (!timeToFirstToken && !totalLatency && !usage) return null; return ( -
+
{timeToFirstToken !== undefined && ( - -
- - TTFT: {(timeToFirstToken / 1000).toFixed(2)}s -
-
+ } + > + TTFT: {(timeToFirstToken / 1000).toFixed(2)}s + )} {totalLatency !== undefined && ( - -
- - Total Latency: {(totalLatency / 1000).toFixed(2)}s -
-
+ } + > + Total Latency: {(totalLatency / 1000).toFixed(2)}s + )} {usage?.promptTokens !== undefined && ( - -
- - In: {usage.promptTokens} -
-
+ } + > + In: {usage.promptTokens} + )} {usage?.completionTokens !== undefined && ( - -
- - Out: {usage.completionTokens} -
-
+ } + > + Out: {usage.completionTokens} + )} {usage?.reasoningTokens !== undefined && ( - -
- - Reasoning: {usage.reasoningTokens} -
-
+ } + > + Reasoning: {usage.reasoningTokens} + )} {usage?.totalTokens !== undefined && ( - -
- - Total: {usage.totalTokens} -
-
+ } + > + Total: {usage.totalTokens} + )} {usage?.cost !== undefined && ( - -
- - ${usage.cost.toFixed(6)} -
-
+ } + > + ${usage.cost.toFixed(6)} + )} {toolName && ( - -
- - Tool: {toolName} -
-
+ } + > + Tool: {toolName} + )}
);