MohitRajput45 commited on
Commit
08a0b7b
Β·
verified Β·
1 Parent(s): d877484

Update app/streamlit_app.py

Browse files
Files changed (1) hide show
  1. app/streamlit_app.py +55 -280
app/streamlit_app.py CHANGED
@@ -17,17 +17,13 @@ from src.pipeline.predict_pipeline import PredictPipeline
17
  from src.explanability.shap_explainer import ShapExplainer
18
  from src.monitoring.db import save_to_db
19
 
20
- # βœ… Stable page config
21
- st.set_page_config(page_title="Fraud Guard", layout="wide", initial_sidebar_state="expanded")
22
 
23
  # --- Initialize Session State for Auto-Fill Buttons ---
24
- if "initialized" not in st.session_state:
25
- st.session_state.t_time = 10000.0
26
- st.session_state.t_amount = 100.0
27
- for i in range(1, 29):
28
- st.session_state[f"v_{i}"] = 0.0
29
- st.session_state.initialized = True
30
-
31
 
32
  def generate_sample(is_fraud=False):
33
  """Fills the UI with either a normal transaction or a simulated fraud attack"""
@@ -36,53 +32,51 @@ def generate_sample(is_fraud=False):
36
  {
37
  "Time": 406.0, "Amount": 0.00,
38
  "V": [-2.312, 1.951, -1.609, 3.997, -0.522, -1.426, -2.537, 1.391, -2.770, -2.772,
39
- 3.202, -2.899, -0.595, -4.289, 0.389, -1.140, -2.830, -0.016, 0.416, 0.126,
40
- 0.517, -0.035, -0.465, 0.320, 0.044, 0.177, 0.261, -0.143]
41
  },
42
  {
43
  "Time": 12500.0, "Amount": 99.99,
44
  "V": [-0.95, 0.52, -1.53, 0.85, -0.21, 0.11, -0.45, 0.22, -0.63, -1.05,
45
- 1.20, -1.55, 0.30, -2.01, 0.10, -0.55, -1.22, 0.20, 0.45, -0.10,
46
- 0.25, 0.15, -0.12, 0.05, 0.22, -0.15, 0.02, 0.05]
47
  },
48
  {
49
  "Time": 4462.0, "Amount": 1.00,
50
  "V": [-2.303, 1.759, -0.359, 2.330, -0.821, -0.075, -0.560, 1.214, -1.385, -2.776,
51
- 3.231, -2.719, -1.059, -3.535, -1.583, -1.488, -2.573, -0.739, 0.380, -0.430,
52
  -0.294, -0.932, 0.172, -0.087, -0.156, -0.542, 0.039, -0.153]
53
  }
54
  ]
55
-
56
  chosen_fraud = random.choice(fraud_database)
57
-
58
  st.session_state.t_time = chosen_fraud["Time"]
59
  st.session_state.t_amount = chosen_fraud["Amount"]
60
  for i in range(1, 29):
61
- st.session_state[f"v_{i}"] = chosen_fraud["V"][i - 1]
62
-
63
  else:
64
  st.session_state.t_time = random.uniform(100, 150000)
65
  st.session_state.t_amount = random.uniform(5, 150)
66
  for i in range(1, 29):
67
  st.session_state[f"v_{i}"] = random.uniform(-1.0, 1.0)
68
 
69
-
70
  # βœ… Cache ONLY heavy computation (safe)
71
  @st.cache_data(show_spinner=False)
72
  def get_prediction_and_shap(payload):
73
  response = requests.post(API_URL, json=payload, timeout=30)
74
  result = response.json()
75
-
76
  input_df = pd.DataFrame([payload])
77
  pipeline = PredictPipeline()
78
  processed_df = pipeline.preprocess(input_df)
79
-
80
  explainer = ShapExplainer()
81
  shap_values = explainer.explain(processed_df)
82
-
83
  return result, shap_values
84
 
