from typing import List, Dict, Any, Optional import json from pydantic import BaseModel from loguru import logger # Mocking Blaxel SDK for now as it's a hypothetical or external dependency not in requirements # In a real scenario, we would import: from blaxel import Scene, Node, Edge, Animation # We will generate a Plotly 3D scatter plot as a fallback/implementation of the concept import plotly.graph_objects as go class StackFrame(BaseModel): function: str file: str line: int is_external: bool = False class ErrorFlowVisualizer: def __init__(self): pass def _get_node_color(self, idx: int, total: int, is_external: bool) -> str: if is_external: return "purple" if idx == 0: return "green" # Entry if idx == total - 1: return "red" # Error return "blue" # Normal def generate_flow(self, stack_trace: List[Dict[str, Any]]) -> str: """ Generate 3D visualization of error flow. Returns HTML string of the visualization. """ try: frames = [StackFrame(**f) for f in stack_trace] if not frames: return "
No stack trace available for visualization.
" # Limit nodes for performance if len(frames) > 50: frames = frames[-50:] # 3D Coordinates layout (simple spiral or linear) x_coords = [] y_coords = [] z_coords = [] colors = [] sizes = [] hover_texts = [] for idx, frame in enumerate(frames): # Simple linear layout along X axis, with slight spiral x_coords.append(idx * 2) y_coords.append(0) z_coords.append(0) color = self._get_node_color(idx, len(frames), frame.is_external) colors.append(color) # Pulse effect for error node (simulated by larger size) size = 20 if idx == len(frames) - 1 else 12 sizes.append(size) hover_texts.append( f"{frame.function}
" f"{frame.file}:{frame.line}
" f"{'External Library' if frame.is_external else 'User Code'}" ) # Create Nodes Trace node_trace = go.Scatter3d( x=x_coords, y=y_coords, z=z_coords, mode='markers+text', marker=dict( size=sizes, color=colors, opacity=0.8, line=dict(width=2, color='white') ), text=[f.function for f in frames], textposition="top center", hoverinfo='text', hovertext=hover_texts ) # Create Edges Trace edge_x = [] edge_y = [] edge_z = [] for i in range(len(frames) - 1): edge_x.extend([x_coords[i], x_coords[i+1], None]) edge_y.extend([y_coords[i], y_coords[i+1], None]) edge_z.extend([z_coords[i], z_coords[i+1], None]) edge_trace = go.Scatter3d( x=edge_x, y=edge_y, z=edge_z, mode='lines', line=dict(color='#888', width=2), hoverinfo='none' ) # Layout layout = go.Layout( title="3D Error Flow Visualization", showlegend=False, scene=dict( xaxis=dict(showgrid=False, zeroline=False, showticklabels=False), yaxis=dict(showgrid=False, zeroline=False, showticklabels=False), zaxis=dict(showgrid=False, zeroline=False, showticklabels=False), bgcolor='rgba(0,0,0,0)' ), margin=dict(l=0, r=0, b=0, t=30), height=500 ) fig = go.Figure(data=[edge_trace, node_trace], layout=layout) # Return HTML div return fig.to_html(full_html=False, include_plotlyjs='cdn') except Exception as e: logger.error(f"Visualization generation failed: {e}") return f"
Error generating visualization: {str(e)}
" def generate_mock_trace(self) -> List[Dict[str, Any]]: """Generate a mock trace for testing.""" return [ {"function": "main", "file": "app.py", "line": 10, "is_external": False}, {"function": "process_request", "file": "core/handler.py", "line": 45, "is_external": False}, {"function": "validate_input", "file": "utils/validation.py", "line": 12, "is_external": False}, {"function": "json.loads", "file": "json/decoder.py", "line": 337, "is_external": True}, {"function": "decode", "file": "json/decoder.py", "line": 355, "is_external": True}, ]