mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-19 00:01:29 +00:00
fix(guardrails): only honor the judge call-origin stamp on logging_only in llm_as_a_judge
On pre_call, during_call and post_call the hook data is the client request body, so a client-supplied litellm_params.metadata.internal_call_origin must not skip enforcement. Type the during_call helper's request payload as dict[str, object] Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
2ee6484898
commit
830d52acbd
3 changed files with 22 additions and 3 deletions
|
|
@ -78,7 +78,10 @@ class _LoggedCallParams(BaseModel):
|
|||
metadata: Mapping[str, object] | None = None
|
||||
|
||||
|
||||
def _is_judge_call(data: Mapping[str, object]) -> bool:
|
||||
def _is_logged_judge_call(data: Mapping[str, object], event_type: GuardrailEventHooks) -> bool:
|
||||
"""logging_only is the only event whose ``data`` is the SDK's model_call_details rather than the client body."""
|
||||
if event_type is not GuardrailEventHooks.logging_only:
|
||||
return False
|
||||
try:
|
||||
params: Final = _LoggedCallParams.model_validate(data.get("litellm_params") or {})
|
||||
except ValidationError:
|
||||
|
|
@ -207,7 +210,7 @@ class LLMAsAJudgeGuardrail(CustomGuardrail):
|
|||
return [GuardrailEventHooks.pre_call, GuardrailEventHooks.during_call, GuardrailEventHooks.post_call]
|
||||
|
||||
def should_run_guardrail(self, data: Mapping[str, object], event_type: GuardrailEventHooks) -> bool:
|
||||
if _is_judge_call(data):
|
||||
if _is_logged_judge_call(data, event_type):
|
||||
return False
|
||||
return super().should_run_guardrail(data, event_type)
|
||||
|
||||
|
|
|
|||
|
|
@ -2671,7 +2671,7 @@ class ProxyLogging:
|
|||
async def _run_during_call_guardrail(
|
||||
self,
|
||||
callback: CustomGuardrail,
|
||||
data: dict,
|
||||
data: dict[str, object], # mutable-ok: request payload dict, guardrail_to_apply is written in place
|
||||
user_api_key_dict: UserAPIKeyAuth | None,
|
||||
user_api_key_auth_dict: UserAPIKeyAuth | dict[str, object] | None,
|
||||
call_type: CallTypesLiteral,
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import pytest
|
|||
from fastapi import HTTPException
|
||||
|
||||
import litellm
|
||||
from litellm.constants import INTERNAL_CALL_ORIGIN_METADATA_KEY
|
||||
from litellm.proxy.guardrails.guardrail_hooks.llm_as_a_judge import (
|
||||
LLMAsAJudgeGuardrail,
|
||||
_build_judge_prompt,
|
||||
|
|
@ -16,6 +17,7 @@ from litellm.proxy.guardrails.guardrail_hooks.llm_as_a_judge import (
|
|||
initialize_guardrail,
|
||||
)
|
||||
from litellm.types.guardrails import GuardrailEventHooks, Mode
|
||||
from litellm.types.utils import LLM_AS_A_JUDGE_GUARDRAIL_CALL_ORIGIN
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
|
|
@ -420,6 +422,20 @@ async def test_logging_only_judge_does_not_judge_its_own_judge_call():
|
|||
assert guardrail.should_run_guardrail(client_call, GuardrailEventHooks.logging_only) is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"event_type", [GuardrailEventHooks.pre_call, GuardrailEventHooks.during_call, GuardrailEventHooks.post_call]
|
||||
)
|
||||
def test_client_supplied_judge_origin_does_not_bypass_enforcing_hooks(event_type: GuardrailEventHooks):
|
||||
guardrail: Final = _make_guardrail(event_hook=event_type)
|
||||
forged_request: Final[dict[str, object]] = {
|
||||
"messages": [{"role": "user", "content": "hi"}],
|
||||
"guardrails": [guardrail.guardrail_name],
|
||||
"litellm_params": {"metadata": {INTERNAL_CALL_ORIGIN_METADATA_KEY: LLM_AS_A_JUDGE_GUARDRAIL_CALL_ORIGIN}},
|
||||
}
|
||||
|
||||
assert guardrail.should_run_guardrail(forged_request, event_type) is True
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_apply_guardrail_response_prompt_unchanged():
|
||||
router: Final = _judge_router(90.0)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue