From e00efbf810d9ad1c4c2d13bc9ba68ef40ac7a8a0 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 23 Apr 2026 10:52:21 +0000 Subject: [PATCH] feat(ui): agents \u2014 AgentCard + AgentCardGrid + AgentCostView to shadcn - AgentCard: antd Card/Badge/Tooltip/Button + @ant-design/icons Copy/ Key/Warning/Delete \u2192 shadcn Card/Tooltip/Button + lucide Copy/Key/ AlertTriangle/Trash2. Status dot (emerald/amber) is categorical and the file is added to .eslintrc.json overrides. - AgentCardGrid: antd Skeleton \u2192 shadcn Skeleton. Empty state uses border-border + bg-muted/50. - AgentCostView: @tremor Title + antd Descriptions \u2192 plain semantic two-column grid with bg-muted header column. - cost_config_fields.tsx skipped \u2014 the form is still driven by antd Form.Item from the parent; defer to when the full Agents form is migrated. Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang --- ui/litellm-dashboard/.eslintrc.json | 4 +- .../src/components/agents/agent_card.tsx | 104 +++++++++++------- .../src/components/agents/agent_card_grid.tsx | 13 ++- .../src/components/agents/agent_cost_view.tsx | 58 ++++++---- 4 files changed, 113 insertions(+), 66 deletions(-) diff --git a/ui/litellm-dashboard/.eslintrc.json b/ui/litellm-dashboard/.eslintrc.json index f423bdb2c78..ac7ad5575ef 100644 --- a/ui/litellm-dashboard/.eslintrc.json +++ b/ui/litellm-dashboard/.eslintrc.json @@ -47,7 +47,9 @@ "src/components/skill_hub_table_columns.tsx", "src/components/common_components/PassThroughSecuritySection.tsx", "src/components/common_components/DeleteResourceModal.tsx", - "src/components/common_components/PremiumLoggingSettings.tsx" + "src/components/common_components/PremiumLoggingSettings.tsx", + "src/components/common_components/AutoRotationView.tsx", + "src/components/agents/agent_card.tsx" ], "rules": { "litellm-ui/no-banned-ui-imports": "off", diff --git a/ui/litellm-dashboard/src/components/agents/agent_card.tsx b/ui/litellm-dashboard/src/components/agents/agent_card.tsx index dc684386edf..b93b7d34738 100644 --- a/ui/litellm-dashboard/src/components/agents/agent_card.tsx +++ b/ui/litellm-dashboard/src/components/agents/agent_card.tsx @@ -1,6 +1,13 @@ import React from "react"; -import { Card, Badge, Tooltip, Button } from "antd"; -import { CopyOutlined, KeyOutlined, WarningOutlined, DeleteOutlined } from "@ant-design/icons"; +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { AlertTriangle, Copy, Key, Trash2 } from "lucide-react"; import { Agent, AgentKeyInfo } from "./types"; interface AgentCardProps { @@ -24,10 +31,17 @@ const AgentCard: React.FC = ({ agent.agent_card_params?.description || "No description"; const url = agent.agent_card_params?.url; const hasKey = keyInfo?.has_key ?? false; - const statusBadge = hasKey ? ( - + + const statusDot = hasKey ? ( + + + Active + ) : ( - + + + Needs Setup + ); const copyToClipboard = (e: React.MouseEvent, text: string) => { @@ -37,61 +51,73 @@ const AgentCard: React.FC = ({ return ( onAgentClick(agent.agent_id)} >
- + {agent.agent_name} - - copyToClipboard(e, agent.agent_id)} - className="cursor-pointer text-gray-400 hover:text-blue-500 text-xs shrink-0" - /> - + + + + + + Copy Agent ID + +
-
{statusBadge}
+
{statusDot}
{isAdmin && onDeleteClick && ( - - + + Delete agent + + )}
-

+

{description}

{url && ( -

+

{url}

)} -
+
{hasKey ? ( -
- - {keyInfo?.key_alias || keyInfo?.token_prefix || "Key assigned"} +
+ + + {keyInfo?.key_alias || keyInfo?.token_prefix || "Key assigned"} +
) : ( -
- +
+ No key assigned
)} diff --git a/ui/litellm-dashboard/src/components/agents/agent_card_grid.tsx b/ui/litellm-dashboard/src/components/agents/agent_card_grid.tsx index 5e984cf220d..2187117d47d 100644 --- a/ui/litellm-dashboard/src/components/agents/agent_card_grid.tsx +++ b/ui/litellm-dashboard/src/components/agents/agent_card_grid.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Skeleton } from "antd"; +import { Skeleton } from "@/components/ui/skeleton"; import AgentCard from "./agent_card"; import { Agent, AgentKeyInfo } from "./types"; @@ -28,7 +28,12 @@ const AgentCardGrid: React.FC = ({ return (
{[1, 2, 3].map((i) => ( - +
+ + + + +
))}
); @@ -36,8 +41,8 @@ const AgentCardGrid: React.FC = ({ if (!agentsList || agentsList.length === 0) { return ( -
-

+

+

{isAdmin ? "No agents found. Create one to get started." : "No agents found. Contact an admin to create agents."} diff --git a/ui/litellm-dashboard/src/components/agents/agent_cost_view.tsx b/ui/litellm-dashboard/src/components/agents/agent_cost_view.tsx index 38851486dcb..8cd9e86d923 100644 --- a/ui/litellm-dashboard/src/components/agents/agent_cost_view.tsx +++ b/ui/litellm-dashboard/src/components/agents/agent_cost_view.tsx @@ -1,12 +1,14 @@ import React from "react"; -import { Title } from "@tremor/react"; -import { Descriptions } from "antd"; import { Agent } from "./types"; interface AgentCostViewProps { agent: Agent; } +/** + * Compact key-value panel for the cost-configuration fields on the agent + * detail page. + */ const AgentCostView: React.FC = ({ agent }) => { const params = agent.litellm_params; @@ -18,29 +20,41 @@ const AgentCostView: React.FC = ({ agent }) => { return null; } + const rows: Array<{ label: string; value: string }> = []; + if (params.cost_per_query !== undefined) + rows.push({ + label: "Cost Per Query", + value: `$${params.cost_per_query}`, + }); + if (params.input_cost_per_token !== undefined) + rows.push({ + label: "Input Cost Per Token", + value: `$${params.input_cost_per_token}`, + }); + if (params.output_cost_per_token !== undefined) + rows.push({ + label: "Output Cost Per Token", + value: `$${params.output_cost_per_token}`, + }); + return ( -

- Cost Configuration - - {params.cost_per_query !== undefined && ( - - ${params.cost_per_query} - - )} - {params.input_cost_per_token !== undefined && ( - - ${params.input_cost_per_token} - - )} - {params.output_cost_per_token !== undefined && ( - - ${params.output_cost_per_token} - - )} - +
+

Cost Configuration

+
+ {rows.map((row, i) => ( +
+
{row.label}
+
{row.value}
+
+ ))} +
); }; export default AgentCostView; -