Spaces:
Sleeping
Sleeping
Deploy ReguAI: Neuro-Symbolic AI GRC & Automated Conformity Assessment Engine
Browse files- app.py +204 -91
- data/active_learning_triplets.jsonl +1 -0
app.py
CHANGED
|
@@ -37,70 +37,48 @@ DEFAULT_FACTSHEET = catalog.render_factsheet_html(DEFAULT_CASE_TITLE)
|
|
| 37 |
DEFAULT_QUICK_BAR = catalog.render_quick_bar(DEFAULT_CASE_TITLE)
|
| 38 |
|
| 39 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
def on_domain_change(selected_domain_name: str):
|
| 41 |
cases = catalog.get_cases_for_domain(selected_domain_name)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
if not cases:
|
| 43 |
-
return gr.update(choices=[], value=None), "", "", "<div style='padding:15px;'>No cases found.</div>"
|
| 44 |
titles = [c["title"] for c in cases]
|
| 45 |
first_title = titles[0]
|
| 46 |
text = catalog.get_case_document_text(first_title)
|
| 47 |
factsheet = catalog.render_factsheet_html(first_title)
|
| 48 |
quick_bar = catalog.render_quick_bar(first_title)
|
| 49 |
-
return gr.update(choices=titles, value=first_title), text, quick_bar, factsheet
|
| 50 |
|
| 51 |
|
| 52 |
def on_case_change(selected_case_title: str):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
if not selected_case_title:
|
| 54 |
-
return "", "", "<div style='padding:15px;'>Select a case study.</div>"
|
| 55 |
text = catalog.get_case_document_text(selected_case_title)
|
| 56 |
factsheet = catalog.render_factsheet_html(selected_case_title)
|
| 57 |
quick_bar = catalog.render_quick_bar(selected_case_title)
|
| 58 |
-
return text, quick_bar, factsheet
|
| 59 |
-
|
| 60 |
|
| 61 |
-
def load_preset(domain_idx: int, case_idx: int = 0):
|
| 62 |
-
domains = catalog.list_domains()
|
| 63 |
-
if domain_idx >= len(domains):
|
| 64 |
-
domain_idx = 0
|
| 65 |
-
dom = domains[domain_idx]
|
| 66 |
-
dom_name = dom["domain_name"]
|
| 67 |
-
cases = dom.get("case_studies", [])
|
| 68 |
-
if not cases:
|
| 69 |
-
return gr.update(), gr.update(), "", "", ""
|
| 70 |
-
case = cases[min(case_idx, len(cases) - 1)]
|
| 71 |
-
case_title = case["title"]
|
| 72 |
-
case_titles = [c["title"] for c in cases]
|
| 73 |
-
text = catalog.get_case_document_text(case_title)
|
| 74 |
-
factsheet = catalog.render_factsheet_html(case_title)
|
| 75 |
-
quick_bar = catalog.render_quick_bar(case_title)
|
| 76 |
-
return (
|
| 77 |
-
gr.update(value=dom_name),
|
| 78 |
-
gr.update(choices=case_titles, value=case_title),
|
| 79 |
-
text,
|
| 80 |
-
quick_bar,
|
| 81 |
-
factsheet,
|
| 82 |
-
)
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
def run_assessment(doc_text: str, auditor_id: str, annual_turnover: float = 50000000.0, is_sme: bool = False):
|
| 86 |
-
if not doc_text or not doc_text.strip():
|
| 87 |
-
return (
|
| 88 |
-
"β οΈ Please enter model card text or select a pre-loaded sample.",
|
| 89 |
-
[],
|
| 90 |
-
[],
|
| 91 |
-
"<div style='padding:20px;text-align:center;'>No graph generated.</div>",
|
| 92 |
-
"N/A",
|
| 93 |
-
"N/A",
|
| 94 |
-
"{}",
|
| 95 |
-
"",
|
| 96 |
-
"<div>No certificate generated.</div>",
|
| 97 |
-
[],
|
| 98 |
-
[],
|
| 99 |
-
"<div style='padding:15px;'>No fine liability evaluated.</div>",
|
| 100 |
-
"{}",
|
| 101 |
-
"{}",
|
| 102 |
-
)
|
| 103 |
|
|
|
|
| 104 |
report = engine.evaluate_system(
|
| 105 |
doc_text,
|
| 106 |
auditor_id=auditor_id or "auditor_01",
|
|
@@ -249,32 +227,121 @@ def run_assessment(doc_text: str, auditor_id: str, annual_turnover: float = 5000
|
|
| 249 |
bom_json = json.dumps(engine.report_generator.generate_cyclonedx_bom(report), indent=2)
|
| 250 |
sarif_json = SarifExporter.export_sarif_json(report, indent=2)
|
| 251 |
|
| 252 |
-
return
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 268 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 274 |
|
| 275 |
|
| 276 |
# Pre-compute live initial evaluation for default case study so dashboard opens fully populated
|
| 277 |
-
DEFAULT_ASSESSMENT =
|
|
|
|
| 278 |
|
| 279 |
|
| 280 |
def record_triage(claim_id: str, new_status: str, new_category: str, notes: str, auditor_id: str):
|
|
@@ -790,6 +857,21 @@ button[variant="primary"]:hover {
|
|
| 790 |
color: #60a5fa !important;
|
| 791 |
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4) !important;
|
| 792 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 793 |
"""
|
| 794 |
|
| 795 |
with gr.Blocks(title="ReguAI: Neuro-Symbolic AI GRC Engine") as demo:
|
|
@@ -880,21 +962,23 @@ with gr.Blocks(title="ReguAI: Neuro-Symbolic AI GRC Engine") as demo:
|
|
| 880 |
with gr.Row():
|
| 881 |
assess_btn = gr.Button("β‘ Run Deterministic Conformity Assessment", variant="primary", size="lg")
|
| 882 |
|
| 883 |
-
gr.HTML(
|
| 884 |
-
|
| 885 |
-
|
| 886 |
-
π‘ Select a sector and case study above, adjust specifications or turnover if needed, and click <strong>Run Deterministic Conformity Assessment</strong>. Switch to tabs <strong>2οΈβ£ Review Grounding</strong>, <strong>3οΈβ£ Run SHACL Proofs</strong>, and <strong>4οΈβ£ Export Annex IV Package</strong> to inspect the results.
|
| 887 |
-
</div>
|
| 888 |
-
"""
|
| 889 |
)
|
| 890 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 891 |
# =============================================================
|
| 892 |
# TAB 2: 2οΈβ£ Review Grounding
|
| 893 |
# =============================================================
|
| 894 |
with gr.TabItem("2οΈβ£ Review Grounding", id="tab_review_grounding"):
|
| 895 |
with gr.Tabs():
|
| 896 |
with gr.TabItem("πΈοΈ Knowledge Graph & Ontology"):
|
| 897 |
-
graph_output = gr.HTML(value=DEFAULT_ASSESSMENT[
|
| 898 |
with gr.TabItem("π EUR-Lex Legal Factsheet"):
|
| 899 |
factsheet_box = gr.HTML(
|
| 900 |
value=DEFAULT_FACTSHEET,
|
|
@@ -904,7 +988,7 @@ with gr.Blocks(title="ReguAI: Neuro-Symbolic AI GRC Engine") as demo:
|
|
| 904 |
claims_table = gr.Dataframe(
|
| 905 |
headers=["Claim ID", "Category", "Status", "Confidence", "Target Article", "Evidence Span"],
|
| 906 |
datatype=["str", "str", "str", "str", "str", "str"],
|
| 907 |
-
value=DEFAULT_ASSESSMENT[
|
| 908 |
label="Extracted Regulatory Claims",
|
| 909 |
)
|
| 910 |
with gr.TabItem("π€ Active Learning Triage Queue"):
|
|
@@ -912,7 +996,7 @@ with gr.Blocks(title="ReguAI: Neuro-Symbolic AI GRC Engine") as demo:
|
|
| 912 |
borderline_table = gr.Dataframe(
|
| 913 |
headers=["Claim ID", "Category", "Status", "Confidence", "Evidence Quote"],
|
| 914 |
datatype=["str", "str", "str", "str", "str"],
|
| 915 |
-
value=DEFAULT_ASSESSMENT[
|
| 916 |
)
|
| 917 |
with gr.Row():
|
| 918 |
triage_claim_id = gr.Textbox(label="Claim ID", placeholder="e.g. clm_001", scale=3)
|
|
@@ -927,47 +1011,61 @@ with gr.Blocks(title="ReguAI: Neuro-Symbolic AI GRC Engine") as demo:
|
|
| 927 |
frameworks_table = gr.Dataframe(
|
| 928 |
headers=["Target Framework", "Control ID", "Control Name", "Status", "Linked AI Act Article", "Audit Guidance"],
|
| 929 |
datatype=["str", "str", "str", "str", "str", "str"],
|
| 930 |
-
value=DEFAULT_ASSESSMENT[
|
| 931 |
label="Harmonized Multi-Framework Controls",
|
| 932 |
)
|
| 933 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 934 |
# =============================================================
|
| 935 |
# TAB 3: 3οΈβ£ Run SHACL Proofs
|
| 936 |
# =============================================================
|
| 937 |
with gr.TabItem("3οΈβ£ Run SHACL Proofs", id="tab_run_shacl_proofs"):
|
| 938 |
with gr.Row():
|
| 939 |
assess_btn_tab3 = gr.Button("β‘ Re-Run Deterministic Conformity Proofs", variant="primary", size="md")
|
| 940 |
-
exec_output = gr.HTML(value=DEFAULT_ASSESSMENT[
|
| 941 |
-
fine_liability_output = gr.HTML(value=DEFAULT_ASSESSMENT[
|
| 942 |
violations_table = gr.Dataframe(
|
| 943 |
headers=["Legal Article", "Normative Requirement", "Severity", "SHACL Path", "Remediation Guidance"],
|
| 944 |
datatype=["str", "str", "str", "str", "str"],
|
| 945 |
-
value=DEFAULT_ASSESSMENT[
|
| 946 |
label="Mathematical Proof: Non-Conformities Found",
|
| 947 |
)
|
| 948 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 949 |
# =============================================================
|
| 950 |
# TAB 4: 4οΈβ£ Export Annex IV Package
|
| 951 |
# =============================================================
|
| 952 |
with gr.TabItem("4οΈβ£ Export Annex IV Package", id="tab_export_annex_iv"):
|
| 953 |
with gr.Tabs():
|
| 954 |
with gr.TabItem("π Attestation Certificate (HTML)"):
|
| 955 |
-
cert_html_output = gr.HTML(value=DEFAULT_ASSESSMENT[
|
| 956 |
with gr.TabItem("π¦ CycloneDX 1.6 AI-BOM"):
|
| 957 |
-
bom_display = gr.Code(value=DEFAULT_ASSESSMENT[
|
| 958 |
with gr.TabItem("π‘οΈ OASIS SARIF 2.1.0 Report"):
|
| 959 |
-
sarif_display = gr.Code(value=DEFAULT_ASSESSMENT[
|
| 960 |
with gr.TabItem("π Annex IV Report (Markdown)"):
|
| 961 |
-
report_markdown = gr.Markdown(value=DEFAULT_ASSESSMENT[
|
| 962 |
with gr.TabItem("π Machine-Readable JSON-LD"):
|
| 963 |
-
jsonld_display = gr.Code(value=DEFAULT_ASSESSMENT[
|
| 964 |
with gr.TabItem("π W3C PROV-O Ledger"):
|
| 965 |
-
token_display = gr.Textbox(value=DEFAULT_ASSESSMENT[
|
| 966 |
-
ledger_display = gr.Markdown(value=DEFAULT_ASSESSMENT[
|
|
|
|
|
|
|
|
|
|
|
|
|
| 967 |
|
| 968 |
-
# Assessment outputs list (
|
| 969 |
assessment_inputs = [spec_input, auditor_input, turnover_input, is_sme_input]
|
| 970 |
assessment_outputs = [
|
|
|
|
|
|
|
| 971 |
exec_output,
|
| 972 |
violations_table,
|
| 973 |
claims_table,
|
|
@@ -988,12 +1086,12 @@ with gr.Blocks(title="ReguAI: Neuro-Symbolic AI GRC Engine") as demo:
|
|
| 988 |
domain_dropdown.change(
|
| 989 |
fn=on_domain_change,
|
| 990 |
inputs=[domain_dropdown],
|
| 991 |
-
outputs=[case_dropdown, spec_input, quick_bar_box, factsheet_box],
|
| 992 |
)
|
| 993 |
case_dropdown.change(
|
| 994 |
fn=on_case_change,
|
| 995 |
inputs=[case_dropdown],
|
| 996 |
-
outputs=[spec_input, quick_bar_box, factsheet_box],
|
| 997 |
)
|
| 998 |
|
| 999 |
# Wire assessment buttons (Tab 1 primary and Tab 3 re-run)
|
|
@@ -1008,6 +1106,20 @@ with gr.Blocks(title="ReguAI: Neuro-Symbolic AI GRC Engine") as demo:
|
|
| 1008 |
outputs=assessment_outputs,
|
| 1009 |
)
|
| 1010 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1011 |
triage_btn.click(
|
| 1012 |
fn=record_triage,
|
| 1013 |
inputs=[triage_claim_id, triage_status, triage_cat, triage_notes, auditor_input],
|
|
@@ -1017,3 +1129,4 @@ with gr.Blocks(title="ReguAI: Neuro-Symbolic AI GRC Engine") as demo:
|
|
| 1017 |
if __name__ == "__main__":
|
| 1018 |
demo.launch(server_name="0.0.0.0", server_port=7860, share=False, theme=gr.themes.Soft(), css=CUSTOM_CSS)
|
| 1019 |
|
|
|
|
|
|
| 37 |
DEFAULT_QUICK_BAR = catalog.render_quick_bar(DEFAULT_CASE_TITLE)
|
| 38 |
|
| 39 |
|
| 40 |
+
READY_BANNER_HTML = """
|
| 41 |
+
<div style="margin-top: 12px; padding: 12px 16px; border-radius: 8px; background: #f8fafc; border: 1px dashed #cbd5e1; font-size: 13px; color: #64748b; display: flex; align-items: center; gap: 8px;">
|
| 42 |
+
<span>βΉοΈ</span>
|
| 43 |
+
<span>Ready to evaluate. Click <strong>β‘ Run Deterministic Conformity Assessment</strong> below to execute SHACL verification and auto-navigate to Proofs.</span>
|
| 44 |
+
</div>
|
| 45 |
+
"""
|
| 46 |
+
|
| 47 |
+
|
| 48 |
def on_domain_change(selected_domain_name: str):
|
| 49 |
cases = catalog.get_cases_for_domain(selected_domain_name)
|
| 50 |
+
banner = """
|
| 51 |
+
<div style="margin-top: 12px; padding: 12px 16px; border-radius: 8px; background: #f8fafc; border: 1px dashed #cbd5e1; font-size: 13px; color: #64748b; display: flex; align-items: center; gap: 8px;">
|
| 52 |
+
<span>βΉοΈ</span>
|
| 53 |
+
<span>New regulatory sector selected. Click <strong>β‘ Run Deterministic Conformity Assessment</strong> below to evaluate.</span>
|
| 54 |
+
</div>
|
| 55 |
+
"""
|
| 56 |
if not cases:
|
| 57 |
+
return gr.update(choices=[], value=None), "", "", "<div style='padding:15px;'>No cases found.</div>", banner
|
| 58 |
titles = [c["title"] for c in cases]
|
| 59 |
first_title = titles[0]
|
| 60 |
text = catalog.get_case_document_text(first_title)
|
| 61 |
factsheet = catalog.render_factsheet_html(first_title)
|
| 62 |
quick_bar = catalog.render_quick_bar(first_title)
|
| 63 |
+
return gr.update(choices=titles, value=first_title), text, quick_bar, factsheet, banner
|
| 64 |
|
| 65 |
|
| 66 |
def on_case_change(selected_case_title: str):
|
| 67 |
+
banner = """
|
| 68 |
+
<div style="margin-top: 12px; padding: 12px 16px; border-radius: 8px; background: #f8fafc; border: 1px dashed #cbd5e1; font-size: 13px; color: #64748b; display: flex; align-items: center; gap: 8px;">
|
| 69 |
+
<span>βΉοΈ</span>
|
| 70 |
+
<span>Case study loaded. Click <strong>β‘ Run Deterministic Conformity Assessment</strong> below to execute deterministic SHACL verification.</span>
|
| 71 |
+
</div>
|
| 72 |
+
"""
|
| 73 |
if not selected_case_title:
|
| 74 |
+
return "", "", "<div style='padding:15px;'>Select a case study.</div>", banner
|
| 75 |
text = catalog.get_case_document_text(selected_case_title)
|
| 76 |
factsheet = catalog.render_factsheet_html(selected_case_title)
|
| 77 |
quick_bar = catalog.render_quick_bar(selected_case_title)
|
| 78 |
+
return text, quick_bar, factsheet, banner
|
|
|
|
| 79 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 80 |
|
| 81 |
+
def compute_assessment_data(doc_text: str, auditor_id: str, annual_turnover: float = 50000000.0, is_sme: bool = False):
|
| 82 |
report = engine.evaluate_system(
|
| 83 |
doc_text,
|
| 84 |
auditor_id=auditor_id or "auditor_01",
|
|
|
|
| 227 |
bom_json = json.dumps(engine.report_generator.generate_cyclonedx_bom(report), indent=2)
|
| 228 |
sarif_json = SarifExporter.export_sarif_json(report, indent=2)
|
| 229 |
|
| 230 |
+
return {
|
| 231 |
+
"report": report,
|
| 232 |
+
"overall_conforms": report.overall_conforms,
|
| 233 |
+
"score": report.conformity_score,
|
| 234 |
+
"system_name": report.system_metadata.name,
|
| 235 |
+
"violations_count": len(report.violations),
|
| 236 |
+
"passed_count": report.passed_requirements_count,
|
| 237 |
+
"total_count": report.total_requirements_evaluated,
|
| 238 |
+
"exec_html": exec_html,
|
| 239 |
+
"violations_data": violations_data,
|
| 240 |
+
"claims_data": claims_data,
|
| 241 |
+
"graph_iframe_html": graph_iframe_html,
|
| 242 |
+
"cert_token": cert_token,
|
| 243 |
+
"hash_summary": hash_summary,
|
| 244 |
+
"json_ld_cert": json_ld_cert,
|
| 245 |
+
"md_report": md_report,
|
| 246 |
+
"cert_iframe_html": cert_iframe_html,
|
| 247 |
+
"borderline_data": borderline_data,
|
| 248 |
+
"frameworks_data": frameworks_data,
|
| 249 |
+
"fine_html": fine_html,
|
| 250 |
+
"bom_json": bom_json,
|
| 251 |
+
"sarif_json": sarif_json,
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
|
| 255 |
+
def run_assessment(doc_text: str, auditor_id: str, annual_turnover: float = 50000000.0, is_sme: bool = False):
|
| 256 |
+
if not doc_text or not doc_text.strip():
|
| 257 |
+
gr.Warning("Please select or enter a system technical specification.")
|
| 258 |
+
empty_banner = """
|
| 259 |
+
<div style="margin-top: 14px; padding: 14px 18px; border-radius: 8px; background: #fef2f2; border: 1.5px solid #fca5a5; color: #991b1b;">
|
| 260 |
+
β οΈ Please enter or select a system technical specification before running assessment.
|
| 261 |
+
</div>
|
| 262 |
+
"""
|
| 263 |
+
return (
|
| 264 |
+
gr.update(),
|
| 265 |
+
empty_banner,
|
| 266 |
+
"β οΈ Please enter model card text or select a pre-loaded sample.",
|
| 267 |
+
[],
|
| 268 |
+
[],
|
| 269 |
+
"<div style='padding:20px;text-align:center;'>No graph generated.</div>",
|
| 270 |
+
"N/A",
|
| 271 |
+
"N/A",
|
| 272 |
+
"{}",
|
| 273 |
+
"",
|
| 274 |
+
"<div>No certificate generated.</div>",
|
| 275 |
+
[],
|
| 276 |
+
[],
|
| 277 |
+
"<div style='padding:15px;'>No fine liability evaluated.</div>",
|
| 278 |
+
"{}",
|
| 279 |
+
"{}",
|
| 280 |
+
)
|
| 281 |
|
| 282 |
+
data = compute_assessment_data(doc_text, auditor_id, annual_turnover, is_sme)
|
| 283 |
+
|
| 284 |
+
status_tag = "PASSED (100%)" if data["overall_conforms"] else f"FAILED ({data['violations_count']} VIOLATIONS)"
|
| 285 |
+
gr.Info(f"Conformity Assessment: {data['system_name']} β {status_tag}. Navigating to Step 3: Run SHACL Proofs.")
|
| 286 |
+
|
| 287 |
+
if data["overall_conforms"]:
|
| 288 |
+
banner_html = f"""
|
| 289 |
+
<div style="margin-top: 14px; padding: 14px 18px; border-radius: 8px; background: #f0fdf4; border: 1.5px solid #86efac; display: flex; align-items: center; justify-content: space-between; box-shadow: 0 2px 4px rgba(0,0,0,0.04); flex-wrap: wrap; gap: 10px;">
|
| 290 |
+
<div style="display: flex; align-items: center; gap: 12px;">
|
| 291 |
+
<span style="font-size: 24px;">β
</span>
|
| 292 |
+
<div>
|
| 293 |
+
<div style="font-weight: 700; color: #166534; font-size: 15px;">Conformity Assessment Passed (100.0% Index)</div>
|
| 294 |
+
<div style="font-size: 13px; color: #15803d; margin-top: 2px;">
|
| 295 |
+
System: <strong>{data['system_name']}</strong> β All {data['passed_count']} normative requirements satisfied with zero SHACL violations.
|
| 296 |
+
</div>
|
| 297 |
+
</div>
|
| 298 |
+
</div>
|
| 299 |
+
<div style="font-size: 13px; font-weight: 600; color: #166534; background: #dcfce7; padding: 6px 12px; border-radius: 6px;">
|
| 300 |
+
β Auto-navigated to Step 3: Run SHACL Proofs
|
| 301 |
+
</div>
|
| 302 |
+
</div>
|
| 303 |
+
"""
|
| 304 |
+
else:
|
| 305 |
+
banner_html = f"""
|
| 306 |
+
<div style="margin-top: 14px; padding: 14px 18px; border-radius: 8px; background: #fef2f2; border: 1.5px solid #fca5a5; display: flex; align-items: center; justify-content: space-between; box-shadow: 0 2px 4px rgba(0,0,0,0.04); flex-wrap: wrap; gap: 10px;">
|
| 307 |
+
<div style="display: flex; align-items: center; gap: 12px;">
|
| 308 |
+
<span style="font-size: 24px;">β</span>
|
| 309 |
+
<div>
|
| 310 |
+
<div style="font-weight: 700; color: #991b1b; font-size: 15px;">Non-Conformities Detected ({data['violations_count']} SHACL Violations Found)</div>
|
| 311 |
+
<div style="font-size: 13px; color: #b91c1c; margin-top: 2px;">
|
| 312 |
+
System: <strong>{data['system_name']}</strong> β Conformity score: {data['score']:.1f}%. Immediate statutory remediation required.
|
| 313 |
+
</div>
|
| 314 |
+
</div>
|
| 315 |
+
</div>
|
| 316 |
+
<div style="font-size: 13px; font-weight: 600; color: #991b1b; background: #fee2e2; padding: 6px 12px; border-radius: 6px;">
|
| 317 |
+
β Auto-navigated to Step 3: Run SHACL Proofs
|
| 318 |
+
</div>
|
| 319 |
+
</div>
|
| 320 |
+
"""
|
| 321 |
|
| 322 |
+
return (
|
| 323 |
+
gr.update(selected="tab_run_shacl_proofs"),
|
| 324 |
+
banner_html,
|
| 325 |
+
data["exec_html"],
|
| 326 |
+
data["violations_data"],
|
| 327 |
+
data["claims_data"],
|
| 328 |
+
data["graph_iframe_html"],
|
| 329 |
+
data["cert_token"],
|
| 330 |
+
data["hash_summary"],
|
| 331 |
+
data["json_ld_cert"],
|
| 332 |
+
data["md_report"],
|
| 333 |
+
data["cert_iframe_html"],
|
| 334 |
+
data["borderline_data"],
|
| 335 |
+
data["frameworks_data"],
|
| 336 |
+
data["fine_html"],
|
| 337 |
+
data["bom_json"],
|
| 338 |
+
data["sarif_json"],
|
| 339 |
+
)
|
| 340 |
|
| 341 |
|
| 342 |
# Pre-compute live initial evaluation for default case study so dashboard opens fully populated
|
| 343 |
+
DEFAULT_ASSESSMENT = compute_assessment_data(DEFAULT_SPEC_TEXT, "lead_compliance_auditor_01", 50000000.0, False)
|
| 344 |
+
|
| 345 |
|
| 346 |
|
| 347 |
def record_triage(claim_id: str, new_status: str, new_category: str, notes: str, auditor_id: str):
|
|
|
|
| 857 |
color: #60a5fa !important;
|
| 858 |
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4) !important;
|
| 859 |
}
|
| 860 |
+
|
| 861 |
+
/* Workflow Step Navigation Buttons */
|
| 862 |
+
.tab-nav-footer {
|
| 863 |
+
margin-top: 24px !important;
|
| 864 |
+
padding-top: 16px !important;
|
| 865 |
+
border-top: 1px solid #e2e8f0 !important;
|
| 866 |
+
}
|
| 867 |
+
|
| 868 |
+
.dark .tab-nav-footer {
|
| 869 |
+
border-top-color: #334155 !important;
|
| 870 |
+
}
|
| 871 |
+
|
| 872 |
+
.tab1-nav-row {
|
| 873 |
+
margin-top: 12px !important;
|
| 874 |
+
}
|
| 875 |
"""
|
| 876 |
|
| 877 |
with gr.Blocks(title="ReguAI: Neuro-Symbolic AI GRC Engine") as demo:
|
|
|
|
| 962 |
with gr.Row():
|
| 963 |
assess_btn = gr.Button("β‘ Run Deterministic Conformity Assessment", variant="primary", size="lg")
|
| 964 |
|
| 965 |
+
status_banner_box = gr.HTML(
|
| 966 |
+
value=READY_BANNER_HTML,
|
| 967 |
+
label="Assessment Execution Status",
|
|
|
|
|
|
|
|
|
|
| 968 |
)
|
| 969 |
|
| 970 |
+
with gr.Row(elem_classes=["tab1-nav-row"]):
|
| 971 |
+
jump_to_tab2_btn = gr.Button("π Step 2: Review Grounding & Graph β", variant="secondary", size="sm")
|
| 972 |
+
jump_to_tab3_btn = gr.Button("π Step 3: View SHACL Proofs & Scorecard β", variant="secondary", size="sm")
|
| 973 |
+
jump_to_tab4_btn = gr.Button("π¦ Step 4: Export Annex IV Package β", variant="secondary", size="sm")
|
| 974 |
+
|
| 975 |
# =============================================================
|
| 976 |
# TAB 2: 2οΈβ£ Review Grounding
|
| 977 |
# =============================================================
|
| 978 |
with gr.TabItem("2οΈβ£ Review Grounding", id="tab_review_grounding"):
|
| 979 |
with gr.Tabs():
|
| 980 |
with gr.TabItem("πΈοΈ Knowledge Graph & Ontology"):
|
| 981 |
+
graph_output = gr.HTML(value=DEFAULT_ASSESSMENT["graph_iframe_html"], label="Force-Directed Knowledge Graph")
|
| 982 |
with gr.TabItem("π EUR-Lex Legal Factsheet"):
|
| 983 |
factsheet_box = gr.HTML(
|
| 984 |
value=DEFAULT_FACTSHEET,
|
|
|
|
| 988 |
claims_table = gr.Dataframe(
|
| 989 |
headers=["Claim ID", "Category", "Status", "Confidence", "Target Article", "Evidence Span"],
|
| 990 |
datatype=["str", "str", "str", "str", "str", "str"],
|
| 991 |
+
value=DEFAULT_ASSESSMENT["claims_data"],
|
| 992 |
label="Extracted Regulatory Claims",
|
| 993 |
)
|
| 994 |
with gr.TabItem("π€ Active Learning Triage Queue"):
|
|
|
|
| 996 |
borderline_table = gr.Dataframe(
|
| 997 |
headers=["Claim ID", "Category", "Status", "Confidence", "Evidence Quote"],
|
| 998 |
datatype=["str", "str", "str", "str", "str"],
|
| 999 |
+
value=DEFAULT_ASSESSMENT["borderline_data"],
|
| 1000 |
)
|
| 1001 |
with gr.Row():
|
| 1002 |
triage_claim_id = gr.Textbox(label="Claim ID", placeholder="e.g. clm_001", scale=3)
|
|
|
|
| 1011 |
frameworks_table = gr.Dataframe(
|
| 1012 |
headers=["Target Framework", "Control ID", "Control Name", "Status", "Linked AI Act Article", "Audit Guidance"],
|
| 1013 |
datatype=["str", "str", "str", "str", "str", "str"],
|
| 1014 |
+
value=DEFAULT_ASSESSMENT["frameworks_data"],
|
| 1015 |
label="Harmonized Multi-Framework Controls",
|
| 1016 |
)
|
| 1017 |
|
| 1018 |
+
with gr.Row(elem_classes=["tab-nav-footer"]):
|
| 1019 |
+
nav_tab2_to_tab1 = gr.Button("β¬
οΈ Back to Step 1: Select Scenario", variant="secondary")
|
| 1020 |
+
nav_tab2_to_tab3 = gr.Button("Continue to Step 3: Run SHACL Proofs β", variant="primary")
|
| 1021 |
+
|
| 1022 |
# =============================================================
|
| 1023 |
# TAB 3: 3οΈβ£ Run SHACL Proofs
|
| 1024 |
# =============================================================
|
| 1025 |
with gr.TabItem("3οΈβ£ Run SHACL Proofs", id="tab_run_shacl_proofs"):
|
| 1026 |
with gr.Row():
|
| 1027 |
assess_btn_tab3 = gr.Button("β‘ Re-Run Deterministic Conformity Proofs", variant="primary", size="md")
|
| 1028 |
+
exec_output = gr.HTML(value=DEFAULT_ASSESSMENT["exec_html"], label="Executive Summary")
|
| 1029 |
+
fine_liability_output = gr.HTML(value=DEFAULT_ASSESSMENT["fine_html"], label="Article 99 Fine Liability")
|
| 1030 |
violations_table = gr.Dataframe(
|
| 1031 |
headers=["Legal Article", "Normative Requirement", "Severity", "SHACL Path", "Remediation Guidance"],
|
| 1032 |
datatype=["str", "str", "str", "str", "str"],
|
| 1033 |
+
value=DEFAULT_ASSESSMENT["violations_data"],
|
| 1034 |
label="Mathematical Proof: Non-Conformities Found",
|
| 1035 |
)
|
| 1036 |
|
| 1037 |
+
with gr.Row(elem_classes=["tab-nav-footer"]):
|
| 1038 |
+
nav_tab3_to_tab2 = gr.Button("β¬
οΈ Back to Step 2: Review Grounding", variant="secondary")
|
| 1039 |
+
nav_tab3_to_tab4 = gr.Button("Continue to Step 4: Export Annex IV Package β", variant="primary")
|
| 1040 |
+
|
| 1041 |
# =============================================================
|
| 1042 |
# TAB 4: 4οΈβ£ Export Annex IV Package
|
| 1043 |
# =============================================================
|
| 1044 |
with gr.TabItem("4οΈβ£ Export Annex IV Package", id="tab_export_annex_iv"):
|
| 1045 |
with gr.Tabs():
|
| 1046 |
with gr.TabItem("π Attestation Certificate (HTML)"):
|
| 1047 |
+
cert_html_output = gr.HTML(value=DEFAULT_ASSESSMENT["cert_iframe_html"])
|
| 1048 |
with gr.TabItem("π¦ CycloneDX 1.6 AI-BOM"):
|
| 1049 |
+
bom_display = gr.Code(value=DEFAULT_ASSESSMENT["bom_json"], language="json", label="CycloneDX 1.6 Machine-Readable AI-BOM")
|
| 1050 |
with gr.TabItem("π‘οΈ OASIS SARIF 2.1.0 Report"):
|
| 1051 |
+
sarif_display = gr.Code(value=DEFAULT_ASSESSMENT["sarif_json"], language="json", label="OASIS SARIF 2.1.0 Static Analysis Report")
|
| 1052 |
with gr.TabItem("π Annex IV Report (Markdown)"):
|
| 1053 |
+
report_markdown = gr.Markdown(value=DEFAULT_ASSESSMENT["md_report"])
|
| 1054 |
with gr.TabItem("π Machine-Readable JSON-LD"):
|
| 1055 |
+
jsonld_display = gr.Code(value=DEFAULT_ASSESSMENT["json_ld_cert"], language="json", label="W3C JSON-LD Digital Certificate")
|
| 1056 |
with gr.TabItem("π W3C PROV-O Ledger"):
|
| 1057 |
+
token_display = gr.Textbox(value=DEFAULT_ASSESSMENT["cert_token"], label="Official Digital Conformity Token", interactive=False)
|
| 1058 |
+
ledger_display = gr.Markdown(value=DEFAULT_ASSESSMENT["hash_summary"])
|
| 1059 |
+
|
| 1060 |
+
with gr.Row(elem_classes=["tab-nav-footer"]):
|
| 1061 |
+
nav_tab4_to_tab3 = gr.Button("β¬
οΈ Back to Step 3: Review Proofs", variant="secondary")
|
| 1062 |
+
nav_tab4_to_tab1 = gr.Button("π Start New Scenario (Step 1)", variant="secondary")
|
| 1063 |
|
| 1064 |
+
# Assessment outputs list (16 components: main_tabs + status_banner_box + 14 metrics/deliverables)
|
| 1065 |
assessment_inputs = [spec_input, auditor_input, turnover_input, is_sme_input]
|
| 1066 |
assessment_outputs = [
|
| 1067 |
+
main_tabs,
|
| 1068 |
+
status_banner_box,
|
| 1069 |
exec_output,
|
| 1070 |
violations_table,
|
| 1071 |
claims_table,
|
|
|
|
| 1086 |
domain_dropdown.change(
|
| 1087 |
fn=on_domain_change,
|
| 1088 |
inputs=[domain_dropdown],
|
| 1089 |
+
outputs=[case_dropdown, spec_input, quick_bar_box, factsheet_box, status_banner_box],
|
| 1090 |
)
|
| 1091 |
case_dropdown.change(
|
| 1092 |
fn=on_case_change,
|
| 1093 |
inputs=[case_dropdown],
|
| 1094 |
+
outputs=[spec_input, quick_bar_box, factsheet_box, status_banner_box],
|
| 1095 |
)
|
| 1096 |
|
| 1097 |
# Wire assessment buttons (Tab 1 primary and Tab 3 re-run)
|
|
|
|
| 1106 |
outputs=assessment_outputs,
|
| 1107 |
)
|
| 1108 |
|
| 1109 |
+
# Workflow step navigation clicks
|
| 1110 |
+
jump_to_tab2_btn.click(fn=lambda: gr.update(selected="tab_review_grounding"), outputs=[main_tabs])
|
| 1111 |
+
jump_to_tab3_btn.click(fn=lambda: gr.update(selected="tab_run_shacl_proofs"), outputs=[main_tabs])
|
| 1112 |
+
jump_to_tab4_btn.click(fn=lambda: gr.update(selected="tab_export_annex_iv"), outputs=[main_tabs])
|
| 1113 |
+
|
| 1114 |
+
nav_tab2_to_tab1.click(fn=lambda: gr.update(selected="tab_select_scenario"), outputs=[main_tabs])
|
| 1115 |
+
nav_tab2_to_tab3.click(fn=lambda: gr.update(selected="tab_run_shacl_proofs"), outputs=[main_tabs])
|
| 1116 |
+
|
| 1117 |
+
nav_tab3_to_tab2.click(fn=lambda: gr.update(selected="tab_review_grounding"), outputs=[main_tabs])
|
| 1118 |
+
nav_tab3_to_tab4.click(fn=lambda: gr.update(selected="tab_export_annex_iv"), outputs=[main_tabs])
|
| 1119 |
+
|
| 1120 |
+
nav_tab4_to_tab3.click(fn=lambda: gr.update(selected="tab_run_shacl_proofs"), outputs=[main_tabs])
|
| 1121 |
+
nav_tab4_to_tab1.click(fn=lambda: gr.update(selected="tab_select_scenario"), outputs=[main_tabs])
|
| 1122 |
+
|
| 1123 |
triage_btn.click(
|
| 1124 |
fn=record_triage,
|
| 1125 |
inputs=[triage_claim_id, triage_status, triage_cat, triage_notes, auditor_input],
|
|
|
|
| 1129 |
if __name__ == "__main__":
|
| 1130 |
demo.launch(server_name="0.0.0.0", server_port=7860, share=False, theme=gr.themes.Soft(), css=CUSTOM_CSS)
|
| 1131 |
|
| 1132 |
+
|
data/active_learning_triplets.jsonl
CHANGED
|
@@ -21,3 +21,4 @@
|
|
| 21 |
{"timestamp": "2026-09-21T14:47:50.118531+00:00", "auditor_id": "compliance_lead_01", "claim_id": "clm_test_99", "anchor_text": "Verified operational override in production dashboard.", "positive_label": "HUMAN_OVERSIGHT", "negative_label": "IRRELEVANT_TEXT", "verified_assertion_status": "IMPLEMENTED", "auditor_notes": "Verified operational override in production dashboard."}
|
| 22 |
{"timestamp": "2026-09-21T14:48:40.951594+00:00", "auditor_id": "compliance_lead_01", "claim_id": "clm_test_99", "anchor_text": "Verified operational override in production dashboard.", "positive_label": "HUMAN_OVERSIGHT", "negative_label": "IRRELEVANT_TEXT", "verified_assertion_status": "IMPLEMENTED", "auditor_notes": "Verified operational override in production dashboard."}
|
| 23 |
{"timestamp": "2026-09-21T15:05:44.655438+00:00", "auditor_id": "compliance_lead_01", "claim_id": "clm_test_99", "anchor_text": "Verified operational override in production dashboard.", "positive_label": "HUMAN_OVERSIGHT", "negative_label": "IRRELEVANT_TEXT", "verified_assertion_status": "IMPLEMENTED", "auditor_notes": "Verified operational override in production dashboard."}
|
|
|
|
|
|
| 21 |
{"timestamp": "2026-09-21T14:47:50.118531+00:00", "auditor_id": "compliance_lead_01", "claim_id": "clm_test_99", "anchor_text": "Verified operational override in production dashboard.", "positive_label": "HUMAN_OVERSIGHT", "negative_label": "IRRELEVANT_TEXT", "verified_assertion_status": "IMPLEMENTED", "auditor_notes": "Verified operational override in production dashboard."}
|
| 22 |
{"timestamp": "2026-09-21T14:48:40.951594+00:00", "auditor_id": "compliance_lead_01", "claim_id": "clm_test_99", "anchor_text": "Verified operational override in production dashboard.", "positive_label": "HUMAN_OVERSIGHT", "negative_label": "IRRELEVANT_TEXT", "verified_assertion_status": "IMPLEMENTED", "auditor_notes": "Verified operational override in production dashboard."}
|
| 23 |
{"timestamp": "2026-09-21T15:05:44.655438+00:00", "auditor_id": "compliance_lead_01", "claim_id": "clm_test_99", "anchor_text": "Verified operational override in production dashboard.", "positive_label": "HUMAN_OVERSIGHT", "negative_label": "IRRELEVANT_TEXT", "verified_assertion_status": "IMPLEMENTED", "auditor_notes": "Verified operational override in production dashboard."}
|
| 24 |
+
{"timestamp": "2026-09-21T16:35:03.024528+00:00", "auditor_id": "compliance_lead_01", "claim_id": "clm_test_99", "anchor_text": "Verified operational override in production dashboard.", "positive_label": "HUMAN_OVERSIGHT", "negative_label": "IRRELEVANT_TEXT", "verified_assertion_status": "IMPLEMENTED", "auditor_notes": "Verified operational override in production dashboard."}
|