Merge pull request #41895 from BerriAI/litellm_openai_moderations_model_default

Co-authored-by: yucheng <yucheng@berri.ai>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
yucheng-berri 2026-09-18 17:06:41 -07:00 committed by GitHub
commit 500e880a40
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 42 additions and 5 deletions

View file

@ -17,6 +17,7 @@ from fastapi import HTTPException
import litellm
from litellm._logging import verbose_proxy_logger
from litellm.constants import DEFAULT_OPENAI_MODERATIONS_MODEL
from litellm.integrations.custom_logger import CustomLogger
from litellm.proxy._types import UserAPIKeyAuth
from litellm.proxy.guardrails._content_utils import iter_message_text
@ -24,11 +25,9 @@ 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
@property
def model_name(self) -> str:
return litellm.openai_moderations_model_name or DEFAULT_OPENAI_MODERATIONS_MODEL
#### CALL HOOKS - proxy only ####

View file

@ -158,6 +158,8 @@ DEFAULT_SEMANTIC_GUARD_EMBEDDING_MODEL: Final = str(
)
DEFAULT_SEMANTIC_GUARD_SIMILARITY_THRESHOLD = float(os.getenv("DEFAULT_SEMANTIC_GUARD_SIMILARITY_THRESHOLD", 0.75))
DEFAULT_OPENAI_MODERATIONS_MODEL: Final = "omni-moderation-latest"
# MCP OAuth2 Client Credentials Defaults
MCP_OAUTH2_TOKEN_EXPIRY_BUFFER_SECONDS: Final = int(os.getenv("MCP_OAUTH2_TOKEN_EXPIRY_BUFFER_SECONDS", "60"))
MCP_OAUTH2_TOKEN_CACHE_MAX_SIZE: Final = int(os.getenv("MCP_OAUTH2_TOKEN_CACHE_MAX_SIZE", "200"))

View file

@ -18,7 +18,9 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from httpx import Request, Response
import litellm
from litellm import DualCache
from litellm.constants import DEFAULT_OPENAI_MODERATIONS_MODEL
from litellm.proxy._types import UserAPIKeyAuth
from litellm.types.utils import Choices, Message, ModelResponse
@ -764,6 +766,40 @@ async def test_openai_moderation_inspects_multimodal_content(monkeypatch, user_a
assert seen_inputs == ["alpha beta"]
@pytest.mark.asyncio
@pytest.mark.parametrize(
("configured_after_init", "expected_model"),
[("omni-moderation-2024-09-26", "omni-moderation-2024-09-26"), (None, DEFAULT_OPENAI_MODERATIONS_MODEL)],
)
async def test_openai_moderation_reads_model_name_at_call_time(
monkeypatch, user_api_key, configured_after_init, expected_model
):
"""``litellm_settings`` applies ``callbacks`` and ``openai_moderations_model_name`` in YAML
order, so the hook must resolve the model when it runs, not when it is constructed."""
from enterprise.enterprise_hooks.openai_moderation import (
_ENTERPRISE_OpenAI_Moderation,
)
monkeypatch.setattr(litellm, "openai_moderations_model_name", None)
guard = _ENTERPRISE_OpenAI_Moderation()
monkeypatch.setattr(litellm, "openai_moderations_model_name", configured_after_init)
class FakeModeration:
results = [type("R", (), {"flagged": False})()]
fake_router = MagicMock()
fake_router.amoderation = AsyncMock(return_value=FakeModeration())
monkeypatch.setattr("litellm.proxy.proxy_server.llm_router", fake_router, raising=False)
await guard.async_moderation_hook(
data={"messages": [{"role": "user", "content": "hello"}]},
user_api_key_dict=user_api_key,
call_type="acompletion",
)
fake_router.amoderation.assert_awaited_once_with(model=expected_model, input="hello")
# ── Google Text Moderation ────────────────────────────────────────────────────