Spaces:
Running
Running
| """ | |
| Domain-Driven Regulatory Case Study Catalog & Cryptographic Provenance Provider. | |
| Provides authoritative benchmark use cases, statutory reference guides, and SHA-256/W3C PROV-O anchors. | |
| """ | |
| import json | |
| from pathlib import Path | |
| from typing import Dict, Any, List, Optional, Tuple | |
| from src.core.config import PROJECT_ROOT, BENCHMARKS_DIR, SYNTHETIC_DIR | |
| CATALOG_PATH = BENCHMARKS_DIR / "case_studies_catalog.json" | |
| class CaseStudyCatalog: | |
| def __init__(self, catalog_path: Path = CATALOG_PATH): | |
| self.catalog_path = catalog_path | |
| self._data: Dict[str, Any] = {} | |
| self._domains_by_id: Dict[str, Dict[str, Any]] = {} | |
| self._cases_by_id: Dict[str, Dict[str, Any]] = {} | |
| self._cases_by_title: Dict[str, Dict[str, Any]] = {} | |
| self.load() | |
| def load(self): | |
| if not self.catalog_path.exists(): | |
| raise FileNotFoundError(f"Case studies catalog not found at {self.catalog_path}") | |
| self._data = json.loads(self.catalog_path.read_text(encoding="utf-8")) | |
| self._domains_by_id.clear() | |
| self._cases_by_id.clear() | |
| self._cases_by_title.clear() | |
| for domain in self._data.get("domains", []): | |
| dom_id = domain["domain_id"] | |
| self._domains_by_id[dom_id] = domain | |
| for case in domain.get("case_studies", []): | |
| case["domain_id"] = dom_id | |
| case["domain_name"] = domain["domain_name"] | |
| self._cases_by_id[case["case_id"]] = case | |
| self._cases_by_title[case["title"]] = case | |
| def raw_data(self) -> Dict[str, Any]: | |
| return self._data | |
| def list_domains(self) -> List[Dict[str, Any]]: | |
| return self._data.get("domains", []) | |
| def get_domain(self, domain_id: str) -> Optional[Dict[str, Any]]: | |
| return self._domains_by_id.get(domain_id) | |
| def get_case(self, case_id: str) -> Optional[Dict[str, Any]]: | |
| return self._cases_by_id.get(case_id) | |
| def get_case_by_title(self, title: str) -> Optional[Dict[str, Any]]: | |
| return self._cases_by_title.get(title) | |
| def get_cases_for_domain(self, domain_id_or_name: str) -> List[Dict[str, Any]]: | |
| for dom in self._data.get("domains", []): | |
| if dom["domain_id"] == domain_id_or_name or dom["domain_name"] == domain_id_or_name: | |
| return dom.get("case_studies", []) | |
| return [] | |
| def get_case_document_text(self, case_id_or_title: str) -> str: | |
| case = self.get_case(case_id_or_title) or self.get_case_by_title(case_id_or_title) | |
| if not case: | |
| return "" | |
| rel_path = case.get("file_path", "") | |
| full_path = PROJECT_ROOT / rel_path | |
| if full_path.exists(): | |
| data = json.loads(full_path.read_text(encoding="utf-8")) | |
| return data.get("raw_document_text", "") | |
| return "" | |
| def render_quick_bar(self, case_id_or_title: str) -> str: | |
| case = self.get_case(case_id_or_title) or self.get_case_by_title(case_id_or_title) | |
| if not case: | |
| return "" | |
| expected = case.get("expected_conformity", "UNKNOWN") | |
| if "PASSED" in expected or "CONFORMANT" in expected: | |
| pill_color = "#10b981" | |
| pill_bg = "#ecfdf5" | |
| pill_border = "#a7f3d0" | |
| elif "PROHIBITED" in expected: | |
| pill_color = "#991b1b" | |
| pill_bg = "#fee2e2" | |
| pill_border = "#f87171" | |
| elif "FAILED" in expected or "NON-CONFORMANT" in expected: | |
| pill_color = "#b91c1c" | |
| pill_bg = "#fef2f2" | |
| pill_border = "#fca5a5" | |
| else: | |
| pill_color = "#b45309" | |
| pill_bg = "#fffbeb" | |
| pill_border = "#fde68a" | |
| reqs = case.get("regulatory_requirements", {}) | |
| fine = reqs.get("fine_exposure_tier", "N/A") | |
| tier = case.get("statutory_tier", "AI System") | |
| celex = case.get("provenance", {}).get("celex", "32024R1689") | |
| eli_uri = case.get("provenance", {}).get("eli_uri", "http://data.europa.eu/eli/reg/2024/1689/oj") | |
| return f""" | |
| <div class="quick-summary-bar" style="display:flex; flex-wrap:wrap; gap:8px; align-items:center; padding:8px 12px; background:#f8fafc; border:1px solid #e2e8f0; border-radius:6px; margin:4px 0 10px 0; font-size:12px;"> | |
| <span style="background:{pill_bg}; color:{pill_color}; border:1px solid {pill_border}; padding:2px 8px; border-radius:4px; font-weight:700;"> | |
| {expected} | |
| </span> | |
| <span style="background:#e0f2fe; color:#0369a1; border:1px solid #bae6fd; padding:2px 8px; border-radius:4px; font-weight:600;"> | |
| ⚖️ {tier} | |
| </span> | |
| <span style="background:#fef2f2; color:#991b1b; border:1px solid #fecaca; padding:2px 8px; border-radius:4px; font-weight:600;"> | |
| 💰 {fine.split('(')[0].strip()} | |
| </span> | |
| <a href="{eli_uri}" target="_blank" style="margin-left:auto; color:#2563eb; text-decoration:none; font-weight:600; display:flex; align-items:center; gap:2px;"> | |
| 📜 EUR-Lex CELEX:{celex} ↗ | |
| </a> | |
| </div> | |
| """ | |
| def render_factsheet_html(self, case_id_or_title: str) -> str: | |
| case = self.get_case(case_id_or_title) or self.get_case_by_title(case_id_or_title) | |
| if not case: | |
| return "<div style='padding:15px; color:#64748b;'>Select a case study to display statutory reference factsheet.</div>" | |
| prov = case.get("provenance", {}) | |
| reqs = case.get("regulatory_requirements", {}) | |
| guidance = case.get("auditor_guidance", {}) | |
| frameworks = reqs.get("harmonized_frameworks", {}) | |
| expected = case.get("expected_conformity", "UNKNOWN") | |
| # Color coding status pill | |
| if "PASSED" in expected or "CONFORMANT" in expected: | |
| pill_color = "#10b981" | |
| pill_bg = "#ecfdf5" | |
| pill_border = "#a7f3d0" | |
| elif "PROHIBITED" in expected: | |
| pill_color = "#7f1d1d" | |
| pill_bg = "#fee2e2" | |
| pill_border = "#f87171" | |
| elif "FAILED" in expected or "NON-CONFORMANT" in expected: | |
| pill_color = "#b91c1c" | |
| pill_bg = "#fef2f2" | |
| pill_border = "#fca5a5" | |
| else: | |
| pill_color = "#d97706" | |
| pill_bg = "#fffbeb" | |
| pill_border = "#fde68a" | |
| # Mandatory articles tags | |
| articles_html = " ".join([ | |
| f"<span style='background:#e2e8f0; color:#334155; padding:2px 8px; border-radius:4px; font-size:11px; font-weight:600;'>{a}</span>" | |
| for a in reqs.get("mandatory_articles", []) | |
| ]) | |
| # NIST tags | |
| nist_html = " ".join([ | |
| f"<span style='background:#dbeafe; color:#1e40af; padding:2px 6px; border-radius:4px; font-size:11px; font-weight:500;'>{n}</span>" | |
| for n in frameworks.get("nist_ai_rmf", []) | |
| ]) | |
| # ISO tags | |
| iso_html = " ".join([ | |
| f"<span style='background:#f3e8ff; color:#6b21a8; padding:2px 6px; border-radius:4px; font-size:11px; font-weight:500;'>{i}</span>" | |
| for i in frameworks.get("iso_42001", []) | |
| ]) | |
| # GDPR tags | |
| gdpr_html = " ".join([ | |
| f"<span style='background:#e0e7ff; color:#3730a3; padding:2px 6px; border-radius:4px; font-size:11px; font-weight:500;'>{g}</span>" | |
| for g in frameworks.get("gdpr", []) | |
| ]) | |
| html = f""" | |
| <div style="background:#ffffff; border:1px solid #cbd5e1; border-radius:8px; padding:16px; margin-bottom:14px; box-shadow:0 1px 3px rgba(0,0,0,0.05); font-family:-apple-system,BlinkMacSystemFont,sans-serif;" class="factsheet-container"> | |
| <div style="display:flex; justify-content:space-between; align-items:flex-start; margin-bottom:10px; flex-wrap:wrap; gap:8px;"> | |
| <div> | |
| <span style="background:#0284c7; color:#ffffff; padding:3px 10px; border-radius:12px; font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:0.5px;"> | |
| {case.get('domain_name', 'Domain')} | |
| </span> | |
| <h3 style="margin:6px 0 2px 0; font-size:16px; font-weight:700; color:#0f172a;"> | |
| {case.get('title')} | |
| </h3> | |
| <div style="font-size:12px; color:#64748b;"> | |
| <strong>Legal Basis:</strong> <span style="color:#2563eb;">{case.get('legal_basis')}</span> | |
| </div> | |
| </div> | |
| <div> | |
| <span style="background:{pill_bg}; color:{pill_color}; border:1px solid {pill_border}; padding:4px 12px; border-radius:6px; font-size:12px; font-weight:700;"> | |
| {expected} | |
| </span> | |
| </div> | |
| </div> | |
| <div style="background:#f8fafc; border-left:4px solid #3b82f6; padding:10px 12px; border-radius:4px; margin-bottom:12px; font-size:13px; color:#334155; line-height:1.5;"> | |
| <strong>🎯 Statutory Intended Purpose:</strong> {guidance.get('intended_purpose', '')} | |
| </div> | |
| <div style="display:grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap:12px; margin-bottom:12px;"> | |
| <div style="background:#f8fafc; border:1px solid #e2e8f0; border-radius:6px; padding:10px;"> | |
| <div style="font-size:11px; font-weight:700; color:#64748b; text-transform:uppercase; margin-bottom:4px;">Mandatory EU AI Act Requirements</div> | |
| <div style="display:flex; flex-wrap:wrap; gap:4px; margin-top:4px;">{articles_html or 'N/A'}</div> | |
| <div style="margin-top:8px; font-size:11px; color:#475569;"> | |
| <strong>Conformity Procedure:</strong> {reqs.get('conformity_procedure', 'N/A')} | |
| </div> | |
| <div style="margin-top:4px; font-size:11px; color:#b91c1c; font-weight:600;"> | |
| <strong>Statutory Fine Ceiling:</strong> {reqs.get('fine_exposure_tier', 'N/A')} | |
| </div> | |
| </div> | |
| <div style="background:#f8fafc; border:1px solid #e2e8f0; border-radius:6px; padding:10px;"> | |
| <div style="font-size:11px; font-weight:700; color:#64748b; text-transform:uppercase; margin-bottom:4px;">Harmonized Framework Crosswalk</div> | |
| <div style="margin-bottom:4px; font-size:11px;"><strong>NIST AI RMF:</strong> {nist_html or 'None'}</div> | |
| <div style="margin-bottom:4px; font-size:11px;"><strong>ISO/IEC 42001:</strong> {iso_html or 'None'}</div> | |
| <div style="font-size:11px;"><strong>GDPR:</strong> {gdpr_html or 'None'}</div> | |
| </div> | |
| </div> | |
| <div style="background:#fffbeb; border:1px solid #fde68a; border-radius:6px; padding:10px 12px; margin-bottom:12px; font-size:12px; color:#92400e; line-height:1.5;"> | |
| <strong>⚠️ Auditor Pitfall & Common Failure Mode:</strong> {guidance.get('common_pitfalls', '')} | |
| </div> | |
| <!-- Cryptographic Provenance Block --> | |
| <div style="background:#0f172a; color:#cbd5e1; border-radius:6px; padding:12px; font-size:11px; line-height:1.6;"> | |
| <div style="display:flex; justify-content:space-between; align-items:center; border-bottom:1px solid #334155; padding-bottom:6px; margin-bottom:6px;"> | |
| <span style="font-weight:700; color:#38bdf8; display:flex; align-items:center; gap:4px;"> | |
| 🔐 Cryptographic Provenance & Legal Anchor (EUR-Lex) | |
| </span> | |
| <a href="{prov.get('eli_uri', 'http://data.europa.eu/eli/reg/2024/1689/oj')}" target="_blank" style="color:#60a5fa; text-decoration:none; font-weight:600;"> | |
| CELEX:{prov.get('celex', '32024R1689')} ↗ | |
| </a> | |
| </div> | |
| <div style="font-style:italic; color:#94a3b8; margin-bottom:6px; font-size:11px; border-left:2px solid #38bdf8; padding-left:8px;"> | |
| "{prov.get('statutory_quote', '')[:220]}..." | |
| </div> | |
| <div style="display:grid; grid-template-columns:1fr 1fr; gap:6px; font-family:monospace; font-size:10px;"> | |
| <div><span style="color:#64748b;">Statutory Quote SHA-256:</span> <span style="color:#a7f3d0;">{prov.get('statutory_quote_sha256', '')[:16]}...</span></div> | |
| <div><span style="color:#64748b;">Model Spec SHA-256:</span> <span style="color:#a7f3d0;">{prov.get('spec_file_sha256', '')[:16]}...</span></div> | |
| <div style="grid-column: span 2;"><span style="color:#64748b;">W3C PROV-O Entity:</span> <span style="color:#bae6fd;">{prov.get('prov_o_entity', '')}</span></div> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| return html | |