From e3e4956050853b151b4a85a156891b09cfb151f1 Mon Sep 17 00:00:00 2001 From: Milan Date: Fri, 3 Apr 2026 17:45:11 +0200 Subject: [PATCH] fix compliance checks for array guardrail modes Treat guardrail_mode arrays like [\"pre_call\", \"post_call\"] as valid pre-call executions so EU AI Act and GDPR checks stop reporting false non-compliance. Add regression tests to cover both EU and GDPR paths. Made-with: Cursor --- litellm/proxy/compliance_checks.py | 2 + .../test_compliance_endpoints.py | 42 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/litellm/proxy/compliance_checks.py b/litellm/proxy/compliance_checks.py index 381b0f815d2..018ea47aa66 100644 --- a/litellm/proxy/compliance_checks.py +++ b/litellm/proxy/compliance_checks.py @@ -41,6 +41,8 @@ class ComplianceChecker: # If no mode specified, default to pre_call if g_mode is None and mode == "pre_call": result.append(g) + elif isinstance(g_mode, list) and mode in g_mode: + result.append(g) elif g_mode == mode: result.append(g) return result diff --git a/tests/test_litellm/proxy/management_endpoints/test_compliance_endpoints.py b/tests/test_litellm/proxy/management_endpoints/test_compliance_endpoints.py index 2c41b16ba7f..d96fec8948c 100644 --- a/tests/test_litellm/proxy/management_endpoints/test_compliance_endpoints.py +++ b/tests/test_litellm/proxy/management_endpoints/test_compliance_endpoints.py @@ -310,6 +310,27 @@ class TestEuAiActCompliant: checks = ComplianceChecker(data).check_eu_ai_act() assert all(c.passed for c in checks) + def test_pre_call_detected_when_mode_is_array(self): + """Array guardrail_mode should count toward pre-call screening.""" + data = ComplianceCheckRequest( + request_id="req-203", + user_id="user-2", + model="gpt-4o-mini", + timestamp="2026-02-17T12:00:00Z", + guardrail_information=[ + { + "guardrail_name": "presidio-output-parse", + "guardrail_mode": ["pre_call", "post_call"], + "guardrail_status": "success", + } + ], + ) + checks = ComplianceChecker(data).check_eu_ai_act() + results = {c.check_name: c.passed for c in checks} + assert results["Guardrails applied"] is True + assert results["Content screened before LLM"] is True + assert results["Audit record complete"] is True + # --------------------------------------------------------------------------- # GDPR — Compliant cases (Task #4) @@ -385,3 +406,24 @@ class TestGdprCompliant: ) checks = ComplianceChecker(data).check_gdpr() assert all(c.passed for c in checks) + + def test_gdpr_pre_call_detected_when_mode_is_array(self): + """Array guardrail_mode should satisfy GDPR pre-call data protection checks.""" + data = ComplianceCheckRequest( + request_id="req-304", + user_id="user-2", + model="gpt-4o-mini", + timestamp="2026-02-17T12:00:00Z", + guardrail_information=[ + { + "guardrail_name": "presidio-output-parse", + "guardrail_mode": ["pre_call", "post_call"], + "guardrail_status": "success", + } + ], + ) + checks = ComplianceChecker(data).check_gdpr() + results = {c.check_name: c.passed for c in checks} + assert results["Data protection applied"] is True + assert results["Sensitive data protected"] is True + assert results["Audit record complete"] is True