From 3a93c5d10ae55175940fe5e7e6cb64746ec08aff Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Tue, 17 Feb 2026 16:26:21 -0800 Subject: [PATCH] checkEuAiActCompliance --- .../GuardrailViewer/CompliancePanel.tsx | 195 ++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 ui/litellm-dashboard/src/components/view_logs/GuardrailViewer/CompliancePanel.tsx diff --git a/ui/litellm-dashboard/src/components/view_logs/GuardrailViewer/CompliancePanel.tsx b/ui/litellm-dashboard/src/components/view_logs/GuardrailViewer/CompliancePanel.tsx new file mode 100644 index 00000000000..46a3eb0379c --- /dev/null +++ b/ui/litellm-dashboard/src/components/view_logs/GuardrailViewer/CompliancePanel.tsx @@ -0,0 +1,195 @@ +import React, { useState, useEffect } from "react"; +import { Tooltip } from "antd"; +import { + checkEuAiActCompliance, + checkGdprCompliance, + ComplianceResponse, + ComplianceCheckRequest, +} from "@/components/networking"; + +interface CompliancePanelProps { + accessToken: string | null; + logEntry: { + request_id: string; + user?: string; + model?: string; + startTime?: string; + metadata?: Record; + }; +} + +// -- Icons -- + +const CheckIcon = () => ( + + + + +); + +const CrossIcon = () => ( + + + + +); + +const SpinnerIcon = () => ( + + + + +); + +// -- Sub-components -- + +const ComplianceCard = ({ + title, + data, + loading, + error, +}: { + title: string; + data: ComplianceResponse | null; + loading: boolean; + error: string | null; +}) => { + const [expanded, setExpanded] = useState(false); + + return ( +
+
setExpanded(!expanded)} + > +
+ {loading ? ( + + ) : error ? ( + + -- + + ) : data?.compliant ? ( + + ) : ( + + )} + {title} +
+
+ {!loading && !error && data && ( + + {data.compliant ? "COMPLIANT" : "NON-COMPLIANT"} + + )} + {error && ( + + UNAVAILABLE + + )} + + + +
+
+ + {expanded && ( +
+ {loading &&

Checking compliance...

} + {error &&

{error}

} + {data && ( +
+ {data.checks.map((check, idx) => ( +
+
+ {check.passed ? : } +
+
+
+ {check.check_name} + {check.article} +
+

{check.detail}

+
+
+ ))} +
+ )} +
+ )} +
+ ); +}; + +// -- Main Component -- + +const CompliancePanel: React.FC = ({ accessToken, logEntry }) => { + const [euAiActData, setEuAiActData] = useState(null); + const [gdprData, setGdprData] = useState(null); + const [euAiActLoading, setEuAiActLoading] = useState(false); + const [gdprLoading, setGdprLoading] = useState(false); + const [euAiActError, setEuAiActError] = useState(null); + const [gdprError, setGdprError] = useState(null); + + useEffect(() => { + if (!accessToken || !logEntry.request_id) return; + + const payload: ComplianceCheckRequest = { + request_id: logEntry.request_id, + user_id: logEntry.user, + model: logEntry.model, + timestamp: logEntry.startTime, + guardrail_information: logEntry.metadata?.guardrail_information, + }; + + setEuAiActLoading(true); + setEuAiActError(null); + checkEuAiActCompliance(accessToken, payload) + .then(setEuAiActData) + .catch((err) => setEuAiActError(err.message || "Failed to check EU AI Act compliance")) + .finally(() => setEuAiActLoading(false)); + + setGdprLoading(true); + setGdprError(null); + checkGdprCompliance(accessToken, payload) + .then(setGdprData) + .catch((err) => setGdprError(err.message || "Failed to check GDPR compliance")) + .finally(() => setGdprLoading(false)); + }, [accessToken, logEntry.request_id]); + + return ( +
+

+ Regulatory Compliance +

+
+ + +
+
+ ); +}; + +export default CompliancePanel;