petter2025 commited on
Commit
b987839
·
verified ·
1 Parent(s): 05252c9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +236 -49
app.py CHANGED
@@ -30,6 +30,16 @@ logger = logging.getLogger(__name__)
30
  # Add parent directory to path
31
  sys.path.insert(0, str(Path(__file__).parent))
32
 
 
 
 
 
 
 
 
 
 
 
33
  # ===========================================
34
  # IMPORT UTILITY CLASSES FIRST
35
  # ===========================================
@@ -207,10 +217,10 @@ class AsyncRunner:
207
  return wrapper
208
 
209
  # ===========================================
210
- # SIMPLE SETTINGS
211
  # ===========================================
212
  class Settings:
213
- """Simple settings class"""
214
  def __init__(self):
215
  self.arf_mode = "demo"
216
  self.use_true_arf = True
@@ -220,6 +230,7 @@ class Settings:
220
  self.show_boundaries = True
221
  self.architectural_honesty = True
222
  self.engineer_annual_cost = 200000
 
223
 
224
  settings = Settings()
225
 
@@ -755,10 +766,10 @@ def get_inactive_agent_html(agent_name: str, description: str, is_real_arf: bool
755
  """
756
 
757
  # ===========================================
758
- # IMPORT MODULAR COMPONENTS
759
  # ===========================================
760
  def import_components() -> Dict[str, Any]:
761
- """Safely import all components with proper error handling"""
762
  components = {
763
  "all_available": False,
764
  "error": None,
@@ -835,27 +846,79 @@ def import_components() -> Dict[str, Any]:
835
  }
836
  components["DemoOrchestrator"] = MockOrchestrator
837
 
838
- # Try to import ROI calculator
839
  try:
840
  from core.calculators import EnhancedROICalculator
841
  components["EnhancedROICalculator"] = EnhancedROICalculator()
 
842
  except ImportError:
843
- class MockCalculator:
844
- def calculate_comprehensive_roi(self, **kwargs):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
845
  return {
846
  "status": "✅ Calculated Successfully",
 
 
 
847
  "summary": {
848
- "your_annual_impact": "$1,530,000",
849
- "potential_savings": "$1,254,600",
850
- "enterprise_cost": "$625,000",
851
- "roi_multiplier": "5.2×",
852
- "payback_months": "6.0",
853
- "annual_roi_percentage": "420%",
854
  "boundary_context": "Based on OSS analysis + simulated Enterprise execution"
855
  },
856
- "boundary_note": "ROI calculation includes OSS advisory value and simulated Enterprise execution benefits"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
857
  }
858
- components["EnhancedROICalculator"] = MockCalculator()
 
 
 
 
 
 
 
 
 
 
859
 
860
  # Try to import visualization engine
861
  try:
@@ -896,6 +959,16 @@ def import_components() -> Dict[str, Any]:
896
  "boundary_note": "OSS analysis only - execution requires Enterprise"
897
  }
898
  }
 
 
 
 
 
 
 
 
 
 
899
 
900
  return components
901
 
@@ -910,10 +983,10 @@ def get_components() -> Dict[str, Any]:
910
  return _components
911
 
912
  # ===========================================
913
- # AUDIT TRAIL MANAGER
914
  # ===========================================
915
  class AuditTrailManager:
916
- """Enhanced audit trail manager with boundary tracking"""
917
 
918
  def __init__(self):
919
  self.executions = []
@@ -961,8 +1034,114 @@ class AuditTrailManager:
961
  logger.info(f"📝 Incident analysis recorded: {scenario_name}")
962
  return record
963
 
964
- def get_execution_table(self):
965
- """Get executions as HTML table"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
966
  if not self.executions:
967
  return """
968
  <div style="text-align: center; padding: 30px; color: #64748b;">
@@ -1016,8 +1195,8 @@ class AuditTrailManager:
1016
  </div>
1017
  """
1018
 
1019
- def get_incident_table(self):
1020
- """Get incidents as HTML table"""
1021
  if not self.incidents:
1022
  return """
1023
  <div style="text-align: center; padding: 30px; color: #64748b;">
@@ -1223,7 +1402,7 @@ def update_scenario_display(scenario_name: str) -> tuple:
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:
@@ -1233,7 +1412,7 @@ async def run_true_arf_analysis(scenario_name: str) -> tuple:
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()
@@ -1383,10 +1562,10 @@ async def run_true_arf_analysis(scenario_name: str) -> tuple:
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}")
@@ -1420,15 +1599,20 @@ async def run_true_arf_analysis(scenario_name: str) -> tuple:
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
1427
  # ===========================================
1428
  def execute_enterprise_healing(scenario_name, approval_required, mcp_mode_value):
1429
  """
1430
  MINIMAL FIX: Returns proper data types matching UI expectations
1431
- Keeping original return structure but fixing types
1432
  """
1433
  import gradio as gr
1434
 
@@ -1535,10 +1719,10 @@ def execute_enterprise_healing(scenario_name, approval_required, mcp_mode_value)
1535
  ]
1536
  }
1537
 
1538
- # Get execution table HTML
1539
- execution_table = get_audit_manager().get_execution_table()
1540
 
1541
- return approval_display, enterprise_results, execution_table
1542
 
1543
  # ===========================================
1544
  # FIXED ROI FUNCTION
@@ -1608,7 +1792,7 @@ def calculate_roi(scenario_name, monthly_incidents, team_size):
1608
  return roi_result, fig
1609
 
1610
  # ===========================================
1611
- # CREATE DEMO INTERFACE - UNCHANGED
1612
  # ===========================================
1613
  def create_demo_interface():
1614
  """Create demo interface using modular components with boundary awareness"""
@@ -1687,7 +1871,7 @@ def create_demo_interface():
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],
@@ -1697,7 +1881,7 @@ def create_demo_interface():
1697
  ]
1698
  )
1699
 
1700
- # Execute Enterprise Healing
1701
  enterprise_btn.click(
1702
  fn=execute_enterprise_healing,
1703
  inputs=[scenario_dropdown, approval_toggle, mcp_mode],
@@ -1892,9 +2076,9 @@ def create_demo_interface():
1892
  # Update the enterprise_results_display to include demo completion info
1893
  enterprise_results["demo_completion_message"] = demo_message
1894
 
1895
- # Get updated tables
1896
- incident_table_data = get_audit_manager().get_incident_table()
1897
- execution_table_data = get_audit_manager().get_execution_table()
1898
 
1899
  # Combine all results
1900
  return (
@@ -1903,8 +2087,8 @@ def create_demo_interface():
1903
  oss_result[3], # 1 output: oss_results_display
1904
  enterprise_results, # 1 output: enterprise_results_display
1905
  demo_message, # 1 output: approval_display
1906
- incident_table_data, # 1 output: incident_table
1907
- execution_table_data # 1 output: execution_table
1908
  )
1909
 
1910
  # FIXED: demo_btn.click with correct output count
@@ -1929,10 +2113,10 @@ def create_demo_interface():
1929
  outputs=[roi_output, roi_chart]
1930
  )
1931
 
1932
- # Update ROI scenario
1933
  roi_scenario_dropdown.change(
1934
- fn=lambda x: get_components()["EnhancedROICalculator"]().calculate_comprehensive_roi(),
1935
- inputs=[],
1936
  outputs=[roi_output]
1937
  )
1938
 
@@ -1949,18 +2133,21 @@ def create_demo_interface():
1949
  outputs=[roi_chart]
1950
  )
1951
 
1952
- # Audit Trail Functions
1953
  def refresh_audit_trail():
1954
- """Refresh audit trail tables"""
1955
  return (
1956
- get_audit_manager().get_execution_table(),
1957
- get_audit_manager().get_incident_table()
1958
  )
1959
 
1960
  def clear_audit_trail():
1961
- """Clear audit trail"""
1962
  get_audit_manager().clear()
1963
- return [], []
 
 
 
1964
 
1965
  def export_audit_trail():
1966
  """Export audit trail as JSON"""
 
30
  # Add parent directory to path
31
  sys.path.insert(0, str(Path(__file__).parent))
32
 
33
+ # ===========================================
34
+ # FIX FOR ASYNC EVENT LOOP ISSUES
35
+ # ===========================================
36
+ try:
37
+ import nest_asyncio
38
+ nest_asyncio.apply()
39
+ logger.info("✅ Applied nest_asyncio for async event loop compatibility")
40
+ except ImportError:
41
+ logger.warning("⚠️ nest_asyncio not available, async operations may have issues")
42
+
43
  # ===========================================
44
  # IMPORT UTILITY CLASSES FIRST
45
  # ===========================================
 
217
  return wrapper
218
 
219
  # ===========================================
220
+ # SIMPLE SETTINGS - FIXED: Added missing attribute
221
  # ===========================================
222
  class Settings:
223
+ """Simple settings class - FIXED: Added default_savings_rate"""
224
  def __init__(self):
225
  self.arf_mode = "demo"
226
  self.use_true_arf = True
 
230
  self.show_boundaries = True
231
  self.architectural_honesty = True
232
  self.engineer_annual_cost = 200000
233
+ self.default_savings_rate = 0.25 # FIXED: Added missing attribute
234
 
235
  settings = Settings()
236
 
 
766
  """
767
 
768
  # ===========================================
769
+ # IMPORT MODULAR COMPONENTS - FIXED: Added MockEnhancedROICalculator
770
  # ===========================================
771
  def import_components() -> Dict[str, Any]:
772
+ """Safely import all components with proper error handling - FIXED: Added mock ROI calculator"""
773
  components = {
774
  "all_available": False,
775
  "error": None,
 
846
  }
847
  components["DemoOrchestrator"] = MockOrchestrator
848
 
849
+ # FIXED: EnhancedROICalculator with proper mock fallback
850
  try:
851
  from core.calculators import EnhancedROICalculator
852
  components["EnhancedROICalculator"] = EnhancedROICalculator()
853
+ logger.info("✅ Real EnhancedROICalculator loaded")
854
  except ImportError:
855
+ # Create comprehensive mock ROI calculator
856
+ class MockEnhancedROICalculator:
857
+ """Mock ROI calculator for demo purposes - FIXED to prevent KeyError"""
858
+
859
+ def calculate_comprehensive_roi(self, scenario_name=None, monthly_incidents=15, team_size=5, **kwargs):
860
+ """Calculate comprehensive ROI metrics with realistic mock data"""
861
+ from datetime import datetime
862
+
863
+ # Mock ROI calculation with realistic values
864
+ impact_map = {
865
+ "Cache Miss Storm": 8500,
866
+ "Database Connection Pool Exhaustion": 4200,
867
+ "Kubernetes Memory Leak": 5500,
868
+ "API Rate Limit Storm": 3800,
869
+ "Network Partition": 12000,
870
+ "Storage I/O Saturation": 6800
871
+ }
872
+
873
+ impact_per_incident = impact_map.get(scenario_name or "Cache Miss Storm", 5000)
874
+ annual_impact = impact_per_incident * monthly_incidents * 12
875
+ potential_savings = int(annual_impact * 0.82)
876
+ enterprise_cost = 625000
877
+ roi_multiplier = round(potential_savings / enterprise_cost, 1)
878
+ payback_months = round((enterprise_cost / (potential_savings / 12)), 1)
879
+
880
  return {
881
  "status": "✅ Calculated Successfully",
882
+ "scenario": scenario_name or "Cache Miss Storm",
883
+ "timestamp": datetime.now().isoformat(),
884
+ "calculator": "MockEnhancedROICalculator",
885
  "summary": {
886
+ "your_annual_impact": f"${annual_impact:,}",
887
+ "potential_savings": f"${potential_savings:,}",
888
+ "enterprise_cost": f"${enterprise_cost:,}",
889
+ "roi_multiplier": f"{roi_multiplier}×",
890
+ "payback_months": f"{payback_months}",
891
+ "annual_roi_percentage": f"{int((potential_savings - enterprise_cost) / enterprise_cost * 100)}%",
892
  "boundary_context": "Based on OSS analysis + simulated Enterprise execution"
893
  },
894
+ "breakdown": {
895
+ "direct_cost_savings": f"${int(potential_savings * 0.7):,}",
896
+ "productivity_gains": f"${int(potential_savings * 0.2):,}",
897
+ "risk_reduction": f"${int(potential_savings * 0.1):,}"
898
+ },
899
+ "annual_projection": {
900
+ "incidents_prevented": monthly_incidents * 12,
901
+ "annual_savings": f"${potential_savings:,}",
902
+ "roi": f"{roi_multiplier}×"
903
+ },
904
+ "notes": [
905
+ "📊 ROI calculation using mock data",
906
+ "💡 Real enterprise ROI includes additional factors",
907
+ "🔒 Full ROI requires Enterprise edition",
908
+ f"📈 Based on {monthly_incidents} incidents/month"
909
+ ]
910
  }
911
+
912
+ def get_roi_visualization_data(self):
913
+ """Get data for ROI visualization"""
914
+ return {
915
+ "labels": ["Direct Savings", "Productivity", "Risk Reduction", "Upsell"],
916
+ "values": [65, 20, 10, 5],
917
+ "colors": ["#10b981", "#3b82f6", "#8b5cf6", "#f59e0b"]
918
+ }
919
+
920
+ components["EnhancedROICalculator"] = MockEnhancedROICalculator()
921
+ logger.info("✅ Mock EnhancedROICalculator created (preventing KeyError)")
922
 
923
  # Try to import visualization engine
924
  try:
 
959
  "boundary_note": "OSS analysis only - execution requires Enterprise"
960
  }
