diff --git a/ui/litellm-dashboard/src/components/all_keys_table.test.tsx b/ui/litellm-dashboard/src/components/all_keys_table.test.tsx
index 201a4be75bd..1458143d77e 100644
--- a/ui/litellm-dashboard/src/components/all_keys_table.test.tsx
+++ b/ui/litellm-dashboard/src/components/all_keys_table.test.tsx
@@ -93,6 +93,10 @@ const mockKey: KeyResponse = {
user_tpm_limit: 1000,
user_rpm_limit: 100,
user_email: "user@example.com",
+ user: {
+ user_email: "user@example.com",
+ user_id: "user-1",
+ },
};
const mockTeam: Team = {
@@ -190,3 +194,35 @@ it("should display key information correctly", async () => {
expect(screen.getByText("5.5000")).toBeInTheDocument();
});
});
+
+it("should display user email correctly", async () => {
+ const mockProps = {
+ keys: [mockKey],
+ setKeys: vi.fn(),
+ isLoading: false,
+ pagination: {
+ currentPage: 1,
+ totalPages: 1,
+ totalCount: 1,
+ },
+ onPageChange: vi.fn(),
+ pageSize: 50,
+ teams: [mockTeam],
+ selectedTeam: null,
+ setSelectedTeam: vi.fn(),
+ selectedKeyAlias: null,
+ setSelectedKeyAlias: vi.fn(),
+ accessToken: "test-token",
+ userID: "user-1",
+ userRole: "admin",
+ organizations: [mockOrganization],
+ setCurrentOrg: vi.fn(),
+ premiumUser: false,
+ };
+
+ renderWithProviders();
+
+ await waitFor(() => {
+ expect(screen.getByText("user@example.com")).toBeInTheDocument();
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/all_keys_table.tsx b/ui/litellm-dashboard/src/components/all_keys_table.tsx
index 210ce09fa34..10f678be03f 100644
--- a/ui/litellm-dashboard/src/components/all_keys_table.tsx
+++ b/ui/litellm-dashboard/src/components/all_keys_table.tsx
@@ -1,23 +1,39 @@
"use client";
-import React, { useEffect, useState } from "react";
-import { ColumnDef, ColumnResizeMode, ColumnResizeDirection } from "@tanstack/react-table";
-import { Select, SelectItem } from "@tremor/react";
-import { Button } from "@tremor/react";
-import KeyInfoView from "./templates/key_info_view";
-import { Tooltip } from "antd";
-import { Team, KeyResponse } from "./key_team_helpers/key_list";
-import FilterComponent from "./molecules/filter";
-import { FilterOption } from "./molecules/filter";
-import { Organization, userListCall } from "./networking";
-import { useFilterLogic } from "./key_team_helpers/filter_logic";
import { Setter } from "@/types";
-import { updateExistingKeys } from "@/utils/dataUtils";
-import { flexRender, getCoreRowModel, getSortedRowModel, SortingState, useReactTable } from "@tanstack/react-table";
-import { Table, TableHead, TableHeaderCell, TableBody, TableRow, TableCell, Icon } from "@tremor/react";
-import { SwitchVerticalIcon, ChevronUpIcon, ChevronDownIcon, ChevronRightIcon } from "@heroicons/react/outline";
-import { Badge, Text } from "@tremor/react";
+import { formatNumberWithCommas, updateExistingKeys } from "@/utils/dataUtils";
+import { ChevronDownIcon, ChevronRightIcon, ChevronUpIcon, SwitchVerticalIcon } from "@heroicons/react/outline";
+import {
+ ColumnDef,
+ ColumnResizeDirection,
+ ColumnResizeMode,
+ flexRender,
+ getCoreRowModel,
+ getSortedRowModel,
+ SortingState,
+ useReactTable,
+} from "@tanstack/react-table";
+import {
+ Badge,
+ Button,
+ Icon,
+ Select,
+ SelectItem,
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableHeaderCell,
+ TableRow,
+ Text,
+} from "@tremor/react";
+import { Tooltip } from "antd";
+import React, { useEffect, useState } from "react";
import { getModelDisplayName } from "./key_team_helpers/fetch_available_models_team_key";
-import { formatNumberWithCommas } from "@/utils/dataUtils";
+import { useFilterLogic } from "./key_team_helpers/filter_logic";
+import { KeyResponse, Team } from "./key_team_helpers/key_list";
+import FilterComponent, { FilterOption } from "./molecules/filter";
+import { Organization } from "./networking";
+import KeyInfoView from "./templates/key_info_view";
interface AllKeysTableProps {
keys: KeyResponse[];
@@ -124,7 +140,6 @@ export function AllKeysTable({
setAccessToken,
}: AllKeysTableProps) {
const [selectedKeyId, setSelectedKeyId] = useState(null);
- const [userList, setUserList] = useState([]);
const [columnResizeMode, setColumnResizeMode] = React.useState("onChange");
const [columnResizeDirection, setColumnResizeDirection] = React.useState("ltr");
const [sorting, setSorting] = React.useState(() => {
@@ -155,17 +170,6 @@ export function AllKeysTable({
accessToken,
});
- useEffect(() => {
- if (accessToken) {
- const user_IDs = keys.map((key) => key.user_id).filter((id) => id !== null);
- const fetchUserList = async () => {
- const userListData = await userListCall(accessToken, user_IDs, 1, 100);
- setUserList(userListData.users);
- };
- fetchUserList();
- }
- }, [accessToken, keys]);
-
// Add a useEffect to call refresh when a key is created
useEffect(() => {
if (refresh) {
@@ -269,18 +273,19 @@ export function AllKeysTable({
},
{
id: "user_email",
- accessorKey: "user_id",
+ accessorKey: "user",
header: "User Email",
size: 160,
cell: (info) => {
- const userId = info.getValue() as string;
- const user = userList.find((u) => u.user_id === userId);
- return user?.user_email ? (
-
- {user?.user_email.slice(0, 20)}...
+ const user = info.getValue() as any;
+ const value = user?.user_email;
+ const width = info.cell.column.getSize();
+ return (
+
+
+ {value ?? "-"}
+
- ) : (
- "-"
);
},
},
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 6bb014e6187..a04fbf3943d 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
@@ -1,6 +1,6 @@
-import { useState, useEffect } from "react";
-import { keyListCall, Member, Organization } from "../networking";
import { Setter } from "@/types";
+import { useEffect, useState } from "react";
+import { keyListCall, Member, Organization } from "../networking";
export interface Team {
team_id: string;
@@ -91,6 +91,10 @@ export interface KeyResponse {
last_rotation_at?: string;
key_rotation_at?: string;
next_rotation_at?: string;
+ user?: {
+ user_id: string;
+ user_email: string;
+ };
}
interface KeyListResponse {
@@ -106,6 +110,7 @@ interface UseKeyListProps {
selectedKeyAlias: string | null;
accessToken: string;
createClicked: boolean;
+ expand?: string[];
}
interface PaginationData {
@@ -129,6 +134,7 @@ const useKeyList = ({
selectedKeyAlias,
accessToken,
createClicked,
+ expand = [],
}: UseKeyListProps): UseKeyListReturn => {
const [keyData, setKeyData] = useState({
keys: [],
@@ -151,7 +157,19 @@ const useKeyList = ({
const page = typeof params.page === "number" ? params.page : 1;
const pageSize = typeof params.pageSize === "number" ? params.pageSize : 100;
- const data = await keyListCall(accessToken, null, null, null, null, null, page, pageSize);
+ const data = await keyListCall(
+ accessToken,
+ null,
+ null,
+ null,
+ null,
+ null,
+ page,
+ pageSize,
+ null,
+ null,
+ expand.join(","),
+ );
console.log("data", data);
setKeyData(data);
setError(null);
diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx
index aeba207db26..987aaf96cb3 100644
--- a/ui/litellm-dashboard/src/components/networking.tsx
+++ b/ui/litellm-dashboard/src/components/networking.tsx
@@ -3250,6 +3250,7 @@ export const keyListCall = async (
pageSize: number,
sortBy: string | null = null,
sortOrder: string | null = null,
+ expand: string | null = null,
) => {
/**
* Get all available teams on proxy
@@ -3294,6 +3295,11 @@ export const keyListCall = async (
if (sortOrder) {
queryParams.append("sort_order", sortOrder);
}
+
+ if (expand) {
+ queryParams.append("expand", expand);
+ }
+
queryParams.append("return_full_object", "true");
queryParams.append("include_team_keys", "true");
queryParams.append("include_created_by_keys", "true");
diff --git a/ui/litellm-dashboard/src/components/templates/view_key_table.tsx b/ui/litellm-dashboard/src/components/templates/view_key_table.tsx
index 5defc287a07..f8a86f0fad4 100644
--- a/ui/litellm-dashboard/src/components/templates/view_key_table.tsx
+++ b/ui/litellm-dashboard/src/components/templates/view_key_table.tsx
@@ -1,17 +1,14 @@
"use client";
-import React, { useEffect, useState } from "react";
-import { keyDeleteCall, Organization } from "../networking";
-import { add } from "date-fns";
-import { regenerateKeyCall } from "../networking";
-import { Grid, Col, Button, Text, Title, TextInput } from "@tremor/react";
-import { fetchAvailableModelsForTeamOrKey } from "../key_team_helpers/fetch_available_models_team_key";
-import { Modal, Form, InputNumber } from "antd";
-import { CopyToClipboard } from "react-copy-to-clipboard";
-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";
+import { Button, Col, Grid, Text, TextInput, Title } from "@tremor/react";
+import { Form, InputNumber, Modal } from "antd";
+import { add } from "date-fns";
+import React, { useEffect, useState } from "react";
+import { CopyToClipboard } from "react-copy-to-clipboard";
+import { AllKeysTable } from "../all_keys_table";
+import { fetchAvailableModelsForTeamOrKey } from "../key_team_helpers/fetch_available_models_team_key";
+import useKeyList, { KeyResponse, Team } from "../key_team_helpers/key_list";
+import { keyDeleteCall, Organization, regenerateKeyCall } from "../networking";
import NotificationManager from "../molecules/notifications_manager";
@@ -125,6 +122,7 @@ const ViewKeyTable: React.FC = ({
selectedKeyAlias,
accessToken: accessToken || "",
createClicked,
+ expand: ["user"],
});
const handlePageChange = (newPage: number) => {