85
-
86
  # --- UI Sidebar & Navigation ---
87
  page = st.sidebar.selectbox("πŸ“Œ Choose Section", ["Prediction", "Drift Monitoring"])
88
 
@@ -98,7 +92,6 @@ if page == "Prediction":
98
 
99
  with col1:
100
  st.markdown("### πŸ› οΈ Demo Controls")
101
-
102
  demo_col1, demo_col2 = st.columns(2)
103
  with demo_col1:
104
  if st.button("βœ… Simulate Normal User", use_container_width=True):
@@ -110,281 +103,62 @@ if page == "Prediction":
110
  st.markdown("### πŸ“₯ Transaction Input")
111
  with st.container(border=True):
112
  with st.form("transaction_form"):
 
113
  t_time = st.slider("Time (Sec)", 0.0, 172800.0, key="t_time")
114
  t_amount = st.slider("Amount ($)", 0.0, 5000.0, key="t_amount")
115
-
 
 
116
  with st.expander("PCA Feature Vectors (V1 - V28)", expanded=False):
117
- v_data = {}
118
  for i in range(1, 29):
119
  v_data[f"V{i}"] = st.number_input(f"V{i}", key=f"v_{i}", format="%.4f")
120
-
121
  st.markdown("---")
122
  threshold = st.slider("AI Sensitivity (Threshold)", 0.05, 0.95, 0.15)
123
  submit_btn = st.form_submit_button("πŸ” Run Analysis", use_container_width=True)
124
 
125
  with col2:
126
  st.markdown("### πŸ“Š Live Telemetry & Assessment")
127
-
128
- result_container = st.container()
129
-
130
  if not submit_btn:
131
  st.info("Awaiting transaction payload. Click 'Simulate' then 'Run Analysis'.")
132
 
133
  if submit_btn:
134
- with result_container:
135
- payload = {"Time": st.session_state.t_time, "Amount": st.session_state.t_amount, **v_data}
136
-
137
- try:
138
- with st.spinner("Analyzing threat vectors..."):
139
- result, shap_values = get_prediction_and_shap(payload)
140
-
141
- prob = result["fraud_probability"]
142
- pred = 1 if prob > threshold else 0
143
- action = "🚫 Block Transaction" if pred == 1 else "βœ… Allow Transaction"
144
-
145
- if pred == 1:
146
- st.error(f"🚨 FRAUD DETECTED: {action}")
147
- else:
148
- st.success(f"βœ… TRANSACTION SAFE: {action}")
149
-
150
- m_col1, m_col2 = st.columns(2)
151
- m_col1.metric("Risk Level", f"{prob:.4%}")
152
- m_col2.metric("Prediction Output", pred)
153
-
154
- st.progress(float(prob))
155
- st.markdown("---")
156
-
157
- st.subheader("🧠 Explainable AI (SHAP)")
158
-
159
- # βœ… NO CACHE HERE (fixes your error)
160
- fig, ax = plt.subplots(figsize=(8, 4))
161
- shap.plots.waterfall(shap_values[0], show=False)
162
- st.pyplot(fig)
163
-
164
- except Exception as e:
165
- st.error(f"⏳ Error connecting to API: {e}")
166
-
167
- elif page == "Drift Monitoring":
168
- st.title("πŸ“‰ Data Drift Monitoring")
169
- st.markdown("### Monitor model health & trigger verified retraining")
170
-
171
- with st.expander("πŸ› οΈ Demo Tools: Force Data Drift", expanded=True):
172
- st.write("Inject 50 heavily skewed transactions into the database to trigger a statistical drift warning.")
173
- if st.button("πŸ’‰ Inject Synthetic Drift Data", type="primary"):
174
- with st.spinner("Injecting bad data into Cloud DB..."):
175
- for _ in range(50):
176
- skewed_data = {"Time": random.uniform(10, 50000), "Amount": random.uniform(1000, 5000)}
177
- for i in range(1, 29):
178
- skewed_data[f"V{i}"] = random.uniform(-15.0, 15.0)
179
- save_to_db(skewed_data, pred=1, prob=0.99)
180
- st.success("βœ… 50 Skewed rows injected!")
181
-
182
- st.markdown("---")
183
-
184
- try:
185
- from src.monitoring.drift import detect_drift
186
- from src.pipeline.retrain_pipeline import retrain
187
- except:
188
- st.error("⚠️ Drift feature not supported in this environment")
189
- st.stop()
190
-
191
- if st.button("πŸš€ Run Drift Detection"):
192
- with st.spinner("Running statistical drift analysis..."):
193
  try:
194
- report_path = detect_drift("data/creditcard.csv")
195
- if report_path:
196
- st.success("βœ… Drift report generated!")
197
- st.session_state["drift_done"] = True
 
 
 
 
 
198
  else:
199
- st.warning("⚠️ Not enough data in live DB.")
200
- except Exception as e:
201
- st.error(f"⚠️ Error running drift: {e}")
202
-
203
- report_path = "reports/drift_report.html"
204
- if os.path.exists(report_path):
205
- with open(report_path, "r", encoding="utf-8") as f:
206
- html = f.read()
207
- components.html(html, height=800, scrolling=True)
208
-
209
- st.markdown("---")
210
-
211
- if st.session_state.get("drift_done", False):
212
- if st.button("⚑ Retrain Model"):
213
- with st.spinner("Retraining model..."):
214
- try:
215
- retrain()
216
- st.success("βœ… Model retrained successfully!")
217
- except Exception as e:
218
- st.error(f"❌ Error: {e}")import streamlit as st
219
- import pandas as pd
220
- import sys
221
- import os
222
- import requests
223
- import shap
224
- import matplotlib.pyplot as plt
225
- import streamlit.components.v1 as components
226
- import random
227
- from dotenv import load_dotenv
228
-
229
- sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
230
- load_dotenv()
231
- API_URL = os.getenv("API_URL", "http://127.0.0.1:8000/predict")
232
-
233
- from src.pipeline.predict_pipeline import PredictPipeline
234
- from src.explanability.shap_explainer import ShapExplainer
235
- from src.monitoring.db import save_to_db
236
-
237
- # βœ… Stable page config
238
- st.set_page_config(page_title="Fraud Guard", layout="wide", initial_sidebar_state="expanded")
239
-
240
- # --- Initialize Session State for Auto-Fill Buttons ---
241
- if "initialized" not in st.session_state:
242
- st.session_state.t_time = 10000.0
243
- st.session_state.t_amount = 100.0
244
- for i in range(1, 29):
245
- st.session_state[f"v_{i}"] = 0.0
246
- st.session_state.initialized = True
247
-
248
-
249
- def generate_sample(is_fraud=False):
250
- """Fills the UI with either a normal transaction or a simulated fraud attack"""
251
- if is_fraud:
252
- fraud_database = [
253
- {
254
- "Time": 406.0, "Amount": 0.00,
255
- "V": [-2.312, 1.951, -1.609, 3.997, -0.522, -1.426, -2.537, 1.391, -2.770, -2.772,
256
- 3.202, -2.899, -0.595, -4.289, 0.389, -1.140, -2.830, -0.016, 0.416, 0.126,
257
- 0.517, -0.035, -0.465, 0.320, 0.044, 0.177, 0.261, -0.143]
258
- },
259
- {
260
- "Time": 12500.0, "Amount": 99.99,
261
- "V": [-0.95, 0.52, -1.53, 0.85, -0.21, 0.11, -0.45, 0.22, -0.63, -1.05,
262
- 1.20, -1.55, 0.30, -2.01, 0.10, -0.55, -1.22, 0.20, 0.45, -0.10,
263
- 0.25, 0.15, -0.12, 0.05, 0.22, -0.15, 0.02, 0.05]
264
- },
265
- {
266
- "Time": 4462.0, "Amount": 1.00,
267
- "V": [-2.303, 1.759, -0.359, 2.330, -0.821, -0.075, -0.560, 1.214, -1.385, -2.776,
268
- 3.231, -2.719, -1.059, -3.535, -1.583, -1.488, -2.573, -0.739, 0.380, -0.430,
269
- -0.294, -0.932, 0.172, -0.087, -0.156, -0.542, 0.039, -0.153]
270
- }
271
- ]
272
-
273
- chosen_fraud = random.choice(fraud_database)
274
-
275
- st.session_state.t_time = chosen_fraud["Time"]
276
- st.session_state.t_amount = chosen_fraud["Amount"]
277
- for i in range(1, 29):
278
- st.session_state[f"v_{i}"] = chosen_fraud["V"][i - 1]
279
-
280
- else:
281
- st.session_state.t_time = random.uniform(100, 150000)
282
- st.session_state.t_amount = random.uniform(5, 150)
283
- for i in range(1, 29):
284
- st.session_state[f"v_{i}"] = random.uniform(-1.0, 1.0)
285
-
286
-
287
- # βœ… Cache ONLY heavy computation (safe)
288
- @st.cache_data(show_spinner=False)
289
- def get_prediction_and_shap(payload):
290
- response = requests.post(API_URL, json=payload, timeout=30)
291
- result = response.json()
292
-
293
- input_df = pd.DataFrame([payload])
294
- pipeline = PredictPipeline()
295
- processed_df = pipeline.preprocess(input_df)
296
-
297
- explainer = ShapExplainer()
298
- shap_values = explainer.explain(processed_df)
299
-
300
- return result, shap_values
301
-
302
-
303
- # --- UI Sidebar & Navigation ---
304
- page = st.sidebar.selectbox("πŸ“Œ Choose Section", ["Prediction", "Drift Monitoring"])
305
-
306
- if page == "Prediction":
307
- st.markdown("""
308
- <div style='text-align: center; padding: 1rem 0;'>
309
- <h1 style='color: #1E3A8A;'>πŸ’³ Fraud Guard Intelligence</h1>
310
- <p style='color: #6B7280; font-size: 1.2rem;'>Real-Time Transaction Risk Analysis</p>
311
- </div>
312
- """, unsafe_allow_html=True)
313
-
314
- col1, col2 = st.columns([1, 2])
315
-
316
- with col1:
317
- st.markdown("### πŸ› οΈ Demo Controls")
318
-
319
- demo_col1, demo_col2 = st.columns(2)
320
- with demo_col1:
321
- if st.button("βœ… Simulate Normal User", use_container_width=True):
322
- generate_sample(is_fraud=False)
323
- with demo_col2:
324
- if st.button("🚨 Simulate Fraud Attack", type="primary", use_container_width=True):
325
- generate_sample(is_fraud=True)
326
-
327
- st.markdown("### πŸ“₯ Transaction Input")
328
- with st.container(border=True):
329
- with st.form("transaction_form"):
330
- t_time = st.slider("Time (Sec)", 0.0, 172800.0, key="t_time")
331
- t_amount = st.slider("Amount ($)", 0.0, 5000.0, key="t_amount")
332
-
333
- with st.expander("PCA Feature Vectors (V1 - V28)", expanded=False):
334
- v_data = {}
335
- for i in range(1, 29):
336
- v_data[f"V{i}"] = st.number_input(f"V{i}", key=f"v_{i}", format="%.4f")
337
-
338
  st.markdown("---")
339
- threshold = st.slider("AI Sensitivity (Threshold)", 0.05, 0.95, 0.15)
340
- submit_btn = st.form_submit_button("πŸ” Run Analysis", use_container_width=True)
341
-
342
- with col2:
343
- st.markdown("### πŸ“Š Live Telemetry & Assessment")
344
-
345
- result_container = st.container()
346
-
347
- if not submit_btn:
348
- st.info("Awaiting transaction payload. Click 'Simulate' then 'Run Analysis'.")
349
-
350
- if submit_btn:
351
- with result_container:
352
- payload = {"Time": st.session_state.t_time, "Amount": st.session_state.t_amount, **v_data}
353
-
354
- try:
355
- with st.spinner("Analyzing threat vectors..."):
356
- result, shap_values = get_prediction_and_shap(payload)
357
-
358
- prob = result["fraud_probability"]
359
- pred = 1 if prob > threshold else 0
360
- action = "🚫 Block Transaction" if pred == 1 else "βœ… Allow Transaction"
361
-
362
- if pred == 1:
363
- st.error(f"🚨 FRAUD DETECTED: {action}")
364
- else:
365
- st.success(f"βœ… TRANSACTION SAFE: {action}")
366
-
367
- m_col1, m_col2 = st.columns(2)
368
- m_col1.metric("Risk Level", f"{prob:.4%}")
369
- m_col2.metric("Prediction Output", pred)
370
 
