From d11c97ac51b9b36742ea16e9b6b4553582bb7eb4 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Wed, 11 Feb 2026 17:57:37 -0800 Subject: [PATCH] commit new expansion --- .../src/components/view_logs/McpChildRows.tsx | 222 ++++++++++++++++++ .../src/components/view_logs/table.tsx | 25 +- 2 files changed, 238 insertions(+), 9 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/view_logs/McpChildRows.tsx diff --git a/ui/litellm-dashboard/src/components/view_logs/McpChildRows.tsx b/ui/litellm-dashboard/src/components/view_logs/McpChildRows.tsx new file mode 100644 index 00000000000..ed63011eaa0 --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/McpChildRows.tsx @@ -0,0 +1,222 @@ +import { useQuery } from "@tanstack/react-query"; +import type { Row } from "@tanstack/react-table"; +import { Tooltip } from "antd"; +import { TableRow, TableCell } from "@tremor/react"; +import { getSpendString } from "@/utils/dataUtils"; +import { sessionSpendLogsCall } from "../networking"; +import type { LogEntry } from "./columns"; +import { TimeCell } from "./time_cell"; +import { getProviderLogoAndName } from "../provider_info_helpers"; + +interface SessionChildRowsProps { + row: Row; + accessToken: string; + onChildClick?: (log: LogEntry) => void; +} + +const MCP_CALL_TYPES = ["call_mcp_tool", "list_mcp_tools"]; + +const LlmBadge = () => ( + + + + + LLM + +); + +const McpBadge = () => ( + + + + + MCP + +); + +export function SessionChildRows({ row, accessToken, onChildClick }: SessionChildRowsProps) { + const sessionId = row.original.session_id; + + const { data: children, isLoading } = useQuery({ + queryKey: ["sessionChildren", sessionId], + queryFn: async () => { + if (!sessionId) return []; + const response = await sessionSpendLogsCall(accessToken, sessionId); + const allLogs: LogEntry[] = response.data || response || []; + // Sort chronologically + return allLogs.sort((a, b) => + new Date(a.startTime).getTime() - new Date(b.startTime).getTime() + ); + }, + enabled: !!sessionId && !!accessToken, + staleTime: 60_000, + }); + + if (isLoading) { + return ( + + + Loading session calls... + + + ); + } + + if (!children || children.length === 0) { + return ( + + + No calls found + + + ); + } + + return ( + <> + {children.map((child) => { + const isMcp = MCP_CALL_TYPES.includes(child.call_type); + const modelOrTool = isMcp + ? (child.model?.replace("MCP: ", "") || "unknown") + : (child.model || "-"); + const serverName = isMcp + ? (child.metadata?.mcp_tool_call_metadata?.mcp_server_name || "") + : ""; + const provider = child.custom_llm_provider || ""; + const logoUrl = isMcp + ? (child.metadata?.mcp_tool_call_metadata?.mcp_server_logo_url || "") + : (provider ? getProviderLogoAndName(provider).logo : ""); + const duration = + child.startTime && child.endTime + ? ((Date.parse(child.endTime) - Date.parse(child.startTime)) / 1000).toFixed(3) + : "-"; + const status = (child.metadata?.status || "Success").toLowerCase(); + const isSuccess = status !== "failure"; + + return ( + onChildClick?.(child)} + > + {/* Expander: branch connector */} + +
+ + + + +
+
+ + {/* Time */} + + + + + {/* Type */} + + {isMcp ? : } + + + {/* Status */} + + + {isSuccess ? "Success" : "Failure"} + + + + {/* Session ID */} + + + {child.session_id || ""} + + + + {/* Request ID */} + + + {child.request_id} + + + + {/* Cost */} + + {getSpendString(child.spend || 0)} + + + {/* Duration */} + + {duration} + + + {/* Team Name */} + + + {child.metadata?.user_api_key_team_alias || "-"} + + + + {/* Key Hash */} + + + {child.metadata?.user_api_key || "-"} + + + + {/* Key Name */} + + + {child.metadata?.user_api_key_alias || "-"} + + + + {/* Model / Tool */} + +
+ {logoUrl && ( + { (e.target as HTMLImageElement).style.display = "none"; }} + /> + )} + + {modelOrTool} + + {serverName && ( + {serverName} + )} +
+
+ + {/* Tokens */} + + {child.total_tokens || "0"} + + + {/* Internal User */} + + {child.user || "-"} + + + {/* End User */} + + {child.end_user || "-"} + + + {/* Tags */} + + - + +
+ ); + })} + + ); +} diff --git a/ui/litellm-dashboard/src/components/view_logs/table.tsx b/ui/litellm-dashboard/src/components/view_logs/table.tsx index fb7706cba19..77cb273d4fe 100644 --- a/ui/litellm-dashboard/src/components/view_logs/table.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/table.tsx @@ -7,8 +7,10 @@ interface DataTableProps { data: TData[]; columns: ColumnDef[]; onRowClick?: (row: TData) => void; - // Legacy props for backward compatibility (audit logs) + /** Renders inside a single colspan cell (used by audit logs) */ renderSubComponent?: (props: { row: Row }) => React.ReactElement; + /** Renders directly in tbody as sibling table rows (used by MCP children) */ + renderChildRows?: (props: { row: Row }) => React.ReactNode; getRowCanExpand?: (row: Row) => boolean; isLoading?: boolean; loadingMessage?: string; @@ -20,24 +22,24 @@ export function DataTable({ columns, onRowClick, renderSubComponent, + renderChildRows, getRowCanExpand, isLoading = false, loadingMessage = "🚅 Loading logs...", noDataMessage = "No logs found", }: DataTableProps) { - // Determine if we're in legacy expansion mode or new drawer mode - const isLegacyMode = !!renderSubComponent && !!getRowCanExpand; + const supportsExpansion = !!(renderSubComponent || renderChildRows) && !!getRowCanExpand; const table = useReactTable({ data, columns, - ...(isLegacyMode && { getRowCanExpand }), + ...(supportsExpansion && { getRowCanExpand }), getRowId: (row: TData, index: number) => { const _row: any = row as any; return _row?.request_id ?? String(index); }, getCoreRowModel: getCoreRowModel(), - ...(isLegacyMode && { getExpandedRowModel: getExpandedRowModel() }), + ...(supportsExpansion && { getExpandedRowModel: getExpandedRowModel() }), }); return ( @@ -69,8 +71,8 @@ export function DataTable({ table.getRowModel().rows.map((row) => ( !isLegacyMode && onRowClick?.(row.original)} + className={`h-8 ${onRowClick ? "cursor-pointer hover:bg-gray-50" : ""}`} + onClick={() => onRowClick?.(row.original)} > {row.getVisibleCells().map((cell) => ( @@ -79,8 +81,13 @@ export function DataTable({ ))} - {/* Legacy expansion mode for audit logs */} - {isLegacyMode && row.getIsExpanded() && renderSubComponent && ( + {/* Child rows rendered as real table rows (MCP children) */} + {supportsExpansion && row.getIsExpanded() && renderChildRows && ( + renderChildRows({ row }) + )} + + {/* Legacy sub-component in colspan cell (audit logs) */} + {supportsExpansion && row.getIsExpanded() && renderSubComponent && !renderChildRows && (
{renderSubComponent({ row })}