petter2025 commited on
Commit
a5398f8
·
verified ·
1 Parent(s): 9f47de8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +185 -68
app.py CHANGED
@@ -299,10 +299,13 @@ import plotly.express as px
299
  import pandas as pd
300
  import numpy as np
301
 
 
 
 
302
  def create_simple_telemetry_plot(scenario_name: str, is_real_arf: bool = True) -> go.Figure:
303
  """
304
  MINIMAL FIX: Returns Plotly figure instead of HTML string
305
- Keeping original logic but returning Plotly figure
306
  """
307
  try:
308
  # Generate sample telemetry data
@@ -361,19 +364,17 @@ def create_simple_telemetry_plot(scenario_name: str, is_real_arf: bool = True) -
361
  line=dict(color='#ef4444', width=3)
362
  ))
363
 
364
- # Add threshold line
365
  fig.add_hline(y=threshold, line_dash="dash",
366
- line_color="#f59e0b",
367
- annotation_text="Threshold",
368
- annotation_position="top right")
369
 
370
- # Update layout - FIXED: Using 'size' not 'weight' in font
371
  fig.update_layout(
372
- title=dict(
373
- text=title,
374
- font=dict(size=18, color='#1e293b'), # FIX: Removed 'weight'
375
- x=0.5
376
- ),
377
  xaxis_title="Time",
378
  yaxis_title=y_label,
379
  height=300,
@@ -395,9 +396,13 @@ def create_simple_telemetry_plot(scenario_name: str, is_real_arf: bool = True) -
395
  )
396
  return fig
397
 
 
 
 
398
  def create_simple_impact_plot(scenario_name: str, is_real_arf: bool = True) -> go.Figure:
399
  """
400
  MINIMAL FIX: Returns Plotly figure (gauge chart) instead of HTML string
 
401
  """
402
  try:
403
  # Impact values based on scenario
@@ -411,18 +416,16 @@ def create_simple_impact_plot(scenario_name: str, is_real_arf: bool = True) -> g
411
  }
412
 
413
  impact = impact_values.get(scenario_name, 5000)
414
- savings = int(impact * 0.85)
415
 
416
- # Create gauge chart
417
  fig = go.Figure(go.Indicator(
418
- mode = "gauge+number+delta",
419
- value = impact,
420
- domain = {'x': [0, 1], 'y': [0, 1]},
421
- title = {'text': f"Revenue Impact: {scenario_name}", 'font': {'size': 16}},
422
- delta = {'reference': 0, 'position': "top", 'prefix': "Potential loss: $"},
423
- number = {'prefix': "$", 'suffix': "/hour", 'font': {'size': 28}},
424
- gauge = {
425
- 'axis': {'range': [None, impact * 1.2], 'tickwidth': 1, 'tickcolor': "darkblue"},
426
  'bar': {'color': "#ef4444"},
427
  'bgcolor': "white",
428
  'borderwidth': 2,
@@ -431,21 +434,15 @@ def create_simple_impact_plot(scenario_name: str, is_real_arf: bool = True) -> g
431
  {'range': [0, impact * 0.3], 'color': '#10b981'},
432
  {'range': [impact * 0.3, impact * 0.7], 'color': '#f59e0b'},
433
  {'range': [impact * 0.7, impact], 'color': '#ef4444'}
434
- ],
435
- 'threshold': {
436
- 'line': {'color': "red", 'width': 4},
437
- 'thickness': 0.75,
438
- 'value': impact
439
- }
440
  }
441
  ))
442
 
443
- # Update layout - FIXED: Using simpler layout without problematic font properties
444
  fig.update_layout(
445
  height=400,
446
  margin=dict(l=20, r=20, t=60, b=20),
447
- paper_bgcolor='white',
448
- font=dict(color='#1e293b')
449
  )
450
 
451
  return fig
@@ -461,31 +458,35 @@ def create_simple_impact_plot(scenario_name: str, is_real_arf: bool = True) -> g
461
  fig.update_layout(height=400)
462
  return fig
463
 
 
 
 
464
  def create_empty_plot(title: str, is_real_arf: bool = True) -> go.Figure:
465
  """
466
  MINIMAL FIX: Returns Plotly figure (placeholder) instead of HTML string
 
467
  """
468
  fig = go.Figure()
469
 
470
- # Add text annotation
471
  fig.add_annotation(
472
  x=0.5, y=0.5,
473
  text=title,
474
  showarrow=False,
475
- font=dict(size=16, color="#64748b"),
476
  xref="paper",
477
  yref="paper"
478
  )
479
 
480
  fig.update_layout(
481
- title=dict(
482
- text="Visualization Placeholder",
483
- font=dict(size=14, color="#94a3b8")
484
- ),
485
  height=300,
486
  plot_bgcolor='white',
487
- xaxis=dict(visible=False),
488
- yaxis=dict(visible=False),
489
  margin=dict(l=20, r=20, t=40, b=20)
490
  )
491
 
@@ -1125,12 +1126,15 @@ def extract_roi_multiplier(roi_result: Dict) -> float:
1125
  return 5.2
1126
 
1127
  # ===========================================
1128
- # FIXED SCENARIO UPDATE FUNCTION
1129
  # ===========================================
1130
  def update_scenario_display(scenario_name: str) -> tuple:
1131
  """
1132
- MINIMAL FIX: Returns Plotly figures, not HTML strings for visualizations
1133
- But keeps scenario_card as HTML
 
 
 
1134
  """
1135
  components = get_components()
1136
  scenarios = components["INCIDENT_SCENARIOS"]
@@ -1212,22 +1216,24 @@ def update_scenario_display(scenario_name: str) -> tuple:
1212
  """
1213
 
1214
  # Get visualizations as Plotly figures (FIXED)
1215
- telemetry_viz = create_simple_telemetry_plot(scenario_name, settings.use_true_arf)
1216
- impact_viz = create_simple_impact_plot(scenario_name, settings.use_true_arf)
1217
-
1218
- # Create timeline visualization as Plotly figure (FIXED)
1219
- timeline_viz = create_empty_plot(f"Timeline: {scenario_name}", settings.use_true_arf)
1220
 
1221
- return scenario_card_html, telemetry_viz, impact_viz, timeline_viz
1222
 
1223
  # ===========================================
1224
- # FIXED ANALYSIS FUNCTION
1225
  # ===========================================
1226
  @AsyncRunner.async_to_sync
1227
- async def run_true_arf_analysis(scenario_name: str):
1228
  """
1229
- MINIMAL FIX: Returns JSON/dict instead of HTML string
1230
- Keeping original logic but returning dict for gr.JSON()
 
 
 
 
1231
  """
1232
 
1233
  components = get_components()
@@ -1253,12 +1259,94 @@ async def run_true_arf_analysis(scenario_name: str):
1253
  # Check if we have real ARF
1254
  is_real_arf = installation["oss_installed"] or settings.use_true_arf
1255
 
1256
- # Return dict instead of HTML strings
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1257
  if is_real_arf and "real" in str(analysis_result).lower():
1258
- return {
1259
  "status": "success",
1260
  "scenario": scenario_name,
1261
- "arf_version": "3.3.9",
1262
  "analysis": {
1263
  "detected": True,
1264
  "confidence": 94,
@@ -1272,38 +1360,67 @@ async def run_true_arf_analysis(scenario_name: str):
1272
  "recall": {"status": "active", "similar_incidents": 3},
1273
  "decision": {"status": "active", "healing_intent_created": True}
1274
  },
1275
- "boundary_note": "OSS analysis complete → Ready for Enterprise execution"
1276
  }
1277
  else:
1278
- return {
1279
  "status": "mock_analysis",
1280
  "scenario": scenario_name,
1281
  "arf_version": "mock",
1282
  "analysis": {
1283
- "detected": False,
1284
- "confidence": 0,
1285
- "similar_incidents": 0,
1286
- "healing_intent_created": False,
1287
- "recommended_action": "Install ARF OSS for real analysis",
1288
- "estimated_recovery": "N/A"
1289
  },
1290
  "agents": {
1291
- "detection": {"status": "inactive", "confidence": 0},
1292
- "recall": {"status": "inactive", "similar_incidents": 0},
1293
- "decision": {"status": "inactive", "healing_intent_created": False}
1294
  },
1295
- "boundary_note": "Mock analysis - install agentic-reliability-framework==3.3.7"
1296
  }
1297
 
 
 
 
 
 
1298
  except Exception as e:
1299
  logger.error(f"True ARF analysis failed: {e}")
1300
- return {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1301
  "status": "error",
1302
  "error": str(e),
1303
  "scenario": scenario_name,
1304
  "arf_version": "3.3.9",
1305
  "recommendation": "Check ARF installation"
1306
  }
 
 
1307
 
1308
  # ===========================================
1309
  # FIXED EXECUTION FUNCTION
@@ -1570,7 +1687,7 @@ def create_demo_interface():
1570
  outputs=[scenario_card, telemetry_viz, impact_viz, timeline_viz]
1571
  )
1572
 
1573
- # Run OSS Analysis
1574
  oss_btn.click(
1575
  fn=run_true_arf_analysis,
1576
  inputs=[scenario_dropdown],
 
299
  import pandas as pd
300
  import numpy as np
301
 
302
+ # ===========================================
303
+ # SURGICAL FIX 1: create_simple_telemetry_plot()
304
+ # ===========================================
305
  def create_simple_telemetry_plot(scenario_name: str, is_real_arf: bool = True) -> go.Figure:
306
  """
307
  MINIMAL FIX: Returns Plotly figure instead of HTML string
308
+ FIXED: Removed font weight properties, returns valid Plotly figure
309
  """
310
  try:
311
  # Generate sample telemetry data
 
364
  line=dict(color='#ef4444', width=3)
365
  ))
366
 
367
+ # Add threshold line - FIXED: Simplified without problematic properties
368
  fig.add_hline(y=threshold, line_dash="dash",
369
+ line_color="#f59e0b")
 
 
370
 
371
+ # Update layout - FIXED: Using only 'size' not 'weight' in font
372
  fig.update_layout(
373
+ title={
374
+ 'text': title,
375
+ 'font': {'size': 18, 'color': '#1e293b'},
376
+ 'x': 0.5
377
+ },
378
  xaxis_title="Time",
379
  yaxis_title=y_label,
380
  height=300,
 
396
  )
397
  return fig
398
 
399
+ # ===========================================
400
+ # SURGICAL FIX 2: create_simple_impact_plot()
401
+ # ===========================================
402
  def create_simple_impact_plot(scenario_name: str, is_real_arf: bool = True) -> go.Figure:
403
  """
404
  MINIMAL FIX: Returns Plotly figure (gauge chart) instead of HTML string
405
+ FIXED: Removed problematic font properties, simplified gauge
406
  """
407
  try:
408
  # Impact values based on scenario
 
416
  }
417
 
418
  impact = impact_values.get(scenario_name, 5000)
 
419
 
420
+ # Create gauge chart - FIXED: Simplified without problematic properties
421
  fig = go.Figure(go.Indicator(
422
+ mode="gauge+number",
423
+ value=impact,
424
+ domain={'x': [0, 1], 'y': [0, 1]},
425
+ title={'text': f"Revenue Impact: ${impact:,}/hour"},
426
+ number={'prefix': "$", 'suffix': "/hour"},
427
+ gauge={
428
+ 'axis': {'range': [None, impact * 1.2]},
 
429
  'bar': {'color': "#ef4444"},
430
  'bgcolor': "white",
431
  'borderwidth': 2,
 
434
  {'range': [0, impact * 0.3], 'color': '#10b981'},
435
  {'range': [impact * 0.3, impact * 0.7], 'color': '#f59e0b'},
436
  {'range': [impact * 0.7, impact], 'color': '#ef4444'}
437
+ ]
 
 
 
 
 
438
  }
439
  ))
440
 
441
+ # Update layout - FIXED: Simplified layout
442
  fig.update_layout(
443
  height=400,
444
  margin=dict(l=20, r=20, t=60, b=20),
445
+ paper_bgcolor='white'
 
446
  )
447
 
448
  return fig
 
458
  fig.update_layout(height=400)
459
  return fig
460
 
461
+ # ===========================================
462
+ # SURGICAL FIX 3: create_empty_plot()
463
+ # ===========================================
464
  def create_empty_plot(title: str, is_real_arf: bool = True) -> go.Figure:
465
  """
