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.
This commit is contained in:
yuneng-jiang 2026-08-19 14:07:50 -07:00 • committed by GitHub
parent a613773fca
commit 3d51eb378a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 460 additions and 443 deletions

View file

@ -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

View file

@ -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<AdminPanelProps> = ({ proxySettings }) => {
<>
<Card className="block p-6">
<Title level={4}> ✨ Security Settings</Title>
<AntdAlert
message="SSO Configuration Deprecated"
description="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."
type="warning"
showIcon
/>
<Alert variant="warning">
<TriangleAlert />
<AlertTitle>SSO Configuration Deprecated</AlertTitle>
<AlertDescription>
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.
</AlertDescription>
</Alert>
<div
style={{
display: "flex",

View file

@ -1,5 +1,7 @@
import React, { useEffect } from "react";
import { Alert, Select, Tooltip, Collapse, Input, Space, Switch } from "antd";
import { Select, Tooltip, Collapse, Input, Space, Switch } from "antd";
import { TriangleAlert } from "lucide-react";
import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { Button } from "@/components/ui/button";
import { InfoCircleOutlined, MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";
import { useFieldArray, useFormContext, useWatch } from "react-hook-form";
@ -271,13 +273,15 @@ const MCPPermissionManagement: React.FC<MCPPermissionManagementProps> = ({
)}
{showInternalDelegatePkceWarning && (
<Alert
type="warning"
showIcon
className="mb-2"
message="Internal server with upstream OAuth delegation"
description="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."
/>
<Alert variant="warning" className="mb-2">
<TriangleAlert />
<AlertTitle>Internal server with upstream OAuth delegation</AlertTitle>
<AlertDescription>
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.
</AlertDescription>
</Alert>
)}
<MountedFormField

View file

@ -1,5 +1,7 @@
import React from "react";
import { Modal, Alert, Spin, Tag, Typography } from "antd";
import { Modal, Spin, Tag, Typography } from "antd";
import { CircleAlert, Info } from "lucide-react";
import { Alert, AlertTitle } from "@/components/shared/Alert";
import { useMutation, useQuery } from "@tanstack/react-query";
import { z } from "zod/v4";
import { MCPServer, MCPUserEnvVarsStatus, MCPUserEnvVarSpec } from "@/components/mcp_tools/types";
@ -158,9 +160,15 @@ const UserEnvVarsModal: React.FC<UserEnvVarsModalProps> = ({ server, open, acces
<Spin />
</div>
) : isError ? (
<Alert type="error" showIcon message="Failed to load env vars" />
<Alert variant="error">
<CircleAlert />
<AlertTitle>Failed to load env vars</AlertTitle>
</Alert>
) : required.length === 0 ? (
<Alert type="info" showIcon message="No per-user fields configured for this server." />
<Alert variant="info">
<Info />
<AlertTitle>No per-user fields configured for this server.</AlertTitle>
</Alert>
) : (
<>
<Text className="text-sm text-muted-foreground block">

View file

@ -1,10 +1,22 @@
/* eslint-disable react/no-unescaped-entities */
import React, { useState } from "react";
import { Card, Typography, Space, Alert, Switch } from "antd";
import { Card, Typography, Space, Switch } from "antd";
import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { Button } from "@/components/ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { CopyIcon, Code, Terminal, Globe, CheckIcon, ExternalLinkIcon, KeyIcon, ServerIcon, Zap } from "lucide-react";
import {
CopyIcon,
Code,
Terminal,
Globe,
CheckIcon,
ExternalLinkIcon,
Info,
KeyIcon,
ServerIcon,
Zap,
} from "lucide-react";
import { getProxyBaseUrl } from "@/components/networking";
import { copyToClipboard as utilCopyToClipboard } from "@/utils/dataUtils";
@ -69,25 +81,21 @@ const FeatureCard: React.FC<FeatureCardProps> = ({
</Text>
</div>
{useServerHeader && (
<Alert
className="mt-2"
type="info"
showIcon
message="Two Options"
description={
<div>
<p>
<strong>Option 1:</strong> Get a specific server: <code>"{serverName.replace(/\s+/g, "_")}"</code>
</p>
<p>
<strong>Option 2:</strong> Get a group of MCPs: <code>"dev-group"</code>
</p>
<p className="mt-2 text-sm text-muted-foreground">
You can also mix both: <code>"Server1,dev-group"</code>
</p>
</div>
}
/>
<Alert className="mt-2" variant="info">
<Info />
<AlertTitle>Two Options</AlertTitle>
<AlertDescription>
<p>
<strong>Option 1:</strong> Get a specific server: <code>"{serverName.replace(/\s+/g, "_")}"</code>
</p>
<p>
<strong>Option 2:</strong> Get a group of MCPs: <code>"dev-group"</code>
</p>
<p className="mt-2 text-sm text-muted-foreground">
You can also mix both: <code>"Server1,dev-group"</code>
</p>
</AlertDescription>
</Alert>
)}
</div>
)}

View file

@ -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<MCPServerEditProps> = ({
{!isStdioTransport && isOAuthAuthType && (
<>
{!oauthFlowTypeValue && !isDelegateAuth && (
<Alert
type="warning"
showIcon
className="mb-4 rounded-lg"
message="This server has no OAuth flow set"
description="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."
/>
<Alert variant="warning" className="mb-4 rounded-lg">
<TriangleAlert />
<AlertTitle>This server has no OAuth flow set</AlertTitle>
<AlertDescription>
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.
</AlertDescription>
</Alert>
)}
<OAuthFormFields
isM2M={isM2MFlow}

View file

@ -1,5 +1,5 @@
import React, { useState, useEffect } from "react";
import { Modal, Alert, Tag } from "antd";
import { Modal, Tag } from "antd";
import { z } from "zod/v4";
import { Policy, PolicyCreateRequest, PolicyUpdateRequest } from "@/components/policies/types";
import { Guardrail } from "@/components/guardrails/types";
@ -19,7 +19,8 @@ import { Textarea } from "@/components/ui/textarea";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner";
import { useZodForm } from "@/lib/forms/useZodForm";
import { CircleHelp } from "lucide-react";
import { CircleHelp, Info } from "lucide-react";
import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
interface AddPolicyFormProps {
visible: boolean;
@ -340,10 +341,13 @@ const AddPolicyForm: React.FC<AddPolicyFormProps> = ({
{selectedMode === "flow_builder" && (
<Alert
message="You'll be redirected to the full-screen Flow Builder to design your policy logic visually."
type="info"
variant="info"
className="mt-4 border border-indigo-200 bg-indigo-50 dark:border-indigo-800 dark:bg-indigo-950"
/>
>
<AlertTitle>
You&apos;ll be redirected to the full-screen Flow Builder to design your policy logic visually.
</AlertTitle>
</Alert>
)}
<div className="mt-6 flex justify-end gap-2">
@ -457,35 +461,33 @@ const AddPolicyForm: React.FC<AddPolicyFormProps> = ({
</FormField>
{resolvedGuardrails.length > 0 && (
<Alert
message="Resolved Guardrails"
description={
<div>
<span className="mb-2 block text-muted-foreground">
These are the final guardrails that will be applied (including inheritance):
</span>
<div className="flex flex-wrap gap-1">
{resolvedGuardrails.map((g) => (
<Tag key={g} color="blue">
{g}
</Tag>
))}
</div>
<Alert variant="info">
<Info />
<AlertTitle>Resolved Guardrails</AlertTitle>
<AlertDescription>
<span className="mb-2 block text-muted-foreground">
These are the final guardrails that will be applied (including inheritance):
</span>
<div className="flex flex-wrap gap-1">
{resolvedGuardrails.map((g) => (
<Tag key={g} color="blue">
{g}
</Tag>
))}
</div>
}
type="info"
showIcon
/>
</AlertDescription>
</Alert>
)}
<SectionHeading label="Conditions (Optional)" />
<Alert
message="Model Scope"
description="By default, this policy will run on all models. You can optionally restrict it to specific models below."
type="info"
showIcon
/>
<Alert variant="info">
<Info />
<AlertTitle>Model Scope</AlertTitle>
<AlertDescription>
By default, this policy will run on all models. You can optionally restrict it to specific models below.
</AlertDescription>
</Alert>
<div role="group" className="flex w-full flex-col gap-3">
<span className="text-sm leading-snug font-medium text-foreground">Model Condition Type</span>

View file

@ -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<PolicyTestPanelProps> = ({ accessToken }) => {
)}
{hasSearched && !result && !isLoading && (
<Alert message="Error" description="Failed to resolve policies. Check the proxy logs." type="error" showIcon />
<Alert variant="error">
<CircleAlert />
<AlertTitle>Error</AlertTitle>
<AlertDescription>Failed to resolve policies. Check the proxy logs.</AlertDescription>
</Alert>
)}
</div>
);

View file

@ -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<CreateVectorStoreProps> = ({ accessToken, onSu
{/* Success Message */}
{ingestResults.length > 0 && (
<Alert
message="Vector Store Created Successfully"
description={
<div>
<p>
<strong>Vector Store ID:</strong> {ingestResults[0]?.vector_store_id}
</p>
<p>
<strong>Documents Ingested:</strong> {ingestResults.length}
</p>
</div>
}
type="success"
showIcon
closable
/>
<Alert>
<CircleCheck />
<AlertTitle>Vector Store Created Successfully</AlertTitle>
<AlertDescription>
<p>
<strong>Vector Store ID:</strong> {ingestResults[0]?.vector_store_id}
</p>
<p>
<strong>Documents Ingested:</strong> {ingestResults.length}
</p>
</AlertDescription>
<AlertAction>
<Button variant="ghost" size="icon-sm" aria-label="Close" onClick={() => setIngestResults([])}>
<X />
</Button>
</AlertAction>
</Alert>
)}
</div>
</TooltipProvider>

View file

@ -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<S3VectorsConfigProps> = ({ accessToken, provider
return (
<TooltipProvider>
<Alert
message="AWS S3 Vectors Setup"
description={
<div>
<p>AWS S3 Vectors allows you to store and query vector embeddings directly in S3:</p>
<ul style={{ marginLeft: "16px", marginTop: "8px" }}>
<li>Vector buckets and indexes will be automatically created if they don&apos;t exist</li>
<li>Vector dimensions are auto-detected from your selected embedding model</li>
<li>Ensure your AWS credentials have permissions for S3 Vectors operations</li>
<li>
Learn more:{" "}
<a
href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-vector-buckets.html"
target="_blank"
rel="noopener noreferrer"
>
AWS S3 Vectors Documentation
</a>
</li>
</ul>
</div>
}
type="info"
showIcon
style={{ marginBottom: "16px" }}
/>
<Alert variant="info" className="mb-4">
<Info />
<AlertTitle>AWS S3 Vectors Setup</AlertTitle>
<AlertDescription>
<p>AWS S3 Vectors allows you to store and query vector embeddings directly in S3:</p>
<ul style={{ marginLeft: "16px", marginTop: "8px" }}>
<li>Vector buckets and indexes will be automatically created if they don&apos;t exist</li>
<li>Vector dimensions are auto-detected from your selected embedding model</li>
<li>Ensure your AWS credentials have permissions for S3 Vectors operations</li>
<li>
Learn more:{" "}
<a
href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/s3-vector-buckets.html"
target="_blank"
rel="noopener noreferrer"
>
AWS S3 Vectors Documentation
</a>
</li>
</ul>
</AlertDescription>
</Alert>
<Field data-invalid={bucketNameError !== undefined || undefined}>
<FieldLabel htmlFor="s3-vector-bucket-name">

View file

@ -1,6 +1,7 @@
import React, { useState, useEffect } from "react";
import { Modal, Alert } from "antd";
import { CircleHelp, Eye, EyeOff } from "lucide-react";
import { Modal } from "antd";
import { CircleHelp, Eye, EyeOff, Info } from "lucide-react";
import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { useWatch } from "react-hook-form";
import { z } from "zod/v4";
import { CredentialItem, vectorStoreCreateCall } from "@/components/networking";
@ -310,142 +311,129 @@ const VectorStoreForm: React.FC<VectorStoreFormProps> = ({
</FormField>
{selectedProvider === "pg_vector" && (
<Alert
message="PG Vector Setup Required"
description={
<div>
<p>LiteLLM provides a server to connect to PG Vector. To use this provider:</p>
<ol style={{ marginLeft: "16px", marginTop: "8px", listStyleType: "decimal" }}>
<li>
Deploy the litellm-pgvector server from:{" "}
<a href="https://github.com/BerriAI/litellm-pgvector" target="_blank" rel="noopener noreferrer">
https://github.com/BerriAI/litellm-pgvector
</a>
</li>
<li>Configure your PostgreSQL database with pgvector extension</li>
<li>Start the server and note the API base URL and API key</li>
<li>Enter those details in the fields below</li>
</ol>
</div>
}
type="info"
showIcon
/>
<Alert variant="info">
<Info />
<AlertTitle>PG Vector Setup Required</AlertTitle>
<AlertDescription>
<p>LiteLLM provides a server to connect to PG Vector. To use this provider:</p>
<ol style={{ marginLeft: "16px", marginTop: "8px", listStyleType: "decimal" }}>
<li>
Deploy the litellm-pgvector server from:{" "}
<a href="https://github.com/BerriAI/litellm-pgvector" target="_blank" rel="noopener noreferrer">
https://github.com/BerriAI/litellm-pgvector
</a>
</li>
<li>Configure your PostgreSQL database with pgvector extension</li>
<li>Start the server and note the API base URL and API key</li>
<li>Enter those details in the fields below</li>
</ol>
</AlertDescription>
</Alert>
)}
{selectedProvider === "valkey" && (
<Alert
message="Valkey Setup Required"
description={
<div>
<p>
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:
</p>
<ol style={{ marginLeft: "16px", marginTop: "8px", listStyleType: "decimal" }}>
<li>
Your Valkey server has vector search enabled (the valkey-search module, included in the
valkey-bundle image and in AWS ElastiCache / MemoryDB for Valkey)
</li>
<li>
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
</li>
<li>
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
</li>
<li>
You know the field names your documents use for their text and their embedding. If they are not
&quot;text&quot; and &quot;embedding&quot;, set them below
</li>
</ol>
<p style={{ marginTop: "8px" }}>
When a query comes in, LiteLLM converts it to an embedding with the model below and returns the
closest matching documents from your index.
</p>
</div>
}
type="info"
showIcon
/>
<Alert variant="info">
<Info />
<AlertTitle>Valkey Setup Required</AlertTitle>
<AlertDescription>
<p>
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:
</p>
<ol style={{ marginLeft: "16px", marginTop: "8px", listStyleType: "decimal" }}>
<li>
Your Valkey server has vector search enabled (the valkey-search module, included in the
valkey-bundle image and in AWS ElastiCache / MemoryDB for Valkey)
</li>
<li>
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
</li>
<li>
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
</li>
<li>
You know the field names your documents use for their text and their embedding. If they are not
&quot;text&quot; and &quot;embedding&quot;, set them below
</li>
</ol>
<p style={{ marginTop: "8px" }}>
When a query comes in, LiteLLM converts it to an embedding with the model below and returns the
closest matching documents from your index.
</p>
</AlertDescription>
</Alert>
)}
{selectedProvider === "vertex_rag_engine" && (
<Alert
message="Vertex AI RAG Engine Setup"
description={
<div>
<p>To use Vertex AI RAG Engine:</p>
<p style={{ marginTop: "4px", fontStyle: "italic" }}>
Note: Google Cloud has renamed this to &quot;RAG Engine&quot; in its console — the steps below
still apply.
</p>
<ol style={{ marginLeft: "16px", marginTop: "8px", listStyleType: "decimal" }}>
<li>
Set up your Vertex AI RAG Engine corpus following the guide:{" "}
<a
href="https://cloud.google.com/vertex-ai/generative-ai/docs/rag-engine/rag-overview"
target="_blank"
rel="noopener noreferrer"
>
Vertex AI RAG Engine Overview
</a>
</li>
<li>Create a corpus in your Google Cloud project</li>
<li>
Note the corpus ID from the Vertex AI console (now labeled &quot;RAG Engine&quot; in Google
Cloud)
</li>
<li>Enter the corpus ID in the Vector Store ID field below</li>
</ol>
</div>
}
type="info"
showIcon
/>
<Alert variant="info">
<Info />
<AlertTitle>Vertex AI RAG Engine Setup</AlertTitle>
<AlertDescription>
<p>To use Vertex AI RAG Engine:</p>
<p style={{ marginTop: "4px", fontStyle: "italic" }}>
Note: Google Cloud has renamed this to &quot;RAG Engine&quot; in its console — the steps below still
apply.
</p>
<ol style={{ marginLeft: "16px", marginTop: "8px", listStyleType: "decimal" }}>
<li>
Set up your Vertex AI RAG Engine corpus following the guide:{" "}
<a
href="https://cloud.google.com/vertex-ai/generative-ai/docs/rag-engine/rag-overview"
target="_blank"
rel="noopener noreferrer"
>
Vertex AI RAG Engine Overview
</a>
</li>
<li>Create a corpus in your Google Cloud project</li>
<li>
Note the corpus ID from the Vertex AI console (now labeled &quot;RAG Engine&quot; in Google Cloud)
</li>
<li>Enter the corpus ID in the Vector Store ID field below</li>
</ol>
</AlertDescription>
</Alert>
)}
{selectedProvider === "vertex_ai/search_api" && (
<Alert
message="Vertex AI Search Setup"
description={
<div>
<p>To use Vertex AI Search (Discovery Engine):</p>
<p style={{ marginTop: "4px", fontStyle: "italic" }}>
Note: Google Cloud has renamed this to &quot;Agent Search&quot; in its console — the steps below
still apply.
</p>
<ol style={{ marginLeft: "16px", marginTop: "8px", listStyleType: "decimal" }}>
<li>
Enable the Discovery Engine API on your Google Cloud project and create a data store following
the guide:{" "}
<a
href="https://cloud.google.com/generative-ai-app-builder/docs/create-data-store-es"
target="_blank"
rel="noopener noreferrer"
style={{ textDecoration: "underline" }}
>
Create a Vertex AI Search data store
</a>
</li>
<li>Pick a supported location: global, us, or eu</li>
<li>
For most data store types (Cloud Storage, BigQuery, Media): copy the data store ID and enter it
in the Vector Store ID field below.
</li>
<li>
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 <strong>Engine ID</strong> 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&apos;t used in the GCP URL when Engine ID is set.
</li>
</ol>
</div>
}
type="info"
showIcon
/>
<Alert variant="info">
<Info />
<AlertTitle>Vertex AI Search Setup</AlertTitle>
<AlertDescription>
<p>To use Vertex AI Search (Discovery Engine):</p>
<p style={{ marginTop: "4px", fontStyle: "italic" }}>
Note: Google Cloud has renamed this to &quot;Agent Search&quot; in its console — the steps below
still apply.
</p>
<ol style={{ marginLeft: "16px", marginTop: "8px", listStyleType: "decimal" }}>
<li>
Enable the Discovery Engine API on your Google Cloud project and create a data store following the
guide:{" "}
<a
href="https://cloud.google.com/generative-ai-app-builder/docs/create-data-store-es"
target="_blank"
rel="noopener noreferrer"
style={{ textDecoration: "underline" }}
>
Create a Vertex AI Search data store
</a>
</li>
<li>Pick a supported location: global, us, or eu</li>
<li>
For most data store types (Cloud Storage, BigQuery, Media): copy the data store ID and enter it in
the Vector Store ID field below.
</li>
<li>
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 <strong>Engine ID</strong> 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&apos;t used in the GCP URL when Engine ID is set.
</li>
</ol>
</AlertDescription>
</Alert>
)}
<FormField

View file

@ -4,6 +4,7 @@ import { useLogin } from "@/app/(dashboard)/hooks/login/useLogin";
import { useUIConfig } from "@/app/(dashboard)/hooks/uiConfig/useUIConfig";
import LoadingScreen from "@/components/common_components/LoadingScreen";
import { exchangeLoginCode, getProxyBaseUrl, switchToWorkerUrl } from "@/components/networking";
import { Alert, AlertAction, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { PasswordInput } from "@/components/shared/PasswordInput";
import { Field, FieldGroup, FieldLabel } from "@/components/shared/form/field";
import { FormField } from "@/components/shared/form/FormField";
@ -17,8 +18,7 @@ import { useZodForm } from "@/lib/forms/useZodForm";
import { clearTokenCookies, getCookieFromDocument } from "@/utils/cookieUtils";
import { isJwtExpired } from "@/utils/jwtUtils";
import { consumeReturnUrl, getLoginUrl, getReturnUrl, isValidReturnUrl } from "@/utils/returnUrlUtils";
import { InfoCircleOutlined } from "@ant-design/icons";
import { Alert } from "antd";
import { CircleAlert, Info, TriangleAlert, X } from "lucide-react";
import { useRouter } from "next/navigation";
import { useEffect, useId, useState } from "react";
import { z } from "zod/v4";
@ -31,6 +31,28 @@ const loginSchema = z.object({
type LoginFormValues = z.infer<typeof loginSchema>;
function SsoEnabledNotice() {
const [isDismissed, setIsDismissed] = useState(false);
if (isDismissed) return null;
return (
<Alert variant="info" className="mt-4">
<Info />
<AlertTitle>
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{" "}
<code className="bg-muted px-1 py-0.5 rounded-sm text-xs">AUTO_REDIRECT_UI_LOGIN_TO_SSO=true</code> in your
environment configuration.
</AlertTitle>
<AlertAction>
<Button variant="ghost" size="icon-sm" aria-label="Close" onClick={() => setIsDismissed(true)}>
<X />
</Button>
</AlertAction>
</Alert>
);
}
function LoginPageContent() {
const [isLoading, setIsLoading] = useState(true);
const { data: uiConfig, isLoading: isConfigLoading } = useUIConfig();
@ -170,22 +192,19 @@ function LoginPageContent() {
<h2 className="text-3xl font-semibold text-foreground">🚅 LiteLLM</h2>
</div>
<Alert
message="Admin UI Disabled"
description={
<>
<p className="text-sm">
The Admin UI has been disabled by the administrator. To re-enable it, please update the following
environment variable:
</p>
<p className="mt-2 text-sm">
<code className="bg-muted px-1 py-0.5 rounded-sm text-xs">DISABLE_ADMIN_UI=False</code>
</p>
</>
}
type="warning"
showIcon
/>
<Alert variant="warning">
<TriangleAlert />
<AlertTitle>Admin UI Disabled</AlertTitle>
<AlertDescription>
<p className="text-sm">
The Admin UI has been disabled by the administrator. To re-enable it, please update the following
environment variable:
</p>
<p className="mt-2 text-sm">
<code className="bg-muted px-1 py-0.5 rounded-sm text-xs">DISABLE_ADMIN_UI=False</code>
</p>
</AlertDescription>
</Alert>
</div>
</CardContent>
</Card>
@ -209,31 +228,32 @@ function LoginPageContent() {
</div>
{!uiConfig?.hide_default_credentials_hint && (
<Alert
message="Default Credentials"
description={
<>
<p className="text-sm">
By default, Username is <code className="bg-muted px-1 py-0.5 rounded-sm text-xs">admin</code>{" "}
and Password is your set LiteLLM Proxy
<code className="bg-muted px-1 py-0.5 rounded-sm text-xs">MASTER_KEY</code>.
</p>
<p className="mt-2 text-sm">
Need to set UI credentials or SSO?{" "}
<a href="https://docs.litellm.ai/docs/proxy/ui" target="_blank" rel="noopener noreferrer">
Check the documentation
</a>
.
</p>
</>
}
type="info"
icon={<InfoCircleOutlined />}
showIcon
/>
<Alert variant="info">
<Info />
<AlertTitle>Default Credentials</AlertTitle>
<AlertDescription>
<p className="text-sm">
By default, Username is <code className="bg-muted px-1 py-0.5 rounded-sm text-xs">admin</code> and
Password is your set LiteLLM Proxy
<code className="bg-muted px-1 py-0.5 rounded-sm text-xs">MASTER_KEY</code>.
</p>
<p className="mt-2 text-sm">
Need to set UI credentials or SSO?{" "}
<a href="https://docs.litellm.ai/docs/proxy/ui" target="_blank" rel="noopener noreferrer">
Check the documentation
</a>
.
</p>
</AlertDescription>
</Alert>
)}
{error && <Alert message={error} type="error" showIcon />}
{error && (
<Alert variant="error">
<CircleAlert />
<AlertTitle>{error}</AlertTitle>
</Alert>
)}
<form onSubmit={form.handleSubmit(handleSubmit)}>
<FieldGroup>
@ -326,22 +346,7 @@ function LoginPageContent() {
</FieldGroup>
</form>
</div>
{uiConfig?.sso_configured && (
<Alert
type="info"
showIcon
closable
className="mt-4"
message={
<span>
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{" "}
<code className="bg-muted px-1 py-0.5 rounded-sm text-xs">AUTO_REDIRECT_UI_LOGIN_TO_SSO=true</code>{" "}
in your environment configuration.
</span>
}
/>
)}
{uiConfig?.sso_configured && <SsoEnabledNotice />}
</TooltipProvider>
</CardContent>
</Card>

View file

@ -1,6 +1,7 @@
import { Alert } from "antd";
import { CircleAlert, Info } from "lucide-react";
import React from "react";
import { z } from "zod/v4";
import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { PasswordInput } from "@/components/shared/PasswordInput";
import { Field, FieldLabel, FieldGroup } from "@/components/shared/form/field";
import { FormField } from "@/components/shared/form/FormField";
@ -46,11 +47,10 @@ export function OnboardingFormBody({ variant, userEmail, isPending, claimError,
</p>
{variant === "signup" && (
<Alert
className="mt-4"
type="info"
message="SSO"
description={
<Alert className="mt-4" variant="info">
<Info />
<AlertTitle>SSO</AlertTitle>
<AlertDescription>
<div className="flex justify-between items-center">
<span>SSO is under the Enterprise Tier.</span>
<a
@ -62,9 +62,8 @@ export function OnboardingFormBody({ variant, userEmail, isPending, claimError,
Get Free Trial
</a>
</div>
}
showIcon
/>
</AlertDescription>
</Alert>
)}
<form className="mt-10 mb-5" onSubmit={form.handleSubmit(handleSubmit)}>
@ -84,7 +83,12 @@ export function OnboardingFormBody({ variant, userEmail, isPending, claimError,
</FormField>
</FieldGroup>
{claimError && <Alert type="error" message={claimError} showIcon className="mt-6 mb-4" />}
{claimError && (
<Alert variant="error" className="mt-6 mb-4">
<CircleAlert />
<AlertTitle>{claimError}</AlertTitle>
</Alert>
)}
<div className="mt-10">
<Button type="submit" variant="outline" disabled={isPending}>

View file

@ -10,8 +10,9 @@ import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { Alert, Modal, Typography } from "antd";
import { ChevronRight, CircleHelp, UserPlus } from "lucide-react";
import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { Modal, Typography } from "antd";
import { ChevronRight, CircleHelp, Info, UserPlus } from "lucide-react";
import React, { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import TeamDropdown from "./common_components/team_dropdown";
@ -134,20 +135,16 @@ const labelWithHint = (label: string, hint: string): React.ReactNode => (
);
const EmailInvitationsNotice: React.FC = () => (
<Alert
message="Email invitations"
description={
<>
New users receive an email invite only when an email integration (SMTP, Resend, or SendGrid) is configured.{" "}
<Link href="https://docs.litellm.ai/docs/proxy/email" target="_blank">
Learn how to set up email notifications
</Link>
</>
}
type="info"
showIcon
className="mb-4"
/>
<Alert variant="info" className="mb-4">
<Info />
<AlertTitle>Email invitations</AlertTitle>
<AlertDescription>
New users receive an email invite only when an email integration (SMTP, Resend, or SendGrid) is configured.{" "}
<Link href="https://docs.litellm.ai/docs/proxy/email" target="_blank">
Learn how to set up email notifications
</Link>
</AlertDescription>
</Alert>
);
export const CreateUserButton: React.FC<CreateuserProps> = ({

View file

@ -3,9 +3,9 @@
import { useMCPSemanticFilterSettings } from "@/app/(dashboard)/hooks/mcpSemanticFilterSettings/useMCPSemanticFilterSettings";
import { useUpdateMCPSemanticFilterSettings } from "@/app/(dashboard)/hooks/mcpSemanticFilterSettings/useUpdateMCPSemanticFilterSettings";
import { toast } from "@/lib/toast";
import { Alert, Card, Col, Row, Skeleton } from "antd";
import { CheckCircleOutlined } from "@ant-design/icons";
import { CircleHelp, Save } from "lucide-react";
import { Card, Col, Row, Skeleton } from "antd";
import { CircleCheck, CircleHelp, Info, Save, X } from "lucide-react";
import { Alert, AlertAction, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { fetchAvailableModels, ModelGroup } from "@/components/llm_calls/fetch_models";
@ -177,40 +177,39 @@ export default function MCPSemanticFilterSettings({ accessToken }: MCPSemanticFi
{isLoading ? (
<Skeleton active />
) : isError ? (
<Alert
type="error"
message="Could not load MCP Semantic Filter settings"
description={error instanceof Error ? error.message : undefined}
style={{ marginBottom: 24 }}
/>
<Alert variant="error" className="mb-6">
<AlertTitle>Could not load MCP Semantic Filter settings</AlertTitle>
{error instanceof Error && <AlertDescription>{error.message}</AlertDescription>}
</Alert>
) : (
<>
<Alert
type="info"
message="Semantic Tool Filtering"
description="Filter MCP tools semantically based on query relevance. This reduces context window size and improves tool selection accuracy. Click 'Save Settings' to apply changes across all pods (takes effect within 10 seconds)."
showIcon
style={{ marginBottom: 24 }}
/>
<Alert variant="info" className="mb-6">
<Info />
<AlertTitle>Semantic Tool Filtering</AlertTitle>
<AlertDescription>
Filter MCP tools semantically based on query relevance. This reduces context window size and improves tool
selection accuracy. Click &apos;Save Settings&apos; to apply changes across all pods (takes effect within
10 seconds).
</AlertDescription>
</Alert>
{saveSuccess && (
<Alert
type="success"
message="Settings saved successfully"
icon={<CheckCircleOutlined />}
showIcon
closable
style={{ marginBottom: 16 }}
/>
<Alert className="mb-4">
<CircleCheck />
<AlertTitle>Settings saved successfully</AlertTitle>
<AlertAction>
<Button variant="ghost" size="icon-sm" aria-label="Close" onClick={() => setSaveSuccess(false)}>
<X />
</Button>
</AlertAction>
</Alert>
)}
{updateError && (
<Alert
type="error"
message="Could not update settings"
description={updateError instanceof Error ? updateError.message : undefined}
style={{ marginBottom: 16 }}
/>
<Alert variant="error" className="mb-4">
<AlertTitle>Could not update settings</AlertTitle>
{updateError instanceof Error && <AlertDescription>{updateError.message}</AlertDescription>}
</Alert>
)}
<Row gutter={24}>

View file

@ -5,7 +5,9 @@ import { all_admin_roles, isUserTeamAdminForAnyTeam } from "@/utils/roles";
import { modelCreationScope } from "@/utils/modelPermissions";
import { Switch } from "@/components/ui/switch";
import { Field, FieldLabel } from "@/components/shared/form/field";
import { Select as AntdSelect, Card, Col, Modal, Row, Tooltip, Typography, Alert } from "antd";
import { Select as AntdSelect, Card, Col, Modal, Row, Tooltip, Typography } from "antd";
import { Info } from "lucide-react";
import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { Button } from "@/components/ui/button";
import type { UploadProps } from "antd/es/upload";
import React, { useEffect, useMemo, useState } from "react";
@ -168,13 +170,13 @@ const AddModelForm: React.FC<AddModelFormProps> = ({
)}
</MountedFormField>
{!teamAdminSelectedTeam && (
<Alert
message="Team Selection Required"
description="As a team admin, you need to select your team first before adding models."
type="info"
showIcon
className="mb-4"
/>
<Alert variant="info" className="mb-4">
<Info />
<AlertTitle>Team Selection Required</AlertTitle>
<AlertDescription>
As a team admin, you need to select your team first before adding models.
</AlertDescription>
</Alert>
)}
</>
)}

View file

@ -1,8 +1,9 @@
"use client";
import React, { useState } from "react";
import { Modal, Alert } from "antd";
import { CircleHelp, Plug } from "lucide-react";
import { Modal } from "antd";
import { CircleHelp, Info, Plug } from "lucide-react";
import { Alert, AlertDescription, AlertTitle } from "@/components/shared/Alert";
import { useWatch } from "react-hook-form";
import { z } from "zod/v4";
@ -156,13 +157,14 @@ const AddPassThroughEndpoint: React.FC<AddFallbacksProps> = ({
}}
>
<div className="mt-6">
<Alert
message="What is a Pass-Through Endpoint?"
description="Route requests from your LiteLLM proxy to any external API. Perfect for custom models, image generation APIs, or any service you want to proxy through LiteLLM."
type="info"
showIcon
className="mb-6"
/>
<Alert variant="info" className="mb-6">
<Info />
<AlertTitle>What is a Pass-Through Endpoint?</AlertTitle>
<AlertDescription>
Route requests from your LiteLLM proxy to any external API. Perfect for custom models, image generation
APIs, or any service you want to proxy through LiteLLM.
</AlertDescription>
</Alert>
<form onSubmit={form.handleSubmit(addPassThrough)} className="space-y-6">
<Card className="block p-5">

View file

@ -1,6 +1,6 @@
import React 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 GuardrailSelector from "../guardrails/GuardrailSelector";
import { TagsInput } from "@/app/(dashboard)/guardrails/_components/content_filter/TagsInput";
@ -67,21 +67,20 @@ const PassThroughGuardrailsSection: React.FC<PassThroughGuardrailsSectionProps>
endpoints.
</p>
<Alert
message={
<span>
Field-Level Targeting{" "}
<a
href="https://docs.litellm.ai/docs/proxy/pass_through_guardrails#field-level-targeting"
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 underline hover:text-blue-800"
>
(Learn More)
</a>
</span>
}
description={
<Alert variant="info" className="mb-4">
<Info />
<AlertTitle>
Field-Level Targeting{" "}
<a
href="https://docs.litellm.ai/docs/proxy/pass_through_guardrails#field-level-targeting"
target="_blank"
rel="noopener noreferrer"
className="text-blue-600 underline hover:text-blue-800"
>
(Learn More)
</a>
</AlertTitle>
<AlertDescription>
<div className="space-y-2">
<div>
Optionally specify which fields to check. If left empty, the entire request/response is sent to the
@ -100,11 +99,8 @@ const PassThroughGuardrailsSection: React.FC<PassThroughGuardrailsSectionProps>
</div>
</div>
</div>
}
type="info"
showIcon
className="mb-4"
/>
</AlertDescription>
</Alert>
<Field>
<FieldLabel htmlFor="pass-through-guardrails">

View file

@ -74,7 +74,7 @@ describe("UserSearchModal", () => {
expect(notice).toHaveTextContent(/users that already exist/i);
expect(notice).toHaveTextContent(/ask a proxy admin to create their account first/i);
// info, not warning: a warning here would read as an error state on an empty form
expect(notice.className).toMatch(/ant-alert-info/);
expect(notice.className).toMatch(/text-info/);
});
});

View file

@ -1,5 +1,7 @@
import { useState } from "react";
import { Modal, Alert } from "antd";
import { Modal } from "antd";
import { Info } from "lucide-react";
import { Alert, AlertTitle } from "@/components/shared/Alert";
import { UserAddOutlined } from "@ant-design/icons";
import { useDebouncedCallback } from "@tanstack/react-pacer/debouncer";
import { useForm } from "react-hook-form";
@ -202,13 +204,13 @@ const UserSearchModal: React.FC<UserSearchModalProps> = ({
<Modal title={title} open={isVisible} onCancel={handleClose} footer={null} width={800} maskClosable={!isSubmitting}>
<TooltipProvider>
<form onSubmit={form.handleSubmit(handleSubmit)} noValidate>
<Alert
type="info"
showIcon
className="mb-4"
message="Search selects from users that already exist. To add someone new, ask a proxy admin to create their account first."
data-testid="member-existing-users-notice"
/>
<Alert variant="info" className="mb-4" data-testid="member-existing-users-notice">
<Info />
<AlertTitle>
Search selects from users that already exist. To add someone new, ask a proxy admin to create their
account first.
</AlertTitle>
</Alert>
<FieldGroup>
<FormField control={form.control} name="user_email" label="Email">

View file

@ -1,8 +1,9 @@
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { CheckOutlined, CopyOutlined, SyncOutlined } from "@ant-design/icons";
import { Alert, Modal, Space } from "antd";
import { Alert, AlertTitle } from "@/components/shared/Alert";
import { Modal, Space } from "antd";
import { Button } from "@/components/ui/button";
import { CircleHelp } from "lucide-react";
import { CircleHelp, TriangleAlert } from "lucide-react";
import React, { useEffect, useMemo, useState } from "react";
import { useWatch } from "react-hook-form";
import { CopyToClipboard } from "react-copy-to-clipboard";
@ -184,7 +185,10 @@ export function RegenerateKeyModal({ selectedToken, visible, onClose, onKeyUpdat
>
{regeneratedKey ? (
<div className="flex flex-col gap-4">
<Alert type="warning" showIcon message="Save it now, you will not see it again" />
<Alert variant="warning">
<TriangleAlert />
<AlertTitle>Save it now, you will not see it again</AlertTitle>
</Alert>
<div className="flex flex-col gap-0.5">
<span className="text-xs text-muted-foreground">Key Alias</span>

View file

@ -1,10 +1,12 @@
import { Alert, Modal, Typography } from "antd";
import { Modal, Typography } from "antd";
import { TriangleAlert } from "lucide-react";
import { useState } from "react";
import { z } from "zod/v4";
import { modelPatchUpdateCall } from "./networking";
import { toast } from "@/lib/toast";
import { FieldGroup } from "@/components/shared/form/field";
import { FormField } from "@/components/shared/form/FormField";
import { Alert, AlertTitle } from "@/components/shared/Alert";
import { PasswordInput } from "@/components/shared/PasswordInput";
import { Button } from "@/components/ui/button";
import { UiLoadingSpinner } from "@/components/ui/ui-loading-spinner";
@ -74,12 +76,14 @@ export default function UpdateModelCredentialsModal({
Update this model&apos;s API key. Only the new key is sent; the rest of the deployment configuration is left
untouched.
</Text>
<Alert
type="warning"
showIcon
className="mb-4"
message="Only the API key is rotated here. Models that authenticate with an Azure AD token, AWS credentials, or a Vertex service-account JSON aren't supported yet; update those from the model's LiteLLM Params for now."
/>
<Alert variant="warning" className="mb-4">
<TriangleAlert />
<AlertTitle>
Only the API key is rotated here. Models that authenticate with an Azure AD token, AWS credentials, or a
Vertex service-account JSON aren&apos;t supported yet; update those from the model&apos;s LiteLLM Params for
now.
</AlertTitle>
</Alert>
<form onSubmit={form.handleSubmit(handleSubmit)}>
<FieldGroup>
<FormField control={form.control} name="api_key" label="New API Key">