-
-
Notifications
You must be signed in to change notification settings - Fork 4.9k
(feat) Generic Guardrail API - allows guardrail providers to add INSTANT support for LiteLLM w/out PR to repo #17175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
krrishdholakia
wants to merge
3
commits into
main
Choose a base branch
from
litellm_dev_11_26_2025_p2
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,104
−14
Open
Changes from 2 commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
557 changes: 557 additions & 0 deletions
557
cookbook/mock_guardrail_server/mock_bedrock_guardrail_server.py
Large diffs are not rendered by default.
Oops, something went wrong.
160 changes: 160 additions & 0 deletions
160
docs/my-website/docs/adding_provider/generic_guardrail_api.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,160 @@ | ||
| # Generic Guardrail API - Integrate Without a PR | ||
|
|
||
| ## The Problem | ||
|
|
||
| As a guardrail provider, integrating with LiteLLM traditionally requires: | ||
| - Making a PR to the LiteLLM repository | ||
| - Waiting for review and merge | ||
| - Maintaining provider-specific code in LiteLLM's codebase | ||
| - Updating the integration for changes to your API | ||
|
|
||
| ## The Solution | ||
|
|
||
| The **Generic Guardrail API** lets you integrate with LiteLLM **instantly** by implementing a simple API endpoint. No PR required. | ||
|
|
||
| ### Key Benefits | ||
|
|
||
| 1. **No PR Needed** - Deploy and integrate immediately | ||
| 2. **Universal Support** - Works across ALL LiteLLM endpoints (chat, embeddings, image generation, etc.) | ||
| 3. **Simple Contract** - One endpoint, three response types | ||
| 4. **Custom Parameters** - Pass provider-specific params via config | ||
| 5. **Full Control** - You own and maintain your guardrail API | ||
|
|
||
| ## How It Works | ||
|
|
||
| 1. LiteLLM extracts text from any request (chat messages, embeddings, image prompts, etc.) | ||
| 2. Sends extracted text + original request to your API endpoint | ||
| 3. Your API responds with: `BLOCKED`, `NONE`, or `GUARDRAIL_INTERVENED` | ||
| 4. LiteLLM enforces the decision | ||
|
|
||
| ## API Contract | ||
|
|
||
| ### Endpoint | ||
|
|
||
| Implement `POST /beta/litellm_basic_guardrail_api` | ||
|
|
||
| ### Request Format | ||
|
|
||
| ```json | ||
| { | ||
| "text": "extracted text from the request", | ||
| "request_body": {}, // full original request for context | ||
| "additional_provider_specific_params": { | ||
| // your custom params from config | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| ### Response Format | ||
|
|
||
| ```json | ||
| { | ||
| "action": "BLOCKED" | "NONE" | "GUARDRAIL_INTERVENED", | ||
| "blocked_reason": "why content was blocked", // required if action=BLOCKED | ||
| "text": "modified text" // required if action=GUARDRAIL_INTERVENED | ||
| } | ||
| ``` | ||
|
|
||
| **Actions:** | ||
| - `BLOCKED` - LiteLLM raises error and blocks request | ||
| - `NONE` - Request proceeds unchanged | ||
| - `GUARDRAIL_INTERVENED` - Request proceeds with modified text | ||
|
|
||
| ## LiteLLM Configuration | ||
|
|
||
| Add to `config.yaml`: | ||
|
|
||
| ```yaml | ||
| litellm_settings: | ||
| guardrails: | ||
| - guardrail_name: "my-guardrail" | ||
| litellm_params: | ||
| guardrail: generic_guardrail_api | ||
| mode: pre_call # or post_call, during_call | ||
| api_base: https://your-guardrail-api.com | ||
| api_key: os.environ/YOUR_GUARDRAIL_API_KEY # optional | ||
| additional_provider_specific_params: | ||
| # your custom parameters | ||
| threshold: 0.8 | ||
| language: "en" | ||
| ``` | ||
|
|
||
| ## Usage | ||
|
|
||
| Users apply your guardrail by name: | ||
|
|
||
| ```python | ||
| response = client.chat.completions.create( | ||
| model="gpt-4", | ||
| messages=[{"role": "user", "content": "hello"}], | ||
| guardrails=["my-guardrail"] | ||
| ) | ||
| ``` | ||
|
|
||
| Or with dynamic parameters: | ||
|
|
||
| ```python | ||
| response = client.chat.completions.create( | ||
| model="gpt-4", | ||
| messages=[{"role": "user", "content": "hello"}], | ||
| guardrails=[{ | ||
| "my-guardrail": { | ||
| "extra_body": { | ||
| "custom_threshold": 0.9 | ||
| } | ||
| } | ||
| }] | ||
| ) | ||
| ``` | ||
|
|
||
| ## Implementation Example | ||
|
|
||
| See [mock_bedrock_guardrail_server.py](https://github.com/BerriAI/litellm/blob/main/cookbook/mock_guardrail_server/mock_bedrock_guardrail_server.py) for a complete reference implementation. | ||
|
|
||
| **Minimal FastAPI example:** | ||
|
|
||
| ```python | ||
| from fastapi import FastAPI | ||
| from pydantic import BaseModel | ||
|
|
||
| app = FastAPI() | ||
|
|
||
| class GuardrailRequest(BaseModel): | ||
| text: str | ||
| request_body: dict | ||
| additional_provider_specific_params: dict | ||
|
|
||
| class GuardrailResponse(BaseModel): | ||
| action: str # BLOCKED, NONE, or GUARDRAIL_INTERVENED | ||
| blocked_reason: str | None = None | ||
| text: str | None = None | ||
|
|
||
| @app.post("/beta/litellm_basic_guardrail_api") | ||
| async def apply_guardrail(request: GuardrailRequest): | ||
| # Your guardrail logic here | ||
| if "badword" in request.text.lower(): | ||
| return GuardrailResponse( | ||
| action="BLOCKED", | ||
| blocked_reason="Content contains prohibited terms" | ||
| ) | ||
|
|
||
| return GuardrailResponse(action="NONE") | ||
| ``` | ||
|
|
||
| ## When to Use This | ||
|
|
||
| ✅ **Use Generic Guardrail API when:** | ||
| - You want instant integration without waiting for PRs | ||
| - You maintain your own guardrail service | ||
| - You need full control over updates and features | ||
| - You want to support all LiteLLM endpoints automatically | ||
|
|
||
| ❌ **Make a PR when:** | ||
| - You want deeper integration with LiteLLM internals | ||
| - Your guardrail requires complex LiteLLM-specific logic | ||
| - You want to be featured as a built-in provider | ||
|
|
||
| ## Questions? | ||
|
|
||
| This is a **beta API**. We're actively improving it based on feedback. Open an issue or PR if you need additional capabilities. | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
litellm/proxy/guardrails/guardrail_hooks/generic_guardrail_api/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| from typing import TYPE_CHECKING | ||
|
|
||
| from litellm.types.guardrails import SupportedGuardrailIntegrations | ||
|
|
||
| from .generic_guardrail_api import GenericGuardrailAPI | ||
|
|
||
| if TYPE_CHECKING: | ||
| from litellm.types.guardrails import Guardrail, LitellmParams | ||
|
|
||
|
|
||
| def initialize_guardrail(litellm_params: "LitellmParams", guardrail: "Guardrail"): | ||
| import litellm | ||
|
|
||
| _generic_guardrail_api_callback = GenericGuardrailAPI( | ||
| api_base=litellm_params.api_base, | ||
| api_key=litellm_params.api_key, | ||
| additional_provider_specific_params=getattr( | ||
| litellm_params, "additional_provider_specific_params", {} | ||
| ), | ||
| guardrail_name=guardrail.get("guardrail_name", ""), | ||
| event_hook=litellm_params.mode, | ||
| default_on=litellm_params.default_on, | ||
| ) | ||
|
|
||
| litellm.logging_callback_manager.add_litellm_callback( | ||
| _generic_guardrail_api_callback | ||
| ) | ||
| return _generic_guardrail_api_callback | ||
|
|
||
|
|
||
| guardrail_initializer_registry = { | ||
| SupportedGuardrailIntegrations.GENERIC_GUARDRAIL_API.value: initialize_guardrail, | ||
| } | ||
|
|
||
| guardrail_class_registry = { | ||
| SupportedGuardrailIntegrations.GENERIC_GUARDRAIL_API.value: GenericGuardrailAPI, | ||
| } | ||
52 changes: 52 additions & 0 deletions
52
litellm/proxy/guardrails/guardrail_hooks/generic_guardrail_api/example_config.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| # Example configuration for Generic Guardrail API | ||
|
|
||
| model_list: | ||
| - model_name: gpt-4 | ||
| litellm_params: | ||
| model: gpt-4 | ||
| api_key: os.environ/OPENAI_API_KEY | ||
|
|
||
| litellm_settings: | ||
| guardrails: | ||
| - guardrail_name: "my-generic-guardrail" | ||
| litellm_params: | ||
| guardrail: generic_guardrail_api | ||
| mode: pre_call # Options: pre_call, post_call, during_call, [pre_call, post_call] | ||
| api_key: os.environ/GENERIC_GUARDRAIL_API_KEY # Optional if using Bearer auth | ||
| api_base: http://localhost:8080 # Required. Endpoint /beta/litellm_basic_guardrail_api is automatically appended | ||
| default_on: false # Set to true to apply to all requests by default | ||
| additional_provider_specific_params: | ||
| # Any additional parameters your guardrail API needs | ||
| api_version: "v1" | ||
| custom_param: "value" | ||
|
|
||
| # Usage examples: | ||
|
|
||
| # 1. Apply guardrail to a specific request: | ||
| # curl --location 'http://localhost:4000/chat/completions' \ | ||
| # --header 'Authorization: Bearer sk-1234' \ | ||
| # --header 'Content-Type: application/json' \ | ||
| # --data '{ | ||
| # "model": "gpt-4", | ||
| # "messages": [{"role": "user", "content": "Test message"}], | ||
| # "guardrails": ["my-generic-guardrail"] | ||
| # }' | ||
|
|
||
| # 2. Apply guardrail with dynamic parameters: | ||
| # curl --location 'http://localhost:4000/chat/completions' \ | ||
| # --header 'Authorization: Bearer sk-1234' \ | ||
| # --header 'Content-Type: application/json' \ | ||
| # --data '{ | ||
| # "model": "gpt-4", | ||
| # "messages": [{"role": "user", "content": "Test message"}], | ||
| # "guardrails": [ | ||
| # { | ||
| # "my-generic-guardrail": { | ||
| # "extra_body": { | ||
| # "custom_threshold": 0.8 | ||
| # } | ||
| # } | ||
| # } | ||
| # ] | ||
| # }' | ||
|
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Headers from config not passed to GenericGuardrailAPI
The
initialize_guardrailfunction creates aGenericGuardrailAPIinstance without passing theheadersparameter from the configuration. TheGenericGuardrailAPI.__init__expectsheadersas a parameter (used for authentication), but onlyapi_base,api_key, andadditional_provider_specific_paramsare passed. Sinceheadersisn't forwarded, any authentication headers (likeAuthorization: Bearer ...) configured in the YAML will be ignored, and API calls to the external guardrail will fail authentication. Theapi_keybeing passed is also unused sinceGenericGuardrailAPIdoesn't have anapi_keyparameter—it expects authentication viaheaders.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@cursor can you fix this?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect! I've fixed all three issues identified in the PR review:
Fixed Issues
1. Headers not passed to GenericGuardrailAPI (High Severity)
litellm/proxy/guardrails/guardrail_hooks/generic_guardrail_api/__init__.pyheaders=getattr(litellm_params, "headers", None)to pass authentication headers from the config to theGenericGuardrailAPIinstanceapi_keyparameter that wasn't accepted byGenericGuardrailAPI2. Request body always empty (Medium Severity)
litellm/proxy/guardrails/guardrail_hooks/generic_guardrail_api/generic_guardrail_api.pyrequest_body={}torequest_body=request_datato pass the full original request for context to the external guardrail API3. Regular expression injection vulnerability (High Severity - Security)
cookbook/mock_guardrail_server/mock_bedrock_guardrail_server.pyre.errorexceptions for invalid patternsAll changes passed linter checks with no errors.
Learn more about Cursor Agents