466
  MINIMAL FIX: Returns Plotly figure (placeholder) instead of HTML string
467
+ FIXED: Simplified font properties
468
  """
469
  fig = go.Figure()
470
 
471
+ # Add text annotation - FIXED: Simplified font
472
  fig.add_annotation(
473
  x=0.5, y=0.5,
474
  text=title,
475
  showarrow=False,
476
+ font={'size': 16, 'color': "#64748b"},
477
  xref="paper",
478
  yref="paper"
479
  )
480
 
481
  fig.update_layout(
482
+ title={
483
+ 'text': "Visualization Placeholder",
484
+ 'font': {'size': 14, 'color': "#94a3b8"}
485
+ },
486
  height=300,
487
  plot_bgcolor='white',
488
+ xaxis={'visible': False},
489
+ yaxis={'visible': False},
490
  margin=dict(l=20, r=20, t=40, b=20)
491
  )
492
 
 
1126
  return 5.2
1127
 
1128
  # ===========================================
1129
+ # SURGICAL FIX 4: update_scenario_display()
1130
  # ===========================================
1131
  def update_scenario_display(scenario_name: str) -> tuple:
1132
  """
1133
+ FIXED: Returns exactly 4 values as expected by UI:
1134
+ 1. scenario_card_html (HTML string)
1135
+ 2. telemetry_fig (Plotly figure from create_simple_telemetry_plot())
1136
+ 3. impact_fig (Plotly figure from create_simple_impact_plot())
1137
+ 4. timeline_fig (Plotly figure from create_empty_plot())
1138
  """
1139
  components = get_components()
1140
  scenarios = components["INCIDENT_SCENARIOS"]
 
1216
  """
1217
 
1218
  # Get visualizations as Plotly figures (FIXED)
1219
+ telemetry_fig = create_simple_telemetry_plot(scenario_name, settings.use_true_arf)
1220
+ impact_fig = create_simple_impact_plot(scenario_name, settings.use_true_arf)
1221
+ timeline_fig = create_empty_plot(f"Timeline: {scenario_name}", settings.use_true_arf)
 
 
1222
 
1223
+ return scenario_card_html, telemetry_fig, impact_fig, timeline_fig
1224
 
1225
  # ===========================================
1226
+ # SURGICAL FIX 5: run_true_arf_analysis()
1227
  # ===========================================
1228
  @AsyncRunner.async_to_sync
1229
+ async def run_true_arf_analysis(scenario_name: str) -> tuple:
1230
  """
1231
+ FIXED: Returns exactly 5 values as expected by UI:
1232
+ 1. detection_html (HTML string)
1233
+ 2. recall_html (HTML string)
1234
+ 3. decision_html (HTML string)
1235
+ 4. oss_results_dict (Python dict for JSON display)
1236
+ 5. incident_table_html (HTML string)
1237
  """
1238
 
1239
  components = get_components()
 
1259
  # Check if we have real ARF
1260
  is_real_arf = installation["oss_installed"] or settings.use_true_arf
1261
 
