mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-11 03:38:38 +00:00
[Feat UI] Add Guardrails on LiteLLM UI (#10834)
* feat: add DB Add, Edit, Delete for Guardrails * feat: endpoints for guardrail management * add guardrail info helpers * add presidio logo * add createGuardrailCall * basic add guardrails on UI * fix cleanup form * feat: working add guardrails * ui view guardrails * allow viweing list of guardrails * fix: show created_at and updated_at for guardrails on UI * cleanup guardrail view * use 1 file for guardrail table * docs adding guardrails * fix: expose endpoint to show allowed modes, entities, actions * fix allow adding entity configs for PII * fixes guardrails * fix adding guardrails on UI * feat: add PII_ENTITY_CATEGORIES_MAP * use categories for pii * use step based form * fixes pii configs * fix pii config * refactor pii config on ui * clear form on submit * add ability to create guardrails * fix linting error
This commit is contained in:
parent
ee1557afcd
commit
b9c3a3835c
13 changed files with 2121 additions and 180 deletions
|
|
@ -4038,25 +4038,24 @@
|
|||
"supports_prompt_caching": true
|
||||
},
|
||||
"groq/deepseek-r1-distill-llama-70b": {
|
||||
"max_tokens": 131072,
|
||||
"max_input_tokens": 131072,
|
||||
"max_output_tokens": 131072,
|
||||
"input_cost_per_token": 0.00000075,
|
||||
"output_cost_per_token": 0.00000099,
|
||||
"max_tokens": 128000,
|
||||
"max_input_tokens": 128000,
|
||||
"max_output_tokens": 128000,
|
||||
"input_cost_per_token": 7.5e-07,
|
||||
"output_cost_per_token": 9.9e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_system_messages": false,
|
||||
"supports_function_calling": false,
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_response_schema": false,
|
||||
"supports_tool_choice": true
|
||||
},
|
||||
"groq/llama-3.3-70b-versatile": {
|
||||
"max_tokens": 8192,
|
||||
"max_tokens": 32768,
|
||||
"max_input_tokens": 128000,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 0.00000059,
|
||||
"output_cost_per_token": 0.00000079,
|
||||
"max_output_tokens": 32768,
|
||||
"input_cost_per_token": 5.9e-07,
|
||||
"output_cost_per_token": 7.9e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -4067,11 +4066,21 @@
|
|||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 0.00000059,
|
||||
"output_cost_per_token": 0.00000099,
|
||||
"input_cost_per_token": 5.9e-07,
|
||||
"output_cost_per_token": 9.9e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_tool_choice": true
|
||||
"supports_tool_choice": true,
|
||||
"deprecation_date": "2025-04-14"
|
||||
},
|
||||
"groq/llama-guard-3-8b": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 2e-07,
|
||||
"output_cost_per_token": 2e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat"
|
||||
},
|
||||
"groq/llama2-70b-4096": {
|
||||
"max_tokens": 4096,
|
||||
|
|
@ -4089,106 +4098,106 @@
|
|||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 0.00000005,
|
||||
"output_cost_per_token": 0.00000008,
|
||||
"input_cost_per_token": 5e-08,
|
||||
"output_cost_per_token": 8e-08,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true
|
||||
"mode": "chat"
|
||||
},
|
||||
"groq/llama-3.2-1b-preview": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 0.00000004,
|
||||
"output_cost_per_token": 0.00000004,
|
||||
"input_cost_per_token": 4e-08,
|
||||
"output_cost_per_token": 4e-08,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true
|
||||
"supports_tool_choice": true,
|
||||
"deprecation_date": "2025-04-14"
|
||||
},
|
||||
"groq/llama-3.2-3b-preview": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 0.00000006,
|
||||
"output_cost_per_token": 0.00000006,
|
||||
"input_cost_per_token": 6e-08,
|
||||
"output_cost_per_token": 6e-08,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true
|
||||
"supports_tool_choice": true,
|
||||
"deprecation_date": "2025-04-14"
|
||||
},
|
||||
"groq/llama-3.2-11b-text-preview": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 0.00000018,
|
||||
"output_cost_per_token": 0.00000018,
|
||||
"input_cost_per_token": 1.8e-07,
|
||||
"output_cost_per_token": 1.8e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true
|
||||
"supports_tool_choice": true,
|
||||
"deprecation_date": "2024-10-28"
|
||||
},
|
||||
"groq/llama-3.2-11b-vision-preview": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 0.00000018,
|
||||
"output_cost_per_token": 0.00000018,
|
||||
"input_cost_per_token": 1.8e-07,
|
||||
"output_cost_per_token": 1.8e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_vision": true,
|
||||
"supports_tool_choice": true
|
||||
"supports_tool_choice": true,
|
||||
"deprecation_date": "2025-04-14"
|
||||
},
|
||||
"groq/llama-3.2-90b-text-preview": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 0.0000009,
|
||||
"output_cost_per_token": 0.0000009,
|
||||
"input_cost_per_token": 9e-07,
|
||||
"output_cost_per_token": 9e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true
|
||||
"supports_tool_choice": true,
|
||||
"deprecation_date": "2024-11-25"
|
||||
},
|
||||
"groq/llama-3.2-90b-vision-preview": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 0.0000009,
|
||||
"output_cost_per_token": 0.0000009,
|
||||
"input_cost_per_token": 9e-07,
|
||||
"output_cost_per_token": 9e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_vision": true,
|
||||
"supports_tool_choice": true
|
||||
"supports_tool_choice": true,
|
||||
"deprecation_date": "2025-04-14"
|
||||
},
|
||||
"groq/llama3-70b-8192": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 0.00000059,
|
||||
"output_cost_per_token": 0.00000079,
|
||||
"input_cost_per_token": 5.9e-07,
|
||||
"output_cost_per_token": 7.9e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true
|
||||
"mode": "chat"
|
||||
},
|
||||
"groq/llama-3.1-8b-instant": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
"max_input_tokens": 128000,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 0.00000005,
|
||||
"output_cost_per_token": 0.00000008,
|
||||
"input_cost_per_token": 5e-08,
|
||||
"output_cost_per_token": 8e-08,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
|
|
@ -4199,13 +4208,14 @@
|
|||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 0.00000059,
|
||||
"output_cost_per_token": 0.00000079,
|
||||
"input_cost_per_token": 5.9e-07,
|
||||
"output_cost_per_token": 7.9e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true
|
||||
"supports_tool_choice": true,
|
||||
"deprecation_date": "2025-01-24"
|
||||
},
|
||||
"groq/llama-3.1-405b-reasoning": {
|
||||
"max_tokens": 8192,
|
||||
|
|
@ -4219,83 +4229,141 @@
|
|||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true
|
||||
},
|
||||
"groq/mixtral-8x7b-32768": {
|
||||
"max_tokens": 32768,
|
||||
"max_input_tokens": 32768,
|
||||
"max_output_tokens": 32768,
|
||||
"input_cost_per_token": 0.00000024,
|
||||
"output_cost_per_token": 0.00000024,
|
||||
"groq/meta-llama/llama-4-scout-17b-16e-instruct": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 131072,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 1.1e-07,
|
||||
"output_cost_per_token": 3.4e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true
|
||||
},
|
||||
"groq/meta-llama/llama-4-maverick-17b-128e-instruct": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 131072,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 2e-07,
|
||||
"output_cost_per_token": 6e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true
|
||||
},
|
||||
"groq/mistral-saba-24b": {
|
||||
"max_tokens": 32000,
|
||||
"max_input_tokens": 32000,
|
||||
"max_output_tokens": 32000,
|
||||
"input_cost_per_token": 7.9e-07,
|
||||
"output_cost_per_token": 7.9e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat"
|
||||
},
|
||||
"groq/mixtral-8x7b-32768": {
|
||||
"max_tokens": 32768,
|
||||
"max_input_tokens": 32768,
|
||||
"max_output_tokens": 32768,
|
||||
"input_cost_per_token": 2.4e-07,
|
||||
"output_cost_per_token": 2.4e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"deprecation_date": "2025-03-20"
|
||||
},
|
||||
"groq/gemma-7b-it": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 0.00000007,
|
||||
"output_cost_per_token": 0.00000007,
|
||||
"input_cost_per_token": 7e-08,
|
||||
"output_cost_per_token": 7e-08,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true
|
||||
"supports_tool_choice": true,
|
||||
"deprecation_date": "2024-12-18"
|
||||
},
|
||||
"groq/gemma2-9b-it": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 0.00000020,
|
||||
"output_cost_per_token": 0.00000020,
|
||||
"input_cost_per_token": 2e-07,
|
||||
"output_cost_per_token": 2e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_function_calling": false,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true
|
||||
"supports_tool_choice": false
|
||||
},
|
||||
"groq/llama3-groq-70b-8192-tool-use-preview": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 0.00000089,
|
||||
"output_cost_per_token": 0.00000089,
|
||||
"input_cost_per_token": 8.9e-07,
|
||||
"output_cost_per_token": 8.9e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true
|
||||
"supports_tool_choice": true,
|
||||
"deprecation_date": "2025-1-6"
|
||||
},
|
||||
"groq/llama3-groq-8b-8192-tool-use-preview": {
|
||||
"max_tokens": 8192,
|
||||
"max_input_tokens": 8192,
|
||||
"max_output_tokens": 8192,
|
||||
"input_cost_per_token": 0.00000019,
|
||||
"output_cost_per_token": 0.00000019,
|
||||
"input_cost_per_token": 1.9e-07,
|
||||
"output_cost_per_token": 1.9e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_tool_choice": true,
|
||||
"deprecation_date": "2025-1-6"
|
||||
},
|
||||
"groq/qwen-qwq-32b": {
|
||||
"max_tokens": 128000,
|
||||
"max_input_tokens": 128000,
|
||||
"max_output_tokens": 128000,
|
||||
"input_cost_per_token": 2.9e-07,
|
||||
"output_cost_per_token": 3.9e-07,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "chat",
|
||||
"supports_function_calling": true,
|
||||
"supports_response_schema": true,
|
||||
"supports_reasoning": true,
|
||||
"supports_tool_choice": true
|
||||
},
|
||||
"groq/playai-tts": {
|
||||
"max_tokens": 10000,
|
||||
"max_input_tokens": 10000,
|
||||
"max_output_tokens": 10000,
|
||||
"input_cost_per_character": 5e-05,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "audio_speech"
|
||||
},
|
||||
"groq/whisper-large-v3": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00003083,
|
||||
"output_cost_per_second": 0,
|
||||
"litellm_provider": "groq"
|
||||
"input_cost_per_second": 3.083e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "audio_transcription"
|
||||
},
|
||||
"groq/whisper-large-v3-turbo": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00001111,
|
||||
"output_cost_per_second": 0,
|
||||
"litellm_provider": "groq"
|
||||
"input_cost_per_second": 1.111e-05,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "audio_transcription"
|
||||
},
|
||||
"groq/distil-whisper-large-v3-en": {
|
||||
"mode": "audio_transcription",
|
||||
"input_cost_per_second": 0.00000556,
|
||||
"output_cost_per_second": 0,
|
||||
"litellm_provider": "groq"
|
||||
"input_cost_per_second": 5.56e-06,
|
||||
"output_cost_per_second": 0.0,
|
||||
"litellm_provider": "groq",
|
||||
"mode": "audio_transcription"
|
||||
},
|
||||
"cerebras/llama3.1-8b": {
|
||||
"max_tokens": 128000,
|
||||
|
|
|
|||
|
|
@ -7,12 +7,18 @@ from typing import Dict, List, Optional, cast
|
|||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
from litellm._logging import verbose_proxy_logger
|
||||
from litellm.proxy.auth.user_api_key_auth import user_api_key_auth
|
||||
from litellm.proxy.guardrails.guardrail_registry import GuardrailRegistry
|
||||
from litellm.types.guardrails import (
|
||||
PII_ENTITY_CATEGORIES_MAP,
|
||||
Guardrail,
|
||||
GuardrailEventHooks,
|
||||
GuardrailInfoResponse,
|
||||
GuardrailUIAddGuardrailSettings,
|
||||
ListGuardrailsResponse,
|
||||
PiiAction,
|
||||
PiiEntityType,
|
||||
)
|
||||
|
||||
#### GUARDRAILS ENDPOINTS ####
|
||||
|
|
@ -92,6 +98,73 @@ async def list_guardrails():
|
|||
return _get_guardrails_list_response(_guardrails_config)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/v2/guardrails/list",
|
||||
tags=["Guardrails"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
response_model=ListGuardrailsResponse,
|
||||
)
|
||||
async def list_guardrails_v2():
|
||||
"""
|
||||
List the guardrails that are available in the database using GuardrailRegistry
|
||||
|
||||
👉 [Guardrail docs](https://docs.litellm.ai/docs/proxy/guardrails/quick_start)
|
||||
|
||||
Example Request:
|
||||
```bash
|
||||
curl -X GET "http://localhost:4000/v2/guardrails/list" -H "Authorization: Bearer <your_api_key>"
|
||||
```
|
||||
|
||||
Example Response:
|
||||
```json
|
||||
{
|
||||
"guardrails": [
|
||||
{
|
||||
"guardrail_id": "123e4567-e89b-12d3-a456-426614174000",
|
||||
"guardrail_name": "my-bedrock-guard",
|
||||
"litellm_params": {
|
||||
"guardrail": "bedrock",
|
||||
"mode": "pre_call",
|
||||
"guardrailIdentifier": "ff6ujrregl1q",
|
||||
"guardrailVersion": "DRAFT",
|
||||
"default_on": true
|
||||
},
|
||||
"guardrail_info": {
|
||||
"description": "Bedrock content moderation guardrail"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
"""
|
||||
from litellm.proxy.proxy_server import prisma_client
|
||||
|
||||
if prisma_client is None:
|
||||
raise HTTPException(status_code=500, detail="Prisma client not initialized")
|
||||
|
||||
try:
|
||||
guardrails = await GUARDRAIL_REGISTRY.get_all_guardrails_from_db(
|
||||
prisma_client=prisma_client
|
||||
)
|
||||
|
||||
guardrail_configs: List[GuardrailInfoResponse] = []
|
||||
for guardrail in guardrails:
|
||||
guardrail_configs.append(
|
||||
GuardrailInfoResponse(
|
||||
guardrail_name=guardrail.get("guardrail_name"),
|
||||
litellm_params=guardrail.get("litellm_params"),
|
||||
guardrail_info=guardrail.get("guardrail_info"),
|
||||
created_at=guardrail.get("created_at"),
|
||||
updated_at=guardrail.get("updated_at"),
|
||||
)
|
||||
)
|
||||
|
||||
return ListGuardrailsResponse(guardrails=guardrail_configs)
|
||||
except Exception as e:
|
||||
verbose_proxy_logger.exception(f"Error getting guardrails from db: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
class CreateGuardrailRequest(BaseModel):
|
||||
guardrail: Guardrail
|
||||
|
||||
|
|
@ -160,6 +233,7 @@ async def create_guardrail(request: CreateGuardrailRequest):
|
|||
)
|
||||
return result
|
||||
except Exception as e:
|
||||
verbose_proxy_logger.exception(f"Error adding guardrail to db: {e}")
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
|
|
@ -353,3 +427,30 @@ async def delete_guardrail(guardrail_id: str):
|
|||
raise e
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=str(e))
|
||||
|
||||
|
||||
@router.get(
|
||||
"/guardrails/ui/add_guardrail_settings",
|
||||
tags=["Guardrails"],
|
||||
dependencies=[Depends(user_api_key_auth)],
|
||||
)
|
||||
async def get_guardrail_ui_settings():
|
||||
"""
|
||||
Get the UI settings for the guardrails
|
||||
|
||||
Returns:
|
||||
- Supported entities for guardrails
|
||||
- Supported modes for guardrails
|
||||
- PII entity categories for UI organization
|
||||
"""
|
||||
# Convert the PII_ENTITY_CATEGORIES_MAP to the format expected by the UI
|
||||
category_maps = []
|
||||
for category, entities in PII_ENTITY_CATEGORIES_MAP.items():
|
||||
category_maps.append({"category": category, "entities": entities})
|
||||
|
||||
return GuardrailUIAddGuardrailSettings(
|
||||
supported_entities=list(PiiEntityType),
|
||||
supported_actions=list(PiiAction),
|
||||
supported_modes=list(GuardrailEventHooks),
|
||||
pii_entity_categories=category_maps,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
from datetime import datetime, timezone
|
||||
from typing import List, Optional
|
||||
|
||||
from litellm.litellm_core_utils.safe_json_dumps import safe_dumps
|
||||
from litellm.proxy.utils import PrismaClient
|
||||
from litellm.types.guardrails import Guardrail, SupportedGuardrailIntegrations
|
||||
|
||||
|
|
@ -48,8 +49,8 @@ class GuardrailRegistry:
|
|||
"""
|
||||
try:
|
||||
guardrail_name = guardrail.get("guardrail_name")
|
||||
litellm_params = guardrail.get("litellm_params", {})
|
||||
guardrail_info = guardrail.get("guardrail_info", {})
|
||||
litellm_params: str = safe_dumps(guardrail.get("litellm_params", {}))
|
||||
guardrail_info: str = safe_dumps(guardrail.get("guardrail_info", {}))
|
||||
|
||||
# Create guardrail in DB
|
||||
created_guardrail = await prisma_client.db.litellm_guardrailstable.create(
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Any, Dict, List, Literal, Optional, TypedDict, Union
|
||||
|
||||
|
|
@ -85,6 +86,20 @@ class PiiAction(str, Enum):
|
|||
MASK = "MASK"
|
||||
|
||||
|
||||
class PiiEntityCategory(str, Enum):
|
||||
GENERAL = "General"
|
||||
FINANCE = "Finance"
|
||||
USA = "USA"
|
||||
UK = "UK"
|
||||
SPAIN = "Spain"
|
||||
ITALY = "Italy"
|
||||
POLAND = "Poland"
|
||||
SINGAPORE = "Singapore"
|
||||
AUSTRALIA = "Australia"
|
||||
INDIA = "India"
|
||||
FINLAND = "Finland"
|
||||
|
||||
|
||||
class PiiEntityType(str, Enum):
|
||||
# General
|
||||
CREDIT_CARD = "CREDIT_CARD"
|
||||
|
|
@ -137,7 +152,65 @@ class PiiEntityType(str, Enum):
|
|||
FI_PERSONAL_IDENTITY_CODE = "FI_PERSONAL_IDENTITY_CODE"
|
||||
|
||||
|
||||
class LitellmParams(TypedDict):
|
||||
# Define mappings of PII entity types by category
|
||||
PII_ENTITY_CATEGORIES_MAP = {
|
||||
PiiEntityCategory.GENERAL: [
|
||||
PiiEntityType.DATE_TIME,
|
||||
PiiEntityType.EMAIL_ADDRESS,
|
||||
PiiEntityType.IP_ADDRESS,
|
||||
PiiEntityType.NRP,
|
||||
PiiEntityType.LOCATION,
|
||||
PiiEntityType.PERSON,
|
||||
PiiEntityType.PHONE_NUMBER,
|
||||
PiiEntityType.MEDICAL_LICENSE,
|
||||
PiiEntityType.URL,
|
||||
],
|
||||
PiiEntityCategory.FINANCE: [
|
||||
PiiEntityType.CREDIT_CARD,
|
||||
PiiEntityType.CRYPTO,
|
||||
PiiEntityType.IBAN_CODE,
|
||||
],
|
||||
PiiEntityCategory.USA: [
|
||||
PiiEntityType.US_BANK_NUMBER,
|
||||
PiiEntityType.US_DRIVER_LICENSE,
|
||||
PiiEntityType.US_ITIN,
|
||||
PiiEntityType.US_PASSPORT,
|
||||
PiiEntityType.US_SSN,
|
||||
],
|
||||
PiiEntityCategory.UK: [PiiEntityType.UK_NHS, PiiEntityType.UK_NINO],
|
||||
PiiEntityCategory.SPAIN: [PiiEntityType.ES_NIF, PiiEntityType.ES_NIE],
|
||||
PiiEntityCategory.ITALY: [
|
||||
PiiEntityType.IT_FISCAL_CODE,
|
||||
PiiEntityType.IT_DRIVER_LICENSE,
|
||||
PiiEntityType.IT_VAT_CODE,
|
||||
PiiEntityType.IT_PASSPORT,
|
||||
PiiEntityType.IT_IDENTITY_CARD,
|
||||
],
|
||||
PiiEntityCategory.POLAND: [PiiEntityType.PL_PESEL],
|
||||
PiiEntityCategory.SINGAPORE: [PiiEntityType.SG_NRIC_FIN, PiiEntityType.SG_UEN],
|
||||
PiiEntityCategory.AUSTRALIA: [
|
||||
PiiEntityType.AU_ABN,
|
||||
PiiEntityType.AU_ACN,
|
||||
PiiEntityType.AU_TFN,
|
||||
PiiEntityType.AU_MEDICARE,
|
||||
],
|
||||
PiiEntityCategory.INDIA: [
|
||||
PiiEntityType.IN_PAN,
|
||||
PiiEntityType.IN_AADHAAR,
|
||||
PiiEntityType.IN_VEHICLE_REGISTRATION,
|
||||
PiiEntityType.IN_VOTER,
|
||||
PiiEntityType.IN_PASSPORT,
|
||||
],
|
||||
PiiEntityCategory.FINLAND: [PiiEntityType.FI_PERSONAL_IDENTITY_CODE],
|
||||
}
|
||||
|
||||
|
||||
class PiiEntityCategoryMap(TypedDict):
|
||||
category: PiiEntityCategory
|
||||
entities: List[PiiEntityType]
|
||||
|
||||
|
||||
class LitellmParams(TypedDict, total=False):
|
||||
guardrail: str
|
||||
mode: str
|
||||
api_key: Optional[str]
|
||||
|
|
@ -178,6 +251,8 @@ class Guardrail(TypedDict, total=False):
|
|||
guardrail_name: str
|
||||
litellm_params: LitellmParams
|
||||
guardrail_info: Optional[Dict]
|
||||
created_at: Optional[datetime]
|
||||
updated_at: Optional[datetime]
|
||||
|
||||
|
||||
class guardrailConfig(TypedDict):
|
||||
|
|
@ -214,6 +289,8 @@ class GuardrailInfoResponse(BaseModel):
|
|||
guardrail_name: str
|
||||
litellm_params: GuardrailLiteLLMParamsResponse
|
||||
guardrail_info: Optional[Dict]
|
||||
created_at: Optional[datetime]
|
||||
updated_at: Optional[datetime]
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
|
|
@ -221,3 +298,10 @@ class GuardrailInfoResponse(BaseModel):
|
|||
|
||||
class ListGuardrailsResponse(BaseModel):
|
||||
guardrails: List[GuardrailInfoResponse]
|
||||
|
||||
|
||||
class GuardrailUIAddGuardrailSettings(BaseModel):
|
||||
supported_entities: List[PiiEntityType]
|
||||
supported_actions: List[PiiAction]
|
||||
supported_modes: List[GuardrailEventHooks]
|
||||
pii_entity_categories: List[PiiEntityCategoryMap]
|
||||
|
|
|
|||
|
|
@ -1,104 +1,140 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeaderCell,
|
||||
TableRow,
|
||||
Card,
|
||||
Text,
|
||||
Button,
|
||||
Icon,
|
||||
TextInput,
|
||||
} from "@tremor/react";
|
||||
import { getGuardrailsList } from "./networking";
|
||||
import {
|
||||
PlusIcon,
|
||||
} from "@heroicons/react/outline";
|
||||
import { Modal, message } from "antd";
|
||||
import { getGuardrailsList, deleteGuardrailCall } from "./networking";
|
||||
import AddGuardrailForm from "./guardrails/add_guardrail_form";
|
||||
import GuardrailTable from "./guardrails/guardrail_table";
|
||||
|
||||
interface GuardrailsPanelProps {
|
||||
accessToken: string | null;
|
||||
}
|
||||
|
||||
interface GuardrailsResponse {
|
||||
guardrails: GuardrailItem[];
|
||||
interface GuardrailItem {
|
||||
guardrail_id?: string;
|
||||
guardrail_name: string | null;
|
||||
litellm_params: {
|
||||
guardrail: string;
|
||||
mode: string;
|
||||
default_on: boolean;
|
||||
};
|
||||
guardrail_info: Record<string, any> | null;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
interface GuardrailItem {
|
||||
guardrail_name: string | null;
|
||||
litellm_params: {
|
||||
guardrail: string;
|
||||
mode: string;
|
||||
default_on: boolean;
|
||||
};
|
||||
guardrail_info: Record<string, any> | null;
|
||||
interface GuardrailsResponse {
|
||||
guardrails: GuardrailItem[];
|
||||
}
|
||||
|
||||
const GuardrailsPanel: React.FC<GuardrailsPanelProps> = ({ accessToken }) => {
|
||||
const [guardrailsList, setGuardrailsList] = useState<GuardrailItem[]>([]);
|
||||
const [isAddModalVisible, setIsAddModalVisible] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [isDeleting, setIsDeleting] = useState(false);
|
||||
const [guardrailToDelete, setGuardrailToDelete] = useState<{id: string, name: string} | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchGuardrails = async () => {
|
||||
if (!accessToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchGuardrails = async () => {
|
||||
try {
|
||||
const response: GuardrailsResponse = await getGuardrailsList(accessToken);
|
||||
console.log(`guardrails: ${JSON.stringify(response)}`);
|
||||
setGuardrailsList(response.guardrails);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Error fetching guardrails:', error);
|
||||
}
|
||||
};
|
||||
setIsLoading(true);
|
||||
try {
|
||||
const response: GuardrailsResponse = await getGuardrailsList(accessToken);
|
||||
console.log(`guardrails: ${JSON.stringify(response)}`);
|
||||
setGuardrailsList(response.guardrails);
|
||||
} catch (error) {
|
||||
console.error('Error fetching guardrails:', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
fetchGuardrails();
|
||||
}, [accessToken]);
|
||||
|
||||
const handleAddGuardrail = () => {
|
||||
setIsAddModalVisible(true);
|
||||
};
|
||||
|
||||
const handleCloseModal = () => {
|
||||
setIsAddModalVisible(false);
|
||||
};
|
||||
|
||||
const handleSuccess = () => {
|
||||
fetchGuardrails();
|
||||
};
|
||||
|
||||
const handleDeleteClick = (guardrailId: string, guardrailName: string) => {
|
||||
setGuardrailToDelete({id: guardrailId, name: guardrailName});
|
||||
};
|
||||
|
||||
const handleDeleteConfirm = async () => {
|
||||
if (!guardrailToDelete || !accessToken) return;
|
||||
|
||||
setIsDeleting(true);
|
||||
try {
|
||||
await deleteGuardrailCall(accessToken, guardrailToDelete.id);
|
||||
message.success(`Guardrail "${guardrailToDelete.name}" deleted successfully`);
|
||||
fetchGuardrails(); // Refresh the list
|
||||
} catch (error) {
|
||||
console.error('Error deleting guardrail:', error);
|
||||
message.error('Failed to delete guardrail');
|
||||
} finally {
|
||||
setIsDeleting(false);
|
||||
setGuardrailToDelete(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteCancel = () => {
|
||||
setGuardrailToDelete(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="w-full mx-auto flex-auto overflow-y-auto m-8 p-2">
|
||||
<Text className="mb-4">
|
||||
Configured guardrails and their current status. Setup guardrails in config.yaml.{" "}
|
||||
<a
|
||||
href="https://docs.litellm.ai/docs/proxy/guardrails/quick_start"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-500 hover:text-blue-700 underline"
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<Button
|
||||
icon={PlusIcon}
|
||||
onClick={handleAddGuardrail}
|
||||
disabled={!accessToken}
|
||||
>
|
||||
Docs
|
||||
</a>
|
||||
</Text>
|
||||
<Card>
|
||||
<Table>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableHeaderCell>Guardrail Name</TableHeaderCell>
|
||||
<TableHeaderCell>Mode</TableHeaderCell>
|
||||
<TableHeaderCell>Status</TableHeaderCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
Add Guardrail
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<GuardrailTable
|
||||
guardrailsList={guardrailsList}
|
||||
isLoading={isLoading}
|
||||
onDeleteClick={handleDeleteClick}
|
||||
/>
|
||||
|
||||
<TableBody>
|
||||
{ (!guardrailsList || guardrailsList.length === 0) ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={3} className="mt-4 text-gray-500 text-center py-4">No guardrails configured</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
guardrailsList?.map((guardrail: GuardrailItem, index: number) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell>{guardrail.guardrail_name}</TableCell>
|
||||
<TableCell>{guardrail.litellm_params.mode}</TableCell>
|
||||
<TableCell>
|
||||
<div className={`inline-flex rounded-full px-2 py-1 text-xs font-medium
|
||||
${guardrail.litellm_params.default_on
|
||||
? 'bg-green-100 text-green-800' // Always On styling
|
||||
: 'bg-gray-100 text-gray-800' // Per Request styling
|
||||
}`}>
|
||||
{guardrail.litellm_params.default_on ? 'Always On' : 'Per Request'}
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</Card>
|
||||
<AddGuardrailForm
|
||||
visible={isAddModalVisible}
|
||||
onClose={handleCloseModal}
|
||||
accessToken={accessToken}
|
||||
onSuccess={handleSuccess}
|
||||
/>
|
||||
|
||||
<Modal
|
||||
title="Delete Guardrail"
|
||||
open={guardrailToDelete !== null}
|
||||
onOk={handleDeleteConfirm}
|
||||
onCancel={handleDeleteCancel}
|
||||
confirmLoading={isDeleting}
|
||||
okText="Delete"
|
||||
okButtonProps={{ danger: true }}
|
||||
>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,504 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { Card, Form, Typography, Select, Input, Switch, Tooltip, Modal, message, Divider, Space, Tag, Image, Steps } from 'antd';
|
||||
import { Button, TextInput } from '@tremor/react';
|
||||
import type { FormInstance } from 'antd';
|
||||
import { GuardrailProviders, guardrail_provider_map, provider_specific_fields, guardrailLogoMap } from './guardrail_info_helpers';
|
||||
import { createGuardrailCall, getGuardrailUISettings } from '../networking';
|
||||
import PiiConfiguration from './pii_configuration';
|
||||
|
||||
const { Title, Text, Link } = Typography;
|
||||
const { Option } = Select;
|
||||
const { Step } = Steps;
|
||||
|
||||
interface AddGuardrailFormProps {
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
accessToken: string | null;
|
||||
onSuccess: () => void;
|
||||
}
|
||||
|
||||
interface GuardrailSettings {
|
||||
supported_entities: string[];
|
||||
supported_actions: string[];
|
||||
supported_modes: string[];
|
||||
pii_entity_categories: Array<{
|
||||
category: string;
|
||||
entities: string[];
|
||||
}>;
|
||||
}
|
||||
|
||||
interface LiteLLMParams {
|
||||
guardrail: string;
|
||||
mode: string;
|
||||
default_on: boolean;
|
||||
[key: string]: any; // Allow additional properties for specific guardrails
|
||||
}
|
||||
|
||||
const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({
|
||||
visible,
|
||||
onClose,
|
||||
accessToken,
|
||||
onSuccess
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [selectedProvider, setSelectedProvider] = useState<string | null>(null);
|
||||
const [guardrailSettings, setGuardrailSettings] = useState<GuardrailSettings | null>(null);
|
||||
const [selectedEntities, setSelectedEntities] = useState<string[]>([]);
|
||||
const [selectedActions, setSelectedActions] = useState<{[key: string]: string}>({});
|
||||
const [currentStep, setCurrentStep] = useState(0);
|
||||
|
||||
// Fetch guardrail settings when the component mounts
|
||||
useEffect(() => {
|
||||
const fetchGuardrailSettings = async () => {
|
||||
try {
|
||||
if (!accessToken) return;
|
||||
|
||||
const data = await getGuardrailUISettings(accessToken);
|
||||
setGuardrailSettings(data);
|
||||
} catch (error) {
|
||||
console.error('Error fetching guardrail settings:', error);
|
||||
message.error('Failed to load guardrail settings');
|
||||
}
|
||||
};
|
||||
|
||||
fetchGuardrailSettings();
|
||||
}, [accessToken]);
|
||||
|
||||
const handleProviderChange = (value: string) => {
|
||||
setSelectedProvider(value);
|
||||
// Reset form fields that are provider-specific
|
||||
form.setFieldsValue({
|
||||
config: undefined
|
||||
});
|
||||
|
||||
// Reset PII selections when changing provider
|
||||
setSelectedEntities([]);
|
||||
setSelectedActions({});
|
||||
};
|
||||
|
||||
const handleEntitySelect = (entity: string) => {
|
||||
setSelectedEntities(prev => {
|
||||
if (prev.includes(entity)) {
|
||||
return prev.filter(e => e !== entity);
|
||||
} else {
|
||||
return [...prev, entity];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleActionSelect = (entity: string, action: string) => {
|
||||
setSelectedActions(prev => ({
|
||||
...prev,
|
||||
[entity]: action
|
||||
}));
|
||||
};
|
||||
|
||||
const nextStep = async () => {
|
||||
try {
|
||||
// Validate current step fields
|
||||
if (currentStep === 0) {
|
||||
await form.validateFields(['guardrail_name', 'provider', 'mode', 'default_on']);
|
||||
}
|
||||
setCurrentStep(currentStep + 1);
|
||||
} catch (error) {
|
||||
console.error("Form validation failed:", error);
|
||||
}
|
||||
};
|
||||
|
||||
const prevStep = () => {
|
||||
setCurrentStep(currentStep - 1);
|
||||
};
|
||||
|
||||
const resetForm = () => {
|
||||
form.resetFields();
|
||||
setSelectedProvider(null);
|
||||
setSelectedEntities([]);
|
||||
setSelectedActions({});
|
||||
setCurrentStep(0);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
resetForm();
|
||||
onClose();
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
// First validate currently visible fields (step 1)
|
||||
await form.validateFields();
|
||||
|
||||
// After validation, fetch *all* form values (including those from previous steps)
|
||||
const values = form.getFieldsValue(true);
|
||||
|
||||
// Get the guardrail provider value from the map
|
||||
const guardrailProvider = guardrail_provider_map[values.provider];
|
||||
|
||||
// Prepare the guardrail data with proper typings
|
||||
const guardrailData: {
|
||||
guardrail_name: string;
|
||||
litellm_params: {
|
||||
guardrail: string;
|
||||
mode: string;
|
||||
default_on: boolean;
|
||||
[key: string]: any; // Allow dynamic properties
|
||||
};
|
||||
guardrail_info: any;
|
||||
} = {
|
||||
guardrail_name: values.guardrail_name,
|
||||
litellm_params: {
|
||||
guardrail: guardrailProvider,
|
||||
mode: values.mode,
|
||||
default_on: values.default_on
|
||||
},
|
||||
guardrail_info: {}
|
||||
};
|
||||
|
||||
// For Presidio PII, add the entity and action configurations
|
||||
if (values.provider === 'PresidioPII' && selectedEntities.length > 0) {
|
||||
const piiEntitiesConfig: {[key: string]: string} = {};
|
||||
selectedEntities.forEach(entity => {
|
||||
piiEntitiesConfig[entity] = selectedActions[entity] || 'MASK'; // Default to MASK if no action selected
|
||||
});
|
||||
|
||||
guardrailData.litellm_params.pii_entities_config = piiEntitiesConfig;
|
||||
}
|
||||
// Add config values to the guardrail_info if provided
|
||||
else if (values.config) {
|
||||
try {
|
||||
const configObj = JSON.parse(values.config);
|
||||
// For some guardrails, the config values need to be in litellm_params
|
||||
// Especially for providers like Bedrock that need guardrailIdentifier and guardrailVersion
|
||||
if (values.provider === 'Bedrock' && configObj) {
|
||||
if (configObj.guardrail_id) {
|
||||
guardrailData.litellm_params.guardrailIdentifier = configObj.guardrail_id;
|
||||
}
|
||||
if (configObj.guardrail_version) {
|
||||
guardrailData.litellm_params.guardrailVersion = configObj.guardrail_version;
|
||||
}
|
||||
} else {
|
||||
// For other providers, add the config to guardrail_info
|
||||
guardrailData.guardrail_info = configObj;
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('Invalid JSON in configuration');
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error("No access token available");
|
||||
}
|
||||
|
||||
console.log("Sending guardrail data:", JSON.stringify(guardrailData));
|
||||
await createGuardrailCall(accessToken, guardrailData);
|
||||
|
||||
message.success('Guardrail created successfully');
|
||||
|
||||
// Reset form and close modal
|
||||
resetForm();
|
||||
onSuccess();
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error("Failed to create guardrail:", error);
|
||||
message.error('Failed to create guardrail: ' + (error instanceof Error ? error.message : String(error)));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const renderBasicInfo = () => {
|
||||
return (
|
||||
<>
|
||||
<Form.Item
|
||||
name="guardrail_name"
|
||||
label="Guardrail Name"
|
||||
rules={[{ required: true, message: 'Please enter a guardrail name' }]}
|
||||
>
|
||||
<TextInput placeholder="Enter a name for this guardrail" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="provider"
|
||||
label="Guardrail Provider"
|
||||
rules={[{ required: true, message: 'Please select a provider' }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="Select a guardrail provider"
|
||||
onChange={handleProviderChange}
|
||||
optionLabelProp="label"
|
||||
>
|
||||
{Object.entries(GuardrailProviders).map(([key, value]) => (
|
||||
<Option
|
||||
key={key}
|
||||
value={key}
|
||||
label={value}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
{guardrailLogoMap[value] && (
|
||||
<img
|
||||
src={guardrailLogoMap[value]}
|
||||
alt=""
|
||||
style={{
|
||||
height: '20px',
|
||||
width: '20px',
|
||||
marginRight: '8px',
|
||||
objectFit: 'contain'
|
||||
}}
|
||||
onError={(e) => {
|
||||
// Hide broken image icon if image fails to load
|
||||
e.currentTarget.style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span>{value}</span>
|
||||
</div>
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="mode"
|
||||
label="Mode"
|
||||
tooltip="How the guardrail should be applied"
|
||||
rules={[{ required: true, message: 'Please select a mode' }]}
|
||||
>
|
||||
<Select>
|
||||
{guardrailSettings?.supported_modes?.map(mode => (
|
||||
<Option key={mode} value={mode}>{mode}</Option>
|
||||
)) || (
|
||||
<>
|
||||
<Option value="pre_call">pre_call</Option>
|
||||
<Option value="post_call">post_call</Option>
|
||||
</>
|
||||
)}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="default_on"
|
||||
label="Always On"
|
||||
tooltip="If enabled, this guardrail will be applied to all requests by default"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const renderPiiConfiguration = () => {
|
||||
if (!guardrailSettings || selectedProvider !== 'PresidioPII') return null;
|
||||
|
||||
return (
|
||||
<PiiConfiguration
|
||||
entities={guardrailSettings.supported_entities}
|
||||
actions={guardrailSettings.supported_actions}
|
||||
selectedEntities={selectedEntities}
|
||||
selectedActions={selectedActions}
|
||||
onEntitySelect={handleEntitySelect}
|
||||
onActionSelect={handleActionSelect}
|
||||
entityCategories={guardrailSettings.pii_entity_categories}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const renderProviderSpecificConfig = () => {
|
||||
if (!selectedProvider || selectedProvider === 'PresidioPII') return null;
|
||||
|
||||
switch (selectedProvider) {
|
||||
case 'Aporia':
|
||||
return (
|
||||
<Form.Item
|
||||
label="Aporia Configuration"
|
||||
name="config"
|
||||
tooltip="JSON configuration for Aporia"
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
"api_key": "your_aporia_api_key",
|
||||
"project_name": "your_project_name"
|
||||
}`}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'AimSecurity':
|
||||
return (
|
||||
<Form.Item
|
||||
label="Aim Security Configuration"
|
||||
name="config"
|
||||
tooltip="JSON configuration for Aim Security"
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
"api_key": "your_aim_api_key"
|
||||
}`}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'Bedrock':
|
||||
return (
|
||||
<Form.Item
|
||||
label="Amazon Bedrock Configuration"
|
||||
name="config"
|
||||
tooltip="JSON configuration for Amazon Bedrock guardrails"
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
"guardrail_id": "your_guardrail_id",
|
||||
"guardrail_version": "your_guardrail_version"
|
||||
}`}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'GuardrailsAI':
|
||||
return (
|
||||
<Form.Item
|
||||
label="Guardrails.ai Configuration"
|
||||
name="config"
|
||||
tooltip="JSON configuration for Guardrails.ai"
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
"api_key": "your_guardrails_api_key",
|
||||
"guardrail_id": "your_guardrail_id"
|
||||
}`}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'LakeraAI':
|
||||
return (
|
||||
<Form.Item
|
||||
label="Lakera AI Configuration"
|
||||
name="config"
|
||||
tooltip="JSON configuration for Lakera AI"
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
"api_key": "your_lakera_api_key"
|
||||
}`}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'PromptInjection':
|
||||
return (
|
||||
<Form.Item
|
||||
label="Prompt Injection Configuration"
|
||||
name="config"
|
||||
tooltip="JSON configuration for prompt injection detection"
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
"threshold": 0.8
|
||||
}`}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<Form.Item
|
||||
label="Custom Configuration"
|
||||
name="config"
|
||||
tooltip="JSON configuration for your custom guardrail"
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
"key1": "value1",
|
||||
"key2": "value2"
|
||||
}`}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const renderStepContent = () => {
|
||||
switch (currentStep) {
|
||||
case 0:
|
||||
return renderBasicInfo();
|
||||
case 1:
|
||||
if (selectedProvider === 'PresidioPII') {
|
||||
return renderPiiConfiguration();
|
||||
} else {
|
||||
return renderProviderSpecificConfig();
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const renderStepButtons = () => {
|
||||
return (
|
||||
<div className="flex justify-end space-x-2 mt-4">
|
||||
{currentStep > 0 && (
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={prevStep}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
)}
|
||||
{currentStep < 1 && (
|
||||
<Button
|
||||
onClick={nextStep}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
)}
|
||||
{currentStep === 1 && (
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
loading={loading}
|
||||
>
|
||||
Create Guardrail
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={handleClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="Add Guardrail"
|
||||
open={visible}
|
||||
onCancel={handleClose}
|
||||
footer={null}
|
||||
width={700}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={{
|
||||
mode: guardrailSettings?.supported_modes?.[0] || "pre_call",
|
||||
default_on: false
|
||||
}}
|
||||
>
|
||||
<Steps current={currentStep} className="mb-6">
|
||||
<Step title="Basic Info" />
|
||||
<Step title={selectedProvider === 'PresidioPII' ? "PII Configuration" : "Provider Configuration"} />
|
||||
</Steps>
|
||||
|
||||
{renderStepContent()}
|
||||
{renderStepButtons()}
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddGuardrailForm;
|
||||
|
|
@ -0,0 +1,456 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { Form, Typography, Select, Input, Switch, Modal, message, Divider } from 'antd';
|
||||
import { Button, TextInput } from '@tremor/react';
|
||||
import { GuardrailProviders, guardrail_provider_map, provider_specific_fields, guardrailLogoMap } from './guardrail_info_helpers';
|
||||
import { getGuardrailUISettings } from '../networking';
|
||||
import PiiConfiguration from './pii_configuration';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
const { Option } = Select;
|
||||
|
||||
interface EditGuardrailFormProps {
|
||||
visible: boolean;
|
||||
onClose: () => void;
|
||||
accessToken: string | null;
|
||||
onSuccess: () => void;
|
||||
guardrailId: string;
|
||||
initialValues: {
|
||||
guardrail_name: string;
|
||||
provider: string;
|
||||
mode: string;
|
||||
default_on: boolean;
|
||||
pii_entities_config?: {[key: string]: string};
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
interface GuardrailSettings {
|
||||
supported_entities: string[];
|
||||
supported_actions: string[];
|
||||
supported_modes: string[];
|
||||
pii_entity_categories: Array<{
|
||||
category: string;
|
||||
entities: string[];
|
||||
}>;
|
||||
}
|
||||
|
||||
const EditGuardrailForm: React.FC<EditGuardrailFormProps> = ({
|
||||
visible,
|
||||
onClose,
|
||||
accessToken,
|
||||
onSuccess,
|
||||
guardrailId,
|
||||
initialValues
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [selectedProvider, setSelectedProvider] = useState<string | null>(initialValues?.provider || null);
|
||||
const [guardrailSettings, setGuardrailSettings] = useState<GuardrailSettings | null>(null);
|
||||
const [selectedEntities, setSelectedEntities] = useState<string[]>([]);
|
||||
const [selectedActions, setSelectedActions] = useState<{[key: string]: string}>({});
|
||||
|
||||
// Fetch guardrail settings when the component mounts
|
||||
useEffect(() => {
|
||||
const fetchGuardrailSettings = async () => {
|
||||
try {
|
||||
if (!accessToken) return;
|
||||
|
||||
const data = await getGuardrailUISettings(accessToken);
|
||||
setGuardrailSettings(data);
|
||||
} catch (error) {
|
||||
console.error('Error fetching guardrail settings:', error);
|
||||
message.error('Failed to load guardrail settings');
|
||||
}
|
||||
};
|
||||
|
||||
fetchGuardrailSettings();
|
||||
}, [accessToken]);
|
||||
|
||||
// Initialize selected entities and actions from initialValues
|
||||
useEffect(() => {
|
||||
if (initialValues?.pii_entities_config && Object.keys(initialValues.pii_entities_config).length > 0) {
|
||||
const entities = Object.keys(initialValues.pii_entities_config);
|
||||
setSelectedEntities(entities);
|
||||
setSelectedActions(initialValues.pii_entities_config);
|
||||
}
|
||||
}, [initialValues]);
|
||||
|
||||
const handleProviderChange = (value: string) => {
|
||||
setSelectedProvider(value);
|
||||
// Reset form fields that are provider-specific
|
||||
form.setFieldsValue({
|
||||
config: undefined
|
||||
});
|
||||
|
||||
// Reset PII selections when changing provider
|
||||
setSelectedEntities([]);
|
||||
setSelectedActions({});
|
||||
};
|
||||
|
||||
const handleEntitySelect = (entity: string) => {
|
||||
setSelectedEntities(prev => {
|
||||
if (prev.includes(entity)) {
|
||||
return prev.filter(e => e !== entity);
|
||||
} else {
|
||||
return [...prev, entity];
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const handleActionSelect = (entity: string, action: string) => {
|
||||
setSelectedActions(prev => ({
|
||||
...prev,
|
||||
[entity]: action
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
const values = await form.validateFields();
|
||||
|
||||
// Get the guardrail provider value from the map
|
||||
const guardrailProvider = guardrail_provider_map[values.provider];
|
||||
|
||||
// Prepare the guardrail data with proper types for litellm_params
|
||||
const guardrailData: {
|
||||
guardrail_id: string;
|
||||
guardrail: {
|
||||
guardrail_name: string;
|
||||
litellm_params: {
|
||||
guardrail: string;
|
||||
mode: string;
|
||||
default_on: boolean;
|
||||
[key: string]: any; // Allow dynamic properties
|
||||
};
|
||||
guardrail_info: any;
|
||||
}
|
||||
} = {
|
||||
guardrail_id: guardrailId,
|
||||
guardrail: {
|
||||
guardrail_name: values.guardrail_name,
|
||||
litellm_params: {
|
||||
guardrail: guardrailProvider,
|
||||
mode: values.mode,
|
||||
default_on: values.default_on
|
||||
},
|
||||
guardrail_info: {}
|
||||
}
|
||||
};
|
||||
|
||||
// For Presidio PII, add the entity and action configurations
|
||||
if (values.provider === 'PresidioPII' && selectedEntities.length > 0) {
|
||||
const piiEntitiesConfig: {[key: string]: string} = {};
|
||||
selectedEntities.forEach(entity => {
|
||||
piiEntitiesConfig[entity] = selectedActions[entity] || 'MASK'; // Default to MASK if no action selected
|
||||
});
|
||||
|
||||
guardrailData.guardrail.litellm_params.pii_entities_config = piiEntitiesConfig;
|
||||
}
|
||||
// Add config values to the guardrail_info if provided
|
||||
else if (values.config) {
|
||||
try {
|
||||
const configObj = JSON.parse(values.config);
|
||||
// For some guardrails, the config values need to be in litellm_params
|
||||
// Especially for providers like Bedrock that need guardrailIdentifier and guardrailVersion
|
||||
if (values.provider === 'Bedrock' && configObj) {
|
||||
if (configObj.guardrail_id) {
|
||||
guardrailData.guardrail.litellm_params.guardrailIdentifier = configObj.guardrail_id;
|
||||
}
|
||||
if (configObj.guardrail_version) {
|
||||
guardrailData.guardrail.litellm_params.guardrailVersion = configObj.guardrail_version;
|
||||
}
|
||||
} else {
|
||||
// For other providers, add the config to guardrail_info
|
||||
guardrailData.guardrail.guardrail_info = configObj;
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('Invalid JSON in configuration');
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error("No access token available");
|
||||
}
|
||||
|
||||
console.log("Sending guardrail update data:", JSON.stringify(guardrailData));
|
||||
|
||||
// Call the update endpoint
|
||||
const url = `/guardrails/${guardrailId}`;
|
||||
const response = await fetch(url, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Authorization": `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(guardrailData),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.text();
|
||||
throw new Error(errorData || "Failed to update guardrail");
|
||||
}
|
||||
|
||||
message.success('Guardrail updated successfully');
|
||||
|
||||
// Reset and close
|
||||
onSuccess();
|
||||
onClose();
|
||||
} catch (error) {
|
||||
console.error("Failed to update guardrail:", error);
|
||||
message.error('Failed to update guardrail: ' + (error instanceof Error ? error.message : String(error)));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const renderPiiConfiguration = () => {
|
||||
if (!guardrailSettings || !selectedProvider || selectedProvider !== 'PresidioPII') return null;
|
||||
|
||||
return (
|
||||
<PiiConfiguration
|
||||
entities={guardrailSettings.supported_entities}
|
||||
actions={guardrailSettings.supported_actions}
|
||||
selectedEntities={selectedEntities}
|
||||
selectedActions={selectedActions}
|
||||
onEntitySelect={handleEntitySelect}
|
||||
onActionSelect={handleActionSelect}
|
||||
entityCategories={guardrailSettings.pii_entity_categories}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const renderProviderSpecificFields = () => {
|
||||
if (!selectedProvider) return null;
|
||||
|
||||
// For Presidio, we use the new PII configuration UI
|
||||
if (selectedProvider === 'PresidioPII') {
|
||||
return renderPiiConfiguration();
|
||||
}
|
||||
|
||||
switch (selectedProvider) {
|
||||
case 'Aporia':
|
||||
return (
|
||||
<Form.Item
|
||||
label="Aporia Configuration"
|
||||
name="config"
|
||||
tooltip="JSON configuration for Aporia"
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
"api_key": "your_aporia_api_key",
|
||||
"project_name": "your_project_name"
|
||||
}`}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'AimSecurity':
|
||||
return (
|
||||
<Form.Item
|
||||
label="Aim Security Configuration"
|
||||
name="config"
|
||||
tooltip="JSON configuration for Aim Security"
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
"api_key": "your_aim_api_key"
|
||||
}`}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'Bedrock':
|
||||
return (
|
||||
<Form.Item
|
||||
label="Amazon Bedrock Configuration"
|
||||
name="config"
|
||||
tooltip="JSON configuration for Amazon Bedrock guardrails"
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
"guardrail_id": "your_guardrail_id",
|
||||
"guardrail_version": "your_guardrail_version"
|
||||
}`}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'GuardrailsAI':
|
||||
return (
|
||||
<Form.Item
|
||||
label="Guardrails.ai Configuration"
|
||||
name="config"
|
||||
tooltip="JSON configuration for Guardrails.ai"
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
"api_key": "your_guardrails_api_key",
|
||||
"guardrail_id": "your_guardrail_id"
|
||||
}`}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'LakeraAI':
|
||||
return (
|
||||
<Form.Item
|
||||
label="Lakera AI Configuration"
|
||||
name="config"
|
||||
tooltip="JSON configuration for Lakera AI"
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
"api_key": "your_lakera_api_key"
|
||||
}`}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
case 'PromptInjection':
|
||||
return (
|
||||
<Form.Item
|
||||
label="Prompt Injection Configuration"
|
||||
name="config"
|
||||
tooltip="JSON configuration for prompt injection detection"
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
"threshold": 0.8
|
||||
}`}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<Form.Item
|
||||
label="Custom Configuration"
|
||||
name="config"
|
||||
tooltip="JSON configuration for your custom guardrail"
|
||||
>
|
||||
<Input.TextArea
|
||||
rows={4}
|
||||
placeholder={`{
|
||||
"key1": "value1",
|
||||
"key2": "value2"
|
||||
}`}
|
||||
/>
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title="Edit Guardrail"
|
||||
open={visible}
|
||||
onCancel={onClose}
|
||||
footer={null}
|
||||
width={700}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
initialValues={initialValues}
|
||||
>
|
||||
<Form.Item
|
||||
name="guardrail_name"
|
||||
label="Guardrail Name"
|
||||
rules={[{ required: true, message: 'Please enter a guardrail name' }]}
|
||||
>
|
||||
<TextInput placeholder="Enter a name for this guardrail" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="provider"
|
||||
label="Guardrail Provider"
|
||||
rules={[{ required: true, message: 'Please select a provider' }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="Select a guardrail provider"
|
||||
onChange={handleProviderChange}
|
||||
disabled={true} // Disable changing provider in edit mode
|
||||
optionLabelProp="label"
|
||||
>
|
||||
{Object.entries(GuardrailProviders).map(([key, value]) => (
|
||||
<Option
|
||||
key={key}
|
||||
value={key}
|
||||
label={value}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
{guardrailLogoMap[value] && (
|
||||
<img
|
||||
src={guardrailLogoMap[value]}
|
||||
alt=""
|
||||
style={{
|
||||
height: '20px',
|
||||
width: '20px',
|
||||
marginRight: '8px',
|
||||
objectFit: 'contain'
|
||||
}}
|
||||
onError={(e) => {
|
||||
// Hide broken image icon if image fails to load
|
||||
e.currentTarget.style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span>{value}</span>
|
||||
</div>
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="mode"
|
||||
label="Mode"
|
||||
tooltip="How the guardrail should be applied"
|
||||
rules={[{ required: true, message: 'Please select a mode' }]}
|
||||
>
|
||||
<Select>
|
||||
{guardrailSettings?.supported_modes?.map(mode => (
|
||||
<Option key={mode} value={mode}>{mode}</Option>
|
||||
)) || (
|
||||
<>
|
||||
<Option value="pre_call">pre_call</Option>
|
||||
<Option value="post_call">post_call</Option>
|
||||
</>
|
||||
)}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
name="default_on"
|
||||
label="Always On"
|
||||
tooltip="If enabled, this guardrail will be applied to all requests by default"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
|
||||
{renderProviderSpecificFields()}
|
||||
|
||||
<div className="flex justify-end space-x-2 mt-4">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleSubmit}
|
||||
loading={loading}
|
||||
>
|
||||
Update Guardrail
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditGuardrailForm;
|
||||
|
|
@ -1,34 +1,20 @@
|
|||
export enum GuardrailProviders {
|
||||
PresidioPII = "Presidio PII",
|
||||
Aporia = "Aporia",
|
||||
AimSecurity = "Aim Security",
|
||||
Bedrock = "Amazon Bedrock",
|
||||
GuardrailsAI = "Guardrails.ai",
|
||||
LakeraAI = "Lakera AI",
|
||||
Custom = "Custom Guardrail",
|
||||
PromptInjection = "Prompt Injection Detection",
|
||||
}
|
||||
|
||||
export const guardrail_provider_map: Record<string, string> = {
|
||||
Aporia: "aporia",
|
||||
AimSecurity: "aim",
|
||||
Bedrock: "bedrock",
|
||||
GuardrailsAI: "guardrails_ai",
|
||||
LakeraAI: "lakera",
|
||||
PromptInjection: "detect_prompt_injection",
|
||||
PresidioPII: "presidio",
|
||||
};
|
||||
|
||||
// Define which providers need specific fields
|
||||
export const provider_specific_fields: Record<string, string[]> = {
|
||||
PresidioPII: ["pii_entities", "pii_actions"]
|
||||
};
|
||||
|
||||
const asset_logos_folder = '../ui/assets/logos/';
|
||||
|
||||
export const guardrailLogoMap: Record<string, string> = {
|
||||
[GuardrailProviders.Aporia]: `${asset_logos_folder}aporia.svg`,
|
||||
[GuardrailProviders.AimSecurity]: `${asset_logos_folder}aim.svg`,
|
||||
[GuardrailProviders.Bedrock]: `${asset_logos_folder}bedrock.svg`,
|
||||
[GuardrailProviders.GuardrailsAI]: `${asset_logos_folder}guardrails_ai.svg`,
|
||||
[GuardrailProviders.LakeraAI]: `${asset_logos_folder}lakera.svg`,
|
||||
[GuardrailProviders.PromptInjection]: `${asset_logos_folder}prompt_injection.svg`,
|
||||
[GuardrailProviders.PresidioPII]: `${asset_logos_folder}presidio.svg`
|
||||
[GuardrailProviders.PresidioPII]: `${asset_logos_folder}presidio.png`
|
||||
};
|
||||
|
||||
export const getGuardrailLogoAndName = (guardrailValue: string): { logo: string, displayName: string } => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,325 @@
|
|||
import React, { useState } from "react";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeaderCell,
|
||||
TableRow,
|
||||
Icon,
|
||||
} from "@tremor/react";
|
||||
import {
|
||||
TrashIcon,
|
||||
SwitchVerticalIcon,
|
||||
ChevronUpIcon,
|
||||
ChevronDownIcon,
|
||||
PencilIcon,
|
||||
} from "@heroicons/react/outline";
|
||||
import { Tooltip } from "antd";
|
||||
import {
|
||||
ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getSortedRowModel,
|
||||
SortingState,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import { getGuardrailLogoAndName, guardrail_provider_map } from "./guardrail_info_helpers";
|
||||
import EditGuardrailForm from "./edit_guardrail_form";
|
||||
|
||||
interface GuardrailItem {
|
||||
guardrail_id?: string;
|
||||
guardrail_name: string | null;
|
||||
litellm_params: {
|
||||
guardrail: string;
|
||||
mode: string;
|
||||
default_on: boolean;
|
||||
pii_entities_config?: {[key: string]: string};
|
||||
[key: string]: any;
|
||||
};
|
||||
guardrail_info: Record<string, any> | null;
|
||||
created_at?: string;
|
||||
updated_at?: string;
|
||||
}
|
||||
|
||||
interface GuardrailTableProps {
|
||||
guardrailsList: GuardrailItem[];
|
||||
isLoading: boolean;
|
||||
onDeleteClick: (guardrailId: string, guardrailName: string) => void;
|
||||
accessToken: string | null;
|
||||
onGuardrailUpdated: () => void;
|
||||
}
|
||||
|
||||
const GuardrailTable: React.FC<GuardrailTableProps> = ({
|
||||
guardrailsList,
|
||||
isLoading,
|
||||
onDeleteClick,
|
||||
accessToken,
|
||||
onGuardrailUpdated,
|
||||
}) => {
|
||||
const [sorting, setSorting] = useState<SortingState>([
|
||||
{ id: "created_at", desc: true }
|
||||
]);
|
||||
const [editModalVisible, setEditModalVisible] = useState(false);
|
||||
const [selectedGuardrail, setSelectedGuardrail] = useState<GuardrailItem | null>(null);
|
||||
|
||||
// Format date helper function
|
||||
const formatDate = (dateString?: string) => {
|
||||
if (!dateString) return "-";
|
||||
const date = new Date(dateString);
|
||||
return date.toLocaleString();
|
||||
};
|
||||
|
||||
const handleEditClick = (guardrail: GuardrailItem) => {
|
||||
setSelectedGuardrail(guardrail);
|
||||
setEditModalVisible(true);
|
||||
};
|
||||
|
||||
const handleEditSuccess = () => {
|
||||
setEditModalVisible(false);
|
||||
setSelectedGuardrail(null);
|
||||
onGuardrailUpdated();
|
||||
};
|
||||
|
||||
const columns: ColumnDef<GuardrailItem>[] = [
|
||||
{
|
||||
header: "Name",
|
||||
accessorKey: "guardrail_name",
|
||||
cell: ({ row }) => {
|
||||
const guardrail = row.original;
|
||||
return (
|
||||
<Tooltip title={guardrail.guardrail_name}>
|
||||
<span className="text-xs font-medium">
|
||||
{guardrail.guardrail_name || "-"}
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Provider",
|
||||
accessorKey: "litellm_params.guardrail",
|
||||
cell: ({ row }) => {
|
||||
const guardrail = row.original;
|
||||
const { logo, displayName } = getGuardrailLogoAndName(guardrail.litellm_params.guardrail);
|
||||
return (
|
||||
<div className="flex items-center space-x-2">
|
||||
{logo && (
|
||||
<img
|
||||
src={logo}
|
||||
alt={`${displayName} logo`}
|
||||
className="w-4 h-4"
|
||||
onError={(e) => {
|
||||
// Hide broken image
|
||||
(e.target as HTMLImageElement).style.display = 'none';
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span className="text-xs">{displayName}</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Mode",
|
||||
accessorKey: "litellm_params.mode",
|
||||
cell: ({ row }) => {
|
||||
const guardrail = row.original;
|
||||
return (
|
||||
<span className="text-xs">
|
||||
{guardrail.litellm_params.mode}
|
||||
</span>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Status",
|
||||
accessorKey: "litellm_params.default_on",
|
||||
cell: ({ row }) => {
|
||||
const guardrail = row.original;
|
||||
return (
|
||||
<div className={`inline-flex rounded-full px-2 py-1 text-xs font-medium
|
||||
${guardrail.litellm_params.default_on
|
||||
? 'bg-green-100 text-green-800' // Always On styling
|
||||
: 'bg-gray-100 text-gray-800' // Per Request styling
|
||||
}`}>
|
||||
{guardrail.litellm_params.default_on ? 'Always On' : 'Per Request'}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Created At",
|
||||
accessorKey: "created_at",
|
||||
cell: ({ row }) => {
|
||||
const guardrail = row.original;
|
||||
return (
|
||||
<Tooltip title={guardrail.created_at}>
|
||||
<span className="text-xs">
|
||||
{formatDate(guardrail.created_at)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
header: "Updated At",
|
||||
accessorKey: "updated_at",
|
||||
cell: ({ row }) => {
|
||||
const guardrail = row.original;
|
||||
return (
|
||||
<Tooltip title={guardrail.updated_at}>
|
||||
<span className="text-xs">
|
||||
{formatDate(guardrail.updated_at)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
header: "",
|
||||
cell: ({ row }) => {
|
||||
const guardrail = row.original;
|
||||
return (
|
||||
<div className="flex space-x-2">
|
||||
<Icon
|
||||
icon={PencilIcon}
|
||||
size="sm"
|
||||
onClick={() => guardrail.guardrail_id && handleEditClick(guardrail)}
|
||||
className="cursor-pointer hover:text-blue-500"
|
||||
tooltip="Edit guardrail"
|
||||
/>
|
||||
<Icon
|
||||
icon={TrashIcon}
|
||||
size="sm"
|
||||
onClick={() => guardrail.guardrail_id && onDeleteClick(guardrail.guardrail_id, guardrail.guardrail_name || 'Unnamed Guardrail')}
|
||||
className="cursor-pointer hover:text-red-500"
|
||||
tooltip="Delete guardrail"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const table = useReactTable({
|
||||
data: guardrailsList,
|
||||
columns,
|
||||
state: {
|
||||
sorting,
|
||||
},
|
||||
onSortingChange: setSorting,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
enableSorting: true,
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="rounded-lg custom-border relative">
|
||||
<div className="overflow-x-auto">
|
||||
<Table className="[&_td]:py-0.5 [&_th]:py-1">
|
||||
<TableHead>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHeaderCell
|
||||
key={header.id}
|
||||
className={`py-1 h-8 ${
|
||||
header.id === 'actions'
|
||||
? 'sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)]'
|
||||
: ''
|
||||
}`}
|
||||
onClick={header.column.getToggleSortingHandler()}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex items-center">
|
||||
{header.isPlaceholder ? null : (
|
||||
flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
{header.id !== 'actions' && (
|
||||
<div className="w-4">
|
||||
{header.column.getIsSorted() ? (
|
||||
{
|
||||
asc: <ChevronUpIcon className="h-4 w-4 text-blue-500" />,
|
||||
desc: <ChevronDownIcon className="h-4 w-4 text-blue-500" />
|
||||
}[header.column.getIsSorted() as string]
|
||||
) : (
|
||||
<SwitchVerticalIcon className="h-4 w-4 text-gray-400" />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</TableHeaderCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{isLoading ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-8 text-center">
|
||||
<div className="text-center text-gray-500">
|
||||
<p>Loading...</p>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : guardrailsList.length > 0 ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow key={row.id} className="h-8">
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell
|
||||
key={cell.id}
|
||||
className={`py-0.5 max-h-8 overflow-hidden text-ellipsis whitespace-nowrap ${
|
||||
cell.column.id === 'actions'
|
||||
? 'sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)]'
|
||||
: ''
|
||||
}`}
|
||||
>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-8 text-center">
|
||||
<div className="text-center text-gray-500">
|
||||
<p>No guardrails found</p>
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
{/* Edit Modal */}
|
||||
{selectedGuardrail && (
|
||||
<EditGuardrailForm
|
||||
visible={editModalVisible}
|
||||
onClose={() => setEditModalVisible(false)}
|
||||
accessToken={accessToken}
|
||||
onSuccess={handleEditSuccess}
|
||||
guardrailId={selectedGuardrail.guardrail_id || ''}
|
||||
initialValues={{
|
||||
guardrail_name: selectedGuardrail.guardrail_name || '',
|
||||
provider: Object.keys(guardrail_provider_map).find(
|
||||
key => guardrail_provider_map[key] === selectedGuardrail?.litellm_params.guardrail
|
||||
) || '',
|
||||
mode: selectedGuardrail.litellm_params.mode,
|
||||
default_on: selectedGuardrail.litellm_params.default_on,
|
||||
pii_entities_config: selectedGuardrail.litellm_params.pii_entities_config,
|
||||
...selectedGuardrail.guardrail_info
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GuardrailTable;
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
import React from 'react';
|
||||
import { Typography, Select, Button, Checkbox, Tooltip, Tag } from 'antd';
|
||||
import { CloseOutlined, EyeInvisibleOutlined, StopOutlined, FilterOutlined } from '@ant-design/icons';
|
||||
import { PiiEntityCategory } from './types';
|
||||
|
||||
const { Text } = Typography;
|
||||
const { Option } = Select;
|
||||
|
||||
// Helper functions
|
||||
export const formatEntityName = (name: string) => {
|
||||
return name.replace(/_/g, ' ');
|
||||
};
|
||||
|
||||
export const getActionIcon = (action: string) => {
|
||||
switch(action) {
|
||||
case 'MASK':
|
||||
return <EyeInvisibleOutlined style={{ marginRight: 4 }} />;
|
||||
case 'BLOCK':
|
||||
return <StopOutlined style={{ marginRight: 4 }} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// CategoryFilter component
|
||||
export interface CategoryFilterProps {
|
||||
categories: PiiEntityCategory[];
|
||||
selectedCategories: string[];
|
||||
onChange: (categories: string[]) => void;
|
||||
}
|
||||
|
||||
export const CategoryFilter: React.FC<CategoryFilterProps> = ({
|
||||
categories,
|
||||
selectedCategories,
|
||||
onChange
|
||||
}) => {
|
||||
return (
|
||||
<div>
|
||||
<div className="flex items-center mb-2">
|
||||
<FilterOutlined className="text-gray-500 mr-1" />
|
||||
<Text className="text-gray-500 font-medium">Filter by category</Text>
|
||||
</div>
|
||||
<Select
|
||||
mode="multiple"
|
||||
placeholder="Select categories to filter by"
|
||||
style={{ width: '100%' }}
|
||||
onChange={onChange}
|
||||
value={selectedCategories}
|
||||
allowClear
|
||||
showSearch
|
||||
optionFilterProp="children"
|
||||
className="mb-4"
|
||||
tagRender={(props) => (
|
||||
<Tag
|
||||
color="blue"
|
||||
closable={props.closable}
|
||||
onClose={props.onClose}
|
||||
className="mr-2 mb-2"
|
||||
>
|
||||
{props.label}
|
||||
</Tag>
|
||||
)}
|
||||
>
|
||||
{categories.map(cat => (
|
||||
<Option key={cat.category} value={cat.category}>
|
||||
{cat.category}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// QuickActions component
|
||||
export interface QuickActionsProps {
|
||||
onSelectAll: (action: string) => void;
|
||||
onUnselectAll: () => void;
|
||||
hasSelectedEntities: boolean;
|
||||
}
|
||||
|
||||
export const QuickActions: React.FC<QuickActionsProps> = ({
|
||||
onSelectAll,
|
||||
onUnselectAll,
|
||||
hasSelectedEntities
|
||||
}) => {
|
||||
return (
|
||||
<div className="bg-gray-50 p-5 rounded-lg mb-6 border border-gray-200 shadow-sm">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex items-center">
|
||||
<Text strong className="text-gray-700 text-base">Quick Actions</Text>
|
||||
<Tooltip title="Apply action to all PII types at once">
|
||||
<div className="ml-2 text-gray-400 cursor-help text-xs">ⓘ</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<Button
|
||||
type="default"
|
||||
onClick={onUnselectAll}
|
||||
disabled={!hasSelectedEntities}
|
||||
icon={<CloseOutlined />}
|
||||
className="border-gray-300 hover:text-red-600 hover:border-red-300"
|
||||
>
|
||||
Unselect All
|
||||
</Button>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<Button
|
||||
type="default"
|
||||
onClick={() => onSelectAll('MASK')}
|
||||
className="flex items-center justify-center h-10 border-blue-200 hover:border-blue-300 hover:text-blue-700 bg-blue-50 hover:bg-blue-100 text-blue-600"
|
||||
block
|
||||
icon={<EyeInvisibleOutlined />}
|
||||
>
|
||||
Select All & Mask
|
||||
</Button>
|
||||
<Button
|
||||
type="default"
|
||||
onClick={() => onSelectAll('BLOCK')}
|
||||
className="flex items-center justify-center h-10 border-red-200 hover:border-red-300 hover:text-red-700 bg-red-50 hover:bg-red-100 text-red-600"
|
||||
block
|
||||
icon={<StopOutlined />}
|
||||
>
|
||||
Select All & Block
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// PiiEntityList component
|
||||
export interface PiiEntityListProps {
|
||||
entities: string[];
|
||||
selectedEntities: string[];
|
||||
selectedActions: {[key: string]: string};
|
||||
actions: string[];
|
||||
onEntitySelect: (entity: string) => void;
|
||||
onActionSelect: (entity: string, action: string) => void;
|
||||
entityToCategoryMap: Map<string, string>;
|
||||
}
|
||||
|
||||
export const PiiEntityList: React.FC<PiiEntityListProps> = ({
|
||||
entities,
|
||||
selectedEntities,
|
||||
selectedActions,
|
||||
actions,
|
||||
onEntitySelect,
|
||||
onActionSelect,
|
||||
entityToCategoryMap
|
||||
}) => {
|
||||
return (
|
||||
<div className="border rounded-lg overflow-hidden shadow-sm">
|
||||
<div className="bg-gray-50 px-5 py-3 border-b flex">
|
||||
<Text strong className="flex-1 text-gray-700">PII Type</Text>
|
||||
<Text strong className="w-32 text-right text-gray-700">Action</Text>
|
||||
</div>
|
||||
<div className="max-h-[400px] overflow-y-auto">
|
||||
{entities.length === 0 ? (
|
||||
<div className="py-10 text-center text-gray-500">
|
||||
No PII types match your filter criteria
|
||||
</div>
|
||||
) : (
|
||||
entities.map(entity => (
|
||||
<div
|
||||
key={entity}
|
||||
className={`px-5 py-3 flex items-center justify-between hover:bg-gray-50 border-b ${
|
||||
selectedEntities.includes(entity) ? 'bg-blue-50' : ''
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center flex-1">
|
||||
<Checkbox
|
||||
checked={selectedEntities.includes(entity)}
|
||||
onChange={() => onEntitySelect(entity)}
|
||||
className="mr-3"
|
||||
/>
|
||||
<Text className={selectedEntities.includes(entity) ? 'font-medium text-gray-900' : 'text-gray-700'}>
|
||||
{formatEntityName(entity)}
|
||||
</Text>
|
||||
{entityToCategoryMap.get(entity) && (
|
||||
<Tag className="ml-2 text-xs" color="blue">{entityToCategoryMap.get(entity)}</Tag>
|
||||
)}
|
||||
</div>
|
||||
<div className="w-32">
|
||||
<Select
|
||||
value={selectedEntities.includes(entity) ? (selectedActions[entity] || 'MASK') : 'MASK'}
|
||||
onChange={(value) => onActionSelect(entity, value)}
|
||||
style={{ width: 120 }}
|
||||
disabled={!selectedEntities.includes(entity)}
|
||||
className={`${!selectedEntities.includes(entity) ? 'opacity-50' : ''}`}
|
||||
dropdownMatchSelectWidth={false}
|
||||
>
|
||||
{actions.map(action => (
|
||||
<Option key={action} value={action}>
|
||||
<div className="flex items-center">
|
||||
{getActionIcon(action)}
|
||||
{action}
|
||||
</div>
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
import React, { useState } from 'react';
|
||||
import { Typography, Badge } from 'antd';
|
||||
import { PiiConfigurationProps } from './types';
|
||||
import {
|
||||
CategoryFilter,
|
||||
QuickActions,
|
||||
PiiEntityList,
|
||||
formatEntityName,
|
||||
getActionIcon
|
||||
} from './pii_components';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
/**
|
||||
* A reusable component for rendering PII entity selection and action configuration
|
||||
* Used in both add and edit guardrail forms
|
||||
*/
|
||||
const PiiConfiguration: React.FC<PiiConfigurationProps> = ({
|
||||
entities,
|
||||
actions,
|
||||
selectedEntities,
|
||||
selectedActions,
|
||||
onEntitySelect,
|
||||
onActionSelect,
|
||||
entityCategories = []
|
||||
}) => {
|
||||
const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
|
||||
|
||||
// Create a lookup map to quickly find an entity's category
|
||||
const entityToCategoryMap = new Map<string, string>();
|
||||
entityCategories.forEach(category => {
|
||||
category.entities.forEach(entity => {
|
||||
entityToCategoryMap.set(entity, category.category);
|
||||
});
|
||||
});
|
||||
|
||||
// Filter entities based on selected categories
|
||||
const filteredEntities = entities.filter(entity => {
|
||||
return selectedCategories.length === 0 ||
|
||||
selectedCategories.includes(entityToCategoryMap.get(entity) || '');
|
||||
});
|
||||
|
||||
// Select all entities with a specified action
|
||||
const handleSelectAll = (action: string) => {
|
||||
entities.forEach(entity => {
|
||||
if (!selectedEntities.includes(entity)) {
|
||||
onEntitySelect(entity);
|
||||
}
|
||||
onActionSelect(entity, action);
|
||||
});
|
||||
};
|
||||
|
||||
// Unselect all entities
|
||||
const handleUnselectAll = () => {
|
||||
selectedEntities.forEach(entity => {
|
||||
onEntitySelect(entity);
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="pii-configuration">
|
||||
<div className="flex justify-between items-center mb-5">
|
||||
<div className="flex items-center">
|
||||
<Title level={4} className="mb-0 font-semibold text-gray-800">Configure PII Protection</Title>
|
||||
</div>
|
||||
<Badge
|
||||
count={selectedEntities.length}
|
||||
showZero
|
||||
style={{ backgroundColor: selectedEntities.length > 0 ? '#4f46e5' : '#d9d9d9' }}
|
||||
overflowCount={999}
|
||||
>
|
||||
<Text className="text-gray-500">{selectedEntities.length} items selected</Text>
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<CategoryFilter
|
||||
categories={entityCategories}
|
||||
selectedCategories={selectedCategories}
|
||||
onChange={setSelectedCategories}
|
||||
/>
|
||||
|
||||
<QuickActions
|
||||
onSelectAll={handleSelectAll}
|
||||
onUnselectAll={handleUnselectAll}
|
||||
hasSelectedEntities={selectedEntities.length > 0}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<PiiEntityList
|
||||
entities={filteredEntities}
|
||||
selectedEntities={selectedEntities}
|
||||
selectedActions={selectedActions}
|
||||
actions={actions}
|
||||
onEntitySelect={onEntitySelect}
|
||||
onActionSelect={onActionSelect}
|
||||
entityToCategoryMap={entityToCategoryMap}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PiiConfiguration;
|
||||
19
ui/litellm-dashboard/src/components/guardrails/types.ts
Normal file
19
ui/litellm-dashboard/src/components/guardrails/types.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
export interface PiiEntity {
|
||||
name: string;
|
||||
category: string;
|
||||
}
|
||||
|
||||
export interface PiiEntityCategory {
|
||||
category: string;
|
||||
entities: string[];
|
||||
}
|
||||
|
||||
export interface PiiConfigurationProps {
|
||||
entities: string[];
|
||||
actions: string[];
|
||||
selectedEntities: string[];
|
||||
selectedActions: {[key: string]: string};
|
||||
onEntitySelect: (entity: string) => void;
|
||||
onActionSelect: (entity: string, action: string) => void;
|
||||
entityCategories?: PiiEntityCategory[];
|
||||
}
|
||||
|
|
@ -4278,8 +4278,7 @@ export const getProxyUISettings = async (
|
|||
|
||||
export const getGuardrailsList = async (accessToken: String) => {
|
||||
try {
|
||||
let url = proxyBaseUrl ? `${proxyBaseUrl}/guardrails/list` : `/guardrails/list`;
|
||||
|
||||
const url = proxyBaseUrl ? `${proxyBaseUrl}/v2/guardrails/list` : `/v2/guardrails/list`;
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
|
|
@ -4295,10 +4294,9 @@ export const getGuardrailsList = async (accessToken: String) => {
|
|||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log("Guardrails list response:", data);
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch guardrails list:", error);
|
||||
console.error("Failed to get guardrails list:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
|
@ -5044,3 +5042,57 @@ export const resetEmailEventSettings = async (accessToken: string) => {
|
|||
|
||||
export { type UserInfo } from "./view_users/types"; // Re-export UserInfo
|
||||
export { type Team } from "./key_team_helpers/key_list"; // Re-export Team
|
||||
|
||||
export const deleteGuardrailCall = async (accessToken: string, guardrailId: string) => {
|
||||
try {
|
||||
const url = proxyBaseUrl ? `${proxyBaseUrl}/guardrails/${guardrailId}` : `/guardrails/${guardrailId}`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
[globalLitellmHeaderName]: `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.text();
|
||||
handleError(errorData);
|
||||
throw new Error(errorData);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log("Delete guardrail response:", data);
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Failed to delete guardrail:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export const getGuardrailUISettings = async (accessToken: string) => {
|
||||
try {
|
||||
const url = proxyBaseUrl ? `${proxyBaseUrl}/guardrails/ui/add_guardrail_settings` : `/guardrails/ui/add_guardrail_settings`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
[globalLitellmHeaderName]: `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.text();
|
||||
handleError(errorData);
|
||||
throw new Error("Failed to get guardrail UI settings");
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
console.log("Guardrail UI settings response:", data);
|
||||
return data;
|
||||
} catch (error) {
|
||||
console.error("Failed to get guardrail UI settings:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue