mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
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:
parent
a613773fca
commit
3d51eb378a
22 changed files with 460 additions and 443 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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'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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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'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'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">
|
||||
|
|
|
|||
|
|
@ -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
|
||||
"text" and "embedding", 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
|
||||
"text" and "embedding", 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 "RAG Engine" 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 "RAG Engine" 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 "RAG Engine" 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 "RAG Engine" 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 "Agent Search" 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'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 "Agent Search" 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't used in the GCP URL when Engine ID is set.
|
||||
</li>
|
||||
</ol>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<FormField
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}>
|
||||
|
|
|
|||
|
|
@ -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> = ({
|
||||
|
|
|
|||
|
|
@ -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 'Save Settings' 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}>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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/);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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'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't supported yet; update those from the model's LiteLLM Params for
|
||||
now.
|
||||
</AlertTitle>
|
||||
</Alert>
|
||||
<form onSubmit={form.handleSubmit(handleSubmit)}>
|
||||
<FieldGroup>
|
||||
<FormField control={form.control} name="api_key" label="New API Key">
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue