From 2f2bae26e466abbc5aa9d9632fd5cf06b5ff3f6e Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Sat, 28 Feb 2026 18:00:40 -0800 Subject: [PATCH] feat(ui): add LiveTester component --- .../components/routing_groups/LiveTester.tsx | 208 ++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 ui/litellm-dashboard/src/components/routing_groups/LiveTester.tsx diff --git a/ui/litellm-dashboard/src/components/routing_groups/LiveTester.tsx b/ui/litellm-dashboard/src/components/routing_groups/LiveTester.tsx new file mode 100644 index 00000000000..d614ff68bb7 --- /dev/null +++ b/ui/litellm-dashboard/src/components/routing_groups/LiveTester.tsx @@ -0,0 +1,208 @@ +"use client"; +import React, { useState } from "react"; +import { Button, InputNumber, Radio, Spin, Typography } from "antd"; +import { SwapOutlined } from "@ant-design/icons"; +import OrderedFallbackFlow from "./OrderedFallbackFlow"; +import WeightedRoundRobinFlow from "./WeightedRoundRobinFlow"; +import StatsBar from "./StatsBar"; +import { routingGroupSimulateCall } from "@/components/networking"; + +interface Deployment { + deployment_id: string; + provider: string; + display_name: string; + priority?: number; + weight?: number; + avg_latency_ms: number; + percent_of_total: number; + request_count: number; + success_count: number; + failure_count: number; +} + +interface LiveTesterProps { + accessToken: string; + routingGroupId: string; + routingGroupName: string; + routingStrategy: string; // "priority-failover" | "weighted" | etc. + initialDeployments?: Deployment[]; +} + +const isOrderedStrategy = (s: string) => + s === "priority-failover"; + +export default function LiveTester({ + accessToken, + routingGroupId, + routingGroupName, + routingStrategy, + initialDeployments = [], +}: LiveTesterProps) { + // Force mode based on strategy, but allow user to toggle + const [viewMode, setViewMode] = useState<"ordered" | "weighted">( + isOrderedStrategy(routingStrategy) ? "ordered" : "weighted" + ); + const [loading, setLoading] = useState(false); + const [numRequests, setNumRequests] = useState(100); + const [concurrency, setConcurrency] = useState(10); + const [mockMode, setMockMode] = useState(true); + + // Live stats state + const [deployments, setDeployments] = useState( + initialDeployments.length > 0 ? initialDeployments : [] + ); + const [stats, setStats] = useState({ + totalRequests: 0, + successRate: 100, + avgLatencyMs: 0, + fallbackCount: 0, + }); + + const isOrdered = viewMode === "ordered"; + + const handleSimulate = async () => { + if (!accessToken) return; + setLoading(true); + try { + const result = await routingGroupSimulateCall(accessToken, routingGroupId, { + num_requests: numRequests, + concurrency, + mock: mockMode, + }) as Record; + + const dist = (result.traffic_distribution as Deployment[]) || []; + setDeployments(dist); + const total = (result.total_requests as number) || 0; + const successful = (result.successful_requests as number) || 0; + const avgLatency = (result.avg_latency_ms as number) || 0; + const fallbacks = (result.fallback_count as number) || 0; + setStats({ + totalRequests: total, + successRate: total > 0 ? (successful / total) * 100 : 100, + avgLatencyMs: avgLatency, + fallbackCount: fallbacks, + }); + } catch (e) { + console.error("Simulation error:", e); + } finally { + setLoading(false); + } + }; + + const infoText = isOrdered + ? "Traffic flows to the primary provider first. When a request fails, it cascades down the priority chain. Thicker streams = more traffic. Dashed red lines show the fallback path." + : "Traffic is distributed across providers based on weights. Thicker streams = more traffic. Adjust weights in the routing group settings."; + + const badgeLabel = isOrdered ? "Ordered Fallback" : "Weighted Round-Robin"; + const badgeColor = isOrdered ? "#92400e" : "#134e4a"; + const badgeTextColor = isOrdered ? "#fbbf24" : "#2dd4bf"; + + return ( +
+ {/* Header */} +
+
+
+ + Live Tester + + + {badgeLabel} + +
+ + Sending to{" "} + + {routingGroupName} + + +
+ +
+ + {/* Stats bar */} + + + {/* Flow diagram */} + + {deployments.length === 0 ? ( +
+ Run a simulation to see traffic flow +
+ ) : isOrdered ? ( + + ) : ( + + )} +
+ + {/* Info box */} +
+ {infoText} +
+ + {/* Simulation controls */} +
+ + Simulation Controls + +
+
+
Requests
+ setNumRequests(v ?? 100)} + style={{ width: 100, background: "#0f172a", borderColor: "#334155", color: "#f1f5f9" }} + /> +
+
+
Concurrency
+ setConcurrency(v ?? 10)} + style={{ width: 90, background: "#0f172a", borderColor: "#334155", color: "#f1f5f9" }} + /> +
+
+
Mode
+ setMockMode(e.target.value === "mock")} + buttonStyle="solid" + size="small" + > + Mock + Real + +
+ +
+
+
+ ); +}