mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-09 03:18:44 +00:00
Fix: support standalone conditional matching without inherit_from
- Updated loading logic to activate conditional matching when either: 1. identifier_words + inherit_from (existing pattern) 2. identifier_words + additional_block_words (new standalone pattern) - Modified _load_conditional_category to handle standalone templates - EU AI Act template now works properly without inherit_from - All 45 tests passing Fixes Greptile feedback: conditional matching now activates for templates that define additional_block_words without requiring inherit_from
This commit is contained in:
parent
51f82073a1
commit
a83636728a
1 changed files with 103 additions and 58 deletions
|
|
@ -10,8 +10,19 @@ import json
|
|||
import os
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import (TYPE_CHECKING, Any, AsyncGenerator, Dict, List, Literal,
|
||||
Optional, Pattern, Tuple, Union, cast)
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
AsyncGenerator,
|
||||
Dict,
|
||||
List,
|
||||
Literal,
|
||||
Optional,
|
||||
Pattern,
|
||||
Tuple,
|
||||
Union,
|
||||
cast,
|
||||
)
|
||||
|
||||
import yaml
|
||||
from fastapi import HTTPException
|
||||
|
|
@ -26,12 +37,20 @@ if TYPE_CHECKING:
|
|||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLoggingObj
|
||||
from litellm.types.utils import GenericGuardrailAPIInputs, GuardrailStatus
|
||||
|
||||
from litellm.types.guardrails import (BlockedWord, ContentFilterAction,
|
||||
ContentFilterPattern,
|
||||
GuardrailEventHooks, Mode)
|
||||
from litellm.types.guardrails import (
|
||||
BlockedWord,
|
||||
ContentFilterAction,
|
||||
ContentFilterPattern,
|
||||
GuardrailEventHooks,
|
||||
Mode,
|
||||
)
|
||||
from litellm.types.proxy.guardrails.guardrail_hooks.litellm_content_filter import (
|
||||
BlockedWordDetection, CategoryKeywordDetection,
|
||||
ContentFilterCategoryConfig, ContentFilterDetection, PatternDetection)
|
||||
BlockedWordDetection,
|
||||
CategoryKeywordDetection,
|
||||
ContentFilterCategoryConfig,
|
||||
ContentFilterDetection,
|
||||
PatternDetection,
|
||||
)
|
||||
|
||||
from .patterns import PATTERN_EXTRA_CONFIG, get_compiled_pattern
|
||||
|
||||
|
|
@ -306,10 +325,10 @@ class ContentFilterGuardrail(CustomGuardrail):
|
|||
action if action else category_config_obj.default_action
|
||||
)
|
||||
|
||||
# Handle conditional categories (with identifier_words + inherit_from)
|
||||
if (
|
||||
category_config_obj.identifier_words
|
||||
and category_config_obj.inherit_from
|
||||
# Handle conditional categories (with identifier_words + inherit_from OR identifier_words + additional_block_words)
|
||||
if category_config_obj.identifier_words and (
|
||||
category_config_obj.inherit_from
|
||||
or category_config_obj.additional_block_words
|
||||
):
|
||||
self._load_conditional_category(
|
||||
category_name,
|
||||
|
|
@ -364,64 +383,81 @@ class ContentFilterGuardrail(CustomGuardrail):
|
|||
categories_dir: str,
|
||||
) -> None:
|
||||
"""
|
||||
Load a conditional category that uses identifier_words + inherited block_words.
|
||||
Load a conditional category that uses identifier_words + block_words.
|
||||
|
||||
Supports two patterns:
|
||||
1. Inherit + additional: identifier_words + inherit_from + optional additional_block_words
|
||||
2. Standalone: identifier_words + additional_block_words (no inheritance)
|
||||
|
||||
Args:
|
||||
category_name: Name of the category
|
||||
category_config_obj: CategoryConfig object with identifier_words and inherit_from
|
||||
category_config_obj: CategoryConfig object with identifier_words and either inherit_from or additional_block_words
|
||||
category_action: Action to take when match is found
|
||||
severity_threshold: Minimum severity threshold
|
||||
categories_dir: Directory containing category files
|
||||
"""
|
||||
# Load the inherited category to get block words
|
||||
block_words = []
|
||||
inherit_from = category_config_obj.inherit_from
|
||||
if not inherit_from:
|
||||
return
|
||||
|
||||
# Remove .json or .yaml extension if included
|
||||
inherit_base = inherit_from.replace(".json", "").replace(".yaml", "")
|
||||
# Pattern 1: Load inherited category to get base block words
|
||||
if inherit_from:
|
||||
# Remove .json or .yaml extension if included
|
||||
inherit_base = inherit_from.replace(".json", "").replace(".yaml", "")
|
||||
|
||||
# Find the inherited category file
|
||||
inherit_yaml_path = os.path.join(categories_dir, f"{inherit_base}.yaml")
|
||||
inherit_json_path = os.path.join(categories_dir, f"{inherit_base}.json")
|
||||
# Find the inherited category file
|
||||
inherit_yaml_path = os.path.join(categories_dir, f"{inherit_base}.yaml")
|
||||
inherit_json_path = os.path.join(categories_dir, f"{inherit_base}.json")
|
||||
|
||||
if os.path.exists(inherit_yaml_path):
|
||||
inherit_file_path = inherit_yaml_path
|
||||
elif os.path.exists(inherit_json_path):
|
||||
inherit_file_path = inherit_json_path
|
||||
else:
|
||||
if os.path.exists(inherit_yaml_path):
|
||||
inherit_file_path = inherit_yaml_path
|
||||
elif os.path.exists(inherit_json_path):
|
||||
inherit_file_path = inherit_json_path
|
||||
else:
|
||||
verbose_proxy_logger.warning(
|
||||
f"Category {category_name}: inherit_from '{inherit_from}' file not found at {categories_dir}"
|
||||
)
|
||||
verbose_proxy_logger.debug(
|
||||
f"Tried paths: {inherit_yaml_path}, {inherit_json_path}"
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
# Load the inherited category
|
||||
inherited_category = self._load_category_file(inherit_file_path)
|
||||
|
||||
# Extract block words from inherited category that meet severity threshold
|
||||
for keyword_data in inherited_category.keywords:
|
||||
keyword = keyword_data["keyword"].lower()
|
||||
severity = keyword_data["severity"]
|
||||
if self._should_apply_severity(severity, severity_threshold):
|
||||
block_words.append(keyword)
|
||||
except Exception as e:
|
||||
verbose_proxy_logger.error(
|
||||
f"Error loading inherited category for {category_name}: {e}"
|
||||
)
|
||||
return
|
||||
|
||||
# Pattern 2 or supplement to Pattern 1: Add additional block words
|
||||
if category_config_obj.additional_block_words:
|
||||
block_words.extend(category_config_obj.additional_block_words)
|
||||
|
||||
# Ensure we have block words before storing
|
||||
if not block_words:
|
||||
verbose_proxy_logger.warning(
|
||||
f"Category {category_name}: inherit_from '{inherit_from}' file not found at {categories_dir}"
|
||||
)
|
||||
verbose_proxy_logger.debug(
|
||||
f"Tried paths: {inherit_yaml_path}, {inherit_json_path}"
|
||||
f"Category {category_name}: no block words found (check inherit_from or additional_block_words)"
|
||||
)
|
||||
return
|
||||
|
||||
try:
|
||||
# Load the inherited category
|
||||
inherited_category = self._load_category_file(inherit_file_path)
|
||||
|
||||
# Extract block words from inherited category that meet severity threshold
|
||||
block_words = []
|
||||
for keyword_data in inherited_category.keywords:
|
||||
keyword = keyword_data["keyword"].lower()
|
||||
severity = keyword_data["severity"]
|
||||
if self._should_apply_severity(severity, severity_threshold):
|
||||
block_words.append(keyword)
|
||||
|
||||
# Add additional block words specific to this category
|
||||
if category_config_obj.additional_block_words:
|
||||
block_words.extend(category_config_obj.additional_block_words)
|
||||
|
||||
# Store the conditional category configuration
|
||||
self.conditional_categories[category_name] = {
|
||||
"identifier_words": category_config_obj.identifier_words,
|
||||
"block_words": block_words,
|
||||
"action": category_action,
|
||||
"severity": "high", # Combinations are always high severity
|
||||
}
|
||||
# Store the conditional category configuration
|
||||
self.conditional_categories[category_name] = {
|
||||
"identifier_words": category_config_obj.identifier_words,
|
||||
"block_words": block_words,
|
||||
"action": category_action,
|
||||
"severity": "high", # Combinations are always high severity
|
||||
}
|
||||
|
||||
# Log different messages based on pattern
|
||||
if inherit_from and category_config_obj.additional_block_words:
|
||||
verbose_proxy_logger.info(
|
||||
f"Loaded conditional category {category_name}: "
|
||||
f"{len(category_config_obj.identifier_words)} identifiers + "
|
||||
|
|
@ -429,9 +465,17 @@ class ContentFilterGuardrail(CustomGuardrail):
|
|||
f"({len(category_config_obj.additional_block_words)} additional + "
|
||||
f"{len(block_words) - len(category_config_obj.additional_block_words)} from {inherit_from})"
|
||||
)
|
||||
except Exception as e:
|
||||
verbose_proxy_logger.error(
|
||||
f"Error loading inherited category for {category_name}: {e}"
|
||||
elif inherit_from:
|
||||
verbose_proxy_logger.info(
|
||||
f"Loaded conditional category {category_name}: "
|
||||
f"{len(category_config_obj.identifier_words)} identifiers + "
|
||||
f"{len(block_words)} block words (from {inherit_from})"
|
||||
)
|
||||
else:
|
||||
verbose_proxy_logger.info(
|
||||
f"Loaded conditional category {category_name}: "
|
||||
f"{len(category_config_obj.identifier_words)} identifiers + "
|
||||
f"{len(block_words)} block words (standalone)"
|
||||
)
|
||||
|
||||
def _load_category_file(self, file_path: str) -> CategoryConfig:
|
||||
|
|
@ -1518,7 +1562,8 @@ class ContentFilterGuardrail(CustomGuardrail):
|
|||
|
||||
@staticmethod
|
||||
def get_config_model():
|
||||
from litellm.types.proxy.guardrails.guardrail_hooks.litellm_content_filter import \
|
||||
LitellmContentFilterGuardrailConfigModel
|
||||
from litellm.types.proxy.guardrails.guardrail_hooks.litellm_content_filter import (
|
||||
LitellmContentFilterGuardrailConfigModel,
|
||||
)
|
||||
|
||||
return LitellmContentFilterGuardrailConfigModel
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue