diff --git a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.test.tsx b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.test.tsx
index 2a6c2ede478..5846a63bc70 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.test.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.test.tsx
@@ -569,6 +569,23 @@ describe("EntityUsage", () => {
expect(screen.getAllByText("Activity Metrics")[1]).toBeInTheDocument();
});
+ it("tells the team view how many keys the proxy left out of the per-key lists", async () => {
+ mockTeamDailyActivityAggregatedCall.mockResolvedValue({
+ ...mockSpendData,
+ metadata: { ...mockSpendData.metadata, api_key_limit: 100, total_api_keys: 3000 },
+ });
+ render();
+
+ await waitFor(() => {
+ expect(mockTeamDailyActivityAggregatedCall).toHaveBeenCalled();
+ });
+ act(() => {
+ fireEvent.click(screen.getByText("Key Activity"));
+ });
+
+ expect(await screen.findByRole("note")).toHaveTextContent("Only the 100 highest-spend keys of 3,000 are loaded");
+ });
+
// An inactive tab panel is marked aria-selected="false" by one tab library and hidden by the
// other, so treat either as "not on screen" and the assertion holds whichever one is rendering.
const isShowing = (element: HTMLElement): boolean => {
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.tsx b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.tsx
index 001fee4b7bc..27460b21108 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.tsx
@@ -25,7 +25,7 @@ import TeamMultiSelect from "@/components/common_components/team_multi_select";
import UserDropdown from "@/components/common_components/UserDropdown";
import { ActivityMetrics, processActivityData } from "@/components/activity_metrics";
import { UsageExportHeader } from "@/components/EntityUsageExport";
-import { getExportBlockedReason } from "@/components/EntityUsageExport/exportBlockedReason";
+import { getApiKeyTruncation, getExportBlockedReason } from "@/components/EntityUsageExport/exportBlockedReason";
import type { EntityType } from "@/components/EntityUsageExport/types";
import {
agentDailyActivityCall,
@@ -71,6 +71,8 @@ interface EntitySpendData {
total_successful_requests: number;
total_failed_requests: number;
total_tokens: number;
+ api_key_limit?: number | null;
+ total_api_keys?: number | null;
};
}
@@ -160,6 +162,7 @@ const EntityUsage: React.FC = ({
});
const spendData = spendDataRaw as unknown as EntitySpendData;
+ const apiKeyTruncation = getApiKeyTruncation(spendData.metadata?.api_key_limit, spendData.metadata?.total_api_keys);
const {
data: agentSpendDataRaw,
@@ -659,12 +662,18 @@ const EntityUsage: React.FC = ({
{
key: "keys",
label: "Key Activity",
- content: ,
+ content: (
+
+ ),
},
{ key: "endpoints", label: "Endpoint Activity", content: },
];
- const spendFetchState = { coversRange, cancelled, failed, apiKeyTruncation: undefined };
+ const spendFetchState = { coversRange, cancelled, failed, apiKeyTruncation };
return (