From d4fa990176cdca0ec7a678fe147115e1759103e3 Mon Sep 17 00:00:00 2001 From: Ryan Crabbe Date: Wed, 11 Mar 2026 19:12:14 -0700 Subject: [PATCH] feat(ui): show user email/alias instead of UUID in Virtual Keys "Created By" column Expand the existing expand=user lookup on /key/list to also resolve created_by user IDs, and display the result in the Created By column with alias > email > UUID fallback and a popover showing all three. --- litellm/proxy/_types.py | 1 + .../key_management_endpoints.py | 24 ++++++++---- .../VirtualKeysPage/VirtualKeysTable.tsx | 39 +++++++++++++------ .../components/key_team_helpers/key_list.tsx | 5 +++ 4 files changed, 50 insertions(+), 19 deletions(-) diff --git a/litellm/proxy/_types.py b/litellm/proxy/_types.py index bb7d787d9d..074fa5719f 100644 --- a/litellm/proxy/_types.py +++ b/litellm/proxy/_types.py @@ -2458,6 +2458,7 @@ class UserAPIKeyAuth( user_max_budget: Optional[float] = None request_route: Optional[str] = None user: Optional[Any] = None # Expanded user object when expand=user is used + created_by_user: Optional[Any] = None # Expanded created_by user when expand=user is used end_user_object_permission: Optional[LiteLLM_ObjectPermissionTable] = None model_config = ConfigDict(arbitrary_types_allowed=True) diff --git a/litellm/proxy/management_endpoints/key_management_endpoints.py b/litellm/proxy/management_endpoints/key_management_endpoints.py index 654205252b..941b2c276d 100644 --- a/litellm/proxy/management_endpoints/key_management_endpoints.py +++ b/litellm/proxy/management_endpoints/key_management_endpoints.py @@ -4597,9 +4597,11 @@ async def _list_key_helper( user_map = {} if expand and "user" in expand: user_ids = [key.user_id for key in keys if key.user_id] - if user_ids: + created_by_ids = [key.created_by for key in keys if key.created_by] + all_ids = list(set(user_ids + created_by_ids)) # Remove duplicates + if all_ids: users = await prisma_client.db.litellm_usertable.find_many( - where={"user_id": {"in": list(set(user_ids))}} # Remove duplicates + where={"user_id": {"in": all_ids}} ) user_map = {user.user_id: user for user in users} @@ -4617,11 +4619,19 @@ async def _list_key_helper( key_dict = await attach_object_permission_to_dict(key_dict, prisma_client) # Include user information if expand includes "user" - if expand and "user" in expand and key.user_id and key.user_id in user_map: - try: - key_dict["user"] = user_map[key.user_id].model_dump() - except Exception: - key_dict["user"] = user_map[key.user_id].dict() + if expand and "user" in expand: + if key.user_id and key.user_id in user_map: + try: + key_dict["user"] = user_map[key.user_id].model_dump() + except Exception: + key_dict["user"] = user_map[key.user_id].dict() + if key.created_by and key.created_by in user_map: + created_by_user = user_map[key.created_by] + key_dict["created_by_user"] = { + "user_id": created_by_user.user_id, + "user_email": created_by_user.user_email, + "user_alias": created_by_user.user_alias, + } if return_full_object is True or (expand and "user" in expand): if use_deleted_table: diff --git a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx index fd0cd4dd50..6091794170 100644 --- a/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx +++ b/ui/litellm-dashboard/src/components/VirtualKeysPage/VirtualKeysTable.tsx @@ -311,25 +311,40 @@ export function VirtualKeysTable({ teams, organizations, onSortChange, currentSo cell: (info) => { const userId = info.getValue() as string | null; if (!userId) return "-"; + const key = info.row.original; + const createdByUser = key.created_by_user; + const userAlias = createdByUser?.user_alias ?? null; + const userEmail = createdByUser?.user_email ?? null; const isDefaultAdmin = userId === "default_user_id"; + const displayValue = userAlias || userEmail || userId; const width = 160; const popoverContent = (
-
- User ID - - {userId} - -
+ {[ + { label: "User Alias", value: userAlias }, + { label: "User Email", value: userEmail }, + { label: "User ID", value: userId }, + ].map(({ label, value }) => ( +
+ {label} + {value ? ( + + {value} + + ) : ( + - + )} +
+ ))}
); - if (isDefaultAdmin) { + if (isDefaultAdmin && !userAlias && !userEmail) { return ( @@ -345,7 +360,7 @@ export function VirtualKeysTable({ teams, organizations, onSortChange, currentSo className="font-mono text-xs truncate block cursor-default" style={{ maxWidth: width, overflow: "hidden" }} > - {userId} + {displayValue} ); diff --git a/ui/litellm-dashboard/src/components/key_team_helpers/key_list.tsx b/ui/litellm-dashboard/src/components/key_team_helpers/key_list.tsx index a31162cb2f..a681e438cd 100644 --- a/ui/litellm-dashboard/src/components/key_team_helpers/key_list.tsx +++ b/ui/litellm-dashboard/src/components/key_team_helpers/key_list.tsx @@ -101,6 +101,11 @@ export interface KeyResponse { user_email: string; user_alias: string | null; }; + created_by_user?: { + user_id: string; + user_email: string; + user_alias: string | null; + }; } interface KeyListResponse {