961
  }
962
+
963
+ # Ensure EnhancedROICalculator exists
964
+ if "EnhancedROICalculator" not in components:
965
+ class MinimalROICalculator:
966
+ def calculate_comprehensive_roi(self, **kwargs):
967
+ return {
968
+ "status": "✅ Minimal ROI Calculation",
969
+ "summary": {"roi_multiplier": "5.2×"}
970
+ }
971
+ components["EnhancedROICalculator"] = MinimalROICalculator()
972
 
973
  return components
974
 
 
983
  return _components
984
 
985
  # ===========================================
986
+ # AUDIT TRAIL MANAGER - FIXED: Returns DataFrames instead of HTML
987
  # ===========================================
988
  class AuditTrailManager:
989
+ """Enhanced audit trail manager with boundary tracking - FIXED to return DataFrames"""
990
 
991
  def __init__(self):
992
  self.executions = []
 
1034
  logger.info(f"📝 Incident analysis recorded: {scenario_name}")
1035
  return record
1036
 
1037
+ def get_execution_dataframe(self) -> pd.DataFrame:
1038
+ """
1039
+ FIXED: Returns pandas DataFrame for Gradio DataFrame component
1040
+ """
1041
+ try:
1042
+ if not self.executions:
1043
+ # Return empty DataFrame with correct columns
1044
+ return pd.DataFrame(columns=[
1045
+ "Execution ID", "Scenario", "Status", "Mode",
1046
+ "Start Time", "End Time", "Duration", "Boundary"
1047
+ ])
1048
+
1049
+ # Build DataFrame from executions
1050
+ data = []
1051
+ for i, execution in enumerate(self.executions):
1052
+ # Extract execution ID from result or generate one
1053
+ exec_id = execution.get("result", {}).get("execution_id", f"exec_{i}")
1054
+ status = "Success" if "success" in str(execution.get("result", {})).lower() else "Failed"
1055
+ mode = execution.get("mode", "unknown")
1056
+ scenario = execution.get("scenario", "Unknown")
1057
+ timestamp = execution.get("timestamp", "")
1058
+ boundary = execution.get("boundary_context", "Unknown")
1059
+
1060
+ # Extract end time from telemetry if available
1061
+ end_time = execution.get("result", {}).get("telemetry", {}).get("end_time", "")
1062
+ duration = "12m" # Mock duration
1063
+
1064
+ data.append({
1065
+ "Execution ID": exec_id,
1066
+ "Scenario": scenario,
1067
+ "Status": status,
1068
+ "Mode": mode,
1069
+ "Start Time": timestamp[:19] if timestamp else "", # Format: YYYY-MM-DD HH:MM:SS
1070
+ "End Time": end_time[:19] if end_time else "",
1071
+ "Duration": duration,
1072
+ "Boundary": boundary
1073
+ })
1074
+
1075
+ df = pd.DataFrame(data)
1076
+
1077
+ # Sort by time (newest first)
1078
+ if not df.empty and "Start Time" in df.columns:
1079
+ df = df.sort_values("Start Time", ascending=False)
1080
+
1081
+ return df
1082
+
1083
+ except Exception as e:
1084
+ logger.error(f"Error creating execution DataFrame: {e}")
1085
+ # Return empty DataFrame as fallback
1086
+ return pd.DataFrame(columns=[
1087
+ "Error", "Message"
1088
+ ]).from_records([{"Error": "DataFrame Error", "Message": str(e)}])
1089
+
1090
+ def get_incident_dataframe(self) -> pd.DataFrame:
1091
+ """
1092
+ FIXED: Returns pandas DataFrame for Gradio DataFrame component
1093
+ """
1094
+ try:
1095
+ if not self.incidents:
1096
+ # Return empty DataFrame with correct columns
1097
+ return pd.DataFrame(columns=[
1098
+ "Scenario", "Status", "Boundary", "Time",
1099
+ "Confidence", "Action", "Target"
1100
+ ])
1101
+
1102
+ # Build DataFrame from incidents
1103
+ data = []
1104
+ for i, incident in enumerate(self.incidents):
1105
+ scenario = incident.get("scenario", "Unknown")
1106
+ analysis = incident.get("analysis", {})
1107
+ status = analysis.get("status", "analyzed").capitalize()
1108
+ boundary = incident.get("boundary_context", "OSS analysis")
1109
+ timestamp = incident.get("timestamp", "")
1110
+ time_display = timestamp[11:19] if len(timestamp) > 11 else ""
1111
+
1112
+ # Extract analysis details
1113
+ healing_intent = analysis.get("oss_analysis", {}).get("analysis", {}).get("decision", {})
1114
+ confidence = healing_intent.get("confidence", 0.85)
1115
+ action = healing_intent.get("action", "Analysis")
1116
+ target = healing_intent.get("target", "system")
1117
+
1118
+ data.append({
1119
+ "Scenario": scenario,
1120
+ "Status": status,
1121
+ "Boundary": boundary,
1122
+ "Time": time_display,
1123
+ "Confidence": f"{confidence * 100:.1f}%",
1124
+ "Action": action,
1125
+ "Target": target
1126
+ })
1127
+
1128
+ df = pd.DataFrame(data)
1129
+
1130
+ # Sort by time (newest first)
1131
+ if not df.empty and "Time" in df.columns:
1132
+ df = df.sort_values("Time", ascending=False)
1133
+
1134
+ return df
1135
+
1136
+ except Exception as e:
1137
+ logger.error(f"Error creating incident DataFrame: {e}")
1138
+ # Return empty DataFrame as fallback
1139
+ return pd.DataFrame(columns=[
1140
+ "Error", "Message"
1141
+ ]).from_records([{"Error": "DataFrame Error", "Message": str(e)}])
1142
+
1143
+ def get_execution_table_html(self):
1144
+ """Legacy HTML method for backward compatibility"""
1145
  if not self.executions:
1146
  return """
1147
  <div style="text-align: center; padding: 30px; color: #64748b;">
 
1195
  </div>
1196
  """
1197
 
1198
+ def get_incident_table_html(self):
1199
+ """Legacy HTML method for backward compatibility"""
1200
  if not self.incidents:
1201
  return """
1202
  <div style="text-align: center; padding: 30px; color: #64748b;">
 
1402
  return scenario_card_html, telemetry_fig, impact_fig, timeline_fig
1403
 
1404
  # ===========================================
1405
+ # SURGICAL FIX 5: run_true_arf_analysis() - FIXED to return DataFrames
1406
  # ===========================================
1407
  @AsyncRunner.async_to_sync
1408
  async def run_true_arf_analysis(scenario_name: str) -> tuple:
 
1412
  2. recall_html (HTML string)
1413
  3. decision_html (HTML string)
1414
  4. oss_results_dict (Python dict for JSON display)
1415
+ 5. incident_df (DataFrame for Gradio DataFrame component)
1416
  """
1417
 
1418
  components = get_components()
 
1562
  "boundary_note": f"Mock analysis - {boundary_text}"
1563
  }
1564
 
1565
+ # Incident DataFrame (FIXED: Returns DataFrame instead of HTML)
1566
+ incident_df = get_audit_manager().get_incident_dataframe()
1567
 