371
- st.progress(float(prob))
372
- st.markdown("---")
373
-
374
- st.subheader("🧠 Explainable AI (SHAP)")
375
-
376
- # βœ… NO CACHE HERE (fixes your error)
377
- fig, ax = plt.subplots(figsize=(8, 4))
378
- shap.plots.waterfall(shap_values[0], show=False)
379
- st.pyplot(fig)
380
-
381
- except Exception as e:
382
- st.error(f"⏳ Error connecting to API: {e}")
383
 
384
  elif page == "Drift Monitoring":
385
  st.title("πŸ“‰ Data Drift Monitoring")
386
  st.markdown("### Monitor model health & trigger verified retraining")
387
-
388
  with st.expander("πŸ› οΈ Demo Tools: Force Data Drift", expanded=True):
389
  st.write("Inject 50 heavily skewed transactions into the database to trigger a statistical drift warning.")
390
  if st.button("πŸ’‰ Inject Synthetic Drift Data", type="primary"):
@@ -394,7 +168,7 @@ elif page == "Drift Monitoring":
394
  for i in range(1, 29):
395
  skewed_data[f"V{i}"] = random.uniform(-15.0, 15.0)
396
  save_to_db(skewed_data, pred=1, prob=0.99)
397
- st.success("βœ… 50 Skewed rows injected!")
398
 
399
  st.markdown("---")
400
 
@@ -413,7 +187,7 @@ elif page == "Drift Monitoring":
413
  st.success("βœ… Drift report generated!")
414
  st.session_state["drift_done"] = True
415
  else:
416
- st.warning("⚠️ Not enough data in live DB.")
417
  except Exception as e:
418
  st.error(f"⚠️ Error running drift: {e}")
419
 
@@ -424,12 +198,13 @@ elif page == "Drift Monitoring":
424
  components.html(html, height=800, scrolling=True)
425
 
426
  st.markdown("---")
 
427
 
428
  if st.session_state.get("drift_done", False):
429
- if st.button("⚑ Retrain Model"):
430
  with st.spinner("Retraining model..."):
431
  try:
432
  retrain()
433
- st.success("βœ… Model retrained successfully!")
434
  except Exception as e:
435
  st.error(f"❌ Error: {e}")
 
17
  from src.explanability.shap_explainer import ShapExplainer
18
  from src.monitoring.db import save_to_db
19
 
20
+ st.set_page_config(page_title="Fraud Guard", layout="wide")
 
21
 
22
  # --- Initialize Session State for Auto-Fill Buttons ---
23
+ if "t_time" not in st.session_state: st.session_state.t_time = 10000.0
24
+ if "t_amount" not in st.session_state: st.session_state.t_amount = 100.0
25
+ for i in range(1, 29):
26
+ if f"v_{i}" not in st.session_state: st.session_state[f"v_{i}"] = 0.0
 
 
 
