diff --git a/ui/litellm-dashboard/src/components/routing_groups/RoutingGroupsTable.tsx b/ui/litellm-dashboard/src/components/routing_groups/RoutingGroupsTable.tsx new file mode 100644 index 00000000000..7d12514d5c8 --- /dev/null +++ b/ui/litellm-dashboard/src/components/routing_groups/RoutingGroupsTable.tsx @@ -0,0 +1,168 @@ +"use client"; + +import React, { useEffect, useState } from "react"; +import { Button, Popconfirm, Table, Tag, Typography } from "antd"; +import { DeleteOutlined, EditOutlined, ThunderboltOutlined } from "@ant-design/icons"; +import { routingGroupDeleteCall, routingGroupListCall } from "@/components/networking"; + +interface RoutingGroupsTableProps { + accessToken: string | null; + refreshKey: number; + onEdit: (group: Record) => void; + onTest?: (group: Record) => void; +} + +const strategyColors: Record = { + "priority-failover": "blue", + weighted: "green", + "cost-based-routing": "gold", + "latency-based-routing": "cyan", + "least-busy": "purple", + "usage-based-routing-v2": "orange", + "simple-shuffle": "geekblue", +}; + +const strategyLabels: Record = { + "priority-failover": "Priority Failover", + weighted: "Weighted", + "cost-based-routing": "Cost-Based", + "latency-based-routing": "Latency-Based", + "least-busy": "Least Busy", + "usage-based-routing-v2": "Usage-Based", + "simple-shuffle": "Round Robin", +}; + +export default function RoutingGroupsTable({ + accessToken, + refreshKey, + onEdit, + onTest, +}: RoutingGroupsTableProps) { + const [groups, setGroups] = useState[]>([]); + const [loading, setLoading] = useState(false); + + useEffect(() => { + if (!accessToken) return; + setLoading(true); + routingGroupListCall(accessToken, 1, 50) + .then((resp) => { + setGroups(resp.routing_groups || []); + }) + .catch(console.error) + .finally(() => setLoading(false)); + }, [accessToken, refreshKey]); + + const handleDelete = async (groupId: string) => { + if (!accessToken) return; + try { + await routingGroupDeleteCall(accessToken, groupId); + setGroups((prev) => + prev.filter((g) => (g.routing_group_id as string) !== groupId) + ); + } catch (err) { + console.error("Failed to delete routing group:", err); + } + }; + + const columns = [ + { + title: "Name", + dataIndex: "routing_group_name", + key: "routing_group_name", + render: (name: string) => ( + {name} + ), + }, + { + title: "Strategy", + dataIndex: "routing_strategy", + key: "routing_strategy", + render: (strategy: string) => { + const label = strategyLabels[strategy] ?? strategy; + const color = strategyColors[strategy] ?? "default"; + return {label}; + }, + }, + { + title: "Deployments", + dataIndex: "deployments", + key: "deployments", + render: (deployments: unknown) => { + const count = Array.isArray(deployments) ? deployments.length : 0; + return {count}; + }, + }, + { + title: "Status", + dataIndex: "status", + key: "status", + render: (status: string) => { + const isActive = !status || status === "active"; + return ( + + {isActive ? "Active" : "Inactive"} + + ); + }, + }, + { + title: "Created", + dataIndex: "created_at", + key: "created_at", + render: (created: string) => { + if (!created) return "-"; + return new Date(created).toLocaleDateString(); + }, + }, + { + title: "Actions", + key: "actions", + render: (_: unknown, record: Record) => { + const groupId = record.routing_group_id as string; + return ( +
+ {onTest && ( + + )} + + handleDelete(groupId)} + okText="Delete" + okButtonProps={{ danger: true }} + cancelText="Cancel" + > + + +
+ ); + }, + }, + ]; + + return ( + (record.routing_group_id as string) ?? Math.random().toString()} + pagination={{ pageSize: 20 }} + /> + ); +}