From 6764b9af124d42aafc10fad35c961546dfe795c5 Mon Sep 17 00:00:00 2001
From: ishaan-berri <155045088+ishaan-berri@users.noreply.github.com>
Date: Mon, 5 Oct 2026 18:36:22 -0700
Subject: [PATCH] feat(lens): type to filter the traces agent dropdown (#44745)
* feat(lens): type to filter the traces agent dropdown
* test(lens): cover typing, Enter and clear in the agent filter
---
.../list/runSearch/RunsToolbar.test.tsx | 55 +++++++++++++++++++
.../traces/list/runSearch/RunsToolbar.tsx | 45 +++++++++------
2 files changed, 82 insertions(+), 18 deletions(-)
create mode 100644 ui/litellm-dashboard/src/components/lens/traces/list/runSearch/RunsToolbar.test.tsx
diff --git a/ui/litellm-dashboard/src/components/lens/traces/list/runSearch/RunsToolbar.test.tsx b/ui/litellm-dashboard/src/components/lens/traces/list/runSearch/RunsToolbar.test.tsx
new file mode 100644
index 00000000000..4b5285b6513
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/lens/traces/list/runSearch/RunsToolbar.test.tsx
@@ -0,0 +1,55 @@
+import { render, screen } from "@testing-library/react";
+import userEvent from "@testing-library/user-event";
+import { withNuqsTestingAdapter, type UrlUpdateEvent } from "nuqs/adapters/testing";
+import { describe, expect, it, vi } from "vitest";
+
+import { runs } from "./__fixtures__/runs";
+import { RunsToolbar } from "./RunsToolbar";
+
+const agentBox = () => screen.getByRole("combobox", { name: "Filter traces by agent" });
+
+describe("RunsToolbar", () => {
+ it("narrows the agent filter as you type and selects the match", async () => {
+ const user = userEvent.setup();
+ const onUrlUpdate = vi.fn<(event: UrlUpdateEvent) => void>();
+ render(, {
+ wrapper: withNuqsTestingAdapter({ onUrlUpdate }),
+ });
+ expect(agentBox()).toHaveAttribute("placeholder", "All agents");
+ await user.click(agentBox());
+ await user.keyboard("tri");
+ expect(screen.getAllByRole("option").map((o) => o.textContent)).toEqual(["triage"]);
+ await user.click(screen.getByRole("option", { name: "triage" }));
+ expect(onUrlUpdate.mock.lastCall?.[0].searchParams.get("agent")).toBe("triage");
+ });
+
+ it("picks the first match on Enter", async () => {
+ const user = userEvent.setup();
+ const onUrlUpdate = vi.fn<(event: UrlUpdateEvent) => void>();
+ render(, {
+ wrapper: withNuqsTestingAdapter({ onUrlUpdate }),
+ });
+ await user.click(agentBox());
+ await user.keyboard("res{Enter}");
+ expect(onUrlUpdate.mock.lastCall?.[0].searchParams.get("agent")).toBe("researcher");
+ });
+
+ it("says when no agent matches", async () => {
+ const user = userEvent.setup();
+ render(, { wrapper: withNuqsTestingAdapter() });
+ await user.click(agentBox());
+ await user.keyboard("zzz");
+ expect(screen.getByText("No matching agents")).toBeVisible();
+ });
+
+ it("clears back to all agents", async () => {
+ const user = userEvent.setup();
+ const onUrlUpdate = vi.fn<(event: UrlUpdateEvent) => void>();
+ render(, {
+ wrapper: withNuqsTestingAdapter({ searchParams: "?agent=triage", onUrlUpdate }),
+ });
+ expect(agentBox()).toHaveValue("triage");
+ await user.click(screen.getByRole("button", { name: "Clear" }));
+ expect(onUrlUpdate.mock.lastCall?.[0].searchParams.get("agent")).toBeNull();
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/lens/traces/list/runSearch/RunsToolbar.tsx b/ui/litellm-dashboard/src/components/lens/traces/list/runSearch/RunsToolbar.tsx
index 67357e70a30..cdf0f1a34a3 100644
--- a/ui/litellm-dashboard/src/components/lens/traces/list/runSearch/RunsToolbar.tsx
+++ b/ui/litellm-dashboard/src/components/lens/traces/list/runSearch/RunsToolbar.tsx
@@ -3,6 +3,14 @@
import type { TraceSummary } from "../../types";
import type { TimeWindow } from "@/components/shared/timeline/Timeline";
+import {
+ Combobox,
+ ComboboxContent,
+ ComboboxEmpty,
+ ComboboxInput,
+ ComboboxItem,
+ ComboboxList,
+} from "@/components/ui/combobox";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { useRunFilterRouting } from "../../routing";
import { traceAgentNames } from "../../utils";
@@ -20,12 +28,7 @@ interface RunsToolbarProps {
export function RunsToolbar({ query, onQueryChange, runs, range, children }: RunsToolbarProps) {
const { agent, status, setAgent, setStatus } = useRunFilterRouting();
- const agents = [
- { value: "", label: "All agents" },
- ...[...new Set([...runs.flatMap(traceAgentNames), ...(agent ? [agent] : [])])]
- .sort()
- .map((name) => ({ value: name, label: name })),
- ];
+ const agents = [...new Set([...runs.flatMap(traceAgentNames), ...(agent ? [agent] : [])])].sort();
const statuses = [
{ value: "all", label: "All status" },
{ value: "ok", label: "No errors" },
@@ -34,18 +37,24 @@ export function RunsToolbar({ query, onQueryChange, runs, range, children }: Run
return (
-
+ setAgent(name ?? "")} autoHighlight>
+
+
+ No matching agents
+
+ {(name: string) => (
+
+ {name}
+
+ )}
+
+
+