11# flow_visualizer.py
22
33import os
4- from pathlib import Path
54
6- from pyvis .network import Network
5+ from pyvis .network import Network # type: ignore[import-untyped]
76
87from crewai .flow .config import COLORS , NODE_STYLES
98from crewai .flow .html_template_handler import HTMLTemplateHandler
109from crewai .flow .legend_generator import generate_legend_items_html , get_legend_items
11- from crewai .flow .path_utils import safe_path_join , validate_path_exists
10+ from crewai .flow .path_utils import safe_path_join
1211from crewai .flow .utils import calculate_node_levels
1312from crewai .flow .visualization_utils import (
1413 add_edges ,
@@ -34,13 +33,13 @@ def __init__(self, flow):
3433 ValueError
3534 If flow object is invalid or missing required attributes.
3635 """
37- if not hasattr (flow , ' _methods' ):
36+ if not hasattr (flow , " _methods" ):
3837 raise ValueError ("Invalid flow object: missing '_methods' attribute" )
39- if not hasattr (flow , ' _listeners' ):
38+ if not hasattr (flow , " _listeners" ):
4039 raise ValueError ("Invalid flow object: missing '_listeners' attribute" )
41- if not hasattr (flow , ' _start_methods' ):
40+ if not hasattr (flow , " _start_methods" ):
4241 raise ValueError ("Invalid flow object: missing '_start_methods' attribute" )
43-
42+
4443 self .flow = flow
4544 self .colors = COLORS
4645 self .node_styles = NODE_STYLES
@@ -65,7 +64,7 @@ def plot(self, filename):
6564 """
6665 if not filename or not isinstance (filename , str ):
6766 raise ValueError ("Filename must be a non-empty string" )
68-
67+
6968 try :
7069 # Initialize network
7170 net = Network (
@@ -96,45 +95,51 @@ def plot(self, filename):
9695 try :
9796 node_levels = calculate_node_levels (self .flow )
9897 except Exception as e :
99- raise ValueError (f"Failed to calculate node levels: { str ( e ) } " )
98+ raise ValueError (f"Failed to calculate node levels: { e !s } " ) from e
10099
101100 # Compute positions
102101 try :
103102 node_positions = compute_positions (self .flow , node_levels )
104103 except Exception as e :
105- raise ValueError (f"Failed to compute node positions: { str ( e ) } " )
104+ raise ValueError (f"Failed to compute node positions: { e !s } " ) from e
106105
107106 # Add nodes to the network
108107 try :
109108 add_nodes_to_network (net , self .flow , node_positions , self .node_styles )
110109 except Exception as e :
111- raise RuntimeError (f"Failed to add nodes to network: { str ( e ) } " )
110+ raise RuntimeError (f"Failed to add nodes to network: { e !s } " ) from e
112111
113112 # Add edges to the network
114113 try :
115114 add_edges (net , self .flow , node_positions , self .colors )
116115 except Exception as e :
117- raise RuntimeError (f"Failed to add edges to network: { str ( e ) } " )
116+ raise RuntimeError (f"Failed to add edges to network: { e !s } " ) from e
118117
119118 # Generate HTML
120119 try :
121120 network_html = net .generate_html ()
122121 final_html_content = self ._generate_final_html (network_html )
123122 except Exception as e :
124- raise RuntimeError (f"Failed to generate network visualization: { str (e )} " )
123+ raise RuntimeError (
124+ f"Failed to generate network visualization: { e !s} "
125+ ) from e
125126
126127 # Save the final HTML content to the file
127128 try :
128129 with open (f"{ filename } .html" , "w" , encoding = "utf-8" ) as f :
129130 f .write (final_html_content )
130131 print (f"Plot saved as { filename } .html" )
131132 except IOError as e :
132- raise IOError (f"Failed to save flow visualization to { filename } .html: { str (e )} " )
133+ raise IOError (
134+ f"Failed to save flow visualization to { filename } .html: { e !s} "
135+ ) from e
133136
134137 except (ValueError , RuntimeError , IOError ) as e :
135138 raise e
136139 except Exception as e :
137- raise RuntimeError (f"Unexpected error during flow visualization: { str (e )} " )
140+ raise RuntimeError (
141+ f"Unexpected error during flow visualization: { e !s} "
142+ ) from e
138143 finally :
139144 self ._cleanup_pyvis_lib ()
140145
@@ -165,7 +170,9 @@ def _generate_final_html(self, network_html):
165170 try :
166171 # Extract just the body content from the generated HTML
167172 current_dir = os .path .dirname (__file__ )
168- template_path = safe_path_join ("assets" , "crewai_flow_visual_template.html" , root = current_dir )
173+ template_path = safe_path_join (
174+ "assets" , "crewai_flow_visual_template.html" , root = current_dir
175+ )
169176 logo_path = safe_path_join ("assets" , "crewai_logo.svg" , root = current_dir )
170177
171178 if not os .path .exists (template_path ):
@@ -179,12 +186,9 @@ def _generate_final_html(self, network_html):
179186 # Generate the legend items HTML
180187 legend_items = get_legend_items (self .colors )
181188 legend_items_html = generate_legend_items_html (legend_items )
182- final_html_content = html_handler .generate_final_html (
183- network_body , legend_items_html
184- )
185- return final_html_content
189+ return html_handler .generate_final_html (network_body , legend_items_html )
186190 except Exception as e :
187- raise IOError (f"Failed to generate visualization HTML: { str ( e ) } " )
191+ raise IOError (f"Failed to generate visualization HTML: { e !s } " ) from e
188192
189193 def _cleanup_pyvis_lib (self ):
190194 """
@@ -197,6 +201,7 @@ def _cleanup_pyvis_lib(self):
197201 lib_folder = safe_path_join ("lib" , root = os .getcwd ())
198202 if os .path .exists (lib_folder ) and os .path .isdir (lib_folder ):
199203 import shutil
204+
200205 shutil .rmtree (lib_folder )
201206 except ValueError as e :
202207 print (f"Error validating lib folder path: { e } " )
0 commit comments