diff --git a/ui/litellm-dashboard/src/components/agents.tsx b/ui/litellm-dashboard/src/components/agents.tsx index 542d890e12f..27b76951008 100644 --- a/ui/litellm-dashboard/src/components/agents.tsx +++ b/ui/litellm-dashboard/src/components/agents.tsx @@ -1,18 +1,39 @@ import React, { useState, useEffect } from "react"; import { - Button, - Card, + Alert, + AlertDescription, + AlertTitle, +} from "@/components/ui/alert"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; +import { Skeleton } from "@/components/ui/skeleton"; +import { Switch } from "@/components/ui/switch"; +import { Table, TableBody, TableCell, TableHead, - TableHeaderCell, + TableHeader, TableRow, - Badge, - Text, -} from "@tremor/react"; -import { Modal, Alert, Tooltip, Skeleton, Switch } from "antd"; -import { CheckCircleOutlined } from "@ant-design/icons"; +} from "@/components/ui/table"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { CheckCircle, Info } from "lucide-react"; import { getAgentsList, deleteAgentCall, keyListCall } from "./networking"; import AddAgentForm from "./agents/add_agent_form"; import { isAdminRole } from "@/utils/roles"; @@ -33,26 +54,35 @@ interface AgentsResponse { agents: Agent[]; } -const AgentsPanel: React.FC = ({ accessToken, userRole, teams }) => { +const AgentsPanel: React.FC = ({ + accessToken, + userRole, + teams, +}) => { const [agentsList, setAgentsList] = useState([]); - const [keyInfoMap, setKeyInfoMap] = useState>({}); + const [keyInfoMap, setKeyInfoMap] = useState>( + {}, + ); const [isAddModalVisible, setIsAddModalVisible] = useState(false); const [isLoading, setIsLoading] = useState(false); const [isDeleting, setIsDeleting] = useState(false); - const [agentToDelete, setAgentToDelete] = useState<{ id: string; name: string } | null>(null); + const [agentToDelete, setAgentToDelete] = useState<{ + id: string; + name: string; + } | null>(null); const [selectedAgentId, setSelectedAgentId] = useState(null); const [healthCheckEnabled, setHealthCheckEnabled] = useState(false); const isAdmin = userRole ? isAdminRole(userRole) : false; const fetchAgents = async (healthCheck?: boolean) => { - if (!accessToken) { - return; - } - + if (!accessToken) return; setIsLoading(true); try { - const response: AgentsResponse = await getAgentsList(accessToken, healthCheck ?? healthCheckEnabled); + const response: AgentsResponse = await getAgentsList( + accessToken, + healthCheck ?? healthCheckEnabled, + ); setAgentsList(response.agents || []); } catch (error) { console.error("Error fetching agents:", error); @@ -72,7 +102,7 @@ const AgentsPanel: React.FC = ({ accessToken, userRole, teams null, null, 1, - 500 + 500, ); const map: Record = {}; for (const key of keys) { @@ -95,6 +125,7 @@ const AgentsPanel: React.FC = ({ accessToken, userRole, teams useEffect(() => { fetchAgents(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [accessToken]); useEffect(() => { @@ -103,6 +134,7 @@ const AgentsPanel: React.FC = ({ accessToken, userRole, teams } else if (agentsList.length === 0) { setKeyInfoMap({}); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [accessToken, agentsList.length]); const handleHealthCheckToggle = (checked: boolean) => { @@ -111,31 +143,18 @@ const AgentsPanel: React.FC = ({ accessToken, userRole, teams }; const handleAddAgent = () => { - if (selectedAgentId) { - setSelectedAgentId(null); - } + if (selectedAgentId) setSelectedAgentId(null); setIsAddModalVisible(true); }; - const handleCloseModal = () => { - setIsAddModalVisible(false); - }; - - const handleSuccess = () => { - fetchAgents(); - }; - - const handleDeleteClick = (agentId: string, agentName: string) => { - setAgentToDelete({ id: agentId, name: agentName }); - }; - const handleDeleteConfirm = async () => { if (!agentToDelete || !accessToken) return; - setIsDeleting(true); try { await deleteAgentCall(accessToken, agentToDelete.id); - NotificationsManager.success(`Agent "${agentToDelete.name}" deleted successfully`); + NotificationsManager.success( + `Agent "${agentToDelete.name}" deleted successfully`, + ); fetchAgents(); } catch (error) { console.error("Error deleting agent:", error); @@ -146,10 +165,6 @@ const AgentsPanel: React.FC = ({ accessToken, userRole, teams } }; - const handleDeleteCancel = () => { - setAgentToDelete(null); - }; - const sortedAgents = [...agentsList].sort((a, b) => { const dateA = a.created_at ? new Date(a.created_at).getTime() : 0; const dateB = b.created_at ? new Date(b.created_at).getTime() : 0; @@ -162,32 +177,49 @@ const AgentsPanel: React.FC = ({ accessToken, userRole, teams

Agents

-

List of A2A-spec agents that are available to be used in your organization. Go to AI Hub, to make agents public.

- +

+ List of A2A-spec agents that are available to be used in your + organization. Go to AI Hub, to make agents public. +

+ + + Why do agents need keys? + + Keys scope access to an agent and allow it to call MCP tools. + Assign a key when creating an agent or from the Virtual Keys page. + +
{isAdmin && ( )} - -
- - Health Check - -
-
+ + + +
+ + + Health Check + + +
+
+ + When enabled, only agents with reachable URLs are shown + +
+
@@ -199,74 +231,90 @@ const AgentsPanel: React.FC = ({ accessToken, userRole, teams isAdmin={isAdmin} /> ) : ( - + {isLoading ? ( - +
+ + + +
) : ( - + - Agent Name - Agent ID - Spend (USD) - Model - Created - Status - {isAdmin && Actions} + Agent Name + Agent ID + Spend (USD) + Model + Created + Status + {isAdmin && Actions} - + {sortedAgents.length === 0 ? ( - - No agents found. Click "+ Add New Agent" to create one. + + No agents found. Click "+ Add New Agent" to + create one. ) : ( sortedAgents.map((agent) => ( + {agent.agent_name} - {agent.agent_name} + + + + + + {agent.agent_id} + + - - - + {formatNumberWithCommas(agent.spend, 4)} - {formatNumberWithCommas(agent.spend, 4)} - - - + {agent.litellm_params?.model || "N/A"} - - {agent.created_at - ? new Date(agent.created_at).toLocaleDateString() - : "N/A"} - + {agent.created_at + ? new Date(agent.created_at).toLocaleDateString() + : "N/A"} {keyInfoMap[agent.agent_id]?.has_key ? ( - Active + Active ) : ( - Needs Setup + Needs Setup )} {isAdmin && ( handleDeleteClick(agent.agent_id, agent.agent_name)} + onClick={() => + setAgentToDelete({ + id: agent.agent_id, + name: agent.agent_name, + }) + } /> )} @@ -281,26 +329,37 @@ const AgentsPanel: React.FC = ({ accessToken, userRole, teams setIsAddModalVisible(false)} accessToken={accessToken} - onSuccess={handleSuccess} + onSuccess={fetchAgents} teams={teams} /> - {agentToDelete && ( - -

Are you sure you want to delete agent: {agentToDelete.name}?

-

This action cannot be undone.

-
- )} + (!o ? setAgentToDelete(null) : undefined)} + > + + + Delete Agent + + Are you sure you want to delete agent: {agentToDelete?.name}? +
+ This action cannot be undone. +
+
+ + Cancel + + {isDeleting ? "Deleting…" : "Delete"} + + +
+
); };