Update app.py
Browse files
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 |
-
#
|
| 839 |
try:
|
| 840 |
from core.calculators import EnhancedROICalculator
|
| 841 |
components["EnhancedROICalculator"] = EnhancedROICalculator()
|
|
|
|
| 842 |
except ImportError:
|
| 843 |
-
|
| 844 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 845 |
return {
|
| 846 |
"status": "✅ Calculated Successfully",
|
|
|
|
|
|
|
|
|
|
| 847 |
"summary": {
|
| 848 |
-
"your_annual_impact": "$
|
| 849 |
-
"potential_savings": "$
|
| 850 |
-
"enterprise_cost": "$
|
| 851 |
-
"roi_multiplier": "
|
| 852 |
-
"payback_months": "
|
| 853 |
-
"annual_roi_percentage": "
|
| 854 |
"boundary_context": "Based on OSS analysis + simulated Enterprise execution"
|
| 855 |
},
|
| 856 |
-
"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 857 |
}
|
| 858 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 965 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 1020 |
-
"""
|
| 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.
|
| 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
|
| 1387 |
-
|
| 1388 |
|
| 1389 |
-
return detection_html, recall_html, decision_html, oss_results_dict,
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 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
|
| 1539 |
-
|
| 1540 |
|
| 1541 |
-
return approval_display, enterprise_results,
|
| 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
|
| 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
|
| 1896 |
-
|
| 1897 |
-
|
| 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 |
-
|
| 1907 |
-
|
| 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"]
|
| 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().
|
| 1957 |
-
get_audit_manager().
|
| 1958 |
)
|
| 1959 |
|
| 1960 |
def clear_audit_trail():
|
| 1961 |
-
"""Clear audit trail"""
|
| 1962 |
get_audit_manager().clear()
|
| 1963 |
-
|
|
|
|
|
|
|
|
|
|
| 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"""
|