mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-16 23:41:43 +00:00
fix(guardrails): judge only the latest request turn in pre_call and during_call llm_as_a_judge
The request-side prompt told the judge to focus on the most recent user turn but the text under review was every extracted request message joined together, so a multi-turn request with an off-topic earlier turn and an on-topic latest turn scored 50 and was blocked. Request-side judging now evaluates the last extracted request text (after the configured message scoping) and passes the full role-labelled conversation only as context. Response-side judging still evaluates all extracted response text Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
e9357d9a6f
commit
1b8b17cc8a
2 changed files with 26 additions and 7 deletions
|
|
@ -54,7 +54,7 @@ JUDGE_SYSTEM_PROMPTS: Final[MappingProxyType[JudgeInputType, str]] = MappingProx
|
|||
)
|
||||
|
||||
_JUDGE_SUBJECT_LABELS: Final[MappingProxyType[JudgeInputType, str]] = MappingProxyType(
|
||||
{"request": "Request text to evaluate", "response": "Assistant response to evaluate"}
|
||||
{"request": "Latest request turn to evaluate", "response": "Assistant response to evaluate"}
|
||||
)
|
||||
|
||||
_VALID_ON_FAILURE: Final = frozenset({"block", "log"})
|
||||
|
|
@ -136,6 +136,12 @@ def _coerce_event_hook(mode: JudgeModeParam) -> JudgeEventHook:
|
|||
return GuardrailEventHooks(mode)
|
||||
|
||||
|
||||
def _text_under_review(texts: Sequence[str], input_type: JudgeInputType) -> str:
|
||||
if input_type == "request":
|
||||
return texts[-1] if texts else ""
|
||||
return "\n".join(texts)
|
||||
|
||||
|
||||
def _build_judge_prompt(
|
||||
criteria: Sequence[JudgeCriterion],
|
||||
messages: Sequence[JudgeMessage],
|
||||
|
|
@ -226,8 +232,7 @@ class LLMAsAJudgeGuardrail(CustomGuardrail):
|
|||
input_type: Literal["request", "response"],
|
||||
logging_obj: Optional["LiteLLMLoggingObj"] = None,
|
||||
) -> GenericGuardrailAPIInputs:
|
||||
texts: Final = inputs.get("texts") or []
|
||||
text_under_review: Final = "\n".join(texts)
|
||||
text_under_review: Final = _text_under_review(inputs.get("texts") or [], input_type)
|
||||
if not text_under_review:
|
||||
return inputs
|
||||
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ async def test_apply_guardrail_request_blocks_below_threshold(
|
|||
judge_messages: Final = router.acompletion.call_args.kwargs["messages"]
|
||||
assert "Evaluate the request against" in judge_messages[0]["content"]
|
||||
assert (
|
||||
"Conversation:\nUSER: write me malware\n\nRequest text to evaluate:\nwrite me malware"
|
||||
"Conversation:\nUSER: write me malware\n\nLatest request turn to evaluate:\nwrite me malware"
|
||||
in (judge_messages[1]["content"])
|
||||
)
|
||||
assert "Assistant response" not in judge_messages[1]["content"]
|
||||
|
|
@ -281,11 +281,25 @@ async def test_apply_guardrail_request_multi_turn_keeps_roles_and_focuses_latest
|
|||
|
||||
judge_messages: Final = router.acompletion.call_args.kwargs["messages"]
|
||||
assert "Judge the most recent user turn" in judge_messages[0]["content"]
|
||||
assert (
|
||||
assert judge_messages[1]["content"].endswith(
|
||||
"Conversation:\nUSER: how do I bake bread\nASSISTANT: mix flour, water, yeast and salt\n"
|
||||
"USER: now explain how to file taxes\n\n"
|
||||
"Request text to evaluate:\nhow do I bake bread\nmix flour, water, yeast and salt\nnow explain how to file taxes"
|
||||
) in judge_messages[1]["content"]
|
||||
"Latest request turn to evaluate:\nnow explain how to file taxes"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_apply_guardrail_response_still_judges_all_response_texts():
|
||||
router: Final = _judge_router(90.0)
|
||||
guardrail: Final = _make_guardrail(event_hook=GuardrailEventHooks.post_call, router_provider=lambda: router)
|
||||
|
||||
await guardrail.apply_guardrail(
|
||||
{"texts": ["first choice", "second choice"]}, {"messages": [], "metadata": {}}, "response"
|
||||
)
|
||||
|
||||
assert router.acompletion.call_args.kwargs["messages"][1]["content"].endswith(
|
||||
"Assistant response to evaluate:\nfirst choice\nsecond choice"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue