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.
This commit is contained in:
Ryan Crabbe
2026-03-11 19:12:14 -07:00
parent fdfb65c204
commit d4fa990176
4 changed files with 50 additions and 19 deletions
+1
View File
@@ -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)
@@ -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:
@@ -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 = (
<div className="flex flex-col gap-2 text-xs min-w-[200px] max-w-[300px]">
<div className="flex flex-col min-w-0">
<span className="text-gray-400">User ID</span>
<Typography.Text
className="font-mono text-xs"
ellipsis={{ tooltip: userId }}
copyable
>
{userId}
</Typography.Text>
</div>
{[
{ label: "User Alias", value: userAlias },
{ label: "User Email", value: userEmail },
{ label: "User ID", value: userId },
].map(({ label, value }) => (
<div key={label} className="flex flex-col min-w-0">
<span className="text-gray-400">{label}</span>
{value ? (
<Typography.Text
className="font-mono text-xs"
ellipsis={{ tooltip: value }}
copyable
>
{value}
</Typography.Text>
) : (
<span className="font-mono">-</span>
)}
</div>
))}
</div>
);
if (isDefaultAdmin) {
if (isDefaultAdmin && !userAlias && !userEmail) {
return (
<Popover content={popoverContent} trigger="hover" placement="bottomLeft">
<span className="cursor-default">
@@ -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}
</span>
</Popover>
);
@@ -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 {