From 3d51eb378a027b17e4f123b3eb768b31150bcccb Mon Sep 17 00:00:00 2001
From: yuneng-jiang
Date: Wed, 19 Aug 2026 14:07:50 -0700
Subject: [PATCH] refactor(ui): migrate the antd Alert call sites onto the
shared Alert (#37513)
Moves all 33 antd Alert usages across 20 dashboard files onto
src/components/shared/Alert, following the composition the rest of the
dashboard already uses: message becomes AlertTitle, description becomes
AlertDescription, showIcon becomes a lucide icon child, and closable
becomes an AlertAction ghost button.
antd type="success" has no counterpart on the shared Alert, so the two
success sites land on the default variant with a CircleCheck icon, which
is what cloudzero_export_modal and CloudZeroIntegrationSettings already
do for the same case.
LoginPage's dismissible SSO notice moves into its own SsoEnabledNotice
component in the same file: antd's closable carried its own dismiss
state, and inlining it pushed LoginPageContent past the complexity
budget.
Four files lose their last antd symbol, so their no-restricted-imports
suppressions are pruned by hand. antd import sites drop from 115 to 111
across 107 to 103 files, and the no-restricted-imports ratchet drops
from 119 to 115 over 110 to 106 files.
One test asserted antd's own ant-alert-info class; it is repointed to
the shared Alert's text-info variant class, which keeps the same
"info, not warning" check. Every other colocated test passes untouched.
---
ui/litellm-dashboard/eslint-suppressions.json | 18 --
.../admin-panel/_components/AdminPanel.tsx | 18 +-
.../_components/MCPPermissionManagement.tsx | 20 +-
.../_components/UserEnvVarsModal.tsx | 14 +-
.../mcp-servers/_components/mcp_connect.tsx | 50 ++--
.../_components/mcp_server_edit.tsx | 20 +-
.../policies/_components/add_policy_form.tsx | 60 ++---
.../_components/policy_test_panel.tsx | 10 +-
.../_components/CreateVectorStore.tsx | 38 +--
.../_components/S3VectorsConfig.tsx | 52 ++--
.../_components/VectorStoreForm.tsx | 246 +++++++++---------
.../src/app/login/LoginPage.tsx | 119 +++++----
.../src/app/onboarding/OnboardingFormBody.tsx | 24 +-
.../src/components/CreateUserButton.tsx | 29 +--
.../MCPSemanticFilterSettings.tsx | 59 +++--
.../src/components/add_model/AddModelForm.tsx | 18 +-
.../src/components/add_pass_through.tsx | 20 +-
.../PassThroughGuardrailsSection.tsx | 40 ++-
.../user_search_modal.test.tsx | 2 +-
.../common_components/user_search_modal.tsx | 18 +-
.../organisms/RegenerateKeyModal.tsx | 10 +-
.../update_model_credentials_modal.tsx | 18 +-
22 files changed, 460 insertions(+), 443 deletions(-)
diff --git a/ui/litellm-dashboard/eslint-suppressions.json b/ui/litellm-dashboard/eslint-suppressions.json
index 98225de5d8b..09e602e269b 100644
--- a/ui/litellm-dashboard/eslint-suppressions.json
+++ b/ui/litellm-dashboard/eslint-suppressions.json
@@ -1404,11 +1404,6 @@
"count": 2
}
},
- "src/app/(dashboard)/vector-stores/_components/S3VectorsConfig.tsx": {
- "no-restricted-imports": {
- "count": 1
- }
- },
"src/app/(dashboard)/vector-stores/_components/VectorStoreForm.tsx": {
"no-nested-ternary": {
"count": 2
@@ -1453,9 +1448,6 @@
"local/no-complex-jsx-arrow": {
"count": 1
},
- "no-restricted-imports": {
- "count": 1
- },
"react-hooks/set-state-in-effect": {
"count": 2
}
@@ -1470,11 +1462,6 @@
"count": 1
}
},
- "src/app/onboarding/OnboardingFormBody.tsx": {
- "no-restricted-imports": {
- "count": 1
- }
- },
"src/components/AIHub/ModelHubTable.test.tsx": {
"max-params": {
"count": 1
@@ -1998,11 +1985,6 @@
"count": 1
}
},
- "src/components/common_components/PassThroughGuardrailsSection.tsx": {
- "no-restricted-imports": {
- "count": 1
- }
- },
"src/components/common_components/RateLimitTypeFormItem.test.tsx": {
"no-restricted-imports": {
"count": 1
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx b/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx
index 48eaa62e37b..e9d7190abf5 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/admin-panel/_components/AdminPanel.tsx
@@ -7,8 +7,8 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
-import { Alert as AntdAlert, Modal, Space, Tabs, Typography } from "antd";
-import { Info } from "lucide-react";
+import { Modal, Space, Tabs, Typography } from "antd";
+import { Info, TriangleAlert } from "lucide-react";
import React, { useEffect, useState } from "react";
import NewBadge from "@/components/common_components/NewBadge";
import { useBaseUrl } from "@/components/constants";
@@ -223,12 +223,14 @@ const AdminPanel: React.FC = ({ proxySettings }) => {
<>
✨ Security Settings
-
+
+
+ SSO Configuration Deprecated
+
+ Editing SSO Settings on this page is deprecated and will be removed in a future version. Please use the
+ SSO Settings tab for SSO configuration.
+
+
= ({
)}
{showInternalDelegatePkceWarning && (
-
+
+
+ Internal server with upstream OAuth delegation
+
+ This MCP server is configured as internal-only but delegates auth to upstream. Anonymous users will be
+ able to reach the upstream OAuth2 /authorize flow without a LiteLLM session. Ensure your upstream
+ provider and network enforce access controls.
+
+
)}
= ({ server, open, acces
- Option 1: Get a specific server: "{serverName.replace(/\s+/g, "_")}"
-
-
- Option 2: Get a group of MCPs: "dev-group"
-
-
- You can also mix both: "Server1,dev-group"
-
-
- }
- />
+
+
+ Two Options
+
+
+ Option 1: Get a specific server: "{serverName.replace(/\s+/g, "_")}"
+
+
+ Option 2: Get a group of MCPs: "dev-group"
+
+
+ You can also mix both: "Server1,dev-group"
+
+
+
)}
)}
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.tsx b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.tsx
index eb462f788a5..94b66f1bdfa 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.tsx
@@ -1,5 +1,7 @@
import React, { useState, useEffect } from "react";
-import { Select, Tooltip, Input, InputNumber, Alert } from "antd";
+import { Select, Tooltip, Input, InputNumber } from "antd";
+import { TriangleAlert } from "lucide-react";
+import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { FormProvider, useForm } from "react-hook-form";
import { InfoCircleOutlined } from "@ant-design/icons";
import { Button } from "@/components/ui/button";
@@ -1041,13 +1043,15 @@ const MCPServerEdit: React.FC = ({
{!isStdioTransport && isOAuthAuthType && (
<>
{!oauthFlowTypeValue && !isDelegateAuth && (
-
+
+
+ This server has no OAuth flow set
+
+ Choose Machine-to-Machine (M2M) or Interactive (PKCE) so LiteLLM authenticates it the way you
+ intend, then save. Until it is set, LiteLLM falls back to interactive per-user auth and treats
+ a machine-to-machine credential shape conservatively.
+
+
)}
= ({
{selectedMode === "flow_builder" && (
+ >
+
+ You'll be redirected to the full-screen Flow Builder to design your policy logic visually.
+
+
)}
@@ -457,35 +461,33 @@ const AddPolicyForm: React.FC = ({
{resolvedGuardrails.length > 0 && (
-
-
- These are the final guardrails that will be applied (including inheritance):
-
-
- }
- type="info"
- showIcon
- />
+
+
)}
-
+
+
+ Model Scope
+
+ By default, this policy will run on all models. You can optionally restrict it to specific models below.
+
+
Model Condition Type
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/policy_test_panel.tsx b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/policy_test_panel.tsx
index 90642d23a46..9c234a7a70c 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/policy_test_panel.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/policies/_components/policy_test_panel.tsx
@@ -1,6 +1,8 @@
import React, { useState, useEffect } from "react";
import { useForm } from "react-hook-form";
-import { Alert, Empty } from "antd";
+import { Empty } from "antd";
+import { CircleAlert } from "lucide-react";
+import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { resolvePoliciesCall, teamListCall, keyListCall, modelAvailableCall } from "@/components/networking";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { FieldGroup } from "@/components/shared/form/field";
@@ -325,7 +327,11 @@ const PolicyTestPanel: React.FC = ({ accessToken }) => {
)}
{hasSearched && !result && !isLoading && (
-
+
+
+ Error
+ Failed to resolve policies. Check the proxy logs.
+
)}
);
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/CreateVectorStore.tsx b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/CreateVectorStore.tsx
index 93aaa90ef7c..271ef31a68e 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/CreateVectorStore.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/CreateVectorStore.tsx
@@ -1,9 +1,10 @@
import React, { useState } from "react";
-import { Upload, Alert } from "antd";
+import { Upload } from "antd";
import { toast } from "@/lib/toast";
import { InboxOutlined } from "@ant-design/icons";
import type { UploadProps } from "antd";
-import { CircleHelp } from "lucide-react";
+import { CircleCheck, CircleHelp, X } from "lucide-react";
+import { Alert, AlertAction, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { ragIngestCall } from "@/components/networking";
import { DocumentUpload, RAGIngestResponse } from "@/components/vector_store_management/types";
import DocumentsTable from "./DocumentsTable";
@@ -359,22 +360,23 @@ const CreateVectorStore: React.FC = ({ accessToken, onSu
{/* Success Message */}
{ingestResults.length > 0 && (
-
-
- Vector Store ID: {ingestResults[0]?.vector_store_id}
-
-
- Documents Ingested: {ingestResults.length}
-
-
- }
- type="success"
- showIcon
- closable
- />
+
+
+ Vector Store Created Successfully
+
+
+ Vector Store ID: {ingestResults[0]?.vector_store_id}
+
+
+ Documents Ingested: {ingestResults.length}
+
+
+
+
+
+
)}
diff --git a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/S3VectorsConfig.tsx b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/S3VectorsConfig.tsx
index 3e286ee9b92..cdc63e8b7ec 100644
--- a/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/S3VectorsConfig.tsx
+++ b/ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/S3VectorsConfig.tsx
@@ -1,6 +1,6 @@
import React, { useState, useEffect } from "react";
-import { Alert } from "antd";
-import { CircleHelp } from "lucide-react";
+import { CircleHelp, Info } from "lucide-react";
+import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { fetchAvailableModels, ModelGroup } from "@/components/llm_calls/fetch_models";
import { Field, FieldError, FieldLabel } from "@/components/shared/form/field";
import {
@@ -72,32 +72,28 @@ const S3VectorsConfig: React.FC = ({ accessToken, provider
return (
-
-
AWS S3 Vectors allows you to store and query vector embeddings directly in S3:
-
-
Vector buckets and indexes will be automatically created if they don't exist
-
Vector dimensions are auto-detected from your selected embedding model
-
Ensure your AWS credentials have permissions for S3 Vectors operations
Configure your PostgreSQL database with pgvector extension
+
Start the server and note the API base URL and API key
+
Enter those details in the fields below
+
+
+
)}
{selectedProvider === "valkey" && (
-
-
- LiteLLM searches documents you have already stored in Valkey. It does not create the index or
- upload documents for you. Before creating this vector store, make sure:
-
-
-
- Your Valkey server has vector search enabled (the valkey-search module, included in the
- valkey-bundle image and in AWS ElastiCache / MemoryDB for Valkey)
-
-
- You have already created a search index and loaded your documents and their embeddings into it.
- Enter that index name as the Vector Store ID
-
-
- You know which embedding model created those stored embeddings. That model must be added to this
- proxy under Models so you can pick it below. Using a different model returns wrong results
-
-
- You know the field names your documents use for their text and their embedding. If they are not
- "text" and "embedding", set them below
-
-
-
- When a query comes in, LiteLLM converts it to an embedding with the model below and returns the
- closest matching documents from your index.
-
+ LiteLLM searches documents you have already stored in Valkey. It does not create the index or upload
+ documents for you. Before creating this vector store, make sure:
+
+
+
+ Your Valkey server has vector search enabled (the valkey-search module, included in the
+ valkey-bundle image and in AWS ElastiCache / MemoryDB for Valkey)
+
+
+ You have already created a search index and loaded your documents and their embeddings into it.
+ Enter that index name as the Vector Store ID
+
+
+ You know which embedding model created those stored embeddings. That model must be added to this
+ proxy under Models so you can pick it below. Using a different model returns wrong results
+
+
+ You know the field names your documents use for their text and their embedding. If they are not
+ "text" and "embedding", set them below
+
+
+
+ When a query comes in, LiteLLM converts it to an embedding with the model below and returns the
+ closest matching documents from your index.
+
- For most data store types (Cloud Storage, BigQuery, Media): copy the data store ID and enter it
- in the Vector Store ID field below.
-
-
- For website, healthcare, and connector-based sources (Drive, Gmail, Slack, Jira, etc.): create a
- search app on top of the data store, then copy the Engine ID and enter it in
- the Engine ID field. The Vector Store ID is still required as the LiteLLM-side name for this
- record, but it isn't used in the GCP URL when Engine ID is set.
-
+ For most data store types (Cloud Storage, BigQuery, Media): copy the data store ID and enter it in
+ the Vector Store ID field below.
+
+
+ For website, healthcare, and connector-based sources (Drive, Gmail, Slack, Jira, etc.): create a
+ search app on top of the data store, then copy the Engine ID and enter it in the
+ Engine ID field. The Vector Store ID is still required as the LiteLLM-side name for this record,
+ but it isn't used in the GCP URL when Engine ID is set.
+
+
+
+
)}
;
+function SsoEnabledNotice() {
+ const [isDismissed, setIsDismissed] = useState(false);
+ if (isDismissed) return null;
+
+ return (
+
+
+
+ Single Sign-On (SSO) is enabled. LiteLLM no longer automatically redirects to the SSO login flow upon loading
+ this page. To re-enable auto-redirect-to-SSO, set{" "}
+ AUTO_REDIRECT_UI_LOGIN_TO_SSO=true in your
+ environment configuration.
+
+
+
+
+
+ );
+}
+
function LoginPageContent() {
const [isLoading, setIsLoading] = useState(true);
const { data: uiConfig, isLoading: isConfigLoading } = useUIConfig();
@@ -170,22 +192,19 @@ function LoginPageContent() {
🚅 LiteLLM
-
-
- The Admin UI has been disabled by the administrator. To re-enable it, please update the following
- environment variable:
-