feat: create missing guardrails, working policy templates

This commit is contained in:
Krrish Dholakia 2026-02-11 20:57:21 -08:00
parent 770519be75
commit 4341b8a5b9
4 changed files with 533 additions and 108 deletions

View file

@ -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": []
}
}
]

View file

@ -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<string>;
onConfirm: (selectedGuardrails: any[]) => void;
onCancel: () => void;
isLoading?: boolean;
}
const GuardrailSelectionModal: React.FC<GuardrailSelectionModalProps> = ({
visible,
template,
existingGuardrails,
onConfirm,
onCancel,
isLoading = false,
}) => {
const [selectedGuardrails, setSelectedGuardrails] = useState<Set<string>>(
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 (
<Modal
title={
<div>
<h3 className="text-lg font-semibold mb-1">{template?.title}</h3>
<p className="text-sm text-gray-500 font-normal">
Review and select guardrails to create for this template
</p>
</div>
}
open={visible}
onCancel={onCancel}
width={700}
footer={[
<Button key="cancel" onClick={onCancel} disabled={isLoading}>
Cancel
</Button>,
<Button
key="confirm"
type="primary"
onClick={handleConfirm}
loading={isLoading}
disabled={selectedCount === 0 && existingCount === 0}
>
{selectedCount > 0
? `Create ${selectedCount} Guardrail${selectedCount > 1 ? "s" : ""} & Use Template`
: "Use Template"}
</Button>,
]}
>
<div className="py-4">
{/* Summary Stats */}
<div className="flex items-center gap-4 mb-4 p-3 bg-blue-50 rounded-lg border border-blue-100">
<InfoCircleOutlined className="text-blue-600 text-lg" />
<div className="flex-1">
<div className="text-sm">
<span className="font-medium text-gray-900">
{guardrailsInfo.length} total guardrails
</span>
<span className="text-gray-600 mx-2">•</span>
<span className="text-green-600 font-medium">
{newGuardrailsCount} new
</span>
{existingCount > 0 && (
<>
<span className="text-gray-600 mx-2">•</span>
<span className="text-gray-600">
{existingCount} already exist
</span>
</>
)}
</div>
</div>
{newGuardrailsCount > 0 && (
<div className="flex gap-2">
<Button size="small" onClick={handleSelectAll}>
Select All New
</Button>
<Button size="small" onClick={handleDeselectAll}>
Deselect All
</Button>
</div>
)}
</div>
{/* Guardrails List */}
<div className="space-y-3 max-h-96 overflow-y-auto">
{guardrailsInfo.map((guardrail) => (
<div
key={guardrail.guardrail_name}
className={`border rounded-lg p-4 ${
guardrail.alreadyExists
? "bg-gray-50 border-gray-200"
: "bg-white border-gray-300 hover:border-blue-400"
} transition-colors`}
>
<div className="flex items-start gap-3">
<div className="flex-shrink-0 pt-0.5">
{guardrail.alreadyExists ? (
<CheckCircleOutlined className="text-green-600 text-lg" />
) : (
<Checkbox
checked={selectedGuardrails.has(guardrail.guardrail_name)}
onChange={() => handleToggle(guardrail.guardrail_name)}
/>
)}
</div>
<div className="flex-1 min-w-0">
<div className="flex items-center gap-2 mb-1">
<span className="font-mono text-sm font-medium text-gray-900">
{guardrail.guardrail_name}
</span>
{guardrail.alreadyExists && (
<Tag color="green" className="text-xs">
Already exists
</Tag>
)}
</div>
<p className="text-sm text-gray-600">
{guardrail.description}
</p>
{/* Show guardrail type and mode */}
<div className="flex gap-2 mt-2">
<Tag className="text-xs">
{guardrail.definition?.litellm_params?.guardrail || "unknown"}
</Tag>
<Tag className="text-xs" color="blue">
{guardrail.definition?.litellm_params?.mode || "unknown"}
</Tag>
{guardrail.definition?.litellm_params?.patterns && (
<Tag className="text-xs" color="purple">
{guardrail.definition.litellm_params.patterns.length} pattern(s)
</Tag>
)}
</div>
</div>
</div>
</div>
))}
</div>
{guardrailsInfo.length === 0 && (
<div className="text-center py-8 text-gray-500">
<p>No guardrails defined for this template.</p>
<p className="text-sm mt-2">
This template will use existing guardrails in your system.
</p>
</div>
)}
<Divider />
{/* Selected Summary */}
<div className="text-sm text-gray-600">
{selectedCount > 0 ? (
<p>
<span className="font-medium text-gray-900">{selectedCount}</span>{" "}
guardrail{selectedCount > 1 ? "s" : ""} will be created
</p>
) : existingCount > 0 ? (
<p className="text-green-600">
All guardrails already exist. You can proceed to use this template.
</p>
) : (
<p className="text-orange-600">
Select at least one guardrail to create, or click "Use Template" to proceed without creating new guardrails.
</p>
)}
</div>
</div>
</Modal>
);
};
export default GuardrailSelectionModal;

View file

@ -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<PoliciesPanelProps> = ({
const [isDeleting, setIsDeleting] = useState(false);
const [policyToDelete, setPolicyToDelete] = useState<Policy | null>(null);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [isGuardrailSelectionModalOpen, setIsGuardrailSelectionModalOpen] = useState(false);
const [selectedTemplate, setSelectedTemplate] = useState<any>(null);
const [existingGuardrailNames, setExistingGuardrailNames] = useState<Set<string>>(new Set());
const [isCreatingGuardrails, setIsCreatingGuardrails] = useState(false);
const isAdmin = userRole ? isAdminRole(userRole) : false;
@ -173,11 +179,87 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({
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<string>(
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<PoliciesPanelProps> = ({
onOk={handleDeleteConfirm}
confirmLoading={isDeleting}
/>
<GuardrailSelectionModal
visible={isGuardrailSelectionModalOpen}
template={selectedTemplate}
existingGuardrails={existingGuardrailNames}
onConfirm={handleGuardrailSelectionConfirm}
onCancel={handleGuardrailSelectionCancel}
isLoading={isCreatingGuardrails}
/>
</TabPanel>
<TabPanel>

View file

@ -170,7 +170,7 @@ const PolicyTemplates: React.FC<PolicyTemplatesProps> = ({ onUseTemplate, access
guardrails={template.guardrails}
inherits={template.inherits}
complexity={template.complexity}
onUseTemplate={() => onUseTemplate(template.templateData)}
onUseTemplate={() => onUseTemplate(template)}
/>
))}
</div>