diff --git a/ui/litellm-dashboard/src/components/lens/setup/GatewayFlow.tsx b/ui/litellm-dashboard/src/components/lens/setup/GatewayFlow.tsx
new file mode 100644
index 00000000000..36657f0aad2
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/lens/setup/GatewayFlow.tsx
@@ -0,0 +1,108 @@
+import { useId } from "react";
+import styles from "./LensIntroduction.module.css";
+
+const streams = [
+ {
+ id: "runs",
+ color: "text-sky-500 dark:text-sky-400",
+ path: "M -32 66 C 24 66 32 22 96 22 S 192 86 256 86 S 368 28 422 28 C 464 28 472 54 500 54 C 558 54 584 86 646 86 S 756 22 818 22 S 924 86 986 86 S 1040 54 1056 54",
+ },
+ {
+ id: "steps",
+ color: "text-violet-500 dark:text-violet-400",
+ path: "M -32 30 C 40 30 58 78 120 78 S 218 24 284 24 S 390 76 444 76 C 470 76 480 54 503 54",
+ },
+ {
+ id: "tools",
+ color: "text-amber-500 dark:text-amber-400",
+ path: "M -32 54 C 26 54 68 68 128 68 S 230 38 288 38 S 396 62 448 62 C 474 62 484 54 503 54",
+ },
+];
+
+export function GatewayFlow() {
+ const id = useId();
+ return (
+
+
+
+
Agent swarms
+
Every run, every recorded step
+
+
+
LiteLLM gateway
+
One place, your infrastructure
+
+
+
Lens
+
Findings to improve your agents
+
+
+
+
+ );
+}
diff --git a/ui/litellm-dashboard/src/components/lens/setup/LensIntroduction.module.css b/ui/litellm-dashboard/src/components/lens/setup/LensIntroduction.module.css
index c2940a9c1b5..8554f4b27d0 100644
--- a/ui/litellm-dashboard/src/components/lens/setup/LensIntroduction.module.css
+++ b/ui/litellm-dashboard/src/components/lens/setup/LensIntroduction.module.css
@@ -1,18 +1,117 @@
-.flow {
- animation: gateway-flow 10s linear infinite;
+.packets {
+ stroke-dasharray: 0 16;
+ animation: signal-flow 14s linear infinite;
}
-@keyframes gateway-flow {
+.trails {
+ stroke-dasharray: 6 42;
+ stroke-dashoffset: -10;
+ opacity: 0.3;
+ animation: signal-trails 14s linear infinite;
+}
+
+.gatewayGlow {
+ opacity: 0.7;
+ transition: opacity 300ms ease;
+}
+
+.flowGraphic:hover .gatewayGlow {
+ opacity: 1;
+}
+
+.sampleCard {
+ transition:
+ border-color 200ms ease,
+ box-shadow 200ms ease;
+}
+
+.sampleCard:hover,
+.sampleCard:focus-within {
+ border-color: color-mix(in oklab, var(--color-indigo-500) 22%, var(--border));
+ box-shadow: 0 4px 20px -16px color-mix(in oklab, var(--color-indigo-500) 40%, transparent);
+}
+
+.examples {
+ --evidence-tint: color-mix(in oklab, var(--color-rose-400) 6%, transparent);
+ --evidence-outline: color-mix(in oklab, var(--color-rose-400) 32%, transparent);
+}
+
+.evidenceLink {
+ cursor: pointer;
+ border-radius: 4px;
+ outline-offset: 4px;
+ transition: background-color 200ms ease;
+}
+
+.evidenceLink:focus-visible {
+ outline: 2px solid var(--ring);
+}
+
+.evidenceLink:hover {
+ background: var(--evidence-tint);
+}
+
+.evidenceOutput,
+.evidenceFinding,
+.evidenceBar {
+ transition:
+ background-color 200ms ease,
+ box-shadow 200ms ease;
+}
+
+.evidenceFinding {
+ border-radius: 6px;
+}
+
+.examples:is(
+ [data-evidence-active="true"],
+ :has([data-evidence-link]:hover),
+ :has([data-evidence-link]:focus-visible)
+ ) {
+ .evidenceOutput {
+ box-shadow: inset 0 0 0 1px var(--evidence-outline);
+ }
+
+ .evidenceFinding {
+ background: var(--evidence-tint);
+ box-shadow: 0 0 0 7px var(--evidence-tint);
+ }
+
+ .evidenceBar {
+ box-shadow: 0 0 10px 2px var(--evidence-outline);
+ }
+}
+
+@keyframes signal-flow {
from {
- transform: translateX(0);
+ stroke-dashoffset: 0;
}
to {
- transform: translateX(240px);
+ stroke-dashoffset: -144;
+ }
+}
+
+@keyframes signal-trails {
+ from {
+ stroke-dashoffset: -10;
+ }
+ to {
+ stroke-dashoffset: -154;
}
}
@media (prefers-reduced-motion: reduce) {
- .flow {
+ .packets,
+ .trails {
animation: none;
}
+
+ .gatewayGlow,
+ .sampleCard,
+ .evidenceLink,
+ .evidenceOutput,
+ .evidenceFinding,
+ .evidenceBar {
+ transition: none;
+ }
}
diff --git a/ui/litellm-dashboard/src/components/lens/setup/LensIntroduction.tsx b/ui/litellm-dashboard/src/components/lens/setup/LensIntroduction.tsx
index 1dadd0141b4..20aff27ca27 100644
--- a/ui/litellm-dashboard/src/components/lens/setup/LensIntroduction.tsx
+++ b/ui/litellm-dashboard/src/components/lens/setup/LensIntroduction.tsx
@@ -1,77 +1,8 @@
-import { useId } from "react";
+import { useState } from "react";
import { ArrowRight, ArrowUpRight, ChevronDown, ChevronRight } from "lucide-react";
import { Button } from "@/components/ui/button";
import styles from "./LensIntroduction.module.css";
-
-const swarmColors = ["fill-violet-400", "fill-sky-400", "fill-amber-400", "fill-rose-400", "fill-indigo-400"];
-
-function flowDot(index: number) {
- const column = (index % 124) - 24;
- const row = Math.floor(index / 124);
- const phase = (column + 24) % 24;
- const wave = 2.5 + 2.6 * Math.sin(((phase + 5) / 24) * Math.PI * 2);
- if (Math.abs(row - wave) > 0.9) return null;
- return {
- x: column * 10 + 5,
- y: row * 10 + 5,
- color: swarmColors[(phase + row) % swarmColors.length],
- };
-}
-
-const flowDots = Array.from({ length: 744 }, (_, index) => flowDot(index)).filter((dot) => dot !== null);
-
-function GatewayFlow() {
- const patternId = useId();
- const beforeGateway = `${patternId}-before`;
- const afterGateway = `${patternId}-after`;
- return (
-
-
-
-
Agent swarms
-
Every run, every recorded step
-
-
-
LiteLLM gateway
-
One place, your infrastructure
-
-
-
Lens
-
Findings to improve your agents
-
-
-
-
- );
-}
+import { GatewayFlow } from "./GatewayFlow";
const traceSteps = [
{ name: "research_agent", start: 0, duration: 14.2, failed: false },
@@ -84,9 +15,14 @@ const traceSteps = [
const sampleAnswer = "The column store is 40% faster than the row store for your workload.";
-function TraceExample() {
+type EvidenceProps = { highlighted: boolean; onHighlight: () => void };
+
+function TraceExample({ highlighted, onHighlight }: EvidenceProps) {
return (
-
+
Tracing
@@ -94,38 +30,59 @@ function TraceExample() {
Sample trace · 14.2s
- {traceSteps.map((item, index) => (
- -
- 0 ? "pl-2" : ""}`}>
- {index === 0 ? (
-
- ) : (
-
- )}
-
- {item.name}
+ {traceSteps.map((item, index) => {
+ const linked = item.failed || item.name === "answer";
+ const rowClass =
+ "grid w-full grid-cols-[minmax(0,1fr)_minmax(48px,1fr)_2.5rem] items-center gap-2 text-left sm:gap-3";
+ const content = (
+ <>
+ 0 ? "pl-2" : ""}`}>
+ {index === 0 ? (
+
+ ) : (
+
+ )}
+
+ {item.name}
+
+ {item.failed && Failed}
- {item.failed && Failed}
-
-
-
-
- {item.duration}s
-
- ))}
+
+
+
+ {item.duration}s
+ >
+ );
+ return (
+ -
+ {linked ? (
+
+ ) : (
+
{content}
+ )}
+
+ );
+ })}
-
+
run_benchmark · output
Error: benchmark runner unavailable (503)
-
+
answer · output
“{sampleAnswer}”
@@ -134,9 +91,12 @@ function TraceExample() {
);
}
-function FindingExamples() {
+function FindingExamples({ highlighted, onHighlight }: EvidenceProps) {
return (
-
+
Lens findings
@@ -144,10 +104,19 @@ function FindingExamples() {
500 sample runs reviewed
-
-
-
- Claims numbers it never measured
+
+
+
Reports a speedup even though the benchmark failed.
@@ -177,6 +146,8 @@ function FindingExamples() {
}
export function LensIntroduction({ onStart, onDemo }: { onStart: () => void; onDemo?: () => void }) {
+ const [highlighted, setHighlighted] = useState(false);
+ const toggleEvidence = () => setHighlighted((current) => !current);
return (
-