Chart-GPT / app.py
openfree's picture
Create app.py
2a02b1f verified
raw
history blame
29.3 kB
import gradio as gr
import os
import sys
import json
import requests
# ํ˜„์žฌ ๋””๋ ‰ํ† ๋ฆฌ์™€ ํฐํŠธ ํŒŒ์ผ ๊ฒฝ๋กœ ์„ค์ •
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
FONT_PATH = os.path.join(CURRENT_DIR, 'NanumGothic-Regular.ttf')
FONTS_CONF_PATH = os.path.join(CURRENT_DIR, 'fonts.conf')
# fonts.conf ํŒŒ์ผ ์ƒ์„ฑ ํ•จ์ˆ˜
def create_fonts_conf():
"""Graphviz๊ฐ€ ๋กœ์ปฌ ํฐํŠธ๋ฅผ ์ธ์‹ํ•  ์ˆ˜ ์žˆ๋„๋ก fonts.conf ํŒŒ์ผ ์ƒ์„ฑ"""
fonts_conf_content = f"""<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
<!-- ๋กœ์ปฌ ํฐํŠธ ๋””๋ ‰ํ† ๋ฆฌ ์ถ”๊ฐ€ -->
<dir>{CURRENT_DIR}</dir>
<!-- NanumGothic ํฐํŠธ ๋ณ„์นญ ์„ค์ • -->
<alias>
<family>NanumGothic</family>
<prefer>
<family>NanumGothic-Regular</family>
</prefer>
</alias>
<alias>
<family>NanumGothic-Regular</family>
<default>
<family>NanumGothic-Regular</family>
</default>
</alias>
<!-- ํ•œ๊ธ€ ํฐํŠธ ๋งคํ•‘ -->
<match target="pattern">
<test name="family">
<string>NanumGothic</string>
</test>
<edit name="family" mode="assign">
<string>NanumGothic-Regular</string>
</edit>
</match>
</fontconfig>
"""
with open(FONTS_CONF_PATH, 'w', encoding='utf-8') as f:
f.write(fonts_conf_content)
print(f"fonts.conf ํŒŒ์ผ ์ƒ์„ฑ๋จ: {FONTS_CONF_PATH}")
# ํฐํŠธ ์„ค์ •
if os.path.exists(FONT_PATH):
print(f"ํ•œ๊ธ€ ํฐํŠธ ํŒŒ์ผ ๋ฐœ๊ฒฌ: {FONT_PATH}")
# fonts.conf ํŒŒ์ผ ์ƒ์„ฑ
create_fonts_conf()
# Graphviz๊ฐ€ ํฐํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋„๋ก ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •
os.environ['GDFONTPATH'] = CURRENT_DIR
os.environ['FONTCONFIG_PATH'] = CURRENT_DIR
os.environ['FONTCONFIG_FILE'] = FONTS_CONF_PATH
print(f"GDFONTPATH ์„ค์ •: {CURRENT_DIR}")
print(f"FONTCONFIG_FILE ์„ค์ •: {FONTS_CONF_PATH}")
else:
print(f"๊ฒฝ๊ณ : ํ•œ๊ธ€ ํฐํŠธ ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {FONT_PATH}")
# ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ํฐํŠธ ๊ฒฝ๋กœ ์ €์žฅ (generator๋“ค์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก)
os.environ['KOREAN_FONT_PATH'] = FONT_PATH
from concept_map_generator import generate_concept_map
from synoptic_chart_generator import generate_synoptic_chart
from radial_diagram_generator import generate_radial_diagram
from process_flow_generator import generate_process_flow_diagram
from wbs_diagram_generator import generate_wbs_diagram
from sample_data import CONCEPT_MAP_JSON, SYNOPTIC_CHART_JSON, RADIAL_DIAGRAM_JSON, PROCESS_FLOW_JSON, WBS_DIAGRAM_JSON
# LLM API ํ•จ์ˆ˜
def call_llm_api(prompt, diagram_type):
"""Friendli AI API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ JSON ์ƒ์„ฑ"""
token = os.environ.get("FRIENDLI_TOKEN") or "YOUR_FRIENDLI_TOKEN"
url = "https://api.friendli.ai/dedicated/v1/chat/completions"
headers = {
"Authorization": "Bearer " + token,
"Content-Type": "application/json"
}
# ๋‹ค์ด์–ด๊ทธ๋žจ ํƒ€์ž…๋ณ„ JSON ๊ตฌ์กฐ ๊ฐ€์ด๋“œ - ์‹ค์ œ ์ƒ์„ฑ๊ธฐ๊ฐ€ ์š”๊ตฌํ•˜๋Š” ์ •ํ™•ํ•œ ํ˜•์‹
json_guides = {
"Concept Map": """Generate a JSON for a concept map with the EXACT following structure:
{
"central_node": "Main Topic",
"nodes": [
{
"id": "node1",
"label": "First Concept",
"relationship": "is part of",
"subnodes": [
{
"id": "node1_1",
"label": "Sub Concept 1",
"relationship": "includes",
"subnodes": []
}
]
},
{
"id": "node2",
"label": "Second Concept",
"relationship": "relates to",
"subnodes": []
}
]
}""",
"Synoptic Chart": """Generate a JSON for a synoptic chart with the EXACT following structure:
{
"central_node": "Chart Title",
"nodes": [
{
"id": "phase1",
"label": "Phase 1 Name",
"relationship": "starts with",
"subnodes": [
{
"id": "sub1_1",
"label": "Sub Item 1",
"relationship": "includes",
"subnodes": []
}
]
}
]
}""",
"Radial Diagram": """Generate a JSON for a radial diagram with the EXACT following structure:
{
"central_node": "Central Concept",
"nodes": [
{
"id": "branch1",
"label": "Branch 1",
"relationship": "connected to",
"subnodes": [
{
"id": "item1",
"label": "Item 1",
"relationship": "example",
"subnodes": []
}
]
}
]
}""",
"Process Flow": """Generate a JSON for a process flow diagram with the EXACT following structure:
{
"start_node": "Start Process",
"nodes": [
{"id": "step1", "label": "First Step", "type": "process"},
{"id": "step2", "label": "Decision Point", "type": "decision"},
{"id": "step3", "label": "Another Step", "type": "process"},
{"id": "end", "label": "End Process", "type": "end"}
],
"connections": [
{"from": "start_node", "to": "step1", "label": "Begin"},
{"from": "step1", "to": "step2", "label": "Next"},
{"from": "step2", "to": "step3", "label": "Yes"},
{"from": "step3", "to": "end", "label": "Complete"}
]
}""",
"WBS Diagram": """Generate a JSON for a WBS diagram with the EXACT following structure:
{
"project_title": "Project Name",
"phases": [
{
"id": "phase1",
"label": "Phase 1",
"tasks": [
{
"id": "task1_1",
"label": "Task 1.1",
"subtasks": [
{
"id": "subtask1_1_1",
"label": "Subtask 1.1.1",
"sub_subtasks": []
}
]
}
]
}
]
}"""
}
system_prompt = f"""You are a helpful assistant that generates JSON structures for diagrams.
{json_guides.get(diagram_type, '')}
Important rules:
1. Generate ONLY valid JSON without any explanation or markdown formatting
2. The JSON must follow the EXACT structure shown above - do not change field names
3. Make the content relevant to the user's prompt
4. Use the user's language (Korean or English) for the content values
5. For IDs, use simple alphanumeric strings without spaces (e.g., "node1", "task1_1")
6. Ensure all connections reference existing node IDs
7. For Process Flow: 'type' can be: "process", "decision", "start", "end", "io"
8. For nested structures (Concept Map, Synoptic Chart, Radial, WBS), include empty 'subnodes' or 'subtasks' arrays when there are no children
9. Do not add any additional fields not shown in the example structure"""
payload = {
"model": "dep89a2fld32mcm",
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Create a {diagram_type} JSON for: {prompt}"}
],
"max_tokens": 16384,
"top_p": 0.8,
"stream": False # ๊ฐ„๋‹จํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด stream์„ False๋กœ ์„ค์ •
}
try:
response = requests.post(url, json=payload, headers=headers, timeout=30)
# API ์‘๋‹ต ์ƒํƒœ ํ™•์ธ
if response.status_code != 200:
return json.dumps({"error": f"API returned status code {response.status_code}: {response.text}"})
response_data = response.json()
if 'choices' in response_data and len(response_data['choices']) > 0:
content = response_data['choices'][0]['message']['content']
# JSON ๋ถ€๋ถ„๋งŒ ์ถ”์ถœ (๋งˆํฌ๋‹ค์šด ์ฝ”๋“œ ๋ธ”๋ก ์ œ๊ฑฐ)
content = content.strip()
if content.startswith("```json"):
content = content[7:]
if content.startswith("```"):
content = content[3:]
if content.endswith("```"):
content = content[:-3]
# ์ถ”๊ฐ€์ ์ธ ํ…์ŠคํŠธ ์ œ๊ฑฐ (JSON ์™ธ์˜ ์„ค๋ช…์ด ์žˆ์„ ๊ฒฝ์šฐ)
content = content.strip()
# JSON ์‹œ์ž‘ ์œ„์น˜ ์ฐพ๊ธฐ
json_start = content.find('{')
if json_start != -1:
content = content[json_start:]
# JSON ๋ ์œ„์น˜ ์ฐพ๊ธฐ
bracket_count = 0
json_end = -1
for i, char in enumerate(content):
if char == '{':
bracket_count += 1
elif char == '}':
bracket_count -= 1
if bracket_count == 0:
json_end = i
break
if json_end != -1:
content = content[:json_end + 1]
return content.strip()
else:
return json.dumps({"error": "No response from LLM"})
except requests.exceptions.Timeout:
return json.dumps({"error": "Request timed out"})
except requests.exceptions.RequestException as e:
print(f"LLM API Request Error: {str(e)}")
return json.dumps({"error": f"Request failed: {str(e)}"})
except Exception as e:
print(f"LLM API Error: {str(e)}")
return json.dumps({"error": str(e)})
def generate_with_llm(prompt, diagram_type, output_format):
"""LLM์œผ๋กœ JSON์„ ์ƒ์„ฑํ•˜๊ณ  ๋‹ค์ด์–ด๊ทธ๋žจ์„ ์ƒ์„ฑ"""
if not prompt:
return None, "Please enter a prompt"
# LLM์œผ๋กœ JSON ์ƒ์„ฑ
generated_json = call_llm_api(prompt, diagram_type)
try:
# JSON ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
json_data = json.loads(generated_json)
json_str = json.dumps(json_data, indent=2, ensure_ascii=False)
# JSON ๊ตฌ์กฐ ๊ฒ€์ฆ
if diagram_type in ["Concept Map", "Synoptic Chart", "Radial Diagram"]:
if "central_node" not in json_data or "nodes" not in json_data:
return None, f"Invalid JSON structure for {diagram_type}. Missing 'central_node' or 'nodes'. Generated JSON:\n{json_str}"
elif diagram_type == "Process Flow":
if "start_node" not in json_data or "nodes" not in json_data or "connections" not in json_data:
return None, f"Invalid JSON structure for Process Flow. Missing 'start_node', 'nodes', or 'connections'. Generated JSON:\n{json_str}"
elif diagram_type == "WBS Diagram":
if "project_title" not in json_data or "phases" not in json_data:
return None, f"Invalid JSON structure for WBS. Missing 'project_title' or 'phases'. Generated JSON:\n{json_str}"
# ๋‹ค์ด์–ด๊ทธ๋žจ ์ƒ์„ฑ
try:
if diagram_type == "Concept Map":
diagram = generate_concept_map(json_str, output_format)
elif diagram_type == "Synoptic Chart":
diagram = generate_synoptic_chart(json_str, output_format)
elif diagram_type == "Radial Diagram":
diagram = generate_radial_diagram(json_str, output_format)
elif diagram_type == "Process Flow":
diagram = generate_process_flow_diagram(json_str, output_format)
elif diagram_type == "WBS Diagram":
diagram = generate_wbs_diagram(json_str, output_format)
else:
return None, "Invalid diagram type"
# ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๊ฐ€ ๋ฐ˜ํ™˜๋œ ๊ฒฝ์šฐ
if isinstance(diagram, str) and diagram.startswith("Error:"):
return None, f"Diagram generation error: {diagram}\n\nGenerated JSON:\n{json_str}"
return diagram, json_str
except Exception as e:
return None, f"Error generating diagram: {str(e)}\n\nGenerated JSON:\n{json_str}"
except json.JSONDecodeError as e:
return None, f"Invalid JSON generated: {str(e)}\n\nGenerated content:\n{generated_json}"
except Exception as e:
return None, f"Unexpected error: {str(e)}\n\nGenerated content:\n{generated_json}"
if __name__ == "__main__":
DEFAULT_BASE_COLOR = '#19191a'
with gr.Blocks(
title="Advanced Graph Generator",
theme=gr.themes.Soft(
primary_hue="violet",
secondary_hue="purple",
),
css="""
/* ๊ทธ๋ผ๋””์–ธํŠธ ๋ฐฐ๊ฒฝ */
.gradio-container {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
}
/* ๋ฉ”์ธ ์ปจํ…Œ์ด๋„ˆ ์Šคํƒ€์ผ */
.main-container {
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 30px;
margin: 20px auto;
max-width: 1400px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
}
/* ํ—ค๋” ์Šคํƒ€์ผ */
.header-section {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 40px;
border-radius: 15px;
margin-bottom: 30px;
text-align: center;
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
}
.header-section h1 {
font-size: 2.5em;
font-weight: 700;
margin-bottom: 10px;
}
.header-section p {
font-size: 1.2em;
opacity: 0.9;
}
/* ํƒญ ์Šคํƒ€์ผ */
.gr-tab-item {
padding: 15px 30px;
font-size: 1.1em;
font-weight: 600;
background: white;
border-radius: 10px 10px 0 0;
transition: all 0.3s ease;
margin-right: 5px;
}
.gr-tab-item:hover {
background: linear-gradient(135deg, #f5f7fa 0%, #e9ecef 100%);
}
.gr-tab-item.selected {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white !important;
}
/* ๋ฒ„ํŠผ ์Šคํƒ€์ผ */
.gr-button {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
padding: 12px 30px;
font-size: 1.1em;
font-weight: 600;
border-radius: 10px;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
}
.gr-button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
}
.gr-button.primary {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
/* ์ž…๋ ฅ ํ•„๋“œ ์Šคํƒ€์ผ */
.gr-textbox, .gr-dropdown {
border: 2px solid #e9ecef;
border-radius: 10px;
padding: 12px;
font-size: 1em;
transition: all 0.3s ease;
background: white;
}
.gr-textbox:focus, .gr-dropdown:focus {
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
/* ํŒจ๋„ ์Šคํƒ€์ผ */
.panel-section {
background: white;
border-radius: 15px;
padding: 25px;
margin-bottom: 20px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
}
/* ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ ์Šคํƒ€์ผ */
.gr-image {
border-radius: 15px;
overflow: hidden;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
/* ์˜ˆ์ œ ์ด๋ฏธ์ง€ ์Šคํƒ€์ผ */
.example-images {
gap: 20px;
}
.example-images .gr-image {
transition: transform 0.3s ease;
}
.example-images .gr-image:hover {
transform: scale(1.02);
}
/* ๋ผ๋””์˜ค ๋ฒ„ํŠผ ์Šคํƒ€์ผ */
.gr-radio {
background: white;
padding: 15px;
border-radius: 10px;
border: 2px solid #e9ecef;
}
/* LLM ํƒญ ํŠน๋ณ„ ์Šคํƒ€์ผ */
.llm-tab {
background: linear-gradient(135deg, #f5f7fa 0%, #ffffff 100%);
padding: 30px;
border-radius: 15px;
}
.llm-input-section {
background: white;
padding: 25px;
border-radius: 15px;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
margin-bottom: 20px;
}
/* ๋ฐ˜์‘ํ˜• ๋””์ž์ธ */
@media (max-width: 768px) {
.main-container {
padding: 15px;
margin: 10px;
}
.header-section h1 {
font-size: 2em;
}
.gr-tab-item {
padding: 10px 15px;
font-size: 1em;
}
}
"""
) as demo:
with gr.Column(elem_classes=["main-container"]):
with gr.Column(elem_classes=["header-section"]):
gr.Markdown(
"""
# ๐ŸŽจ Graphify: AI-Powered Diagram Generator
### Transform your ideas into beautiful diagrams instantly with AI โšก
"""
)
with gr.Row(variant="panel", elem_classes=["panel-section"]):
output_format_radio = gr.Radio(
choices=["png", "svg"],
value="png",
label="๐Ÿ“ Output Format",
interactive=True
)
with gr.Tabs():
# AI ์–ด์‹œ์Šคํ„ดํŠธ ํƒญ (์ฒซ ๋ฒˆ์งธ)
with gr.TabItem("๐Ÿค– AI Assistant", elem_classes=["llm-tab"]):
gr.Markdown(
"""
### ๐Ÿ’ก Describe your diagram in Korean or English, and AI will create it for you!
"""
)
with gr.Row():
with gr.Column(scale=1, elem_classes=["llm-input-section"]):
prompt_input = gr.Textbox(
placeholder="์˜ˆ: '๋จธ์‹ ๋Ÿฌ๋‹ ํ”„๋กœ์„ธ์Šค๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ํ”Œ๋กœ์šฐ์ฐจํŠธ๋ฅผ ๋งŒ๋“ค์–ด์ค˜' or 'Create a concept map about climate change'",
label="๐Ÿ“ Enter your prompt",
lines=3
)
diagram_type_select = gr.Dropdown(
choices=["Concept Map", "Synoptic Chart", "Radial Diagram", "Process Flow", "WBS Diagram"],
value="Concept Map",
label="๐Ÿ“Š Select Diagram Type",
interactive=True
)
generate_btn = gr.Button("โœจ Generate with AI", variant="primary", size="lg")
generated_json_output = gr.Textbox(
label="๐Ÿ“„ Generated JSON",
lines=15,
interactive=True,
visible=True
)
with gr.Column(scale=2):
ai_output_image = gr.Image(
label="๐ŸŽจ Generated Diagram",
type="filepath",
show_download_button=True,
height=600
)
generate_btn.click(
fn=generate_with_llm,
inputs=[prompt_input, diagram_type_select, output_format_radio],
outputs=[ai_output_image, generated_json_output]
)
with gr.Row(elem_classes=["panel-section"]):
gr.Examples(
examples=[
["์†Œํ”„ํŠธ์›จ์–ด ๊ฐœ๋ฐœ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ํ”„๋กœ์„ธ์Šค ํ”Œ๋กœ์šฐ๋ฅผ ๋งŒ๋“ค์–ด์ค˜", "Process Flow"],
["์ธ๊ณต์ง€๋Šฅ์˜ ์ข…๋ฅ˜์™€ ์‘์šฉ ๋ถ„์•ผ์— ๋Œ€ํ•œ ์ปจ์…‰๋งต์„ ๋งŒ๋“ค์–ด์ค˜", "Concept Map"],
["์˜จ๋ผ์ธ ์‡ผํ•‘๋ชฐ ๊ตฌ์ถ• ํ”„๋กœ์ ํŠธ์˜ WBS๋ฅผ ๋งŒ๋“ค์–ด์ค˜", "WBS Diagram"],
["์žฌ์ƒ ๊ฐ€๋Šฅ ์—๋„ˆ์ง€์˜ ์ข…๋ฅ˜๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ๋ฐฉ์‚ฌํ˜• ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๋งŒ๋“ค์–ด์ค˜", "Radial Diagram"],
["๋จธ์‹ ๋Ÿฌ๋‹ ํŒŒ์ดํ”„๋ผ์ธ์˜ ๋‹จ๊ณ„๋ณ„ ๊ตฌ์„ฑ์„ ์‹œ๋†‰ํ‹ฑ ์ฐจํŠธ๋กœ ๋ณด์—ฌ์ค˜", "Synoptic Chart"]
],
inputs=[prompt_input, diagram_type_select],
label="๐Ÿ’ญ Example Prompts"
)
# ๊ธฐ์กด ํƒญ๋“ค
with gr.TabItem("๐Ÿ—บ๏ธ Concept Map"):
with gr.Row():
with gr.Column(scale=1, elem_classes=["panel-section"]):
json_input_cm = gr.Textbox(
value=CONCEPT_MAP_JSON,
placeholder="Paste JSON following the documented format",
label="JSON Input",
lines=20
)
submit_btn_cm = gr.Button("Generate Concept Map", variant="primary")
with gr.Column(scale=2):
output_cm = gr.Image(
label="Generated Diagram",
type="filepath",
show_download_button=True,
height=500
)
submit_btn_cm.click(
fn=generate_concept_map,
inputs=[json_input_cm, output_format_radio],
outputs=output_cm
)
gr.Markdown("## ๐Ÿ“ธ Examples")
with gr.Row(elem_classes=["example-images"]):
gr.Image(value="./images/cm1.svg", label="Sample 1", show_label=True, interactive=False)
gr.Image(value="./images/cm2.svg", label="Sample 2", show_label=True, interactive=False)
with gr.TabItem("๐Ÿ“Š Synoptic Chart"):
with gr.Row():
with gr.Column(scale=1, elem_classes=["panel-section"]):
json_input_sc = gr.Textbox(
value=SYNOPTIC_CHART_JSON,
placeholder="Paste JSON following the documented format",
label="JSON Input",
lines=20
)
submit_btn_sc = gr.Button("Generate Synoptic Chart", variant="primary")
with gr.Column(scale=2):
output_sc = gr.Image(
label="Generated Diagram",
type="filepath",
show_download_button=True,
height=500
)
submit_btn_sc.click(
fn=generate_synoptic_chart,
inputs=[json_input_sc, output_format_radio],
outputs=output_sc
)
gr.Markdown("## ๐Ÿ“ธ Examples")
with gr.Row(elem_classes=["example-images"]):
gr.Image(value="./images/sc1.svg", label="Sample 1", show_label=True, interactive=False)
gr.Image(value="./images/sc2.svg", label="Sample 2", show_label=True, interactive=False)
with gr.TabItem("โ˜€๏ธ Radial Diagram"):
with gr.Row():
with gr.Column(scale=1, elem_classes=["panel-section"]):
json_input_rd = gr.Textbox(
value=RADIAL_DIAGRAM_JSON,
placeholder="Paste JSON following the documented format",
label="JSON Input",
lines=20
)
submit_btn_rd = gr.Button("Generate Radial Diagram", variant="primary")
with gr.Column(scale=2):
output_rd = gr.Image(
label="Generated Diagram",
type="filepath",
show_download_button=True,
height=500
)
submit_btn_rd.click(
fn=generate_radial_diagram,
inputs=[json_input_rd, output_format_radio],
outputs=output_rd
)
gr.Markdown("## ๐Ÿ“ธ Examples")
with gr.Row(elem_classes=["example-images"]):
gr.Image(value="./images/rd1.svg", label="Sample 1", show_label=True, interactive=False)
gr.Image(value="./images/rd2.svg", label="Sample 2", show_label=True, interactive=False)
gr.Image(value="./images/rd3.svg", label="Sample 3", show_label=True, interactive=False)
gr.Image(value="./images/rd4.svg", label="Sample 4", show_label=True, interactive=False)
with gr.TabItem("๐Ÿ”„ Process Flow"):
with gr.Row():
with gr.Column(scale=1, elem_classes=["panel-section"]):
json_input_pf = gr.Textbox(
value=PROCESS_FLOW_JSON,
placeholder="Paste JSON following the documented format",
label="JSON Input",
lines=20
)
submit_btn_pf = gr.Button("Generate Process Flow", variant="primary")
with gr.Column(scale=2):
output_pf = gr.Image(
label="Generated Diagram",
type="filepath",
show_download_button=True,
height=500
)
submit_btn_pf.click(
fn=generate_process_flow_diagram,
inputs=[json_input_pf, output_format_radio],
outputs=output_pf
)
gr.Markdown("## ๐Ÿ“ธ Examples")
with gr.Row(elem_classes=["example-images"]):
gr.Image(value="./images/pf1.svg", label="Sample 1", show_label=True, interactive=False)
gr.Image(value="./images/pf2.svg", label="Sample 2", show_label=True, interactive=False)
with gr.TabItem("๐Ÿ“‹ WBS Diagram"):
with gr.Row():
with gr.Column(scale=1, elem_classes=["panel-section"]):
json_input_wbs = gr.Textbox(
value=WBS_DIAGRAM_JSON,
placeholder="Paste JSON following the documented format",
label="JSON Input",
lines=20
)
submit_btn_wbs = gr.Button("Generate WBS Diagram", variant="primary")
with gr.Column(scale=2):
output_wbs = gr.Image(
label="Generated Diagram",
type="filepath",
show_download_button=True,
height=500
)
submit_btn_wbs.click(
fn=generate_wbs_diagram,
inputs=[json_input_wbs, output_format_radio],
outputs=output_wbs
)
gr.Markdown("## ๐Ÿ“ธ Examples")
with gr.Row(elem_classes=["example-images"]):
gr.Image(value="./images/wd1.svg", label="Sample 1", show_label=True, interactive=False)
gr.Image(value="./images/wd2.svg", label="Sample 2", show_label=True, interactive=False)
demo.launch(
mcp_server=True,
share=False,
server_port=7860,
server_name="0.0.0.0"
)