Spaces:
Running
Running
| 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() |