mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-19 00:01:29 +00:00
fix(guardrails): don't let one invalid guardrail config crash proxy boot
init_guardrails_v2 had no try/except around initialize_guardrail, so a guardrail whose litellm_params fail validation at construction time (for example Lakera's on_flagged=inject_system_message combined with mode=during_call, or a malformed advisory_system_message template) raised uncaught and crashed the entire proxy at startup, taking down every other, correctly-configured guardrail in the list. Catch ValueError/TypeError per guardrail, log a warning, and skip it, matching the same pattern already used for the DB-driven guardrail-creation path in guardrail_endpoints.py.
This commit is contained in:
parent
ba199a9fe9
commit
5d6f314267
2 changed files with 102 additions and 6 deletions
|
|
@ -26,12 +26,20 @@ def init_guardrails_v2(
|
|||
guardrail_list: Final[list[Guardrail]] = []
|
||||
|
||||
for guardrail in all_guardrails:
|
||||
initialized_guardrail = IN_MEMORY_GUARDRAIL_HANDLER.initialize_guardrail(
|
||||
guardrail=cast(Guardrail, guardrail),
|
||||
config_file_path=config_file_path,
|
||||
llm_router=llm_router,
|
||||
source="config",
|
||||
)
|
||||
try:
|
||||
initialized_guardrail = IN_MEMORY_GUARDRAIL_HANDLER.initialize_guardrail(
|
||||
guardrail=cast(Guardrail, guardrail),
|
||||
config_file_path=config_file_path,
|
||||
llm_router=llm_router,
|
||||
source="config",
|
||||
)
|
||||
except (ValueError, TypeError) as init_error:
|
||||
verbose_proxy_logger.warning(
|
||||
"Skipping guardrail '%s': invalid configuration: %s",
|
||||
guardrail.get("guardrail_name"),
|
||||
init_error,
|
||||
)
|
||||
continue
|
||||
if initialized_guardrail:
|
||||
guardrail_list.append(initialized_guardrail)
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import pytest
|
|||
|
||||
|
||||
from litellm.proxy.guardrails.guardrail_registry import InMemoryGuardrailHandler
|
||||
from litellm.proxy.guardrails.init_guardrails import init_guardrails_v2
|
||||
from litellm.types.guardrails import SupportedGuardrailIntegrations
|
||||
|
||||
|
||||
|
|
@ -153,3 +154,90 @@ def test_initialize_presidio_forwards_analyze_chunk_size_bytes():
|
|||
]
|
||||
assert initialized, "presidio guardrail was not registered as a callback"
|
||||
assert initialized[-1].presidio_analyze_chunk_size_bytes == 250_000
|
||||
|
||||
|
||||
def test_init_guardrails_v2_skips_invalid_guardrail_instead_of_crashing_boot():
|
||||
"""
|
||||
Regression: one guardrail with an invalid litellm_params combination (Lakera's
|
||||
on_flagged="inject_system_message" with mode="during_call", which LakeraAIGuardrail's
|
||||
__init__ rejects with ValueError) must not take down the entire proxy at startup.
|
||||
init_guardrails_v2 previously had no try/except around initialize_guardrail, so this
|
||||
ValueError propagated all the way through proxy_server.py's load_config and crashed
|
||||
the whole process, including every other, correctly-configured guardrail in the list.
|
||||
"""
|
||||
from litellm.proxy.guardrails.guardrail_registry import IN_MEMORY_GUARDRAIL_HANDLER
|
||||
|
||||
IN_MEMORY_GUARDRAIL_HANDLER.IN_MEMORY_GUARDRAILS.clear()
|
||||
IN_MEMORY_GUARDRAIL_HANDLER.guardrail_id_to_custom_guardrail.clear()
|
||||
|
||||
all_guardrails = [
|
||||
{
|
||||
"guardrail_name": "broken_lakera_advisory",
|
||||
"litellm_params": {
|
||||
"guardrail": SupportedGuardrailIntegrations.LAKERA_V2.value,
|
||||
"mode": "during_call",
|
||||
"on_flagged": "inject_system_message",
|
||||
"api_key": "fake-key",
|
||||
},
|
||||
},
|
||||
{
|
||||
"guardrail_name": "healthy_presidio",
|
||||
"litellm_params": {
|
||||
"guardrail": SupportedGuardrailIntegrations.PRESIDIO.value,
|
||||
"mode": "pre_call",
|
||||
"presidio_analyzer_api_base": "https://fakelink.com/v1/presidio/analyze",
|
||||
"presidio_anonymizer_api_base": "https://fakelink.com/v1/presidio/anonymize",
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
init_guardrails_v2(all_guardrails=all_guardrails)
|
||||
|
||||
guardrail_names = {
|
||||
guardrail["guardrail_name"] for guardrail in IN_MEMORY_GUARDRAIL_HANDLER.IN_MEMORY_GUARDRAILS.values()
|
||||
}
|
||||
assert "broken_lakera_advisory" not in guardrail_names
|
||||
assert "healthy_presidio" in guardrail_names
|
||||
|
||||
|
||||
def test_init_guardrails_v2_skips_guardrail_with_malformed_advisory_template():
|
||||
"""
|
||||
Regression: a malformed advisory_system_message (missing the {reason} placeholder
|
||||
LakeraAIGuardrail's __init__ requires) is a second, independent trigger for the same
|
||||
uncaught-ValueError-crashes-boot root cause as the during_call+inject_system_message
|
||||
case above. Both must be caught by init_guardrails_v2, not just one.
|
||||
"""
|
||||
from litellm.proxy.guardrails.guardrail_registry import IN_MEMORY_GUARDRAIL_HANDLER
|
||||
|
||||
IN_MEMORY_GUARDRAIL_HANDLER.IN_MEMORY_GUARDRAILS.clear()
|
||||
IN_MEMORY_GUARDRAIL_HANDLER.guardrail_id_to_custom_guardrail.clear()
|
||||
|
||||
all_guardrails = [
|
||||
{
|
||||
"guardrail_name": "broken_lakera_template",
|
||||
"litellm_params": {
|
||||
"guardrail": SupportedGuardrailIntegrations.LAKERA_V2.value,
|
||||
"mode": "pre_call",
|
||||
"on_flagged": "inject_system_message",
|
||||
"advisory_system_message": "This request was flagged, no placeholder here",
|
||||
"api_key": "fake-key",
|
||||
},
|
||||
},
|
||||
{
|
||||
"guardrail_name": "healthy_presidio",
|
||||
"litellm_params": {
|
||||
"guardrail": SupportedGuardrailIntegrations.PRESIDIO.value,
|
||||
"mode": "pre_call",
|
||||
"presidio_analyzer_api_base": "https://fakelink.com/v1/presidio/analyze",
|
||||
"presidio_anonymizer_api_base": "https://fakelink.com/v1/presidio/anonymize",
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
init_guardrails_v2(all_guardrails=all_guardrails)
|
||||
|
||||
guardrail_names = {
|
||||
guardrail["guardrail_name"] for guardrail in IN_MEMORY_GUARDRAIL_HANDLER.IN_MEMORY_GUARDRAILS.values()
|
||||
}
|
||||
assert "broken_lakera_template" not in guardrail_names
|
||||
assert "healthy_presidio" in guardrail_names
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue