From 6cf0e7c27ec38527bd126175d329b9ddc6af252e Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Thu, 19 Feb 2026 18:15:44 -0800 Subject: [PATCH] ui refactor --- .../policies/guardrail_selection_modal.tsx | 291 ++++-------------- .../policies/partner_guardrail_card.tsx | 186 +++++++++++ .../components/policies/policy_templates.tsx | 5 +- .../src/components/policies/types.ts | 42 +++ 4 files changed, 289 insertions(+), 235 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/policies/partner_guardrail_card.tsx diff --git a/ui/litellm-dashboard/src/components/policies/guardrail_selection_modal.tsx b/ui/litellm-dashboard/src/components/policies/guardrail_selection_modal.tsx index 535e96114ec..e3f64bab552 100644 --- a/ui/litellm-dashboard/src/components/policies/guardrail_selection_modal.tsx +++ b/ui/litellm-dashboard/src/components/policies/guardrail_selection_modal.tsx @@ -1,29 +1,15 @@ import React, { useState, useEffect } from "react"; -import { Modal, Checkbox, Button, Divider, Tag, Select, Switch, Tooltip } from "antd"; +import { Modal, Checkbox, Button, Divider, Tag, Tooltip } from "antd"; import { CheckCircleOutlined, InfoCircleOutlined } from "@ant-design/icons"; import { credentialListCall } from "../networking"; +import PartnerGuardrailCard from "./partner_guardrail_card"; +import { + GuardrailInfo, + PartnerGuardrailConfig, + PartnerGuardrailSelection, +} from "./types"; -interface GuardrailInfo { - guardrail_name: string; - description: string; - alreadyExists: boolean; - definition: any; -} - -interface PartnerGuardrailConfig { - provider: string; - label: string; - description: string; - credential_provider: string; - provision_config: Record; -} - -export interface PartnerGuardrailSelection { - provider: string; - credential_name: string; - provision_config: Record; - aws_region_name?: string; -} +export type { PartnerGuardrailSelection } from "./types"; interface GuardrailSelectionModalProps { visible: boolean; @@ -39,181 +25,6 @@ interface GuardrailSelectionModalProps { accessToken?: string; } -const AWS_REGIONS = [ - "us-east-1", - "us-east-2", - "us-west-2", - "eu-west-1", - "eu-west-2", - "eu-central-1", - "ap-southeast-1", - "ap-southeast-2", - "ap-northeast-1", -]; - -const partnerProviderLogoMap: Record = { - bedrock: "../ui/assets/logos/bedrock.svg", -}; - -const ProviderLogo: React.FC<{ provider: string }> = ({ provider }) => { - const logoSrc = partnerProviderLogoMap[provider]; - if (logoSrc) { - return ( - {provider} { e.currentTarget.style.display = "none"; }} - /> - ); - } - return ( -
- ? -
- ); -}; - -const ProvisionConfigTags: React.FC<{ config: Record }> = ({ config }) => ( -
- {config.topicPolicyConfig && ( - - {config.topicPolicyConfig.topicsConfig?.length || 0} topic policy(s) - - )} - {config.contentPolicyConfig && ( - - {config.contentPolicyConfig.filtersConfig?.length || 0} content filter(s) - - )} - {config.wordPolicyConfig && ( - - word policy - - )} - {config.sensitiveInformationPolicyConfig && ( - - PII detection - - )} -
-); - -const CredentialOption: React.FC<{ - credential: any; - provider?: string; - showProviderLogo?: boolean; -}> = ({ credential, provider, showProviderLogo }) => ( -
- {showProviderLogo && provider && } - {credential.credential_name} - {!showProviderLogo && credential.credential_info?.custom_llm_provider && ( - {credential.credential_info.custom_llm_provider} - )} -
-); - -const PartnerGuardrailCard: React.FC<{ - pg: PartnerGuardrailConfig; - isEnabled: boolean; - onToggle: (checked: boolean) => void; - selectedCredential?: string; - onCredentialChange: (value: string) => void; - selectedRegion?: string; - onRegionChange: (value: string) => void; - providerCredentials: any[]; - allCredentials: any[]; - credentialsLoading: boolean; -}> = ({ - pg, - isEnabled, - onToggle, - selectedCredential, - onCredentialChange, - selectedRegion, - onRegionChange, - providerCredentials, - allCredentials, - credentialsLoading, -}) => { - const credentialOptions = (providerCredentials.length > 0 ? providerCredentials : allCredentials).map( - (c: any) => ({ - value: c.credential_name, - label: ( - 0} - /> - ), - }) - ); - - return ( -
-
-
- -
-
-
- - {pg.label} -
-

{pg.description}

- - {isEnabled && ( -
-
- - ({ value: r, label: r }))} - allowClear - /> -
- )} - - -
- )} -
-
-
- ); -}; - const GuardrailSelectionModal: React.FC = ({ visible, template, @@ -228,23 +39,21 @@ const GuardrailSelectionModal: React.FC = ({ new Set() ); - // Partner guardrail state - const [partnerEnabled, setPartnerEnabled] = useState>( - {} - ); + const [partnerEnabled, setPartnerEnabled] = useState< + Record + >({}); const [partnerCredentials, setPartnerCredentials] = useState< Record >({}); - const [partnerRegions, setPartnerRegions] = useState>( - {} - ); + const [partnerRegions, setPartnerRegions] = useState< + Record + >({}); const [credentials, setCredentials] = useState([]); const [credentialsLoading, setCredentialsLoading] = useState(false); - const partnerGuardrails: PartnerGuardrailConfig[] = + const partnerGuardrailConfigs: PartnerGuardrailConfig[] = template?.partnerGuardrails || []; - // Prepare guardrail info with existence status const guardrailsInfo: GuardrailInfo[] = ( template?.guardrailDefinitions || [] ).map((def: any) => ({ @@ -254,22 +63,18 @@ const GuardrailSelectionModal: React.FC = ({ definition: def, })); - // Initialize selection: select only new guardrails by default useEffect(() => { if (visible && template) { const newGuardrails = guardrailsInfo .filter((g) => !g.alreadyExists) .map((g) => g.guardrail_name); setSelectedGuardrails(new Set(newGuardrails)); - - // Reset partner state setPartnerEnabled({}); setPartnerCredentials({}); setPartnerRegions({}); } }, [visible, template]); - // Fetch credentials when a partner guardrail is enabled useEffect(() => { const anyEnabled = Object.values(partnerEnabled).some(Boolean); if (anyEnabled && accessToken && credentials.length === 0) { @@ -315,9 +120,8 @@ const GuardrailSelectionModal: React.FC = ({ .filter((g) => selectedGuardrails.has(g.guardrail_name)) .map((g) => g.definition); - // Build partner guardrail selections const partnerSelections: PartnerGuardrailSelection[] = []; - for (const pg of partnerGuardrails) { + for (const pg of partnerGuardrailConfigs) { if (partnerEnabled[pg.provider] && partnerCredentials[pg.provider]) { partnerSelections.push({ provider: pg.provider, @@ -334,7 +138,6 @@ const GuardrailSelectionModal: React.FC = ({ ); }; - // Filter credentials by provider type const getCredentialsForProvider = (credentialProvider: string) => { return credentials.filter((c: any) => { const provider = @@ -438,7 +241,9 @@ const GuardrailSelectionModal: React.FC = ({ ) : ( handleToggle(guardrail.guardrail_name)} /> )} @@ -457,23 +262,28 @@ const GuardrailSelectionModal: React.FC = ({

{guardrail.description}

- - {/* Show guardrail type and mode */}
- {guardrail.definition?.litellm_params?.guardrail || "unknown"} + {guardrail.definition?.litellm_params?.guardrail || + "unknown"} {guardrail.definition?.litellm_params?.mode || "unknown"} {guardrail.definition?.litellm_params?.patterns && ( - {guardrail.definition.litellm_params.patterns.length} pattern(s) + { + guardrail.definition.litellm_params.patterns.length + }{" "} + pattern(s) )} {guardrail.definition?.litellm_params?.categories && ( - {guardrail.definition.litellm_params.categories.length} category/categories + { + guardrail.definition.litellm_params.categories.length + }{" "} + category/categories )}
@@ -500,7 +310,8 @@ const GuardrailSelectionModal: React.FC = ({
- AI-Discovered Competitors ({template.discoveredCompetitors.length}) + AI-Discovered Competitors ( + {template.discoveredCompetitors.length})
@@ -511,14 +322,15 @@ const GuardrailSelectionModal: React.FC = ({ ))}

- These competitor names will be automatically blocked by the competitor-name-blocker guardrail. + These competitor names will be automatically blocked by the + competitor-name-blocker guardrail.

)} {/* Partner Guardrails Section */} - {partnerGuardrails.length > 0 && ( + {partnerGuardrailConfigs.length > 0 && ( <>
@@ -534,23 +346,36 @@ const GuardrailSelectionModal: React.FC = ({
- {partnerGuardrails.map((pg) => ( + {partnerGuardrailConfigs.map((pg) => ( - setPartnerEnabled((prev) => ({ ...prev, [pg.provider]: checked })) + setPartnerEnabled((prev) => ({ + ...prev, + [pg.provider]: checked, + })) + } + selectedCredential={ + partnerCredentials[pg.provider] || undefined } - selectedCredential={partnerCredentials[pg.provider] || undefined} onCredentialChange={(value) => - setPartnerCredentials((prev) => ({ ...prev, [pg.provider]: value })) + setPartnerCredentials((prev) => ({ + ...prev, + [pg.provider]: value, + })) } selectedRegion={partnerRegions[pg.provider] || undefined} onRegionChange={(value) => - setPartnerRegions((prev) => ({ ...prev, [pg.provider]: value })) + setPartnerRegions((prev) => ({ + ...prev, + [pg.provider]: value, + })) } - providerCredentials={getCredentialsForProvider(pg.credential_provider)} + providerCredentials={getCredentialsForProvider( + pg.credential_provider + )} allCredentials={credentials} credentialsLoading={credentialsLoading} /> @@ -565,7 +390,9 @@ const GuardrailSelectionModal: React.FC = ({
{selectedCount > 0 ? (

- {selectedCount}{" "} + + {selectedCount} + {" "} guardrail{selectedCount > 1 ? "s" : ""} will be created {Object.values(partnerEnabled).some(Boolean) && ( @@ -576,11 +403,13 @@ const GuardrailSelectionModal: React.FC = ({

) : existingCount > 0 ? (

- All guardrails already exist. You can proceed to use this template. + All guardrails already exist. You can proceed to use this + template.

) : (

- Select at least one guardrail to create, or click "Use Template" to proceed without creating new guardrails. + Select at least one guardrail to create, or click "Use + Template" to proceed without creating new guardrails.

)}
diff --git a/ui/litellm-dashboard/src/components/policies/partner_guardrail_card.tsx b/ui/litellm-dashboard/src/components/policies/partner_guardrail_card.tsx new file mode 100644 index 00000000000..fc03afc9f61 --- /dev/null +++ b/ui/litellm-dashboard/src/components/policies/partner_guardrail_card.tsx @@ -0,0 +1,186 @@ +import React from "react"; +import { Tag, Select, Switch } from "antd"; +import { + AWS_REGIONS, + PartnerGuardrailConfig, + partnerProviderLogoMap, +} from "./types"; + +export const ProviderLogo: React.FC<{ provider: string }> = ({ provider }) => { + const logoSrc = partnerProviderLogoMap[provider]; + if (logoSrc) { + return ( + {provider} { + e.currentTarget.style.display = "none"; + }} + /> + ); + } + return ( +
+ ? +
+ ); +}; + +const ProvisionConfigTags: React.FC<{ config: Record }> = ({ + config, +}) => ( +
+ {config.topicPolicyConfig && ( + + {config.topicPolicyConfig.topicsConfig?.length || 0} topic policy(s) + + )} + {config.contentPolicyConfig && ( + + {config.contentPolicyConfig.filtersConfig?.length || 0} content + filter(s) + + )} + {config.wordPolicyConfig && ( + + word policy + + )} + {config.sensitiveInformationPolicyConfig && ( + + PII detection + + )} +
+); + +const CredentialOption: React.FC<{ + credential: any; + provider?: string; + showProviderLogo?: boolean; +}> = ({ credential, provider, showProviderLogo }) => ( +
+ {showProviderLogo && provider && } + {credential.credential_name} + {!showProviderLogo && credential.credential_info?.custom_llm_provider && ( + + {credential.credential_info.custom_llm_provider} + + )} +
+); + +export interface PartnerGuardrailCardProps { + pg: PartnerGuardrailConfig; + isEnabled: boolean; + onToggle: (checked: boolean) => void; + selectedCredential?: string; + onCredentialChange: (value: string) => void; + selectedRegion?: string; + onRegionChange: (value: string) => void; + providerCredentials: any[]; + allCredentials: any[]; + credentialsLoading: boolean; +} + +const PartnerGuardrailCard: React.FC = ({ + pg, + isEnabled, + onToggle, + selectedCredential, + onCredentialChange, + selectedRegion, + onRegionChange, + providerCredentials, + allCredentials, + credentialsLoading, +}) => { + const credentialOptions = ( + providerCredentials.length > 0 ? providerCredentials : allCredentials + ).map((c: any) => ({ + value: c.credential_name, + label: ( + 0} + /> + ), + })); + + return ( +
+
+
+ +
+
+
+ + + {pg.label} + +
+

{pg.description}

+ + {isEnabled && ( +
+
+ + ({ value: r, label: r }))} + allowClear + /> +
+ )} + + +
+ )} +
+
+
+ ); +}; + +export default PartnerGuardrailCard; diff --git a/ui/litellm-dashboard/src/components/policies/policy_templates.tsx b/ui/litellm-dashboard/src/components/policies/policy_templates.tsx index 20d91a4eae7..9ca4cc307b8 100644 --- a/ui/litellm-dashboard/src/components/policies/policy_templates.tsx +++ b/ui/litellm-dashboard/src/components/policies/policy_templates.tsx @@ -8,6 +8,7 @@ import { CheckCircleIcon, } from "@heroicons/react/outline"; import { getPolicyTemplates } from "../networking"; +import { partnerProviderLogoMap } from "./types"; interface PartnerGuardrailInfo { provider: string; @@ -28,10 +29,6 @@ interface PolicyTemplateCardProps { onUseTemplate: () => void; } -const partnerProviderLogoMap: Record = { - bedrock: "../ui/assets/logos/bedrock.svg", -}; - const PolicyTemplateCard: React.FC = ({ title, description, diff --git a/ui/litellm-dashboard/src/components/policies/types.ts b/ui/litellm-dashboard/src/components/policies/types.ts index a1036c013eb..618195cf20d 100644 --- a/ui/litellm-dashboard/src/components/policies/types.ts +++ b/ui/litellm-dashboard/src/components/policies/types.ts @@ -99,3 +99,45 @@ export interface PipelineTestResult { error_message: string | null; modify_response_message: string | null; } + +// --------------------------------------------------------------------------- +// Guardrail selection modal types +// --------------------------------------------------------------------------- + +export interface GuardrailInfo { + guardrail_name: string; + description: string; + alreadyExists: boolean; + definition: any; +} + +export interface PartnerGuardrailConfig { + provider: string; + label: string; + description: string; + credential_provider: string; + provision_config: Record; +} + +export interface PartnerGuardrailSelection { + provider: string; + credential_name: string; + provision_config: Record; + aws_region_name?: string; +} + +export const AWS_REGIONS = [ + "us-east-1", + "us-east-2", + "us-west-2", + "eu-west-1", + "eu-west-2", + "eu-central-1", + "ap-southeast-1", + "ap-southeast-2", + "ap-northeast-1", +]; + +export const partnerProviderLogoMap: Record = { + bedrock: "../ui/assets/logos/bedrock.svg", +};