1568
+ return detection_html, recall_html, decision_html, oss_results_dict, incident_df
1569
 
1570
  except Exception as e:
1571
  logger.error(f"True ARF analysis failed: {e}")
 
1599
  "recommendation": "Check ARF installation"
1600
  }
1601
 
1602
+ # Return empty DataFrame on error
1603
+ error_df = pd.DataFrame(columns=["Error", "Message"]).from_records([
1604
+ {"Error": "Analysis Failed", "Message": str(e)}
1605
+ ])
1606
+
1607
+ return error_html, error_html, error_html, error_dict, error_df
1608
 
1609
  # ===========================================
1610
+ # FIXED EXECUTION FUNCTION - Returns DataFrames
1611
  # ===========================================
1612
  def execute_enterprise_healing(scenario_name, approval_required, mcp_mode_value):
1613
  """
1614
  MINIMAL FIX: Returns proper data types matching UI expectations
1615
+ FIXED: Returns DataFrame instead of HTML for execution table
1616
  """
1617
  import gradio as gr
1618
 
 
1719
  ]
1720
  }
1721
 
1722
+ # Get execution DataFrame (FIXED: Returns DataFrame instead of HTML)
1723
+ execution_df = get_audit_manager().get_execution_dataframe()
1724
 
1725
+ return approval_display, enterprise_results, execution_df
1726
 
