diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json index 2f4def9ffa2..c94686b2f1e 100644 --- a/ui/litellm-dashboard/eslint-suppressions.json +++ b/ui/litellm-dashboard/eslint-suppressions.json @@ -1658,25 +1658,12 @@ "src/app/(dashboard)/usage/_components/components/EndpointUsage/components/EndpointUsageTable.tsx": { "no-nested-ternary": { "count": 1 - }, - "no-restricted-imports": { - "count": 1 } }, "src/app/(dashboard)/usage/_components/components/EntityUsage/EntityUsage.tsx": { "local/no-complex-jsx-arrow": { "count": 2 }, - "no-restricted-imports": { - "count": 2 - } - }, - "src/app/(dashboard)/usage/_components/components/EntityUsage/SpendByProvider.tsx": { - "no-restricted-imports": { - "count": 2 - } - }, - "src/app/(dashboard)/usage/_components/components/EntityUsage/TopModelView.tsx": { "no-restricted-imports": { "count": 1 } @@ -1685,9 +1672,6 @@ "no-nested-ternary": { "count": 1 }, - "no-restricted-imports": { - "count": 1 - }, "react-hooks/immutability": { "count": 1 } @@ -1703,7 +1687,7 @@ "count": 1 }, "no-restricted-imports": { - "count": 2 + "count": 1 }, "react-hooks/purity": { "count": 1 @@ -1712,14 +1696,6 @@ "count": 3 } }, - "src/app/(dashboard)/usage/_components/components/UsageViewSelect/UsageViewSelect.tsx": { - "local/no-complex-jsx-arrow": { - "count": 2 - }, - "no-restricted-imports": { - "count": 1 - } - }, "src/app/(dashboard)/usage/_components/hooks/usePaginatedDailyActivity.ts": { "react-hooks/refs": { "count": 1 @@ -1982,29 +1958,14 @@ "count": 1 } }, - "src/components/EntityUsageExport/EntityUsageExportModal.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, - "src/components/EntityUsageExport/ExportFormatSelector.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/components/EntityUsageExport/ExportSummary.tsx": { "no-restricted-imports": { "count": 1 } }, - "src/components/EntityUsageExport/ExportTypeSelector.tsx": { - "no-restricted-imports": { - "count": 1 - } - }, "src/components/EntityUsageExport/UsageExportHeader.tsx": { "no-restricted-imports": { - "count": 3 + "count": 1 } }, "src/components/EntityUsageExport/types.ts": { @@ -2289,9 +2250,6 @@ }, "no-nested-ternary": { "count": 1 - }, - "no-restricted-imports": { - "count": 2 } }, "src/components/add_model/AdaptiveRoutingConfig.tsx": { @@ -2794,9 +2752,6 @@ "src/components/common_components/team_multi_select.tsx": { "local/filename-pascal-case": { "count": 1 - }, - "no-restricted-imports": { - "count": 1 } }, "src/components/common_components/user_search_modal.tsx": { @@ -3171,9 +3126,6 @@ "local/filename-pascal-case": { "count": 1 }, - "no-restricted-imports": { - "count": 1 - }, "react-hooks/set-state-in-effect": { "count": 1 } @@ -3714,7 +3666,7 @@ "count": 1 }, "no-restricted-imports": { - "count": 3 + "count": 1 }, "react-hooks/set-state-in-effect": { "count": 1 diff --git a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EndpointUsage/components/EndpointUsageTable.tsx b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EndpointUsage/components/EndpointUsageTable.tsx index 7b6bd57e758..5f72caae437 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EndpointUsage/components/EndpointUsageTable.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EndpointUsage/components/EndpointUsageTable.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { Progress } from "antd"; import type { ColumnDef } from "@tanstack/react-table"; +import { Meter, MeterIndicator, MeterTrack } from "@/components/ui/meter"; import { DataTable } from "@/components/shared/DataTable"; import { MoneyCell } from "@/components/shared/table_cells"; import { MetricWithMetadata } from "@/components/UsagePage/types"; @@ -53,19 +53,15 @@ const EndpointUsageTable: React.FC = ({ endpointData }) 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 (
- + {/* The failed share is the track showing through behind the successful share. */} + + 0 ? "bg-red-500" : undefined}> + + +
{record.successful_requests.toLocaleString()} 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 3a22eb7fd10..2d6dbb823cc 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 @@ -14,23 +14,13 @@ import { MoneyCell } from "@/components/shared/table_cells"; import { Card as ShadcnCard, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { hasCapability, type Capability } from "@/utils/capabilities"; import { formatNumberWithCommas } from "@/utils/dataUtils"; -import { - Card, - Col, - DateRangePickerValue, - Grid, - Subtitle, - Tab, - TabGroup, - TabList, - TabPanel, - TabPanels, - Text, - Title, -} from "@tremor/react"; -import { DownOutlined, ExportOutlined, InfoCircleOutlined, LoadingOutlined, RightOutlined } from "@ant-design/icons"; +import type { DateRangePickerValue } from "@tremor/react"; +import { ChevronDown, ChevronRight, ExternalLink, Info, Loader2 } from "lucide-react"; import type { ColumnDef } from "@tanstack/react-table"; -import { Alert, Button, Tooltip } from "antd"; +import { Alert, AlertDescription } from "@/components/shared/Alert"; +import { Button } from "@/components/ui/button"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import React, { type ReactNode, useMemo, useState } from "react"; import TeamMultiSelect from "@/components/common_components/team_multi_select"; import { ActivityMetrics, processActivityData } from "@/components/activity_metrics"; @@ -341,23 +331,29 @@ const EntityUsage: React.FC = ({ [], ); - const chev = "text-gray-400 text-xs"; - const expandIcon = showCostBreakdown ? : ; - const infoIcon = ; + const chev = "size-3 text-gray-400"; + const expandIcon = showCostBreakdown ? : ; const renderSummaryTile = ({ title, value, className, tooltip, expandable }: SummaryTile) => ( - setShowCostBreakdown(!showCostBreakdown) : undefined} > -
- {title} - {tooltip ? {infoIcon} : null} - {expandable ? expandIcon : null} -
- {value} -
+ +
+

{title}

+ {tooltip ? ( + + } /> + {tooltip} + + ) : null} + {expandable ? expandIcon : null} +
+

{value}

+
+ ); const breakdownTiles = showFlatCost && showCostBreakdown ? buildCostBreakdownTiles(spendData.metadata) : []; @@ -366,18 +362,18 @@ const EntityUsage: React.FC = ({ const modelViewTitle = modelViewType === "groups" ? "Top Public Model Names" : "Top Litellm Models"; const costPanel = ( - - - - {capitalizedEntityLabel} Spend Overview - - {summaryTiles.map(renderSummaryTile)} - - - +
+
+ + +

{capitalizedEntityLabel} Spend Overview

+
{summaryTiles.map(renderSummaryTile)}
+
+
+
{/* Daily Spend Chart */} - +
Daily Spend @@ -451,15 +447,15 @@ const EntityUsage: React.FC = ({ /> - +
{/* Entity Breakdown Section */} - - -
+
+ +
- Spend Per {capitalizedEntityLabel} - Showing Top 5 by Spend +

Spend Per {capitalizedEntityLabel}

+

Showing Top 5 by Spend

Get Started by Tracking cost per {capitalizedEntityLabel} = ({
- - +
+
= ({ ); }} /> - - +
+
entity.metrics.spend > 0)} @@ -509,61 +505,69 @@ const EntityUsage: React.FC = ({ noDataMessage={`No ${entityType} spend data`} size="compact" /> - - -
- - +
+
+ + +
{/* Top API Keys */} - - - Top Virtual Keys - - - +
+ + +

Top Virtual Keys

+ +
+
+
{/* Top Models */} - - -
- {entityType === "agent" ? "Top Agents" : modelViewTitle} - -
- -
- +
+ + +
+

+ {entityType === "agent" ? "Top Agents" : modelViewTitle} +

+ +
+ +
+
+
{showAgentBreakdown && ( - - - Top Agents Driving Spend - - - +
+ + +

Top Agents Driving Spend

+ +
+
+
)} {/* Spend by Provider */} - - -
- Provider Usage - - +
+ + +

Provider Usage

+
+
= ({ startAngle={90} endAngle={-270} /> - - +
+
= ({ noDataMessage="No provider usage data" size="compact" /> - - -
- - - +
+
+ + +
+
); const tabs: readonly { key: string; label: string; content: ReactNode }[] = [ @@ -620,80 +624,60 @@ const EntityUsage: React.FC = ({ return (
{isFetchingMore && ( - - - - Currently fetching spend data: fetched {progress.currentPage} / {progress.totalPages} pages. Charts will - update periodically as data loads. Moving off of this page will stop and reset this. To continue using - the UI in the meantime,{" "} - - open a new tab - - . - - -
- } - /> + + + + + Currently fetching spend data: fetched {progress.currentPage} / {progress.totalPages} pages. Charts will + update periodically as data loads. Moving off of this page will stop and reset this. To continue using the + UI in the meantime,{" "} + + open a new tab + + . + + + + )} {cancelled && ( - - Showing partial data ({progress.currentPage}/{progress.totalPages} pages loaded) - - } - /> + + + Showing partial data ({progress.currentPage}/{progress.totalPages} pages loaded) + + )} {agentIsFetchingMore && showAgentBreakdown && ( - - - - Currently fetching agent data: fetched {agentProgress.currentPage} / {agentProgress.totalPages} pages. - Charts will update periodically as data loads. Moving off of this page will stop and reset this. To - continue using the UI in the meantime,{" "} - - open a new tab - - . - - -
- } - /> + + + + + Currently fetching agent data: fetched {agentProgress.currentPage} / {agentProgress.totalPages} pages. + Charts will update periodically as data loads. Moving off of this page will stop and reset this. To + continue using the UI in the meantime,{" "} + + open a new tab + + . + + + + )} {agentCancelled && showAgentBreakdown && ( - - Showing partial agent data ({agentProgress.currentPage}/{agentProgress.totalPages} pages loaded) - - } - /> + + + Showing partial agent data ({agentProgress.currentPage}/{agentProgress.totalPages} pages loaded) + + )} {entityType === "team" && (
- Filter by team +

Filter by team

)} @@ -710,18 +694,20 @@ const EntityUsage: React.FC = ({ filterMode={entityType === "user" ? "single" : "multiple"} teams={teams || []} /> - - + + {tabs.map(({ key, label }) => ( - {label} + + {label} + ))} - - - {tabs.map(({ key, content }) => ( - {content} - ))} - - + + {tabs.map(({ key, content }) => ( + + {content} + + ))} +
); }; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/SpendByProvider.tsx b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/SpendByProvider.tsx index ac8ae0e67e1..38f055f65f3 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/SpendByProvider.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/SpendByProvider.tsx @@ -2,10 +2,11 @@ import { DonutChart } from "@/components/shared/charts"; import { DataTable } from "@/components/shared/DataTable"; import { MoneyCell } from "@/components/shared/table_cells"; import { formatNumberWithCommas } from "@/utils/dataUtils"; -import { InfoCircleOutlined } from "@ant-design/icons"; +import { Info } from "lucide-react"; import type { ColumnDef } from "@tanstack/react-table"; -import { Card, Col, Grid, Switch, Title } from "@tremor/react"; -import { Tooltip } from "antd"; +import { Card, CardAction, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Switch } from "@/components/ui/switch"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import React, { useState } from "react"; import { ProviderLogo } from "@/components/molecules/models/ProviderLogo"; import { ChartLoader } from "@/components/shared/chart_loader"; @@ -85,29 +86,30 @@ const SpendByProvider: React.FC = ({ loading, isDateChangi return ( -
- Spend by Provider -
+ + Spend by Provider +
- +
- - + + } /> + Requests that failed to route to a provider
- +
-
-
- {loading ? ( - - ) : ( - - + + + + {loading ? ( + + ) : ( +
= ({ loading, isDateChangi startAngle={90} endAngle={-270} /> - - = ({ loading, isDateChangi noDataMessage="No provider usage data" size="compact" /> - - - )} +
+ )} +
); }; diff --git a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/TopModelView.tsx b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/TopModelView.tsx index 2e2880e8763..2f5b750c118 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/TopModelView.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/EntityUsage/TopModelView.tsx @@ -1,7 +1,7 @@ import { BarChart } from "@/components/shared/charts"; import { DataTable } from "@/components/shared/DataTable"; import { MoneyCell } from "@/components/shared/table_cells"; -import { Segmented } from "antd"; +import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { useState } from "react"; import { formatNumberWithCommas } from "@/utils/dataUtils"; @@ -19,6 +19,8 @@ interface TopModelViewProps { setTopModelsLimit: (limit: number) => void; } +export const TOP_MODEL_LIMITS = [5, 10, 25, 50]; + export default function TopModelView({ topModels, topModelsLimit, setTopModelsLimit }: TopModelViewProps) { const [modelViewMode, setModelViewMode] = useState<"chart" | "table">("table"); @@ -58,30 +60,25 @@ export default function TopModelView({ topModels, topModelsLimit, setTopModelsLi return ( <>
- setTopModelsLimit(value as number)} - /> -
- - -
+ setTopModelsLimit(Number(value))}> + + {TOP_MODEL_LIMITS.map((limit) => ( + + {limit} + + ))} + + + setModelViewMode(value as "chart" | "table")}> + + + Table View + + + Chart View + + +
{modelViewMode === "chart" ? (
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/UsageAIChatPanel.tsx b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/UsageAIChatPanel.tsx index 9d6b09353a2..21314594cb4 100644 --- a/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/UsageAIChatPanel.tsx +++ b/ui/litellm-dashboard/src/app/(dashboard)/usage/_components/components/UsageAIChatPanel.tsx @@ -1,10 +1,18 @@ import React, { useEffect, useRef, useState } from "react"; -import { Button, Select, Input, Spin } from "antd"; import ReactMarkdown from "react-markdown"; +import { Button } from "@/components/ui/button"; +import { + Combobox, + ComboboxContent, + ComboboxEmpty, + ComboboxInput, + ComboboxItem, + ComboboxList, +} from "@/components/ui/combobox"; +import { Textarea } from "@/components/ui/textarea"; +import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner"; import { modelHubCall, usageAiChatStream, UsageAiToolCallEvent } from "@/components/networking"; -const { TextArea } = Input; - interface ToolCallStep { tool_name: string; tool_label: string; @@ -41,7 +49,7 @@ const ToolCallDisplay: React.FC<{ step: ToolCallStep }> = ({ step }) => {
{step.status === "running" ? ( - + ) : step.status === "error" ? ( ✗ ) : ( @@ -259,18 +267,29 @@ const UsageAIChatPanel: React.FC = ({ open, onClose, acce {/* Model selector */}
-