ZahirJS commited on
Commit
c73b6d7
·
1 Parent(s): 2890c4e

initial commit

Browse files
.gitattributes CHANGED
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ images/rd1.png filter=lfs diff=lfs merge=lfs -text
37
+ images/rd2.png filter=lfs diff=lfs merge=lfs -text
38
+ images/sc1.png filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,14 +1,16 @@
1
  ---
2
  title: Graphify
3
- emoji: 🔥
4
- colorFrom: red
5
- colorTo: gray
6
  sdk: gradio
7
  sdk_version: 5.33.0
8
  app_file: app.py
9
  pinned: false
10
  license: mit
11
- short_description: Generate diagrams from JSON super fast and easy!
 
 
12
  ---
13
 
14
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
  title: Graphify
3
+ emoji:
4
+ colorFrom: purple
5
+ colorTo: indigo
6
  sdk: gradio
7
  sdk_version: 5.33.0
8
  app_file: app.py
9
  pinned: false
10
  license: mit
11
+ short_description: Generate all type of diagrams from JSON
12
+ tags:
13
+ - mcp-server-track
14
  ---
15
 
16
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+
3
+ from concept_map_generator import generate_concept_map
4
+ from synoptic_chart_generator import generate_synoptic_chart
5
+ from radial_diagram_generator import generate_radial_diagram
6
+ from process_flow_generator import generate_process_flow_diagram
7
+ from wbs_diagram_generator import generate_wbs_diagram
8
+
9
+ from sample_data import CONCEPT_MAP_JSON, SYNOPTIC_CHART_JSON, RADIAL_DIAGRAM_JSON, PROCESS_FLOW_JSON, WBS_DIAGRAM_JSON
10
+
11
+ if __name__ == "__main__":
12
+ DEFAULT_BASE_COLOR = '#19191a'
13
+
14
+ with gr.Blocks(
15
+ title="Advanced Graph Generator",
16
+ css="""
17
+ .gradio-container {
18
+ font-family: 'Inter', sans-serif !important;
19
+ }
20
+ .gr-tab-item {
21
+ padding: 10px 20px;
22
+ font-size: 1.1em;
23
+ font-weight: bold;
24
+ }
25
+ .gr-button {
26
+ border-radius: 8px;
27
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
28
+ background-color: #FFA500; /* Orange color for buttons */
29
+ color: white;
30
+ padding: 10px 20px;
31
+ font-size: 1.1em;
32
+ }
33
+ .gr-button:hover {
34
+ background-color: #FF8C00; /* Darker Orange on hover */
35
+ }
36
+ .gr-textbox {
37
+ border-radius: 8px;
38
+ padding: 10px;
39
+ }
40
+ /* Dark mode styles, adjust if needed */
41
+ .gradio-container.dark {
42
+ --tw-bg-opacity: 1;
43
+ background-color: rgb(24 24 27 / var(--tw-bg-opacity));
44
+ color: #d4d4d8; /* text-zinc-300 */
45
+ }
46
+ .gradio-container.dark .gr-textbox {
47
+ background-color: rgb(39 39 42 / var(--tw-bg-opacity));
48
+ color: #d4d4d8;
49
+ border-color: #52525b; /* border-zinc-600 */
50
+ }
51
+ .gradio-container.dark .gr-tab-item {
52
+ color: #d4d4d8;
53
+ }
54
+ .gradio-container.dark .gr-tab-item.selected {
55
+ background-color: rgb(39 39 42 / var(--tw-bg-opacity));
56
+ color: #fff;
57
+ }
58
+ """
59
+ ) as demo:
60
+ gr.Markdown(
61
+ """
62
+ # Graphify: generate diagrams from JSON super fast and easy ⚡!
63
+ Choose a graph type and provide your JSON data to generate a visual representation.
64
+ All graphs maintain a consistent, elegant style with rounded boxes,
65
+ a dark-to-light color gradient, and a clean white background.
66
+ """
67
+ )
68
+
69
+ with gr.Row(variant="panel"):
70
+ gr.Markdown("### Output Format")
71
+ output_format_radio = gr.Radio(
72
+ choices=["png", "svg"],
73
+ value="png",
74
+ label="Select Output File Format",
75
+ interactive=True,
76
+ elem_id="output-format-selector"
77
+ )
78
+
79
+ gr.Markdown("<br>")
80
+
81
+ with gr.Tabs():
82
+ with gr.TabItem("Concept Map"):
83
+ json_input_cm = gr.Textbox(
84
+ value=CONCEPT_MAP_JSON,
85
+ placeholder="Paste JSON following the documented format",
86
+ label="Structured JSON Input",
87
+ lines=25
88
+ )
89
+ output_cm = gr.Image(
90
+ label="Generated Graph",
91
+ type="filepath",
92
+ show_download_button=True
93
+ )
94
+ submit_btn_cm = gr.Button("Submit")
95
+
96
+ submit_btn_cm.click(
97
+ fn=generate_concept_map,
98
+ inputs=[json_input_cm, output_format_radio],
99
+ outputs=output_cm
100
+ )
101
+ gr.Markdown("<br>")
102
+ gr.Markdown("## Example Concept Maps")
103
+ with gr.Row():
104
+ gr.Image(value="./images/cm1.svg", label="Sample 1", show_label=True, interactive=False, height="auto", width="100%")
105
+ gr.Image(value="./images/cm2.svg", label="Sample 2", show_label=True, interactive=False, height="auto", width="100%")
106
+
107
+ with gr.TabItem("Synoptic Chart"):
108
+ json_input_sc = gr.Textbox(
109
+ value=SYNOPTIC_CHART_JSON,
110
+ placeholder="Paste JSON following the documented format",
111
+ label="Structured JSON Input",
112
+ lines=25
113
+ )
114
+ output_sc = gr.Image(
115
+ label="Generated Graph",
116
+ type="filepath",
117
+ show_download_button=True
118
+ )
119
+ submit_btn_sc = gr.Button("Submit")
120
+
121
+ submit_btn_sc.click(
122
+ fn=generate_synoptic_chart,
123
+ inputs=[json_input_sc, output_format_radio],
124
+ outputs=output_sc
125
+ )
126
+ gr.Markdown("<br>")
127
+ gr.Markdown("## Example Synoptic Charts")
128
+ with gr.Row():
129
+ gr.Image(value="./images/sc1.svg", label="Sample 1", show_label=True, interactive=False, height="auto", width="100%")
130
+ gr.Image(value="./images/sc2.svg", label="Sample 2", show_label=True, interactive=False, height="auto", width="100%")
131
+
132
+ with gr.TabItem("Radial Diagram"):
133
+ json_input_rd = gr.Textbox(
134
+ value=RADIAL_DIAGRAM_JSON,
135
+ placeholder="Paste JSON following the documented format",
136
+ label="Structured JSON Input",
137
+ lines=25
138
+ )
139
+ output_rd = gr.Image(
140
+ label="Generated Graph",
141
+ type="filepath",
142
+ show_download_button=True
143
+ )
144
+ submit_btn_rd = gr.Button("Submit")
145
+
146
+ submit_btn_rd.click(
147
+ fn=generate_radial_diagram,
148
+ inputs=[json_input_rd, output_format_radio],
149
+ outputs=output_rd
150
+ )
151
+ gr.Markdown("<br>")
152
+ gr.Markdown("## Example Radial Diagrams")
153
+ with gr.Row():
154
+ gr.Image(value="./images/rd1.svg", label="Sample 1", show_label=True, interactive=False, height="auto", width="100%")
155
+ gr.Image(value="./images/rd2.svg", label="Sample 2", show_label=True, interactive=False, height="auto", width="100%")
156
+ with gr.Row():
157
+ gr.Image(value="./images/rd3.svg", label="Sample 1", show_label=True, interactive=False, height="auto", width="100%")
158
+ gr.Image(value="./images/rd4.svg", label="Sample 2", show_label=True, interactive=False, height="auto", width="100%")
159
+
160
+ with gr.TabItem("Process Flow"):
161
+ json_input_pf = gr.Textbox(
162
+ value=PROCESS_FLOW_JSON,
163
+ placeholder="Paste JSON following the documented format",
164
+ label="Structured JSON Input",
165
+ lines=25
166
+ )
167
+ output_pf = gr.Image(
168
+ label="Generated Graph",
169
+ type="filepath",
170
+ show_download_button=True
171
+ )
172
+ submit_btn_pf = gr.Button("Submit")
173
+
174
+ submit_btn_pf.click(
175
+ fn=generate_process_flow_diagram,
176
+ inputs=[json_input_pf, output_format_radio],
177
+ outputs=output_pf
178
+ )
179
+ gr.Markdown("<br>")
180
+ gr.Markdown("## Example Process Flow Diagrams")
181
+ with gr.Row():
182
+ gr.Image(value="./images/pf1.svg", label="Sample 1", show_label=True, interactive=False, height="auto", width="100%")
183
+ gr.Image(value="./images/pf2.svg", label="Sample 2", show_label=True, interactive=False, height="auto", width="100%")
184
+
185
+ with gr.TabItem("WBS Diagram"):
186
+ json_input_wbs = gr.Textbox(
187
+ value=WBS_DIAGRAM_JSON,
188
+ placeholder="Paste JSON following the documented format",
189
+ label="Structured JSON Input",
190
+ lines=25
191
+ )
192
+ output_wbs = gr.Image(
193
+ label="Generated Graph",
194
+ type="filepath",
195
+ show_download_button=True
196
+ )
197
+ submit_btn_wbs = gr.Button("Submit")
198
+
199
+ submit_btn_wbs.click(
200
+ fn=generate_wbs_diagram,
201
+ inputs=[json_input_wbs, output_format_radio],
202
+ outputs=output_wbs
203
+ )
204
+ gr.Markdown("<br>")
205
+ gr.Markdown("## Example WBS Diagrams")
206
+ with gr.Row():
207
+ gr.Image(value="./images/wd1.svg", label="Sample 1", show_label=True, interactive=False, height="auto", width="100%")
208
+ gr.Image(value="./images/wd2.svg", label="Sample 2", show_label=True, interactive=False, height="auto", width="100%")
209
+
210
+ demo.launch(
211
+ mcp_server=True,
212
+ share=False,
213
+ server_port=7860,
214
+ server_name="0.0.0.0"
215
+ )
216
+
concept_map_generator.py ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import graphviz
2
+ import json
3
+ from tempfile import NamedTemporaryFile
4
+ import os
5
+ from graph_generator_utils import add_nodes_and_edges
6
+
7
+ def generate_concept_map(json_input: str, output_format: str) -> str:
8
+ """
9
+ Generates a concept map from JSON input.
10
+
11
+ Args:
12
+ json_input (str): A JSON string describing the concept map structure.
13
+ It must follow the Expected JSON Format Example below.
14
+
15
+ Expected JSON Format Example:
16
+ {
17
+ "central_node": "Artificial Intelligence (AI)",
18
+ "nodes": [
19
+ {
20
+ "id": "ml_fundamental",
21
+ "label": "Machine Learning",
22
+ "relationship": "is essential for",
23
+ "subnodes": [
24
+ {
25
+ "id": "dl_branch",
26
+ "label": "Deep Learning",
27
+ "relationship": "for example",
28
+ "subnodes": [
29
+ {
30
+ "id": "cnn_example",
31
+ "label": "CNNs",
32
+ "relationship": "for example"
33
+ },
34
+ {
35
+ "id": "rnn_example",
36
+ "label": "RNNs",
37
+ "relationship": "for example"
38
+ }
39
+ ]
40
+ },
41
+ {
42
+ "id": "rl_branch",
43
+ "label": "Reinforcement Learning",
44
+ "relationship": "for example",
45
+ "subnodes": [
46
+ {
47
+ "id": "qlearning_example",
48
+ "label": "Q-Learning",
49
+ "relationship": "example"
50
+ },
51
+ {
52
+ "id": "pg_example",
53
+ "label": "Policy Gradients",
54
+ "relationship": "example"
55
+ }
56
+ ]
57
+ }
58
+ ]
59
+ },
60
+ {
61
+ "id": "ai_types",
62
+ "label": "Types",
63
+ "relationship": "formed by",
64
+ "subnodes": [
65
+ {
66
+ "id": "agi_type",
67
+ "label": "AGI",
68
+ "relationship": "this is",
69
+ "subnodes": [
70
+ {
71
+ "id": "strong_ai",
72
+ "label": "Strong AI",
73
+ "relationship": "provoked by",
74
+ "subnodes": [
75
+ {
76
+ "id": "human_intel",
77
+ "label": "Human-level Intel.",
78
+ "relationship": "of"
79
+ }
80
+ ]
81
+ }
82
+ ]
83
+ },
84
+ {
85
+ "id": "ani_type",
86
+ "label": "ANI",
87
+ "relationship": "this is",
88
+ "subnodes": [
89
+ {
90
+ "id": "weak_ai",
91
+ "label": "Weak AI",
92
+ "relationship": "provoked by",
93
+ "subnodes": [
94
+ {
95
+ "id": "narrow_tasks",
96
+ "label": "Narrow Tasks",
97
+ "relationship": "of"
98
+ }
99
+ ]
100
+ }
101
+ ]
102
+ }
103
+ ]
104
+ },
105
+ {
106
+ "id": "ai_capabilities",
107
+ "label": "Capabilities",
108
+ "relationship": "change",
109
+ "subnodes": [
110
+ {
111
+ "id": "data_proc",
112
+ "label": "Data Processing",
113
+ "relationship": "can",
114
+ "subnodes": [
115
+ {
116
+ "id": "big_data",
117
+ "label": "Big Data",
118
+ "relationship": "as",
119
+ "subnodes": [
120
+ {
121
+ "id": "analysis_example",
122
+ "label": "Data Analysis",
123
+ "relationship": "example"
124
+ },
125
+ {
126
+ "id": "prediction_example",
127
+ "label": "Prediction",
128
+ "relationship": "example"
129
+ }
130
+ ]
131
+ }
132
+ ]
133
+ },
134
+ {
135
+ "id": "decision_making",
136
+ "label": "Decision Making",
137
+ "relationship": "can be",
138
+ "subnodes": [
139
+ {
140
+ "id": "automation",
141
+ "label": "Automation",
142
+ "relationship": "as",
143
+ "subnodes": [
144
+ {
145
+ "id": "robotics_example",
146
+ "label": "Robotics",
147
+ "relationship": "Example"},
148
+ {
149
+ "id": "autonomous_example",
150
+ "label": "Autonomous Vehicles",
151
+ "relationship": "of one"
152
+ }
153
+ ]
154
+ }
155
+ ]
156
+ },
157
+ {
158
+ "id": "problem_solving",
159
+ "label": "Problem Solving",
160
+ "relationship": "can",
161
+ "subnodes": [
162
+ {
163
+ "id": "optimization",
164
+ "label": "Optimization",
165
+ "relationship": "as is",
166
+ "subnodes": [
167
+ {
168
+ "id": "algorithms_example",
169
+ "label": "Algorithms",
170
+ "relationship": "for example"
171
+ }
172
+ ]
173
+ }
174
+ ]
175
+ }
176
+ ]
177
+ }
178
+ ]
179
+ }
180
+
181
+ Returns:
182
+ str: The filepath to the generated PNG image file.
183
+ """
184
+ try:
185
+ if not json_input.strip():
186
+ return "Error: Empty input"
187
+
188
+ data = json.loads(json_input)
189
+
190
+ if 'central_node' not in data or 'nodes' not in data:
191
+ raise ValueError("Missing required fields: central_node or nodes")
192
+
193
+ dot = graphviz.Digraph(
194
+ name='ConceptMap',
195
+ format='png',
196
+ graph_attr={
197
+ 'rankdir': 'TB', # Top-to-Bottom layout (vertical hierarchy)
198
+ 'splines': 'ortho', # Straight lines
199
+ 'bgcolor': 'white', # White background
200
+ 'pad': '0.5' # Padding around the graph
201
+ }
202
+ )
203
+
204
+ base_color = '#19191a' # Hardcoded base color
205
+
206
+ # Central node styling (rounded box, dark color)
207
+ dot.node(
208
+ 'central',
209
+ data['central_node'],
210
+ shape='box', # Rectangular shape
211
+ style='filled,rounded', # Filled and rounded corners
212
+ fillcolor=base_color, # Darkest color
213
+ fontcolor='white', # White text for dark background
214
+ fontsize='16' # Larger font for central node
215
+ )
216
+
217
+ # Add child nodes and edges recursively starting from depth 1
218
+ add_nodes_and_edges(dot, 'central', data.get('nodes', []), current_depth=1, base_color=base_color)
219
+
220
+ with NamedTemporaryFile(delete=False, suffix=f'.{output_format}') as tmp:
221
+ dot.render(tmp.name, format=output_format, cleanup=True)
222
+ return f"{tmp.name}.{output_format}"
223
+
224
+ except json.JSONDecodeError:
225
+ return "Error: Invalid JSON format"
226
+ except Exception as e:
227
+ return f"Error: {str(e)}"
228
+
graph_generator_utils.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import graphviz
2
+
3
+ def add_nodes_and_edges(dot: graphviz.Digraph, parent_id: str, nodes_list: list, current_depth: int, base_color: str):
4
+ """
5
+ Recursively adds nodes and edges to a Graphviz Digraph object,
6
+ applying a color gradient and consistent styling.
7
+
8
+ Args:
9
+ dot (graphviz.Digraph): The Graphviz Digraph object to modify.
10
+ parent_id (str): The ID of the parent node for the current set of nodes.
11
+ nodes_list (list): A list of dictionaries, each representing a node
12
+ with 'id', 'label', 'relationship', and optional 'subnodes'.
13
+ current_depth (int): The current depth in the graph hierarchy (0 for central node).
14
+ base_color (str): The hexadecimal base color for the deepest nodes.
15
+ """
16
+ # Calculate color for current depth, making it lighter
17
+ # This factor determines how quickly the color lightens per level.
18
+ lightening_factor = 0.12
19
+
20
+ # Convert base_color hex to RGB for interpolation
21
+ # Ensure base_color is a valid hex string before converting
22
+ if not isinstance(base_color, str) or not base_color.startswith('#') or len(base_color) != 7:
23
+ base_color = '#19191a' # Fallback to default dark if invalid
24
+
25
+ base_r = int(base_color[1:3], 16)
26
+ base_g = int(base_color[3:5], 16)
27
+ base_b = int(base_color[5:7], 16)
28
+
29
+ # Calculate current node color by blending towards white
30
+ current_r = base_r + int((255 - base_r) * current_depth * lightening_factor)
31
+ current_g = base_g + int((255 - base_g) * current_depth * lightening_factor)
32
+ current_b = base_b + int((255 - base_b) * current_depth * lightening_factor)
33
+
34
+ # Clamp values to 255 to stay within valid RGB range
35
+ current_r = min(255, current_r)
36
+ current_g = min(255, current_g)
37
+ current_b = min(255, current_b)
38
+
39
+ node_fill_color = f'#{current_r:02x}{current_g:02x}{current_b:02x}'
40
+
41
+ # Font color: white for dark nodes, black for very light nodes for readability
42
+ font_color = 'white' if current_depth * lightening_factor < 0.6 else 'black'
43
+
44
+ # Edge colors and font sizes
45
+ edge_color = '#4a4a4a' # Dark gray for lines
46
+ # Font size adjusts based on depth, ensuring a minimum size
47
+ font_size = max(9, 14 - (current_depth * 2))
48
+ edge_font_size = max(7, 10 - (current_depth * 1))
49
+
50
+ for node in nodes_list:
51
+ node_id = node.get('id')
52
+ label = node.get('label')
53
+ relationship = node.get('relationship')
54
+
55
+ # Basic validation for node data
56
+ if not all([node_id, label, relationship]):
57
+ raise ValueError(f"Invalid node: {node}")
58
+
59
+ # Add node with specified style
60
+ dot.node(
61
+ node_id,
62
+ label,
63
+ shape='box', # All nodes are rectangular
64
+ style='filled,rounded', # Filled and rounded corners
65
+ fillcolor=node_fill_color,
66
+ fontcolor=font_color,
67
+ fontsize=str(font_size)
68
+ )
69
+
70
+ # Add edge from parent to current node
71
+ dot.edge(
72
+ parent_id,
73
+ node_id,
74
+ label=relationship,
75
+ color=edge_color,
76
+ fontcolor=edge_color, # Edge label color also dark gray
77
+ fontsize=str(edge_font_size)
78
+ )
79
+
80
+ # Recursively call for subnodes
81
+ if 'subnodes' in node:
82
+ add_nodes_and_edges(dot, node_id, node['subnodes'], current_depth + 1, base_color)
83
+
images/cm1.svg ADDED
images/cm2.svg ADDED
images/pf1.svg ADDED
images/pf2.svg ADDED
images/rd1.svg ADDED
images/rd2.svg ADDED
images/rd3.svg ADDED
images/rd4.svg ADDED
images/sc1.svg ADDED
images/sc2.svg ADDED
images/wd1.svg ADDED
images/wd2.svg ADDED
packages.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ graphviz
process_flow_generator.py ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import graphviz
2
+ import json
3
+ from tempfile import NamedTemporaryFile
4
+ import os
5
+
6
+ def generate_process_flow_diagram(json_input: str, output_format: str) -> str:
7
+ """
8
+ Generates a Process Flow Diagram (Flowchart) from JSON input.
9
+
10
+ Args:
11
+ json_input (str): A JSON string describing the process flow structure.
12
+ It must follow the Expected JSON Format Example below.
13
+
14
+ Expected JSON Format Example:
15
+ {
16
+ "start_node": "Start Inference Request",
17
+ "nodes": [
18
+ {
19
+ "id": "user_input",
20
+ "label": "Receive User Input (Data)",
21
+ "type": "io"
22
+ },
23
+ {
24
+ "id": "preprocess_data",
25
+ "label": "Preprocess Data",
26
+ "type": "process"
27
+ },
28
+ {
29
+ "id": "validate_data",
30
+ "label": "Validate Data Format/Type",
31
+ "type": "decision"
32
+ },
33
+ {
34
+ "id": "data_valid_yes",
35
+ "label": "Data Valid?",
36
+ "type": "decision"
37
+ },
38
+ {
39
+ "id": "load_model",
40
+ "label": "Load AI Model (if not cached)",
41
+ "type": "process"
42
+ },
43
+ {
44
+ "id": "run_inference",
45
+ "label": "Run AI Model Inference",
46
+ "type": "process"
47
+ },
48
+ {
49
+ "id": "postprocess_output",
50
+ "label": "Postprocess Model Output",
51
+ "type": "process"
52
+ },
53
+ {
54
+ "id": "send_response",
55
+ "label": "Send Response to User",
56
+ "type": "io"
57
+ },
58
+ {
59
+ "id": "log_error",
60
+ "label": "Log Error & Notify User",
61
+ "type": "process"
62
+ },
63
+ {
64
+ "id": "end_inference_process",
65
+ "label": "End Inference Process",
66
+ "type": "end"
67
+ }
68
+ ],
69
+ "connections": [
70
+ {"from": "start_node", "to": "user_input", "label": "Request"},
71
+ {"from": "user_input", "to": "preprocess_data", "label": "Data Received"},
72
+ {"from": "preprocess_data", "to": "validate_data", "label": "Cleaned"},
73
+ {"from": "validate_data", "to": "data_valid_yes", "label": "Check"},
74
+ {"from": "data_valid_yes", "to": "load_model", "label": "Yes"},
75
+ {"from": "data_valid_yes", "to": "log_error", "label": "No"},
76
+ {"from": "load_model", "to": "run_inference", "label": "Model Ready"},
77
+ {"from": "run_inference", "to": "postprocess_output", "label": "Output Generated"},
78
+ {"from": "postprocess_output", "to": "send_response", "label": "Ready"},
79
+ {"from": "send_response", "to": "end_inference_process", "label": "Response Sent"},
80
+ {"from": "log_error", "to": "end_inference_process", "label": "Error Handled"}
81
+ ]
82
+ }
83
+
84
+ Returns:
85
+ str: The filepath to the generated PNG image file.
86
+ """
87
+ try:
88
+ if not json_input.strip():
89
+ return "Error: Empty input"
90
+
91
+ data = json.loads(json_input)
92
+
93
+ # Validate required top-level keys for a flowchart
94
+ if 'start_node' not in data or 'nodes' not in data or 'connections' not in data:
95
+ raise ValueError("Missing required fields: 'start_node', 'nodes', or 'connections'")
96
+
97
+ # Define specific node shapes for flowchart types
98
+ node_shapes = {
99
+ "process": "box", # Rectangle for processes
100
+ "decision": "diamond", # Diamond for decisions
101
+ "start": "oval", # Oval for start
102
+ "end": "oval", # Oval for end
103
+ "io": "parallelogram", # Input/Output
104
+ "document": "note", # Document symbol
105
+ "default": "box" # Fallback
106
+ }
107
+
108
+ dot = graphviz.Digraph(
109
+ name='ProcessFlowDiagram',
110
+ format='png',
111
+ graph_attr={
112
+ 'rankdir': 'TB', # Top-to-Bottom flow is common for flowcharts
113
+ 'splines': 'ortho', # Straight lines with 90-degree bends
114
+ 'bgcolor': 'white', # White background
115
+ 'pad': '0.5', # Padding around the graph
116
+ 'nodesep': '0.6', # Spacing between nodes on same rank
117
+ 'ranksep': '0.8' # Spacing between ranks
118
+ }
119
+ )
120
+
121
+ base_color = '#19191a' # Hardcoded base color
122
+
123
+ fill_color_for_nodes = base_color
124
+ font_color_for_nodes = 'white' if base_color == '#19191a' or base_color.lower() in ['#000000', '#19191a'] else 'black'
125
+
126
+ # Store all nodes by ID for easy lookup
127
+ all_defined_nodes = {node['id']: node for node in data['nodes']}
128
+
129
+ # Add start node explicitly
130
+ start_node_id = data['start_node']
131
+ dot.node(
132
+ start_node_id,
133
+ start_node_id, # Label is typically the ID itself for start/end
134
+ shape=node_shapes['start'],
135
+ style='filled,rounded',
136
+ fillcolor='#2196F3', # A distinct blue for Start
137
+ fontcolor='white',
138
+ fontsize='14'
139
+ )
140
+
141
+ # Add all other nodes (process, decision, etc.)
142
+ for node_id, node_info in all_defined_nodes.items():
143
+ if node_id == start_node_id: # Skip if it's the start node, already added
144
+ continue
145
+
146
+ node_type = node_info.get("type", "default")
147
+ shape = node_shapes.get(node_type, "box")
148
+
149
+ node_label = node_info['label']
150
+
151
+ # Use distinct color for end node if it exists
152
+ if node_type == 'end':
153
+ dot.node(
154
+ node_id,
155
+ node_label,
156
+ shape=shape,
157
+ style='filled,rounded',
158
+ fillcolor='#F44336', # A distinct red for End
159
+ fontcolor='white',
160
+ fontsize='14'
161
+ )
162
+ else: # Regular process, decision, etc. nodes use the selected base color
163
+ dot.node(
164
+ node_id,
165
+ node_label,
166
+ shape=shape,
167
+ style='filled,rounded',
168
+ fillcolor=fill_color_for_nodes,
169
+ fontcolor=font_color_for_nodes,
170
+ fontsize='14'
171
+ )
172
+
173
+ # Add connections (edges)
174
+ for connection in data['connections']:
175
+ dot.edge(
176
+ connection['from'],
177
+ connection['to'],
178
+ label=connection.get('label', ''),
179
+ color='#4a4a4a', # Dark gray for lines
180
+ fontcolor='#4a4a4a',
181
+ fontsize='10'
182
+ )
183
+
184
+ with NamedTemporaryFile(delete=False, suffix=f'.{output_format}') as tmp:
185
+ dot.render(tmp.name, format=output_format, cleanup=True)
186
+ return f"{tmp.name}.{output_format}"
187
+
188
+ except json.JSONDecodeError:
189
+ return "Error: Invalid JSON format"
190
+ except Exception as e:
191
+ return f"Error: {str(e)}"
192
+
radial_diagram_generator.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import graphviz
2
+ import json
3
+ from tempfile import NamedTemporaryFile
4
+ import os
5
+ from graph_generator_utils import add_nodes_and_edges
6
+
7
+ def generate_radial_diagram(json_input: str, output_format: str) -> str:
8
+ """
9
+ Generates a radial (center-expanded) diagram from JSON input.
10
+
11
+ Args:
12
+ json_input (str): A JSON string describing the radial diagram structure.
13
+ It must follow the Expected JSON Format Example below.
14
+
15
+ Expected JSON Format Example:
16
+ {
17
+ "central_node": "AI Core Concepts & Domains",
18
+ "nodes": [
19
+ {
20
+ "id": "foundational_ml",
21
+ "label": "Foundational ML",
22
+ "relationship": "builds on",
23
+ "subnodes": [
24
+ {"id": "supervised_l", "label": "Supervised Learning", "relationship": "e.g."},
25
+ {"id": "unsupervised_l", "label": "Unsupervised Learning", "relationship": "e.g."}
26
+ ]
27
+ },
28
+ {
29
+ "id": "dl_architectures",
30
+ "label": "Deep Learning Arch.",
31
+ "relationship": "evolved from",
32
+ "subnodes": [
33
+ {"id": "cnns_rad", "label": "CNNs", "relationship": "e.g."},
34
+ {"id": "rnns_rad", "label": "RNNs", "relationship": "e.g."}
35
+ ]
36
+ },
37
+ {
38
+ "id": "major_applications",
39
+ "label": "Major AI Applications",
40
+ "relationship": "applied in",
41
+ "subnodes": [
42
+ {"id": "nlp_rad", "label": "Natural Language Processing", "relationship": "e.g."},
43
+ {"id": "cv_rad", "label": "Computer Vision", "relationship": "e.g."}
44
+ ]
45
+ },
46
+ {
47
+ "id": "ethical_concerns",
48
+ "label": "Ethical AI Concerns",
49
+ "relationship": "addresses",
50
+ "subnodes": [
51
+ {"id": "fairness_rad", "label": "Fairness & Bias", "relationship": "e.g."},
52
+ {"id": "explainability", "label": "Explainability (XAI)", "relationship": "e.g."}
53
+ ]
54
+ },
55
+ {
56
+ "id": "future_trends",
57
+ "label": "Future AI Trends",
58
+ "relationship": "looking at",
59
+ "subnodes": [
60
+ {"id": "agi_future", "label": "AGI Development", "relationship": "e.g."},
61
+ {"id": "quantum_ai", "label": "Quantum AI", "relationship": "e.g."}
62
+ ]
63
+ }
64
+ ]
65
+ }
66
+
67
+ Returns:
68
+ str: The filepath to the generated PNG image file.
69
+ """
70
+ try:
71
+ if not json_input.strip():
72
+ return "Error: Empty input"
73
+
74
+ data = json.loads(json_input)
75
+
76
+ if 'central_node' not in data or 'nodes' not in data:
77
+ raise ValueError("Missing required fields: central_node or nodes")
78
+
79
+ dot = graphviz.Digraph(
80
+ name='RadialDiagram',
81
+ format='png',
82
+ engine='neato', # Use 'neato' or 'fdp' for radial/force-directed layout
83
+ graph_attr={
84
+ 'overlap': 'false', # Prevent node overlap
85
+ 'splines': 'true', # Smooth splines for edges
86
+ 'bgcolor': 'white', # White background
87
+ 'pad': '0.5', # Padding around the graph
88
+ 'layout': 'neato' # Explicitly set layout engine for consistency
89
+ },
90
+ node_attr={
91
+ 'fixedsize': 'false' # Allow nodes to resize based on content
92
+ }
93
+ )
94
+
95
+ base_color = '#19191a' # Hardcoded base color
96
+
97
+ dot.node(
98
+ 'central',
99
+ data['central_node'],
100
+ shape='box', # Rectangular shape
101
+ style='filled,rounded', # Filled and rounded corners
102
+ fillcolor=base_color, # Darkest color
103
+ fontcolor='white', # White text for dark background
104
+ fontsize='16' # Larger font for central node
105
+ )
106
+
107
+ add_nodes_and_edges(dot, 'central', data.get('nodes', []), current_depth=1, base_color=base_color)
108
+
109
+ with NamedTemporaryFile(delete=False, suffix=f'.{output_format}') as tmp:
110
+ dot.render(tmp.name, format=output_format, cleanup=True)
111
+ return f"{tmp.name}.{output_format}"
112
+
113
+ except json.JSONDecodeError:
114
+ return "Error: Invalid JSON format"
115
+ except Exception as e:
116
+ return f"Error: {str(e)}"
117
+
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ gradio[mcp]
2
+ graphviz
3
+ pydot
sample_data.py ADDED
@@ -0,0 +1,478 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ CONCEPT_MAP_JSON = """
2
+ {
3
+ "central_node": "Artificial Intelligence (AI)",
4
+ "nodes": [
5
+ {
6
+ "id": "ml_fundamental",
7
+ "label": "Machine Learning",
8
+ "relationship": "is essential for",
9
+ "subnodes": [
10
+ {
11
+ "id": "dl_branch",
12
+ "label": "Deep Learning",
13
+ "relationship": "for example",
14
+ "subnodes": [
15
+ {
16
+ "id": "cnn_example",
17
+ "label": "CNNs",
18
+ "relationship": "for example"
19
+ },
20
+ {
21
+ "id": "rnn_example",
22
+ "label": "RNNs",
23
+ "relationship": "for example"
24
+ }
25
+ ]
26
+ },
27
+ {
28
+ "id": "rl_branch",
29
+ "label": "Reinforcement Learning",
30
+ "relationship": "for example",
31
+ "subnodes": [
32
+ {
33
+ "id": "qlearning_example",
34
+ "label": "Q-Learning",
35
+ "relationship": "example"
36
+ },
37
+ {
38
+ "id": "pg_example",
39
+ "label": "Policy Gradients",
40
+ "relationship": "example"
41
+ }
42
+ ]
43
+ }
44
+ ]
45
+ },
46
+ {
47
+ "id": "ai_types",
48
+ "label": "Types",
49
+ "relationship": "formed by",
50
+ "subnodes": [
51
+ {
52
+ "id": "agi_type",
53
+ "label": "AGI",
54
+ "relationship": "this is",
55
+ "subnodes": [
56
+ {
57
+ "id": "strong_ai",
58
+ "label": "Strong AI",
59
+ "relationship": "provoked by",
60
+ "subnodes": [
61
+ {
62
+ "id": "human_intel",
63
+ "label": "Human-level Intel.",
64
+ "relationship": "of"
65
+ }
66
+ ]
67
+ }
68
+ ]
69
+ },
70
+ {
71
+ "id": "ani_type",
72
+ "label": "ANI",
73
+ "relationship": "this is",
74
+ "subnodes": [
75
+ {
76
+ "id": "weak_ai",
77
+ "label": "Weak AI",
78
+ "relationship": "provoked by",
79
+ "subnodes": [
80
+ {
81
+ "id": "narrow_tasks",
82
+ "label": "Narrow Tasks",
83
+ "relationship": "of"
84
+ }
85
+ ]
86
+ }
87
+ ]
88
+ }
89
+ ]
90
+ },
91
+ {
92
+ "id": "ai_capabilities",
93
+ "label": "Capabilities",
94
+ "relationship": "change",
95
+ "subnodes": [
96
+ {
97
+ "id": "data_proc",
98
+ "label": "Data Processing",
99
+ "relationship": "can",
100
+ "subnodes": [
101
+ {
102
+ "id": "big_data",
103
+ "label": "Big Data",
104
+ "relationship": "as",
105
+ "subnodes": [
106
+ {
107
+ "id": "analysis_example",
108
+ "label": "Data Analysis",
109
+ "relationship": "example"
110
+ },
111
+ {
112
+ "id": "prediction_example",
113
+ "label": "Prediction",
114
+ "relationship": "example"
115
+ }
116
+ ]
117
+ }
118
+ ]
119
+ },
120
+ {
121
+ "id": "decision_making",
122
+ "label": "Decision Making",
123
+ "relationship": "can be",
124
+ "subnodes": [
125
+ {
126
+ "id": "automation",
127
+ "label": "Automation",
128
+ "relationship": "as",
129
+ "subnodes": [
130
+ {
131
+ "id": "robotics_example",
132
+ "label": "Robotics",
133
+ "relationship": "Example"},
134
+ {
135
+ "id": "autonomous_example",
136
+ "label": "Autonomous Vehicles",
137
+ "relationship": "of one"
138
+ }
139
+ ]
140
+ }
141
+ ]
142
+ },
143
+ {
144
+ "id": "problem_solving",
145
+ "label": "Problem Solving",
146
+ "relationship": "can",
147
+ "subnodes": [
148
+ {
149
+ "id": "optimization",
150
+ "label": "Optimization",
151
+ "relationship": "as is",
152
+ "subnodes": [
153
+ {
154
+ "id": "algorithms_example",
155
+ "label": "Algorithms",
156
+ "relationship": "for example"
157
+ }
158
+ ]
159
+ }
160
+ ]
161
+ }
162
+ ]
163
+ }
164
+ ]
165
+ }
166
+ """
167
+
168
+ # JSON for Synoptic Chart (horizontal hierarchy) - AI related, 4 levels
169
+ SYNOPTIC_CHART_JSON = """
170
+ {
171
+ "central_node": "AI Project Lifecycle",
172
+ "nodes": [
173
+ {
174
+ "id": "phase1",
175
+ "label": "I. Problem Definition & Data Acquisition",
176
+ "relationship": "Starts with",
177
+ "subnodes": [
178
+ {
179
+ "id": "sub1_1",
180
+ "label": "1. Problem Formulation",
181
+ "relationship": "Involves",
182
+ "subnodes": [
183
+ {"id": "sub1_1_1", "label": "1.1. Identify Business Need", "relationship": "e.g."},
184
+ {"id": "sub1_1_2", "label": "1.2. Define KPIs", "relationship": "e.g."}
185
+ ]
186
+ },
187
+ {
188
+ "id": "sub1_2",
189
+ "label": "2. Data Collection",
190
+ "relationship": "Followed by",
191
+ "subnodes": [
192
+ {"id": "sub1_2_1", "label": "2.1. Source Data", "relationship": "from"},
193
+ {"id": "sub1_2_2", "label": "2.2. Data Cleaning", "relationship": "includes"}
194
+ ]
195
+ }
196
+ ]
197
+ },
198
+ {
199
+ "id": "phase2",
200
+ "label": "II. Model Development",
201
+ "relationship": "Proceeds to",
202
+ "subnodes": [
203
+ {
204
+ "id": "sub2_1",
205
+ "label": "1. Feature Engineering",
206
+ "relationship": "Comprises",
207
+ "subnodes": [
208
+ {"id": "sub2_1_1", "label": "1.1. Feature Selection", "relationship": "e.g."},
209
+ {"id": "sub2_1_2", "label": "1.2. Feature Transformation", "relationship": "e.g."}
210
+ ]
211
+ },
212
+ {
213
+ "id": "sub2_2",
214
+ "label": "2. Model Training",
215
+ "relationship": "Involves",
216
+ "subnodes": [
217
+ {"id": "sub2_2_1", "label": "2.1. Algorithm Selection", "relationship": "uses"},
218
+ {"id": "sub2_2_2", "label": "2.2. Hyperparameter Tuning", "relationship": "optimizes"}
219
+ ]
220
+ }
221
+ ]
222
+ },
223
+ {
224
+ "id": "phase3",
225
+ "label": "III. Evaluation & Deployment",
226
+ "relationship": "Culminates in",
227
+ "subnodes": [
228
+ {
229
+ "id": "sub3_1",
230
+ "label": "1. Model Evaluation",
231
+ "relationship": "Includes",
232
+ "subnodes": [
233
+ {"id": "sub3_1_1", "label": "1.1. Performance Metrics", "relationship": "measures"},
234
+ {"id": "sub3_1_2", "label": "1.2. Bias & Fairness Audits", "relationship": "ensures"}
235
+ ]
236
+ },
237
+ {
238
+ "id": "sub3_2",
239
+ "label": "2. Deployment & Monitoring",
240
+ "relationship": "Requires",
241
+ "subnodes": [
242
+ {"id": "sub3_2_1", "label": "2.1. API/Integration Development", "relationship": "for"},
243
+ {"id": "sub3_2_2", "label": "2.2. Continuous Monitoring", "relationship": "ensures"}
244
+ ]
245
+ }
246
+ ]
247
+ }
248
+ ]
249
+ }
250
+ """
251
+
252
+ # JSON for Radial Diagram (central expansion) - AI related, 3 levels with 5->10 structure
253
+ RADIAL_DIAGRAM_JSON = """
254
+ {
255
+ "central_node": "AI Core Concepts & Domains",
256
+ "nodes": [
257
+ {
258
+ "id": "foundational_ml",
259
+ "label": "Foundational ML",
260
+ "relationship": "builds on",
261
+ "subnodes": [
262
+ {"id": "supervised_l", "label": "Supervised Learning", "relationship": "e.g."},
263
+ {"id": "unsupervised_l", "label": "Unsupervised Learning", "relationship": "e.g."}
264
+ ]
265
+ },
266
+ {
267
+ "id": "dl_architectures",
268
+ "label": "Deep Learning Arch.",
269
+ "relationship": "evolved from",
270
+ "subnodes": [
271
+ {"id": "cnns_rad", "label": "CNNs", "relationship": "e.g."},
272
+ {"id": "rnns_rad", "label": "RNNs", "relationship": "e.g."}
273
+ ]
274
+ },
275
+ {
276
+ "id": "major_applications",
277
+ "label": "Major AI Applications",
278
+ "relationship": "applied in",
279
+ "subnodes": [
280
+ {"id": "nlp_rad", "label": "Natural Language Processing", "relationship": "e.g."},
281
+ {"id": "cv_rad", "label": "Computer Vision", "relationship": "e.g."}
282
+ ]
283
+ },
284
+ {
285
+ "id": "ethical_concerns",
286
+ "label": "Ethical AI Concerns",
287
+ "relationship": "addresses",
288
+ "subnodes": [
289
+ {"id": "fairness_rad", "label": "Fairness & Bias", "relationship": "e.g."},
290
+ {"id": "explainability", "label": "Explainability (XAI)", "relationship": "e.g."}
291
+ ]
292
+ },
293
+ {
294
+ "id": "future_trends",
295
+ "label": "Future AI Trends",
296
+ "relationship": "looking at",
297
+ "subnodes": [
298
+ {"id": "agi_future", "label": "AGI Development", "relationship": "e.g."},
299
+ {"id": "quantum_ai", "label": "Quantum AI", "relationship": "e.g."}
300
+ ]
301
+ }
302
+ ]
303
+ }
304
+ """
305
+
306
+ PROCESS_FLOW_JSON = """
307
+ {
308
+ "start_node": "Start Inference Request",
309
+ "nodes": [
310
+ {
311
+ "id": "user_input",
312
+ "label": "Receive User Input (Data)",
313
+ "type": "io"
314
+ },
315
+ {
316
+ "id": "preprocess_data",
317
+ "label": "Preprocess Data",
318
+ "type": "process"
319
+ },
320
+ {
321
+ "id": "validate_data",
322
+ "label": "Validate Data Format/Type",
323
+ "type": "decision"
324
+ },
325
+ {
326
+ "id": "data_valid_yes",
327
+ "label": "Data Valid?",
328
+ "type": "decision"
329
+ },
330
+ {
331
+ "id": "load_model",
332
+ "label": "Load AI Model (if not cached)",
333
+ "type": "process"
334
+ },
335
+ {
336
+ "id": "run_inference",
337
+ "label": "Run AI Model Inference",
338
+ "type": "process"
339
+ },
340
+ {
341
+ "id": "postprocess_output",
342
+ "label": "Postprocess Model Output",
343
+ "type": "process"
344
+ },
345
+ {
346
+ "id": "send_response",
347
+ "label": "Send Response to User",
348
+ "type": "io"
349
+ },
350
+ {
351
+ "id": "log_error",
352
+ "label": "Log Error & Notify User",
353
+ "type": "process"
354
+ },
355
+ {
356
+ "id": "end_inference_process",
357
+ "label": "End Inference Process",
358
+ "type": "end"
359
+ }
360
+ ],
361
+ "connections": [
362
+ {"from": "start_node", "to": "user_input", "label": "Request"},
363
+ {"from": "user_input", "to": "preprocess_data", "label": "Data Received"},
364
+ {"from": "preprocess_data", "to": "validate_data", "label": "Cleaned"},
365
+ {"from": "validate_data", "to": "data_valid_yes", "label": "Check"},
366
+ {"from": "data_valid_yes", "to": "load_model", "label": "Yes"},
367
+ {"from": "data_valid_yes", "to": "log_error", "label": "No"},
368
+ {"from": "load_model", "to": "run_inference", "label": "Model Ready"},
369
+ {"from": "run_inference", "to": "postprocess_output", "label": "Output Generated"},
370
+ {"from": "postprocess_output", "to": "send_response", "label": "Ready"},
371
+ {"from": "send_response", "to": "end_inference_process", "label": "Response Sent"},
372
+ {"from": "log_error", "to": "end_inference_process", "label": "Error Handled"}
373
+ ]
374
+ }
375
+ """
376
+
377
+ # New JSON for Work Breakdown Structure (WBS) Diagram - similar to image, but not identical
378
+ WBS_DIAGRAM_JSON = """
379
+ {
380
+ "project_title": "AI Model Development Project",
381
+ "phases": [
382
+ {
383
+ "id": "phase_prep",
384
+ "label": "Preparation",
385
+ "tasks": [
386
+ {
387
+ "id": "task_1_1_vision",
388
+ "label": "Identify Vision",
389
+ "subtasks": [
390
+ {
391
+ "id": "subtask_1_1_1_design_staff",
392
+ "label": "Design & Staffing",
393
+ "sub_subtasks": [
394
+ {
395
+ "id": "ss_task_1_1_1_1_env_setup",
396
+ "label": "Environment Setup",
397
+ "sub_sub_subtasks": [
398
+ {
399
+ "id": "sss_task_1_1_1_1_1_lib_install",
400
+ "label": "Install Libraries",
401
+ "final_level_tasks": [
402
+ {"id": "ft_1_1_1_1_1_1_data_access", "label": "Grant Data Access"}
403
+ ]
404
+ }
405
+ ]
406
+ }
407
+ ]
408
+ }
409
+ ]
410
+ }
411
+ ]
412
+ },
413
+ {
414
+ "id": "phase_plan",
415
+ "label": "Planning",
416
+ "tasks": [
417
+ {
418
+ "id": "task_2_1_cost_analysis",
419
+ "label": "Cost Analysis",
420
+ "subtasks": [
421
+ {
422
+ "id": "subtask_2_1_1_benefit_analysis",
423
+ "label": "Benefit Analysis",
424
+ "sub_subtasks": [
425
+ {
426
+ "id": "ss_task_2_1_1_1_risk_assess",
427
+ "label": "AI Risk Assessment",
428
+ "sub_sub_subtasks": [
429
+ {
430
+ "id": "sss_task_2_1_1_1_1_model_selection",
431
+ "label": "Model Selection",
432
+ "final_level_tasks": [
433
+ {"id": "ft_2_1_1_1_1_1_data_strategy", "label": "Data Strategy"}
434
+ ]
435
+ }
436
+ ]
437
+ }
438
+ ]
439
+ }
440
+ ]
441
+ }
442
+ ]
443
+ },
444
+ {
445
+ "id": "phase_dev",
446
+ "label": "Development",
447
+ "tasks": [
448
+ {
449
+ "id": "task_3_1_change_mgmt",
450
+ "label": "Data Preprocessing",
451
+ "subtasks": [
452
+ {
453
+ "id": "subtask_3_1_1_implementation",
454
+ "label": "Feature Engineering",
455
+ "sub_subtasks": [
456
+ {
457
+ "id": "ss_task_3_1_1_1_beta_testing",
458
+ "label": "Model Training",
459
+ "sub_sub_subtasks": [
460
+ {
461
+ "id": "sss_task_3_1_1_1_1_other_task",
462
+ "label": "Model Evaluation",
463
+ "final_level_tasks": [
464
+ {"id": "ft_3_1_1_1_1_1_hyperparam_tune", "label": "Hyperparameter Tuning"}
465
+ ]
466
+ }
467
+ ]
468
+ }
469
+ ]
470
+ }
471
+ ]
472
+ }
473
+ ]
474
+ }
475
+ ]
476
+ }
477
+
478
+ """
synoptic_chart_generator.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import graphviz
2
+ import json
3
+ from tempfile import NamedTemporaryFile
4
+ import os
5
+ from graph_generator_utils import add_nodes_and_edges
6
+
7
+ def generate_synoptic_chart(json_input: str, output_format: str) -> str:
8
+ """
9
+ Generates a synoptic chart (horizontal flowchart) from JSON input.
10
+
11
+ Args:
12
+ json_input (str): A JSON string describing the synoptic chart structure.
13
+ It must follow the Expected JSON Format Example below.
14
+
15
+ Expected JSON Format Example:
16
+ {
17
+ "central_node": "AI Project Lifecycle",
18
+ "nodes": [
19
+ {
20
+ "id": "phase1",
21
+ "label": "I. Problem Definition & Data Acquisition",
22
+ "relationship": "Starts with",
23
+ "subnodes": [
24
+ {
25
+ "id": "sub1_1",
26
+ "label": "1. Problem Formulation",
27
+ "relationship": "Involves",
28
+ "subnodes": [
29
+ {"id": "sub1_1_1", "label": "1.1. Identify Business Need", "relationship": "e.g."},
30
+ {"id": "sub1_1_2", "label": "1.2. Define KPIs", "relationship": "e.g."}
31
+ ]
32
+ },
33
+ {
34
+ "id": "sub1_2",
35
+ "label": "2. Data Collection",
36
+ "relationship": "Followed by",
37
+ "subnodes": [
38
+ {"id": "sub1_2_1", "label": "2.1. Source Data", "relationship": "from"},
39
+ {"id": "sub1_2_2", "label": "2.2. Data Cleaning", "relationship": "includes"}
40
+ ]
41
+ }
42
+ ]
43
+ },
44
+ {
45
+ "id": "phase2",
46
+ "label": "II. Model Development",
47
+ "relationship": "Proceeds to",
48
+ "subnodes": [
49
+ {
50
+ "id": "sub2_1",
51
+ "label": "1. Feature Engineering",
52
+ "relationship": "Comprises",
53
+ "subnodes": [
54
+ {"id": "sub2_1_1", "label": "1.1. Feature Selection", "relationship": "e.g."},
55
+ {"id": "sub2_1_2", "label": "1.2. Feature Transformation", "relationship": "e.g."}
56
+ ]
57
+ },
58
+ {
59
+ "id": "sub2_2",
60
+ "label": "2. Model Training",
61
+ "relationship": "Involves",
62
+ "subnodes": [
63
+ {"id": "sub2_2_1", "label": "2.1. Algorithm Selection", "relationship": "uses"},
64
+ {"id": "sub2_2_2", "label": "2.2. Hyperparameter Tuning", "relationship": "optimizes"}
65
+ ]
66
+ }
67
+ ]
68
+ },
69
+ {
70
+ "id": "phase3",
71
+ "label": "III. Evaluation & Deployment",
72
+ "relationship": "Culminates in",
73
+ "subnodes": [
74
+ {
75
+ "id": "sub3_1",
76
+ "label": "1. Model Evaluation",
77
+ "relationship": "Includes",
78
+ "subnodes": [
79
+ {"id": "sub3_1_1", "label": "1.1. Performance Metrics", "relationship": "measures"},
80
+ {"id": "sub3_1_2", "label": "1.2. Bias & Fairness Audits", "relationship": "ensures"}
81
+ ]
82
+ },
83
+ {
84
+ "id": "sub3_2",
85
+ "label": "2. Deployment & Monitoring",
86
+ "relationship": "Requires",
87
+ "subnodes": [
88
+ {"id": "sub3_2_1", "label": "2.1. API/Integration Development", "relationship": "for"},
89
+ {"id": "sub3_2_2", "label": "2.2. Continuous Monitoring", "relationship": "ensures"}
90
+ ]
91
+ }
92
+ ]
93
+ }
94
+ ]
95
+ }
96
+
97
+ Returns:
98
+ str: The filepath to the generated PNG image file.
99
+ """
100
+ try:
101
+ if not json_input.strip():
102
+ return "Error: Empty input"
103
+
104
+ data = json.loads(json_input)
105
+
106
+ if 'central_node' not in data or 'nodes' not in data:
107
+ raise ValueError("Missing required fields: central_node or nodes")
108
+
109
+ dot = graphviz.Digraph(
110
+ name='SynopticChart',
111
+ format='png',
112
+ graph_attr={
113
+ 'rankdir': 'LR', # Left-to-Right layout (horizontal hierarchy)
114
+ 'splines': 'ortho', # Straight lines
115
+ 'bgcolor': 'white', # White background
116
+ 'pad': '0.5', # Padding around the graph
117
+ 'ranksep': '0.7', # Reduced horizontal separation between ranks (columns)
118
+ 'nodesep': '0.3' # Adjusted vertical separation between nodes in the same rank
119
+ }
120
+ )
121
+
122
+ base_color = '#19191a'
123
+
124
+ dot.node(
125
+ 'central',
126
+ data['central_node'],
127
+ shape='box', # Rectangular shape
128
+ style='filled,rounded', # Filled and rounded corners
129
+ fillcolor=base_color, # Darkest color
130
+ fontcolor='white', # White text for dark background
131
+ fontsize='16' # Larger font for central node
132
+ )
133
+
134
+ add_nodes_and_edges(dot, 'central', data.get('nodes', []), current_depth=1, base_color=base_color)
135
+
136
+ with NamedTemporaryFile(delete=False, suffix=f'.{output_format}') as tmp:
137
+ dot.render(tmp.name, format=output_format, cleanup=True)
138
+ return f"{tmp.name}.{output_format}"
139
+
140
+ except json.JSONDecodeError:
141
+ return "Error: Invalid JSON format"
142
+ except Exception as e:
143
+ return f"Error: {str(e)}"
144
+
wbs_diagram_generator.py ADDED
@@ -0,0 +1,243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import graphviz
2
+ import json
3
+ from tempfile import NamedTemporaryFile
4
+ import os
5
+ from graph_generator_utils import add_nodes_and_edges
6
+
7
+ def generate_wbs_diagram(json_input: str, output_format: str) -> str:
8
+ """
9
+ Generates a Work Breakdown Structure (WBS) Diagram from JSON input.
10
+
11
+ Args:
12
+ json_input (str): A JSON string describing the WBS structure.
13
+ It must follow the Expected JSON Format Example below.
14
+
15
+ Expected JSON Format Example:
16
+ {
17
+ "project_title": "AI Model Development Project",
18
+ "phases": [
19
+ {
20
+ "id": "phase_prep",
21
+ "label": "Preparation",
22
+ "tasks": [
23
+ {
24
+ "id": "task_1_1_vision",
25
+ "label": "Identify Vision",
26
+ "subtasks": [
27
+ {
28
+ "id": "subtask_1_1_1_design_staff",
29
+ "label": "Design & Staffing",
30
+ "sub_subtasks": [
31
+ {
32
+ "id": "ss_task_1_1_1_1_env_setup",
33
+ "label": "Environment Setup",
34
+ "sub_sub_subtasks": [
35
+ {
36
+ "id": "sss_task_1_1_1_1_1_lib_install",
37
+ "label": "Install Libraries",
38
+ "final_level_tasks": [
39
+ {"id": "ft_1_1_1_1_1_1_data_access", "label": "Grant Data Access"}
40
+ ]
41
+ }
42
+ ]
43
+ }
44
+ ]
45
+ }
46
+ ]
47
+ }
48
+ ]
49
+ },
50
+ {
51
+ "id": "phase_plan",
52
+ "label": "Planning",
53
+ "tasks": [
54
+ {
55
+ "id": "task_2_1_cost_analysis",
56
+ "label": "Cost Analysis",
57
+ "subtasks": [
58
+ {
59
+ "id": "subtask_2_1_1_benefit_analysis",
60
+ "label": "Benefit Analysis",
61
+ "sub_subtasks": [
62
+ {
63
+ "id": "ss_task_2_1_1_1_risk_assess",
64
+ "label": "AI Risk Assessment",
65
+ "sub_sub_subtasks": [
66
+ {
67
+ "id": "sss_task_2_1_1_1_1_model_selection",
68
+ "label": "Model Selection",
69
+ "final_level_tasks": [
70
+ {"id": "ft_2_1_1_1_1_1_data_strategy", "label": "Data Strategy"}
71
+ ]
72
+ }
73
+ ]
74
+ }
75
+ ]
76
+ }
77
+ ]
78
+ }
79
+ ]
80
+ },
81
+ {
82
+ "id": "phase_dev",
83
+ "label": "Development",
84
+ "tasks": [
85
+ {
86
+ "id": "task_3_1_change_mgmt",
87
+ "label": "Data Preprocessing",
88
+ "subtasks": [
89
+ {
90
+ "id": "subtask_3_1_1_implementation",
91
+ "label": "Feature Engineering",
92
+ "sub_subtasks": [
93
+ {
94
+ "id": "ss_task_3_1_1_1_beta_testing",
95
+ "label": "Model Training",
96
+ "sub_sub_subtasks": [
97
+ {
98
+ "id": "sss_task_3_1_1_1_1_other_task",
99
+ "label": "Model Evaluation",
100
+ "final_level_tasks": [
101
+ {"id": "ft_3_1_1_1_1_1_hyperparam_tune", "label": "Hyperparameter Tuning"}
102
+ ]
103
+ }
104
+ ]
105
+ }
106
+ ]
107
+ }
108
+ ]
109
+ }
110
+ ]
111
+ }
112
+ ]
113
+ }
114
+
115
+ Returns:
116
+ str: The filepath to the generated PNG image file.
117
+ """
118
+ try:
119
+ if not json_input.strip():
120
+ return "Error: Empty input"
121
+
122
+ data = json.loads(json_input)
123
+
124
+ if 'project_title' not in data or 'phases' not in data:
125
+ raise ValueError("Missing required fields: project_title or phases")
126
+
127
+ dot = graphviz.Digraph(
128
+ name='WBSDiagram',
129
+ format='png',
130
+ graph_attr={
131
+ 'rankdir': 'TB', # Top-to-Bottom hierarchy
132
+ 'splines': 'ortho', # Straight lines
133
+ 'bgcolor': 'white', # White background
134
+ 'pad': '0.5', # Padding
135
+ 'ranksep': '0.6', # Adjust vertical separation between ranks
136
+ 'nodesep': '0.5' # Adjust horizontal separation between nodes
137
+ }
138
+ )
139
+
140
+ base_color = '#19191a' # Hardcoded base color
141
+
142
+ # Project Title node (main node)
143
+ dot.node(
144
+ 'project_root',
145
+ data['project_title'],
146
+ shape='box',
147
+ style='filled,rounded',
148
+ fillcolor=base_color,
149
+ fontcolor='white',
150
+ fontsize='18'
151
+ )
152
+
153
+ # Helper for color and font based on depth for WBS
154
+ def get_gradient_color(depth, base_hex_color, lightening_factor=0.12):
155
+ base_r = int(base_hex_color[1:3], 16)
156
+ base_g = int(base_hex_color[3:5], 16)
157
+ base_b = int(base_hex_color[5:7], 16)
158
+
159
+ current_r = base_r + int((255 - base_r) * depth * lightening_factor)
160
+ current_g = base_g + int((255 - base_g) * depth * lightening_factor)
161
+ current_b = base_b + int((255 - base_b) * depth * lightening_factor)
162
+
163
+ return f'#{min(255, current_r):02x}{min(255, current_g):02x}{min(255, current_b):02x}'
164
+
165
+ def get_font_color_for_background(depth, base_hex_color, lightening_factor=0.12):
166
+ base_r = int(base_hex_color[1:3], 16)
167
+ base_g = int(base_hex_color[3:5], 16)
168
+ base_b = int(base_hex_color[5:7], 16)
169
+ current_r = base_r + (255 - base_r) * depth * lightening_factor
170
+ current_g = base_g + (255 - base_g) * depth * lightening_factor
171
+ current_b = base_b + (255 - base_b) * depth * lightening_factor
172
+
173
+ luminance = (0.2126 * current_r + 0.7152 * current_g + 0.0722 * current_b) / 255
174
+ return 'white' if luminance < 0.5 else 'black'
175
+
176
+ def _add_wbs_nodes_recursive(parent_id, current_level_tasks, current_depth):
177
+ for task_data in current_level_tasks:
178
+ task_id = task_data.get('id')
179
+ task_label = task_data.get('label')
180
+
181
+ if not all([task_id, task_label]):
182
+ raise ValueError(f"Invalid task data at depth {current_depth}: {task_data}")
183
+
184
+ node_fill_color = get_gradient_color(current_depth, base_color)
185
+ node_font_color = get_font_color_for_background(current_depth, base_color)
186
+ font_size = max(9, 14 - (current_depth * 2))
187
+
188
+ dot.node(
189
+ task_id,
190
+ task_label,
191
+ shape='box',
192
+ style='filled,rounded',
193
+ fillcolor=node_fill_color,
194
+ fontcolor=node_font_color,
195
+ fontsize=str(font_size)
196
+ )
197
+ dot.edge(parent_id, task_id, color='#4a4a4a', arrowhead='none')
198
+
199
+ # Recursively call for next level of tasks (subtasks, sub_subtasks, etc.)
200
+ # This handles arbitrary nested keys like 'subtasks', 'sub_subtasks', 'final_level_tasks'
201
+ next_level_keys = ['tasks', 'subtasks', 'sub_subtasks', 'sub_sub_subtasks', 'final_level_tasks']
202
+ for key_idx, key in enumerate(next_level_keys):
203
+ if key in task_data and isinstance(task_data[key], list):
204
+ _add_wbs_nodes_recursive(task_id, task_data[key], current_depth + 1)
205
+ break # Only process the first found sub-level key
206
+
207
+ # Process phases (level 1 from project_root)
208
+ phase_depth = 1
209
+ for phase in data['phases']:
210
+ phase_id = phase.get('id')
211
+ phase_label = phase.get('label')
212
+
213
+ if not all([phase_id, phase_label]):
214
+ raise ValueError(f"Invalid phase data: {phase}")
215
+
216
+ phase_fill_color = get_gradient_color(phase_depth, base_color)
217
+ phase_font_color = get_font_color_for_background(phase_depth, base_color)
218
+ font_size_phase = max(9, 14 - (phase_depth * 2))
219
+
220
+ dot.node(
221
+ phase_id,
222
+ phase_label,
223
+ shape='box',
224
+ style='filled,rounded',
225
+ fillcolor=phase_fill_color,
226
+ fontcolor=phase_font_color,
227
+ fontsize=str(font_size_phase)
228
+ )
229
+ dot.edge('project_root', phase_id, color='#4a4a4a', arrowhead='none')
230
+
231
+ # Start recursion for tasks under this phase
232
+ if 'tasks' in phase and isinstance(phase['tasks'], list):
233
+ _add_wbs_nodes_recursive(phase_id, phase['tasks'], phase_depth + 1)
234
+
235
+ with NamedTemporaryFile(delete=False, suffix=f'.{output_format}') as tmp:
236
+ dot.render(tmp.name, format=output_format, cleanup=True)
237
+ return f"{tmp.name}.{output_format}"
238
+
239
+ except json.JSONDecodeError:
240
+ return "Error: Invalid JSON format"
241
+ except Exception as e:
242
+ return f"Error: {str(e)}"
243
+