Guardrails - new Policy Templates (pre-configured guardrail combinations for specific use-cases) (#21025)
* feat(patterns.json): add australia specific pii patterns - tax file number, abn, medicare number Improve PII detection for australian contexts * feat(patterns.json): add iban + street address pattern detection * feat: support policy templates on ui allows admin to enable pre-configured guardrails helps cover specific use-cases well * feat: create missing guardrails, working policy templates * feat: policy_templates.json support hosted policy templates allows others to contribute to the policy templates * docs: document new policy templates * fix: address greptile feedback * fix: fix linting error
This commit is contained in:
parent
aab8edde67
commit
24b56a14eb
@ -767,6 +767,7 @@ router_settings:
|
||||
| LITELLM_KEY_ROTATION_CHECK_INTERVAL_SECONDS | Interval in seconds for how often to run job that auto-rotates keys. Default is 86400 (24 hours).
|
||||
| LITELLM_LICENSE | License key for LiteLLM usage
|
||||
| LITELLM_LOCAL_MODEL_COST_MAP | Local configuration for model cost mapping in LiteLLM
|
||||
| LITELLM_LOCAL_POLICY_TEMPLATES | When set to "true", uses local backup policy templates instead of fetching from GitHub. Policy templates are fetched from https://raw.githubusercontent.com/BerriAI/litellm/main/policy_templates.json by default, with automatic fallback to local backup on failure
|
||||
| LITELLM_LOG | Enable detailed logging for LiteLLM
|
||||
| LITELLM_MODEL_COST_MAP_URL | URL for fetching model cost map data. Default is https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json
|
||||
| LITELLM_LOG_FILE | File path to write LiteLLM logs to. When set, logs will be written to both console and the specified file
|
||||
|
||||
298
docs/my-website/docs/proxy/guardrails/policy_templates.md
Normal file
298
docs/my-website/docs/proxy/guardrails/policy_templates.md
Normal file
@ -0,0 +1,298 @@
|
||||
# Policy Templates
|
||||
|
||||
Policy templates provide pre-configured guardrail policies that you can use as a starting point for your organization. Instead of manually creating policies and guardrails, you can select a template that matches your use case and deploy it with one click.
|
||||
|
||||
## Using Policy Templates
|
||||
|
||||
### In the UI
|
||||
|
||||
1. Navigate to **Policies → Templates** tab in the LiteLLM Admin UI
|
||||
2. Browse available templates (e.g., "PII Protection", "Cost Control", "HR Compliance")
|
||||
3. Click **"Use Template"** on any template
|
||||
4. Review the guardrails that will be created:
|
||||
- Existing guardrails are marked with a green checkmark
|
||||
- New guardrails can be selected/deselected
|
||||
5. Click **"Create X Guardrails & Use Template"**
|
||||
6. Review and customize the pre-filled policy form
|
||||
7. Click **"Create Policy"** to save
|
||||
|
||||

|
||||
|
||||
### Workflow
|
||||
|
||||
```
|
||||
Select Template → Review Guardrails → Create Selected → Edit Policy → Save
|
||||
```
|
||||
|
||||
The system automatically:
|
||||
- ✅ Detects which guardrails already exist
|
||||
- ✅ Creates only the missing guardrails you select
|
||||
- ✅ Pre-fills the policy form with template data
|
||||
- ✅ Lets you customize before saving
|
||||
|
||||
## Available Templates
|
||||
|
||||
Templates are fetched from [GitHub](https://raw.githubusercontent.com/BerriAI/litellm/main/policy_templates.json) with automatic fallback to local backup.
|
||||
|
||||
### Current Templates
|
||||
|
||||
#### 1. Advanced PII Protection (Australia)
|
||||
- **Complexity:** High
|
||||
- **Use Case:** Comprehensive PII detection for Australian organizations
|
||||
- **Guardrails:**
|
||||
- Australian tax identifiers (TFN, ABN, Medicare)
|
||||
- Australian passports
|
||||
- International PII (SSN, passports, national IDs)
|
||||
- Contact information (email, phone, address)
|
||||
- Financial data (credit cards, IBAN)
|
||||
- API credentials (AWS, GitHub, Slack) - **BLOCKS** requests
|
||||
- Network infrastructure (IP addresses)
|
||||
- Protected class information (gender, race, religion, disability, etc.)
|
||||
|
||||
#### 2. Baseline PII Protection
|
||||
- **Complexity:** Low
|
||||
- **Use Case:** Basic protection for internal tools and testing
|
||||
- **Guardrails:**
|
||||
- Australian tax identifiers
|
||||
- API credentials
|
||||
- Financial data
|
||||
|
||||
## Creating Your Own Policy Templates
|
||||
|
||||
You can contribute policy templates for the entire LiteLLM community to use.
|
||||
|
||||
### Template Structure
|
||||
|
||||
Templates are defined in JSON format with the following structure:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "unique-template-id",
|
||||
"title": "Display Title",
|
||||
"description": "Detailed description of what this template protects",
|
||||
"icon": "ShieldCheckIcon",
|
||||
"iconColor": "text-purple-500",
|
||||
"iconBg": "bg-purple-50",
|
||||
"guardrails": [
|
||||
"guardrail-name-1",
|
||||
"guardrail-name-2"
|
||||
],
|
||||
"complexity": "Low|Medium|High",
|
||||
"guardrailDefinitions": [
|
||||
{
|
||||
"guardrail_name": "example-guardrail",
|
||||
"litellm_params": {
|
||||
"guardrail": "litellm_content_filter",
|
||||
"mode": "pre_call",
|
||||
"patterns": [
|
||||
{
|
||||
"pattern_type": "prebuilt",
|
||||
"pattern_name": "email",
|
||||
"action": "MASK"
|
||||
}
|
||||
],
|
||||
"pattern_redaction_format": "[{pattern_name}_REDACTED]"
|
||||
},
|
||||
"guardrail_info": {
|
||||
"description": "What this guardrail does"
|
||||
}
|
||||
}
|
||||
],
|
||||
"templateData": {
|
||||
"policy_name": "policy-name",
|
||||
"description": "Policy description",
|
||||
"guardrails_add": ["guardrail-name-1", "guardrail-name-2"],
|
||||
"guardrails_remove": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Field Descriptions
|
||||
|
||||
#### Display Fields
|
||||
- **id**: Unique identifier (lowercase with hyphens)
|
||||
- **title**: User-facing name shown in UI
|
||||
- **description**: Detailed explanation of what the template protects
|
||||
- **icon**: Icon name (must be available in UI icon map)
|
||||
- **iconColor**: Tailwind CSS text color class
|
||||
- **iconBg**: Tailwind CSS background color class
|
||||
- **guardrails**: Array of guardrail names (for display only)
|
||||
- **complexity**: Badge showing difficulty ("Low", "Medium", or "High")
|
||||
|
||||
#### Guardrail Definitions
|
||||
- **guardrailDefinitions**: Array of complete guardrail configurations
|
||||
- Each must be a valid guardrail object that can be sent to `/guardrails` POST endpoint
|
||||
- If a guardrail already exists, it will be skipped
|
||||
- Can be empty `[]` if template uses only existing guardrails
|
||||
|
||||
#### Policy Configuration
|
||||
- **templateData**: Object that pre-fills the policy form
|
||||
- **policy_name**: Suggested name (user can edit)
|
||||
- **description**: Policy description
|
||||
- **guardrails_add**: Array of guardrail names to include
|
||||
- **guardrails_remove**: Array to remove (usually `[]` for templates)
|
||||
- **inherit**: (Optional) Parent policy name for inheritance
|
||||
|
||||
### Example Template
|
||||
|
||||
Here's a complete example for a HIPAA compliance template:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "hipaa-compliance",
|
||||
"title": "HIPAA Compliance Policy",
|
||||
"description": "Healthcare compliance policy that masks PHI and enforces HIPAA regulations for healthcare applications.",
|
||||
"icon": "ShieldCheckIcon",
|
||||
"iconColor": "text-red-500",
|
||||
"iconBg": "bg-red-50",
|
||||
"guardrails": [
|
||||
"phi-detector",
|
||||
"medical-record-blocker",
|
||||
"patient-id-masker"
|
||||
],
|
||||
"complexity": "High",
|
||||
"guardrailDefinitions": [
|
||||
{
|
||||
"guardrail_name": "phi-detector",
|
||||
"litellm_params": {
|
||||
"guardrail": "litellm_content_filter",
|
||||
"mode": "pre_call",
|
||||
"patterns": [
|
||||
{
|
||||
"pattern_type": "prebuilt",
|
||||
"pattern_name": "us_ssn",
|
||||
"action": "MASK"
|
||||
},
|
||||
{
|
||||
"pattern_type": "prebuilt",
|
||||
"pattern_name": "email",
|
||||
"action": "MASK"
|
||||
},
|
||||
{
|
||||
"pattern_type": "prebuilt",
|
||||
"pattern_name": "us_phone",
|
||||
"action": "MASK"
|
||||
}
|
||||
],
|
||||
"pattern_redaction_format": "[PHI_REDACTED]"
|
||||
},
|
||||
"guardrail_info": {
|
||||
"description": "Detects and masks Protected Health Information (PHI)"
|
||||
}
|
||||
}
|
||||
],
|
||||
"templateData": {
|
||||
"policy_name": "hipaa-compliance-policy",
|
||||
"description": "HIPAA compliance policy for healthcare applications",
|
||||
"guardrails_add": [
|
||||
"phi-detector",
|
||||
"medical-record-blocker",
|
||||
"patient-id-masker"
|
||||
],
|
||||
"guardrails_remove": []
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Contributing Templates
|
||||
|
||||
To contribute a policy template for everyone to use:
|
||||
|
||||
### Step 1: Create Your Template JSON
|
||||
|
||||
1. Create a JSON file following the structure above
|
||||
2. Test it locally by adding it to your local `policy_templates.json`
|
||||
3. Verify all guardrails work correctly
|
||||
4. Ensure descriptions are clear and helpful
|
||||
|
||||
### Step 2: Submit a Pull Request
|
||||
|
||||
1. Fork the [LiteLLM repository](https://github.com/BerriAI/litellm)
|
||||
2. Add your template to `policy_templates.json` at the root
|
||||
3. Add your template to `litellm/policy_templates_backup.json` (keep both in sync)
|
||||
4. Create a pull request with:
|
||||
- Clear description of what the template protects
|
||||
- Use case examples
|
||||
- Any relevant compliance frameworks (HIPAA, GDPR, SOC 2, etc.)
|
||||
|
||||
### Guidelines
|
||||
|
||||
**DO:**
|
||||
- ✅ Use clear, descriptive names
|
||||
- ✅ Include comprehensive descriptions
|
||||
- ✅ Test all guardrails thoroughly
|
||||
- ✅ Document pattern sources (e.g., "Based on NIST guidelines")
|
||||
- ✅ Group related guardrails logically
|
||||
- ✅ Consider different complexity levels
|
||||
|
||||
**DON'T:**
|
||||
- ❌ Include credentials or secrets
|
||||
- ❌ Use overly broad patterns that may have false positives
|
||||
- ❌ Duplicate existing templates
|
||||
- ❌ Use custom code without thorough testing
|
||||
|
||||
## Using Templates Offline
|
||||
|
||||
For air-gapped or offline deployments, set the environment variable:
|
||||
|
||||
```bash
|
||||
export LITELLM_LOCAL_POLICY_TEMPLATES=true
|
||||
```
|
||||
|
||||
This forces the system to use the local backup (`litellm/policy_templates_backup.json`) instead of fetching from GitHub.
|
||||
|
||||
## Template Sources
|
||||
|
||||
- **GitHub (default):** https://raw.githubusercontent.com/BerriAI/litellm/main/policy_templates.json
|
||||
- **Local backup:** `litellm/policy_templates_backup.json`
|
||||
|
||||
Templates are automatically fetched from GitHub on each request, with fallback to local backup on any failure.
|
||||
|
||||
## Available Pattern Types
|
||||
|
||||
When creating guardrails for templates, you can use these prebuilt patterns:
|
||||
|
||||
### Identity Documents
|
||||
- `passport_australia`, `passport_us`, `passport_uk`, `passport_germany`, etc.
|
||||
- `us_ssn`, `us_ssn_no_dash`
|
||||
- `au_tfn`, `au_abn`, `au_medicare`
|
||||
- `nl_bsn_contextual`
|
||||
- `br_cpf`, `br_rg`, `br_cnpj`
|
||||
|
||||
### Financial
|
||||
- `visa`, `mastercard`, `amex`, `discover`, `credit_card`
|
||||
- `iban`
|
||||
|
||||
### Contact Information
|
||||
- `email`
|
||||
- `us_phone`, `br_phone_landline`, `br_phone_mobile`
|
||||
- `street_address`
|
||||
- `br_cep` (Brazilian postal code)
|
||||
|
||||
### Credentials
|
||||
- `aws_access_key`, `aws_secret_key`
|
||||
- `github_token`
|
||||
- `slack_token`
|
||||
- `generic_api_key`
|
||||
|
||||
### Network
|
||||
- `ipv4`, `ipv6`
|
||||
|
||||
### Protected Class
|
||||
- `gender_sexual_orientation`
|
||||
- `race_ethnicity_national_origin`
|
||||
- `religion`
|
||||
- `age_discrimination`
|
||||
- `disability`
|
||||
- `marital_family_status`
|
||||
- `military_status`
|
||||
- `public_assistance`
|
||||
|
||||
See the [full patterns list](https://github.com/BerriAI/litellm/blob/main/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/patterns.json) for all available patterns.
|
||||
|
||||
## Related Docs
|
||||
|
||||
- [Guardrail Policies](./guardrail_policies)
|
||||
- [Policy Tags](./policy_tags)
|
||||
- [Content Filter Patterns](../hooks/content_filter)
|
||||
- [Custom Code Guardrails](../hooks/custom_code)
|
||||
@ -97,6 +97,7 @@ const sidebars = {
|
||||
label: "Policies",
|
||||
items: [
|
||||
"proxy/guardrails/guardrail_policies",
|
||||
"proxy/guardrails/policy_templates",
|
||||
"proxy/guardrails/policy_tags",
|
||||
],
|
||||
},
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "LiteLLM_PolicyAttachmentTable" ADD COLUMN "tags" TEXT[] DEFAULT ARRAY[]::TEXT[];
|
||||
|
||||
@ -29,7 +29,11 @@ from litellm.types.utils import (
|
||||
LLMResponseTypes,
|
||||
StandardLoggingGuardrailInformation,
|
||||
)
|
||||
from fastapi.exceptions import HTTPException
|
||||
|
||||
try:
|
||||
from fastapi.exceptions import HTTPException
|
||||
except ImportError:
|
||||
HTTPException = None # type: ignore
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
|
||||
@ -664,7 +668,11 @@ class CustomGuardrail(CustomLogger):
|
||||
|
||||
if isinstance(e, ModifyResponseException):
|
||||
return True
|
||||
if isinstance(e, HTTPException) and e.status_code == 400:
|
||||
if (
|
||||
HTTPException is not None
|
||||
and isinstance(e, HTTPException)
|
||||
and e.status_code == 400
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
278
litellm/policy_templates_backup.json
Normal file
278
litellm/policy_templates_backup.json
Normal file
@ -0,0 +1,278 @@
|
||||
[
|
||||
{
|
||||
"id": "advanced-au-pii-protection",
|
||||
"title": "Advanced PII Protection (Australia)",
|
||||
"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",
|
||||
"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"
|
||||
],
|
||||
"complexity": "High",
|
||||
"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": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"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": "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": "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"}
|
||||
}
|
||||
],
|
||||
"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"
|
||||
],
|
||||
"guardrails_remove": []
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -13,15 +13,3 @@ model_list:
|
||||
- model_name: gpt-4.1-mini
|
||||
litellm_params:
|
||||
model: openai/gpt-4.1-mini
|
||||
|
||||
guardrails:
|
||||
- guardrail_name: redact-ssn
|
||||
litellm_params:
|
||||
guardrail: custom_code
|
||||
mode: pre_call
|
||||
custom_code: |
|
||||
def apply_guardrail(inputs, request_data, input_type):
|
||||
for text in inputs["texts"]:
|
||||
if regex_match(text, r"\d{3}-\d{2}-\d{4}"):
|
||||
return block("SSN detected in message")
|
||||
return allow()
|
||||
@ -367,6 +367,43 @@
|
||||
"pattern": "\\b\\d{1,2}\\.\\d{3}\\.\\d{3}-[\\dXx]\\b",
|
||||
"category": "Brazilian PII Patterns",
|
||||
"description": "Detects Brazilian RG identity card numbers (common pattern for SP, RJ, MG states)"
|
||||
},
|
||||
{
|
||||
"name": "au_tfn",
|
||||
"display_name": "TFN (Australian Tax File Number)",
|
||||
"pattern": "\\b\\d{8,9}\\b",
|
||||
"category": "PII Patterns",
|
||||
"description": "Detects Australian Tax File Numbers (8-9 digits) only when near TFN/tax file context to avoid false positives on arbitrary numbers",
|
||||
"keyword_pattern": "\\b(?:TFN|T\\.F\\.N\\.|tax\\s*file\\s*(?:number|no\\.?)|tax\\s*file\\s*#?|ATO\\s*number)\\b",
|
||||
"allow_word_numbers": false
|
||||
},
|
||||
{
|
||||
"name": "au_abn",
|
||||
"display_name": "ABN (Australian Business Number)",
|
||||
"pattern": "\\b\\d{2}\\s?\\d{3}\\s?\\d{3}\\s?\\d{3}\\b",
|
||||
"category": "PII Patterns",
|
||||
"description": "Detects Australian Business Numbers (11 digits, optional spaces: XX XXX XXX XXX)"
|
||||
},
|
||||
{
|
||||
"name": "au_medicare",
|
||||
"display_name": "Medicare Number (Australia)",
|
||||
"pattern": "\\b(?:\\d{4}\\s?\\d{5}\\s?\\d{1}|\\d{11})\\b",
|
||||
"category": "PII Patterns",
|
||||
"description": "Detects Australian Medicare numbers (formatted XXXX XXXXX X or 11 consecutive digits)"
|
||||
},
|
||||
{
|
||||
"name": "iban",
|
||||
"display_name": "IBAN (International Bank Account Number)",
|
||||
"pattern": "\\b[A-Z]{2}\\d{2}[A-Z0-9]{11,30}\\b",
|
||||
"category": "Payment Card Patterns",
|
||||
"description": "Detects IBANs (2 letter country code + 2 check digits + 4 char bank code + 7 digit base + optional 0-16 alphanumeric)"
|
||||
},
|
||||
{
|
||||
"name": "street_address",
|
||||
"display_name": "Street Address",
|
||||
"pattern": "\\b\\d{1,6}\\s+[A-Za-z0-9][A-Za-z0-9\\s.'-]*\\s+(?:Street|St|Avenue|Ave|Road|Rd|Boulevard|Blvd|Drive|Dr|Lane|Ln|Way|Court|Ct|Place|Pl|Circle|Cir)\\b",
|
||||
"category": "PII Patterns",
|
||||
"description": "Detects street addresses (number + street name + street type)"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -6,8 +6,12 @@ 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 policy templates (GitHub with local fallback)
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Request
|
||||
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
@ -257,3 +261,64 @@ async def test_policy_matching(
|
||||
matching_policies=matching_policy_names,
|
||||
resolved_guardrails=resolved_guardrails,
|
||||
)
|
||||
|
||||
|
||||
POLICY_TEMPLATES_GITHUB_URL = "https://raw.githubusercontent.com/BerriAI/litellm/main/policy_templates.json"
|
||||
|
||||
|
||||
def _load_policy_templates_from_local_backup() -> list:
|
||||
"""Load policy templates from local backup file (litellm/policy_templates_backup.json)."""
|
||||
backup_path = os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
"..",
|
||||
"..",
|
||||
"policy_templates_backup.json",
|
||||
)
|
||||
path = os.path.abspath(backup_path)
|
||||
if not os.path.exists(path):
|
||||
return []
|
||||
with open(path, "r") as f:
|
||||
return json.load(f)
|
||||
|
||||
|
||||
@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:
|
||||
"""
|
||||
Get policy templates for the UI (pre-configured guardrail combinations).
|
||||
|
||||
Fetches from GitHub with automatic fallback to local backup on failure.
|
||||
Set LITELLM_LOCAL_POLICY_TEMPLATES=true to skip GitHub and use local backup only.
|
||||
"""
|
||||
use_local = os.getenv("LITELLM_LOCAL_POLICY_TEMPLATES", "").strip().lower() in (
|
||||
"true",
|
||||
"1",
|
||||
"yes",
|
||||
)
|
||||
if use_local:
|
||||
return _load_policy_templates_from_local_backup()
|
||||
|
||||
try:
|
||||
from litellm.llms.custom_httpx.http_handler import get_async_httpx_client
|
||||
from litellm.types.llms.custom_http import httpxSpecialProvider
|
||||
|
||||
async_client = get_async_httpx_client(
|
||||
llm_provider=httpxSpecialProvider.UI,
|
||||
params={"timeout": 10.0},
|
||||
)
|
||||
response = await async_client.get(POLICY_TEMPLATES_GITHUB_URL)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
except Exception as e:
|
||||
verbose_proxy_logger.debug(
|
||||
"Failed to fetch policy templates from GitHub, using local backup: %s", e
|
||||
)
|
||||
|
||||
return _load_policy_templates_from_local_backup()
|
||||
|
||||
278
policy_templates.json
Normal file
278
policy_templates.json
Normal file
@ -0,0 +1,278 @@
|
||||
[
|
||||
{
|
||||
"id": "advanced-au-pii-protection",
|
||||
"title": "Advanced PII Protection (Australia)",
|
||||
"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",
|
||||
"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"
|
||||
],
|
||||
"complexity": "High",
|
||||
"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": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"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": "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": "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"}
|
||||
}
|
||||
],
|
||||
"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"
|
||||
],
|
||||
"guardrails_remove": []
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -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`;
|
||||
|
||||
@ -41,7 +41,9 @@ const AddPolicyForm: React.FC<AddPolicyFormProps> = ({
|
||||
const [availableModels, setAvailableModels] = useState<string[]>([]);
|
||||
const { userId, userRole } = useAuthorized();
|
||||
|
||||
const isEditing = !!editingPolicy;
|
||||
// Only consider it "editing" if editingPolicy has a policy_id (real existing policy)
|
||||
// If editingPolicy is set but has no policy_id, it's just pre-filled data for a new policy (e.g., from a template)
|
||||
const isEditing = !!editingPolicy?.policy_id;
|
||||
|
||||
useEffect(() => {
|
||||
if (visible && editingPolicy) {
|
||||
|
||||
@ -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;
|
||||
@ -9,6 +9,8 @@ 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 GuardrailSelectionModal from "./guardrail_selection_modal";
|
||||
import {
|
||||
getPoliciesList,
|
||||
deletePolicyCall,
|
||||
@ -19,6 +21,7 @@ import {
|
||||
createPolicyCall,
|
||||
updatePolicyCall,
|
||||
createPolicyAttachmentCall,
|
||||
createGuardrailCall,
|
||||
} from "../networking";
|
||||
import {
|
||||
Policy,
|
||||
@ -49,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;
|
||||
|
||||
@ -172,16 +179,133 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({
|
||||
fetchAttachments();
|
||||
};
|
||||
|
||||
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(1); // Switch to Policies tab (now at index 1)
|
||||
|
||||
// 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 (
|
||||
<div className="w-full mx-auto flex-auto overflow-y-auto m-8 p-2">
|
||||
<TabGroup index={activeTab} onIndexChange={setActiveTab}>
|
||||
<TabList className="mb-4">
|
||||
<Tab>Templates</Tab>
|
||||
<Tab>Policies</Tab>
|
||||
<Tab>Attachments</Tab>
|
||||
<Tab>Policy Simulator</Tab>
|
||||
</TabList>
|
||||
|
||||
<TabPanels>
|
||||
<TabPanel>
|
||||
<Alert
|
||||
message="About Policies"
|
||||
description={
|
||||
<div>
|
||||
<p className="mb-3">
|
||||
Use policies to group guardrails and control which ones run for specific teams, keys, or models.
|
||||
</p>
|
||||
<p className="mb-2 font-semibold">Why use policies?</p>
|
||||
<ul className="list-disc list-inside mb-3 space-y-1 ml-2">
|
||||
<li>Enable/disable specific guardrails for teams, keys, or models</li>
|
||||
<li>Group guardrails into a single policy</li>
|
||||
<li>Inherit from existing policies and override what you need</li>
|
||||
</ul>
|
||||
<a
|
||||
href="https://docs.litellm.ai/docs/proxy/guardrails/guardrail_policies"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600 hover:text-blue-800 underline inline-block mt-1"
|
||||
>
|
||||
Learn more in the documentation →
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
type="info"
|
||||
icon={<InfoCircleOutlined />}
|
||||
showIcon
|
||||
closable
|
||||
className="mb-6"
|
||||
/>
|
||||
<PolicyTemplates onUseTemplate={handleUseTemplate} accessToken={accessToken} />
|
||||
</TabPanel>
|
||||
|
||||
<TabPanel>
|
||||
<Alert
|
||||
message="About Policies"
|
||||
@ -273,6 +397,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>
|
||||
|
||||
@ -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)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PolicyTemplates;
|
||||
@ -14,7 +14,7 @@
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react-jsx",
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"plugins": [
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user