mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-11 03:38:38 +00:00
fix(lens): restore the onboarding reference visuals
This commit is contained in:
parent
06a50d055a
commit
debe7b2343
3 changed files with 214 additions and 73 deletions
|
|
@ -65,10 +65,10 @@ export function LensGettingStarted({
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="mx-auto w-full max-w-6xl space-y-8 py-4">
|
||||
<div className="mx-auto w-full max-w-7xl space-y-10 py-5 sm:space-y-12 sm:py-7">
|
||||
{onDemo && <LensPreviewButton onClick={onDemo} />}
|
||||
<LensIntroduction onStart={start} onDemo={onDemo} />
|
||||
<div className="grid items-start gap-6 lg:grid-cols-[minmax(0,1fr)_260px]">
|
||||
<div className="grid items-start gap-6 xl:grid-cols-[minmax(0,1fr)_280px] xl:gap-8">
|
||||
<section
|
||||
ref={setupRef}
|
||||
tabIndex={-1}
|
||||
|
|
@ -76,13 +76,13 @@ export function LensGettingStarted({
|
|||
className="min-w-0 scroll-mt-4 outline-none"
|
||||
onFocusCapture={onStart}
|
||||
>
|
||||
<div className="mb-5 flex flex-wrap items-center justify-between gap-3">
|
||||
<div className="mb-6 flex flex-wrap items-center justify-between gap-3">
|
||||
<div>
|
||||
<h2 id="lens-setup-title" className="text-xl font-semibold tracking-tight">
|
||||
<h2 id="lens-setup-title" className="text-2xl font-semibold tracking-tight">
|
||||
Get Lens running
|
||||
</h2>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
We’ll check each connection. Stay here until your first investigation is ready.
|
||||
<p className="mt-2 text-sm leading-6 text-muted-foreground">
|
||||
Each step checks your connection, so you’ll see when it’s working.
|
||||
</p>
|
||||
</div>
|
||||
{canLeave && (
|
||||
|
|
@ -117,20 +117,23 @@ export function LensGettingStarted({
|
|||
</p>
|
||||
)}
|
||||
</section>
|
||||
<aside className="rounded-lg border p-5" aria-labelledby="lens-prerequisites">
|
||||
<h2 id="lens-prerequisites" className="text-base font-medium">
|
||||
<aside className="rounded-2xl border bg-card p-6" aria-labelledby="lens-prerequisites">
|
||||
<h2 id="lens-prerequisites" className="text-base font-semibold">
|
||||
Before you start
|
||||
</h2>
|
||||
<ul className="mt-4 space-y-4 text-sm text-muted-foreground">
|
||||
<ul className="mt-5 space-y-5 text-sm">
|
||||
{[
|
||||
"Access to your LiteLLM gateway configuration",
|
||||
"ClickHouse for trace storage",
|
||||
"A server with Docker for the worker",
|
||||
"An analysis model on your gateway",
|
||||
{ title: "LiteLLM gateway", detail: "Access to its configuration" },
|
||||
{ title: "ClickHouse", detail: "Self-hosted or managed trace storage" },
|
||||
{ title: "A server with Docker", detail: "To run the analysis worker" },
|
||||
{ title: "An analysis model", detail: "Available through your gateway" },
|
||||
].map((item) => (
|
||||
<li key={item} className="flex gap-2">
|
||||
<Check aria-hidden="true" className="mt-0.5 size-4 shrink-0" />
|
||||
{item}
|
||||
<li key={item.title} className="flex gap-3">
|
||||
<Check aria-hidden="true" className="mt-0.5 size-4 shrink-0 text-muted-foreground" />
|
||||
<div>
|
||||
<p className="font-medium">{item.title}</p>
|
||||
<p className="mt-1 leading-5 text-muted-foreground">{item.detail}</p>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
|
|
|||
|
|
@ -1,70 +1,198 @@
|
|||
import { Aperture, ArrowRight, ArrowUpRight, GitBranch } from "lucide-react";
|
||||
import { useId } from "react";
|
||||
import { ArrowRight, ArrowUpRight, ChevronDown, ChevronRight } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { scenarios } from "../demo/scenarios";
|
||||
|
||||
const swarmColors = ["fill-violet-400", "fill-sky-400", "fill-amber-400", "fill-rose-400", "fill-indigo-400"];
|
||||
|
||||
function flowDot(index: number) {
|
||||
const column = index % 100;
|
||||
const row = Math.floor(index / 100);
|
||||
const wave = 2.5 + 2.6 * Math.sin((column + 5) / 3.8);
|
||||
if (Math.abs(row - wave) > 0.9) return null;
|
||||
return {
|
||||
x: column * 10 + 5,
|
||||
y: row * 10 + 5,
|
||||
color: column < 61 ? swarmColors[(column + row) % swarmColors.length] : "fill-indigo-600 dark:fill-indigo-400",
|
||||
};
|
||||
}
|
||||
|
||||
const flowDots = Array.from({ length: 600 }, (_, index) => flowDot(index)).filter((dot) => dot !== null);
|
||||
|
||||
function GatewayFlow() {
|
||||
const patternId = useId();
|
||||
return (
|
||||
<div className="mt-10 sm:mt-12">
|
||||
<div className="grid grid-cols-3 gap-3 text-xs sm:text-sm">
|
||||
<div>
|
||||
<p className="font-semibold">Agent swarms</p>
|
||||
<p className="mt-1 leading-5 text-muted-foreground">Every run, every recorded step</p>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<p className="font-semibold">LiteLLM gateway</p>
|
||||
<p className="mt-1 leading-5 text-muted-foreground">One place, your infrastructure</p>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="font-semibold">Lens</p>
|
||||
<p className="mt-1 leading-5 text-muted-foreground">Findings to improve your agents</p>
|
||||
</div>
|
||||
</div>
|
||||
<svg aria-hidden="true" viewBox="0 0 1000 60" className="mt-3 h-auto w-full" focusable="false">
|
||||
<defs>
|
||||
<pattern id={patternId} width="10" height="10" patternUnits="userSpaceOnUse">
|
||||
<circle cx="5" cy="5" r="2.3" className="fill-muted-foreground/10" />
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="1000" height="60" fill={`url(#${patternId})`} />
|
||||
{flowDots.map((dot) => (
|
||||
<circle key={`${dot.x}-${dot.y}`} cx={dot.x} cy={dot.y} r="2.3" className={dot.color} />
|
||||
))}
|
||||
<path d="M 615 1 H 609 V 59 H 615" fill="none" stroke="currentColor" strokeWidth="1.8" />
|
||||
</svg>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const traceSteps = [
|
||||
{ name: "research_agent", start: 0, duration: 14.2, failed: false },
|
||||
{ name: "plan", start: 0, duration: 1.7, failed: false },
|
||||
{ name: "search_docs", start: 1.7, duration: 3.1, failed: false },
|
||||
{ name: "run_benchmark", start: 4.8, duration: 1.3, failed: true },
|
||||
{ name: "search_docs", start: 6.1, duration: 3.6, failed: false },
|
||||
{ name: "answer", start: 9.7, duration: 4.5, failed: false },
|
||||
];
|
||||
|
||||
const sampleAnswer = "The column store is 40% faster than the row store for your workload.";
|
||||
|
||||
function TraceExample() {
|
||||
return (
|
||||
<section aria-labelledby="lens-sample-trace" className="min-w-0 rounded-2xl border bg-card p-4 sm:p-5">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2 text-sm">
|
||||
<h3 id="lens-sample-trace" className="font-semibold">
|
||||
Tracing
|
||||
</h3>
|
||||
<p className="text-xs text-muted-foreground">Sample trace · 14.2s</p>
|
||||
</div>
|
||||
<ol className="mt-5 space-y-3.5 text-xs sm:text-[13px]" aria-label="Sample trace timeline">
|
||||
{traceSteps.map((item, index) => (
|
||||
<li
|
||||
key={`${item.name}-${item.start}`}
|
||||
className="grid grid-cols-[minmax(0,1fr)_minmax(48px,1fr)_2.5rem] items-center gap-2 sm:gap-3"
|
||||
>
|
||||
<span className={`flex min-w-0 items-center gap-1 ${index > 0 ? "pl-2" : ""}`}>
|
||||
{index === 0 ? (
|
||||
<ChevronDown aria-hidden="true" className="size-3 shrink-0" />
|
||||
) : (
|
||||
<ChevronRight aria-hidden="true" className="size-3 shrink-0" />
|
||||
)}
|
||||
<span className="truncate" title={item.name}>
|
||||
{item.name}
|
||||
</span>
|
||||
{item.failed && <span className="sr-only">Failed</span>}
|
||||
</span>
|
||||
<span aria-hidden="true" className="relative h-2.5 overflow-hidden rounded-sm bg-muted/70">
|
||||
<span
|
||||
className={`absolute inset-y-0 rounded-sm ${item.failed ? "bg-rose-400" : "bg-indigo-600 dark:bg-indigo-400"}`}
|
||||
style={{ left: `${(item.start / 14.2) * 100}%`, width: `${(item.duration / 14.2) * 100}%` }}
|
||||
/>
|
||||
</span>
|
||||
<span className="text-right tabular-nums text-muted-foreground">{item.duration}s</span>
|
||||
</li>
|
||||
))}
|
||||
</ol>
|
||||
<div className="mt-5 grid gap-3 border-t pt-4 sm:grid-cols-2">
|
||||
<div className="rounded-xl bg-muted/60 p-3.5">
|
||||
<p className="text-xs text-muted-foreground">run_benchmark · output</p>
|
||||
<p className="mt-2 text-[13px] leading-5">Error: benchmark runner unavailable (503)</p>
|
||||
</div>
|
||||
<div className="rounded-xl bg-muted/60 p-3.5">
|
||||
<p className="text-xs text-muted-foreground">answer · output</p>
|
||||
<p className="mt-2 text-[13px] leading-5">“{sampleAnswer}”</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function FindingExamples() {
|
||||
return (
|
||||
<section aria-labelledby="lens-sample-findings" className="min-w-0 rounded-2xl border bg-card p-4 sm:p-5">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2 text-sm">
|
||||
<h3 id="lens-sample-findings" className="font-semibold">
|
||||
Lens findings
|
||||
</h3>
|
||||
<p className="text-xs text-muted-foreground">500 sample runs reviewed</p>
|
||||
</div>
|
||||
<div className="mt-4 divide-y border-t">
|
||||
<article className="py-4">
|
||||
<h4 className="flex items-start gap-2 text-sm font-semibold">
|
||||
<span aria-hidden="true" className="mt-1.5 size-2 shrink-0 rounded-full bg-rose-500" />
|
||||
Claims numbers it never measured
|
||||
</h4>
|
||||
<div className="ml-4 mt-2 space-y-2 text-[13px] leading-5">
|
||||
<p className="text-muted-foreground">
|
||||
When the benchmark tool fails, the agent still reports a speedup in its answer.
|
||||
</p>
|
||||
<blockquote className="border-l-2 pl-3 italic">“{sampleAnswer}”</blockquote>
|
||||
<p>
|
||||
<span className="text-muted-foreground">Next step: </span>Tell the agent to report the failed benchmark
|
||||
instead of estimating.
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground">38 linked runs · High priority</p>
|
||||
</div>
|
||||
</article>
|
||||
<article className="pt-4">
|
||||
<h4 className="flex items-start gap-2 text-sm font-semibold">
|
||||
<span aria-hidden="true" className="mt-1.5 size-2 shrink-0 rounded-full bg-amber-500" />
|
||||
Promises to follow up, then stops
|
||||
</h4>
|
||||
<div className="ml-4 mt-2 space-y-2 text-[13px] leading-5 text-muted-foreground">
|
||||
<p>After the order lookup times out, the support agent replies “I will check” and ends the run.</p>
|
||||
<p className="text-xs">12 linked runs · Medium priority</p>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
export function LensIntroduction({ onStart, onDemo }: { onStart: () => void; onDemo?: () => void }) {
|
||||
const example = scenarios[1];
|
||||
return (
|
||||
<section aria-labelledby="lens-introduction" className="rounded-lg border bg-card p-5 sm:p-8">
|
||||
<h2 id="lens-introduction" className="max-w-2xl text-2xl font-semibold tracking-tight sm:text-3xl">
|
||||
<section
|
||||
aria-labelledby="lens-introduction"
|
||||
className="rounded-2xl border bg-card p-5 sm:p-8 xl:rounded-3xl xl:p-10"
|
||||
>
|
||||
<h2
|
||||
id="lens-introduction"
|
||||
className="max-w-[800px] text-3xl leading-[1.12] font-semibold tracking-[-0.035em] sm:text-4xl xl:text-[42px]"
|
||||
>
|
||||
The gateway that helps your agents improve
|
||||
</h2>
|
||||
<p className="mt-3 max-w-2xl text-sm leading-6 text-muted-foreground">
|
||||
Lens reviews your agents’ recorded activity and finds behavior worth fixing. Each finding links to the original
|
||||
steps, so you can see what happened and what to change next.
|
||||
<p className="mt-5 max-w-[780px] text-base leading-7 text-muted-foreground">
|
||||
Lens turns recorded agent runs into findings tied to the exact steps that caused them, so you can see what needs
|
||||
to change without reading every trace by hand.
|
||||
</p>
|
||||
<div className="mt-6 flex flex-wrap items-center gap-3">
|
||||
<div className="mt-7 flex flex-wrap items-center gap-3">
|
||||
{onDemo && (
|
||||
<Button onClick={onDemo}>
|
||||
<Button size="lg" className="h-11 rounded-lg px-5" onClick={onDemo}>
|
||||
Explore with sample data <ArrowRight aria-hidden="true" className="size-4" />
|
||||
</Button>
|
||||
)}
|
||||
<Button variant={onDemo ? "outline" : "default"} onClick={onStart}>
|
||||
<Button size="lg" variant={onDemo ? "outline" : "default"} className="h-11 rounded-lg px-5" onClick={onStart}>
|
||||
Set up Lens
|
||||
</Button>
|
||||
<a
|
||||
href="https://docs.litellm.ai/docs/proxy/lens"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="inline-flex items-center gap-1 px-2 text-sm text-muted-foreground underline-offset-4 hover:underline"
|
||||
className="inline-flex items-center gap-1 px-3 text-sm font-medium text-muted-foreground underline-offset-4 hover:underline"
|
||||
>
|
||||
Docs <ArrowUpRight aria-hidden="true" className="size-3.5" />
|
||||
Docs <ArrowUpRight aria-hidden="true" className="size-4" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="mt-8 grid overflow-hidden rounded-lg border lg:grid-cols-2">
|
||||
<div className="border-b bg-muted/20 p-5 lg:border-r lg:border-b-0">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2 text-sm">
|
||||
<h3 className="flex items-center gap-2 font-medium">
|
||||
<GitBranch aria-hidden="true" className="size-4" />
|
||||
Agent trace
|
||||
</h3>
|
||||
<span className="text-xs text-muted-foreground">Sample · {example.agent}</span>
|
||||
</div>
|
||||
<dl className="mt-5 space-y-4 text-sm">
|
||||
<div>
|
||||
<dt className="font-mono text-xs text-muted-foreground">{example.tool}</dt>
|
||||
<dd className="mt-1.5 leading-6">{example.result}</dd>
|
||||
</div>
|
||||
<div className="border-t pt-4">
|
||||
<dt className="text-xs text-muted-foreground">Final answer</dt>
|
||||
<dd className="mt-1.5 leading-6">“{example.answer}”</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
<div className="p-5">
|
||||
<h3 className="flex items-center gap-2 text-sm font-medium">
|
||||
<Aperture aria-hidden="true" className="size-4" />
|
||||
Lens finding
|
||||
</h3>
|
||||
<h4 className="mt-5 text-base font-medium">Performance claim has no supporting benchmark</h4>
|
||||
<p className="mt-2 text-sm leading-6 text-muted-foreground">
|
||||
The answer claims a 40% improvement, but the retrieved documentation contains no comparative benchmark.
|
||||
</p>
|
||||
<p className="mt-4 border-t pt-4 text-sm leading-6">
|
||||
<span className="font-medium">Suggested change: </span>Require a benchmark source for numerical performance
|
||||
claims, or remove the comparison.
|
||||
</p>
|
||||
</div>
|
||||
<GatewayFlow />
|
||||
<div className="mt-7 grid items-stretch gap-4 lg:grid-cols-2">
|
||||
<TraceExample />
|
||||
<FindingExamples />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { Button } from "@/components/ui/button";
|
|||
import { TracingSetupFields } from "@/components/view_logs/TraceView/TracingSetupCard";
|
||||
import type { TraceSummary } from "@/components/view_logs/TraceView/traceTypes";
|
||||
import type { LensSetupState } from "./useLensSetup";
|
||||
import { cn } from "@/lib/cva.config";
|
||||
|
||||
type StepProps = {
|
||||
accessToken: string;
|
||||
|
|
@ -160,7 +161,7 @@ export function LensSetupSteps({ step, ...props }: StepProps & { step: number })
|
|||
},
|
||||
];
|
||||
return (
|
||||
<ol className="divide-y overflow-hidden rounded-lg border">
|
||||
<ol className="divide-y overflow-hidden rounded-2xl border bg-card">
|
||||
{items.map((item, index) => (
|
||||
<li key={item.title}>
|
||||
<h3>
|
||||
|
|
@ -169,21 +170,30 @@ export function LensSetupSteps({ step, ...props }: StepProps & { step: number })
|
|||
aria-expanded={step === index}
|
||||
aria-controls={`lens-setup-step-${index}`}
|
||||
onClick={() => props.onStep(index)}
|
||||
className="flex w-full items-start gap-3 p-5 text-left hover:bg-muted/30"
|
||||
className="group flex w-full items-start gap-4 p-5 text-left outline-none hover:bg-muted/30 focus-visible:bg-muted/50 sm:p-6"
|
||||
>
|
||||
<span
|
||||
className={
|
||||
item.complete
|
||||
? "flex size-7 shrink-0 items-center justify-center rounded-full bg-success/10 text-success"
|
||||
: "flex size-7 shrink-0 items-center justify-center rounded-full border text-xs font-medium"
|
||||
}
|
||||
className={cn(
|
||||
"flex size-8 shrink-0 items-center justify-center rounded-full text-sm font-medium",
|
||||
item.complete ? "bg-success/10 text-success" : "border text-muted-foreground",
|
||||
!item.complete && step === index && "border-primary bg-primary text-primary-foreground",
|
||||
)}
|
||||
aria-label={item.complete ? `Step ${index + 1} complete` : `Step ${index + 1}`}
|
||||
>
|
||||
{item.complete ? <Check aria-hidden="true" className="size-4" /> : index + 1}
|
||||
</span>
|
||||
<span className="min-w-0 flex-1">
|
||||
<span className="block text-sm font-medium">{item.title}</span>
|
||||
<span className="mt-1 block text-sm font-normal text-muted-foreground">{item.description}</span>
|
||||
<span
|
||||
className={cn(
|
||||
"block text-sm font-medium sm:text-base",
|
||||
step !== index && "text-muted-foreground group-hover:text-foreground",
|
||||
)}
|
||||
>
|
||||
{item.title}
|
||||
</span>
|
||||
<span className="mt-1 block text-sm leading-6 font-normal text-muted-foreground">
|
||||
{item.description}
|
||||
</span>
|
||||
</span>
|
||||
<ChevronDown
|
||||
aria-hidden="true"
|
||||
|
|
@ -191,7 +201,7 @@ export function LensSetupSteps({ step, ...props }: StepProps & { step: number })
|
|||
/>
|
||||
</button>
|
||||
</h3>
|
||||
<div id={`lens-setup-step-${index}`} hidden={step !== index} className="px-5 pb-5 sm:pl-15">
|
||||
<div id={`lens-setup-step-${index}`} hidden={step !== index} className="px-5 pb-6 sm:pr-6 sm:pb-7 sm:pl-18">
|
||||
{item.content}
|
||||
</div>
|
||||
</li>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue