| """ |
| Enhanced visualization engine for ARF Demo |
| """ |
| import plotly.graph_objects as go |
| import plotly.express as px |
| import numpy as np |
| import pandas as pd |
| from typing import Dict, List, Any, Optional |
| import logging |
|
|
| logger = logging.getLogger(__name__) |
|
|
|
|
| class EnhancedVisualizationEngine: |
| """Enhanced visualization engine with multiple chart types""" |
| |
| def __init__(self): |
| self.color_palette = { |
| "primary": "#3b82f6", |
| "success": "#10b981", |
| "warning": "#f59e0b", |
| "danger": "#ef4444", |
| "info": "#8b5cf6", |
| "dark": "#1e293b", |
| "light": "#f8fafc" |
| } |
| |
| def create_executive_dashboard(self, data: Optional[Dict] = None) -> go.Figure: |
| """Create executive dashboard with ROI visualization""" |
| if data is None: |
| data = {"roi_multiplier": 5.2} |
| |
| roi_multiplier = data.get("roi_multiplier", 5.2) |
| |
| |
| fig = go.Figure() |
| |
| |
| fig.add_trace(go.Indicator( |
| mode="number+gauge", |
| value=roi_multiplier, |
| title={"text": "<b>ROI Multiplier</b><br>Investment Return"}, |
| domain={'x': [0.25, 0.75], 'y': [0.6, 1]}, |
| gauge={ |
| 'axis': {'range': [0, 10], 'tickwidth': 1}, |
| 'bar': {'color': self.color_palette["success"]}, |
| 'steps': [ |
| {'range': [0, 2], 'color': '#e5e7eb'}, |
| {'range': [2, 4], 'color': '#d1d5db'}, |
| {'range': [4, 6], 'color': '#10b981'}, |
| {'range': [6, 10], 'color': '#059669'} |
| ], |
| 'threshold': { |
| 'line': {'color': "black", 'width': 4}, |
| 'thickness': 0.75, |
| 'value': roi_multiplier |
| } |
| } |
| )) |
| |
| |
| fig.add_trace(go.Indicator( |
| mode="number", |
| value=85, |
| title={"text": "MTTR Reduction"}, |
| number={'suffix': "%", 'font': {'size': 24}}, |
| domain={'x': [0.1, 0.4], 'y': [0.2, 0.5]} |
| )) |
| |
| fig.add_trace(go.Indicator( |
| mode="number", |
| value=94, |
| title={"text": "Detection Accuracy"}, |
| number={'suffix': "%", 'font': {'size': 24}}, |
| domain={'x': [0.6, 0.9], 'y': [0.2, 0.5]} |
| )) |
| |
| fig.update_layout( |
| height=700, |
| paper_bgcolor="rgba(0,0,0,0)", |
| plot_bgcolor="rgba(0,0,0,0)", |
| font={'family': "Arial, sans-serif"}, |
| margin=dict(t=50, b=50, l=50, r=50) |
| ) |
| |
| return fig |
| |
| def create_telemetry_plot(self, scenario_name: str, anomaly_detected: bool = True) -> go.Figure: |
| """Create telemetry plot for a scenario""" |
| |
| time_points = np.arange(0, 100, 1) |
| |
| |
| if "Cache" in scenario_name: |
| base_data = 100 + 50 * np.sin(time_points * 0.2) |
| noise = np.random.normal(0, 8, 100) |
| metric_name = "Cache Hit Rate (%)" |
| normal_range = (70, 95) |
| elif "Database" in scenario_name: |
| base_data = 70 + 30 * np.sin(time_points * 0.15) |
| noise = np.random.normal(0, 6, 100) |
| metric_name = "Connection Pool Usage" |
| normal_range = (20, 60) |
| elif "Memory" in scenario_name: |
| base_data = 50 + 40 * np.sin(time_points * 0.1) |
| noise = np.random.normal(0, 10, 100) |
| metric_name = "Memory Usage (%)" |
| normal_range = (40, 80) |
| else: |
| base_data = 80 + 20 * np.sin(time_points * 0.25) |
| noise = np.random.normal(0, 5, 100) |
| metric_name = "System Load" |
| normal_range = (50, 90) |
| |
| data = base_data + noise |
| |
| fig = go.Figure() |
| |
| if anomaly_detected: |
| |
| fig.add_trace(go.Scatter( |
| x=time_points[:70], |
| y=data[:70], |
| mode='lines', |
| name='Normal Operation', |
| line=dict(color=self.color_palette["primary"], width=3), |
| fill='tozeroy', |
| fillcolor='rgba(59, 130, 246, 0.1)' |
| )) |
| |
| |
| fig.add_trace(go.Scatter( |
| x=time_points[70:], |
| y=data[70:], |
| mode='lines', |
| name='Anomaly Detected', |
| line=dict(color=self.color_palette["danger"], width=3, dash='dash'), |
| fill='tozeroy', |
| fillcolor='rgba(239, 68, 68, 0.1)' |
| )) |
| |
| |
| fig.add_vline( |
| x=70, |
| line_dash="dash", |
| line_color=self.color_palette["success"], |
| annotation_text="ARF Detection", |
| annotation_position="top" |
| ) |
| else: |
| |
| fig.add_trace(go.Scatter( |
| x=time_points, |
| y=data, |
| mode='lines', |
| name=metric_name, |
| line=dict(color=self.color_palette["primary"], width=3), |
| fill='tozeroy', |
| fillcolor='rgba(59, 130, 246, 0.1)' |
| )) |
| |
| |
| fig.add_hrect( |
| y0=normal_range[0], |
| y1=normal_range[1], |
| fillcolor="rgba(16, 185, 129, 0.1)", |
| opacity=0.2, |
| line_width=0, |
| annotation_text="Normal Range", |
| annotation_position="top left" |
| ) |
| |
| fig.update_layout( |
| title=f"📈 {metric_name} - Live Telemetry", |
| xaxis_title="Time (minutes)", |
| yaxis_title=metric_name, |
| height=300, |
| margin=dict(l=20, r=20, t=50, b=20), |
| plot_bgcolor='rgba(0,0,0,0)', |
| paper_bgcolor='rgba(0,0,0,0)', |
| legend=dict( |
| orientation="h", |
| yanchor="bottom", |
| y=1.02, |
| xanchor="right", |
| x=1 |
| ) |
| ) |
| |
| return fig |
| |
| def create_impact_gauge(self, scenario_name: str) -> go.Figure: |
| """Create business impact gauge""" |
| impact_map = { |
| "Cache Miss Storm": {"revenue": 8500, "severity": "critical"}, |
| "Database Connection Pool Exhaustion": {"revenue": 4200, "severity": "high"}, |
| "Kubernetes Memory Leak": {"revenue": 5500, "severity": "high"}, |
| "API Rate Limit Storm": {"revenue": 3800, "severity": "medium"}, |
| "Network Partition": {"revenue": 12000, "severity": "critical"}, |
| "Storage I/O Saturation": {"revenue": 6800, "severity": "high"} |
| } |
| |
| impact = impact_map.get(scenario_name, {"revenue": 5000, "severity": "medium"}) |
| |
| fig = go.Figure(go.Indicator( |
| mode="gauge+number", |
| value=impact["revenue"], |
| title={'text': "💰 Hourly Revenue Risk", 'font': {'size': 16}}, |
| number={'prefix': "$", 'font': {'size': 28}}, |
| gauge={ |
| 'axis': {'range': [0, 15000], 'tickwidth': 1}, |
| 'bar': {'color': self._get_severity_color(impact["severity"])}, |
| 'steps': [ |
| {'range': [0, 3000], 'color': '#10b981'}, |
| {'range': [3000, 7000], 'color': '#f59e0b'}, |
| {'range': [7000, 15000], 'color': '#ef4444'} |
| ], |
| 'threshold': { |
| 'line': {'color': "black", 'width': 4}, |
| 'thickness': 0.75, |
| 'value': impact["revenue"] |
| } |
| } |
| )) |
| |
| fig.update_layout( |
| height=300, |
| margin=dict(l=20, r=20, t=50, b=20), |
| paper_bgcolor='rgba(0,0,0,0)' |
| ) |
| |
| return fig |
| |
| def create_agent_performance_chart(self) -> go.Figure: |
| """Create agent performance comparison chart""" |
| agents = ["Detection", "Recall", "Decision"] |
| accuracy = [98.7, 92.0, 94.0] |
| speed = [45, 30, 60] |
| confidence = [99.8, 92.0, 94.0] |
| |
| fig = go.Figure(data=[ |
| go.Bar(name='Accuracy (%)', x=agents, y=accuracy, |
| marker_color=self.color_palette["primary"]), |
| go.Bar(name='Speed (seconds)', x=agents, y=speed, |
| marker_color=self.color_palette["success"]), |
| go.Bar(name='Confidence (%)', x=agents, y=confidence, |
| marker_color=self.color_palette["info"]) |
| ]) |
| |
| fig.update_layout( |
| title="🤖 Agent Performance Metrics", |
| barmode='group', |
| height=400, |
| plot_bgcolor='rgba(0,0,0,0)', |
| paper_bgcolor='rgba(0,0,0,0)', |
| legend=dict( |
| orientation="h", |
| yanchor="bottom", |
| y=1.02, |
| xanchor="right", |
| x=1 |
| ) |
| ) |
| |
| return fig |
| |
| def create_timeline_comparison(self) -> go.Figure: |
| """Create timeline comparison chart""" |
| phases = ["Detection", "Analysis", "Decision", "Execution", "Recovery"] |
| manual_times = [300, 1800, 1200, 1800, 3600] |
| arf_times = [45, 30, 60, 720, 0] |
| |
| |
| manual_times_min = [t/60 for t in manual_times] |
| arf_times_min = [t/60 for t in arf_times] |
| |
| fig = go.Figure() |
| |
| fig.add_trace(go.Bar( |
| name='Manual Process', |
| x=phases, |
| y=manual_times_min, |
| marker_color=self.color_palette["danger"], |
| text=[f"{t:.0f}m" for t in manual_times_min], |
| textposition='auto' |
| )) |
| |
| fig.add_trace(go.Bar( |
| name='ARF Autonomous', |
| x=phases, |
| y=arf_times_min, |
| marker_color=self.color_palette["success"], |
| text=[f"{t:.0f}m" for t in arf_times_min], |
| textposition='auto' |
| )) |
| |
| total_manual = sum(manual_times_min) |
| total_arf = sum(arf_times_min) |
| |
| fig.update_layout( |
| title=f"⏰ Incident Timeline Comparison<br>" |
| f"<span style='font-size: 14px; color: #6b7280'>" |
| f"Total: {total_manual:.0f}m manual vs {total_arf:.0f}m ARF " |
| f"({((total_manual - total_arf) / total_manual * 100):.0f}% faster)</span>", |
| barmode='group', |
| height=400, |
| plot_bgcolor='rgba(0,0,0,0)', |
| paper_bgcolor='rgba(0,0,0,0)', |
| legend=dict( |
| orientation="h", |
| yanchor="bottom", |
| y=1.02, |
| xanchor="right", |
| x=1 |
| ), |
| yaxis_title="Time (minutes)" |
| ) |
| |
| return fig |
| |
| def create_roi_simulation_chart(self, roi_data: Dict) -> go.Figure: |
| """Create ROI simulation chart""" |
| scenarios = ["Worst Case", "Base Case", "Best Case"] |
| roi_values = [ |
| roi_data.get("worst_case", 4.0), |
| roi_data.get("base_case", 5.2), |
| roi_data.get("best_case", 6.5) |
| ] |
| |
| fig = go.Figure(go.Bar( |
| x=scenarios, |
| y=roi_values, |
| marker_color=[ |
| self.color_palette["warning"], |
| self.color_palette["success"], |
| self.color_palette["primary"] |
| ], |
| text=[f"{v:.1f}×" for v in roi_values], |
| textposition='auto' |
| )) |
| |
| fig.update_layout( |
| title="📊 ROI Simulation Scenarios", |
| yaxis_title="ROI Multiplier", |
| height=400, |
| plot_bgcolor='rgba(0,0,0,0)', |
| paper_bgcolor='rgba(0,0,0,0)', |
| yaxis=dict(range=[0, max(roi_values) * 1.2]) |
| ) |
| |
| |
| fig.add_hline( |
| y=5.2, |
| line_dash="dash", |
| line_color="gray", |
| annotation_text="Industry Average", |
| annotation_position="top right" |
| ) |
| |
| return fig |
| |
| def create_learning_graph(self, graph_type: str = "patterns") -> go.Figure: |
| """Create learning engine visualization""" |
| if graph_type == "patterns": |
| return self._create_pattern_graph() |
| elif graph_type == "dependencies": |
| return self._create_dependency_graph() |
| else: |
| return self._create_action_graph() |
| |
| def _create_pattern_graph(self) -> go.Figure: |
| """Create pattern recognition graph""" |
| nodes = ["Cache Miss", "DB Pool", "Memory Leak", "API Limit", "Network"] |
| connections = [ |
| ("Cache Miss", "DB Pool", 0.85), |
| ("DB Pool", "Memory Leak", 0.72), |
| ("Memory Leak", "API Limit", 0.65), |
| ("API Limit", "Network", 0.58), |
| ("Cache Miss", "Network", 0.45) |
| ] |
| |
| fig = go.Figure() |
| |
| |
| for node in nodes: |
| fig.add_trace(go.Scatter( |
| x=[np.random.random()], |
| y=[np.random.random()], |
| mode='markers+text', |
| name=node, |
| marker=dict(size=30, color=self.color_palette["primary"]), |
| text=[node], |
| textposition="top center" |
| )) |
| |
| |
| for src, dst, weight in connections: |
| fig.add_trace(go.Scatter( |
| x=[np.random.random(), np.random.random()], |
| y=[np.random.random(), np.random.random()], |
| mode='lines', |
| line=dict(width=weight * 5, color='gray'), |
| showlegend=False |
| )) |
| |
| fig.update_layout( |
| title="🧠 RAG Memory - Incident Pattern Graph", |
| height=500, |
| plot_bgcolor='rgba(0,0,0,0)', |
| paper_bgcolor='rgba(0,0,0,0)', |
| showlegend=False, |
| xaxis=dict(showgrid=False, zeroline=False, showticklabels=False), |
| yaxis=dict(showgrid=False, zeroline=False, showticklabels=False) |
| ) |
| |
| return fig |
| |
| def _create_dependency_graph(self) -> go.Figure: |
| """Create system dependency graph""" |
| fig = go.Figure(go.Sunburst( |
| labels=["System", "Cache", "Database", "API", "User Service", "Payment"], |
| parents=["", "System", "System", "System", "API", "API"], |
| values=[100, 30, 40, 30, 15, 15], |
| marker=dict(colors=px.colors.sequential.Blues) |
| )) |
| |
| fig.update_layout( |
| title="🔗 System Dependency Map", |
| height=500, |
| plot_bgcolor='rgba(0,0,0,0)', |
| paper_bgcolor='rgba(0,0,0,0)' |
| ) |
| |
| return fig |
| |
| def _create_action_graph(self) -> go.Figure: |
| """Create action-outcome graph""" |
| actions = ["Scale Cache", "Restart DB", "Limit API", "Monitor Memory"] |
| success_rates = [87, 92, 78, 85] |
| |
| fig = go.Figure(go.Bar( |
| x=actions, |
| y=success_rates, |
| marker_color=self.color_palette["success"], |
| text=[f"{rate}%" for rate in success_rates], |
| textposition='auto' |
| )) |
| |
| fig.update_layout( |
| title="🎯 Action Success Rates", |
| yaxis_title="Success Rate (%)", |
| height=400, |
| plot_bgcolor='rgba(0,0,0,0)', |
| paper_bgcolor='rgba(0,0,0,0)', |
| yaxis=dict(range=[0, 100]) |
| ) |
| |
| return fig |
| |
| def _get_severity_color(self, severity: str) -> str: |
| """Get color for severity level""" |
| color_map = { |
| "critical": self.color_palette["danger"], |
| "high": self.color_palette["warning"], |
| "medium": self.color_palette["info"], |
| "low": self.color_palette["success"] |
| } |
| return color_map.get(severity.lower(), self.color_palette["info"]) |