27
 
28
  def generate_sample(is_fraud=False):
29
  """Fills the UI with either a normal transaction or a simulated fraud attack"""
 
32
  {
33
  "Time": 406.0, "Amount": 0.00,
34
  "V": [-2.312, 1.951, -1.609, 3.997, -0.522, -1.426, -2.537, 1.391, -2.770, -2.772,
35
+ 3.202, -2.899, -0.595, -4.289, 0.389, -1.140, -2.830, -0.016, 0.416, 0.126,
36
+ 0.517, -0.035, -0.465, 0.320, 0.044, 0.177, 0.261, -0.143]
37
  },
38
  {
39
  "Time": 12500.0, "Amount": 99.99,
40
  "V": [-0.95, 0.52, -1.53, 0.85, -0.21, 0.11, -0.45, 0.22, -0.63, -1.05,
41
+ 1.20, -1.55, 0.30, -2.01, 0.10, -0.55, -1.22, 0.20, 0.45, -0.10,
42
+ 0.25, 0.15, -0.12, 0.05, 0.22, -0.15, 0.02, 0.05]
43
  },
44
  {
45
  "Time": 4462.0, "Amount": 1.00,
46
  "V": [-2.303, 1.759, -0.359, 2.330, -0.821, -0.075, -0.560, 1.214, -1.385, -2.776,
47
+ 3.231, -2.719, -1.059, -3.535, -1.583, -1.488, -2.573, -0.739, 0.380, -0.430,
48
  -0.294, -0.932, 0.172, -0.087, -0.156, -0.542, 0.039, -0.153]
49
  }
50
  ]
51
+
52
  chosen_fraud = random.choice(fraud_database)
53
+
54
  st.session_state.t_time = chosen_fraud["Time"]
55
  st.session_state.t_amount = chosen_fraud["Amount"]
56
  for i in range(1, 29):
57
+ st.session_state[f"v_{i}"] = chosen_fraud["V"][i-1]
58
+
59
  else:
60
  st.session_state.t_time = random.uniform(100, 150000)
61
  st.session_state.t_amount = random.uniform(5, 150)
62
  for i in range(1, 29):
63
  st.session_state[f"v_{i}"] = random.uniform(-1.0, 1.0)
64
 
 
65
  # βœ… Cache ONLY heavy computation (safe)
66
  @st.cache_data(show_spinner=False)
67
  def get_prediction_and_shap(payload):
68
  response = requests.post(API_URL, json=payload, timeout=30)
69
  result = response.json()
70
+
71
  input_df = pd.DataFrame([payload])
72
  pipeline = PredictPipeline()
73
  processed_df = pipeline.preprocess(input_df)
74
+
75
  explainer = ShapExplainer()
76
  shap_values = explainer.explain(processed_df)
77
+
78
  return result, shap_values
79
 
 
80
  # --- UI Sidebar & Navigation ---
81
  page = st.sidebar.selectbox("πŸ“Œ Choose Section", ["Prediction", "Drift Monitoring"])
82
 
 
92
 
93
  with col1:
94
  st.markdown("### πŸ› οΈ Demo Controls")
 
95
  demo_col1, demo_col2 = st.columns(2)
96
  with demo_col1:
97
  if st.button("βœ… Simulate Normal User", use_container_width=True):
 
103
  st.markdown("### πŸ“₯ Transaction Input")
104
  with st.container(border=True):
105
  with st.form("transaction_form"):
106
+
107
  t_time = st.slider("Time (Sec)", 0.0, 172800.0, key="t_time")
108
  t_amount = st.slider("Amount ($)", 0.0, 5000.0, key="t_amount")
109
+
110
+ # βœ… Ensure v_data always exists
111
+ v_data = {}
112
  with st.expander("PCA Feature Vectors (V1 - V28)", expanded=False):
 
