diff --git a/ui/litellm-dashboard/.eslintrc.json b/ui/litellm-dashboard/.eslintrc.json index 7a9954200cc..b901c5faf6b 100644 --- a/ui/litellm-dashboard/.eslintrc.json +++ b/ui/litellm-dashboard/.eslintrc.json @@ -63,6 +63,8 @@ "src/components/prompts/prompt_editor_view/conversation_panel/MessageBubble.tsx", "src/components/playground/chat_ui/SessionManagement.tsx", "src/components/playground/chat_ui/SearchResultsDisplay.tsx", + "src/components/UsagePage/components/EntityUsage/SpendByProvider.tsx", + "src/components/view_logs/ToolsSection/FormattedToolView.tsx", "src/components/Settings/LoggingAndAlerts/LoggingCallbacks/LoggingCallbacksTable.tsx", "src/components/policies/impact_preview_alert.tsx", "src/components/policies/impact_popover.tsx", diff --git a/ui/litellm-dashboard/src/components/Projects/ProjectModals/EditProjectModal.tsx b/ui/litellm-dashboard/src/components/Projects/ProjectModals/EditProjectModal.tsx index 6c65e518cfd..abb080c7a32 100644 --- a/ui/litellm-dashboard/src/components/Projects/ProjectModals/EditProjectModal.tsx +++ b/ui/litellm-dashboard/src/components/Projects/ProjectModals/EditProjectModal.tsx @@ -1,6 +1,14 @@ import { useEffect } from "react"; -import { Modal, Form, Button, Typography } from "antd"; -import { SaveOutlined } from "@ant-design/icons"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Form } from "antd"; +import { Save } from "lucide-react"; import MessageManager from "@/components/molecules/message_manager"; import { ProjectResponse } from "@/app/(dashboard)/hooks/projects/useProjects"; import { @@ -26,16 +34,20 @@ export function EditProjectModal({ const [form] = Form.useForm(); const updateMutation = useUpdateProject(); - // Populate form with existing project data when modal opens useEffect(() => { if (isOpen && project) { - // Model limits are stored inside metadata by the backend const metadataObj = (project.metadata ?? {}) as Record; - const rpmLimits = (metadataObj.model_rpm_limit ?? {}) as Record; - const tpmLimits = (metadataObj.model_tpm_limit ?? {}) as Record; - const guardrails = (Array.isArray(metadataObj.guardrails) - ? metadataObj.guardrails - : []) as string[]; + const rpmLimits = (metadataObj.model_rpm_limit ?? {}) as Record< + string, + number + >; + const tpmLimits = (metadataObj.model_tpm_limit ?? {}) as Record< + string, + number + >; + const guardrails = ( + Array.isArray(metadataObj.guardrails) ? metadataObj.guardrails : [] + ) as string[]; const modelLimits: ProjectFormValues["modelLimits"] = []; const allLimitModels = new Set([ @@ -50,8 +62,11 @@ export function EditProjectModal({ }); } - // Filter out internal keys from user-facing metadata - const internalKeys = new Set(["model_rpm_limit", "model_tpm_limit", "guardrails"]); + const internalKeys = new Set([ + "model_rpm_limit", + "model_tpm_limit", + "guardrails", + ]); const metadata: ProjectFormValues["metadata"] = []; for (const [key, value] of Object.entries(metadataObj)) { if (!internalKeys.has(key)) { @@ -100,32 +115,30 @@ export function EditProjectModal({ }; return ( - - Edit Project - - } + - Cancel - , - , - ]} + onOpenChange={(o) => (!o ? onClose() : undefined)} > - - + + + + Edit Project + + + + + + + + + ); } diff --git a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/SpendByProvider.tsx b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/SpendByProvider.tsx index 4bbcc48dc4a..58cf0824268 100644 --- a/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/SpendByProvider.tsx +++ b/ui/litellm-dashboard/src/components/UsagePage/components/EntityUsage/SpendByProvider.tsx @@ -1,20 +1,23 @@ import { formatNumberWithCommas } from "@/utils/dataUtils"; -import { InfoCircleOutlined } from "@ant-design/icons"; +import { Card } from "@/components/ui/card"; +import { Switch } from "@/components/ui/switch"; import { - Card, - Col, - DonutChart, - Grid, - Switch, Table, TableBody, TableCell, TableHead, - TableHeaderCell, + TableHeader, TableRow, - Title, -} from "@tremor/react"; -import { Tooltip } from "antd"; +} from "@/components/ui/table"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +// eslint-disable-next-line litellm-ui/no-banned-ui-imports +import { DonutChart } from "@tremor/react"; +import { Info } from "lucide-react"; import React, { useState } from "react"; import { ProviderLogo } from "../../../molecules/models/ProviderLogo"; import { ChartLoader } from "../../../shared/chart_loader"; @@ -34,20 +37,21 @@ interface SpendByProviderProps { providerSpend: ProviderSpendData[]; } -const SpendByProvider: React.FC = ({ loading, isDateChanging, providerSpend }) => { +const SpendByProvider: React.FC = ({ + loading, + isDateChanging, + providerSpend, +}) => { const [includeZeroSpend, setIncludeZeroSpend] = useState(false); const [includeUnknown, setIncludeUnknown] = useState(false); const filteredProviderSpend = providerSpend.filter((provider) => { const isUnknown = provider.provider?.toLowerCase() === "unknown"; - // If includeUnknown is true, always include unknown provider if (isUnknown) { return includeUnknown; } - // If includeZeroSpend is true, include all providers (including those with 0 spend) - // Otherwise, only include providers with spend > 0 if (includeZeroSpend) { return true; } @@ -56,73 +60,101 @@ const SpendByProvider: React.FC = ({ loading, isDateChangi }); return ( - +
- Spend by Provider +

Spend by Provider

- - + +
- - - - + + + + + + + + Requests that failed to route to a provider + + +
- +
{loading ? ( ) : ( - - +
+
`$${formatNumberWithCommas(value, 2)}`} + valueFormatter={(value) => + `$${formatNumberWithCommas(value, 2)}` + } colors={["cyan"]} /> - - - - - - Provider - Spend - Successful - Failed - Tokens - - - - {filteredProviderSpend.map((provider) => ( - - -
- {provider.provider && } - {provider.provider} -
-
- ${formatNumberWithCommas(provider.spend, 2)} - - {provider.successful_requests.toLocaleString()} - - - {provider.failed_requests.toLocaleString()} - - {provider.tokens.toLocaleString()} + +
+
+
+ + + Provider + Spend + + Successful + + Failed + Tokens - ))} - -
- - + + + {filteredProviderSpend.map((provider) => ( + + +
+ {provider.provider && ( + + )} + {provider.provider} +
+
+ + ${formatNumberWithCommas(provider.spend, 2)} + + + {provider.successful_requests.toLocaleString()} + + + {provider.failed_requests.toLocaleString()} + + + {provider.tokens.toLocaleString()} + +
+ ))} +
+ +
+
+ )}
); diff --git a/ui/litellm-dashboard/src/components/view_logs/ToolsSection/FormattedToolView.tsx b/ui/litellm-dashboard/src/components/view_logs/ToolsSection/FormattedToolView.tsx index c159232f71d..dbb1e5a05f4 100644 --- a/ui/litellm-dashboard/src/components/view_logs/ToolsSection/FormattedToolView.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/ToolsSection/FormattedToolView.tsx @@ -1,20 +1,21 @@ -/** - * Formatted view of tool definition with parameters table and call data - */ - -import { Typography, Table } from "antd"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; import { ParsedTool, ParameterRow } from "./types"; -const { Text } = Typography; - interface FormattedToolViewProps { tool: ParsedTool; } export function FormattedToolView({ tool }: FormattedToolViewProps) { - // Parse parameters for table display const parameterRows: ParameterRow[] = Object.entries( - tool.parameters?.properties || {} + tool.parameters?.properties || {}, + // eslint-disable-next-line @typescript-eslint/no-explicit-any ).map(([name, schema]: [string, any]) => ({ key: name, name: name, @@ -23,104 +24,64 @@ export function FormattedToolView({ tool }: FormattedToolViewProps) { required: tool.parameters?.required?.includes(name) || false, })); - const columns = [ - { - title: "Parameter", - dataIndex: "name", - key: "name", - render: (name: string, record: ParameterRow) => ( - - {name} - {record.required && *} - - ), - }, - { - title: "Type", - dataIndex: "type", - key: "type", - render: (type: string) => ( - - {type} - - ), - }, - { - title: "Description", - dataIndex: "description", - key: "description", - render: (desc: string) => {desc}, - }, - ]; - return (
- {/* Description */} {tool.description && ( -
- +
+ {tool.description} - +
)} - {/* Parameters Table */} {parameterRows.length > 0 && (
- + Parameters - - + +
+
+ + + Parameter + Type + Description + + + + {parameterRows.map((row) => ( + + + + {row.name} + {row.required && ( + * + )} + + + + + {row.type} + + + + {row.description} + + + ))} + +
+
)} - {/* If tool was called, show the arguments used */} {tool.called && tool.callData && ( -
- +
+ Called With - -
-
+          
+          
+
               {JSON.stringify(tool.callData.arguments, null, 2)}