fix(guardrails/peyeeye): wrap json decode errors, simplify session cleanup

- Move response.json() inside the try/except in _post so JSON decode
  failures surface as PEyeEyeGuardrailAPIError rather than a raw
  ValueError.
- Drop the redundant ses_-prefix check in async_post_call_success_hook;
  peyeeye_session_mode == 'stateful' is already the gate, and the prefix
  check skipped legitimate cleanup if the API ever returned a different
  id shape.
- Add test_redact_json_decode_error_raises_typed covering the new
  wrapping branch.
This commit is contained in:
Tim 2026-04-25 23:43:08 -05:00
parent 0fa294b18c
commit c35b0e3714
2 changed files with 28 additions and 3 deletions

View file

@ -211,8 +211,9 @@ class PEyeEyeGuardrail(CustomGuardrail):
new_parts.append(part)
message.content = new_parts
# Clean up: drop the session server-side and from cache.
if self.peyeeye_session_mode == "stateful" and session_id.startswith("ses_"):
# Clean up: drop the stateful session server-side. Stateless
# ``skey_…`` blobs hold no server-side state, so skip the DELETE.
if self.peyeeye_session_mode == "stateful":
try:
await self._delete_session(session_id)
except Exception as e:
@ -288,9 +289,9 @@ class PEyeEyeGuardrail(CustomGuardrail):
url=url, headers=self._headers(), json=body, timeout=15.0
)
response.raise_for_status()
return response.json()
except Exception as e:
self._reraise_api_error(e, path)
return response.json()
def _headers(self) -> Dict[str, str]:
return {

View file

@ -236,6 +236,30 @@ async def test_redact_api_error_raises_typed():
)
@pytest.mark.asyncio
async def test_redact_json_decode_error_raises_typed():
"""If response.json() blows up, surface a typed PEyeEyeGuardrailAPIError."""
g = PEyeEyeGuardrail(peyeeye_api_key="pk", guardrail_name="t")
g.async_handler = MagicMock()
bad = MagicMock()
bad.raise_for_status = MagicMock()
bad.json.side_effect = ValueError("not json")
g.async_handler.post = AsyncMock(return_value=bad)
cache = DualCache()
with pytest.raises(PEyeEyeGuardrailAPIError):
await g.async_pre_call_hook(
UserAPIKeyAuth(api_key="x"),
cache,
{
"messages": [{"role": "user", "content": "hi"}],
"litellm_call_id": "json-err",
},
"completion",
)
@pytest.mark.asyncio
async def test_pre_call_raises_on_length_mismatch():
"""If /v1/redact returns fewer texts than sent, refuse to forward."""