diff --git a/ui/litellm-dashboard/src/components/AccessGroups/AccessGroupsModal/AccessGroupCreateModal.tsx b/ui/litellm-dashboard/src/components/AccessGroups/AccessGroupsModal/AccessGroupCreateModal.tsx index b51fbd5bd9..5ec9f04f3b 100644 --- a/ui/litellm-dashboard/src/components/AccessGroups/AccessGroupsModal/AccessGroupCreateModal.tsx +++ b/ui/litellm-dashboard/src/components/AccessGroups/AccessGroupsModal/AccessGroupCreateModal.tsx @@ -1,5 +1,6 @@ import React from "react"; -import { Modal, Form, message } from "antd"; +import { Modal, Form } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { AccessGroupBaseForm, AccessGroupFormValues, @@ -37,7 +38,7 @@ export function AccessGroupCreateModal({ createMutation.mutate(params, { onSuccess: () => { - message.success("Access group created successfully"); + MessageManager.success("Access group created successfully"); form.resetFields(); onSuccess?.(); onCancel(); diff --git a/ui/litellm-dashboard/src/components/AccessGroups/AccessGroupsModal/AccessGroupEditModal.tsx b/ui/litellm-dashboard/src/components/AccessGroups/AccessGroupsModal/AccessGroupEditModal.tsx index 919295b6f7..f05edb3fe9 100644 --- a/ui/litellm-dashboard/src/components/AccessGroups/AccessGroupsModal/AccessGroupEditModal.tsx +++ b/ui/litellm-dashboard/src/components/AccessGroups/AccessGroupsModal/AccessGroupEditModal.tsx @@ -1,5 +1,6 @@ import React, { useEffect } from "react"; -import { Modal, Form, message } from "antd"; +import { Modal, Form } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { AccessGroupBaseForm, AccessGroupFormValues, @@ -55,7 +56,7 @@ export function AccessGroupEditModal({ { accessGroupId: accessGroup.access_group_id, params }, { onSuccess: () => { - message.success("Access group updated successfully"); + MessageManager.success("Access group updated successfully"); onSuccess?.(); onCancel(); }, diff --git a/ui/litellm-dashboard/src/components/BulkEditUsers.tsx b/ui/litellm-dashboard/src/components/BulkEditUsers.tsx index 2f3e57ff2a..2ca69065bc 100644 --- a/ui/litellm-dashboard/src/components/BulkEditUsers.tsx +++ b/ui/litellm-dashboard/src/components/BulkEditUsers.tsx @@ -3,7 +3,6 @@ import { Modal, Typography, Divider, - message, Table, Select, InputNumber, @@ -14,6 +13,7 @@ import { import { userBulkUpdateUserCall, teamBulkMemberAddCall, Member } from "./networking"; import { UserEditView } from "./user_edit_view"; import NotificationsManager from "./molecules/notifications_manager"; +import MessageManager from "@/components/molecules/message_manager"; const { Text, Title } = Typography; @@ -188,7 +188,7 @@ const BulkEditUserModal: React.FC = ({ } if (failedTeams.length > 0) { - message.warning(`Failed to add users to ${failedTeams.length} team(s)`); + MessageManager.warning(`Failed to add users to ${failedTeams.length} team(s)`); } } diff --git a/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroCreateModal.tsx b/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroCreateModal.tsx index feb00fc040..5f460cb7cb 100644 --- a/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroCreateModal.tsx +++ b/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroCreateModal.tsx @@ -1,4 +1,5 @@ -import { Form, Modal, Input, message } from "antd"; +import { Form, Modal, Input } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { useEffect } from "react"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import { useCloudZeroCreate } from "@/app/(dashboard)/hooks/cloudzero/useCloudZeroCreate"; @@ -31,7 +32,7 @@ export default function CloudZeroCreationModal({ open, onOk, onCancel }: CloudZe }, { onSuccess: () => { - message.success("CloudZero integration created successfully"); + MessageManager.success("CloudZero integration created successfully"); form.resetFields(); onOk(); }, @@ -39,7 +40,7 @@ export default function CloudZeroCreationModal({ open, onOk, onCancel }: CloudZe if (error?.errorFields) { return; } - message.error(error?.message || "Failed to create CloudZero integration"); + MessageManager.error(error?.message || "Failed to create CloudZero integration"); }, }, ); @@ -47,7 +48,7 @@ export default function CloudZeroCreationModal({ open, onOk, onCancel }: CloudZe if (error?.errorFields) { return; } - message.error(error?.message || "Failed to create CloudZero integration"); + MessageManager.error(error?.message || "Failed to create CloudZero integration"); } }; diff --git a/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroIntegrationSettings.tsx b/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroIntegrationSettings.tsx index c161d241f7..62709dac52 100644 --- a/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroIntegrationSettings.tsx +++ b/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroIntegrationSettings.tsx @@ -3,7 +3,8 @@ import { useCloudZeroExport } from "@/app/(dashboard)/hooks/cloudzero/useCloudZe import { useCloudZeroDeleteSettings } from "@/app/(dashboard)/hooks/cloudzero/useCloudZeroSettings"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; import DeleteResourceModal from "@/components/common_components/DeleteResourceModal"; -import { Alert, Button, Card, Descriptions, Divider, message, Popconfirm, Tag } from "antd"; +import { Alert, Button, Card, Descriptions, Divider, Popconfirm, Tag } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { CheckCircle, Edit, Play, Trash2, Upload } from "lucide-react"; import { useState } from "react"; import CloudZeroUpdateModal from "./CloudZeroUpdateModal"; @@ -30,10 +31,10 @@ export function CloudZeroIntegrationSettings({ settings, onSettingsUpdated }: Cl { limit: 10 }, { onSuccess: (data) => { - message.success("Dry run completed successfully"); + MessageManager.success("Dry run completed successfully"); }, onError: (error) => { - message.error(error?.message || "Failed to perform dry run"); + MessageManager.error(error?.message || "Failed to perform dry run"); }, }, ); @@ -48,10 +49,10 @@ export function CloudZeroIntegrationSettings({ settings, onSettingsUpdated }: Cl { operation: "replace_hourly" }, { onSuccess: () => { - message.success("Data successfully exported to CloudZero"); + MessageManager.success("Data successfully exported to CloudZero"); }, onError: (error) => { - message.error(error?.message || "Failed to export data"); + MessageManager.error(error?.message || "Failed to export data"); }, }, ); @@ -79,12 +80,12 @@ export function CloudZeroIntegrationSettings({ settings, onSettingsUpdated }: Cl deleteMutation.mutate(undefined, { onSuccess: () => { - message.success("CloudZero integration deleted successfully"); + MessageManager.success("CloudZero integration deleted successfully"); setIsDeleteModalOpen(false); onSettingsUpdated(); }, onError: (error) => { - message.error(error?.message || "Failed to delete CloudZero integration"); + MessageManager.error(error?.message || "Failed to delete CloudZero integration"); }, }); }; diff --git a/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroUpdateModal.tsx b/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroUpdateModal.tsx index 0aca6857b8..a04007897c 100644 --- a/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroUpdateModal.tsx +++ b/ui/litellm-dashboard/src/components/CloudZeroCostTracking/CloudZeroUpdateModal.tsx @@ -1,6 +1,7 @@ import { useCloudZeroUpdateSettings } from "@/app/(dashboard)/hooks/cloudzero/useCloudZeroSettings"; import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized"; -import { Form, Input, message, Modal } from "antd"; +import { Form, Input, Modal } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { useEffect } from "react"; import { CloudZeroSettings } from "./types"; @@ -39,7 +40,7 @@ export default function CloudZeroUpdateModal({ open, onOk, onCancel, settings }: }, { onSuccess: () => { - message.success("CloudZero integration updated successfully"); + MessageManager.success("CloudZero integration updated successfully"); form.resetFields(); onOk(); }, @@ -47,7 +48,7 @@ export default function CloudZeroUpdateModal({ open, onOk, onCancel, settings }: if (error?.errorFields) { return; } - message.error(error?.message || "Failed to update CloudZero integration"); + MessageManager.error(error?.message || "Failed to update CloudZero integration"); }, }, ); @@ -55,7 +56,7 @@ export default function CloudZeroUpdateModal({ open, onOk, onCancel, settings }: if (error?.errorFields) { return; } - message.error(error?.message || "Failed to update CloudZero integration"); + MessageManager.error(error?.message || "Failed to update CloudZero integration"); } }; diff --git a/ui/litellm-dashboard/src/components/Projects/ProjectModals/CreateProjectModal.tsx b/ui/litellm-dashboard/src/components/Projects/ProjectModals/CreateProjectModal.tsx index e490f89303..bbf56e4930 100644 --- a/ui/litellm-dashboard/src/components/Projects/ProjectModals/CreateProjectModal.tsx +++ b/ui/litellm-dashboard/src/components/Projects/ProjectModals/CreateProjectModal.tsx @@ -1,5 +1,6 @@ -import { Modal, Form, Button, Typography, message } from "antd"; +import { Modal, Form, Button, Typography } from "antd"; import { FolderAddOutlined } from "@ant-design/icons"; +import MessageManager from "@/components/molecules/message_manager"; import { useCreateProject, ProjectCreateParams, @@ -32,12 +33,12 @@ export function CreateProjectModal({ createMutation.mutate(params, { onSuccess: () => { - message.success("Project created successfully"); + MessageManager.success("Project created successfully"); form.resetFields(); onClose(); }, onError: (error) => { - message.error(error.message || "Failed to create project"); + MessageManager.error(error.message || "Failed to create project"); }, }); } catch (error) { diff --git a/ui/litellm-dashboard/src/components/Projects/ProjectModals/EditProjectModal.tsx b/ui/litellm-dashboard/src/components/Projects/ProjectModals/EditProjectModal.tsx index 75f56b1373..dc3b43ef73 100644 --- a/ui/litellm-dashboard/src/components/Projects/ProjectModals/EditProjectModal.tsx +++ b/ui/litellm-dashboard/src/components/Projects/ProjectModals/EditProjectModal.tsx @@ -1,6 +1,7 @@ import { useEffect } from "react"; -import { Modal, Form, Button, Typography, message } from "antd"; +import { Modal, Form, Button, Typography } from "antd"; import { SaveOutlined } from "@ant-design/icons"; +import MessageManager from "@/components/molecules/message_manager"; import { ProjectResponse } from "@/app/(dashboard)/hooks/projects/useProjects"; import { useUpdateProject, @@ -80,12 +81,12 @@ export function EditProjectModal({ { projectId: project.project_id, params }, { onSuccess: () => { - message.success("Project updated successfully"); + MessageManager.success("Project updated successfully"); onSuccess?.(); onClose(); }, onError: (error) => { - message.error(error.message || "Failed to update project"); + MessageManager.error(error.message || "Failed to update project"); }, }, ); diff --git a/ui/litellm-dashboard/src/components/SearchTools/SearchToolTester.tsx b/ui/litellm-dashboard/src/components/SearchTools/SearchToolTester.tsx index 28b34b0608..d1cb5077b1 100644 --- a/ui/litellm-dashboard/src/components/SearchTools/SearchToolTester.tsx +++ b/ui/litellm-dashboard/src/components/SearchTools/SearchToolTester.tsx @@ -1,5 +1,6 @@ import React, { useState } from "react"; -import { Button, Input, Typography, Spin, message } from "antd"; +import { Button, Input, Typography, Spin } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { SearchOutlined, LoadingOutlined } from "@ant-design/icons"; import { searchToolQueryCall } from "../networking"; import NotificationsManager from "../molecules/notifications_manager"; @@ -39,7 +40,7 @@ export const SearchToolTester: React.FC = ({ searchToolNa const handleSearch = async () => { if (!query.trim()) { - message.warning("Please enter a search query"); + MessageManager.warning("Please enter a search query"); return; } diff --git a/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/AddFallbacks.tsx b/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/AddFallbacks.tsx index 34f059516a..e55089d27d 100644 --- a/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/AddFallbacks.tsx +++ b/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/AddFallbacks.tsx @@ -5,8 +5,9 @@ */ import { Button as TremorButton } from "@tremor/react"; -import { Button, message } from "antd"; +import { Button } from "antd"; import React, { useEffect, useState } from "react"; +import MessageManager from "@/components/molecules/message_manager"; import NotificationManager from "../../../molecules/notifications_manager"; import { fetchAvailableModels, ModelGroup } from "../../../playground/llm_calls/fetch_models"; import { AddFallbacksModal } from "./AddFallbacksModal"; @@ -90,7 +91,7 @@ export default function AddFallbacks({ (g) => !g.primaryModel || g.fallbackModels.length === 0, ); if (invalidGroups.length > 0) { - message.error( + MessageManager.error( `Please complete configuration for all groups. ${invalidGroups.length} group(s) incomplete.`, ); return; diff --git a/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/FallbackSelectionForm.tsx b/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/FallbackSelectionForm.tsx index 08b031c683..0482161bc6 100644 --- a/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/FallbackSelectionForm.tsx +++ b/ui/litellm-dashboard/src/components/Settings/RouterSettings/Fallbacks/FallbackSelectionForm.tsx @@ -5,9 +5,10 @@ */ import { Button } from "@tremor/react"; -import { message, Tabs } from "antd"; +import { Tabs } from "antd"; import { Plus } from "lucide-react"; import React, { useEffect, useState } from "react"; +import MessageManager from "@/components/molecules/message_manager"; import { FallbackGroup, FallbackGroupConfig } from "./FallbackGroupConfig"; interface FallbackSelectionFormProps { @@ -60,7 +61,7 @@ export function FallbackSelectionForm({ const handleRemoveGroup = (targetId: string) => { if (groups.length === 1) { - message.warning("At least one group is required"); + MessageManager.warning("At least one group is required"); return; } const newGroups = groups.filter((g) => g.id !== targetId); diff --git a/ui/litellm-dashboard/src/components/agents/add_agent_form.tsx b/ui/litellm-dashboard/src/components/agents/add_agent_form.tsx index c5518596b8..b6d96f0445 100644 --- a/ui/litellm-dashboard/src/components/agents/add_agent_form.tsx +++ b/ui/litellm-dashboard/src/components/agents/add_agent_form.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect } from "react"; -import { Modal, Form, message, Select, Input, Steps, Radio, Tag, Divider, Switch, InputNumber, Collapse } from "antd"; +import { Modal, Form, Select, Input, Steps, Radio, Tag, Divider, Switch, InputNumber, Collapse } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { Button } from "@tremor/react"; import { CheckCircleFilled, KeyOutlined, RobotOutlined, AppstoreOutlined, InfoCircleOutlined } from "@ant-design/icons"; import CreatedKeyDisplay from "../shared/CreatedKeyDisplay"; @@ -216,7 +217,7 @@ const AddAgentForm: React.FC = ({ const handleCreateAgent = async () => { if (!accessToken) { - message.error("No access token available"); + MessageManager.error("No access token available"); return; } @@ -226,7 +227,7 @@ const AddAgentForm: React.FC = ({ const values = { ...form.getFieldsValue(true) }; const agentData = buildAgentData(values); if (!agentData) { - message.error("Failed to build agent data"); + MessageManager.error("Failed to build agent data"); setIsSubmitting(false); return; } @@ -301,7 +302,7 @@ const AddAgentForm: React.FC = ({ setCreatedKeyValue(keyResponse.key || null); } else if (keyAssignOption === "existing_key") { if (!selectedExistingKey) { - message.error("Please select an existing key to assign"); + MessageManager.error("Please select an existing key to assign"); setIsSubmitting(false); return; } @@ -318,7 +319,7 @@ const AddAgentForm: React.FC = ({ } catch (error) { console.error("Error creating agent:", error); const errorMessage = error instanceof Error ? error.message : String(error); - message.error(errorMessage ? `Failed to create agent: ${errorMessage}` : "Failed to create agent"); + MessageManager.error(errorMessage ? `Failed to create agent: ${errorMessage}` : "Failed to create agent"); } finally { setIsSubmitting(false); } diff --git a/ui/litellm-dashboard/src/components/agents/agent_info.tsx b/ui/litellm-dashboard/src/components/agents/agent_info.tsx index b41e318a76..d543be8356 100644 --- a/ui/litellm-dashboard/src/components/agents/agent_info.tsx +++ b/ui/litellm-dashboard/src/components/agents/agent_info.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from "react"; import { Card, Title, Text, Button as TremorButton, Tab, TabGroup, TabList, TabPanel, TabPanels} from "@tremor/react"; -import { Form, Input, InputNumber, Button as AntButton, message, Spin, Descriptions, Divider } from "antd"; +import { Form, Input, InputNumber, Button as AntButton, Spin, Descriptions, Divider } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { ArrowLeftIcon } from "@heroicons/react/outline"; import { getAgentInfo, patchAgentCall, getAgentCreateMetadata, AgentCreateInfo } from "../networking"; import { Agent } from "./types"; @@ -72,7 +73,7 @@ const AgentInfoView: React.FC = ({ } } catch (error) { console.error("Error fetching agent info:", error); - message.error("Failed to load agent information"); + MessageManager.error("Failed to load agent information"); } finally { setIsLoading(false); } @@ -111,12 +112,12 @@ const AgentInfoView: React.FC = ({ } await patchAgentCall(accessToken, agentId, updateData); - message.success("Agent updated successfully"); + MessageManager.success("Agent updated successfully"); setIsEditing(false); fetchAgentInfo(); } catch (error) { console.error("Error updating agent:", error); - message.error("Failed to update agent"); + MessageManager.error("Failed to update agent"); } finally { setIsSaving(false); } diff --git a/ui/litellm-dashboard/src/components/chat/ChatPage.tsx b/ui/litellm-dashboard/src/components/chat/ChatPage.tsx index ccf39d2147..b547f74917 100644 --- a/ui/litellm-dashboard/src/components/chat/ChatPage.tsx +++ b/ui/litellm-dashboard/src/components/chat/ChatPage.tsx @@ -1,7 +1,8 @@ "use client"; import React, { useCallback, useEffect, useRef, useState, useLayoutEffect } from "react"; -import { Tooltip, Skeleton, Popover, message } from "antd"; +import { Tooltip, Skeleton, Popover } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { SettingOutlined, PlusOutlined, @@ -212,7 +213,7 @@ const ChatPage: React.FC = ({ accessToken, userRole, userId, user localStorage.setItem(LOCALSTORAGE_MODEL_KEY, JSON.stringify([names[0]])); } }) - .catch(() => message.error("Could not load models")) + .catch(() => MessageManager.error("Could not load models")) .finally(() => setIsLoadingModels(false)); }, [accessToken]); diff --git a/ui/litellm-dashboard/src/components/chat/MCPAppsPanel.tsx b/ui/litellm-dashboard/src/components/chat/MCPAppsPanel.tsx index b805cab71e..d25db73ae4 100644 --- a/ui/litellm-dashboard/src/components/chat/MCPAppsPanel.tsx +++ b/ui/litellm-dashboard/src/components/chat/MCPAppsPanel.tsx @@ -5,7 +5,7 @@ import { Spin, Input, Button, Skeleton } from "antd"; import { SearchOutlined, ArrowLeftOutlined, RightOutlined, ToolOutlined, CheckCircleOutlined } from "@ant-design/icons"; import { deleteMCPOAuthUserCredential, fetchMCPServers, getMCPOAuthUserCredentialStatus, listMCPTools } from "../networking"; import { AUTH_TYPE, MCPServer, MCPTool, handleTransport } from "../mcp_tools/types"; -import { message } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { useUserMcpOAuthFlow } from "@/hooks/useUserMcpOAuthFlow"; // ── OAuth2 connect button ───────────────────────────────────────────────────── @@ -198,7 +198,7 @@ const MCPAppsPanel: React.FC = ({ accessToken, selectedServers, onChange const idToFetch = serverId ?? serverName; const result = await listMCPTools(accessToken, idToFetch); if (result?.error) { - message.warning(`Could not load tools for ${serverName}`); + MessageManager.warning(`Could not load tools for ${serverName}`); return; } // Use the ref so we read the most up-to-date list; guard against duplicates @@ -207,7 +207,7 @@ const MCPAppsPanel: React.FC = ({ accessToken, selectedServers, onChange onChange([...selectedServersRef.current, serverName]); } } catch { - message.warning(`Could not load tools for ${serverName}`); + MessageManager.warning(`Could not load tools for ${serverName}`); } finally { setTogglingOn((prev) => { const next = new Set(prev); diff --git a/ui/litellm-dashboard/src/components/chat/MCPConnectPicker.tsx b/ui/litellm-dashboard/src/components/chat/MCPConnectPicker.tsx index a52ce18156..6ef3aecc46 100644 --- a/ui/litellm-dashboard/src/components/chat/MCPConnectPicker.tsx +++ b/ui/litellm-dashboard/src/components/chat/MCPConnectPicker.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from "react"; -import { Switch, Spin, message } from "antd"; +import { Switch, Spin } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { fetchMCPServers, listMCPTools } from "../networking"; import { MCPServer } from "../mcp_tools/types"; @@ -57,7 +58,7 @@ const MCPConnectPicker: React.FC = ({ accessToken, selectedServers, onCha const result = await listMCPTools(accessToken, serverName); // listMCPTools never throws; it returns { tools, error, message } on failure if (result?.error) { - message.warning( + MessageManager.warning( `Could not load tools for ${serverName} — it will be excluded from this message.` ); // Do not add to selectedServers @@ -65,7 +66,7 @@ const MCPConnectPicker: React.FC = ({ accessToken, selectedServers, onCha } onChange([...selectedServers, serverName]); } catch { - message.warning( + MessageManager.warning( `Could not load tools for ${serverName} — it will be excluded from this message.` ); // Do not add to selectedServers diff --git a/ui/litellm-dashboard/src/components/chat/MCPCredentialsTab.tsx b/ui/litellm-dashboard/src/components/chat/MCPCredentialsTab.tsx index 363ec8c0e4..e2d879b22a 100644 --- a/ui/litellm-dashboard/src/components/chat/MCPCredentialsTab.tsx +++ b/ui/litellm-dashboard/src/components/chat/MCPCredentialsTab.tsx @@ -8,7 +8,8 @@ */ import React, { useCallback, useEffect, useState } from "react"; -import { Spin, message } from "antd"; +import { Spin } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { DeleteOutlined, LinkOutlined } from "@ant-design/icons"; import { Badge, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from "@tremor/react"; import { @@ -77,7 +78,7 @@ const MCPCredentialsTab: React.FC = ({ accessToken }) => { await deleteMCPOAuthUserCredential(accessToken, serverId); setCredentials((prev) => prev.filter((c) => c.server_id !== serverId)); } catch { - message.error("Failed to revoke connection. Please try again."); + MessageManager.error("Failed to revoke connection. Please try again."); } finally { setRevoking((prev) => { const n = new Set(prev); n.delete(serverId); return n; }); } diff --git a/ui/litellm-dashboard/src/components/claude_code_plugins/add_plugin_form.tsx b/ui/litellm-dashboard/src/components/claude_code_plugins/add_plugin_form.tsx index 217851f128..d5e417a9a8 100644 --- a/ui/litellm-dashboard/src/components/claude_code_plugins/add_plugin_form.tsx +++ b/ui/litellm-dashboard/src/components/claude_code_plugins/add_plugin_form.tsx @@ -1,5 +1,6 @@ import React, { useState } from "react"; -import { Modal, Form, Input, Select, message } from "antd"; +import { Modal, Form, Input, Select } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { Button } from "@tremor/react"; import { registerClaudeCodePlugin } from "../networking"; import { @@ -43,13 +44,13 @@ const AddPluginForm: React.FC = ({ const handleSubmit = async (values: any) => { if (!accessToken) { - message.error("No access token available"); + MessageManager.error("No access token available"); return; } // Validate plugin name if (!validatePluginName(values.name)) { - message.error( + MessageManager.error( "Plugin name must be kebab-case (lowercase letters, numbers, and hyphens only)" ); return; @@ -57,7 +58,7 @@ const AddPluginForm: React.FC = ({ // Validate semantic version if provided if (values.version && !isValidSemanticVersion(values.version)) { - message.error( + MessageManager.error( "Version must be in semantic versioning format (e.g., 1.0.0)" ); return; @@ -65,13 +66,13 @@ const AddPluginForm: React.FC = ({ // Validate email if provided if (values.authorEmail && !isValidEmail(values.authorEmail)) { - message.error("Invalid email format"); + MessageManager.error("Invalid email format"); return; } // Validate homepage URL if provided if (values.homepage && !isValidUrl(values.homepage)) { - message.error("Invalid homepage URL format"); + MessageManager.error("Invalid homepage URL format"); return; } @@ -119,14 +120,14 @@ const AddPluginForm: React.FC = ({ } await registerClaudeCodePlugin(accessToken, pluginData); - message.success("Plugin registered successfully"); + MessageManager.success("Plugin registered successfully"); form.resetFields(); setSourceType("github"); onSuccess(); onClose(); } catch (error) { console.error("Error registering plugin:", error); - message.error("Failed to register plugin"); + MessageManager.error("Failed to register plugin"); } finally { setIsSubmitting(false); } diff --git a/ui/litellm-dashboard/src/components/mcp_tools/ByokCredentialModal.tsx b/ui/litellm-dashboard/src/components/mcp_tools/ByokCredentialModal.tsx index 473918c126..58c2a965e0 100644 --- a/ui/litellm-dashboard/src/components/mcp_tools/ByokCredentialModal.tsx +++ b/ui/litellm-dashboard/src/components/mcp_tools/ByokCredentialModal.tsx @@ -1,7 +1,8 @@ "use client"; import React, { useState } from "react"; -import { Modal, Input, Switch, message } from "antd"; +import { Modal, Input, Switch } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { KeyOutlined, LockOutlined, @@ -46,7 +47,7 @@ export const ByokCredentialModal: React.FC = ({ const handleAuthorize = async () => { if (!apiKey.trim()) { - message.error("Please enter your API key"); + MessageManager.error("Please enter your API key"); return; } setLoading(true); @@ -63,11 +64,11 @@ export const ByokCredentialModal: React.FC = ({ const err = await response.json(); throw new Error(err?.detail?.error || "Failed to save credential"); } - message.success(`Connected to ${serverDisplayName}`); + MessageManager.success(`Connected to ${serverDisplayName}`); onSuccess(server.server_id); handleClose(); } catch (e: any) { - message.error(e.message || "Failed to connect"); + MessageManager.error(e.message || "Failed to connect"); } finally { setLoading(false); } diff --git a/ui/litellm-dashboard/src/components/molecules/message_manager.tsx b/ui/litellm-dashboard/src/components/molecules/message_manager.tsx new file mode 100644 index 0000000000..e4c1552d5e --- /dev/null +++ b/ui/litellm-dashboard/src/components/molecules/message_manager.tsx @@ -0,0 +1,38 @@ +import { message as staticMessage } from "antd"; +import type { MessageInstance } from "antd/es/message/interface"; + +let messageInstance: MessageInstance | null = null; + +export const setMessageInstance = (instance: MessageInstance) => { + messageInstance = instance; +}; + +const getMessageApi = () => messageInstance || staticMessage; + +const MessageManager = { + success(content: string, duration?: number) { + getMessageApi().success(content, duration); + }, + + error(content: string, duration?: number) { + getMessageApi().error(content, duration); + }, + + warning(content: string, duration?: number) { + getMessageApi().warning(content, duration); + }, + + info(content: string, duration?: number) { + getMessageApi().info(content, duration); + }, + + loading(content: string, duration?: number) { + return getMessageApi().loading(content, duration); + }, + + destroy() { + getMessageApi().destroy(); + }, +}; + +export default MessageManager; diff --git a/ui/litellm-dashboard/src/components/networking.tsx b/ui/litellm-dashboard/src/components/networking.tsx index bb9a0c1e01..956968dfee 100644 --- a/ui/litellm-dashboard/src/components/networking.tsx +++ b/ui/litellm-dashboard/src/components/networking.tsx @@ -68,7 +68,7 @@ export const getInProductNudgesCall = async (accessToken: string) => { /** * Helper file for calls being made to proxy */ -import { message } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { clearTokenCookies } from "@/utils/cookieUtils"; import { TagNewRequest, TagUpdateRequest, TagListResponse, TagInfoResponse } from "./tag_management/types"; import { Team } from "./key_team_helpers/key_list"; @@ -555,7 +555,7 @@ export const modelCreateCall = async (accessToken: string, formValues: Model) => console.log("API Response:", data); // Close any existing messages before showing new ones - message.destroy(); + MessageManager.destroy(); // Sequential success messages NotificationsManager.success(`Model ${formValues.model_name} created successfully`); diff --git a/ui/litellm-dashboard/src/components/organisms/create_key_button.tsx b/ui/litellm-dashboard/src/components/organisms/create_key_button.tsx index d0a7a909d6..df5b3a4b32 100644 --- a/ui/litellm-dashboard/src/components/organisms/create_key_button.tsx +++ b/ui/litellm-dashboard/src/components/organisms/create_key_button.tsx @@ -8,7 +8,7 @@ import { formatNumberWithCommas } from "@/utils/dataUtils"; import { InfoCircleOutlined } from "@ant-design/icons"; import { useQueryClient } from "@tanstack/react-query"; import { Accordion, AccordionBody, AccordionHeader, Button, Col, Grid, Text, TextInput, Title } from "@tremor/react"; -import { Button as Button2, Form, Input, message, Modal, Radio, Select, Switch, Tag, Tooltip } from "antd"; +import { Button as Button2, Form, Input, Modal, Radio, Select, Switch, Tag, Tooltip } from "antd"; import debounce from "lodash/debounce"; import React, { useCallback, useEffect, useState } from "react"; import { rolesWithWriteAccess } from "../../utils/roles"; diff --git a/ui/litellm-dashboard/src/components/playground/chat_ui/CodeInterpreterTool.tsx b/ui/litellm-dashboard/src/components/playground/chat_ui/CodeInterpreterTool.tsx index 04711ede9f..dd37769556 100644 --- a/ui/litellm-dashboard/src/components/playground/chat_ui/CodeInterpreterTool.tsx +++ b/ui/litellm-dashboard/src/components/playground/chat_ui/CodeInterpreterTool.tsx @@ -1,5 +1,6 @@ import React from "react"; -import { Switch, Tooltip, message } from "antd"; +import { Switch, Tooltip } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { CodeOutlined, InfoCircleOutlined, ExclamationCircleOutlined } from "@ant-design/icons"; import { Text } from "@tremor/react"; @@ -38,7 +39,7 @@ const CodeInterpreterTool: React.FC = ({ const handleToggle = (checked: boolean) => { if (checked && !isOpenAI) { - message.warning("Code Interpreter is only available for OpenAI models"); + MessageManager.warning("Code Interpreter is only available for OpenAI models"); return; } onEnabledChange(checked); diff --git a/ui/litellm-dashboard/src/components/policies/index.tsx b/ui/litellm-dashboard/src/components/policies/index.tsx index bd77f3ab9b..f47b8d78b9 100644 --- a/ui/litellm-dashboard/src/components/policies/index.tsx +++ b/ui/litellm-dashboard/src/components/policies/index.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect, useCallback } from "react"; import { Button, TabGroup, TabList, Tab, TabPanels, TabPanel } from "@tremor/react"; -import { Modal, message, Alert } from "antd"; +import { Modal, Alert } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { ExclamationCircleOutlined, InfoCircleOutlined } from "@ant-design/icons"; import { isAdminRole } from "@/utils/roles"; import PolicyTable from "./policy_table"; @@ -80,7 +81,7 @@ const PoliciesPanel: React.FC = ({ setPoliciesList(response.policies || []); } catch (error) { console.error("Error fetching policies:", error); - message.error("Failed to fetch policies"); + MessageManager.error("Failed to fetch policies"); } finally { setIsLoading(false); } @@ -95,7 +96,7 @@ const PoliciesPanel: React.FC = ({ setAttachmentsList(response.attachments || []); } catch (error) { console.error("Error fetching attachments:", error); - message.error("Failed to fetch attachments"); + MessageManager.error("Failed to fetch attachments"); } finally { setIsAttachmentsLoading(false); } @@ -148,11 +149,11 @@ const PoliciesPanel: React.FC = ({ setIsDeleting(true); try { await deletePolicyCall(accessToken, policyToDelete.policy_id); - message.success(`Policy "${policyToDelete.policy_name}" deleted successfully`); + MessageManager.success(`Policy "${policyToDelete.policy_name}" deleted successfully`); await fetchPolicies(); } catch (error) { console.error("Error deleting policy:", error); - message.error("Failed to delete policy"); + MessageManager.error("Failed to delete policy"); } finally { setIsDeleting(false); setIsDeleteModalOpen(false); @@ -177,11 +178,11 @@ const PoliciesPanel: React.FC = ({ if (!accessToken) return; try { await deletePolicyAttachmentCall(accessToken, attachmentId); - message.success("Attachment deleted successfully"); + MessageManager.success("Attachment deleted successfully"); fetchAttachments(); } catch (error) { console.error("Error deleting attachment:", error); - message.error("Failed to delete attachment"); + MessageManager.error("Failed to delete attachment"); } }, }); @@ -193,7 +194,7 @@ const PoliciesPanel: React.FC = ({ const handleUseTemplate = async (template: any) => { if (!accessToken) { - message.error("Authentication required"); + MessageManager.error("Authentication required"); return; } @@ -221,7 +222,7 @@ const PoliciesPanel: React.FC = ({ setIsGuardrailSelectionModalOpen(true); } catch (error) { console.error("Error fetching guardrails:", error); - message.error("Failed to load guardrails. Please try again."); + MessageManager.error("Failed to load guardrails. Please try again."); } }; @@ -271,7 +272,7 @@ const PoliciesPanel: React.FC = ({ await proceedWithTemplate(enrichedTemplate); } catch (error) { console.error("Error enriching template:", error); - message.error("Failed to configure template. Please try again."); + MessageManager.error("Failed to configure template. Please try again."); setIsEnrichingTemplate(false); } }; @@ -318,15 +319,15 @@ const PoliciesPanel: React.FC = ({ // Show success message if (createdGuardrails.length > 0) { - message.success( + MessageManager.success( `Created ${createdGuardrails.length} guardrail${createdGuardrails.length > 1 ? "s" : ""}! Complete the policy form to save.` ); } else { - message.success("Template ready! Complete the policy form to save."); + MessageManager.success("Template ready! Complete the policy form to save."); } if (failedGuardrails.length > 0) { - message.warning( + MessageManager.warning( `Failed to create ${failedGuardrails.length} guardrail(s): ${failedGuardrails.join(", ")}. You may need to create them manually.` ); } @@ -348,7 +349,7 @@ const PoliciesPanel: React.FC = ({ setTemplateQueue([]); setTemplateQueueProgress(null); console.error("Error creating guardrails:", error); - message.error("Failed to create guardrails. Please try again."); + MessageManager.error("Failed to create guardrails. Please try again."); } }; diff --git a/ui/litellm-dashboard/src/components/policies/pipeline_flow_builder.tsx b/ui/litellm-dashboard/src/components/policies/pipeline_flow_builder.tsx index 89665774ee..b1768d5b81 100644 --- a/ui/litellm-dashboard/src/components/policies/pipeline_flow_builder.tsx +++ b/ui/litellm-dashboard/src/components/policies/pipeline_flow_builder.tsx @@ -1,5 +1,6 @@ import React, { useState } from "react"; -import { Select, Typography, message, Spin } from "antd"; +import { Select, Typography, Spin } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { Button, TextInput } from "@tremor/react"; import { ArrowLeftIcon, PlusIcon } from "@heroicons/react/outline"; import { DotsVerticalIcon } from "@heroicons/react/solid"; @@ -1385,17 +1386,17 @@ export const FlowBuilderPage: React.FC = ({ const handleSave = async () => { if (!policyName.trim()) { - message.error("Please enter a policy name"); + MessageManager.error("Please enter a policy name"); return; } if (!accessToken) { - message.error("No access token available"); + MessageManager.error("No access token available"); return; } const emptySteps = pipeline.steps.filter((s) => !s.guardrail); if (emptySteps.length > 0) { - message.error("Please select a guardrail for all steps"); + MessageManager.error("Please select a guardrail for all steps"); return; } diff --git a/ui/litellm-dashboard/src/components/policies/policy_templates.tsx b/ui/litellm-dashboard/src/components/policies/policy_templates.tsx index 98ba0acb6a..c4ea22651c 100644 --- a/ui/litellm-dashboard/src/components/policies/policy_templates.tsx +++ b/ui/litellm-dashboard/src/components/policies/policy_templates.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect, useMemo } from "react"; -import { Card, Button, Spin, message, Checkbox } from "antd"; +import { Card, Button, Spin, Checkbox } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { ShieldCheckIcon, ShieldExclamationIcon, @@ -184,7 +185,7 @@ const PolicyTemplates: React.FC = ({ onUseTemplate, onOpen onTemplatesLoaded?.(data); } catch (error) { console.error("Error fetching policy templates:", error); - message.error("Failed to fetch policy templates"); + MessageManager.error("Failed to fetch policy templates"); } finally { setIsLoading(false); } diff --git a/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.test.tsx b/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.test.tsx index 013e222c6b..eb6fe5f50c 100644 --- a/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.test.tsx +++ b/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.test.tsx @@ -3,15 +3,11 @@ import { render, screen, act } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import CreatedKeyDisplay from "./CreatedKeyDisplay"; -vi.mock("antd", async (importOriginal) => { - const actual = await importOriginal(); - return { - ...actual, - message: { success: vi.fn() }, - }; -}); +vi.mock("@/components/molecules/message_manager", () => ({ + default: { success: vi.fn(), error: vi.fn(), warning: vi.fn(), info: vi.fn(), loading: vi.fn(), destroy: vi.fn() }, +})); -import { message } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; describe("CreatedKeyDisplay", () => { beforeEach(() => { @@ -52,7 +48,7 @@ describe("CreatedKeyDisplay", () => { await user.click(screen.getByRole("button", { name: /copy virtual key/i })); - expect(message.success).toHaveBeenCalledWith("Key copied to clipboard"); + expect(MessageManager.success).toHaveBeenCalledWith("Key copied to clipboard"); }); it("should revert button text back after 2 seconds", async () => { diff --git a/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.tsx b/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.tsx index d75c63c646..00ffb8c6e2 100644 --- a/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.tsx +++ b/ui/litellm-dashboard/src/components/shared/CreatedKeyDisplay.tsx @@ -1,6 +1,7 @@ import React, { useState } from "react"; import { CopyToClipboard } from "react-copy-to-clipboard"; -import { Button, message } from "antd"; +import { Button } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; interface CreatedKeyDisplayProps { apiKey: string; @@ -15,7 +16,7 @@ const CreatedKeyDisplay: React.FC = ({ apiKey }) => { const handleCopy = () => { setCopied(true); - message.success("Key copied to clipboard"); + MessageManager.success("Key copied to clipboard"); setTimeout(() => setCopied(false), 2000); }; diff --git a/ui/litellm-dashboard/src/components/team/TeamInfo.tsx b/ui/litellm-dashboard/src/components/team/TeamInfo.tsx index d2ce79580d..a4c7ae2bbb 100644 --- a/ui/litellm-dashboard/src/components/team/TeamInfo.tsx +++ b/ui/litellm-dashboard/src/components/team/TeamInfo.tsx @@ -20,7 +20,8 @@ import { isProxyAdminRole } from "@/utils/roles"; import { EditOutlined, InfoCircleOutlined, SaveOutlined } from "@ant-design/icons"; import { ArrowLeftIcon } from "@heroicons/react/outline"; import { Badge, Card, Grid, Text, TextInput, Title } from "@tremor/react"; -import { Button, Form, Input, message, Select, Switch, Tabs, Tooltip } from "antd"; +import { Button, Form, Input, Select, Switch, Tabs, Tooltip } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { CheckIcon, CopyIcon } from "lucide-react"; import React, { useEffect, useMemo, useState } from "react"; import { copyToClipboard as utilCopyToClipboard } from "../../utils/dataUtils"; @@ -366,7 +367,7 @@ const TeamInfoView: React.FC = ({ tpm_limit: values.tpm_limit, rpm_limit: values.rpm_limit, }; - message.destroy(); // Remove all existing toasts + MessageManager.destroy(); // Remove all existing toasts await teamMemberUpdateCall(accessToken, teamId, member); @@ -388,7 +389,7 @@ const TeamInfoView: React.FC = ({ } setIsEditMemberModalVisible(false); - message.destroy(); // Remove all existing toasts + MessageManager.destroy(); // Remove all existing toasts NotificationsManager.fromBackend(errMsg); console.error("Error updating team member:", error); diff --git a/ui/litellm-dashboard/src/components/vector_store_management/CreateVectorStore.tsx b/ui/litellm-dashboard/src/components/vector_store_management/CreateVectorStore.tsx index 97162aa132..b189168e1b 100644 --- a/ui/litellm-dashboard/src/components/vector_store_management/CreateVectorStore.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_management/CreateVectorStore.tsx @@ -1,6 +1,7 @@ import React, { useState } from "react"; import { Card, Title, Text } from "@tremor/react"; -import { Upload, Button, Select, Form, message, Alert, Tooltip, Input } from "antd"; +import { Upload, Button, Select, Form, Alert, Tooltip, Input } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { InboxOutlined, InfoCircleOutlined } from "@ant-design/icons"; import type { UploadProps } from "antd"; import { ragIngestCall } from "../networking"; @@ -47,13 +48,13 @@ const CreateVectorStore: React.FC = ({ accessToken, onSu ].includes(file.type); if (!isValidType) { - message.error(`${file.name} is not a supported file type. Please upload PDF, TXT, DOCX, or MD files.`); + MessageManager.error(`${file.name} is not a supported file type. Please upload PDF, TXT, DOCX, or MD files.`); return Upload.LIST_IGNORE; } const isLt50M = file.size / 1024 / 1024 < 50; if (!isLt50M) { - message.error(`${file.name} must be smaller than 50MB!`); + MessageManager.error(`${file.name} must be smaller than 50MB!`); return Upload.LIST_IGNORE; } @@ -87,12 +88,12 @@ const CreateVectorStore: React.FC = ({ accessToken, onSu const handleCreateVectorStore = async () => { if (documents.length === 0) { - message.warning("Please upload at least one document"); + MessageManager.warning("Please upload at least one document"); return; } if (!selectedProvider) { - message.warning("Please select a provider"); + MessageManager.warning("Please select a provider"); return; } @@ -100,7 +101,7 @@ const CreateVectorStore: React.FC = ({ accessToken, onSu const requiredFields = getProviderSpecificFields(selectedProvider).filter((field) => field.required); for (const field of requiredFields) { if (!providerParams[field.name]) { - message.warning(`Please provide ${field.label}`); + MessageManager.warning(`Please provide ${field.label}`); return; } } @@ -108,17 +109,17 @@ const CreateVectorStore: React.FC = ({ accessToken, onSu // S3 Vectors specific validation if (selectedProvider === "s3_vectors") { if (providerParams.vector_bucket_name && providerParams.vector_bucket_name.length < 3) { - message.warning("Vector bucket name must be at least 3 characters"); + MessageManager.warning("Vector bucket name must be at least 3 characters"); return; } if (providerParams.index_name && providerParams.index_name.length > 0 && providerParams.index_name.length < 3) { - message.warning("Index name must be at least 3 characters if provided"); + MessageManager.warning("Index name must be at least 3 characters if provided"); return; } } if (!accessToken) { - message.error("No access token available"); + MessageManager.error("No access token available"); return; } diff --git a/ui/litellm-dashboard/src/components/vector_store_management/DocumentsTable.tsx b/ui/litellm-dashboard/src/components/vector_store_management/DocumentsTable.tsx index aeb4240d36..c1ce57ad33 100644 --- a/ui/litellm-dashboard/src/components/vector_store_management/DocumentsTable.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_management/DocumentsTable.tsx @@ -1,5 +1,6 @@ import React from "react"; -import { Table, Badge, Tooltip, message } from "antd"; +import { Table, Badge, Tooltip } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { EyeOutlined, CopyOutlined, DeleteOutlined } from "@ant-design/icons"; import { DocumentUpload } from "./types"; @@ -11,7 +12,7 @@ interface DocumentsTableProps { const DocumentsTable: React.FC = ({ documents, onRemove }) => { const handleCopyId = (uid: string) => { navigator.clipboard.writeText(uid); - message.success("Document ID copied to clipboard"); + MessageManager.success("Document ID copied to clipboard"); }; const getStatusBadge = (status: DocumentUpload["status"]) => { diff --git a/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTester.tsx b/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTester.tsx index f8b5ada0c3..5c59334fa4 100644 --- a/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTester.tsx +++ b/ui/litellm-dashboard/src/components/vector_store_management/VectorStoreTester.tsx @@ -1,5 +1,6 @@ import React, { useState } from "react"; -import { Button, Input, Card, Typography, Spin, message, Divider } from "antd"; +import { Button, Input, Card, Typography, Spin, Divider } from "antd"; +import MessageManager from "@/components/molecules/message_manager"; import { SendOutlined, DatabaseOutlined, LoadingOutlined, DownOutlined, RightOutlined } from "@ant-design/icons"; import { vectorStoreSearchCall } from "../networking"; import NotificationsManager from "../molecules/notifications_manager"; @@ -46,7 +47,7 @@ export const VectorStoreTester: React.FC = ({ vectorStor const handleSearch = async () => { if (!query.trim()) { - message.warning("Please enter a search query"); + MessageManager.warning("Please enter a search query"); return; } diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/InputCard.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/InputCard.tsx index 1299f9c168..8fb47274a8 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/InputCard.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/InputCard.tsx @@ -4,7 +4,7 @@ */ import { useState } from 'react'; -import { message } from 'antd'; +import MessageManager from "@/components/molecules/message_manager"; import { ParsedMessage } from './prettyMessagesTypes'; import { SectionHeader } from './SectionHeader'; import { CollapsibleMessage } from './CollapsibleMessage'; @@ -33,7 +33,7 @@ export function InputCard({ messages, promptTokens, inputCost }: InputCardProps) const handleCopy = () => { const content = lastMessage?.content || ''; navigator.clipboard.writeText(content); - message.success('Input copied'); + MessageManager.success('Input copied'); }; return ( diff --git a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/OutputCard.tsx b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/OutputCard.tsx index eff8d83cbd..22f1708ee4 100644 --- a/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/OutputCard.tsx +++ b/ui/litellm-dashboard/src/components/view_logs/LogDetailsDrawer/OutputCard.tsx @@ -4,7 +4,8 @@ */ import { useState } from 'react'; -import { Typography, message as antdMessage } from 'antd'; +import { Typography } from 'antd'; +import MessageManager from "@/components/molecules/message_manager"; import { ParsedMessage } from './prettyMessagesTypes'; import { SectionHeader } from './SectionHeader'; import { SimpleMessageBlock } from './SimpleMessageBlock'; @@ -25,7 +26,7 @@ export function OutputCard({ message, completionTokens, outputCost }: OutputCard const content = message.content || ''; navigator.clipboard.writeText(content); - antdMessage.success('Output copied'); + MessageManager.success('Output copied'); }; if (!message) { diff --git a/ui/litellm-dashboard/src/contexts/AntdGlobalProvider.tsx b/ui/litellm-dashboard/src/contexts/AntdGlobalProvider.tsx index 5b5c1036fa..cc182ee36d 100644 --- a/ui/litellm-dashboard/src/contexts/AntdGlobalProvider.tsx +++ b/ui/litellm-dashboard/src/contexts/AntdGlobalProvider.tsx @@ -1,23 +1,27 @@ "use client"; import React, { useEffect, useRef } from "react"; -import { notification } from "antd"; +import { notification, message } from "antd"; import { setNotificationInstance } from "@/components/molecules/notifications_manager"; +import { setMessageInstance } from "@/components/molecules/message_manager"; export default function AntdGlobalProvider({ children }: { children: React.ReactNode }) { - const [api, contextHolder] = notification.useNotification(); + const [notificationApi, notificationContextHolder] = notification.useNotification(); + const [messageApi, messageContextHolder] = message.useMessage(); const initialized = useRef(false); useEffect(() => { if (!initialized.current) { - setNotificationInstance(api); + setNotificationInstance(notificationApi); + setMessageInstance(messageApi); initialized.current = true; } - }, [api]); + }, [notificationApi, messageApi]); return ( <> - {contextHolder} + {notificationContextHolder} + {messageContextHolder} {children} );