1262
+ # Create HTML for active agents
1263
+ boundary_color = boundaries["oss"]["color"] if is_real_arf else "#f59e0b"
1264
+ boundary_text = boundaries["oss"]["label"] if is_real_arf else "Mock ARF"
1265
+
1266
+ # Detection Agent HTML
1267
+ detection_html = f"""
1268
+ <div style="border: 2px solid {boundary_color}; border-radius: 14px; padding: 18px;
1269
+ background: linear-gradient(135deg, {boundary_color}10 0%, #ffffff 100%);
1270
+ text-align: center; flex: 1; margin: 5px; min-height: 180px;">
1271
+ <div style="font-size: 32px; margin-bottom: 10px; color: {boundary_color};">🕵️‍♂️</div>
1272
+ <div style="width: 100%;">
1273
+ <h4 style="margin: 0 0 8px 0; font-size: 16px; color: #1e293b;">Detection Agent</h4>
1274
+ <p style="font-size: 13px; color: #64748b; margin-bottom: 12px; line-height: 1.4;">
1275
+ Anomaly detected with 94% confidence
1276
+ </p>
1277
+ <div style="display: flex; justify-content: space-around; margin-bottom: 12px;">
1278
+ <span style="font-size: 11px; padding: 3px 8px; background: {boundary_color}20;
1279
+ border-radius: 6px; color: {boundary_color}; font-weight: 500;">
1280
+ Status: Active
1281
+ </span>
1282
+ </div>
1283
+ <div style="display: inline-block; padding: 5px 14px; background: {boundary_color};
1284
+ border-radius: 20px; font-size: 12px; font-weight: bold; color: white;
1285
+ text-transform: uppercase; letter-spacing: 0.5px;">
1286
+ DETECTED
1287
+ </div>
1288
+ </div>
1289
+ </div>
1290
+ """
1291
+
1292
+ # Recall Agent HTML
1293
+ recall_html = f"""
1294
+ <div style="border: 2px solid {boundary_color}; border-radius: 14px; padding: 18px;
1295
+ background: linear-gradient(135deg, {boundary_color}10 0%, #ffffff 100%);
1296
+ text-align: center; flex: 1; margin: 5px; min-height: 180px;">
1297
+ <div style="font-size: 32px; margin-bottom: 10px; color: {boundary_color};">🧠</div>
1298
+ <div style="width: 100%;">
1299
+ <h4 style="margin: 0 0 8px 0; font-size: 16px; color: #1e293b;">Recall Agent</h4>
1300
+ <p style="font-size: 13px; color: #64748b; margin-bottom: 12px; line-height: 1.4;">
1301
+ 3 similar incidents found in RAG memory
1302
+ </p>
1303
+ <div style="display: flex; justify-content: space-around; margin-bottom: 12px;">
1304
+ <span style="font-size: 11px; padding: 3px 8px; background: {boundary_color}20;
1305
+ border-radius: 6px; color: {boundary_color}; font-weight: 500;">
1306
+ Status: Active
1307
+ </span>
1308
+ </div>
1309
+ <div style="display: inline-block; padding: 5px 14px; background: {boundary_color};
1310
+ border-radius: 20px; font-size: 12px; font-weight: bold; color: white;
1311
+ text-transform: uppercase; letter-spacing: 0.5px;">
1312
+ RECALLED
1313
+ </div>
1314
+ </div>
1315
+ </div>
1316
+ """
1317
+
1318
+ # Decision Agent HTML
1319
+ decision_html = f"""
1320
+ <div style="border: 2px solid {boundary_color}; border-radius: 14px; padding: 18px;
1321
+ background: linear-gradient(135deg, {boundary_color}10 0%, #ffffff 100%);
1322
+ text-align: center; flex: 1; margin: 5px; min-height: 180px;">
1323
+ <div style="font-size: 32px; margin-bottom: 10px; color: {boundary_color};">🎯</div>
1324
+ <div style="width: 100%;">
1325
+ <h4 style="margin: 0 0 8px 0; font-size: 16px; color: #1e293b;">Decision Agent</h4>
1326
+ <p style="font-size: 13px; color: #64748b; margin-bottom: 12px; line-height: 1.4;">
1327
+ HealingIntent created: Scale Redis cluster
1328
+ </p>
1329
+ <div style="display: flex; justify-content: space-around; margin-bottom: 12px;">
1330
+ <span style="font-size: 11px; padding: 3px 8px; background: {boundary_color}20;
1331
+ border-radius: 6px; color: {boundary_color}; font-weight: 500;">
1332
+ Status: Active
1333
+ </span>
1334
+ </div>
1335
+ <div style="display: inline-block; padding: 5px 14px; background: {boundary_color};
1336
+ border-radius: 20px; font-size: 12px; font-weight: bold; color: white;
1337
+ text-transform: uppercase; letter-spacing: 0.5px;">
1338
+ DECIDED
1339
+ </div>
1340
+ </div>
1341
+ </div>
1342
+ """
1343
+
1344
+ # OSS Results Dict for JSON display
1345
  if is_real_arf and "real" in str(analysis_result).lower():
