File size: 7,537 Bytes
df32ee4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4f43ed0
 
 
df32ee4
4f43ed0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
df32ee4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# https://chatgpt.com/c/692f03df-88e8-8326-9e22-230b32dd2194

import gradio as gr

# -----------------------------
# Compliance configuration
# -----------------------------
compliance_areas = {
    "Data Privacy": {
        "description": "Ensure protection of personal user data (GDPR, HIPAA).",
        "checks": ["User data is anonymized", "Consent collected"],
    },
    "Explainability": {
        "description": "Ensure outputs are interpretable to humans (EU AI Act, IEEE guidelines).",
        "checks": ["Model outputs are interpretable", "Feature importance is logged"],
    },
    "Auditability": {
        "description": "Ensure chatbot decisions can be traced (NIST AI RMF).",
        "checks": ["All conversations are stored securely", "Logs include timestamps and user IDs"],
    },
    "Fairness": {
        "description": "Ensure equal treatment and inclusive language (AI Fairness 360, IBM).",
        "checks": ["Bias metrics are checked monthly", "Language models use inclusive vocabulary"],
    },
}


def build_compliance_instructions() -> str:
    """
    Build a textual description of the compliance areas to include in the prompt.
    """
    lines = []
    for area_name, area_data in compliance_areas.items():
        lines.append(f"{area_name}:")
        lines.append(f"- Description: {area_data['description']}")
        lines.append("- Checks:")
        for check in area_data["checks"]:
            lines.append(f"  - {check}")
        lines.append("")  # blank line between areas
    return "\n".join(lines)


def analyze_compliance(api_key: str, free_text: str, uploaded_file) -> str:
    """
    Use OpenAI to analyze the given text against the compliance_areas and
    return a markdown report.
    """
    # Basic validation
    if not api_key or api_key.strip() == "":
        return "⚠️ **Error:** Please enter your OpenAI API key."

    # Resolve text input source: uploaded file has priority if present
    text_to_check = ""
    if uploaded_file is not None:
        try:
            file_path = getattr(uploaded_file, "name", None)
            if file_path:
                with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
                    text_to_check = f.read()
            else:
                if hasattr(uploaded_file, "read"):
                    raw_bytes = uploaded_file.read()
                    if isinstance(raw_bytes, bytes):
                        text_to_check = raw_bytes.decode("utf-8", errors="ignore")
                    else:
                        text_to_check = str(raw_bytes)
        except Exception as e:
            return f"⚠️ **Error reading uploaded file:** {e}"

    # If no file content, use the textbox content
    if not text_to_check.strip():
        if not free_text or free_text.strip() == "":
            return "⚠️ **Error:** Please type some text or upload a .txt file to analyze."
        text_to_check = free_text

    # Build the prompt
    compliance_description = build_compliance_instructions()

    system_message = (
        "You are an AI compliance auditor for financial AI systems. "
        "You strictly evaluate text against the given compliance areas. "
        "You return clear, structured markdown that a non-technical user "
        "can read in a dashboard."
    )

    user_message = (
        "You are given the following text from a finance-related AI system.\n\n"
        "---- TEXT START ----\n"
        f"{text_to_check}\n"
        "---- TEXT END ----\n\n"
        "Evaluate the text against these compliance areas and checks:\n\n"
        f"{compliance_description}\n"
        "For EACH compliance area (Data Privacy, Explainability, Auditability, Fairness):\n"
        "1. State **Overall: PASSED** or **Overall: FAILED** for that area.\n"
        "2. For each individual check, state:\n"
        "   - **Check:** <check name>\n"
        "   - **Status:** PASSED or FAILED\n"
        "   - **Reason:** One short sentence quoting or paraphrasing the text.\n"
        "3. If the area FAILED, add a short '**Risk:**' line explaining why this matters "
        "for a financial institution.\n\n"
        "Return the result in markdown format with headings:\n"
        "## Data Privacy\n"
        "## Explainability\n"
        "## Auditability\n"
        "## Fairness\n"
    )

    # -----------------------------
    # OpenAI call with robust import handling
    # -----------------------------
    try:
        # Try new SDK style first (openai>=1.x)
        try:
            from openai import OpenAI  # type: ignore
            client = OpenAI(api_key=api_key)

            response = client.chat.completions.create(
                model="gpt-4.1-mini",  # change to "gpt-4.1" if desired
                messages=[
                    {"role": "system", "content": system_message},
                    {"role": "user", "content": user_message},
                ],
                temperature=0.1,
            )
            result = response.choices[0].message.content
            return result

        except ModuleNotFoundError:
            # Fallback: older SDK style `import openai`
            try:
                import openai  # type: ignore

                openai.api_key = api_key
                response = openai.ChatCompletion.create(
                    model="gpt-4.1-mini",
                    messages=[
                        {"role": "system", "content": system_message},
                        {"role": "user", "content": user_message},
                    ],
                    temperature=0.1,
                )
                result = response["choices"][0]["message"]["content"]
                return result
            except ModuleNotFoundError:
                return (
                    "⚠️ **OpenAI library not installed.**\n\n"
                    "Please add `openai` to your `requirements.txt`:\n\n"
                    "```text\nopenai>=1.0.0\n```\n"
                    "and rebuild/restart the app."
                )

    except Exception as e:
        return f"⚠️ **OpenAI API Error:** {e}"


# -----------------------------
# Gradio UI
# -----------------------------
def build_interface():
    with gr.Blocks(title="AI Compliance Checker (Finance)") as demo:
        gr.Markdown(
            """
# 🧭 AI Compliance Checker (Finance)

Paste or upload a **finance-related AI system description** and check it against:

- **Data Privacy**
- **Explainability**
- **Auditability**
- **Fairness**

The app uses the **OpenAI API** to generate a structured compliance report.
"""
        )

        with gr.Group():
            api_key = gr.Textbox(
                label="OpenAI API Key",
                placeholder="sk-...",
                type="password",
            )

        with gr.Row():
            text_input = gr.Textbox(
                label="Text to Analyze (you can type or paste here)",
                lines=12,
                placeholder="Paste your AI policy, system description, or output here...",
            )
            file_input = gr.File(
                label="Or Upload a .txt Document",
                file_types=["text"],
            )

        output = gr.Markdown(label="Compliance Report")

        run_button = gr.Button("🔍 Run Compliance Check")

        run_button.click(
            fn=analyze_compliance,
            inputs=[api_key, text_input, file_input],
            outputs=output,
        )

    return demo


if __name__ == "__main__":
    demo = build_interface()
    demo.launch()