diff --git a/ui/litellm-dashboard/src/components/molecules/filter.test.tsx b/ui/litellm-dashboard/src/components/molecules/filter.test.tsx index 1a90c4a069..3a15c5c84f 100644 --- a/ui/litellm-dashboard/src/components/molecules/filter.test.tsx +++ b/ui/litellm-dashboard/src/components/molecules/filter.test.tsx @@ -103,7 +103,7 @@ describe("FilterComponent", () => { }); }); - it("should render filters in correct order", async () => { + it("renders filters in the caller-supplied order", async () => { const user = userEvent.setup({ delay: null }); const options: FilterOption[] = [ { name: "model", label: "Model" }, @@ -113,11 +113,7 @@ describe("FilterComponent", () => { ]; renderWithProviders( - , + , ); const filterButton = screen.getByRole("button", { name: "Filters" }); @@ -125,8 +121,7 @@ describe("FilterComponent", () => { await waitFor(() => { const labels = screen.getAllByText(/^(Team ID|Status|User ID|Model)$/); - expect(labels[0]).toHaveTextContent("Team ID"); - expect(labels[1]).toHaveTextContent("Status"); + expect(labels.map((l) => l.textContent)).toEqual(["Model", "Team ID", "Status", "User ID"]); }); }); @@ -218,11 +213,7 @@ describe("FilterComponent", () => { ]; renderWithProviders( - , + , ); const filterButton = screen.getByRole("button", { name: "Filters" }); @@ -245,9 +236,7 @@ describe("FilterComponent", () => { it("should debounce search input for searchable filters", async () => { const user = userEvent.setup({ delay: null }); - const mockSearchFn = vi.fn().mockResolvedValue([ - { label: "Result", value: "result" }, - ]); + const mockSearchFn = vi.fn().mockResolvedValue([{ label: "Result", value: "result" }]); const options: FilterOption[] = [ { @@ -259,11 +248,7 @@ describe("FilterComponent", () => { ]; renderWithProviders( - , + , ); const filterButton = screen.getByRole("button", { name: "Filters" }); @@ -311,11 +296,7 @@ describe("FilterComponent", () => { ]; renderWithProviders( - , + , ); const filterButton = screen.getByRole("button", { name: "Filters" }); @@ -360,11 +341,7 @@ describe("FilterComponent", () => { ]; renderWithProviders( - , + , ); const filterButton = screen.getByRole("button", { name: "Filters" }); @@ -393,9 +370,7 @@ describe("FilterComponent", () => { it("should load initial options when dropdown opens for searchable filter", async () => { const user = userEvent.setup({ delay: null }); - const mockSearchFn = vi.fn().mockResolvedValue([ - { label: "Initial Result", value: "initial" }, - ]); + const mockSearchFn = vi.fn().mockResolvedValue([{ label: "Initial Result", value: "initial" }]); const options: FilterOption[] = [ { @@ -407,11 +382,7 @@ describe("FilterComponent", () => { ]; renderWithProviders( - , + , ); const filterButton = screen.getByRole("button", { name: "Filters" }); @@ -433,7 +404,7 @@ describe("FilterComponent", () => { }); }); - it("should not render filters that are not in orderedFilters list", async () => { + it("renders caller-supplied options that match no predefined filter name (LIT-3151)", async () => { const user = userEvent.setup({ delay: null }); const options: FilterOption[] = [ { @@ -443,18 +414,36 @@ describe("FilterComponent", () => { ]; renderWithProviders( - , + , ); const filterButton = screen.getByRole("button", { name: "Filters" }); await user.click(filterButton); await waitFor(() => { - expect(screen.queryByText("Unknown Filter")).not.toBeInTheDocument(); + expect(screen.getByText("Unknown Filter")).toBeInTheDocument(); + expect(screen.getByPlaceholderText("Enter Unknown Filter...")).toBeInTheDocument(); + }); + }); + + it("renders every Tool Policies filter when none match a predefined name (LIT-3151)", async () => { + const user = userEvent.setup({ delay: null }); + const options: FilterOption[] = [ + { name: "Input Policy", label: "Input Policy", options: [{ label: "Trusted", value: "trusted" }] }, + { name: "Output Policy", label: "Output Policy", options: [{ label: "Blocked", value: "blocked" }] }, + { name: "Team Name", label: "Team Name", options: [] }, + { name: "Key Name", label: "Key Name", options: [] }, + ]; + + renderWithProviders( + , + ); + + await user.click(screen.getByRole("button", { name: "Filters" })); + + await waitFor(() => { + const labels = screen.getAllByText(/^(Input Policy|Output Policy|Team Name|Key Name)$/); + expect(labels.map((l) => l.textContent)).toEqual(["Input Policy", "Output Policy", "Team Name", "Key Name"]); }); }); diff --git a/ui/litellm-dashboard/src/components/molecules/filter.tsx b/ui/litellm-dashboard/src/components/molecules/filter.tsx index 29a55c1f03..7086892c32 100644 --- a/ui/litellm-dashboard/src/components/molecules/filter.tsx +++ b/ui/litellm-dashboard/src/components/molecules/filter.tsx @@ -129,21 +129,6 @@ const FilterComponent: React.FC = ({ } }; - // Define the order of filters - const orderedFilters = [ - "Team ID", - "Status", - "Organization ID", - "Key Alias", - "User ID", - "End User", - "Error Code", - "Error Message", - "Key Hash", - "Model", - "Public model / search tool", - ]; - return (
@@ -159,10 +144,7 @@ const FilterComponent: React.FC = ({ {showFilters && (
- {orderedFilters.map((filterName) => { - const option = options.find((opt) => opt.label === filterName || opt.name === filterName); - if (!option) return null; - + {options.map((option) => { return (
diff --git a/ui/litellm-dashboard/src/components/view_logs/filter_options.ts b/ui/litellm-dashboard/src/components/view_logs/filter_options.ts index 59ac58b674..52632ea586 100644 --- a/ui/litellm-dashboard/src/components/view_logs/filter_options.ts +++ b/ui/litellm-dashboard/src/components/view_logs/filter_options.ts @@ -22,16 +22,6 @@ export function getLogFilterOptions(accessToken: string): FilterOption[] { { label: "Failure", value: "failure" }, ], }, - { - name: "Model", - label: "Model", - customComponent: PaginatedModelSelect, - }, - { - name: FILTER_KEYS.PUBLIC_MODEL_OR_SEARCH_TOOL, - label: "Public model / search tool", - isSearchable: false, - }, { name: "Key Alias", label: "Key Alias", @@ -63,14 +53,24 @@ export function getLogFilterOptions(accessToken: string): FilterOption[] { return filtered; }, }, + { + name: "Error Message", + label: "Error Message", + isSearchable: false, + }, { name: "Key Hash", label: "Key Hash", isSearchable: false, }, { - name: "Error Message", - label: "Error Message", + name: "Model", + label: "Model", + customComponent: PaginatedModelSelect, + }, + { + name: FILTER_KEYS.PUBLIC_MODEL_OR_SEARCH_TOOL, + label: "Public model / search tool", isSearchable: false, }, ];