Skip to content

Commit 08b56ff

Browse files
feat: add graph visualization
1 parent 6cbad68 commit 08b56ff

1 file changed

Lines changed: 72 additions & 0 deletions

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import json
2+
import logging
3+
import os
4+
import sys
5+
from typing import Optional
6+
7+
import graphviz
8+
9+
logger = logging.getLogger(__name__)
10+
11+
12+
def visualize_graph(
13+
graph_json_path: str, output_path: Optional[str] = None
14+
) -> None:
15+
"""Reads a search graph JSON file and exports it to a PNG."""
16+
if not os.path.exists(graph_json_path):
17+
logger.error(f"Graph JSON file not found: {graph_json_path}")
18+
return
19+
20+
if output_path is None:
21+
output_base = os.path.splitext(graph_json_path)[0]
22+
else:
23+
output_base = os.path.splitext(output_path)[0]
24+
25+
with open(graph_json_path, "r") as f:
26+
data = json.load(f)
27+
28+
nodes_data = data.get("nodes", {})
29+
best_node_id = data.get("best_node_id")
30+
31+
dot = graphviz.Digraph(name="SearchGraph")
32+
dot.attr("node", shape="box", style="filled", fontname="Helvetica")
33+
34+
for node_id, node_data in nodes_data.items():
35+
eval_data = node_data.get("evaluation") or {}
36+
latency_ms = eval_data.get("latency_ms")
37+
latency = f"{latency_ms:.4f} ms" if latency_ms is not None else "N/A"
38+
39+
execution_status = node_data.get("execution_status", "UNKNOWN")
40+
depth = node_data.get("depth", 0)
41+
42+
color = "lightblue"
43+
if node_id == best_node_id:
44+
color = "lightgreen"
45+
elif execution_status != "SUCCESS":
46+
color = "lightpink"
47+
48+
label = f"ID: {node_id}\nDepth: {depth}\nLatency: {latency}"
49+
dot.node(node_id, label, fillcolor=color)
50+
51+
parent_id = node_data.get("parent_id")
52+
if parent_id:
53+
dot.edge(parent_id, node_id)
54+
55+
try:
56+
dot.render(filename=output_base, format="png", cleanup=True)
57+
logger.info(f"Successfully rendered graph to PNG at {output_base}.png")
58+
except Exception as e:
59+
logger.error(f"Failed to generate PNG: {e}")
60+
61+
62+
if __name__ == "__main__":
63+
logging.basicConfig(level=logging.INFO, format="%(levelname)s: %(message)s")
64+
65+
if len(sys.argv) < 2:
66+
print(
67+
"Usage: python -m auto_search.utils.visualize_graph <path_to_graph.json> [output_graph_path]"
68+
)
69+
sys.exit(1)
70+
71+
output = sys.argv[2] if len(sys.argv) > 2 else None
72+
visualize_graph(sys.argv[1], output)

0 commit comments

Comments
 (0)