""" CRUD ENDPOINTS FOR GUARDRAILS """ from typing import Any, Dict, List, Optional, Type, 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, BedrockGuardrailConfigModel, Guardrail, GuardrailEventHooks, GuardrailInfoResponse, GuardrailParamUITypes, GuardrailUIAddGuardrailSettings, LakeraV2GuardrailConfigModel, ListGuardrailsResponse, LitellmParams, PatchGuardrailRequest, PiiAction, PiiEntityType, PresidioPresidioConfigModelUserInterface, SupportedGuardrailIntegrations, ) #### GUARDRAILS ENDPOINTS #### router = APIRouter() GUARDRAIL_REGISTRY = GuardrailRegistry() def _get_guardrails_list_response( guardrails_config: List[Dict], ) -> ListGuardrailsResponse: """ Helper function to get the guardrails list response """ guardrail_configs: List[GuardrailInfoResponse] = [] for guardrail in guardrails_config: guardrail_configs.append( GuardrailInfoResponse( guardrail_name=guardrail.get("guardrail_name"), litellm_params=guardrail.get("litellm_params"), guardrail_info=guardrail.get("guardrail_info"), ) ) return ListGuardrailsResponse(guardrails=guardrail_configs) @router.get( "/guardrails/list", tags=["Guardrails"], dependencies=[Depends(user_api_key_auth)], response_model=ListGuardrailsResponse, ) async def list_guardrails(): """ List the guardrails that are available on the proxy server 👉 [Guardrail docs](https://docs.litellm.ai/docs/proxy/guardrails/quick_start) Example Request: ```bash curl -X GET "http://localhost:4000/guardrails/list" -H "Authorization: Bearer " ``` Example Response: ```json { "guardrails": [ { "guardrail_name": "bedrock-pre-guard", "guardrail_info": { "params": [ { "name": "toxicity_score", "type": "float", "description": "Score between 0-1 indicating content toxicity level" }, { "name": "pii_detection", "type": "boolean" } ] } } ] } ``` """ from litellm.proxy.proxy_server import proxy_config config = proxy_config.config _guardrails_config = cast(Optional[list[dict]], config.get("guardrails")) if _guardrails_config is None: return _get_guardrails_list_response([]) 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 " ``` 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.guardrails.guardrail_registry import IN_MEMORY_GUARDRAIL_HANDLER 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] = [] seen_guardrail_ids = set() for guardrail in guardrails: guardrail_configs.append( GuardrailInfoResponse( guardrail_id=guardrail.get("guardrail_id"), 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"), guardrail_definition_location="db", ) ) seen_guardrail_ids.add(guardrail.get("guardrail_id")) # get guardrails initialized on litellm config.yaml in_memory_guardrails = IN_MEMORY_GUARDRAIL_HANDLER.list_in_memory_guardrails() for guardrail in in_memory_guardrails: # only add guardrails that are not in DB guardrail list already if guardrail.get("guardrail_id") not in seen_guardrail_ids: guardrail_configs.append( GuardrailInfoResponse( guardrail_id=guardrail.get("guardrail_id"), guardrail_name=guardrail.get("guardrail_name"), litellm_params=dict(guardrail.get("litellm_params") or {}), guardrail_info=dict(guardrail.get("guardrail_info") or {}), guardrail_definition_location="config", ) ) seen_guardrail_ids.add(guardrail.get("guardrail_id")) 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 @router.post( "/guardrails", tags=["Guardrails"], dependencies=[Depends(user_api_key_auth)], ) async def create_guardrail(request: CreateGuardrailRequest): """ Create a new guardrail 👉 [Guardrail docs](https://docs.litellm.ai/docs/proxy/guardrails/quick_start) Example Request: ```bash curl -X POST "http://localhost:4000/guardrails" \\ -H "Authorization: Bearer " \\ -H "Content-Type: application/json" \\ -d '{ "guardrail": { "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" } } }' ``` Example Response: ```json { "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" }, "created_at": "2023-11-09T12:34:56.789Z", "updated_at": "2023-11-09T12:34:56.789Z" } ``` """ from litellm.proxy.proxy_server import prisma_client if prisma_client is None: raise HTTPException(status_code=500, detail="Prisma client not initialized") try: result = await GUARDRAIL_REGISTRY.add_guardrail_to_db( guardrail=request.guardrail, prisma_client=prisma_client ) 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)) @router.get( "/guardrails/{guardrail_id}", tags=["Guardrails"], dependencies=[Depends(user_api_key_auth)], ) async def get_guardrail(guardrail_id: str): """ Get a guardrail by ID 👉 [Guardrail docs](https://docs.litellm.ai/docs/proxy/guardrails/quick_start) Example Request: ```bash curl -X GET "http://localhost:4000/guardrails/123e4567-e89b-12d3-a456-426614174000" \\ -H "Authorization: Bearer " ``` Example Response: ```json { "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" }, "created_at": "2023-11-09T12:34:56.789Z", "updated_at": "2023-11-09T12:34:56.789Z" } ``` """ from litellm.proxy.proxy_server import prisma_client if prisma_client is None: raise HTTPException(status_code=500, detail="Prisma client not initialized") try: result = await GUARDRAIL_REGISTRY.get_guardrail_by_id_from_db( guardrail_id=guardrail_id, prisma_client=prisma_client ) if result is None: raise HTTPException( status_code=404, detail=f"Guardrail with ID {guardrail_id} not found" ) return result except HTTPException as e: raise e except Exception as e: raise HTTPException(status_code=500, detail=str(e)) class UpdateGuardrailRequest(BaseModel): guardrail: Guardrail @router.put( "/guardrails/{guardrail_id}", tags=["Guardrails"], dependencies=[Depends(user_api_key_auth)], ) async def update_guardrail(guardrail_id: str, request: UpdateGuardrailRequest): """ Update an existing guardrail 👉 [Guardrail docs](https://docs.litellm.ai/docs/proxy/guardrails/quick_start) Example Request: ```bash curl -X PUT "http://localhost:4000/guardrails/123e4567-e89b-12d3-a456-426614174000" \\ -H "Authorization: Bearer " \\ -H "Content-Type: application/json" \\ -d '{ "guardrail": { "guardrail_name": "updated-bedrock-guard", "litellm_params": { "guardrail": "bedrock", "mode": "pre_call", "guardrailIdentifier": "ff6ujrregl1q", "guardrailVersion": "1.0", "default_on": true }, "guardrail_info": { "description": "Updated Bedrock content moderation guardrail" } } }' ``` Example Response: ```json { "guardrail_id": "123e4567-e89b-12d3-a456-426614174000", "guardrail_name": "updated-bedrock-guard", "litellm_params": { "guardrail": "bedrock", "mode": "pre_call", "guardrailIdentifier": "ff6ujrregl1q", "guardrailVersion": "1.0", "default_on": true }, "guardrail_info": { "description": "Updated Bedrock content moderation guardrail" }, "created_at": "2023-11-09T12:34:56.789Z", "updated_at": "2023-11-09T13:45:12.345Z" } ``` """ from litellm.proxy.proxy_server import prisma_client if prisma_client is None: raise HTTPException(status_code=500, detail="Prisma client not initialized") try: # Check if guardrail exists existing_guardrail = await GUARDRAIL_REGISTRY.get_guardrail_by_id_from_db( guardrail_id=guardrail_id, prisma_client=prisma_client ) if existing_guardrail is None: raise HTTPException( status_code=404, detail=f"Guardrail with ID {guardrail_id} not found" ) result = await GUARDRAIL_REGISTRY.update_guardrail_in_db( guardrail_id=guardrail_id, guardrail=request.guardrail, prisma_client=prisma_client, ) return result except HTTPException as e: raise e except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.delete( "/guardrails/{guardrail_id}", tags=["Guardrails"], dependencies=[Depends(user_api_key_auth)], ) async def delete_guardrail(guardrail_id: str): """ Delete a guardrail 👉 [Guardrail docs](https://docs.litellm.ai/docs/proxy/guardrails/quick_start) Example Request: ```bash curl -X DELETE "http://localhost:4000/guardrails/123e4567-e89b-12d3-a456-426614174000" \\ -H "Authorization: Bearer " ``` Example Response: ```json { "message": "Guardrail 123e4567-e89b-12d3-a456-426614174000 deleted successfully" } ``` """ from litellm.proxy.guardrails.guardrail_registry import IN_MEMORY_GUARDRAIL_HANDLER from litellm.proxy.proxy_server import prisma_client if prisma_client is None: raise HTTPException(status_code=500, detail="Prisma client not initialized") try: # Check if guardrail exists existing_guardrail = await GUARDRAIL_REGISTRY.get_guardrail_by_id_from_db( guardrail_id=guardrail_id, prisma_client=prisma_client ) if existing_guardrail is None: raise HTTPException( status_code=404, detail=f"Guardrail with ID {guardrail_id} not found" ) result = await GUARDRAIL_REGISTRY.delete_guardrail_from_db( guardrail_id=guardrail_id, prisma_client=prisma_client ) # delete in memory guardrail IN_MEMORY_GUARDRAIL_HANDLER.delete_in_memory_guardrail( guardrail_id=guardrail_id, ) return result except HTTPException as e: raise e except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @router.patch( "/guardrails/{guardrail_id}", tags=["Guardrails"], dependencies=[Depends(user_api_key_auth)], ) async def patch_guardrail(guardrail_id: str, request: PatchGuardrailRequest): """ Partially update an existing guardrail 👉 [Guardrail docs](https://docs.litellm.ai/docs/proxy/guardrails/quick_start) This endpoint allows updating specific fields of a guardrail without sending the entire object. Only the following fields can be updated: - guardrail_name: The name of the guardrail - default_on: Whether the guardrail is enabled by default - guardrail_info: Additional information about the guardrail Example Request: ```bash curl -X PATCH "http://localhost:4000/guardrails/123e4567-e89b-12d3-a456-426614174000" \\ -H "Authorization: Bearer " \\ -H "Content-Type: application/json" \\ -d '{ "guardrail_name": "updated-name", "default_on": true, "guardrail_info": { "description": "Updated description" } }' ``` Example Response: ```json { "guardrail_id": "123e4567-e89b-12d3-a456-426614174000", "guardrail_name": "updated-name", "litellm_params": { "guardrail": "bedrock", "mode": "pre_call", "guardrailIdentifier": "ff6ujrregl1q", "guardrailVersion": "DRAFT", "default_on": true }, "guardrail_info": { "description": "Updated description" }, "created_at": "2023-11-09T12:34:56.789Z", "updated_at": "2023-11-09T14:22:33.456Z" } ``` """ from litellm.proxy.guardrails.guardrail_registry import IN_MEMORY_GUARDRAIL_HANDLER from litellm.proxy.proxy_server import prisma_client if prisma_client is None: raise HTTPException(status_code=500, detail="Prisma client not initialized") try: # Check if guardrail exists and get current data existing_guardrail = await GUARDRAIL_REGISTRY.get_guardrail_by_id_from_db( guardrail_id=guardrail_id, prisma_client=prisma_client ) if existing_guardrail is None: raise HTTPException( status_code=404, detail=f"Guardrail with ID {guardrail_id} not found" ) # Create updated guardrail object guardrail_name = ( request.guardrail_name if request.guardrail_name is not None else existing_guardrail.get("guardrail_name") ) # Update litellm_params if default_on is provided or pii_entities_config is provided litellm_params = LitellmParams( **dict(existing_guardrail.get("litellm_params", {})) ) if ( request.litellm_params is not None and request.litellm_params.default_on is not None ): litellm_params.default_on = request.litellm_params.default_on if ( request.litellm_params is not None and request.litellm_params.pii_entities_config is not None ): litellm_params.pii_entities_config = ( request.litellm_params.pii_entities_config ) # Update guardrail_info if provided guardrail_info = ( request.guardrail_info if request.guardrail_info is not None else existing_guardrail.get("guardrail_info", {}) ) # Create the guardrail object guardrail = Guardrail( guardrail_name=guardrail_name or "", litellm_params=litellm_params, guardrail_info=guardrail_info, ) result = await GUARDRAIL_REGISTRY.update_guardrail_in_db( guardrail_id=guardrail_id, guardrail=guardrail, prisma_client=prisma_client, ) # update in memory guardrail IN_MEMORY_GUARDRAIL_HANDLER.update_in_memory_guardrail( guardrail_id=guardrail_id, guardrail=guardrail, ) return result except HTTPException as e: raise e except Exception as e: verbose_proxy_logger.exception(f"Error updating guardrail: {e}") raise HTTPException(status_code=500, detail=str(e)) @router.get( "/guardrails/{guardrail_id}/info", tags=["Guardrails"], dependencies=[Depends(user_api_key_auth)], ) async def get_guardrail_info(guardrail_id: str): """ Get detailed information about a specific guardrail by ID 👉 [Guardrail docs](https://docs.litellm.ai/docs/proxy/guardrails/quick_start) Example Request: ```bash curl -X GET "http://localhost:4000/guardrails/123e4567-e89b-12d3-a456-426614174000/info" \\ -H "Authorization: Bearer " ``` Example Response: ```json { "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" }, "created_at": "2023-11-09T12:34:56.789Z", "updated_at": "2023-11-09T12:34:56.789Z" } ``` """ from litellm.proxy.guardrails.guardrail_registry import IN_MEMORY_GUARDRAIL_HANDLER from litellm.proxy.proxy_server import prisma_client if prisma_client is None: raise HTTPException(status_code=500, detail="Prisma client not initialized") try: result = await GUARDRAIL_REGISTRY.get_guardrail_by_id_from_db( guardrail_id=guardrail_id, prisma_client=prisma_client ) if result is None: result = IN_MEMORY_GUARDRAIL_HANDLER.get_guardrail_by_id( guardrail_id=guardrail_id ) if result is None: raise HTTPException( status_code=404, detail=f"Guardrail with ID {guardrail_id} not found" ) return GuardrailInfoResponse( guardrail_id=result.get("guardrail_id"), guardrail_name=result.get("guardrail_name"), litellm_params=dict(result.get("litellm_params") or {}), guardrail_info=dict(result.get("guardrail_info") or {}), created_at=result.get("created_at"), updated_at=result.get("updated_at"), ) except HTTPException as e: 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, ) def _get_fields_from_model(model_class: Type[BaseModel]) -> List[Dict[str, Any]]: """ Get the fields from a Pydantic model """ fields = [] for field_name, field in model_class.model_fields.items(): # Get field metadata description = field.description or field_name # Check if this field is in the required_fields class variable required = field.is_required() field_type: Optional[GuardrailParamUITypes] = None field_json_schema_extra = getattr(field, "json_schema_extra", {}) if field_json_schema_extra and "ui_type" in field_json_schema_extra: field_type = field_json_schema_extra["ui_type"] fields.append( { "param": field_name, "description": description, "required": required, "type": field_type.value if field_type else None, } ) return fields @router.get( "/guardrails/ui/provider_specific_params", tags=["Guardrails"], dependencies=[Depends(user_api_key_auth)], ) async def get_provider_specific_params(): """ Get provider-specific parameters for different guardrail types. Returns a dictionary mapping guardrail providers to their specific parameters, including parameter names, descriptions, and whether they are required. Example Response: ```json { "bedrock": [ { "param": "guardrailIdentifier", "description": "The ID of your guardrail on Bedrock", "required": true }, { "param": "guardrailVersion", "description": "The version of your Bedrock guardrail (e.g., DRAFT or version number)", "required": true } ], "presidio": [ { "param": "presidio_analyzer_api_base", "description": "Base URL for the Presidio analyzer API", "required": true }, { "param": "presidio_anonymizer_api_base", "description": "Base URL for the Presidio anonymizer API", "required": true }, { "param": "output_parse_pii", "description": "Whether to parse PII in model outputs", "required": false, "type": "bool" } ] } ``` """ # Get fields from the models bedrock_fields = _get_fields_from_model(BedrockGuardrailConfigModel) presidio_fields = _get_fields_from_model(PresidioPresidioConfigModelUserInterface) lakera_v2_fields = _get_fields_from_model(LakeraV2GuardrailConfigModel) # Return the provider-specific parameters provider_params = { SupportedGuardrailIntegrations.BEDROCK.value: bedrock_fields, SupportedGuardrailIntegrations.PRESIDIO.value: presidio_fields, SupportedGuardrailIntegrations.LAKERA_V2.value: lakera_v2_fields, } return provider_params