diff --git a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py index a89099759ce..7f5cdbc6777 100644 --- a/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py +++ b/litellm/proxy/pass_through_endpoints/pass_through_endpoints.py @@ -55,6 +55,7 @@ from litellm.constants import ( ) from litellm.integrations.custom_guardrail import ( CustomGuardrail, + guardrail_entry_names, guardrail_request_data_with_streaming, without_server_streaming_classification, ) @@ -966,14 +967,14 @@ async def _inherit_auth_guardrails( user_api_key_dict=user_api_key_dict, ) merged: Final = (as_str_mapping(parsed_body["metadata"]) or {}).get("guardrails") - if not isinstance(merged, list) or not merged or not _named_guardrail_runs_post_call(parsed_body): - return None - return _GUARDRAIL_ENTRIES.validate_python(merged) + entries: Final = _GUARDRAIL_ENTRIES.validate_python(merged) if isinstance(merged, list) else [] + return entries if entries and _named_guardrail_runs_post_call(parsed_body, entries) else None -def _named_guardrail_runs_post_call(data: Mapping[str, object]) -> bool: +def _named_guardrail_runs_post_call(data: Mapping[str, object], entries: Sequence[object]) -> bool: + names: Final = frozenset(chain.from_iterable(map(guardrail_entry_names, entries))) return any( - guardrail.default_on is not True + guardrail.guardrail_name in names and guardrail.should_run_guardrail( # pyright: ignore[reportUnknownMemberType] # takes the untyped request dict data=data, event_type=GuardrailEventHooks.post_call ) diff --git a/tests/unit/proxy/guardrails/guardrail_hooks/test_lakera_ai_v2.py b/tests/unit/proxy/guardrails/guardrail_hooks/test_lakera_ai_v2.py index 350ebd80733..006069b8abe 100644 --- a/tests/unit/proxy/guardrails/guardrail_hooks/test_lakera_ai_v2.py +++ b/tests/unit/proxy/guardrails/guardrail_hooks/test_lakera_ai_v2.py @@ -1746,7 +1746,7 @@ async def _run_gemini_pre_call( guardrail: LakeraAIGuardrail, lakera_response: Mapping[str, object], data: dict[str, object] ) -> Sequence[object]: """Run the pre-call hook against a Lakera double and return the messages each guard request carried.""" - sent: Final = list[object]() + sent: Final = list[object]() # mutable-ok: the Lakera double records each guard request it receives def respond(request: httpx.Request) -> httpx.Response: sent.append(json.loads(request.content)["messages"]) diff --git a/tests/unit/proxy/pass_through_endpoints/test_pass_through_endpoints.py b/tests/unit/proxy/pass_through_endpoints/test_pass_through_endpoints.py index 62a41720545..6f3ff0b5ed4 100644 --- a/tests/unit/proxy/pass_through_endpoints/test_pass_through_endpoints.py +++ b/tests/unit/proxy/pass_through_endpoints/test_pass_through_endpoints.py @@ -11904,6 +11904,20 @@ class TestInheritAuthGuardrails: post_call.assert_not_awaited() assert response.body == b'{\n "id":"chatcmpl-1"\n}' + @pytest.mark.asyncio + async def test_a_named_default_on_post_call_guardrail_still_checks_the_response(self): + auth: Final = UserAPIKeyAuth(api_key="sk-test", metadata={"guardrails": ["global-post"]}) + + _, post_call = await self._run( + self._chat_body(), + inherit=True, + user_api_key_dict=auth, + guardrails=(self._guardrail("global-post", GuardrailEventHooks.post_call, default_on=True),), + ) + + post_call.assert_awaited_once() + assert post_call.call_args.kwargs["data"]["metadata"]["guardrails"] == ["global-post"] + @pytest.mark.asyncio async def test_request_body_guardrails_are_added_to_the_key_guardrails(self): auth: Final = UserAPIKeyAuth(api_key="sk-test", metadata={"guardrails": ["key-guard"]})