mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
* feat(guardrails): wire apply_guardrail into proxy logging callbacks Route /apply_guardrail through pre/post proxy hooks and LiteLLM success/failure handlers so Langfuse and OTEL integrations receive input/output on guardrail-only requests. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(guardrails): fix Greptile review comments on apply_guardrail logging Co-authored-by: Cursor <cursoragent@cursor.com> * fix(apply_guardrail): preserve original exception and capture modified response - Capture return value from post_call_success_hook so callback-modified responses propagate to the caller. - Wrap success/failure logging calls in defensive try/except so logging infrastructure failures don't replace the user-visible response or mask the original guardrail exception. Co-authored-by: Yassin Kortam <yassin@berri.ai> * Fix mypy * fix(apply_guardrail): isolate failure logging and use post-hook response for logging - Split async_failure_handler and post_call_failure_hook into independent try/except blocks so a callback bug in one does not silently skip the other. - Build response_for_logging inside _emit_guardrail_success_logs after post_call_success_hook runs, so logged data matches the response the caller actually receives when the hook modifies the response. Co-authored-by: Yassin Kortam <yassin@berri.ai> * fix(apply_guardrail): fix black formatting and update tests for fastapi_request param - Run black on guardrail_endpoints.py to fix CI formatting check - Add _mock_proxy_logging() helper to enterprise guardrail tests to patch proxy-server globals imported at call time - Pass fastapi_request=Mock() in all direct apply_guardrail test calls to match updated function signature Co-authored-by: Cursor <cursoragent@cursor.com> * fix(guardrails): use transformed exception from post_call_failure_hook in apply_guardrail Co-authored-by: Yassin Kortam <yassin@berri.ai> * fix(guardrails): isolate sync/async logging handlers in apply_guardrail Separate each logging handler call into its own try/except so a failure in the async handler does not silently skip the sync handler submission (and vice versa). Matches the docstring's defensive intent. Co-authored-by: Yassin Kortam <yassin@berri.ai> * fix(apply_guardrail): guard transformed_exception with isinstance check Co-authored-by: Cursor <cursoragent@cursor.com> * test(guardrails): mock proxy globals in not_found test and share apply_guardrail logging fixture - Add proxy-server global mocks to test_apply_guardrail_not_found so the failure-path post_call_failure_hook call doesn't touch the real proxy logging singleton. - Extract the duplicated _mock_proxy_logging context manager out of the two enterprise apply_guardrail test files into a shared conftest fixture so the helper stays in one place. * fix(guardrails): use update_messages to keep logging obj in sync Co-authored-by: Yassin Kortam <yassin@berri.ai> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Yassin Kortam <yassin@berri.ai> Co-authored-by: mateo-berri <277851410+mateo-berri@users.noreply.github.com>
42 lines
1.6 KiB
Python
42 lines
1.6 KiB
Python
"""Shared fixtures for guardrail apply_guardrail tests."""
|
|
|
|
from contextlib import contextmanager
|
|
from unittest.mock import AsyncMock, MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
|
|
@contextmanager
|
|
def _mock_proxy_logging():
|
|
"""Patch the proxy-server globals that apply_guardrail imports at call time."""
|
|
mock_proxy_logging = MagicMock()
|
|
mock_proxy_logging.post_call_success_hook = AsyncMock(return_value=None)
|
|
mock_proxy_logging.post_call_failure_hook = AsyncMock(return_value=None)
|
|
mock_logging_obj = MagicMock()
|
|
mock_logging_obj.async_success_handler = AsyncMock(return_value=None)
|
|
mock_logging_obj.async_failure_handler = AsyncMock(return_value=None)
|
|
mock_logging_obj.success_handler = MagicMock(return_value=None)
|
|
mock_logging_obj.failure_handler = MagicMock(return_value=None)
|
|
mock_logging_obj.model_call_details = {}
|
|
|
|
with (
|
|
patch(
|
|
"litellm.proxy.common_request_processing.ProxyBaseLLMRequestProcessing"
|
|
) as mock_proc_cls,
|
|
patch("litellm.proxy.proxy_server.proxy_logging_obj", mock_proxy_logging),
|
|
patch("litellm.proxy.proxy_server.general_settings", {}),
|
|
patch("litellm.proxy.proxy_server.proxy_config", MagicMock()),
|
|
patch("litellm.proxy.proxy_server.version", "0.0.0"),
|
|
):
|
|
mock_proc = MagicMock()
|
|
mock_proc.common_processing_pre_call_logic = AsyncMock(
|
|
return_value=({}, mock_logging_obj)
|
|
)
|
|
mock_proc_cls.return_value = mock_proc
|
|
yield mock_proxy_logging
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_proxy_logging_ctx():
|
|
"""Return the proxy-logging context manager factory for use as `with ctx():`."""
|
|
return _mock_proxy_logging
|