113
  for i in range(1, 29):
114
  v_data[f"V{i}"] = st.number_input(f"V{i}", key=f"v_{i}", format="%.4f")
115
+
116
  st.markdown("---")
117
  threshold = st.slider("AI Sensitivity (Threshold)", 0.05, 0.95, 0.15)
118
  submit_btn = st.form_submit_button("πŸ” Run Analysis", use_container_width=True)
119
 
120
  with col2:
121
  st.markdown("### πŸ“Š Live Telemetry & Assessment")
 
 
 
122
  if not submit_btn:
123
  st.info("Awaiting transaction payload. Click 'Simulate' then 'Run Analysis'.")
124
 
125
  if submit_btn:
126
+ payload = {"Time": st.session_state.t_time, "Amount": st.session_state.t_amount, **v_data}
127
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
  try:
129
+ with st.spinner("Analyzing threat vectors..."):
130
+ result, shap_values = get_prediction_and_shap(payload)
131
+
132
+ prob = result["fraud_probability"]
133
+ pred = 1 if prob > threshold else 0
134
+ action = "🚫 Block Transaction" if pred == 1 else "βœ… Allow Transaction"
135
+
136
+ if pred == 1:
137
+ st.error(f"🚨 FRAUD DETECTED: {action}")
138
  else:
139
+ st.success(f"βœ… TRANSACTION SAFE: {action}")
140
+
141
+ m_col1, m_col2 = st.columns(2)
142
+ m_col1.metric("Risk Level", f"{prob:.4%}")
143
+ m_col2.metric("Prediction Output", pred)
144
+
145
+ st.progress(float(prob))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  st.markdown("---")
147
+
148
+ st.subheader("🧠 Explainable AI (SHAP)")
149
+
150
+ # βœ… FIX: No caching here (prevents shap hashing error)
151
+ fig, ax = plt.subplots(figsize=(8, 4))
152
+ shap.plots.waterfall(shap_values[0], show=False)
153
+ st.pyplot(fig)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
 
155
+ except Exception as e:
156
+ st.error(f"⏳ Error connecting to API: {e}")
 
 
 
 
 
 
 
 
 
 
157
 
158
  elif page == "Drift Monitoring":
159
  st.title("πŸ“‰ Data Drift Monitoring")
160
  st.markdown("### Monitor model health & trigger verified retraining")
161
+
162
  with st.expander("πŸ› οΈ Demo Tools: Force Data Drift", expanded=True):
163
  st.write("Inject 50 heavily skewed transactions into the database to trigger a statistical drift warning.")
164
  if st.button("πŸ’‰ Inject Synthetic Drift Data", type="primary"):
 
168
  for i in range(1, 29):
169
  skewed_data[f"V{i}"] = random.uniform(-15.0, 15.0)
170
  save_to_db(skewed_data, pred=1, prob=0.99)
171
+ st.success("βœ… 50 Skewed rows injected! Now click 'Run Drift Detection' below.")
172
 
173
  st.markdown("---")
174
 
 
187
  st.success("βœ… Drift report generated!")
188
  st.session_state["drift_done"] = True
189
  else:
190
+ st.warning("⚠️ Not enough data in live DB (Needs 50 rows). Use the Demo Injector above!")
191
  except Exception as e:
192
  st.error(f"⚠️ Error running drift: {e}")
193
 
 
198
  components.html(html, height=800, scrolling=True)
199
 
200
  st.markdown("---")
201
+ st.subheader("πŸ” Human-in-the-Loop Retraining")
202
 
203
  if st.session_state.get("drift_done", False):
204
+ if st.button("⚑ Retrain Model (Requires Verified Data)"):
205
  with st.spinner("Retraining model..."):
206
  try:
207
  retrain()
208
+ st.success("βœ… Model retrained successfully with verified data!")
209
  except Exception as e:
210
  st.error(f"❌ Error: {e}")