Skip to content

Virtual Keys: Filter by key alias (#10035) #10085

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion ui/litellm-dashboard/src/components/all_keys_table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ interface AllKeysTableProps {
teams: Team[] | null;
selectedTeam: Team | null;
setSelectedTeam: (team: Team | null) => void;
selectedKeyAlias: string | null;
setSelectedKeyAlias: Setter<string | null>;
accessToken: string | null;
userID: string | null;
userRole: string | null;
Expand Down Expand Up @@ -98,6 +100,8 @@ export function AllKeysTable({
teams,
selectedTeam,
setSelectedTeam,
selectedKeyAlias,
setSelectedKeyAlias,
accessToken,
userID,
userRole,
Expand All @@ -123,7 +127,8 @@ export function AllKeysTable({
organizations,
accessToken,
setSelectedTeam,
setCurrentOrg
setCurrentOrg,
setSelectedKeyAlias
});

useEffect(() => {
Expand Down Expand Up @@ -358,6 +363,23 @@ export function AllKeysTable({
}));
}
},
{
name: "Key Alias",
label: "Key Alias",
isSearchable: true,
searchFn: async (searchText) => {
const filteredKeyAliases = allKeyAliases.filter(key => {
return key.toLowerCase().includes(searchText.toLowerCase())
});

return filteredKeyAliases.map((key) => {
return {
label: key,
value: key
}
});
}
}
];


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export const fetchAllKeyAliases = async (accessToken: string | null): Promise<st
accessToken,
null, // organization_id
"", // team_id
null, // selectedKeyAlias
currentPage,
100 // larger page size to reduce number of requests
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ import { useEffect, useState } from "react";
import { KeyResponse } from "../key_team_helpers/key_list";
import { Organization } from "../networking";
import { Team } from "../key_team_helpers/key_list";
import { useQuery } from "@tanstack/react-query";
import { fetchAllKeyAliases, fetchAllOrganizations, fetchAllTeams } from "./filter_helpers";
import { Setter } from "@/types";


export interface FilterState {
'Team ID': string;
'Organization ID': string;
'Key Alias': string;
[key: string]: string;
}

Expand All @@ -15,20 +20,22 @@ export function useFilterLogic({
organizations,
accessToken,
setSelectedTeam,
setCurrentOrg
setCurrentOrg,
setSelectedKeyAlias
}: {
keys: KeyResponse[];
teams: Team[] | null;
organizations: Organization[] | null;
accessToken: string | null;
setSelectedTeam: (team: Team | null) => void;
setCurrentOrg: React.Dispatch<React.SetStateAction<Organization | null>>;
setSelectedKeyAlias: Setter<string | null>
}) {
const [filters, setFilters] = useState<FilterState>({
'Team ID': '',
'Organization ID': '',
'Key Alias': ''
});
const [allKeyAliases, setAllKeyAliases] = useState<string[]>([]);
const [allTeams, setAllTeams] = useState<Team[]>(teams || []);
const [allOrganizations, setAllOrganizations] = useState<Organization[]>(organizations || []);
const [filteredKeys, setFilteredKeys] = useState<KeyResponse[]>(keys);
Expand Down Expand Up @@ -79,6 +86,16 @@ export function useFilterLogic({
}
}, [accessToken]);

const queryAllKeysQuery = useQuery({
queryKey: ['allKeys'],
queryFn: async () => {
if (!accessToken) throw new Error('Access token required');
return await fetchAllKeyAliases(accessToken);
},
enabled: !!accessToken
});
const allKeyAliases = queryAllKeysQuery.data || []

// Update teams and organizations when props change
useEffect(() => {
if (teams && teams.length > 0) {
Expand All @@ -103,6 +120,7 @@ export function useFilterLogic({
setFilters({
'Team ID': newFilters['Team ID'] || '',
'Organization ID': newFilters['Organization ID'] || '',
'Key Alias': newFilters['Key Alias'] || ''
});

// Handle Team change
Expand All @@ -120,13 +138,20 @@ export function useFilterLogic({
setCurrentOrg(selectedOrg);
}
}

const keyAlias = newFilters['Key Alias'];
const selectedKeyAlias = keyAlias
? allKeyAliases.find((k) => k === keyAlias) || null
: null;
setSelectedKeyAlias(selectedKeyAlias)
};

const handleFilterReset = () => {
// Reset filters state
setFilters({
'Team ID': '',
'Organization ID': '',
'Key Alias': ''
});

// Reset team and org selections
Expand All @@ -144,6 +169,3 @@ export function useFilterLogic({
handleFilterReset
};
}

// These functions are imported from key_team_helpers/filter_helpers.ts
import { fetchAllKeyAliases, fetchAllTeams, fetchAllOrganizations } from './filter_helpers';
19 changes: 15 additions & 4 deletions ui/litellm-dashboard/src/components/key_team_helpers/key_list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ total_pages: number;
interface UseKeyListProps {
selectedTeam?: Team;
currentOrg: Organization | null;
selectedKeyAlias: string | null;
accessToken: string;
currentPage?: number;
}
Expand All @@ -108,9 +109,9 @@ setKeys: Setter<KeyResponse[]>;
const useKeyList = ({
selectedTeam,
currentOrg,
selectedKeyAlias,
accessToken,
currentPage = 1,

}: UseKeyListProps): UseKeyListReturn => {
const [keyData, setKeyData] = useState<KeyListResponse>({
keys: [],
Expand All @@ -134,8 +135,9 @@ const useKeyList = ({
accessToken,
currentOrg?.organization_id || null,
selectedTeam?.team_id || "",
selectedKeyAlias,
params.page as number || 1,
50
50,
);
console.log("data", data);
setKeyData(data);
Expand All @@ -149,8 +151,17 @@ const useKeyList = ({

useEffect(() => {
fetchKeys();
console.log("selectedTeam", selectedTeam, "currentOrg", currentOrg, "accessToken", accessToken);
}, [selectedTeam, currentOrg, accessToken]);
console.log(
'selectedTeam',
selectedTeam,
'currentOrg',
currentOrg,
'accessToken',
accessToken,
'selectedKeyAlias',
selectedKeyAlias
);
}, [selectedTeam, currentOrg, accessToken, selectedKeyAlias]);

const setKeys = (newKeysOrUpdater: KeyResponse[] | ((prevKeys: KeyResponse[]) => KeyResponse[])) => {
setKeyData(prevData => {
Expand Down
7 changes: 6 additions & 1 deletion ui/litellm-dashboard/src/components/networking.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2569,8 +2569,9 @@ export const keyListCall = async (
accessToken: String,
organizationID: string | null,
teamID: string | null,
selectedKeyAlias: string | null,
page: number,
pageSize: number
pageSize: number,
) => {
/**
* Get all available teams on proxy
Expand All @@ -2588,6 +2589,10 @@ export const keyListCall = async (
queryParams.append('organization_id', organizationID.toString());
}

if (selectedKeyAlias) {
queryParams.append('key_alias', selectedKeyAlias)
}

if (page) {
queryParams.append('page', page.toString());
}
Expand Down
3 changes: 3 additions & 0 deletions ui/litellm-dashboard/src/components/user_dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ const UserDashboard: React.FC<UserDashboardProps> = ({
team_id: null,
};
const [selectedTeam, setSelectedTeam] = useState<any | null>(null);
const [selectedKeyAlias, setSelectedKeyAlias] = useState<string | null>(null);
// check if window is not undefined
if (typeof window !== "undefined") {
window.addEventListener("beforeunload", function () {
Expand Down Expand Up @@ -350,6 +351,8 @@ const UserDashboard: React.FC<UserDashboardProps> = ({
accessToken={accessToken}
selectedTeam={selectedTeam ? selectedTeam : null}
setSelectedTeam={setSelectedTeam}
selectedKeyAlias={selectedKeyAlias}
setSelectedKeyAlias={setSelectedKeyAlias}
data={keys}
setData={setKeys}
premiumUser={premiumUser}
Expand Down
10 changes: 9 additions & 1 deletion ui/litellm-dashboard/src/components/view_key_table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ import useKeyList from "./key_team_helpers/key_list";
import { KeyResponse } from "./key_team_helpers/key_list";
import { AllKeysTable } from "./all_keys_table";
import { Team } from "./key_team_helpers/key_list";
import { Setter } from "@/types";

const isLocal = process.env.NODE_ENV === "development";
const proxyBaseUrl = isLocal ? "http://localhost:4000" : null;
Expand Down Expand Up @@ -107,6 +108,8 @@ interface ViewKeyTableProps {
currentOrg: Organization | null;
organizations: Organization[] | null;
setCurrentOrg: React.Dispatch<React.SetStateAction<Organization | null>>;
selectedKeyAlias: string | null;
setSelectedKeyAlias: Setter<string | null>;
}

interface ItemData {
Expand Down Expand Up @@ -154,7 +157,9 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
premiumUser,
currentOrg,
organizations,
setCurrentOrg
setCurrentOrg,
selectedKeyAlias,
setSelectedKeyAlias
}) => {
const [isButtonClicked, setIsButtonClicked] = useState(false);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
Expand All @@ -179,6 +184,7 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
const { keys, isLoading, error, pagination, refresh, setKeys } = useKeyList({
selectedTeam,
currentOrg,
selectedKeyAlias,
accessToken,
});

Expand Down Expand Up @@ -432,6 +438,8 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
organizations={organizations}
setCurrentOrg={setCurrentOrg}
refresh={refresh}
selectedKeyAlias={selectedKeyAlias}
setSelectedKeyAlias={setSelectedKeyAlias}
/>

{isDeleteModalOpen && (
Expand Down
Loading