1346
+ oss_results_dict = {
1347
  "status": "success",
1348
  "scenario": scenario_name,
1349
+ "arf_version": boundaries["oss"]["version"],
1350
  "analysis": {
1351
  "detected": True,
1352
  "confidence": 94,
 
1360
  "recall": {"status": "active", "similar_incidents": 3},
1361
  "decision": {"status": "active", "healing_intent_created": True}
1362
  },
1363
+ "boundary_note": f"OSS analysis complete → Ready for Enterprise execution"
1364
  }
1365
  else:
1366
+ oss_results_dict = {
1367
  "status": "mock_analysis",
1368
  "scenario": scenario_name,
1369
  "arf_version": "mock",
1370
  "analysis": {
1371
+ "detected": True,
1372
+ "confidence": 94,
1373
+ "similar_incidents": 3,
1374
+ "healing_intent_created": True,
1375
+ "recommended_action": "Scale Redis cluster from 3 to 5 nodes",
1376
+ "estimated_recovery": "12 minutes"
1377
  },
1378
  "agents": {
1379
+ "detection": {"status": "active", "confidence": 94},
1380
+ "recall": {"status": "active", "similar_incidents": 3},
1381
+ "decision": {"status": "active", "healing_intent_created": True}
1382
  },
1383
+ "boundary_note": f"Mock analysis - {boundary_text}"
1384
  }
1385
 
1386
+ # Incident Table HTML
1387
+ incident_table_html = get_audit_manager().get_incident_table()
1388
+
1389
+ return detection_html, recall_html, decision_html, oss_results_dict, incident_table_html
1390
+
1391
  except Exception as e:
1392
  logger.error(f"True ARF analysis failed: {e}")
1393
+
1394
+ # Return error state with proper types
1395
+ error_html = f"""
1396
+ <div style="border: 2px solid #ef4444; border-radius: 14px; padding: 18px;
1397
+ background: linear-gradient(135deg, #fef2f2 0%, #ffffff 100%);
1398
+ text-align: center; flex: 1; margin: 5px; min-height: 180px;">
1399
+ <div style="font-size: 32px; margin-bottom: 10px; color: #ef4444;">❌</div>
1400
+ <div style="width: 100%;">
1401
+ <h4 style="margin: 0 0 8px 0; font-size: 16px; color: #1e293b;">Analysis Error</h4>
1402
+ <p style="font-size: 13px; color: #64748b; margin-bottom: 12px; line-height: 1.4;">
1403
+ Failed to analyze incident
1404
+ </p>
1405
+ <div style="display: flex; justify-content: space-around; margin-bottom: 12px;">
1406
+ <span style="font-size: 11px; padding: 3px 8px; background: #ef4444;
1407
+ border-radius: 6px; color: white; font-weight: 500;">
1408
+ Status: Error
1409
+ </span>
1410
+ </div>
1411
+ </div>
1412
+ </div>
1413
+ """
1414
+
1415
+ error_dict = {
1416
  "status": "error",
1417
  "error": str(e),
1418
  "scenario": scenario_name,
1419
  "arf_version": "3.3.9",
1420
  "recommendation": "Check ARF installation"
1421
  }
1422
+
1423
+ return error_html, error_html, error_html, error_dict, "Error loading incident table"
1424
 
1425
  # ===========================================
1426
  # FIXED EXECUTION FUNCTION
 
1687
  outputs=[scenario_card, telemetry_viz, impact_viz, timeline_viz]
1688
  )
1689
 
1690
+ # Run OSS Analysis - FIXED: Now receives 5 values
1691
  oss_btn.click(
1692
  fn=run_true_arf_analysis,
1693
  inputs=[scenario_dropdown],