Custom Guardrail
import Image from '@theme/IdealImage'; import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem';
Overview
Custom Guardrail
Use this if you want to write code to run a custom guardrail
Quick Start
1. Write a CustomGuardrail Class
The simplest way to create a custom guardrail is by implementing the apply_guardrail method. This method is called to check text content and can block requests by raising an exception.
Example CustomGuardrail Class
Create a new file called custom_guardrail.py and add this code to it:
from typing import Optional, List
from litellm.integrations.custom_guardrail import CustomGuardrail
from litellm.types.guardrails import PiiEntityType
from litellm._logging import verbose_proxy_logger
from litellm.llms.custom_httpx.http_handler import (
get_async_httpx_client,
httpxSpecialProvider,
)
class myCustomGuardrail(CustomGuardrail):
def __init__(self, api_key: Optional[str] = None, api_base: Optional[str] = None, **kwargs):
self.api_key = api_key or os.getenv("MY_GUARDRAIL_API_KEY")
self.api_base = api_base or os.getenv("MY_GUARDRAIL_API_BASE", "https://api.myguardrail.com")
super().__init__(**kwargs)
async def apply_guardrail(
self,
text: str, # IMPORTANT: This is the text to check against your guardrail rules. It's extracted from the request or response across all LLM call types.
language: Optional[str] = None, # ignore
entities: Optional[List[PiiEntityType]] = None, # ignore
request_data: Optional[dict] = None, # ignore
) -> str:
"""
Check text content against your guardrail rules.
Raise an exception to block the request.
Return the text (optionally modified) to allow it through.
"""
result = await self._check_with_api(text, request_data)
if result.get("action") == "BLOCK":
raise Exception(f"Content blocked: {result.get('reason', 'Policy violation')}")
return text
async def _check_with_api(self, text: str, request_data: Optional[dict]) -> dict:
async_client = get_async_httpx_client(llm_provider=httpxSpecialProvider.LoggingCallback)
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {self.api_key}",
}
response = await async_client.post(
f"{self.api_base}/check",
headers=headers,
json={"text": text},
timeout=5,
)
response.raise_for_status()
return response.json()
:::tip Advanced: Using Individual Event Hooks
If you need more fine-grained control, you can implement individual event hooks instead of (or in addition to) apply_guardrail:
async_pre_call_hook- Modify input or reject request before making LLM API callasync_moderation_hook- Reject request, runs in parallel with LLM API call (helps lower latency)async_post_call_success_hook- Apply guardrail on input/output, runs after making LLM API callasync_post_call_streaming_iterator_hook- Pass the entire stream to the guardrail
See examples of individual event hooks here | See detailed spec of methods here
:::
2. Pass your custom guardrail class in LiteLLM config.yaml
In the config below, we point the guardrail to our custom guardrail by setting guardrail: custom_guardrail.myCustomGuardrail
- Python Filename:
custom_guardrail.py - Guardrail class name :
myCustomGuardrail. This is defined in Step 1
guardrail: custom_guardrail.myCustomGuardrail
model_list:
- model_name: gpt-4
litellm_params:
model: openai/gpt-4o
api_key: os.environ/OPENAI_API_KEY
guardrails:
- guardrail_name: "my-custom-guardrail"
litellm_params:
guardrail: custom_guardrail.myCustomGuardrail # š Key change
mode: "during_call" # runs apply_guardrail method
api_key: os.environ/MY_GUARDRAIL_API_KEY
api_base: https://api.myguardrail.com
:::info Mode Options
during_call- Default mode, runsapply_guardrailmethod (orasync_moderation_hookif using individual hooks)pre_call- Runsasync_pre_call_hookfor input modificationpost_call- Runsasync_post_call_success_hookfor output validation
:::
:::note Streaming and post_call guardrails
For streaming responses, post_call guardrails run on the fully assembled response after all chunks have been delivered to the client. This means post_call guardrails on streaming are audit-only ā they can inspect and log the complete response, but cannot block content delivery. Guardrail results are recorded in guardrail_information within the logging payload for compliance and auditing.
To filter or block streaming content in real-time, use async_post_call_streaming_iterator_hook instead, which processes chunks as they arrive.
:::
<details> <summary>Advanced: Multiple modes with individual event hooks</summary>If you're using individual event hooks, you can configure multiple guardrails with different modes:
guardrails:
- guardrail_name: "custom-pre-guard"
litellm_params:
guardrail: custom_guardrail.myCustomGuardrail
mode: "pre_call" # runs async_pre_call_hook
- guardrail_name: "custom-during-guard"
litellm_params:
guardrail: custom_guardrail.myCustomGuardrail
mode: "during_call" # runs async_moderation_hook
- guardrail_name: "custom-post-guard"
litellm_params:
guardrail: custom_guardrail.myCustomGuardrail
mode: "post_call" # runs async_post_call_success_hook
</details>
3. Start LiteLLM Gateway
Mount your custom_guardrail.py on the LiteLLM Docker container
This mounts your custom_guardrail.py file from your local directory to the /app directory in the Docker container, making it accessible to the LiteLLM Gateway.
docker run -d \\
-p 4000:4000 \\
-e OPENAI_API_KEY=$OPENAI_API_KEY \\
--name my-app \\
-v $(pwd)/my_config.yaml:/app/config.yaml \\
-v $(pwd)/custom_guardrail.py:/app/custom_guardrail.py \\
my-app:latest \\
--config /app/config.yaml \\
--port 4000 \\
--detailed_debug \\
litellm --config config.yaml --detailed_debug
4. Test it
Langchain, OpenAI SDK Usage Examples
This request will be blocked if it violates your guardrail policy:
curl -i -X POST http://localhost:4000/v1/chat/completions \\
-H "Content-Type: application/json" \\
-H "Authorization: Bearer sk-1234" \\
-d '{
"model": "gpt-4",
"messages": [
{
"role": "user",
"content": "Content that violates policy"
}
],
"guardrails": ["my-custom-guardrail"]
}'
Expected response when blocked:
{
"error": {
"message": "Content blocked: Policy violation",
"type": "None",
"param": "None",
"code": "500"
}
}
This request passes the guardrail:
curl -i http://localhost:4000/v1/chat/completions \\
-H "Content-Type: application/json" \\
-H "Authorization: Bearer sk-1234" \\
-d '{
"model": "gpt-4",
"messages": [
{"role": "user", "content": "What is the weather like today?"}
],
"guardrails": ["my-custom-guardrail"]
}'
<details>
<summary>Advanced: Testing individual event hooks</summary>
If you're using individual event hooks, you can test each mode separately:
Test "custom-pre-guard"
Expect this to mask the word litellm before sending the request to the LLM API. This runs the async_pre_call_hook
curl -i -X POST http://localhost:4000/v1/chat/completions \\
-H "Content-Type: application/json" \\
-H "Authorization: Bearer sk-1234" \\
-d '{
"model": "gpt-4",
"messages": [
{
"role": "user",
"content": "say the word - `litellm`"
}
],
"guardrails": ["custom-pre-guard"]
}'
curl -i http://localhost:4000/v1/chat/completions \\
-H "Content-Type: application/json" \\
-H "Authorization: Bearer sk-1234" \\
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{"role": "user", "content": "hi what is the weather"}
],
"guardrails": ["custom-pre-guard"]
}'
Test "custom-during-guard"
Expect this to fail since litellm is in the message content. This runs the async_moderation_hook
curl -i -X POST http://localhost:4000/v1/chat/completions \\
-H "Content-Type: application/json" \\
-H "Authorization: Bearer sk-1234" \\
-d '{
"model": "gpt-4",
"messages": [
{
"role": "user",
"content": "say the word - `litellm`"
}
],
"guardrails": ["custom-during-guard"]
}'
Expected response:
{
"error": {
"message": "Guardrail failed words - `litellm` detected",
"type": "None",
"param": "None",
"code": "500"
}
}
curl -i http://localhost:4000/v1/chat/completions \\
-H "Content-Type: application/json" \\
-H "Authorization: Bearer sk-1234" \\
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{"role": "user", "content": "hi what is the weather"}
],
"guardrails": ["custom-during-guard"]
}'
Test "custom-post-guard"
Expect this to fail since coffee will be in the response content. This runs the async_post_call_success_hook
curl -i -X POST http://localhost:4000/v1/chat/completions \\
-H "Content-Type: application/json" \\
-H "Authorization: Bearer sk-1234" \\
-d '{
"model": "gpt-4",
"messages": [
{
"role": "user",
"content": "what is coffee"
}
],
"guardrails": ["custom-post-guard"]
}'
Expected response:
{
"error": {
"message": "Guardrail failed Coffee Detected",
"type": "None",
"param": "None",
"code": "500"
}
}
curl -i -X POST http://localhost:4000/v1/chat/completions \\
-H "Content-Type: application/json" \\
-H "Authorization: Bearer sk-1234" \\
-d '{
"model": "gpt-4",
"messages": [
{
"role": "user",
"content": "what is tea"
}
],
"guardrails": ["custom-post-guard"]
}'
</details>
⨠Pass additional parameters to guardrail
:::info
⨠This is an Enterprise only feature Contact us to get a free trial
:::
Use this to pass additional parameters to the guardrail API call. e.g. things like success threshold
- Use
get_guardrail_dynamic_request_body_params
get_guardrail_dynamic_request_body_params is a method of the litellm.integrations.custom_guardrail.CustomGuardrail class that fetches the dynamic guardrail params passed in the request body.
from typing import Any, Dict, List, Literal, Optional, Union
from litellm._logging import verbose_proxy_logger
from litellm.caching.caching import DualCache
from litellm.integrations.custom_guardrail import CustomGuardrail
from litellm.proxy._types import UserAPIKeyAuth
class myCustomGuardrail(CustomGuardrail):
def __init__(self, **kwargs):
super().__init__(**kwargs)
async def async_pre_call_hook(
self,
user_api_key_dict: UserAPIKeyAuth,
cache: DualCache,
data: dict,
call_type: Literal[
"completion",
"text_completion",
"embeddings",
"image_generation",
"moderation",
"audio_transcription",
"pass_through_endpoint",
"rerank"
],
) -> Optional[Union[Exception, str, dict]]:
# Get dynamic params from request body
params = self.get_guardrail_dynamic_request_body_params(request_data=data)
# params will contain: {"success_threshold": 0.9}
verbose_proxy_logger.debug("Guardrail params: %s", params)
return data
- Pass parameters in your API requests:
LiteLLM Proxy allows you to pass guardrails in the request body, following the guardrails spec.
client = openai.OpenAI(
api_key="anything",
base_url="http://0.0.0.0:4000"
)
response = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "Write a short poem"}],
extra_body={
"guardrails": [
"custom-pre-guard": {
"extra_body": {
"success_threshold": 0.9
}
}
]
}
)
curl 'http://0.0.0.0:4000/chat/completions' \\
-H 'Content-Type: application/json' \\
-d '{
"model": "gpt-3.5-turbo",
"messages": [
{
"role": "user",
"content": "Write a short poem"
}
],
"guardrails": [
"custom-pre-guard": {
"extra_body": {
"success_threshold": 0.9
}
}
]
}'
The get_guardrail_dynamic_request_body_params method will return:
{
"success_threshold": 0.9
}
Advanced: Individual Event Hooks
Pro: More flexibility Con: You need to implement this for each LLM call type (chat completions, text completions, embeddings, image generation, moderation, audio transcription, pass through endpoint, rerank, etc. )
For more fine-grained control over when and how your guardrail runs, you can implement individual event hooks. This gives you flexibility to:
- Modify inputs before the LLM call
- Run checks in parallel with the LLM call (lower latency)
- Validate or modify outputs after the LLM call
- Process streaming responses
Example with Individual Event Hooks
from typing import Any, AsyncGenerator, Literal, Optional, Union
from litellm._logging import verbose_proxy_logger
from litellm.caching.caching import DualCache
from litellm.integrations.custom_guardrail impo