mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
feat: create missing guardrails, working policy templates
This commit is contained in:
parent
770519be75
commit
4341b8a5b9
4 changed files with 533 additions and 108 deletions
|
|
@ -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": []
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue