mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-05 06:22:12 +00:00
fix(proxy): preserve dict guardrail HTTPException.detail + bedrock context (#25558)
This commit is contained in:
@@ -190,3 +190,79 @@ def test_get_projected_spend_over_limit_includes_current_spend(monkeypatch):
|
||||
projected_spend, projected_exceeded_date = result
|
||||
assert projected_spend == 290.0
|
||||
assert projected_exceeded_date == real_datetime.date(2026, 4, 21)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# L2: _enrich_http_exception_with_guardrail_context
|
||||
# Regression coverage for case 2026-04-10-internal-bedrock-guardrail-streaming-error.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_enrich_http_exception_with_guardrail_context_dict_detail():
|
||||
"""L2: dict-detail HTTPException is enriched with guardrail_name and mode."""
|
||||
from litellm.proxy.utils import _enrich_http_exception_with_guardrail_context
|
||||
|
||||
class StubCallback:
|
||||
guardrail_name = "bedrock-pii-guard"
|
||||
event_hook = "post_call"
|
||||
|
||||
exc = HTTPException(
|
||||
status_code=400, detail={"error": "Violated guardrail policy"}
|
||||
)
|
||||
_enrich_http_exception_with_guardrail_context(exc, StubCallback())
|
||||
assert exc.detail["guardrail_name"] == "bedrock-pii-guard"
|
||||
assert exc.detail["guardrail_mode"] == "post_call"
|
||||
|
||||
|
||||
def test_enrich_http_exception_string_detail_noop():
|
||||
"""L2: string-detail HTTPException is not mutated (can't add fields to a str)."""
|
||||
from litellm.proxy.utils import _enrich_http_exception_with_guardrail_context
|
||||
|
||||
class StubCallback:
|
||||
guardrail_name = "x"
|
||||
event_hook = "pre_call"
|
||||
|
||||
exc = HTTPException(status_code=400, detail="Content blocked")
|
||||
_enrich_http_exception_with_guardrail_context(exc, StubCallback())
|
||||
assert exc.detail == "Content blocked"
|
||||
|
||||
|
||||
def test_enrich_http_exception_setdefault_does_not_overwrite():
|
||||
"""L2: a guardrail that already populates guardrail_name explicitly wins."""
|
||||
from litellm.proxy.utils import _enrich_http_exception_with_guardrail_context
|
||||
|
||||
class StubCallback:
|
||||
guardrail_name = "inferred-name"
|
||||
event_hook = "pre_call"
|
||||
|
||||
exc = HTTPException(
|
||||
status_code=400,
|
||||
detail={"error": "x", "guardrail_name": "explicit-name"},
|
||||
)
|
||||
_enrich_http_exception_with_guardrail_context(exc, StubCallback())
|
||||
assert exc.detail["guardrail_name"] == "explicit-name"
|
||||
|
||||
|
||||
def test_enrich_http_exception_non_http_exception_noop():
|
||||
"""L2: non-HTTPException is left alone and the helper does not raise."""
|
||||
from litellm.proxy.utils import _enrich_http_exception_with_guardrail_context
|
||||
|
||||
class StubCallback:
|
||||
guardrail_name = "x"
|
||||
event_hook = "pre_call"
|
||||
|
||||
exc = ValueError("not an HTTPException")
|
||||
_enrich_http_exception_with_guardrail_context(exc, StubCallback())
|
||||
assert str(exc) == "not an HTTPException"
|
||||
|
||||
|
||||
def test_enrich_http_exception_callback_without_guardrail_name_noop():
|
||||
"""L2: callback without guardrail_name attribute leaves detail alone."""
|
||||
from litellm.proxy.utils import _enrich_http_exception_with_guardrail_context
|
||||
|
||||
class StubCallback:
|
||||
pass
|
||||
|
||||
exc = HTTPException(status_code=400, detail={"error": "x"})
|
||||
_enrich_http_exception_with_guardrail_context(exc, StubCallback())
|
||||
assert exc.detail == {"error": "x"}
|
||||
|
||||
Reference in New Issue
Block a user