1727
  # ===========================================
1728
  # FIXED ROI FUNCTION
 
1792
  return roi_result, fig
1793
 
1794
  # ===========================================
1795
+ # CREATE DEMO INTERFACE - UNCHANGED except for DataFrame fixes
1796
  # ===========================================
1797
  def create_demo_interface():
1798
  """Create demo interface using modular components with boundary awareness"""
 
1871
  outputs=[scenario_card, telemetry_viz, impact_viz, timeline_viz]
1872
  )
1873
 
1874
+ # Run OSS Analysis - FIXED: Now returns DataFrame for incident_table
1875
  oss_btn.click(
1876
  fn=run_true_arf_analysis,
1877
  inputs=[scenario_dropdown],
 
1881
  ]
1882
  )
1883
 
1884
+ # Execute Enterprise Healing - FIXED: Now returns DataFrame for execution_table
1885
  enterprise_btn.click(
1886
  fn=execute_enterprise_healing,
1887
  inputs=[scenario_dropdown, approval_toggle, mcp_mode],
 
2076
  # Update the enterprise_results_display to include demo completion info
2077
  enterprise_results["demo_completion_message"] = demo_message
2078
 
2079
+ # Get updated DataFrames (FIXED: Returns DataFrames)
2080
+ incident_df = get_audit_manager().get_incident_dataframe()
2081
+ execution_df = get_audit_manager().get_execution_dataframe()
2082
 
2083
  # Combine all results
2084
  return (
 
2087
  oss_result[3], # 1 output: oss_results_display
2088
  enterprise_results, # 1 output: enterprise_results_display
2089
  demo_message, # 1 output: approval_display
2090
+ incident_df, # 1 output: incident_table (DataFrame)
2091
+ execution_df # 1 output: execution_table (DataFrame)
2092
  )
2093
 
2094
  # FIXED: demo_btn.click with correct output count
 
2113
  outputs=[roi_output, roi_chart]
2114
  )
