mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
fix(ui): give Lens traces a flush toolbar layout (#44972)
Co-authored-by: Yujong Lee <yujong@berri.ai> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
950da7c2ec
commit
51c49038ef
4 changed files with 70 additions and 50 deletions
|
|
@ -631,6 +631,10 @@ describe("AgentTracesPage", () => {
|
|||
const live = screen.getByRole("button", { name: "Live" });
|
||||
expect(live).toHaveAttribute("aria-pressed", "true");
|
||||
expect(trigger).toHaveTextContent("Last 24 hours");
|
||||
expect(screen.getByRole("combobox", { name: "Filter traces by agent" })).toBeVisible();
|
||||
expect(screen.getByRole("combobox", { name: "Filter traces by status" })).toBeVisible();
|
||||
await waitFor(() => expect(screen.getByRole("button", { name: "Refresh traces" })).toBeEnabled());
|
||||
expect(screen.getByRole("button", { name: "Set up tracing" })).toBeEnabled();
|
||||
|
||||
fireEvent.click(trigger);
|
||||
fireEvent.click(await screen.findByRole("menuitemradio", { name: "Last 7 days" }));
|
||||
|
|
@ -646,6 +650,13 @@ describe("AgentTracesPage", () => {
|
|||
expect(trigger).not.toHaveTextContent("Last 7 days");
|
||||
|
||||
await waitFor(() => expect(vi.mocked(agentTraceListCall).mock.calls.at(-1)?.[0].endMs).toBe(pausedAt));
|
||||
expect(screen.getByRole("button", { name: "Set up tracing" })).toBeEnabled();
|
||||
await waitFor(() => expect(screen.getByRole("button", { name: "Refresh traces" })).toBeEnabled());
|
||||
|
||||
fireEvent.click(live);
|
||||
await waitFor(() => expect(live).toHaveAttribute("aria-pressed", "true"));
|
||||
expect(trigger).toHaveTextContent("Last 7 days");
|
||||
expect(screen.getByRole("button", { name: "Set up tracing" })).toBeEnabled();
|
||||
});
|
||||
|
||||
it("keeps the time controls on an empty range the user picked, instead of showing onboarding", async () => {
|
||||
|
|
|
|||
|
|
@ -185,8 +185,9 @@ export function AgentTracesSection({
|
|||
>
|
||||
<TracingSetupAction available={traces.traces.length > 0} live={live} onSetup={() => setShowSetup(true)} />
|
||||
<Button
|
||||
variant="outline"
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
className="h-full w-10 shrink-0 rounded-none border-l"
|
||||
aria-label="Refresh traces"
|
||||
title="Refresh traces"
|
||||
disabled={traces.isFetching}
|
||||
|
|
@ -195,15 +196,13 @@ export function AgentTracesSection({
|
|||
<RefreshCw className="size-3.5" />
|
||||
</Button>
|
||||
{timeControls && (
|
||||
<div className="h-8 min-w-0 overflow-hidden rounded-md border [&>div]:h-full [&>div]:border-l-0">
|
||||
<TimeRangeControls
|
||||
range={range}
|
||||
zoom={zoom}
|
||||
onHoursChange={(hours) => changeRange(hours, timeControls.setHours)}
|
||||
showLive={live}
|
||||
onLiveChange={timeControls.setLive}
|
||||
/>
|
||||
</div>
|
||||
<TimeRangeControls
|
||||
range={range}
|
||||
zoom={zoom}
|
||||
onHoursChange={(hours) => changeRange(hours, timeControls.setHours)}
|
||||
showLive={live}
|
||||
onLiveChange={timeControls.setLive}
|
||||
/>
|
||||
)}
|
||||
</RunsToolbar>
|
||||
<TraceCounts runs={filtered} />
|
||||
|
|
@ -259,7 +258,13 @@ function TraceFooter({ runs, hasMore }: { runs: readonly TraceSummary[]; hasMore
|
|||
function TracingSetupAction({ available, live, onSetup }: { available: boolean; live: boolean; onSetup: () => void }) {
|
||||
if (!available) return null;
|
||||
return (
|
||||
<Button variant="outline" size="sm" className="h-8 text-xs" disabled={!live} onClick={onSetup}>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-full shrink-0 rounded-none border-l px-3 text-xs"
|
||||
disabled={!live}
|
||||
onClick={onSetup}
|
||||
>
|
||||
Set up tracing
|
||||
</Button>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -32,10 +32,10 @@ export function RunSearch({ value, onChange, runs, range, busy = false }: RunSea
|
|||
value={value}
|
||||
onValueChange={onChange}
|
||||
label="Search runs"
|
||||
className="min-w-40 basis-full sm:basis-72 sm:max-w-96"
|
||||
className="h-10 min-w-0 basis-full sm:w-auto sm:basis-0"
|
||||
>
|
||||
<SearchBox.Input
|
||||
className="h-8 overflow-hidden rounded-md px-3 whitespace-nowrap"
|
||||
className="h-full overflow-hidden rounded-none border-0 px-3 whitespace-nowrap focus-within:bg-muted/40 focus-within:ring-0 dark:bg-transparent"
|
||||
placeholder="Search input or trace ID"
|
||||
busy={busy}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -37,43 +37,47 @@ export function RunsToolbar({ query, onQueryChange, runs, range, busy, children
|
|||
{ value: "error", label: "With errors" },
|
||||
];
|
||||
return (
|
||||
<div className="flex shrink-0 flex-wrap items-center gap-2 border-b bg-card p-2">
|
||||
<RunSearch value={query} onChange={onQueryChange} runs={runs} range={range} busy={busy} />
|
||||
<Combobox items={agents} value={agent || null} onValueChange={(name) => setAgent(name ?? "")} autoHighlight>
|
||||
<ComboboxInput
|
||||
aria-label="Filter traces by agent"
|
||||
placeholder="All agents"
|
||||
showClear={!!agent}
|
||||
className="h-8 w-48 text-xs"
|
||||
/>
|
||||
<ComboboxContent>
|
||||
<ComboboxEmpty>No matching agents</ComboboxEmpty>
|
||||
<ComboboxList>
|
||||
{(name: string) => (
|
||||
<ComboboxItem key={name} value={name} className="text-xs">
|
||||
{name}
|
||||
</ComboboxItem>
|
||||
)}
|
||||
</ComboboxList>
|
||||
</ComboboxContent>
|
||||
</Combobox>
|
||||
<Select
|
||||
items={statuses}
|
||||
value={status}
|
||||
onValueChange={(value: "all" | "ok" | "error" | null) => value !== null && setStatus(value)}
|
||||
>
|
||||
<SelectTrigger size="sm" className="h-8 min-w-28 text-xs" aria-label="Filter traces by status">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{statuses.map((item) => (
|
||||
<SelectItem key={item.value} value={item.value}>
|
||||
{item.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{children && <div className="ml-auto flex max-w-full flex-wrap items-center justify-end gap-2">{children}</div>}
|
||||
<div className="flex shrink-0 flex-col bg-card">
|
||||
<div className="flex min-h-10 flex-wrap items-stretch border-b border-border">
|
||||
<RunSearch value={query} onChange={onQueryChange} runs={runs} range={range} busy={busy} />
|
||||
{children && <div className="ml-auto flex h-10 max-w-full items-stretch">{children}</div>}
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2 border-b px-3 py-2">
|
||||
<Combobox items={agents} value={agent || null} onValueChange={(name) => setAgent(name ?? "")} autoHighlight>
|
||||
<ComboboxInput
|
||||
aria-label="Filter traces by agent"
|
||||
placeholder="All agents"
|
||||
showClear={!!agent}
|
||||
className="h-8 w-48 max-w-full text-xs"
|
||||
/>
|
||||
<ComboboxContent>
|
||||
<ComboboxEmpty>No matching agents</ComboboxEmpty>
|
||||
<ComboboxList>
|
||||
{(name: string) => (
|
||||
<ComboboxItem key={name} value={name} className="text-xs">
|
||||
{name}
|
||||
</ComboboxItem>
|
||||
)}
|
||||
</ComboboxList>
|
||||
</ComboboxContent>
|
||||
</Combobox>
|
||||
<Select
|
||||
items={statuses}
|
||||
value={status}
|
||||
onValueChange={(value: "all" | "ok" | "error" | null) => value !== null && setStatus(value)}
|
||||
>
|
||||
<SelectTrigger size="sm" className="h-8 min-w-28 text-xs" aria-label="Filter traces by status">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{statuses.map((item) => (
|
||||
<SelectItem key={item.value} value={item.value}>
|
||||
{item.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue