soupstick commited on
Commit
2132a21
Β·
1 Parent(s): 2382167

Enhanced fraud detection platform with KYC, Sanctions, and Credit Risk modules

Browse files

- Added KYC identity fraud detection with duplicate/synthetic account detection
- Implemented sanctions screening with fuzzy name matching capabilities
- Integrated credit risk assessment with multi-factor scoring
- Enhanced AI consultant chatbot with fraud domain expertise
- Improved UI/UX with professional theme and statistics panels
- Added comprehensive error handling and data validation
- Updated requirements and documentation

Files changed (1) hide show
  1. app.py +503 -564
app.py CHANGED
@@ -1,609 +1,548 @@
1
- import os
2
- import json
3
- from datetime import datetime
4
- import pandas as pd
5
  import gradio as gr
 
 
 
 
6
  from huggingface_hub import InferenceClient
 
 
 
 
 
 
 
 
7
 
8
- # local modules
9
- from database import DatabaseManager
10
- from models import ModelRouter
11
- from velocity import detect_burst
12
- from export_utils import generate_csv_report, generate_pdf_report
13
- from alerts import EmailNotifier, SlackNotifier
14
- from rag import RAGStore
15
-
16
- # =========================
17
- # Config from environment
18
- # =========================
19
- HF_TOKEN = os.getenv("HF_TOKEN", "")
20
- ADMIN_EMAIL = os.getenv("ADMIN_EMAIL", "")
21
- SLACK_WEBHOOK = os.getenv("SLACK_WEBHOOK", "")
22
-
23
- # =========================
24
- # Data dirs (writeable in HF Spaces)
25
- # =========================
26
- DATA_DIR = os.path.join(os.getcwd(), "app_data")
27
- os.makedirs(DATA_DIR, exist_ok=True)
28
-
29
- DB_PATH = os.path.join(DATA_DIR, "fraud_detector.db")
30
- VECTOR_DIR = os.path.join(DATA_DIR, "chroma_collection")
31
-
32
- # =========================
33
- # Utils / Logging
34
- # =========================
35
- def _log_ex(prefix: str, e: Exception):
36
- import traceback
37
- print(f"{prefix}: {e}")
38
- print(traceback.format_exc())
39
-
40
- # =========================
41
- # Initialize components
42
- # =========================
43
- try:
44
- db = DatabaseManager(DB_PATH)
45
- print("βœ… Database initialized")
46
- except Exception as e:
47
- _log_ex("Warning: Database initialization failed", e)
48
- db = None
49
-
50
- try:
51
- rag = RAGStore(collection_dir=VECTOR_DIR)
52
- print("βœ… RAG store initialized")
53
- except Exception as e:
54
- _log_ex("Warning: RAG store initialization failed", e)
55
- rag = None
56
-
57
- try:
58
- model_router = ModelRouter(hf_token=HF_TOKEN)
59
- print("βœ… Model router initialized")
60
- except Exception as e:
61
- _log_ex("Warning: Model router initialization failed", e)
62
- model_router = None
63
-
64
- try:
65
- emailer = EmailNotifier()
66
- print("βœ… Email notifier ready")
67
- except Exception as e:
68
- _log_ex("Warning: Email notifier initialization failed", e)
69
- emailer = None
70
-
71
- try:
72
- slacknot = SlackNotifier(SLACK_WEBHOOK) if SLACK_WEBHOOK else None
73
- if slacknot:
74
- print("βœ… Slack notifier ready")
75
- except Exception as e:
76
- _log_ex("Warning: Slack notifier initialization failed", e)
77
- slacknot = None
78
-
79
- # simple in-memory session (for demo only)
80
- SESSIONS = {}
81
-
82
- # =========================
83
- # LLM Clients (Text Generation)
84
- # =========================
85
  DEFAULT_MODEL_ID = "mistralai/Mistral-7B-Instruct-v0.2"
86
  QWEN_MODEL_ID = "Qwen/Qwen2.5-Coder-32B-Instruct"
87
 
88
- client_ministral = None
89
- client_qwen = None
90
-
91
- if HF_TOKEN:
 
92
  try:
93
- client_ministral = InferenceClient(model=DEFAULT_MODEL_ID, token=HF_TOKEN, timeout=60)
94
- print(f"βœ… Initialized HF client: {DEFAULT_MODEL_ID}")
 
 
 
 
 
 
95
  except Exception as e:
96
- _log_ex(f"⚠️ Failed to initialize {DEFAULT_MODEL_ID}", e)
97
-
 
 
 
 
 
 
 
 
 
 
 
98
  try:
99
- client_qwen = InferenceClient(model=QWEN_MODEL_ID, token=HF_TOKEN, timeout=60)
100
- print(f"βœ… Initialized HF client: {QWEN_MODEL_ID}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  except Exception as e:
102
- _log_ex(f"⚠️ Failed to initialize {QWEN_MODEL_ID}", e)
103
- else:
104
- print("⚠️ No HF_TOKEN provided - chatbot functionality will be limited")
105
-
106
- # FIXED: Match the model labels with what's used in the Gradio interface
107
- MODEL_LABELS = {
108
- "Mistral-7B-Instruct-v0.2": {"id": DEFAULT_MODEL_ID, "client": lambda: client_ministral},
109
- "Qwen2.5-Coder-32B-Instruct": {"id": QWEN_MODEL_ID, "client": lambda: client_qwen},
110
- }
111
-
112
- # System prompt for chat completion
113
- SYSTEM_PROMPT = "You are a helpful fraud detection analyst AI. Be concise, clear, and practical."
114
-
115
- def build_messages(user_msg: str) -> list:
116
- # Chat completion format with system and user messages
117
- return [
118
- {"role": "system", "content": SYSTEM_PROMPT},
119
- {"role": "user", "content": user_msg}
120
- ]
121
-
122
- # =========================
123
- # Fraud Detector Functions
124
- # =========================
125
- def register_user(username: str, password: str, email: str):
126
- if not db:
127
- return "Database not available"
128
- if not username or not password:
129
- return "Username and password required"
130
-
131
- print(f"πŸ”„ Attempting to register user: '{username}'")
132
  try:
133
- ok = db.create_user(username=username, password=password, email=email)
134
- if ok:
135
- # Debug: List all users after registration
136
- db.list_users()
137
- return "βœ… Registered successfully"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  else:
139
- return "❌ Registration failed (username taken or database error)"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  except Exception as e:
141
- print(f"Registration error: {e}")
142
- return f"❌ Registration error: {e}"
143
-
144
- def login_user(username: str, password: str):
145
- if not db:
146
- return "", "Database not available"
147
- if not username or not password:
148
- return "", "Username and password required"
149
-
150
- print(f"πŸ”„ Attempting to login user: '{username}'")
151
  try:
152
- # Debug: List all users before authentication
153
- db.list_users()
154
-
155
- user_id = db.authenticate_user(username, password)
156
- if user_id:
157
- token = f"session-{user_id}-{int(datetime.utcnow().timestamp())}"
158
- SESSIONS[token] = user_id
159
- print(f"βœ… Login successful. Token: {token}")
160
- return token, f"βœ… Logged in as {username} (ID: {user_id})"
 
 
 
 
 
161
  else:
162
- return "", "❌ Invalid credentials - please check username and password"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  except Exception as e:
164
- print(f"Login error: {e}")
165
- return "", f"❌ Login error: {e}"
166
 
167
- def debug_database():
168
- """Debug function to check database state"""
169
- if not db:
170
- return "Database not available"
171
-
172
  try:
173
- users = db.list_users()
174
- return f"Found {len(users)} users in database"
175
- except Exception as e:
176
- return f"Error checking database: {e}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
 
178
- def process_file(session_token: str, file, rag_query: str = ""):
179
- if session_token not in SESSIONS:
180
- return "❌ Please log in first", {}, {}, ""
181
- if not file:
182
- return "❌ Please upload a CSV file", {}, {}, ""
183
 
184
- user_id = SESSIONS[session_token]
185
- print(f"πŸ”„ Processing file for user {user_id}")
186
 
187
- try:
188
- # Read CSV file
189
- try:
190
- df = pd.read_csv(file.name)
191
- print(f"βœ… CSV loaded successfully: {len(df)} rows, {len(df.columns)} columns")
192
- except Exception as e:
193
- print(f"❌ CSV reading failed: {e}")
194
- return f"❌ Error reading CSV: {e}", {}, {}, ""
195
-
196
- # Validate required columns
197
- required_cols = {"transaction_id", "timestamp", "amount", "description", "merchant"}
198
- missing_cols = required_cols - set(df.columns)
199
- if missing_cols:
200
- return f"❌ CSV missing required columns: {missing_cols}", {}, {}, ""
201
-
202
- print(f"βœ… CSV validation passed")
203
-
204
- # Store transactions in database
205
- if db:
206
- try:
207
- db.store_transactions(user_id, df)
208
- print("βœ… Transactions stored in database")
209
- except Exception as e:
210
- _log_ex("Storing transactions failed", e)
211
 
212
- # Add to RAG store
213
- if rag:
214
- try:
215
- texts = df.apply(
216
- lambda x: f"txn_id:{x['transaction_id']} amount:{x['amount']} merchant:{x['merchant']} desc:{x.get('description','')}",
217
- axis=1
218
- ).tolist()
219
- metadatas = df.to_dict(orient="records")
220
- rag.add(texts=texts, metadatas=metadatas)
221
- print("βœ… Transactions added to RAG store")
222
- except Exception as e:
223
- _log_ex("RAG add failed", e)
224
-
225
- # Velocity detection
226
- velocity_flags = []
227
- try:
228
- velocity_flags = detect_burst(df, window_minutes=10, threshold=5)
229
- print(f"βœ… Velocity detection completed: {len(velocity_flags)} flags")
230
- except Exception as e:
231
- _log_ex("Velocity detection failed", e)
232
-
233
- # Amount anomaly detection
234
- amount_flags = []
235
- try:
236
- if len(df) > 5:
237
- mean = df['amount'].mean()
238
- std = df['amount'].std()
239
- for _, row in df.iterrows():
240
- z = abs((row['amount'] - mean) / (std if std > 0 else 1))
241
- if z > 2.5 or row['amount'] > 1000:
242
- amount_flags.append({
243
- "transaction": row.to_dict(),
244
- "z_score": float(z),
245
- "risk_factor": "amount_anomaly",
246
- "risk_score": float(min(z/3.0, 1.0))
247
- })
248
- print(f"βœ… Amount anomaly detection completed: {len(amount_flags)} flags")
249
- except Exception as e:
250
- _log_ex("Amount anomaly detection failed", e)
251
-
252
- all_flagged = velocity_flags + amount_flags
253
- print(f"πŸ“Š Total flagged transactions: {len(all_flagged)}")
254
-
255
- # RAG query processing
256
- rag_results = []
257
- if rag_query and rag:
258
- try:
259
- rag_results = rag.query(rag_query, k=5)
260
- print(f"βœ… RAG query completed: {len(rag_results)} results")
261
- except Exception as e:
262
- _log_ex("RAG query failed", e)
263
-
264
- # AI Analysis with multiple fallbacks
265
- context = {
266
- "num_transactions": len(df),
267
- "velocity_flags": len(velocity_flags),
268
- "amount_flags": len(amount_flags),
269
- "rag_snippets": rag_results[:3]
270
- }
271
-
272
- # Generate AI analysis with multiple fallback options
273
- llm_output = None
274
-
275
- # Try 1: Model router
276
- if model_router:
277
- try:
278
- prompt = f"""
279
- Analyze these fraud detection results and provide a concise risk assessment:
280
-
281
- Transaction Summary:
282
- - Total transactions: {len(df)}
283
- - Velocity anomalies: {len(velocity_flags)} transactions
284
- - Amount anomalies: {len(amount_flags)} transactions
285
- - Total flagged: {len(all_flagged)} transactions
286
-
287
- Please provide:
288
- 1. Risk level (LOW/MEDIUM/HIGH)
289
- 2. Key findings
290
- 3. Recommended actions
291
- 4. Next steps
292
-
293
- Keep response under 300 words.
294
- """
295
- llm_output = model_router.run(prompt, task="analysis")
296
- if llm_output and not llm_output.startswith("Error"):
297
- print("βœ… Model router analysis successful")
298
- else:
299
- print(f"⚠️ Model router returned error: {llm_output}")
300
- llm_output = None
301
- except Exception as e:
302
- _log_ex("Model router run failed", e)
303
- llm_output = None
304
-
305
- # Try 2: Direct HF client (Mistral)
306
- if llm_output is None and client_ministral:
307
- try:
308
- messages = build_messages(f"""
309
- Analyze these fraud detection results:
310
- - Total transactions: {len(df)}
311
- - Velocity anomalies: {len(velocity_flags)}
312
- - Amount anomalies: {len(amount_flags)}
313
- - Total flagged: {len(all_flagged)}
314
-
315
- Provide a brief risk assessment and recommend next steps. Keep it under 250 words.
316
- """)
317
- response = client_ministral.chat_completion(
318
- messages=messages,
319
- max_tokens=300,
320
- temperature=0.3
321
- )
322
-
323
- # Extract response text
324
- if hasattr(response, 'choices') and len(response.choices) > 0:
325
- llm_output = response.choices[0].message.content
326
- elif isinstance(response, dict) and 'choices' in response:
327
- llm_output = response['choices'][0]['message']['content']
328
- else:
329
- llm_output = str(response)
330
-
331
- if llm_output and llm_output.strip():
332
- print("βœ… Direct HF client (Mistral) analysis successful")
333
- else:
334
- llm_output = None
335
-
336
- except Exception as e:
337
- _log_ex("Direct HF client (Mistral) analysis failed", e)
338
- llm_output = None
339
-
340
- # Try 3: Direct HF client (Qwen)
341
- if llm_output is None and client_qwen:
342
- try:
343
- messages = build_messages(f"""
344
- Fraud Analysis Request:
345
- Transactions: {len(df)} total, {len(all_flagged)} flagged
346
- Velocity alerts: {len(velocity_flags)}
347
- Amount alerts: {len(amount_flags)}
348
-
349
- Provide risk assessment and recommendations.
350
- """)
351
- response = client_qwen.chat_completion(
352
- messages=messages,
353
- max_tokens=250,
354
- temperature=0.3
355
- )
356
-
357
- # Extract response text
358
- if hasattr(response, 'choices') and len(response.choices) > 0:
359
- llm_output = response.choices[0].message.content
360
- elif isinstance(response, dict) and 'choices' in response:
361
- llm_output = response['choices'][0]['message']['content']
362
- else:
363
- llm_output = str(response)
364
-
365
- if llm_output and llm_output.strip():
366
- print("βœ… Direct HF client (Qwen) analysis successful")
367
- else:
368
- llm_output = None
369
-
370
- except Exception as e:
371
- _log_ex("Direct HF client (Qwen) analysis failed", e)
372
- llm_output = None
373
-
374
- # Final fallback: Structured report
375
- if llm_output is None:
376
- print("⚠️ All AI models failed, using structured fallback")
377
- risk_level = "HIGH" if len(all_flagged) > 5 else "MEDIUM" if len(all_flagged) > 0 else "LOW"
378
- risk_color = "πŸ”΄" if risk_level == "HIGH" else "🟑" if risk_level == "MEDIUM" else "🟒"
379
-
380
- llm_output = f"""
381
- πŸ” FRAUD ANALYSIS REPORT
382
-
383
- πŸ“Š **Transaction Summary:**
384
- β€’ Total transactions processed: {len(df):,}
385
- β€’ Suspicious transactions flagged: {len(all_flagged)}
386
- β€’ Velocity pattern anomalies: {len(velocity_flags)}
387
- β€’ Amount-based anomalies: {len(amount_flags)}
388
-
389
- {risk_color} **Risk Assessment: {risk_level} RISK**
390
-
391
- 🎯 **Key Findings:**
392
- {"β€’ Multiple fraud indicators detected across transactions" if len(all_flagged) > 3 else "β€’ Some suspicious patterns identified" if len(all_flagged) > 0 else "β€’ No major anomalies detected in transaction patterns"}
393
- {"β€’ High velocity transactions may indicate automated attacks" if len(velocity_flags) > 2 else ""}
394
- {"β€’ Unusual amount patterns suggest potential fraud" if len(amount_flags) > 2 else ""}
395
-
396
- πŸ“‹ **Recommended Actions:**
397
- {"β€’ IMMEDIATE: Manual review of all flagged transactions required" if len(all_flagged) > 3 else "β€’ Review flagged transactions within 24 hours" if len(all_flagged) > 0 else "β€’ Continue standard monitoring procedures"}
398
- {"β€’ URGENT: Consider temporarily blocking high-risk accounts" if len(all_flagged) > 5 else ""}
399
- {"β€’ Notify compliance team and document findings" if len(all_flagged) > 1 else ""}
400
- β€’ Download detailed reports for compliance records
401
- β€’ {"Set up enhanced monitoring for similar patterns" if len(all_flagged) > 0 else "Maintain current monitoring protocols"}
402
-
403
- πŸ”„ **Next Steps:**
404
- 1. Review detailed flagged transactions in the results below
405
- 2. Cross-reference with historical fraud cases
406
- 3. {"Implement additional transaction controls" if len(all_flagged) > 2 else "Continue monitoring"}
407
- 4. Schedule follow-up analysis in 24-48 hours
408
- """
409
-
410
- # Generate reports
411
- report_paths = {}
412
- if all_flagged:
413
- try:
414
- ts = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
415
- csv_path = os.path.join(DATA_DIR, f"fraud_report_{user_id}_{ts}.csv")
416
- pdf_path = os.path.join(DATA_DIR, f"fraud_report_{user_id}_{ts}.pdf")
417
-
418
- generate_csv_report(all_flagged, csv_path)
419
- generate_pdf_report(all_flagged, pdf_path)
420
- report_paths = {"csv": csv_path, "pdf": pdf_path}
421
- print(f"βœ… Reports generated: CSV and PDF")
422
- except Exception as e:
423
- _log_ex("Report generation failed", e)
424
-
425
- # Send alerts
426
- if all_flagged:
427
- try:
428
- admin = ADMIN_EMAIL
429
- if admin and emailer:
430
- emailer.send_alert(
431
- recipient=admin,
432
- subject=f"Fraud Alert for user {user_id}",
433
- body=f"Detected {len(all_flagged)} suspicious txns. See attached.",
434
- attachment=pdf_path if 'pdf_path' in locals() else None
435
- )
436
- if slacknot:
437
- slacknot.send(f"Fraud Alert: user {user_id} has {len(all_flagged)} flagged transactions.")
438
- print("βœ… Alerts sent")
439
- except Exception as e:
440
- _log_ex("Alert sending failed", e)
441
-
442
- status_msg = f"βœ… Analysis complete. Found {len(all_flagged)} suspicious transactions."
443
- print(f"πŸŽ‰ Processing completed successfully")
444
- return llm_output, all_flagged, report_paths, status_msg
445
 
 
 
 
 
446
  except Exception as e:
447
- error_msg = f"❌ Processing error: {e}"
448
- _log_ex("Overall processing error", e)
449
- return error_msg, {}, {}, ""
450
-
451
- # =========================
452
- # Chatbot (Chat Completion)
453
- # =========================
454
- def respond(message, history, model_choice):
455
- # Guard: empty message
456
- if not message or not message.strip():
457
- yield history
458
- return
459
-
460
- # Debug: print what model was selected
461
- print(f"Selected model: '{model_choice}'")
462
- print(f"Available models: {list(MODEL_LABELS.keys())}")
463
-
464
- # Resolve model/client
465
- info = MODEL_LABELS.get(model_choice)
466
- if not info:
467
- history.append([message, f"❌ Unknown model selected: '{model_choice}'. Available: {list(MODEL_LABELS.keys())}"])
468
- yield history
469
- return
470
-
471
- client = info["client"]()
472
- if client is None:
473
- history.append([message, "❌ Selected model not available. Check HF_TOKEN in Space secrets."])
474
- yield history
475
- return
476
-
477
- # Add placeholder assistant message
478
- history.append([message, ""])
479
- try:
480
- messages = build_messages(message)
481
-
482
- # Use chat_completion instead of text_generation
483
- response = client.chat_completion(
484
- messages=messages,
485
- max_tokens=512,
486
- temperature=0.7,
487
- # stream=False, # Set to False for single response
488
- )
489
 
490
- # Extract text from response
491
- if hasattr(response, 'choices') and len(response.choices) > 0:
492
- # Standard OpenAI-style response
493
- text = response.choices[0].message.content
494
- elif isinstance(response, dict) and 'choices' in response:
495
- # Dict-style response
496
- text = response['choices'][0]['message']['content']
497
- elif hasattr(response, 'content'):
498
- # Direct content attribute
499
- text = response.content
500
- elif isinstance(response, str):
501
- # Direct string response
502
- text = response
503
- else:
504
- # Fallback - convert to string
505
- text = str(response)
506
 
507
- history[-1][1] = text.strip() if text else "❌ Empty response."
508
- yield history
509
 
 
 
 
 
510
  except Exception as e:
511
- _log_ex("Chatbot chat_completion failed", e)
512
- history[-1][1] = f"❌ Error: {e}"
513
- yield history
514
-
515
- def user_submit(message, history, model_choice):
516
- # clear the textbox but keep history as-is (respond will append)
517
- return "", history
518
-
519
- # =========================
520
- # Gradio UI
521
- # =========================
522
- with gr.Blocks(css=".output_json {height:300px;}", title="πŸ” Fraud Detector Analyst") as demo:
523
- gr.Markdown("# πŸ” Fraud Detector Analyst")
524
- gr.Markdown("Upload transaction data to detect fraud patterns using AI analysis and velocity detection.")
525
-
526
- with gr.Tab("πŸ” Authentication"):
527
- gr.Markdown("### Register New Account")
528
- with gr.Row():
529
- reg_user = gr.Textbox(label="Username", placeholder="Enter username")
530
- reg_pass = gr.Textbox(label="Password", type="password", placeholder="Enter password")
531
- reg_email = gr.Textbox(label="Email", placeholder="Enter email (optional)")
532
- reg_btn = gr.Button("πŸ“ Register", variant="primary")
533
- reg_msg = gr.Textbox(label="Registration Status", interactive=False)
534
 
535
- gr.Markdown("### Login to Existing Account")
 
536
  with gr.Row():
537
- login_user_tb = gr.Textbox(label="Username", placeholder="Enter username")
538
- login_pass_tb = gr.Textbox(label="Password", type="password", placeholder="Enter password")
539
- login_btn = gr.Button("πŸ”‘ Login", variant="primary")
540
- token_out = gr.Textbox(label="Session Token", interactive=False)
541
- login_msg = gr.Textbox(label="Login Status", interactive=False)
542
-
543
- # Debug section
544
- gr.Markdown("### πŸ› Debug (for troubleshooting)")
545
- debug_btn = gr.Button("πŸ“‹ Check Database", variant="secondary")
546
- debug_msg = gr.Textbox(label="Debug Info", interactive=False)
547
-
548
- with gr.Tab("πŸ“Š Fraud Analysis"):
549
- gr.Markdown("### Upload Transaction Data")
550
- gr.Markdown("**Required CSV columns:** `transaction_id`, `timestamp`, `amount`, `description`, `merchant`")
551
-
552
- session_token_tb = gr.Textbox(label="Session Token (from login)", placeholder="Paste your session token here")
553
- csv_file = gr.File(label="πŸ“„ Upload transactions CSV (.csv)", file_types=[".csv"])
554
- rag_query_tb = gr.Textbox(label="πŸ” RAG Query (optional)", placeholder="e.g., 'similar gift card purchases'")
555
- analyze_btn = gr.Button("πŸ” Analyze Transactions", variant="primary", size="lg")
556
-
557
  with gr.Row():
558
- analysis_out = gr.Textbox(label="πŸ€– AI Analysis", lines=8, interactive=False)
559
- status_out = gr.Textbox(label="πŸ“‹ Status", lines=2, interactive=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
560
 
 
 
 
 
 
 
 
 
 
561
  with gr.Row():
562
- flagged_out = gr.JSON(label="⚠️ Flagged Transactions")
563
- reports_out = gr.JSON(label="πŸ“„ Generated Reports")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
564
 
565
- with gr.Tab("πŸ’¬ Chatbot"):
566
- gr.Markdown("### πŸ›‘ Fraud Detector Chatbot")
567
- gr.Markdown("Ask questions about fraud detection, transaction analysis, or get general assistance.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
568
 
569
- # IMPORTANT: default type uses list of [user, bot] pairs
570
- chatbot = gr.Chatbot(height=400, show_label=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
571
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
572
  with gr.Row():
573
  msg = gr.Textbox(
574
- label="Your message",
575
- placeholder="Ask about fraud patterns, transaction analysis, etc...",
576
  scale=4
577
  )
578
- # FIXED: Match the model choices with MODEL_LABELS keys
579
- model_choice = gr.Radio(
580
- ["Mistral-7B-Instruct-v0.2", "Qwen2.5-Coder-32B-Instruct"],
581
- value="Mistral-7B-Instruct-v0.2",
582
- label="Select Model",
583
- scale=1
584
- )
585
-
586
- # Event handlers for Fraud Detector
587
- reg_btn.click(fn=register_user, inputs=[reg_user, reg_pass, reg_email], outputs=[reg_msg])
588
- login_btn.click(fn=login_user, inputs=[login_user_tb, login_pass_tb], outputs=[token_out, login_msg])
589
- debug_btn.click(fn=debug_database, inputs=[], outputs=[debug_msg])
590
- analyze_btn.click(fn=process_file, inputs=[session_token_tb, csv_file, rag_query_tb], outputs=[analysis_out, flagged_out, reports_out, status_out])
591
-
592
- # Event handlers for Chatbot (call respond first, then clear input)
593
- msg.submit(respond, [msg, chatbot, model_choice], chatbot).then(
594
- lambda: "", None, msg
595
- )
596
 
597
- # Add sample data format
598
  gr.Markdown("""
599
- ### πŸ“‹ Sample CSV Format:
600
- ```csv
601
- transaction_id,timestamp,amount,description,merchant
602
- TXN001,2024-01-15 10:30:00,25.99,Coffee Purchase,Starbucks
603
- TXN002,2024-01-15 10:31:00,1500.00,Gift Card Purchase,Amazon
604
- TXN003,2024-01-15 10:32:00,2000.00,Wire Transfer,Bank Transfer
605
- ```
 
606
  """)
607
 
608
  if __name__ == "__main__":
609
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ import pandas as pd
3
+ import re
4
+ import numpy as np
5
+ from datetime import datetime, timedelta
6
  from huggingface_hub import InferenceClient
7
+ import io
8
+ import base64
9
+
10
+ # -------------------------
11
+ # HF Inference Client Setup
12
+ # -------------------------
13
+ HF_TOKEN = "YOUR_HF_TOKEN" # Replace with your actual token
14
+ client = InferenceClient(token=HF_TOKEN)
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  DEFAULT_MODEL_ID = "mistralai/Mistral-7B-Instruct-v0.2"
17
  QWEN_MODEL_ID = "Qwen/Qwen2.5-Coder-32B-Instruct"
18
 
19
+ # -------------------------
20
+ # Utility Functions
21
+ # -------------------------
22
+ def generate_summary(prompt, model_id=DEFAULT_MODEL_ID):
23
+ """Generate AI-powered analysis summary using HuggingFace models"""
24
  try:
25
+ response = client.text_generation(
26
+ model=model_id,
27
+ inputs=prompt,
28
+ max_new_tokens=500,
29
+ temperature=0.7,
30
+ do_sample=True
31
+ )
32
+ return response
33
  except Exception as e:
34
+ return f"⚠️ Error generating AI summary: {str(e)}"
35
+
36
+ def create_download_link(df, filename):
37
+ """Create downloadable CSV from DataFrame"""
38
+ csv = df.to_csv(index=False)
39
+ b64 = base64.b64encode(csv.encode()).decode()
40
+ return f'<a href="data:file/csv;base64,{b64}" download="{filename}">πŸ“₯ Download {filename}</a>'
41
+
42
+ # -------------------------
43
+ # 1. Transaction Fraud (Enhanced from original)
44
+ # -------------------------
45
+ def process_transaction_file(file):
46
+ """Process transaction data for fraud detection"""
47
  try:
48
+ df = pd.read_csv(file.name)
49
+
50
+ # Enhanced fraud detection rules
51
+ suspicious_conditions = (
52
+ (df['amount'] > 10000) | # Large transactions
53
+ (df['amount'] < 0) | # Negative amounts
54
+ (df.get('merchant_category', '') == 'HIGH_RISK') | # High-risk merchants
55
+ (df.groupby('customer_id')['amount'].transform('sum') > 50000) # Daily limits
56
+ )
57
+
58
+ suspicious = df[suspicious_conditions].copy()
59
+ suspicious['risk_reason'] = suspicious.apply(lambda x:
60
+ 'Large Amount' if x['amount'] > 10000 else
61
+ 'Negative Amount' if x['amount'] < 0 else
62
+ 'High Risk Merchant' if x.get('merchant_category') == 'HIGH_RISK' else
63
+ 'Daily Limit Exceeded', axis=1)
64
+
65
+ # AI Analysis
66
+ prompt = f"""You are a financial fraud analyst. Analyze these suspicious transactions:
67
+
68
+ Transaction Data Sample:
69
+ {df.head(10).to_string()}
70
+
71
+ Suspicious Transactions Found: {len(suspicious)}
72
+ Key Patterns: Large amounts, negative values, high-risk merchants
73
+
74
+ Provide a detailed risk assessment and recommended actions."""
75
+
76
+ summary = generate_summary(prompt)
77
+
78
+ return suspicious, summary, f"Found {len(suspicious)} suspicious transactions out of {len(df)} total"
79
+
80
  except Exception as e:
81
+ return pd.DataFrame(), f"Error processing file: {str(e)}", ""
82
+
83
+ # -------------------------
84
+ # 2. KYC Fraud Analysis
85
+ # -------------------------
86
+ def process_kyc_file(file):
87
+ """Process KYC data for identity fraud detection"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  try:
89
+ df = pd.read_csv(file.name)
90
+ flagged_records = []
91
+
92
+ # Check for duplicate emails
93
+ duplicate_emails = df[df.duplicated('email', keep=False)]
94
+ if not duplicate_emails.empty:
95
+ duplicate_emails = duplicate_emails.copy()
96
+ duplicate_emails['flag_reason'] = 'Duplicate Email'
97
+ flagged_records.append(duplicate_emails)
98
+
99
+ # Check for duplicate phone numbers
100
+ if 'phone' in df.columns:
101
+ duplicate_phones = df[df.duplicated('phone', keep=False)]
102
+ if not duplicate_phones.empty:
103
+ duplicate_phones = duplicate_phones.copy()
104
+ duplicate_phones['flag_reason'] = 'Duplicate Phone'
105
+ flagged_records.append(duplicate_phones)
106
+
107
+ # Check for invalid DOB patterns
108
+ if 'dob' in df.columns:
109
+ # Flag future dates or unrealistic ages
110
+ try:
111
+ df['dob_parsed'] = pd.to_datetime(df['dob'], errors='coerce')
112
+ invalid_dob = df[
113
+ (df['dob_parsed'].isna()) | # Invalid date format
114
+ (df['dob_parsed'] > datetime.now()) | # Future dates
115
+ (df['dob_parsed'] < datetime.now() - timedelta(days=365*120)) # Age > 120
116
+ ].copy()
117
+ if not invalid_dob.empty:
118
+ invalid_dob['flag_reason'] = 'Invalid DOB'
119
+ flagged_records.append(invalid_dob)
120
+ except:
121
+ pass
122
+
123
+ # Check for suspicious name patterns
124
+ if 'name' in df.columns:
125
+ suspicious_names = df[
126
+ df['name'].str.contains(r'^[A-Z]+$', na=False) | # All caps
127
+ df['name'].str.contains(r'\d', na=False) | # Contains numbers
128
+ (df['name'].str.len() < 3) # Too short
129
+ ].copy()
130
+ if not suspicious_names.empty:
131
+ suspicious_names['flag_reason'] = 'Suspicious Name Pattern'
132
+ flagged_records.append(suspicious_names)
133
+
134
+ # Combine all flagged records
135
+ if flagged_records:
136
+ flagged_df = pd.concat(flagged_records, ignore_index=True)
137
+ flagged_df = flagged_df.drop_duplicates()
138
  else:
139
+ flagged_df = pd.DataFrame()
140
+
141
+ # AI Analysis
142
+ prompt = f"""You are a KYC fraud analyst. Review these customer identity records for potential fraud:
143
+
144
+ Total Records: {len(df)}
145
+ Flagged Records: {len(flagged_df)}
146
+
147
+ Sample Data:
148
+ {df.head(10).to_string()}
149
+
150
+ Flagged Issues:
151
+ {flagged_df[['flag_reason']].value_counts().to_string() if not flagged_df.empty else 'No issues found'}
152
+
153
+ Identify patterns of synthetic identities, duplicate accounts, or data quality issues. Recommend verification steps."""
154
+
155
+ summary = generate_summary(prompt)
156
+
157
+ return flagged_df, summary, f"Flagged {len(flagged_df)} suspicious KYC records out of {len(df)} total"
158
+
159
  except Exception as e:
160
+ return pd.DataFrame(), f"Error processing KYC file: {str(e)}", ""
161
+
162
+ # -------------------------
163
+ # 3. Sanctions Check
164
+ # -------------------------
165
+ def process_sanctions_file(file, sanctions_file=None):
166
+ """Process customer data against sanctions/PEP lists"""
 
 
 
167
  try:
168
+ df = pd.read_csv(file.name)
169
+
170
+ # Default sanctions list (you can replace with actual data)
171
+ default_sanctions = [
172
+ "John Doe", "Jane Smith", "Muhammad Ali", "Vladimir Putin",
173
+ "Kim Jong Un", "Alexander Petrov", "Maria Gonzalez"
174
+ ]
175
+
176
+ if sanctions_file:
177
+ try:
178
+ sanctions_df = pd.read_csv(sanctions_file.name)
179
+ sanctions_list = sanctions_df['name'].str.lower().tolist()
180
+ except:
181
+ sanctions_list = [name.lower() for name in default_sanctions]
182
  else:
183
+ sanctions_list = [name.lower() for name in default_sanctions]
184
+
185
+ # Exact match check
186
+ exact_matches = df[df['name'].str.lower().isin(sanctions_list)].copy()
187
+
188
+ # Fuzzy match check (simple implementation)
189
+ fuzzy_matches = []
190
+ for idx, row in df.iterrows():
191
+ customer_name = str(row['name']).lower()
192
+ for sanction_name in sanctions_list:
193
+ # Simple similarity check (can be enhanced with fuzzy matching libraries)
194
+ if len(set(customer_name.split()) & set(sanction_name.split())) >= 2:
195
+ fuzzy_matches.append(idx)
196
+ break
197
+
198
+ fuzzy_df = df.loc[fuzzy_matches].copy() if fuzzy_matches else pd.DataFrame()
199
+
200
+ # Combine results
201
+ flagged_customers = pd.concat([exact_matches, fuzzy_df]).drop_duplicates()
202
+
203
+ if not flagged_customers.empty:
204
+ flagged_customers['match_type'] = 'Exact Match'
205
+ flagged_customers.loc[fuzzy_df.index, 'match_type'] = 'Fuzzy Match'
206
+
207
+ # AI Analysis
208
+ prompt = f"""You are a compliance officer conducting sanctions screening. Review these results:
209
+
210
+ Total Customers Screened: {len(df)}
211
+ Potential Matches Found: {len(flagged_customers)}
212
+
213
+ Customer Sample:
214
+ {df.head(5).to_string()}
215
+
216
+ Flagged Customers:
217
+ {flagged_customers.to_string() if not flagged_customers.empty else 'No matches found'}
218
+
219
+ Assess the risk level and recommend enhanced due diligence procedures for flagged customers."""
220
+
221
+ summary = generate_summary(prompt)
222
+
223
+ return flagged_customers, summary, f"Found {len(flagged_customers)} potential sanctions matches out of {len(df)} customers screened"
224
+
225
  except Exception as e:
226
+ return pd.DataFrame(), f"Error processing sanctions check: {str(e)}", ""
 
227
 
228
+ # -------------------------
229
+ # 4. Credit Risk Analysis
230
+ # -------------------------
231
+ def process_credit_file(file):
232
+ """Process credit data for risk assessment"""
233
  try:
234
+ df = pd.read_csv(file.name)
235
+
236
+ # Credit risk scoring rules
237
+ high_risk_conditions = []
238
+ risk_reasons = []
239
+
240
+ # Rule 1: Low credit score
241
+ if 'credit_score' in df.columns:
242
+ low_score = df['credit_score'] < 600
243
+ high_risk_conditions.append(low_score)
244
+ risk_reasons.append('Low Credit Score')
245
+
246
+ # Rule 2: High utilization rate
247
+ if 'utilization_rate' in df.columns:
248
+ high_util = df['utilization_rate'] > 0.8
249
+ high_risk_conditions.append(high_util)
250
+ risk_reasons.append('High Utilization')
251
+
252
+ # Rule 3: High debt-to-income ratio
253
+ if 'debt_to_income' in df.columns:
254
+ high_dti = df['debt_to_income'] > 0.4
255
+ high_risk_conditions.append(high_dti)
256
+ risk_reasons.append('High Debt-to-Income')
257
+
258
+ # Rule 4: Recent defaults
259
+ if 'recent_defaults' in df.columns:
260
+ has_defaults = df['recent_defaults'] > 0
261
+ high_risk_conditions.append(has_defaults)
262
+ risk_reasons.append('Recent Defaults')
263
+
264
+ # Rule 5: Low income
265
+ if 'income' in df.columns:
266
+ low_income = df['income'] < 30000
267
+ high_risk_conditions.append(low_income)
268
+ risk_reasons.append('Low Income')
269
+
270
+ # Combine risk conditions
271
+ if high_risk_conditions:
272
+ risk_mask = pd.concat(high_risk_conditions, axis=1).any(axis=1)
273
+ risky_customers = df[risk_mask].copy()
274
+
275
+ # Add risk scoring
276
+ risky_customers['risk_score'] = 0
277
+ for i, condition in enumerate(high_risk_conditions):
278
+ risky_customers.loc[condition, 'risk_score'] += 1
279
+
280
+ risky_customers['risk_level'] = risky_customers['risk_score'].apply(
281
+ lambda x: 'High' if x >= 3 else 'Medium' if x >= 2 else 'Low'
282
+ )
283
+ else:
284
+ risky_customers = pd.DataFrame()
285
+
286
+ # AI Analysis
287
+ prompt = f"""You are a credit risk analyst. Assess these customer credit profiles:
288
 
289
+ Total Customers: {len(df)}
290
+ High-Risk Customers: {len(risky_customers)}
 
 
 
291
 
292
+ Sample Data:
293
+ {df.head(10).to_string()}
294
 
295
+ Risk Distribution:
296
+ {risky_customers['risk_level'].value_counts().to_string() if not risky_customers.empty else 'No high-risk customers identified'}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
297
 
298
+ Provide risk assessment insights and recommend credit policies or monitoring actions."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
 
300
+ summary = generate_summary(prompt)
301
+
302
+ return risky_customers, summary, f"Identified {len(risky_customers)} high-risk customers out of {len(df)} total"
303
+
304
  except Exception as e:
305
+ return pd.DataFrame(), f"Error processing credit risk file: {str(e)}", ""
306
+
307
+ # -------------------------
308
+ # 5. Chatbot (Enhanced)
309
+ # -------------------------
310
+ def chatbot_respond(message, history, model_choice):
311
+ """Enhanced chatbot for fraud and risk analysis queries"""
312
+ # Build conversation context
313
+ conversation = ""
314
+ for msg, response in history:
315
+ conversation += f"User: {msg}\nAssistant: {response}\n\n"
316
+
317
+ prompt = f"""You are an expert fraud analyst and risk management consultant. Help users with:
318
+ - Transaction fraud detection
319
+ - KYC/Identity verification
320
+ - Sanctions screening
321
+ - Credit risk assessment
322
+ - Regulatory compliance
323
+ - Financial crime prevention
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
 
325
+ Previous conversation:
326
+ {conversation}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
 
328
+ User: {message}
329
+ Assistant:"""
330
 
331
+ try:
332
+ response = generate_summary(prompt, model_id=model_choice)
333
+ history.append((message, response))
334
+ return history, ""
335
  except Exception as e:
336
+ error_response = f"I apologize, but I encountered an error: {str(e)}"
337
+ history.append((message, error_response))
338
+ return history, ""
339
+
340
+ # -------------------------
341
+ # Gradio Interface
342
+ # -------------------------
343
+ with gr.Blocks(theme=gr.themes.Soft(), title="πŸ›‘οΈ Fraud Detector Analyst") as demo:
344
+ gr.Markdown("""
345
+ # πŸ›‘οΈ Fraud Detector Analyst (Multi-Module Risk Platform)
346
+
347
+ **Comprehensive Risk Intelligence Platform** featuring:
348
+ - πŸ“Š Transaction Fraud Detection
349
+ - πŸ†” KYC Identity Fraud Analysis
350
+ - 🌍 Sanctions & PEP Screening
351
+ - πŸ’³ Credit Risk Assessment
352
+ - πŸ’¬ AI-Powered Risk Consultant
353
+ """)
 
 
 
 
 
354
 
355
+ with gr.Tab("πŸ“Š Transaction Fraud"):
356
+ gr.Markdown("### Upload transaction data to detect fraudulent patterns")
357
  with gr.Row():
358
+ trans_file = gr.File(
359
+ label="Upload Transaction CSV",
360
+ file_types=[".csv"],
361
+ type="filepath"
362
+ )
363
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
  with gr.Row():
365
+ with gr.Column():
366
+ trans_summary = gr.Textbox(
367
+ label="AI Analysis Summary",
368
+ lines=8,
369
+ interactive=False
370
+ )
371
+ with gr.Column():
372
+ trans_stats = gr.Textbox(
373
+ label="Detection Statistics",
374
+ lines=2,
375
+ interactive=False
376
+ )
377
+
378
+ trans_results = gr.Dataframe(
379
+ label="Suspicious Transactions",
380
+ interactive=False
381
+ )
382
+
383
+ trans_file.upload(
384
+ process_transaction_file,
385
+ inputs=[trans_file],
386
+ outputs=[trans_results, trans_summary, trans_stats]
387
+ )
388
 
389
+ with gr.Tab("πŸ†” KYC Fraud Analysis"):
390
+ gr.Markdown("### Detect identity fraud and synthetic accounts in customer onboarding data")
391
+ with gr.Row():
392
+ kyc_file = gr.File(
393
+ label="Upload KYC Customer Data CSV",
394
+ file_types=[".csv"],
395
+ type="filepath"
396
+ )
397
+
398
  with gr.Row():
399
+ with gr.Column():
400
+ kyc_summary = gr.Textbox(
401
+ label="KYC Fraud Analysis",
402
+ lines=8,
403
+ interactive=False
404
+ )
405
+ with gr.Column():
406
+ kyc_stats = gr.Textbox(
407
+ label="KYC Statistics",
408
+ lines=2,
409
+ interactive=False
410
+ )
411
+
412
+ kyc_results = gr.Dataframe(
413
+ label="Flagged KYC Records",
414
+ interactive=False
415
+ )
416
+
417
+ kyc_file.upload(
418
+ process_kyc_file,
419
+ inputs=[kyc_file],
420
+ outputs=[kyc_results, kyc_summary, kyc_stats]
421
+ )
422
 
423
+ with gr.Tab("🌍 Sanctions Check"):
424
+ gr.Markdown("### Screen customers against sanctions lists and PEP databases")
425
+ with gr.Row():
426
+ sanctions_customer_file = gr.File(
427
+ label="Upload Customer List CSV",
428
+ file_types=[".csv"],
429
+ type="filepath"
430
+ )
431
+ sanctions_list_file = gr.File(
432
+ label="Upload Sanctions List CSV (Optional)",
433
+ file_types=[".csv"],
434
+ type="filepath"
435
+ )
436
+
437
+ with gr.Row():
438
+ with gr.Column():
439
+ sanctions_summary = gr.Textbox(
440
+ label="Sanctions Screening Results",
441
+ lines=8,
442
+ interactive=False
443
+ )
444
+ with gr.Column():
445
+ sanctions_stats = gr.Textbox(
446
+ label="Screening Statistics",
447
+ lines=2,
448
+ interactive=False
449
+ )
450
+
451
+ sanctions_results = gr.Dataframe(
452
+ label="Flagged Customers",
453
+ interactive=False
454
+ )
455
+
456
+ sanctions_customer_file.upload(
457
+ lambda f1, f2: process_sanctions_file(f1, f2),
458
+ inputs=[sanctions_customer_file, sanctions_list_file],
459
+ outputs=[sanctions_results, sanctions_summary, sanctions_stats]
460
+ )
461
 
462
+ with gr.Tab("πŸ’³ Credit Risk"):
463
+ gr.Markdown("### Assess credit risk and default probability for loan applicants")
464
+ with gr.Row():
465
+ credit_file = gr.File(
466
+ label="Upload Credit Profile CSV",
467
+ file_types=[".csv"],
468
+ type="filepath"
469
+ )
470
+
471
+ with gr.Row():
472
+ with gr.Column():
473
+ credit_summary = gr.Textbox(
474
+ label="Credit Risk Analysis",
475
+ lines=8,
476
+ interactive=False
477
+ )
478
+ with gr.Column():
479
+ credit_stats = gr.Textbox(
480
+ label="Risk Statistics",
481
+ lines=2,
482
+ interactive=False
483
+ )
484
+
485
+ credit_results = gr.Dataframe(
486
+ label="High-Risk Customers",
487
+ interactive=False
488
+ )
489
+
490
+ credit_file.upload(
491
+ process_credit_file,
492
+ inputs=[credit_file],
493
+ outputs=[credit_results, credit_summary, credit_stats]
494
+ )
495
 
496
+ with gr.Tab("πŸ’¬ AI Risk Consultant"):
497
+ gr.Markdown("### Chat with our AI expert about fraud detection and risk management")
498
+
499
+ model_choice = gr.Dropdown(
500
+ choices=[DEFAULT_MODEL_ID, QWEN_MODEL_ID],
501
+ label="Choose AI Model",
502
+ value=DEFAULT_MODEL_ID,
503
+ info="Select the language model for analysis"
504
+ )
505
+
506
+ chatbot = gr.Chatbot(
507
+ label="Risk Management Consultant",
508
+ height=500
509
+ )
510
+
511
  with gr.Row():
512
  msg = gr.Textbox(
513
+ label="Ask about fraud detection, risk assessment, compliance...",
514
+ placeholder="e.g., How can I improve my transaction fraud detection?",
515
  scale=4
516
  )
517
+ submit_btn = gr.Button("Send", scale=1, variant="primary")
518
+
519
+ # Handle both enter key and button click
520
+ msg.submit(
521
+ chatbot_respond,
522
+ inputs=[msg, chatbot, model_choice],
523
+ outputs=[chatbot, msg]
524
+ )
525
+ submit_btn.click(
526
+ chatbot_respond,
527
+ inputs=[msg, chatbot, model_choice],
528
+ outputs=[chatbot, msg]
529
+ )
 
 
 
 
 
530
 
531
+ # Footer
532
  gr.Markdown("""
533
+ ---
534
+ **⚠️ Disclaimer:** This tool is for demonstration purposes. Always validate results with domain experts and comply with relevant regulations.
535
+
536
+ **πŸ“‹ Supported CSV Formats:**
537
+ - **Transactions:** `customer_id, amount, merchant_category, timestamp`
538
+ - **KYC:** `customer_id, name, email, phone, dob, address`
539
+ - **Sanctions:** `name, dob, country` (customers) | `name, list_type` (sanctions)
540
+ - **Credit:** `customer_id, credit_score, utilization_rate, debt_to_income, income, recent_defaults`
541
  """)
542
 
543
  if __name__ == "__main__":
544
+ demo.launch(
545
+ share=True,
546
+ server_name="0.0.0.0",
547
+ server_port=7860
548
+ )