2115
 
2116
+ # Update ROI scenario - FIXED: Use the EnhancedROICalculator
2117
  roi_scenario_dropdown.change(
2118
+ fn=lambda x: get_components()["EnhancedROICalculator"].calculate_comprehensive_roi(scenario_name=x),
2119
+ inputs=[roi_scenario_dropdown],
2120
  outputs=[roi_output]
2121
  )
2122
 
 
2133
  outputs=[roi_chart]
2134
  )
2135
 
2136
+ # Audit Trail Functions - FIXED: Returns DataFrames
2137
  def refresh_audit_trail():
2138
+ """Refresh audit trail tables - FIXED to return DataFrames"""
2139
  return (
2140
+ get_audit_manager().get_execution_dataframe(), # DataFrame
2141
+ get_audit_manager().get_incident_dataframe() # DataFrame
2142
  )
2143
 
2144
  def clear_audit_trail():
2145
+ """Clear audit trail - FIXED to return empty DataFrames"""
2146
  get_audit_manager().clear()
2147
+ # Return empty DataFrames with correct columns
2148
+ exec_df = pd.DataFrame(columns=["Execution ID", "Scenario", "Status", "Mode", "Start Time"])
2149
+ incident_df = pd.DataFrame(columns=["Scenario", "Status", "Boundary", "Time"])
2150
+ return exec_df, incident_df
2151
 
2152
  def export_audit_trail():
2153
  """Export audit trail as JSON"""