From 4341b8a5b94ad36c30683a74ae0ffc680875f701 Mon Sep 17 00:00:00 2001 From: Krrish Dholakia Date: Wed, 11 Feb 2026 20:57:21 -0800 Subject: [PATCH] feat: create missing guardrails, working policy templates --- litellm/proxy/policy_templates.json | 294 ++++++++++++------ .../policies/guardrail_selection_modal.tsx | 244 +++++++++++++++ .../src/components/policies/index.tsx | 101 +++++- .../components/policies/policy_templates.tsx | 2 +- 4 files changed, 533 insertions(+), 108 deletions(-) create mode 100644 ui/litellm-dashboard/src/components/policies/guardrail_selection_modal.tsx diff --git a/litellm/proxy/policy_templates.json b/litellm/proxy/policy_templates.json index 96d7a2192c7..8eff8ead40c 100644 --- a/litellm/proxy/policy_templates.json +++ b/litellm/proxy/policy_templates.json @@ -1,8 +1,8 @@ [ { - "id": "advanced-pii-protection", + "id": "advanced-au-pii-protection", "title": "Advanced PII Protection (Australia)", - "description": "Comprehensive PII detection and masking policy. Protects Australian-specific identifiers, international employee data, financial information, credentials, protected class information, and industry-specific sensitive data.", + "description": "Comprehensive PII detection and masking for Australia. Protects Australian-specific identifiers, international employee data, financial information, credentials, protected class information, and industry-specific sensitive data.", "icon": "ShieldCheckIcon", "iconColor": "text-purple-500", "iconBg": "bg-purple-50", @@ -14,26 +14,192 @@ "financial-pii", "credentials-api-keys", "network-infrastructure-pii", - "protected-class-information", - "mining-industry-custom-pii" + "protected-class-information" ], "complexity": "High", - "templateData": { - "policy_name": "fortescue-pii-protection", - "description": "Comprehensive PII detection and masking policy for Fortescue. Protects Australian-specific identifiers, international employee data, financial information, credentials, protected class information, and mining industry-specific sensitive data.", - "guardrails": { - "add": [ - "au-pii-tax-identifiers", - "au-pii-passports", - "international-pii-identifiers", - "contact-information-pii", - "financial-pii", - "credentials-api-keys", - "network-infrastructure-pii", - "protected-class-information", - "mining-industry-custom-pii" - ] + "guardrailDefinitions": [ + { + "guardrail_name": "au-pii-tax-identifiers", + "litellm_params": { + "guardrail": "litellm_content_filter", + "mode": "pre_call", + "patterns": [ + { + "pattern_type": "prebuilt", + "pattern_name": "au_tfn", + "action": "MASK" + }, + { + "pattern_type": "prebuilt", + "pattern_name": "au_abn", + "action": "MASK" + }, + { + "pattern_type": "prebuilt", + "pattern_name": "au_medicare", + "action": "MASK" + } + ], + "pattern_redaction_format": "[{pattern_name}_REDACTED]" + }, + "guardrail_info": { + "description": "Masks Australian Tax File Numbers, Business Numbers, and Medicare Numbers" + } + }, + { + "guardrail_name": "au-pii-passports", + "litellm_params": { + "guardrail": "litellm_content_filter", + "mode": "pre_call", + "patterns": [ + { + "pattern_type": "prebuilt", + "pattern_name": "passport_australia", + "action": "MASK" + } + ], + "pattern_redaction_format": "[PASSPORT_REDACTED]" + }, + "guardrail_info": { + "description": "Masks Australian passport numbers" + } + }, + { + "guardrail_name": "international-pii-identifiers", + "litellm_params": { + "guardrail": "litellm_content_filter", + "mode": "pre_call", + "patterns": [ + {"pattern_type": "prebuilt", "pattern_name": "us_ssn", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "us_ssn_no_dash", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "passport_us", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "passport_uk", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "passport_germany", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "passport_france", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "passport_netherlands", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "nl_bsn_contextual", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "passport_china", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "passport_india", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "passport_japan", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "passport_canada", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "br_cpf", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "br_cpf_unformatted", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "br_rg", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "br_cnpj", "action": "MASK"} + ], + "pattern_redaction_format": "[{pattern_name}_REDACTED]" + }, + "guardrail_info": { + "description": "Masks international PII identifiers including passports and national IDs" + } + }, + { + "guardrail_name": "contact-information-pii", + "litellm_params": { + "guardrail": "litellm_content_filter", + "mode": "pre_call", + "patterns": [ + {"pattern_type": "prebuilt", "pattern_name": "email", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "us_phone", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "br_phone_landline", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "br_phone_mobile", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "street_address", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "br_cep", "action": "MASK"} + ], + "pattern_redaction_format": "[{pattern_name}_REDACTED]" + }, + "guardrail_info": { + "description": "Masks contact information including emails, phone numbers, and addresses" + } + }, + { + "guardrail_name": "financial-pii", + "litellm_params": { + "guardrail": "litellm_content_filter", + "mode": "pre_call", + "patterns": [ + {"pattern_type": "prebuilt", "pattern_name": "visa", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "mastercard", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "amex", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "discover", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "credit_card", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "iban", "action": "MASK"} + ], + "pattern_redaction_format": "[{pattern_name}_REDACTED]" + }, + "guardrail_info": { + "description": "Masks financial information including credit cards and bank account numbers" + } + }, + { + "guardrail_name": "credentials-api-keys", + "litellm_params": { + "guardrail": "litellm_content_filter", + "mode": "pre_call", + "patterns": [ + {"pattern_type": "prebuilt", "pattern_name": "aws_access_key", "action": "BLOCK"}, + {"pattern_type": "prebuilt", "pattern_name": "aws_secret_key", "action": "BLOCK"}, + {"pattern_type": "prebuilt", "pattern_name": "github_token", "action": "BLOCK"}, + {"pattern_type": "prebuilt", "pattern_name": "slack_token", "action": "BLOCK"}, + {"pattern_type": "prebuilt", "pattern_name": "generic_api_key", "action": "BLOCK"} + ], + "pattern_redaction_format": "[{pattern_name}_REDACTED]" + }, + "guardrail_info": { + "description": "Blocks requests containing API keys and credentials (AWS, GitHub, Slack)" + } + }, + { + "guardrail_name": "network-infrastructure-pii", + "litellm_params": { + "guardrail": "litellm_content_filter", + "mode": "pre_call", + "patterns": [ + {"pattern_type": "prebuilt", "pattern_name": "ipv4", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "ipv6", "action": "MASK"} + ], + "pattern_redaction_format": "[INTERNAL_IP_REDACTED]" + }, + "guardrail_info": { + "description": "Masks IP addresses in requests" + } + }, + { + "guardrail_name": "protected-class-information", + "litellm_params": { + "guardrail": "litellm_content_filter", + "mode": "pre_call", + "patterns": [ + {"pattern_type": "prebuilt", "pattern_name": "gender_sexual_orientation", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "race_ethnicity_national_origin", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "religion", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "age_discrimination", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "disability", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "marital_family_status", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "military_status", "action": "MASK"}, + {"pattern_type": "prebuilt", "pattern_name": "public_assistance", "action": "MASK"} + ], + "pattern_redaction_format": "[PROTECTED_CLASS_INFO_REDACTED]" + }, + "guardrail_info": { + "description": "Masks protected class information for HR compliance and anti-discrimination" + } } + ], + "templateData": { + "policy_name": "advanced-pii-protection-australia", + "description": "Comprehensive PII detection and masking policy for Australia. Protects Australian-specific identifiers, international employee data, financial information, credentials, protected class information, and industry-specific sensitive data.", + "guardrails_add": [ + "au-pii-tax-identifiers", + "au-pii-passports", + "international-pii-identifiers", + "contact-information-pii", + "financial-pii", + "credentials-api-keys", + "network-infrastructure-pii", + "protected-class-information" + ], + "guardrails_remove": [] } }, { @@ -49,92 +215,16 @@ "financial-pii" ], "complexity": "Low", + "guardrailDefinitions": [], "templateData": { "policy_name": "baseline-pii-protection", "description": "Baseline PII protection for internal tools and testing. Focuses on credentials and high-risk identifiers only.", - "guardrails": { - "add": [ - "au-pii-tax-identifiers", - "credentials-api-keys", - "financial-pii" - ] - } - } - }, - { - "id": "hr-recruiting-policy", - "title": "HR & Recruiting Policy", - "description": "Strict PII protection for HR, recruiting, and workforce management. Emphasizes protected class information to ensure compliance with anti-discrimination laws and workplace regulations.", - "icon": "ShieldExclamationIcon", - "iconColor": "text-indigo-500", - "iconBg": "bg-indigo-50", - "guardrails": [ - "au-pii-tax-identifiers", - "au-pii-passports", - "international-pii-identifiers", - "contact-information-pii", - "financial-pii", - "credentials-api-keys", - "network-infrastructure-pii", - "protected-class-information" - ], - "inherits": "Fortescue PII Protection", - "complexity": "High", - "templateData": { - "policy_name": "hr-recruiting-policy", - "description": "Strict PII protection for HR, recruiting, and workforce management. Emphasizes protected class information to ensure compliance with anti-discrimination laws and workplace regulations.", - "inherit": "fortescue-pii-protection", - "guardrails": { - "add": [] - } - } - }, - { - "id": "cost-control-policy", - "title": "Cost Control Policy", - "description": "Budget-focused guardrails to prevent unexpected spend. Routes expensive queries to cheaper models and enforces rate limiting.", - "icon": "CurrencyDollarIcon", - "iconColor": "text-green-500", - "iconBg": "bg-green-50", - "guardrails": [ - "budget-limit", - "model-routing", - "rate-limit" - ], - "complexity": "Medium", - "templateData": { - "policy_name": "cost-control-policy", - "description": "Budget-focused guardrails to prevent unexpected spend. Routes expensive queries to cheaper models.", - "guardrails": { - "add": [ - "budget-limit", - "model-routing", - "rate-limit" - ] - } - } - }, - { - "id": "development-testing-policy", - "title": "Development / Testing", - "description": "Relaxed guardrails for development environments. Enables full logging for debugging purposes with minimal restrictions.", - "icon": "BeakerIcon", - "iconColor": "text-orange-500", - "iconBg": "bg-orange-50", - "guardrails": [ - "debug-logging", - "latency-monitoring" - ], - "complexity": "Low", - "templateData": { - "policy_name": "development-testing-policy", - "description": "Relaxed guardrails for development environments. Enables full logging for debugging purposes.", - "guardrails": { - "add": [ - "debug-logging", - "latency-monitoring" - ] - } + "guardrails_add": [ + "au-pii-tax-identifiers", + "credentials-api-keys", + "financial-pii" + ], + "guardrails_remove": [] } } ] diff --git a/ui/litellm-dashboard/src/components/policies/guardrail_selection_modal.tsx b/ui/litellm-dashboard/src/components/policies/guardrail_selection_modal.tsx new file mode 100644 index 00000000000..5d2a1c8f016 --- /dev/null +++ b/ui/litellm-dashboard/src/components/policies/guardrail_selection_modal.tsx @@ -0,0 +1,244 @@ +import React, { useState, useEffect } from "react"; +import { Modal, Checkbox, Button, Divider, Tag, Spin } from "antd"; +import { CheckCircleOutlined, InfoCircleOutlined } from "@ant-design/icons"; + +interface GuardrailInfo { + guardrail_name: string; + description: string; + alreadyExists: boolean; + definition: any; +} + +interface GuardrailSelectionModalProps { + visible: boolean; + template: any; + existingGuardrails: Set; + onConfirm: (selectedGuardrails: any[]) => void; + onCancel: () => void; + isLoading?: boolean; +} + +const GuardrailSelectionModal: React.FC = ({ + visible, + template, + existingGuardrails, + onConfirm, + onCancel, + isLoading = false, +}) => { + const [selectedGuardrails, setSelectedGuardrails] = useState>( + new Set() + ); + + // Prepare guardrail info with existence status + const guardrailsInfo: GuardrailInfo[] = ( + template?.guardrailDefinitions || [] + ).map((def: any) => ({ + guardrail_name: def.guardrail_name, + description: def.guardrail_info?.description || "No description available", + alreadyExists: existingGuardrails.has(def.guardrail_name), + 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)); + } + }, [visible, template]); + + const handleToggle = (guardrailName: string) => { + setSelectedGuardrails((prev) => { + const newSet = new Set(prev); + if (newSet.has(guardrailName)) { + newSet.delete(guardrailName); + } else { + newSet.add(guardrailName); + } + return newSet; + }); + }; + + const handleSelectAll = () => { + const allNew = guardrailsInfo + .filter((g) => !g.alreadyExists) + .map((g) => g.guardrail_name); + setSelectedGuardrails(new Set(allNew)); + }; + + const handleDeselectAll = () => { + setSelectedGuardrails(new Set()); + }; + + const handleConfirm = () => { + const selectedDefinitions = guardrailsInfo + .filter((g) => selectedGuardrails.has(g.guardrail_name)) + .map((g) => g.definition); + onConfirm(selectedDefinitions); + }; + + const newGuardrailsCount = guardrailsInfo.filter( + (g) => !g.alreadyExists + ).length; + const existingCount = guardrailsInfo.filter((g) => g.alreadyExists).length; + const selectedCount = selectedGuardrails.size; + + return ( + +

{template?.title}

+

+ Review and select guardrails to create for this template +

+ + } + open={visible} + onCancel={onCancel} + width={700} + footer={[ + , + , + ]} + > +
+ {/* Summary Stats */} +
+ +
+
+ + {guardrailsInfo.length} total guardrails + + • + + {newGuardrailsCount} new + + {existingCount > 0 && ( + <> + • + + {existingCount} already exist + + + )} +
+
+ {newGuardrailsCount > 0 && ( +
+ + +
+ )} +
+ + {/* Guardrails List */} +
+ {guardrailsInfo.map((guardrail) => ( +
+
+
+ {guardrail.alreadyExists ? ( + + ) : ( + handleToggle(guardrail.guardrail_name)} + /> + )} +
+
+
+ + {guardrail.guardrail_name} + + {guardrail.alreadyExists && ( + + Already exists + + )} +
+

+ {guardrail.description} +

+ + {/* Show guardrail type and mode */} +
+ + {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) + + )} +
+
+
+
+ ))} +
+ + {guardrailsInfo.length === 0 && ( +
+

No guardrails defined for this template.

+

+ This template will use existing guardrails in your system. +

+
+ )} + + + + {/* Selected Summary */} +
+ {selectedCount > 0 ? ( +

+ {selectedCount}{" "} + guardrail{selectedCount > 1 ? "s" : ""} will be created +

+ ) : existingCount > 0 ? ( +

+ 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. +

+ )} +
+
+
+ ); +}; + +export default GuardrailSelectionModal; diff --git a/ui/litellm-dashboard/src/components/policies/index.tsx b/ui/litellm-dashboard/src/components/policies/index.tsx index 419eb684a3c..77e48fc2c89 100644 --- a/ui/litellm-dashboard/src/components/policies/index.tsx +++ b/ui/litellm-dashboard/src/components/policies/index.tsx @@ -10,6 +10,7 @@ import AttachmentTable from "./attachment_table"; import AddAttachmentForm from "./add_attachment_form"; import PolicyTestPanel from "./policy_test_panel"; import PolicyTemplates from "./policy_templates"; +import GuardrailSelectionModal from "./guardrail_selection_modal"; import { getPoliciesList, deletePolicyCall, @@ -20,6 +21,7 @@ import { createPolicyCall, updatePolicyCall, createPolicyAttachmentCall, + createGuardrailCall, } from "../networking"; import { Policy, @@ -50,6 +52,10 @@ const PoliciesPanel: React.FC = ({ const [isDeleting, setIsDeleting] = useState(false); const [policyToDelete, setPolicyToDelete] = useState(null); const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [isGuardrailSelectionModalOpen, setIsGuardrailSelectionModalOpen] = useState(false); + const [selectedTemplate, setSelectedTemplate] = useState(null); + const [existingGuardrailNames, setExistingGuardrailNames] = useState>(new Set()); + const [isCreatingGuardrails, setIsCreatingGuardrails] = useState(false); const isAdmin = userRole ? isAdminRole(userRole) : false; @@ -173,11 +179,87 @@ const PoliciesPanel: React.FC = ({ fetchAttachments(); }; - const handleUseTemplate = (templateData: any) => { - // Pre-fill the add policy form with template data - setEditingPolicy(templateData as Policy); - setIsAddPolicyModalVisible(true); - setActiveTab(0); // Switch to Policies tab + const handleUseTemplate = async (template: any) => { + if (!accessToken) { + message.error("Authentication required"); + return; + } + + try { + // Fetch existing guardrails to show in the modal + const existingGuardrailsResponse = await getGuardrailsList(accessToken); + const existingNames = new Set( + existingGuardrailsResponse.guardrails?.map((g: any) => g.guardrail_name as string) || [] + ); + + setExistingGuardrailNames(existingNames); + setSelectedTemplate(template); + setIsGuardrailSelectionModalOpen(true); + } catch (error) { + console.error("Error fetching guardrails:", error); + message.error("Failed to load guardrails. Please try again."); + } + }; + + const handleGuardrailSelectionConfirm = async (selectedGuardrailDefinitions: any[]) => { + if (!accessToken || !selectedTemplate) return; + + setIsCreatingGuardrails(true); + + try { + const createdGuardrails: string[] = []; + const failedGuardrails: string[] = []; + + // Create selected guardrails + for (const guardrailDef of selectedGuardrailDefinitions) { + const guardrailName = guardrailDef.guardrail_name; + + try { + await createGuardrailCall(accessToken, guardrailDef); + createdGuardrails.push(guardrailName); + console.log(`Successfully created guardrail: ${guardrailName}`); + } catch (error) { + console.error(`Failed to create guardrail "${guardrailName}":`, error); + failedGuardrails.push(guardrailName); + } + } + + // Refresh guardrails list + await fetchGuardrails(); + + // Close modal + setIsGuardrailSelectionModalOpen(false); + setIsCreatingGuardrails(false); + + // Pre-fill the add policy form with template data + setEditingPolicy(selectedTemplate.templateData as Policy); + setIsAddPolicyModalVisible(true); + setActiveTab(0); // Switch to Policies tab + + // Show success message + if (createdGuardrails.length > 0) { + message.success( + `Created ${createdGuardrails.length} guardrail${createdGuardrails.length > 1 ? "s" : ""}! Complete the policy form to save.` + ); + } else { + message.success("Template ready! Complete the policy form to save."); + } + + if (failedGuardrails.length > 0) { + message.warning( + `Failed to create ${failedGuardrails.length} guardrail(s): ${failedGuardrails.join(", ")}. You may need to create them manually.` + ); + } + } catch (error) { + setIsCreatingGuardrails(false); + console.error("Error creating guardrails:", error); + message.error("Failed to create guardrails. Please try again."); + } + }; + + const handleGuardrailSelectionCancel = () => { + setIsGuardrailSelectionModalOpen(false); + setSelectedTemplate(null); }; return ( @@ -282,6 +364,15 @@ const PoliciesPanel: React.FC = ({ onOk={handleDeleteConfirm} confirmLoading={isDeleting} /> + + diff --git a/ui/litellm-dashboard/src/components/policies/policy_templates.tsx b/ui/litellm-dashboard/src/components/policies/policy_templates.tsx index 8b5cfd1cec8..588fca80923 100644 --- a/ui/litellm-dashboard/src/components/policies/policy_templates.tsx +++ b/ui/litellm-dashboard/src/components/policies/policy_templates.tsx @@ -170,7 +170,7 @@ const PolicyTemplates: React.FC = ({ onUseTemplate, access guardrails={template.guardrails} inherits={template.inherits} complexity={template.complexity} - onUseTemplate={() => onUseTemplate(template.templateData)} + onUseTemplate={() => onUseTemplate(template)} /> ))}