litellm/enterprise/enterprise_hooks/openai_moderation.py
user 7514bb4740
fix(guardrails): close mixed-list gap, drop dead code, rename helper
Greptile P2 follow-ups on _content_utils.py:

- Drop unreachable ``_resolve_messages``. The new
  ``_iter_inspection_messages`` walks ``messages`` AND ``input``
  independently; leaving the old fallback-only variant around invited a
  future maintainer to wire it back up and silently narrow coverage.
- Rename ``iter_user_text`` → ``iter_message_text``. The helper walks
  every role (user, assistant, system); the old name implied user-turn
  content only. Callers and tests updated.
- Close mixed-list coverage gap. When ``data["input"]`` was a list
  mixing content-part dicts and bare strings, ``iter_message_text`` and
  ``build_inspection_messages`` only saw the dict parts while
  ``walk_user_text`` already inspected both. ``_iter_text_parts_in_content``
  now treats bare strings inside a content list as text fragments, so
  read and write helpers agree on coverage.

Adds two regression tests for the mixed-list shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 04:10:04 +00:00

59 lines
1.8 KiB
Python

# +-------------------------------------------------------------+
#
# Use OpenAI /moderations for your LLM calls
#
# +-------------------------------------------------------------+
# Thank you users! We ❤️ you! - Krrish & Ishaan
import os
import sys
sys.path.insert(
0, os.path.abspath("../..")
) # Adds the parent directory to the system path
import sys
from fastapi import HTTPException
import litellm
from litellm._logging import verbose_proxy_logger
from litellm.integrations.custom_logger import CustomLogger
from litellm.proxy._types import UserAPIKeyAuth
from litellm.proxy.guardrails._content_utils import iter_message_text
from litellm.types.utils import CallTypesLiteral
class _ENTERPRISE_OpenAI_Moderation(CustomLogger):
def __init__(self):
self.model_name = (
litellm.openai_moderations_model_name or "text-moderation-latest"
) # pass the model_name you initialized on litellm.Router()
pass
#### CALL HOOKS - proxy only ####
async def async_moderation_hook(
self,
data: dict,
user_api_key_dict: UserAPIKeyAuth,
call_type: CallTypesLiteral,
):
# Covers multimodal list content + Responses-API input.
text = "".join(iter_message_text(data))
from litellm.proxy.proxy_server import llm_router
if llm_router is None:
return
moderation_response = await llm_router.amoderation(
model=self.model_name, input=text
)
verbose_proxy_logger.debug("Moderation response: %s", moderation_response)
if moderation_response and moderation_response.results[0].flagged is True:
raise HTTPException(
status_code=403, detail={"error": "Violated content safety policy"}
)
pass