mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
Merge 1b45935b80 into 1a7023366f
This commit is contained in:
commit
798bb0d3bc
13 changed files with 3291 additions and 2 deletions
|
|
@ -0,0 +1,33 @@
|
|||
from typing import TYPE_CHECKING, Final
|
||||
|
||||
from litellm.types.guardrails import SupportedGuardrailIntegrations
|
||||
|
||||
from .llm_shield_proxy import LLMShieldProxyGuardrail
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from litellm.types.guardrails import Guardrail, LitellmParams
|
||||
|
||||
|
||||
def initialize_guardrail(litellm_params: "LitellmParams", guardrail: "Guardrail") -> LLMShieldProxyGuardrail:
|
||||
import litellm
|
||||
|
||||
_llm_shield_guardrail_callback: Final = LLMShieldProxyGuardrail(
|
||||
api_key=litellm_params.api_key,
|
||||
api_base=litellm_params.api_base,
|
||||
guardrail_name=guardrail.get("guardrail_name", ""),
|
||||
event_hook=litellm_params.mode,
|
||||
default_on=litellm_params.default_on,
|
||||
)
|
||||
|
||||
litellm.logging_callback_manager.add_litellm_callback(_llm_shield_guardrail_callback)
|
||||
return _llm_shield_guardrail_callback
|
||||
|
||||
|
||||
guardrail_initializer_registry: Final = {
|
||||
SupportedGuardrailIntegrations.LLM_SHIELD_PROXY.value: initialize_guardrail,
|
||||
}
|
||||
|
||||
|
||||
guardrail_class_registry: Final = {
|
||||
SupportedGuardrailIntegrations.LLM_SHIELD_PROXY.value: LLMShieldProxyGuardrail,
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
# Example LiteLLM Proxy configuration for LLM Shield Proxy
|
||||
# LLM Shield Proxy is a self-hosted PII gateway: https://github.com/ninadphalak/LLM-Shield-Proxy
|
||||
#
|
||||
# Unlike a masking guardrail, LLM Shield Proxy's substitution is reversible. Personal data is
|
||||
# replaced with placeholders before the request goes to the provider, and the original
|
||||
# values are put back into the model's reply, so the end user still sees real data while
|
||||
# the provider never received it.
|
||||
|
||||
model_list:
|
||||
- model_name: gpt-4o
|
||||
litellm_params:
|
||||
model: openai/gpt-4o
|
||||
api_key: os.environ/OPENAI_API_KEY
|
||||
|
||||
guardrails:
|
||||
# Both modes belong on ONE entry. pre_call redacts the outbound request and post_call
|
||||
# restores the reply; listing only pre_call would send placeholders back to the user.
|
||||
- guardrail_name: "llm_shield_proxy"
|
||||
litellm_params:
|
||||
guardrail: llm_shield_proxy
|
||||
mode: ["pre_call", "post_call"]
|
||||
default_on: true
|
||||
# Your own LLM Shield Proxy deployment. Defaults to http://localhost:8000, and also reads
|
||||
# LLM_SHIELD_PROXY_API_BASE from the environment.
|
||||
api_base: "http://localhost:8000"
|
||||
# A virtual key configured on that deployment. Also reads LLM_SHIELD_PROXY_API_KEY.
|
||||
api_key: os.environ/LLM_SHIELD_PROXY_API_KEY
|
||||
|
||||
# Usage:
|
||||
#
|
||||
# 1. Run LLM Shield Proxy somewhere the proxy can reach:
|
||||
# pip install llm-shield-proxy
|
||||
# llm-shield-proxy --port 8000
|
||||
#
|
||||
# 2. Point this config at it and start the proxy:
|
||||
# export LLM_SHIELD_PROXY_API_KEY="your-virtual-key"
|
||||
# litellm --config example_config.yaml
|
||||
#
|
||||
# 3. Send a request containing personal data:
|
||||
# curl http://localhost:4000/v1/chat/completions \
|
||||
# -H "Authorization: Bearer sk-1234" \
|
||||
# -H "Content-Type: application/json" \
|
||||
# -d '{"model":"gpt-4o","messages":[{"role":"user","content":"Email jane.doe@example.com the invoice"}]}'
|
||||
#
|
||||
# The provider receives a stand-in value in place of the address. The reply you get
|
||||
# back carries the real address again.
|
||||
#
|
||||
# Notes:
|
||||
#
|
||||
# - Requests are refused if LLM Shield Proxy is unreachable or returns an error, rather than
|
||||
# being forwarded. Sending them on would hand the provider exactly the data this
|
||||
# guardrail exists to withhold.
|
||||
# - Restoring a value requires the request and the reply to share a session. LiteLLM's
|
||||
# session id is used when present; otherwise one is generated per request.
|
||||
# - Streaming replies are restored as chunks arrive. A placeholder split across two
|
||||
# chunks is held back until it is complete, so partial values are never emitted.
|
||||
# - Only text is redacted; images and audio pass through untouched.
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -145,6 +145,7 @@ class SupportedGuardrailIntegrations(Enum):
|
|||
STRAIKER = "straiker"
|
||||
ALICE = "alice"
|
||||
AGENT_365 = "agent_365"
|
||||
LLM_SHIELD_PROXY = "llm_shield_proxy"
|
||||
CONDUCT = "conduct"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
from pydantic import Field
|
||||
|
||||
from .base import GuardrailConfigModel
|
||||
|
||||
|
||||
class LLMShieldProxyGuardrailConfigModel(GuardrailConfigModel):
|
||||
api_key: str | None = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"The virtual key for the LLM Shield Proxy instance. If not provided, the "
|
||||
"`LLM_SHIELD_PROXY_API_KEY` environment variable is checked."
|
||||
),
|
||||
)
|
||||
api_base: str | None = Field(
|
||||
default=None,
|
||||
description=(
|
||||
"The base URL of the LLM Shield Proxy instance. If not provided, the `LLM_SHIELD_PROXY_API_BASE` "
|
||||
"environment variable is checked, then `http://localhost:8000`."
|
||||
),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def ui_friendly_name() -> str:
|
||||
return "LLM Shield Proxy"
|
||||
|
|
@ -30,6 +30,10 @@ external = [
|
|||
# grows over time; typing it concretely (`object`) broke that forwarding call outright —
|
||||
# basedpyright turned every named param into a reportArgumentType error. Any is correct here.
|
||||
"litellm/proxy/guardrails/guardrail_hooks/alice/alice.py" = ["ANN401"]
|
||||
# Same reason: `**kwargs` forwards verbatim to CustomGuardrail.__init__, and the lifecycle
|
||||
# hook signatures inherit `Any` for `response` from CustomLogger, so narrowing them here
|
||||
# would break the override rather than describe it.
|
||||
"litellm/proxy/guardrails/guardrail_hooks/llm_shield_proxy/llm_shield_proxy.py" = ["ANN401"]
|
||||
|
||||
[lint.mccabe]
|
||||
max-complexity = 15
|
||||
|
|
|
|||
1589
tests/unit/proxy/guardrails/guardrail_hooks/test_llm_shield_proxy.py
Normal file
1589
tests/unit/proxy/guardrails/guardrail_hooks/test_llm_shield_proxy.py
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,6 @@
|
|||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="24" height="24" rx="8" fill="#0B1F33"/>
|
||||
<path d="M12 4L18 6.35V11.5C18 15.28 15.62 18.2 12 19.4C8.38 18.2 6 15.28 6 11.5V6.35L12 4Z" fill="#BDEFF7"/>
|
||||
<path d="M12 4L18 6.35V11.5C18 15.28 15.62 18.2 12 19.4V4Z" fill="#3FC5DE"/>
|
||||
<path d="M12 4L18 6.35V11.5C18 15.28 15.62 18.2 12 19.4C8.38 18.2 6 15.28 6 11.5V6.35L12 4Z" stroke="#6FE3F2" stroke-width="0.9" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 498 B |
|
|
@ -73,7 +73,9 @@ interface GuardrailPreset {
|
|||
provider: string;
|
||||
categoryName?: string;
|
||||
guardrailNameSuggestion: string;
|
||||
mode: string;
|
||||
// A guardrail that both rewrites the request and repairs the response needs two
|
||||
// modes seeded, not one; the form already normalises either shape.
|
||||
mode: string | string[];
|
||||
defaultOn: boolean;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,9 @@ export interface GuardrailPreset {
|
|||
provider: string;
|
||||
categoryName?: string;
|
||||
guardrailNameSuggestion: string;
|
||||
mode: string;
|
||||
// A guardrail that both rewrites the request and repairs the response needs two
|
||||
// modes seeded, not one; the form already normalises either shape.
|
||||
mode: string | string[];
|
||||
defaultOn: boolean;
|
||||
}
|
||||
|
||||
|
|
@ -325,6 +327,14 @@ export const GUARDRAIL_PRESETS: Record<string, GuardrailPreset> = {
|
|||
// MCP-only: default_on is the only activation path on the MCP hook
|
||||
defaultOn: true,
|
||||
},
|
||||
llm_shield_proxy: {
|
||||
provider: "LLM Shield Proxy",
|
||||
guardrailNameSuggestion: "LLM Shield Proxy",
|
||||
// Both halves are required. With only pre_call the request is redacted and the
|
||||
// placeholders are handed straight back to the caller.
|
||||
mode: ["pre_call", "post_call"],
|
||||
defaultOn: false,
|
||||
},
|
||||
conduct: {
|
||||
provider: "Conduct",
|
||||
guardrailNameSuggestion: "Conduct Guard",
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ const EXPECTED_PARTNER_LOGO_FILES: Record<string, string> = {
|
|||
straiker: "straiker.svg",
|
||||
alice: "alice.svg",
|
||||
agent_365: "microsoft_azure.svg",
|
||||
llm_shield_proxy: "llm_shield_proxy.svg",
|
||||
conduct: "conduct.png",
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -484,6 +484,16 @@ export const PARTNER_GUARDRAIL_CARDS: GuardrailCardInfo[] = [
|
|||
tags: ["Agentic", "MCP", "Tool Misuse", "Observability"],
|
||||
providerKey: "Agent365",
|
||||
},
|
||||
{
|
||||
id: "llm_shield_proxy",
|
||||
name: "LLM Shield Proxy",
|
||||
description:
|
||||
"Self-hosted PII redaction that puts the original values back into the model's response, so the provider never receives personal data while the end user still sees it.",
|
||||
category: "partner",
|
||||
logo: guardrailLogoMap["LLM Shield Proxy"],
|
||||
tags: ["PII", "Data Privacy", "Compliance", "Streaming"],
|
||||
providerKey: "LLM Shield Proxy",
|
||||
},
|
||||
{
|
||||
id: "conduct",
|
||||
name: "Conduct Guard",
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import aimSecurityLogo from "../../../../../public/assets/logos/aim_security.jpeg";
|
||||
import aktoLogo from "../../../../../public/assets/logos/akto.svg";
|
||||
import aliceLogo from "../../../../../public/assets/logos/alice.svg";
|
||||
import llmShieldProxyLogo from "../../../../../public/assets/logos/llm_shield_proxy.svg";
|
||||
import conductLogo from "../../../../../public/assets/logos/conduct.png";
|
||||
import aporiaLogo from "../../../../../public/assets/logos/aporia.png";
|
||||
import bedrockLogo from "../../../../../public/assets/logos/bedrock.svg";
|
||||
|
|
@ -86,6 +87,7 @@ export const guardrail_provider_map: Record<string, string> = {
|
|||
QostodianNexus: "qostodian_nexus",
|
||||
Repelloai: "repelloai",
|
||||
Alice: "alice",
|
||||
"LLM Shield Proxy": "llm_shield_proxy",
|
||||
Conduct: "conduct",
|
||||
};
|
||||
|
||||
|
|
@ -211,6 +213,7 @@ export const guardrailLogoMap = {
|
|||
Straiker: straikerLogo.src,
|
||||
Alice: aliceLogo.src,
|
||||
"Microsoft Agent 365": microsoftAzureLogo.src,
|
||||
"LLM Shield Proxy": llmShieldProxyLogo.src,
|
||||
"Conduct Guard": conductLogo.src,
|
||||
} satisfies Record<string, string>;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue