feat: support policy templates on ui

allows admin to enable pre-configured guardrails

helps cover specific use-cases well
This commit is contained in:
Krrish Dholakia 2026-02-11 20:21:23 -08:00
parent 9c2ceb90c7
commit 770519be75
7 changed files with 793 additions and 5 deletions

View file

@ -14,14 +14,388 @@ model_list:
litellm_params:
model: openai/gpt-4.1-mini
# ==============================================================================
# GUARDRAILS - PII Detection for Fortescue (Australian Mining Company)
# ==============================================================================
guardrails:
- guardrail_name: redact-ssn
# --------------------------------------------------------------------------
# 1. AUSTRALIAN-SPECIFIC PII PATTERNS
# --------------------------------------------------------------------------
- guardrail_name: au-pii-tax-identifiers
litellm_params:
guardrail: litellm_content_filter
mode: pre_call
patterns:
# Australian Tax File Number (8-9 digits)
- pattern_type: prebuilt
pattern_name: au_tfn
action: MASK
# Australian Business Number (11 digits)
- pattern_type: prebuilt
pattern_name: au_abn
action: MASK
# Australian Medicare Number
- pattern_type: prebuilt
pattern_name: au_medicare
action: MASK
pattern_redaction_format: "[{pattern_name}_REDACTED]"
- guardrail_name: au-pii-passports
litellm_params:
guardrail: litellm_content_filter
mode: pre_call
patterns:
# Australian passport (critical for mining contractors/fly-in-fly-out workers)
- pattern_type: prebuilt
pattern_name: passport_australia
action: MASK
pattern_redaction_format: "[PASSPORT_REDACTED]"
# --------------------------------------------------------------------------
# 2. INTERNATIONAL PII (for global workforce)
# --------------------------------------------------------------------------
- guardrail_name: international-pii-identifiers
litellm_params:
guardrail: litellm_content_filter
mode: pre_call
patterns:
# US employees/contractors
- 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
# UK operations
- pattern_type: prebuilt
pattern_name: passport_uk
action: MASK
# European operations
- 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
# Dutch BSN (contextual)
- pattern_type: prebuilt
pattern_name: nl_bsn_contextual
action: MASK
# Asian Pacific operations
- 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
# Canadian operations
- pattern_type: prebuilt
pattern_name: passport_canada
action: MASK
# South American operations (mining companies often have Brazilian presence)
- 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]"
# --------------------------------------------------------------------------
# 3. CONTACT INFORMATION
# --------------------------------------------------------------------------
- guardrail_name: contact-information-pii
litellm_params:
guardrail: litellm_content_filter
mode: pre_call
patterns:
# Email addresses (employees, contractors, suppliers)
- pattern_type: prebuilt
pattern_name: email
action: MASK
# Phone numbers (US format, commonly used internationally)
- pattern_type: prebuilt
pattern_name: us_phone
action: MASK
# Brazilian phones (if applicable)
- pattern_type: prebuilt
pattern_name: br_phone_landline
action: MASK
- pattern_type: prebuilt
pattern_name: br_phone_mobile
action: MASK
# Street addresses (mining sites, offices, residential)
- pattern_type: prebuilt
pattern_name: street_address
action: MASK
# Postal codes
- pattern_type: prebuilt
pattern_name: br_cep
action: MASK
pattern_redaction_format: "[{pattern_name}_REDACTED]"
# --------------------------------------------------------------------------
# 4. FINANCIAL & PAYMENT INFORMATION
# --------------------------------------------------------------------------
- guardrail_name: financial-pii
litellm_params:
guardrail: litellm_content_filter
mode: pre_call
patterns:
# Credit cards (corporate cards, employee expenses)
- 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
# International banking (IBAN for international payments)
- pattern_type: prebuilt
pattern_name: iban
action: MASK
pattern_redaction_format: "[{pattern_name}_REDACTED]"
# --------------------------------------------------------------------------
# 5. CREDENTIALS & API KEYS (BLOCK - High Risk)
# --------------------------------------------------------------------------
- guardrail_name: credentials-api-keys
litellm_params:
guardrail: litellm_content_filter
mode: pre_call
patterns:
# AWS credentials (critical infrastructure access)
- pattern_type: prebuilt
pattern_name: aws_access_key
action: BLOCK
- pattern_type: prebuilt
pattern_name: aws_secret_key
action: BLOCK
# GitHub tokens (code repository access)
- pattern_type: prebuilt
pattern_name: github_token
action: BLOCK
# Slack tokens (internal communications)
- pattern_type: prebuilt
pattern_name: slack_token
action: BLOCK
# Generic API keys
- pattern_type: prebuilt
pattern_name: generic_api_key
action: BLOCK
pattern_redaction_format: "[{pattern_name}_REDACTED]"
# --------------------------------------------------------------------------
# 6. NETWORK INFORMATION (Mining operations, remote sites)
# --------------------------------------------------------------------------
- guardrail_name: network-infrastructure-pii
litellm_params:
guardrail: litellm_content_filter
mode: pre_call
patterns:
# IP addresses (internal network, mining site infrastructure)
- pattern_type: prebuilt
pattern_name: ipv4
action: MASK
- pattern_type: prebuilt
pattern_name: ipv6
action: MASK
pattern_redaction_format: "[INTERNAL_IP_REDACTED]"
# --------------------------------------------------------------------------
# 7. PROTECTED CLASS INFORMATION (Fair Lending, HR Compliance)
# Critical for mining industry with diverse workforce
# --------------------------------------------------------------------------
- guardrail_name: protected-class-information
litellm_params:
guardrail: litellm_content_filter
mode: pre_call
patterns:
# Gender and sexual orientation (workplace discrimination prevention)
- pattern_type: prebuilt
pattern_name: gender_sexual_orientation
action: MASK
# Race, ethnicity, national origin (diversity/inclusion compliance)
- pattern_type: prebuilt
pattern_name: race_ethnicity_national_origin
action: MASK
# Religion and creed
- pattern_type: prebuilt
pattern_name: religion
action: MASK
# Age discrimination (important for mining workforce management)
- pattern_type: prebuilt
pattern_name: age_discrimination
action: MASK
# Disability status (safety accommodations, worker's compensation)
- pattern_type: prebuilt
pattern_name: disability
action: MASK
# Marital and family status (relocation, benefits)
- pattern_type: prebuilt
pattern_name: marital_family_status
action: MASK
# Military status
- pattern_type: prebuilt
pattern_name: military_status
action: MASK
# Public assistance status
- pattern_type: prebuilt
pattern_name: public_assistance
action: MASK
pattern_redaction_format: "[PROTECTED_CLASS_INFO_REDACTED]"
# --------------------------------------------------------------------------
# 8. CUSTOM CODE GUARDRAIL - Mining Industry Specific PII
# --------------------------------------------------------------------------
- guardrail_name: mining-industry-custom-pii
litellm_params:
guardrail: custom_code
mode: pre_call
custom_code: |
def apply_guardrail(inputs, request_data, input_type):
"""
Custom PII detection for mining industry:
- Australian Company Numbers (ACN)
- Mining License Numbers
- Site-specific employee IDs
- Geological coordinates (sensitive mine locations)
"""
import re
for text in inputs["texts"]:
if regex_match(text, r"\d{3}-\d{2}-\d{4}"):
return block("SSN detected in message")
return allow()
# Australian Company Number (ACN) - 9 digits, space-separated
# Format: XXX XXX XXX
acn_pattern = r'\b\d{3}\s\d{3}\s\d{3}\b'
if re.search(acn_pattern, text):
# Mask ACNs
text = re.sub(acn_pattern, '[ACN_REDACTED]', text)
# Mining tenement/license numbers (Australian format)
# Format: M/E/P + numbers (e.g., M12345, EL6789)
mining_license_pattern = r'\b[MEP][A-Z]?\s?\d{4,6}\b'
if re.search(mining_license_pattern, text, re.IGNORECASE):
text = re.sub(mining_license_pattern, '[MINING_LICENSE_REDACTED]', text, flags=re.IGNORECASE)
# Precise GPS coordinates (sensitive mine locations)
# Format: latitude, longitude with high precision
gps_pattern = r'-?\d{1,3}\.\d{4,}\s*,\s*-?\d{1,3}\.\d{4,}'
if re.search(gps_pattern, text):
text = re.sub(gps_pattern, '[GPS_COORDINATES_REDACTED]', text)
# FIFO/DIDO roster numbers or employee mining IDs
# Format: Common mining employee ID patterns
employee_id_pattern = r'\b(?:EMP|FIF|DID|MIN)[_-]?\d{5,8}\b'
if re.search(employee_id_pattern, text, re.IGNORECASE):
text = re.sub(employee_id_pattern, '[EMPLOYEE_ID_REDACTED]', text, flags=re.IGNORECASE)
# Update the text in inputs if any modifications were made
if text != inputs["texts"][inputs["texts"].index(inputs["texts"][next(i for i, t in enumerate(inputs["texts"]) if t == text)])]:
inputs["texts"][inputs["texts"].index(inputs["texts"][next(i for i, t in enumerate(inputs["texts"]) if t == text)])] = text
return allow()
# ==============================================================================
# POLICIES - Combining PII Guardrails
# ==============================================================================
policies:
# Comprehensive PII policy for Fortescue mining operations
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:
# Australian-specific
- au-pii-tax-identifiers
- au-pii-passports
# International workforce
- international-pii-identifiers
# Contact information
- contact-information-pii
# Financial data
- financial-pii
# Credentials (blocks instead of masks)
- credentials-api-keys
# Network infrastructure
- network-infrastructure-pii
# Protected class compliance
- protected-class-information
# Mining industry custom
- mining-industry-custom-pii
# Baseline policy for non-sensitive internal use
fortescue-pii-baseline:
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
# HR and recruiting policy (extra strict on protected class)
fortescue-pii-hr-recruiting:
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:
# Already includes protected-class-information from parent
# This policy ensures all PII filters are active for HR use cases
# ==============================================================================
# POLICY ATTACHMENTS - Apply Policies to Teams/Keys/Models
# ==============================================================================
policy_attachments:
# Apply comprehensive PII protection globally
- policy: fortescue-pii-protection
scope: "*" # Applies to all requests by default
# Override for HR team with stricter policy
# Uncomment and modify team alias after creating teams
# - policy: fortescue-pii-hr-recruiting
# teams:
# - hr-team
# - recruiting-team
# - workforce-management
# Override for internal development/testing with baseline policy
# - policy: fortescue-pii-baseline
# teams:
# - internal-dev
# - testing-team
# keys:
# - dev-*
# - test-*

