diff --git a/litellm/types/guardrails.py b/litellm/types/guardrails.py index 7a1388ed8ba..ecc8de3b7ea 100644 --- a/litellm/types/guardrails.py +++ b/litellm/types/guardrails.py @@ -2,7 +2,7 @@ from datetime import datetime from enum import Enum from typing import Any, Dict, List, Literal, Optional, Union -from pydantic import BaseModel, ConfigDict, Field +from pydantic import BaseModel, ConfigDict, Field, field_validator from typing_extensions import Required, TypedDict from litellm.types.llms.openai import AllMessageValues, ChatCompletionToolCallChunk, ChatCompletionToolParam @@ -671,12 +671,29 @@ class LitellmParams( description="When to apply the guardrail (pre_call, post_call, during_call, logging_only)" ) + @field_validator("default_action", mode="before", check_fields=False) + @classmethod + def normalize_default_action_litellm_params(cls, v): + """Normalize default_action to lowercase for ALL guardrail types.""" + if isinstance(v, str): + return v.lower() + return v + + @field_validator("on_disallowed_action", mode="before", check_fields=False) + @classmethod + def normalize_on_disallowed_action_litellm_params(cls, v): + """Normalize on_disallowed_action to lowercase for ALL guardrail types.""" + if isinstance(v, str): + return v.lower() + return v + def __init__(self, **kwargs): default_on = kwargs.pop("default_on", None) if default_on is not None: kwargs["default_on"] = default_on else: kwargs["default_on"] = False + super().__init__(**kwargs) def __contains__(self, key): diff --git a/tests/test_litellm/types/test_guardrails_case_normalization.py b/tests/test_litellm/types/test_guardrails_case_normalization.py new file mode 100644 index 00000000000..317a16d149f --- /dev/null +++ b/tests/test_litellm/types/test_guardrails_case_normalization.py @@ -0,0 +1,90 @@ +""" +Test case normalization in LitellmParams for all guardrail types +""" +import pytest +from litellm.types.guardrails import LitellmParams + + +class TestLitellmParamsCaseNormalization: + """Test that LitellmParams normalizes case for all guardrail types""" + + def test_presidio_guardrail_with_capitalized_default_action(self): + """Test Presidio guardrail with capitalized default_action""" + params = LitellmParams( + guardrail="presidio", + mode="post_call", + default_action="Deny", # Capitalized + ) + assert params.default_action == "deny" + + def test_azure_guardrail_with_capitalized_default_action(self): + """Test Azure guardrail with capitalized default_action""" + params = LitellmParams( + guardrail="azure/text_moderations", + mode="pre_call", + default_action="Allow", # Capitalized + ) + assert params.default_action == "allow" + + def test_tool_permission_with_capitalized_fields(self): + """Test tool_permission with capitalized fields""" + params = LitellmParams( + guardrail="tool_permission", + mode="post_call", + default_action="DENY", # Uppercase + on_disallowed_action="BLOCK", # Uppercase + ) + assert params.default_action == "deny" + assert params.on_disallowed_action == "block" + + def test_lakera_with_capitalized_default_action(self): + """Test Lakera guardrail with capitalized default_action""" + params = LitellmParams( + guardrail="lakera_v2", + mode="pre_call", + default_action="Deny", # Capitalized + ) + assert params.default_action == "deny" + + def test_bedrock_with_capitalized_default_action(self): + """Test Bedrock guardrail with capitalized default_action""" + params = LitellmParams( + guardrail="bedrock", + mode="pre_call", + default_action="Allow", # Capitalized + ) + assert params.default_action == "allow" + + def test_multiple_guardrails_all_normalized(self): + """Test that all guardrail types benefit from normalization""" + test_cases = [ + ("presidio", "Deny"), + ("azure/text_moderations", "Allow"), + ("tool_permission", "DENY"), + ("lakera_v2", "allow"), # Already lowercase - should still work + ("bedrock", "Deny"), + ] + + for guardrail_type, default_action_input in test_cases: + params = LitellmParams( + guardrail=guardrail_type, + mode="pre_call", + default_action=default_action_input, + ) + # Should always be lowercase + assert params.default_action.lower() == params.default_action + # Should match the expected lowercase value + assert params.default_action in ["allow", "deny"] + + def test_on_disallowed_action_all_cases(self): + """Test on_disallowed_action normalization across all cases""" + test_cases = ["block", "Block", "BLOCK", "rewrite", "Rewrite", "REWRITE"] + + for action in test_cases: + params = LitellmParams( + guardrail="tool_permission", + mode="post_call", + on_disallowed_action=action, + ) + assert params.on_disallowed_action in ["block", "rewrite"] + assert params.on_disallowed_action.islower()