fix: support list of modes in Mode.default for tag-based guardrails
This commit is contained in:
parent
22e682b1e8
commit
d661419109
@ -10,10 +10,15 @@ class EnterpriseCustomGuardrailHelper:
|
||||
event_hook: Optional[
|
||||
Union[GuardrailEventHooks, List[GuardrailEventHooks], Mode]
|
||||
],
|
||||
event_type: Optional[GuardrailEventHooks] = None,
|
||||
) -> Optional[bool]:
|
||||
"""
|
||||
Assumes check for event match is done in `should_run_guardrail`
|
||||
Returns True if the guardrail should be run by tag
|
||||
Returns True if the guardrail should be run for this request and event_type.
|
||||
|
||||
Logic:
|
||||
- If a request tag matches a Mode tag key, only run if event_type matches
|
||||
the tag's value (the mode for that tag).
|
||||
- If no request tag matches, fall back to default mode(s).
|
||||
"""
|
||||
from litellm.litellm_core_utils.litellm_logging import (
|
||||
StandardLoggingPayloadSetup,
|
||||
@ -36,11 +41,29 @@ class EnterpriseCustomGuardrailHelper:
|
||||
proxy_server_request=proxy_server_request,
|
||||
)
|
||||
|
||||
if request_tags and any(tag in event_hook.tags for tag in request_tags):
|
||||
return True
|
||||
elif event_hook.default and any(
|
||||
tag in event_hook.default for tag in request_tags
|
||||
):
|
||||
# Check if any request tag matches a Mode tag key
|
||||
matched_mode = None
|
||||
if request_tags:
|
||||
for tag in request_tags:
|
||||
if tag in event_hook.tags:
|
||||
matched_mode = event_hook.tags[tag]
|
||||
break
|
||||
|
||||
if matched_mode is not None:
|
||||
# Tag matched: only run if event_type matches the tag's mode value
|
||||
if event_type is not None:
|
||||
return event_type.value == matched_mode
|
||||
return True
|
||||
|
||||
# No tag matched: fall back to default mode(s)
|
||||
if event_hook.default is not None:
|
||||
if event_type is not None:
|
||||
default_list = (
|
||||
event_hook.default
|
||||
if isinstance(event_hook.default, list)
|
||||
else [event_hook.default]
|
||||
)
|
||||
return event_type.value in default_list
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
@ -420,7 +420,7 @@ class CustomGuardrail(CustomLogger):
|
||||
"Setting tag-based guardrails is only available in litellm-enterprise. You must be a premium user to use this feature."
|
||||
)
|
||||
result = EnterpriseCustomGuardrailHelper._should_run_if_mode_by_tag(
|
||||
data, self.event_hook
|
||||
data, self.event_hook, event_type
|
||||
)
|
||||
if result is not None:
|
||||
return result
|
||||
@ -447,7 +447,7 @@ class CustomGuardrail(CustomLogger):
|
||||
"Setting tag-based guardrails is only available in litellm-enterprise. You must be a premium user to use this feature."
|
||||
)
|
||||
result = EnterpriseCustomGuardrailHelper._should_run_if_mode_by_tag(
|
||||
data, self.event_hook
|
||||
data, self.event_hook, event_type
|
||||
)
|
||||
if result is not None:
|
||||
return result
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from unittest.mock import ANY, MagicMock, patch
|
||||
|
||||
sys.path.insert(
|
||||
0, os.path.abspath("../..")
|
||||
@ -12,6 +8,132 @@ from litellm.integrations.custom_guardrail import CustomGuardrail
|
||||
from litellm.types.guardrails import GuardrailEventHooks, Mode
|
||||
|
||||
|
||||
def test_custom_guardrail_with_mode_default_list(monkeypatch):
|
||||
"""Test Mode with default as a list of modes (e.g. default: ["pre_call", "post_call"])"""
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.premium_user", True)
|
||||
cg = CustomGuardrail(
|
||||
guardrail_name="test_guardrail",
|
||||
supported_event_hooks=[
|
||||
GuardrailEventHooks.pre_call,
|
||||
GuardrailEventHooks.post_call,
|
||||
GuardrailEventHooks.logging_only,
|
||||
],
|
||||
event_hook=Mode(
|
||||
tags={"test_tag": "logging_only"},
|
||||
default=["pre_call", "post_call"],
|
||||
),
|
||||
default_on=True,
|
||||
)
|
||||
|
||||
# No tag match → default fires for pre_call
|
||||
assert (
|
||||
cg.should_run_guardrail(
|
||||
data={"messages": [{"role": "user", "content": "test"}]},
|
||||
event_type=GuardrailEventHooks.pre_call,
|
||||
)
|
||||
is True
|
||||
)
|
||||
|
||||
# No tag match → default fires for post_call
|
||||
assert (
|
||||
cg.should_run_guardrail(
|
||||
data={"messages": [{"role": "user", "content": "test"}]},
|
||||
event_type=GuardrailEventHooks.post_call,
|
||||
)
|
||||
is True
|
||||
)
|
||||
|
||||
# No tag match → logging_only NOT in default list, should not fire
|
||||
assert (
|
||||
cg.should_run_guardrail(
|
||||
data={"messages": [{"role": "user", "content": "test"}]},
|
||||
event_type=GuardrailEventHooks.logging_only,
|
||||
)
|
||||
is False
|
||||
)
|
||||
|
||||
# Tag matches → only logging_only should fire
|
||||
assert (
|
||||
cg.should_run_guardrail(
|
||||
data={
|
||||
"messages": [{"role": "user", "content": "test"}],
|
||||
"litellm_metadata": {"tags": ["test_tag"]},
|
||||
},
|
||||
event_type=GuardrailEventHooks.logging_only,
|
||||
)
|
||||
is True
|
||||
)
|
||||
|
||||
# Tag matches → pre_call should NOT fire (tag says logging_only)
|
||||
assert (
|
||||
cg.should_run_guardrail(
|
||||
data={
|
||||
"messages": [{"role": "user", "content": "test"}],
|
||||
"litellm_metadata": {"tags": ["test_tag"]},
|
||||
},
|
||||
event_type=GuardrailEventHooks.pre_call,
|
||||
)
|
||||
is False
|
||||
)
|
||||
|
||||
# Tag matches → post_call should NOT fire (tag says logging_only)
|
||||
assert (
|
||||
cg.should_run_guardrail(
|
||||
data={
|
||||
"messages": [{"role": "user", "content": "test"}],
|
||||
"litellm_metadata": {"tags": ["test_tag"]},
|
||||
},
|
||||
event_type=GuardrailEventHooks.post_call,
|
||||
)
|
||||
is False
|
||||
)
|
||||
|
||||
|
||||
def test_custom_guardrail_with_mode_no_default(monkeypatch):
|
||||
"""Test Mode with no default — guardrail only fires when tag matches"""
|
||||
monkeypatch.setattr("litellm.proxy.proxy_server.premium_user", True)
|
||||
cg = CustomGuardrail(
|
||||
guardrail_name="test_guardrail",
|
||||
supported_event_hooks=[
|
||||
GuardrailEventHooks.pre_call,
|
||||
GuardrailEventHooks.logging_only,
|
||||
],
|
||||
event_hook=Mode(
|
||||
tags={"test_tag": "logging_only"},
|
||||
),
|
||||
default_on=True,
|
||||
)
|
||||
|
||||
# No tag, no default → nothing fires
|
||||
assert (
|
||||
cg.should_run_guardrail(
|
||||
data={"messages": [{"role": "user", "content": "test"}]},
|
||||
event_type=GuardrailEventHooks.pre_call,
|
||||
)
|
||||
is False
|
||||
)
|
||||
|
||||
assert (
|
||||
cg.should_run_guardrail(
|
||||
data={"messages": [{"role": "user", "content": "test"}]},
|
||||
event_type=GuardrailEventHooks.logging_only,
|
||||
)
|
||||
is False
|
||||
)
|
||||
|
||||
# Tag matches → only logging_only fires
|
||||
assert (
|
||||
cg.should_run_guardrail(
|
||||
data={
|
||||
"messages": [{"role": "user", "content": "test"}],
|
||||
"litellm_metadata": {"tags": ["test_tag"]},
|
||||
},
|
||||
event_type=GuardrailEventHooks.logging_only,
|
||||
)
|
||||
is True
|
||||
)
|
||||
|
||||
|
||||
def test_custom_guardrail_with_mode(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
"litellm.proxy.proxy_server.premium_user", True
|
||||
|
||||
Loading…
Reference in New Issue
Block a user