import graphviz import json from tempfile import NamedTemporaryFile import os from graph_generator_utils import add_nodes_and_edges def generate_radial_diagram(json_input: str, output_format: str) -> str: """ Generates a radial (center-expanded) diagram from JSON input. Args: json_input (str): A JSON string describing the radial diagram structure. It must follow the Expected JSON Format Example below. output_format (str): The output format for the generated diagram. Supported formats: "png" or "svg" Expected JSON Format Example: { "central_node": "AI Core Concepts & Domains", "nodes": [ { "id": "foundational_ml", "label": "Foundational ML", "relationship": "builds on", "subnodes": [ {"id": "supervised_l", "label": "Supervised Learning", "relationship": "e.g."}, {"id": "unsupervised_l", "label": "Unsupervised Learning", "relationship": "e.g."} ] }, { "id": "dl_architectures", "label": "Deep Learning Arch.", "relationship": "evolved from", "subnodes": [ {"id": "cnns_rad", "label": "CNNs", "relationship": "e.g."}, {"id": "rnns_rad", "label": "RNNs", "relationship": "e.g."} ] }, { "id": "major_applications", "label": "Major AI Applications", "relationship": "applied in", "subnodes": [ {"id": "nlp_rad", "label": "Natural Language Processing", "relationship": "e.g."}, {"id": "cv_rad", "label": "Computer Vision", "relationship": "e.g."} ] }, { "id": "ethical_concerns", "label": "Ethical AI Concerns", "relationship": "addresses", "subnodes": [ {"id": "fairness_rad", "label": "Fairness & Bias", "relationship": "e.g."}, {"id": "explainability", "label": "Explainability (XAI)", "relationship": "e.g."} ] }, { "id": "future_trends", "label": "Future AI Trends", "relationship": "looking at", "subnodes": [ {"id": "agi_future", "label": "AGI Development", "relationship": "e.g."}, {"id": "quantum_ai", "label": "Quantum AI", "relationship": "e.g."} ] } ] } Returns: str: The filepath to the generated image file. """ try: if not json_input.strip(): return "Error: Empty input" data = json.loads(json_input) if 'central_node' not in data or 'nodes' not in data: raise ValueError("Missing required fields: central_node or nodes") dot = graphviz.Digraph( name='RadialDiagram', format='png', engine='neato', graph_attr={ 'overlap': 'false', # Prevent node overlap 'splines': 'true', # Smooth splines for edges 'bgcolor': 'white', # White background 'pad': '0.5', # Padding around the graph 'layout': 'neato' # Explicitly set layout engine for consistency }, node_attr={ 'fixedsize': 'false' # Allow nodes to resize based on content } ) # base_color = '#19191a' base_color = '#BEBEBE' dot.node( 'central', data['central_node'], shape='box', # Rectangular shape style='filled,rounded', # Filled and rounded corners fillcolor=base_color, # Darkest color fontcolor='black', # White text for dark background fontsize='16' # Larger font for central node ) add_nodes_and_edges(dot, 'central', data.get('nodes', []), current_depth=1, base_color=base_color) with NamedTemporaryFile(delete=False, suffix=f'.{output_format}') as tmp: dot.render(tmp.name, format=output_format, cleanup=True) return f"{tmp.name}.{output_format}" except json.JSONDecodeError: return "Error: Invalid JSON format" except Exception as e: return f"Error: {str(e)}"