diff --git a/ui/litellm-dashboard/.eslintrc.json b/ui/litellm-dashboard/.eslintrc.json index a16d6a90d1e..726da817a6c 100644 --- a/ui/litellm-dashboard/.eslintrc.json +++ b/ui/litellm-dashboard/.eslintrc.json @@ -62,6 +62,7 @@ "src/components/prompts/prompt_table.tsx", "src/components/common_components/ModelAliasManager.tsx", "src/components/mcp_tools/mcp_server_columns.tsx", + "src/components/playground/chat_ui/A2AMetrics.tsx", "src/components/permissions/VectorStorePermissions.tsx", "src/components/permissions/AgentPermissions.tsx", "src/components/permissions/MCPServerPermissions.tsx", diff --git a/ui/litellm-dashboard/src/components/playground/chat_ui/A2AMetrics.tsx b/ui/litellm-dashboard/src/components/playground/chat_ui/A2AMetrics.tsx index 3b2ea0eb50b..e1c38346c0f 100644 --- a/ui/litellm-dashboard/src/components/playground/chat_ui/A2AMetrics.tsx +++ b/ui/litellm-dashboard/src/components/playground/chat_ui/A2AMetrics.tsx @@ -1,17 +1,24 @@ import React, { useState } from "react"; -import { Tooltip, Button } from "antd"; +import { Button } from "@/components/ui/button"; import { - CheckCircleOutlined, - ClockCircleOutlined, - LoadingOutlined, - ExclamationCircleOutlined, - CopyOutlined, - DownOutlined, - RightOutlined, - LinkOutlined, - FileTextOutlined, - RobotOutlined, -} from "@ant-design/icons"; + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { cn } from "@/lib/utils"; +import { + AlertCircle, + Bot, + CheckCircle2, + ChevronDown, + ChevronRight, + Clock, + Copy, + FileText, + Link as LinkIcon, + Loader2, +} from "lucide-react"; export interface A2ATaskMetadata { taskId?: string; @@ -21,6 +28,7 @@ export interface A2ATaskMetadata { timestamp?: string; message?: string; }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any metadata?: Record; } @@ -33,30 +41,30 @@ interface A2AMetricsProps { const getStatusIcon = (state?: string) => { switch (state) { case "completed": - return ; + return ; case "working": case "submitted": - return ; + return ; case "failed": case "canceled": - return ; + return ; default: - return ; + return ; } }; const getStatusColor = (state?: string) => { switch (state) { case "completed": - return "bg-green-100 text-green-700"; + return "bg-emerald-100 text-emerald-700 dark:bg-emerald-950/30 dark:text-emerald-300"; case "working": case "submitted": - return "bg-blue-100 text-blue-700"; + return "bg-blue-100 text-blue-700 dark:bg-blue-950/30 dark:text-blue-300"; case "failed": case "canceled": - return "bg-red-100 text-red-700"; + return "bg-red-100 text-red-700 dark:bg-red-950/30 dark:text-red-300"; default: - return "bg-gray-100 text-gray-700"; + return "bg-muted text-muted-foreground"; } }; @@ -79,7 +87,11 @@ const copyToClipboard = (text: string) => { navigator.clipboard.writeText(text); }; -const A2AMetrics: React.FC = ({ a2aMetadata, timeToFirstToken, totalLatency }) => { +const A2AMetrics: React.FC = ({ + a2aMetadata, + timeToFirstToken, + totalLatency, +}) => { const [showDetails, setShowDetails] = useState(false); if (!a2aMetadata && !timeToFirstToken && !totalLatency) return null; @@ -88,115 +100,138 @@ const A2AMetrics: React.FC = ({ a2aMetadata, timeToFirstToken, const formattedTime = formatTimestamp(status?.timestamp); return ( -
- {/* A2A Metadata Header */} -
- - A2A Metadata +
+
+ + A2A Metadata
- {/* Main metrics row */} -
- {/* Status badge */} +
{status?.state && ( - + {getStatusIcon(status.state)} - {status.state} + {status.state} )} - {/* Timestamp */} {formattedTime && ( - - - - {formattedTime} - - + + + + + + {formattedTime} + + + {status?.timestamp} + + )} - {/* Latency */} {totalLatency !== undefined && ( - - - - {(totalLatency / 1000).toFixed(2)}s - - + + + + + + {(totalLatency / 1000).toFixed(2)}s + + + Total latency + + )} - {/* Time to first token */} {timeToFirstToken !== undefined && ( - - - TTFT: {(timeToFirstToken / 1000).toFixed(2)}s - - + + + + + TTFT: {(timeToFirstToken / 1000).toFixed(2)}s + + + Time to first token + + )}
- {/* IDs row */} -
- {/* Task ID */} +
{taskId && ( - - copyToClipboard(taskId)} - > - - Task: {truncateId(taskId)} - - - + + + + copyToClipboard(taskId)} + > + + Task: {truncateId(taskId)} + + + + Click to copy: {taskId} + + )} - {/* Context/Session ID */} {contextId && ( - - copyToClipboard(contextId)} - > - - Session: {truncateId(contextId)} - - - + + + + copyToClipboard(contextId)} + > + + Session: {truncateId(contextId)} + + + + Click to copy: {contextId} + + )} - {/* Details toggle */} {(metadata || status?.message) && ( )}
- {/* Expandable details panel */} {showDetails && ( -
- {/* Status message */} +
{status?.message && (
- Status Message: + Status Message: {status.message}
)} - {/* Full IDs */} {taskId && (
- Task ID: - {taskId} - Task ID: + + {taskId} + + copyToClipboard(taskId)} />
@@ -204,20 +239,21 @@ const A2AMetrics: React.FC = ({ a2aMetadata, timeToFirstToken, {contextId && (
- Session ID: - {contextId} - Session ID: + + {contextId} + + copyToClipboard(contextId)} />
)} - {/* Metadata fields */} {metadata && Object.keys(metadata).length > 0 && (
- Custom Metadata: -
+              Custom Metadata:
+              
                 {JSON.stringify(metadata, null, 2)}