View file

@ -6,8 +6,13 @@ All /policy management endpoints
/policy/validate - Validate a policy configuration
/policy/list - List all loaded policies
/policy/info - Get information about a specific policy
/policy/templates - Get available policy templates
"""
import json
import os
from typing import Any, List
from fastapi import APIRouter, Depends, HTTPException, Request
from litellm._logging import verbose_proxy_logger
@ -257,3 +262,52 @@ async def test_policy_matching(
matching_policies=matching_policy_names,
resolved_guardrails=resolved_guardrails,
)
@router.get(
"/policy/templates",
tags=["policy management"],
dependencies=[Depends(user_api_key_auth)],
)
@management_endpoint_wrapper
async def get_policy_templates(
request: Request,
user_api_key_dict: UserAPIKeyAuth = Depends(user_api_key_auth),
) -> List[Any]:
"""
Get available policy templates for quick policy setup.
Returns a list of pre-configured policy templates that users can use
as a starting point for creating their own policies.
"""
try:
# Get the path to the policy_templates.json file
current_dir = os.path.dirname(os.path.abspath(__file__))
templates_path = os.path.join(
os.path.dirname(current_dir), "policy_templates.json"
)
# Read and return the templates
with open(templates_path, "r") as f:
templates = json.load(f)
verbose_proxy_logger.debug(f"Loaded {len(templates)} policy templates")
return templates
except FileNotFoundError:
raise HTTPException(
status_code=404,
detail="Policy templates file not found",
)
except json.JSONDecodeError as e:
verbose_proxy_logger.error(f"Error parsing policy templates JSON: {e}")
raise HTTPException(
status_code=500,
detail="Error parsing policy templates file",
)
except Exception as e:
verbose_proxy_logger.error(f"Error loading policy templates: {e}")
raise HTTPException(
status_code=500,
detail=f"Error loading policy templates: {str(e)}",
)

View file

@ -0,0 +1,140 @@
[
{
"id": "advanced-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.",
"icon": "ShieldCheckIcon",
"iconColor": "text-purple-500",
"iconBg": "bg-purple-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",
"mining-industry-custom-pii"
],
"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"
]
}
}
},
{
"id": "baseline-pii-protection",
"title": "Baseline PII Protection",
"description": "Baseline PII protection for internal tools and testing. Focuses on credentials and high-risk identifiers only. Suitable for non-sensitive internal use.",
"icon": "ShieldCheckIcon",
"iconColor": "text-blue-500",
"iconBg": "bg-blue-50",
"guardrails": [
"au-pii-tax-identifiers",
"credentials-api-keys",
"financial-pii"
],
"complexity": "Low",
"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"
]
}
}
}
]

View file

@ -5438,6 +5438,32 @@ export const getPolicyInfoWithGuardrails = async (accessToken: string, policyNam
}
};
export const getPolicyTemplates = async (accessToken: string) => {
try {
const url = proxyBaseUrl ? `${proxyBaseUrl}/policy/templates` : `/policy/templates`;
const response = await fetch(url, {
method: "GET",
headers: {
[globalLitellmHeaderName]: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
});
if (!response.ok) {
const errorData = await response.json();
const errorMessage = deriveErrorMessage(errorData);
handleError(errorMessage);
throw new Error(errorMessage);
}
const data = await response.json();
return data;
} catch (error) {
console.error("Failed to get policy templates:", error);
throw error;
}
};
export const createPolicyCall = async (accessToken: string, policyData: any) => {
try {
const url = proxyBaseUrl ? `${proxyBaseUrl}/policies` : `/policies`;

View file

@ -9,6 +9,7 @@ import AddPolicyForm from "./add_policy_form";
import AttachmentTable from "./attachment_table";
import AddAttachmentForm from "./add_attachment_form";
import PolicyTestPanel from "./policy_test_panel";
import PolicyTemplates from "./policy_templates";
import {
getPoliciesList,
deletePolicyCall,
@ -172,6 +173,13 @@ 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
};
return (
<div className="w-full mx-auto flex-auto overflow-y-auto m-8 p-2">
<TabGroup index={activeTab} onIndexChange={setActiveTab}>
@ -179,6 +187,7 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({
<Tab>Policies</Tab>
<Tab>Attachments</Tab>
<Tab>Policy Simulator</Tab>
<Tab>Templates</Tab>
</TabList>
<TabPanels>
@ -338,6 +347,10 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({
<TabPanel>
<PolicyTestPanel accessToken={accessToken} />
</TabPanel>
<TabPanel>
<PolicyTemplates onUseTemplate={handleUseTemplate} accessToken={accessToken} />
</TabPanel>
</TabPanels>
</TabGroup>
</div>

View file

@ -0,0 +1,181 @@
import React, { useState, useEffect } from "react";
import { Card, Button, Spin, message } from "antd";
import {
ShieldCheckIcon,
ShieldExclamationIcon,
BeakerIcon,
CurrencyDollarIcon,
CheckCircleIcon,
} from "@heroicons/react/outline";
import { getPolicyTemplates } from "../networking";
interface PolicyTemplateCardProps {
title: string;
description: string;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
iconColor: string;
iconBg: string;
guardrails: string[];
inherits?: string;
complexity: "Low" | "Medium" | "High";
onUseTemplate: () => void;
}
const PolicyTemplateCard: React.FC<PolicyTemplateCardProps> = ({
title,
description,
icon: Icon,
iconColor,
iconBg,
guardrails,
inherits,
complexity,
onUseTemplate,
}) => {
const getComplexityStyle = () => {
switch (complexity) {
case "Low":
return "bg-gray-50 text-gray-600 border-gray-200";
case "Medium":
return "bg-blue-50 text-blue-600 border-blue-100";
case "High":
return "bg-purple-50 text-purple-600 border-purple-100";
}
};
return (
<Card
className="h-full hover:shadow-md transition-shadow"
bodyStyle={{ display: "flex", flexDirection: "column", height: "100%" }}
>
<div className="flex items-start justify-between mb-4">
<div className={`p-2 rounded-lg ${iconBg}`}>
<Icon className={`h-6 w-6 ${iconColor}`} />
</div>
<span
className={`px-2.5 py-0.5 rounded-full text-xs font-medium border ${getComplexityStyle()}`}
>
{complexity} Complexity
</span>
</div>
<h3 className="text-base font-semibold text-gray-900 mb-2">{title}</h3>
<p className="text-sm text-gray-500 mb-6 flex-grow">{description}</p>
{inherits && (
<div className="mb-4 text-xs">
<span className="text-gray-500">Inherits from: </span>
<span className="font-medium text-gray-700 bg-gray-100 px-2 py-0.5 rounded">
{inherits}
</span>
</div>
)}
<div className="mb-6">
<span className="text-xs font-medium text-gray-500 uppercase tracking-wider block mb-2">
Included Guardrails
</span>
<div className="flex flex-wrap gap-2">
{guardrails.map((g) => (
<span
key={g}
className="inline-flex items-center px-2 py-1 rounded text-xs font-medium bg-gray-50 text-gray-700 border border-gray-200"
>
{g}
</span>
))}
</div>
</div>
<Button
type="primary"
block
className="mt-auto"
onClick={onUseTemplate}
>
Use Template
</Button>
</Card>
);
};
interface PolicyTemplatesProps {
onUseTemplate: (templateData: any) => void;
accessToken: string | null;
}
// Map icon names from JSON to actual icon components
const iconMap: Record<string, React.ComponentType<React.SVGProps<SVGSVGElement>>> = {
ShieldCheckIcon: ShieldCheckIcon,
ShieldExclamationIcon: ShieldExclamationIcon,
BeakerIcon: BeakerIcon,
CurrencyDollarIcon: CurrencyDollarIcon,
CheckCircleIcon: CheckCircleIcon,
};
const PolicyTemplates: React.FC<PolicyTemplatesProps> = ({ onUseTemplate, accessToken }) => {
const [templates, setTemplates] = useState<any[]>([]);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
const fetchTemplates = async () => {
if (!accessToken) return;
setIsLoading(true);
try {
const data = await getPolicyTemplates(accessToken);
setTemplates(data);
} catch (error) {
console.error("Error fetching policy templates:", error);
message.error("Failed to fetch policy templates");
} finally {
setIsLoading(false);
}
};
fetchTemplates();
}, [accessToken]);
if (isLoading) {
return (
<div className="flex justify-center items-center py-20">
<Spin size="large" tip="Loading policy templates..." />
</div>
);
}
return (
<div className="space-y-6">
<div className="flex justify-between items-end">
<div>
<h2 className="text-lg font-medium text-gray-900">
Policy Templates
</h2>
<p className="text-sm text-gray-500 mt-1">
Start with a pre-configured policy template to quickly set up
guardrails for your organization.
</p>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
{templates.map((template, index) => (
<PolicyTemplateCard
key={template.id || index}
title={template.title}
description={template.description}
icon={iconMap[template.icon] || ShieldCheckIcon}
iconColor={template.iconColor}
iconBg={template.iconBg}
guardrails={template.guardrails}
inherits={template.inherits}
complexity={template.complexity}
onUseTemplate={() => onUseTemplate(template.templateData)}
/>
))}
</div>
</div>
);
};
export default PolicyTemplates;

View file

@ -14,7 +14,7 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"jsx": "preserve",
"incremental": true,
"plugins": [
{