diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/EndpointUsage.test.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/EndpointUsage.test.tsx
new file mode 100644
index 00000000000..69987d1481f
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/EndpointUsage.test.tsx
@@ -0,0 +1,25 @@
+import { render, screen } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+import EndpointUsage from "./EndpointUsage";
+
+vi.mock("./components/EndpointUsageBarChart", () => ({
+ default: () =>
Endpoint Usage Bar Chart
,
+}));
+
+vi.mock("./components/EndpointUsageLineChart", () => ({
+ default: () => Endpoint Usage Line Chart
,
+}));
+
+vi.mock("./components/EndpointUsageTable", () => ({
+ default: () => Endpoint Usage Table
,
+}));
+
+describe("EndpointUsage", () => {
+ it("should render", () => {
+ render();
+
+ expect(screen.getByText("Endpoint Usage Table")).toBeInTheDocument();
+ expect(screen.getByText("Endpoint Usage Bar Chart")).toBeInTheDocument();
+ expect(screen.getByText("Endpoint Usage Line Chart")).toBeInTheDocument();
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/EndpointUsage.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/EndpointUsage.tsx
new file mode 100644
index 00000000000..68959405a8f
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/EndpointUsage.tsx
@@ -0,0 +1,67 @@
+import React, { useMemo } from "react";
+import EndpointUsageBarChart from "./components/EndpointUsageBarChart";
+import EndpointUsageLineChart from "./components/EndpointUsageLineChart";
+import EndpointUsageTable from "./components/EndpointUsageTable";
+import { DailyData, MetricWithMetadata } from "../../types";
+
+interface EndpointUsageProps {
+ userSpendData?: {
+ results: DailyData[];
+ metadata: any;
+ };
+}
+
+const EndpointUsage: React.FC = ({ userSpendData }) => {
+ // Aggregate endpoints data from all days
+ const endpointData = useMemo(() => {
+ const aggregatedEndpoints: Record = {};
+
+ if (!userSpendData?.results) {
+ return aggregatedEndpoints;
+ }
+
+ userSpendData.results.forEach((day) => {
+ Object.entries(day.breakdown.endpoints || {}).forEach(([endpoint, metrics]) => {
+ if (!aggregatedEndpoints[endpoint]) {
+ aggregatedEndpoints[endpoint] = {
+ metrics: {
+ spend: 0,
+ prompt_tokens: 0,
+ completion_tokens: 0,
+ total_tokens: 0,
+ api_requests: 0,
+ successful_requests: 0,
+ failed_requests: 0,
+ cache_read_input_tokens: 0,
+ cache_creation_input_tokens: 0,
+ },
+ metadata: metrics.metadata || {},
+ api_key_breakdown: {},
+ };
+ }
+ aggregatedEndpoints[endpoint].metrics.spend += metrics.metrics.spend;
+ aggregatedEndpoints[endpoint].metrics.prompt_tokens += metrics.metrics.prompt_tokens;
+ aggregatedEndpoints[endpoint].metrics.completion_tokens += metrics.metrics.completion_tokens;
+ aggregatedEndpoints[endpoint].metrics.total_tokens += metrics.metrics.total_tokens;
+ aggregatedEndpoints[endpoint].metrics.api_requests += metrics.metrics.api_requests;
+ aggregatedEndpoints[endpoint].metrics.successful_requests += metrics.metrics.successful_requests || 0;
+ aggregatedEndpoints[endpoint].metrics.failed_requests += metrics.metrics.failed_requests || 0;
+ aggregatedEndpoints[endpoint].metrics.cache_read_input_tokens += metrics.metrics.cache_read_input_tokens || 0;
+ aggregatedEndpoints[endpoint].metrics.cache_creation_input_tokens +=
+ metrics.metrics.cache_creation_input_tokens || 0;
+ });
+ });
+
+ return aggregatedEndpoints;
+ }, [userSpendData]);
+
+ return (
+
+
+
+
+
+ );
+};
+
+export default EndpointUsage;
diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageBarChart.test.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageBarChart.test.tsx
new file mode 100644
index 00000000000..96b66cbb3d2
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageBarChart.test.tsx
@@ -0,0 +1,41 @@
+import { render, screen } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+import EndpointUsageBarChart from "./EndpointUsageBarChart";
+
+vi.mock("@tremor/react", async () => {
+ const React = await import("react");
+
+ function Card({ children }: any) {
+ return React.createElement("div", { "data-testid": "tremor-card" }, children);
+ }
+ (Card as any).displayName = "Card";
+
+ function Title({ children }: any) {
+ return React.createElement("h2", { "data-testid": "tremor-title" }, children);
+ }
+ (Title as any).displayName = "Title";
+
+ function BarChart(_props: any) {
+ return React.createElement("div", { "data-testid": "tremor-bar-chart" }, "Bar Chart");
+ }
+ (BarChart as any).displayName = "BarChart";
+
+ return { Card, Title, BarChart };
+});
+
+vi.mock("@/components/common_components/chartUtils", () => ({
+ CustomLegend: ({ categories }: any) => (
+ {categories.join(", ")}
+ ),
+ CustomTooltip: () => Tooltip
,
+}));
+
+describe("EndpointUsageBarChart", () => {
+ it("should render", () => {
+ render();
+
+ expect(screen.getByTestId("tremor-card")).toBeInTheDocument();
+ expect(screen.getByText("Success vs Failed Requests by Endpoint")).toBeInTheDocument();
+ expect(screen.getByTestId("tremor-bar-chart")).toBeInTheDocument();
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageBarChart.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageBarChart.tsx
new file mode 100644
index 00000000000..dd712fdb232
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageBarChart.tsx
@@ -0,0 +1,53 @@
+import React from "react";
+import { BarChart, Card, Title } from "@tremor/react";
+import { CustomLegend, CustomTooltip } from "@/components/common_components/chartUtils";
+import { MetricWithMetadata } from "../../../types";
+
+interface EndpointUsageBarChartProps {
+ endpointData?: Record;
+}
+
+const EndpointUsageBarChart: React.FC = ({ endpointData }) => {
+ const dataToUse = endpointData || {};
+
+ // Transform endpoint data into chart format
+ const chartData = React.useMemo(() => {
+ return Object.entries(dataToUse).map(([endpoint, data]) => ({
+ endpoint,
+ "metrics.successful_requests": data.metrics.successful_requests,
+ "metrics.failed_requests": data.metrics.failed_requests,
+ metrics: {
+ successful_requests: data.metrics.successful_requests,
+ failed_requests: data.metrics.failed_requests,
+ },
+ }));
+ }, [dataToUse]);
+
+ const valueFormatter = (value: number) => value.toLocaleString();
+
+ return (
+
+
+
Success vs Failed Requests by Endpoint
+
+
+
+
+ );
+};
+
+export default EndpointUsageBarChart;
diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageLineChart.test.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageLineChart.test.tsx
new file mode 100644
index 00000000000..ec12779a697
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageLineChart.test.tsx
@@ -0,0 +1,60 @@
+import { render, screen } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+import EndpointUsageLineChart from "./EndpointUsageLineChart";
+
+vi.mock("@tremor/react", async () => {
+ const React = await import("react");
+
+ function Card({ children }: any) {
+ return React.createElement("div", { "data-testid": "tremor-card" }, children);
+ }
+ (Card as any).displayName = "Card";
+
+ function Title({ children }: any) {
+ return React.createElement("h2", { "data-testid": "tremor-title" }, children);
+ }
+ (Title as any).displayName = "Title";
+
+ function LineChart(_props: any) {
+ return React.createElement("div", { "data-testid": "tremor-line-chart" }, "Line Chart");
+ }
+ (LineChart as any).displayName = "LineChart";
+
+ return { Card, Title, LineChart };
+});
+
+vi.mock("antd", async () => {
+ const React = await import("react");
+
+ function Segmented({ options, value, onChange }: any) {
+ return React.createElement(
+ "div",
+ { "data-testid": "antd-segmented", "data-value": value },
+ options?.map((opt: any) =>
+ React.createElement(
+ "button",
+ {
+ key: opt.value,
+ onClick: () => onChange?.(opt.value),
+ "data-selected": value === opt.value,
+ },
+ opt.label,
+ ),
+ ),
+ );
+ }
+ (Segmented as any).displayName = "Segmented";
+
+ return { Segmented };
+});
+
+describe("EndpointUsageLineChart", () => {
+ it("should render", () => {
+ render();
+
+ expect(screen.getByTestId("tremor-card")).toBeInTheDocument();
+ expect(screen.getByText("Endpoint Usage Trends")).toBeInTheDocument();
+ expect(screen.getByTestId("antd-segmented")).toBeInTheDocument();
+ expect(screen.getByTestId("tremor-line-chart")).toBeInTheDocument();
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageLineChart.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageLineChart.tsx
new file mode 100644
index 00000000000..9c9236edaa2
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageLineChart.tsx
@@ -0,0 +1,116 @@
+import React, { useMemo, useState } from "react";
+import { Card, LineChart, Title } from "@tremor/react";
+import { Segmented } from "antd";
+import { DailyData } from "../../../types";
+
+interface EndpointUsageLineChartProps {
+ dailyData?: { results: DailyData[] };
+ endpointData?: Record;
+}
+
+type TimePeriod = "7" | "30" | "90";
+
+
+// Transform daily data into chart format
+function transformDailyDataToChart(dailyData: DailyData[], days: number): Array> {
+ const chartData: Array> = [];
+
+ // Get all unique endpoint names
+ const endpointNames = new Set();
+ dailyData.forEach((day) => {
+ if (day.breakdown.endpoints) {
+ Object.keys(day.breakdown.endpoints).forEach((name) => endpointNames.add(name));
+ }
+ });
+
+ // Filter to the last N days based on time period
+ const filteredData = dailyData.slice(-days);
+
+ filteredData.forEach((day) => {
+ const date = new Date(day.date);
+ const dateStr = date.toLocaleDateString("en-US", {
+ month: "short",
+ day: "numeric",
+ });
+
+ const dataPoint: Record = {
+ date: dateStr,
+ };
+
+ endpointNames.forEach((endpointName) => {
+ const endpoint = day.breakdown.endpoints?.[endpointName];
+ dataPoint[endpointName] = endpoint?.metrics.api_requests || 0;
+ });
+
+ chartData.push(dataPoint);
+ });
+
+ return chartData;
+}
+
+export function EndpointUsageLineChart({ dailyData, endpointData }: EndpointUsageLineChartProps) {
+ const [timePeriod, setTimePeriod] = useState("7");
+ const days = parseInt(timePeriod);
+
+ const chartData = useMemo(() => {
+ if (!dailyData?.results || dailyData.results.length === 0) {
+ return [];
+ }
+
+ return transformDailyDataToChart(dailyData.results, days);
+ }, [dailyData, days]);
+
+ // Get endpoint names from chart data
+ const categories = useMemo(() => {
+ if (chartData.length === 0) return [];
+ const keys = Object.keys(chartData[0]).filter((key) => key !== "date");
+ return keys;
+ }, [chartData]);
+
+ // Tremor color palette for multiple lines
+ const colors = ["blue", "cyan", "indigo", "violet", "purple", "fuchsia", "pink", "rose", "red", "orange"];
+
+ return (
+
+
+
Endpoint Usage Trends
+ setTimePeriod(value as TimePeriod)}
+ style={{
+ backgroundColor: "#f3f4f6",
+ }}
+ />
+
+ value.toLocaleString()}
+ showLegend={true}
+ showGridLines={true}
+ yAxisWidth={60}
+ connectNulls={true}
+ curveType="natural"
+ />
+
+ );
+}
+
+export default EndpointUsageLineChart;
diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.test.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.test.tsx
new file mode 100644
index 00000000000..793e4c6e3cf
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.test.tsx
@@ -0,0 +1,70 @@
+import { render, screen } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+import EndpointUsageTable from "./EndpointUsageTable";
+
+vi.mock("antd", async () => {
+ const React = await import("react");
+
+ function Table({ columns, dataSource }: any) {
+ return React.createElement(
+ "div",
+ { "data-testid": "antd-table" },
+ columns?.map((col: any) =>
+ React.createElement("div", { key: col.key, "data-testid": `column-${col.key}` }, col.title),
+ ),
+ dataSource?.map((row: any) =>
+ React.createElement(
+ "div",
+ { key: row.key, "data-testid": `row-${row.key}` },
+ React.createElement("div", null, row.endpoint),
+ ),
+ ),
+ );
+ }
+ (Table as any).displayName = "Table";
+
+ function Progress({ percent }: any) {
+ return React.createElement("div", { "data-testid": "antd-progress", "data-percent": percent });
+ }
+ (Progress as any).displayName = "Progress";
+
+ return { Table, Progress };
+});
+
+vi.mock("@/utils/dataUtils", () => ({
+ formatNumberWithCommas: (value: number, decimals?: number) => {
+ return value.toFixed(decimals || 0);
+ },
+}));
+
+describe("EndpointUsageTable", () => {
+ it("should render", () => {
+ const mockEndpointData = {
+ "endpoint-1": {
+ metrics: {
+ spend: 100.5,
+ prompt_tokens: 5000,
+ completion_tokens: 3000,
+ total_tokens: 8000,
+ api_requests: 100,
+ successful_requests: 95,
+ failed_requests: 5,
+ cache_read_input_tokens: 0,
+ cache_creation_input_tokens: 0,
+ },
+ metadata: {},
+ api_key_breakdown: {},
+ },
+ };
+
+ render();
+
+ expect(screen.getByTestId("antd-table")).toBeInTheDocument();
+ expect(screen.getByTestId("column-endpoint")).toBeInTheDocument();
+ expect(screen.getByTestId("column-requests")).toBeInTheDocument();
+ expect(screen.getByTestId("column-api_requests")).toBeInTheDocument();
+ expect(screen.getByTestId("column-successRate")).toBeInTheDocument();
+ expect(screen.getByTestId("column-total_tokens")).toBeInTheDocument();
+ expect(screen.getByTestId("column-spend")).toBeInTheDocument();
+ });
+});
diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.tsx
new file mode 100644
index 00000000000..f5cfb553370
--- /dev/null
+++ b/ui/litellm-dashboard/src/components/UsagePage/components/EndpointUsage/components/EndpointUsageTable.tsx
@@ -0,0 +1,122 @@
+import React from "react";
+import { Table, Progress } from "antd";
+import type { ColumnsType } from "antd/es/table";
+import { formatNumberWithCommas } from "@/utils/dataUtils";
+import { MetricWithMetadata } from "../../../types";
+
+interface EndpointUsageTableProps {
+ endpointData: Record;
+}
+
+interface EndpointRow {
+ key: string;
+ endpoint: string;
+ successful_requests: number;
+ failed_requests: number;
+ api_requests: number;
+ total_tokens: number;
+ spend: number;
+ successRate: number;
+}
+
+const EndpointUsageTable: React.FC = ({ endpointData }) => {
+ const calculateSuccessRate = (successful: number, total: number): number => {
+ if (total === 0) return 0;
+ return (successful / total) * 100;
+ };
+
+ const dataSource: EndpointRow[] = Object.entries(endpointData).map(([endpoint, data]) => ({
+ key: endpoint,
+ endpoint,
+ successful_requests: data.metrics.successful_requests,
+ failed_requests: data.metrics.failed_requests,
+ api_requests: data.metrics.api_requests,
+ total_tokens: data.metrics.total_tokens,
+ spend: data.metrics.spend,
+ successRate: calculateSuccessRate(data.metrics.successful_requests, data.metrics.api_requests),
+ }));
+
+ const columns: ColumnsType = [
+ {
+ title: "Endpoint",
+ dataIndex: "endpoint",
+ key: "endpoint",
+ render: (text: string) => {text},
+ },
+ {
+ title: "Successful / Failed",
+ key: "requests",
+ render: (_: any, record: EndpointRow) => {
+ const successPercentage =
+ record.api_requests > 0 ? (record.successful_requests / record.api_requests) * 100 : 0;
+ const failurePercentage = record.api_requests > 0 ? (record.failed_requests / record.api_requests) * 100 : 0;
+ const totalPercentage = successPercentage + failurePercentage;
+
+ const strokeColorConfig: Record = {
+ "0%": "#22c55e",
+ };
+ if (successPercentage > 0 && successPercentage < 100) {
+ strokeColorConfig[`${successPercentage}%`] = "#22c55e";
+ strokeColorConfig[`${successPercentage + 0.01}%`] = "#ef4444";
+ }
+ strokeColorConfig["100%"] = failurePercentage > 0 ? "#ef4444" : "#22c55e";
+
+ return (
+
+
+
+ {record.successful_requests.toLocaleString()}
+ /
+ {record.failed_requests.toLocaleString()}
+
+
+ );
+ },
+ },
+ {
+ title: "Total Request",
+ dataIndex: "api_requests",
+ key: "api_requests",
+ render: (value: number) => value.toLocaleString(),
+ },
+ {
+ title: "Success Rate",
+ dataIndex: "successRate",
+ key: "successRate",
+ render: (value: number) => {
+ const successRateStr = value.toFixed(2);
+ return (
+ = 95
+ ? "text-green-600 font-medium"
+ : value >= 80
+ ? "text-yellow-600 font-medium"
+ : "text-red-600 font-medium"
+ }
+ >
+ {successRateStr}%
+
+ );
+ },
+ },
+ {
+ title: "Total Tokens",
+ dataIndex: "total_tokens",
+ key: "total_tokens",
+ render: (value: number) => value.toLocaleString(),
+ },
+ {
+ title: "Spend",
+ dataIndex: "spend",
+ key: "spend",
+ render: (value: number) => `$${formatNumberWithCommas(value, 2)}`,
+ },
+ ];
+
+ return ;
+};
+
+export default EndpointUsageTable;
diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.tsx
index 75cf8c9292c..52e75005ccc 100644
--- a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.tsx
+++ b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/EntityUsage.tsx
@@ -38,6 +38,8 @@ import { valueFormatterSpend } from "../../utils/value_formatters";
import TopKeyView from "./TopKeyView";
import TopModelView from "./TopModelView";
import useTeams from "@/app/(dashboard)/hooks/useTeams";
+import EndpointUsage from "../EndpointUsage/EndpointUsage";
+import NewBadge from "../../../common_components/NewBadge";
interface EntityMetrics {
metrics: {
@@ -399,11 +401,14 @@ const EntityUsage: React.FC = ({
filterOptions={getAllTags() || undefined}
/>
-
- Cost
- {entityType === "agent" ? "Request / Token Consumption" : "Model Activity"}
- Key Activity
-
+
+
+ Cost
+ {entityType === "agent" ? "Request / Token Consumption" : "Model Activity"}
+ Key Activity
+ Endpoint Activity
+
+
@@ -682,6 +687,9 @@ const EntityUsage: React.FC = ({
+
+
+
diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/UsagePageView.test.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/UsagePageView.test.tsx
index 09415e87443..69a1bb4e305 100644
--- a/ui/litellm-dashboard/src/components/UsagePage/components/UsagePageView.test.tsx
+++ b/ui/litellm-dashboard/src/components/UsagePage/components/UsagePageView.test.tsx
@@ -70,8 +70,9 @@ vi.mock("@/app/(dashboard)/hooks/useAuthorized", () => ({
default: vi.fn(),
}));
-vi.mock("antd", async () => {
+vi.mock("antd", async (importOriginal) => {
const React = await import("react");
+ const actual = await importOriginal();
function Select(props: any) {
const { value, onChange, options, ...rest } = props;
@@ -111,7 +112,34 @@ vi.mock("antd", async () => {
}
(Badge as any).displayName = "AntdBadge";
- return { Select, Alert, Badge };
+ function Table({ columns, dataSource, ...rest }: any) {
+ return React.createElement(
+ "div",
+ { ...rest, "data-testid": "antd-table" },
+ columns?.map((col: any) =>
+ React.createElement("div", { key: col.key, "data-testid": `column-${col.key}` }, col.title),
+ ),
+ dataSource?.map((row: any) =>
+ React.createElement(
+ "div",
+ { key: row.key, "data-testid": `row-${row.key}` },
+ columns?.map((col: any) => {
+ const value = col.render ? col.render(row[col.dataIndex], row) : row[col.dataIndex];
+ return React.createElement("div", { key: col.key }, value);
+ }),
+ ),
+ ),
+ );
+ }
+ (Table as any).displayName = "Table";
+
+ return {
+ ...actual,
+ Select,
+ Alert,
+ Badge,
+ Table,
+ };
});
vi.mock("@ant-design/icons", async () => {
@@ -341,9 +369,11 @@ describe("NewUsage", () => {
});
// Check that key metrics are displayed
- expect(screen.getByText("Total Requests")).toBeInTheDocument();
+ const totalRequestElements = screen.getAllByText("Total Requests");
+ expect(totalRequestElements.length).toBeGreaterThan(0);
expect(screen.getByText("1,500")).toBeInTheDocument();
- expect(screen.getByText("Successful Requests")).toBeInTheDocument();
+ const successfulRequestLabelElements = screen.getAllByText("Successful Requests");
+ expect(successfulRequestLabelElements.length).toBeGreaterThan(0);
// Use getAllByText since this value appears in multiple places (metrics card + table)
const successfulRequestElements = screen.getAllByText("1,450");
expect(successfulRequestElements.length).toBeGreaterThan(0);
@@ -357,10 +387,14 @@ describe("NewUsage", () => {
});
// Check for usage metrics cards
- expect(screen.getByText("Total Requests")).toBeInTheDocument();
- expect(screen.getByText("Successful Requests")).toBeInTheDocument();
- expect(screen.getByText("Failed Requests")).toBeInTheDocument();
- expect(screen.getByText("Total Tokens")).toBeInTheDocument();
+ const totalRequestElements = screen.getAllByText("Total Requests");
+ expect(totalRequestElements.length).toBeGreaterThan(0);
+ const successfulRequestElements = screen.getAllByText("Successful Requests");
+ expect(successfulRequestElements.length).toBeGreaterThan(0);
+ const failedRequestElements = screen.getAllByText("Failed Requests");
+ expect(failedRequestElements.length).toBeGreaterThan(0);
+ const totalTokensElements = screen.getAllByText("Total Tokens");
+ expect(totalTokensElements.length).toBeGreaterThan(0);
// Check for chart titles
expect(screen.getByText("Daily Spend")).toBeInTheDocument();
diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/UsagePageView.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/UsagePageView.tsx
index a9c86c064ef..d891fcf604c 100644
--- a/ui/litellm-dashboard/src/components/UsagePage/components/UsagePageView.tsx
+++ b/ui/litellm-dashboard/src/components/UsagePage/components/UsagePageView.tsx
@@ -53,6 +53,8 @@ import EntityUsage, { EntityList } from "./EntityUsage/EntityUsage";
import TopKeyView from "./EntityUsage/TopKeyView";
import { UsageOption, UsageViewSelect } from "./UsageViewSelect/UsageViewSelect";
import { useCurrentUser } from "@/app/(dashboard)/hooks/users/useCurrentUser";
+import EndpointUsage from "./EndpointUsage/EndpointUsage";
+import NewBadge from "../../common_components/NewBadge";
interface UsagePageProps {
teams: Team[];
@@ -434,12 +436,15 @@ const UsagePage: React.FC = ({ teams, organizations }) => {
{usageView === "global" && (
-
- Cost
- Model Activity
- Key Activity
- MCP Server Activity
-
+
+
+ Cost
+ Model Activity
+ Key Activity
+ MCP Server Activity
+ Endpoint Activity
+
+