diff --git a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py index d0ad9eb4044..df71217c34c 100644 --- a/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py +++ b/litellm/proxy/guardrails/guardrail_hooks/litellm_content_filter/content_filter.py @@ -214,7 +214,7 @@ class ContentFilterGuardrail(CustomGuardrail): self.category_keywords: Dict[str, Tuple[str, str, ContentFilterAction]] = ( {} ) # keyword -> (category, severity, action) - # Always-block keywords are checked before exceptions (cannot be bypassed) + # Always-block keywords are checked after exceptions (exceptions take precedence) self.always_block_category_keywords: Dict[ str, Tuple[str, str, ContentFilterAction] ] = {} @@ -1068,7 +1068,15 @@ class ContentFilterGuardrail(CustomGuardrail): """ text_lower = text.lower() - # Always-block keywords are matched first — exceptions do not apply to them. + # Check exceptions first — they take precedence over always-block keywords too. + for exception in exceptions: + if exception in text_lower: + verbose_proxy_logger.debug( + f"Exception phrase '{exception}' found, skipping category keyword check" + ) + return None + + # Always-block keywords are checked after exceptions. for keyword, (category, severity, action) in self.always_block_category_keywords.items(): keyword_pattern_str = self._keyword_to_regex_pattern(keyword) if " " in keyword: @@ -1082,14 +1090,6 @@ class ContentFilterGuardrail(CustomGuardrail): ) return (keyword, category, severity, action) - # First check if any exception applies - for exception in exceptions: - if exception in text_lower: - verbose_proxy_logger.debug( - f"Exception phrase '{exception}' found, skipping category keyword check" - ) - return None - # Check category keywords for keyword, (category, severity, action) in self.category_keywords.items(): # Convert asterisks (*) in keywords to regex wildcards