import os import re import textwrap import gradio as gr ENGAGEMENT_EMAIL = os.getenv("ENGAGEMENT_EMAIL", "engagement@bpm.ba") GOVERNANCE_EMAIL = os.getenv("GOVERNANCE_EMAIL", "governance@bpm.ba") # Optional gating (comma-separated) ACCESS_CODES_RAW = os.getenv("FINC2E_ACCESS_CODES", "").strip() ACCESS_CODES = {c.strip() for c in ACCESS_CODES_RAW.split(",") if c.strip()} TITLE = "FinC2E — Governance Gateway" TAGLINE = "Structured, audit-oriented reasoning for regulated decision support (preview)." DISCLAIMER = ( "FinC2E outputs are informational and non-executive. " "No legal advice, financial advice, trading signals, or decision execution is provided. " "Users remain responsible for compliance, policies, and final decisions." ) RISK_DOMAINS = [ "AML / CFT", "KYC / CDD", "Sanctions screening", "Fraud / transaction anomaly", "Market conduct / suitability", "Operational risk", "Model risk / AI governance", "Data privacy / security", "Other (specify)" ] def normalize(s: str) -> str: return (s or "").strip() def valid_access(code: str) -> bool: if not ACCESS_CODES: return True # no gate return normalize(code) in ACCESS_CODES def ensure_min_len(field: str, n: int, label: str): if len(normalize(field)) < n: return f"[Missing/too short] {label} (min {n} chars)" return None def finc2e_reason(case_id, jurisdiction, domain, other_domain, objective, facts, constraints, decision_stage, access_code): # Gate if not valid_access(access_code): return ( "ACCESS DENIED\n" "------------\n" "This preview requires a valid FinC2E access code.\n" f"Request access: {ENGAGEMENT_EMAIL}\n" ) domain_final = other_domain if domain == "Other (specify)" else domain domain_final = normalize(domain_final) or domain # Basic validation issues = [] for (val, n, label) in [ (objective, 25, "Objective"), (facts, 40, "Case Facts / Data"), (constraints, 20, "Constraints / Policies"), (jurisdiction, 2, "Jurisdiction"), ]: m = ensure_min_len(val, n, label) if m: issues.append(m) if issues: return "INPUT QUALITY WARNING\n---------------------\n" + "\n".join(f"- {i}" for i in issues) + "\n\nProvide clearer inputs for higher analytical value." # Produce a structured, audit-oriented "preview" (still non-executive) out = f""" FINC2E GOVERNANCE OUTPUT (PREVIEW) — NON-EXECUTIVE ================================================= Case Header ----------- - Case ID: {normalize(case_id) or "N/A"} - Jurisdiction: {normalize(jurisdiction)} - Domain: {domain_final} - Decision Stage: {normalize(decision_stage) or "N/A"} 1) Decision Objective (What must be decided?) -------------------------------------------- {normalize(objective)} 2) Known Facts / Inputs (What is observed?) ------------------------------------------ {normalize(facts)} 3) Constraints & Policies (What must be respected?) -------------------------------------------------- {normalize(constraints)} 4) Risk Framing (What can go wrong?) ----------------------------------- - Primary risk vectors (domain-dependent) - Irreversibility / tail risk considerations - Compliance exposure and evidentiary needs 5) Minimum Evidence Checklist (Audit-oriented) --------------------------------------------- - Identity / entity attributes sufficient for the domain - Source of funds / source of wealth (if relevant) - Transaction narrative coherence (if relevant) - Sanctions/PEP screening artifacts (if relevant) - Data provenance + timestamps + responsible reviewer 6) Structured Questions (What must be clarified next?) ----------------------------------------------------- - What assumptions are being made due to missing data? - Which policy threshold(s) apply in this jurisdiction? - What would change the classification outcome? - What is the acceptable false-positive / false-negative posture? 7) Suggested Review Path (Human-in-the-loop) ------------------------------------------- - Step A: Normalize inputs and verify provenance - Step B: Apply policy thresholds to classify risk band - Step C: Collect missing evidence (if required) - Step D: Document rationale and reviewer accountability - Step E: Escalate if any high-severity triggers are present Governance Notes ---------------- - Output is informational; no decision is executed. - For production pilots: policy binding, audit logs, and access controls are mandatory. DISCLAIMER ---------- {DISCLAIMER} © 2026 BPM RED Academy — All rights reserved. """ return textwrap.dedent(out).strip() with gr.Blocks(theme=gr.themes.Soft(), title=TITLE) as demo: gr.Markdown( f""" # {TITLE} **{TAGLINE}** ⚠️ **Preview only.** {DISCLAIMER} --- """ ) # Optional gate UI if ACCESS_CODES: access_code = gr.Textbox(label="FinC2E Access Code (Licensed)", placeholder="e.g., FINC2E-CLIENT-001") else: access_code = gr.Textbox(label="FinC2E Access Code (optional)", placeholder="(optional)") with gr.Row(): case_id = gr.Textbox(label="Case ID (optional)", placeholder="e.g., CASE-2026-0007") jurisdiction = gr.Textbox(label="Jurisdiction", placeholder="e.g., EU / UK / BiH / UAE") with gr.Row(): domain = gr.Dropdown(label="Domain", choices=RISK_DOMAINS, value="AML / CFT") other_domain = gr.Textbox(label="If Other, specify domain", placeholder="e.g., Payments monitoring, UBO risk, etc.") decision_stage = gr.Textbox(label="Decision stage (optional)", placeholder="e.g., onboarding / ongoing monitoring / escalation review") objective = gr.Textbox( label="Decision objective", lines=2, placeholder="Describe the decision to be supported (not executed). Example: classify risk level and define what evidence is required for review." ) facts = gr.Textbox( label="Case facts / inputs", lines=6, placeholder="Provide relevant facts only (no unnecessary personal data). Include amounts, timelines, entity types, triggers, and what is already verified." ) constraints = gr.Textbox( label="Constraints / policies", lines=4, placeholder="List policy constraints: thresholds, prohibited conditions, escalation triggers, documentation requirements, and any jurisdictional rules you must respect." ) run = gr.Button("Generate Governance Output (Preview)", variant="primary") output = gr.Textbox(label="FinC2E Output (Non-Executive)", lines=20) run.click( finc2e_reason, inputs=[case_id, jurisdiction, domain, other_domain, objective, facts, constraints, decision_stage, access_code], outputs=[output] ) gr.Markdown( f""" --- ### Request access / enterprise pilots - Engagement: **{ENGAGEMENT_EMAIL}** - Governance: **{GOVERNANCE_EMAIL}** © 2026 BPM RED Academy — All rights reserved. """ ) if __name__ == "__main__": demo.launch()