fix(ui): wire team_id filter to key alias dropdown on Virtual Keys tab

The Key Alias dropdown on the Virtual Keys page was showing aliases from
all teams regardless of which team was selected. The team_id was never
passed through the frontend chain to the backend /key/aliases endpoint.

- Backend: add optional team_id query param to /key/aliases endpoint
- networking.tsx: add team_id param to keyAliasesCall
- useKeyAliases: accept and forward team_id to API call and query key
- filter.tsx: pass allFilters context to custom filter components
- PaginatedKeyAliasSelect: read Team ID from allFilters and pass to hook
This commit is contained in:
Ryan Crabbe 2026-04-03 15:38:16 -07:00
parent 3a3fb83a0a
commit ecb951a320
No known key found for this signature in database
5 changed files with 19 additions and 1 deletions

View file

@ -4259,6 +4259,9 @@ async def key_aliases(
search: Optional[str] = Query(
None, description="Search key aliases (case-insensitive partial match)"
),
team_id: Optional[str] = Query(
None, description="Filter aliases to keys belonging to this team"
),
) -> Dict[str, Any]:
"""
Lists key aliases with pagination and optional search.
@ -4334,6 +4337,10 @@ async def key_aliases(
query_params.append(f"%{search}%")
where_parts.append(f"key_alias ILIKE ${len(query_params)}")
if team_id:
query_params.append(team_id)
where_parts.append(f"team_id = ${len(query_params)}")
where_sql = " AND ".join(where_parts)
count_sql = f'SELECT COUNT(*) AS count FROM "LiteLLM_VerificationToken" WHERE {where_sql}'

View file

@ -8,6 +8,7 @@ const infiniteKeyAliasKeys = createQueryKeys("infiniteKeyAliases");
export const useInfiniteKeyAliases = (
size: number = 50,
search?: string,
team_id?: string,
) => {
const { accessToken } = useAuthorized();
return useInfiniteQuery<PaginatedKeyAliasResponse>({
@ -15,6 +16,7 @@ export const useInfiniteKeyAliases = (
filters: {
size,
...(search && { search }),
...(team_id && { team_id }),
},
}),
queryFn: async ({ pageParam }) => {
@ -23,6 +25,7 @@ export const useInfiniteKeyAliases = (
pageParam as number,
size,
search,
team_id,
);
},
initialPageParam: 1,

View file

@ -12,6 +12,7 @@ export interface PaginatedKeyAliasSelectProps {
pageSize?: number;
allowClear?: boolean;
disabled?: boolean;
allFilters?: { [key: string]: string };
}
const SCROLL_THRESHOLD = 0.8;
@ -25,19 +26,22 @@ export const PaginatedKeyAliasSelect = ({
pageSize = 50,
allowClear = true,
disabled = false,
allFilters,
}: PaginatedKeyAliasSelectProps) => {
const [searchInput, setSearchInput] = useState("");
const [debouncedSearch, setDebouncedSearch] = useDebouncedState("", {
wait: DEBOUNCE_MS,
});
const teamId = allFilters?.["Team ID"] || undefined;
const {
data,
fetchNextPage,
hasNextPage,
isFetchingNextPage,
isLoading,
} = useInfiniteKeyAliases(pageSize, debouncedSearch || undefined);
} = useInfiniteKeyAliases(pageSize, debouncedSearch || undefined, teamId);
const options = useMemo(() => {
if (!data?.pages) return [];

View file

@ -7,6 +7,7 @@ export interface FilterOptionCustomComponentProps {
value?: string;
onChange: (value: string) => void;
placeholder?: string;
allFilters?: { [key: string]: string };
}
export interface FilterOption {
@ -209,6 +210,7 @@ const FilterComponent: React.FC<FilterComponentProps> = ({
value={tempValues[option.name] || undefined}
onChange={(value) => handleFilterChange(option.name, value ?? "")}
placeholder={`Select ${option.label || option.name}...`}
allFilters={tempValues}
/>
);
})()

View file

@ -3205,6 +3205,7 @@ export const keyAliasesCall = async (
page: number = 1,
size: number = 50,
search?: string,
team_id?: string,
): Promise<PaginatedKeyAliasResponse> => {
/**
* Get key aliases from proxy with pagination and optional search
@ -3215,6 +3216,7 @@ export const keyAliasesCall = async (
page: String(page),
size: String(size),
...(search ? { search } : {}),
...(team_id ? { team_id } : {}),
}),
);
let url = proxyBaseUrl ? `${proxyBaseUrl}/key/aliases` : `/key/aliases`;