diff --git a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.test.tsx b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.test.tsx
index 65de44bb309..5347f202757 100644
--- a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.test.tsx
+++ b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.test.tsx
@@ -203,8 +203,7 @@ beforeEach(() => {
"User ID": "user-1",
"User Email": "user@example.com",
"User Role": "user",
- "Sort By": "created_at",
- "Sort Order": "desc",
+ "Key Hash": "",
},
allTeams: [mockTeam],
allOrganizations: [mockOrganization],
@@ -322,8 +321,7 @@ it("should show 'No keys found' message when filteredKeys is empty", () => {
"Organization ID": "",
"Key Alias": "",
"User ID": "",
- "Sort By": "created_at",
- "Sort Order": "desc",
+ "Key Hash": "",
},
allTeams: [mockTeam],
allOrganizations: [mockOrganization],
@@ -370,8 +368,7 @@ it("should handle models with more than 3 entries to trigger expansion UI", () =
"Organization ID": "",
"Key Alias": "",
"User ID": "",
- "Sort By": "created_at",
- "Sort Order": "desc",
+ "Key Hash": "",
},
allTeams: [mockTeam],
allOrganizations: [mockOrganization],
@@ -518,8 +515,7 @@ it("should display 'Default Proxy Admin' for user_id when value is 'default_user
"Organization ID": "",
"Key Alias": "",
"User ID": "",
- "Sort By": "created_at",
- "Sort Order": "desc",
+ "Key Hash": "",
},
allTeams: [mockTeam],
allOrganizations: [mockOrganization],
@@ -568,8 +564,7 @@ it("should display 'Default Proxy Admin' for created_by when value is 'default_u
"Organization ID": "",
"Key Alias": "",
"User ID": "",
- "Sort By": "created_at",
- "Sort Order": "desc",
+ "Key Hash": "",
},
allTeams: [mockTeam],
allOrganizations: [mockOrganization],
@@ -626,8 +621,7 @@ it("should display created_by_user email in 'Created By' column when available",
"Organization ID": "",
"Key Alias": "",
"User ID": "",
- "Sort By": "created_at",
- "Sort Order": "desc",
+ "Key Hash": "",
},
allTeams: [mockTeam],
allOrganizations: [mockOrganization],
@@ -681,8 +675,7 @@ it("should display created_by_user alias over email when both available", async
"Organization ID": "",
"Key Alias": "",
"User ID": "",
- "Sort By": "created_at",
- "Sort Order": "desc",
+ "Key Hash": "",
},
allTeams: [mockTeam],
allOrganizations: [mockOrganization],
@@ -731,8 +724,7 @@ it("should render table without crashing when models is null", async () => {
"Organization ID": "",
"Key Alias": "",
"User ID": "",
- "Sort By": "created_at",
- "Sort Order": "desc",
+ "Key Hash": "",
},
allTeams: [mockTeam],
allOrganizations: [mockOrganization],
@@ -782,8 +774,7 @@ it("should render table without crashing when models is undefined", async () =>
"Organization ID": "",
"Key Alias": "",
"User ID": "",
- "Sort By": "created_at",
- "Sort Order": "desc",
+ "Key Hash": "",
},
allTeams: [mockTeam],
allOrganizations: [mockOrganization],
@@ -868,8 +859,7 @@ it("should display 'Unknown' for last_active when value is null", async () => {
"Organization ID": "",
"Key Alias": "",
"User ID": "",
- "Sort By": "created_at",
- "Sort Order": "desc",
+ "Key Hash": "",
},
allTeams: [mockTeam],
allOrganizations: [mockOrganization],
@@ -916,7 +906,7 @@ describe("pagination display – total count and page count", () => {
} as any);
mockUseFilterLogic.mockReturnValue({
- filters: { "Team ID": "", "Organization ID": "", "Key Alias": "", "User ID": "", "Sort By": "created_at", "Sort Order": "desc" },
+ filters: { "Team ID": "", "Organization ID": "", "Key Alias": "", "User ID": "", "Key Hash": "" },
allTeams: [mockTeam],
allOrganizations: [mockOrganization],
handleFilterChange: vi.fn(),
@@ -947,7 +937,7 @@ describe("pagination display – total count and page count", () => {
} as any);
mockUseFilterLogic.mockReturnValue({
- filters: { "Team ID": "", "Organization ID": "", "Key Alias": "aaaaa", "User ID": "", "Sort By": "created_at", "Sort Order": "desc" },
+ filters: { "Team ID": "", "Organization ID": "", "Key Alias": "aaaaa", "User ID": "", "Key Hash": "" },
allTeams: [mockTeam],
allOrganizations: [mockOrganization],
handleFilterChange: vi.fn(),
@@ -964,7 +954,7 @@ describe("pagination display – total count and page count", () => {
it("should pass current filter values into useKeys so remount/refetch keeps the filter applied", async () => {
mockUseFilterLogic.mockReturnValue({
- filters: { "Team ID": "", "Organization ID": "", "Key Alias": "aaaaa", "User ID": "", "Sort By": "created_at", "Sort Order": "desc" },
+ filters: { "Team ID": "", "Organization ID": "", "Key Alias": "aaaaa", "User ID": "", "Key Hash": "" },
allTeams: [mockTeam],
allOrganizations: [mockOrganization],
handleFilterChange: vi.fn(),
@@ -983,6 +973,26 @@ describe("pagination display – total count and page count", () => {
);
});
});
+
+ it("should forward the Key Hash filter into useKeys", async () => {
+ mockUseFilterLogic.mockReturnValue({
+ filters: { "Team ID": "", "Organization ID": "", "Key Alias": "", "User ID": "", "Key Hash": "hash-xyz" },
+ allTeams: [mockTeam],
+ allOrganizations: [mockOrganization],
+ handleFilterChange: vi.fn(),
+ handleFilterReset: vi.fn(),
+ });
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ expect(mockUseKeys).toHaveBeenCalledWith(
+ expect.any(Number),
+ expect.any(Number),
+ expect.objectContaining({ keyHash: "hash-xyz" }),
+ );
+ });
+ });
});
describe("refetch button", () => {
diff --git a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx
index 2dfc6f45f29..059fc462866 100644
--- a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx
+++ b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx
@@ -105,6 +105,7 @@ export function VirtualKeysTable({ teams, organizations, onSortChange, currentSo
debouncedFilters["Organization ID"],
debouncedFilters["Key Alias"],
debouncedFilters["User ID"],
+ debouncedFilters["Key Hash"],
]);
const {
@@ -121,6 +122,7 @@ export function VirtualKeysTable({ teams, organizations, onSortChange, currentSo
organizationID: debouncedFilters["Organization ID"] || undefined,
selectedKeyAlias: debouncedFilters["Key Alias"] || undefined,
userID: debouncedFilters["User ID"] || undefined,
+ keyHash: debouncedFilters["Key Hash"] || undefined,
});
const [expandedAccordions, setExpandedAccordions] = useState>({});
@@ -700,7 +702,11 @@ export function VirtualKeysTable({ teams, organizations, onSortChange, currentSo
options={filterOptions}
onApplyFilters={handleFilterChange}
initialValues={filters}
- onResetFilters={handleFilterReset}
+ onResetFilters={() => {
+ handleFilterReset();
+ setSorting([{ id: "created_at", desc: true }]);
+ onSortChange?.("created_at", "desc");
+ }}
/>
diff --git a/ui/litellm-dashboard/src/components/key_team_helpers/filter_logic.test.tsx b/ui/litellm-dashboard/src/components/key_team_helpers/filter_logic.test.tsx
index 0158f159cba..1105052265c 100644
--- a/ui/litellm-dashboard/src/components/key_team_helpers/filter_logic.test.tsx
+++ b/ui/litellm-dashboard/src/components/key_team_helpers/filter_logic.test.tsx
@@ -17,8 +17,7 @@ const DEFAULT_FILTERS = {
"Organization ID": "",
"Key Alias": "",
"User ID": "",
- "Sort By": "created_at",
- "Sort Order": "desc",
+ "Key Hash": "",
};
describe("useFilterLogic – filter state management", () => {
@@ -42,6 +41,16 @@ describe("useFilterLogic – filter state management", () => {
expect(result.current.filters["Key Alias"]).toBe("my-alias");
});
+ it("records the Key Hash filter when handleFilterChange is called", () => {
+ const { result } = renderHook(() => useFilterLogic(defaultProps));
+
+ act(() => {
+ result.current.handleFilterChange({ "Key Hash": "abc123" });
+ });
+
+ expect(result.current.filters["Key Hash"]).toBe("abc123");
+ });
+
it("preserves filter state across re-renders (regression: bug where results reset on remount)", () => {
const { result, rerender } = renderHook(() => useFilterLogic(defaultProps));
diff --git a/ui/litellm-dashboard/src/components/key_team_helpers/filter_logic.tsx b/ui/litellm-dashboard/src/components/key_team_helpers/filter_logic.tsx
index 3f762d26b7c..52630bf708f 100644
--- a/ui/litellm-dashboard/src/components/key_team_helpers/filter_logic.tsx
+++ b/ui/litellm-dashboard/src/components/key_team_helpers/filter_logic.tsx
@@ -10,8 +10,7 @@ export interface FilterState {
"Key Alias": string;
[key: string]: string;
"User ID": string;
- "Sort By": string;
- "Sort Order": string;
+ "Key Hash": string;
}
const DEFAULT_FILTERS: FilterState = {
@@ -19,8 +18,7 @@ const DEFAULT_FILTERS: FilterState = {
"Organization ID": "",
"Key Alias": "",
"User ID": "",
- "Sort By": "created_at",
- "Sort Order": "desc",
+ "Key Hash": "",
};
export function useFilterLogic({
@@ -71,8 +69,7 @@ export function useFilterLogic({
"Organization ID": newFilters["Organization ID"] || "",
"Key Alias": newFilters["Key Alias"] || "",
"User ID": newFilters["User ID"] || "",
- "Sort By": newFilters["Sort By"] || "created_at",
- "Sort Order": newFilters["Sort Order"] || "desc",
+ "Key Hash": newFilters["Key Hash"] || "",
});
};