Update app.py
Browse files
app.py
CHANGED
@@ -1,890 +1,35 @@
|
|
1 |
-
import gradio as gr
|
2 |
import os
|
3 |
import sys
|
4 |
-
import
|
5 |
-
import
|
6 |
-
import re
|
7 |
-
from typing import List, Tuple
|
8 |
|
9 |
-
|
10 |
-
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
|
11 |
-
FONT_PATH = os.path.join(CURRENT_DIR, 'NanumGothic-Regular.ttf')
|
12 |
-
FONTS_CONF_PATH = os.path.join(CURRENT_DIR, 'fonts.conf')
|
13 |
-
|
14 |
-
# fonts.conf ํ์ผ ์์ฑ ํจ์
|
15 |
-
def create_fonts_conf():
|
16 |
-
"""Graphviz๊ฐ ๋ก์ปฌ ํฐํธ๋ฅผ ์ธ์ํ ์ ์๋๋ก fonts.conf ํ์ผ ์์ฑ"""
|
17 |
-
fonts_conf_content = f"""<?xml version="1.0"?>
|
18 |
-
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
|
19 |
-
<fontconfig>
|
20 |
-
<!-- ๋ก์ปฌ ํฐํธ ๋๋ ํ ๋ฆฌ ์ถ๊ฐ -->
|
21 |
-
<dir>{CURRENT_DIR}</dir>
|
22 |
-
|
23 |
-
<!-- NanumGothic ํฐํธ ๋ณ์นญ ์ค์ -->
|
24 |
-
<alias>
|
25 |
-
<family>NanumGothic</family>
|
26 |
-
<prefer>
|
27 |
-
<family>NanumGothic-Regular</family>
|
28 |
-
</prefer>
|
29 |
-
</alias>
|
30 |
-
|
31 |
-
<alias>
|
32 |
-
<family>NanumGothic-Regular</family>
|
33 |
-
<default>
|
34 |
-
<family>NanumGothic-Regular</family>
|
35 |
-
</default>
|
36 |
-
</alias>
|
37 |
-
|
38 |
-
<!-- ํ๊ธ ํฐํธ ๋งคํ -->
|
39 |
-
<match target="pattern">
|
40 |
-
<test name="family">
|
41 |
-
<string>NanumGothic</string>
|
42 |
-
</test>
|
43 |
-
<edit name="family" mode="assign">
|
44 |
-
<string>NanumGothic-Regular</string>
|
45 |
-
</edit>
|
46 |
-
</match>
|
47 |
-
</fontconfig>
|
48 |
-
"""
|
49 |
-
|
50 |
-
with open(FONTS_CONF_PATH, 'w', encoding='utf-8') as f:
|
51 |
-
f.write(fonts_conf_content)
|
52 |
-
print(f"fonts.conf ํ์ผ ์์ฑ๋จ: {FONTS_CONF_PATH}")
|
53 |
-
|
54 |
-
# ํฐํธ ์ค์
|
55 |
-
if os.path.exists(FONT_PATH):
|
56 |
-
print(f"ํ๊ธ ํฐํธ ํ์ผ ๋ฐ๊ฒฌ: {FONT_PATH}")
|
57 |
-
|
58 |
-
# fonts.conf ํ์ผ ์์ฑ
|
59 |
-
create_fonts_conf()
|
60 |
-
|
61 |
-
# Graphviz๊ฐ ํฐํธ๋ฅผ ์ฐพ์ ์ ์๋๋ก ํ๊ฒฝ ๋ณ์ ์ค์
|
62 |
-
os.environ['GDFONTPATH'] = CURRENT_DIR
|
63 |
-
os.environ['FONTCONFIG_PATH'] = CURRENT_DIR
|
64 |
-
os.environ['FONTCONFIG_FILE'] = FONTS_CONF_PATH
|
65 |
-
|
66 |
-
print(f"GDFONTPATH ์ค์ : {CURRENT_DIR}")
|
67 |
-
print(f"FONTCONFIG_FILE ์ค์ : {FONTS_CONF_PATH}")
|
68 |
-
else:
|
69 |
-
print(f"๊ฒฝ๊ณ : ํ๊ธ ํฐํธ ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค: {FONT_PATH}")
|
70 |
-
|
71 |
-
# ํ๊ฒฝ ๋ณ์๋ก ํฐํธ ๊ฒฝ๋ก ์ ์ฅ (generator๋ค์ด ์ฌ์ฉํ ์ ์๋๋ก)
|
72 |
-
os.environ['KOREAN_FONT_PATH'] = FONT_PATH
|
73 |
-
|
74 |
-
from concept_map_generator import generate_concept_map
|
75 |
-
from synoptic_chart_generator import generate_synoptic_chart
|
76 |
-
from radial_diagram_generator import generate_radial_diagram
|
77 |
-
from process_flow_generator import generate_process_flow_diagram
|
78 |
-
from wbs_diagram_generator import generate_wbs_diagram
|
79 |
-
|
80 |
-
from sample_data import CONCEPT_MAP_JSON, SYNOPTIC_CHART_JSON, RADIAL_DIAGRAM_JSON, PROCESS_FLOW_JSON, WBS_DIAGRAM_JSON
|
81 |
-
|
82 |
-
# ๋ธ๋ ์ด๋ธ ์์น API ํจ์
|
83 |
-
def brave_search(query: str) -> List[dict]:
|
84 |
-
"""๋ธ๋ ์ด๋ธ ์์น API๋ฅผ ์ฌ์ฉํ์ฌ ๊ฒ์ ์ํ"""
|
85 |
-
api_key = os.getenv("BSEARCH_API")
|
86 |
-
if not api_key:
|
87 |
-
print("Warning: BSEARCH_API environment variable not set")
|
88 |
-
return []
|
89 |
-
|
90 |
-
url = "https://api.search.brave.com/res/v1/web/search"
|
91 |
-
headers = {
|
92 |
-
"Accept": "application/json",
|
93 |
-
"X-Subscription-Token": api_key
|
94 |
-
}
|
95 |
-
params = {
|
96 |
-
"q": query,
|
97 |
-
"count": 5 # ์์ 5๊ฐ ๊ฒฐ๊ณผ๋ง ๊ฐ์ ธ์ค๊ธฐ
|
98 |
-
}
|
99 |
-
|
100 |
-
try:
|
101 |
-
response = requests.get(url, headers=headers, params=params, timeout=10)
|
102 |
-
if response.status_code == 200:
|
103 |
-
data = response.json()
|
104 |
-
results = []
|
105 |
-
if "web" in data and "results" in data["web"]:
|
106 |
-
for result in data["web"]["results"][:5]:
|
107 |
-
results.append({
|
108 |
-
"title": result.get("title", ""),
|
109 |
-
"description": result.get("description", ""),
|
110 |
-
"url": result.get("url", "")
|
111 |
-
})
|
112 |
-
return results
|
113 |
-
else:
|
114 |
-
print(f"Brave Search API error: {response.status_code}")
|
115 |
-
return []
|
116 |
-
except Exception as e:
|
117 |
-
print(f"Brave Search error: {str(e)}")
|
118 |
-
return []
|
119 |
-
|
120 |
-
def extract_keywords(prompt: str, diagram_type: str) -> str:
|
121 |
-
"""ํ๋กฌํํธ์์ ํต์ฌ ํค์๋ ์ถ์ถ"""
|
122 |
-
# ๋ค์ด์ด๊ทธ๋จ ํ์
์ ๊ฑฐ
|
123 |
-
prompt_clean = prompt.lower()
|
124 |
-
|
125 |
-
# ์ผ๋ฐ์ ์ธ ์์ฒญ ํํ ์ ๊ฑฐ
|
126 |
-
remove_patterns = [
|
127 |
-
r"create\s+a?\s*", r"make\s+a?\s*", r"generate\s+a?\s*", r"build\s+a?\s*",
|
128 |
-
r"design\s+a?\s*", r"develop\s+a?\s*", r"show\s+me\s*", r"i\s+need\s+a?\s*",
|
129 |
-
r"diagram\s*", r"chart\s*", r"map\s*", r"flow\s*", r"๋ฅผ\s*๋ง๋ค์ด\s*",
|
130 |
-
r"์์ฑํด\s*", r"๊ทธ๋ ค\s*", r"๋ณด์ฌ\s*", r"์ค๊ณ\s*", r"๊ตฌ์ถ\s*"
|
131 |
-
]
|
132 |
-
|
133 |
-
for pattern in remove_patterns:
|
134 |
-
prompt_clean = re.sub(pattern, "", prompt_clean)
|
135 |
-
|
136 |
-
# ์ฃผ์ ๋ช
์ฌ์ ํต์ฌ ์ฉ์ด๋ง ์ถ์ถ
|
137 |
-
# ๊ฐ๋จํ ๋ฐฉ๋ฒ: ๊ธธ์ด๊ฐ 3์ ์ด์์ธ ๋จ์ด๋ค์ ์ถ์ถ
|
138 |
-
words = prompt_clean.split()
|
139 |
-
keywords = []
|
140 |
-
|
141 |
-
for word in words:
|
142 |
-
# ๋ถํ์ํ ๋จ์ด ์ ์ธ
|
143 |
-
if len(word) > 2 and word not in ["the", "and", "for", "with", "that", "this", "from", "about"]:
|
144 |
-
keywords.append(word)
|
145 |
-
|
146 |
-
# ์ต๋ 5๊ฐ ํค์๋๋ง ์ฌ์ฉ
|
147 |
-
return " ".join(keywords[:5])
|
148 |
-
|
149 |
-
def enrich_prompt_with_search(prompt: str, diagram_type: str, search_results: List[dict]) -> str:
|
150 |
-
"""๊ฒ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํ์ผ๋ก ํ๋กฌํํธ๋ฅผ ๋ณด๊ฐ"""
|
151 |
-
if not search_results:
|
152 |
-
return prompt
|
153 |
-
|
154 |
-
# ๊ฒ์ ๊ฒฐ๊ณผ์์ ์ ์ฉํ ์ ๋ณด ์ถ์ถ
|
155 |
-
enrichment = "\n\nAdditional context from web search:\n"
|
156 |
-
for i, result in enumerate(search_results[:3]): # ์์ 3๊ฐ๋ง ์ฌ์ฉ
|
157 |
-
enrichment += f"- {result['title']}: {result['description']}\n"
|
158 |
-
|
159 |
-
enriched_prompt = f"{prompt}{enrichment}"
|
160 |
-
return enriched_prompt
|
161 |
-
|
162 |
-
# LLM API ํจ์ ์์
|
163 |
-
def call_llm_api(prompt, diagram_type, use_search=False, search_results=None):
|
164 |
-
"""Friendli AI API๋ฅผ ํธ์ถํ์ฌ JSON ์์ฑ"""
|
165 |
-
token = os.environ.get("FRIENDLI_TOKEN") or "YOUR_FRIENDLI_TOKEN"
|
166 |
-
url = "https://api.friendli.ai/dedicated/v1/chat/completions"
|
167 |
-
headers = {
|
168 |
-
"Authorization": "Bearer " + token,
|
169 |
-
"Content-Type": "application/json"
|
170 |
-
}
|
171 |
-
|
172 |
-
# ๋ค์ด์ด๊ทธ๋จ ํ์
๋ณ JSON ๊ตฌ์กฐ ๊ฐ์ด๋ - ์ค์ ์์ฑ๊ธฐ๊ฐ ์๊ตฌํ๋ ์ ํํ ํ์
|
173 |
-
json_guides = {
|
174 |
-
"Concept Map": """Generate a JSON for a concept map with the EXACT following structure:
|
175 |
-
{
|
176 |
-
"central_node": "Main Topic",
|
177 |
-
"nodes": [
|
178 |
-
{
|
179 |
-
"id": "node1",
|
180 |
-
"label": "First Concept",
|
181 |
-
"relationship": "is part of",
|
182 |
-
"subnodes": [
|
183 |
-
{
|
184 |
-
"id": "node1_1",
|
185 |
-
"label": "Sub Concept 1",
|
186 |
-
"relationship": "includes",
|
187 |
-
"subnodes": []
|
188 |
-
}
|
189 |
-
]
|
190 |
-
},
|
191 |
-
{
|
192 |
-
"id": "node2",
|
193 |
-
"label": "Second Concept",
|
194 |
-
"relationship": "relates to",
|
195 |
-
"subnodes": []
|
196 |
-
}
|
197 |
-
]
|
198 |
-
}""",
|
199 |
-
"Synoptic Chart": """Generate a JSON for a synoptic chart with the EXACT following structure:
|
200 |
-
{
|
201 |
-
"central_node": "Chart Title",
|
202 |
-
"nodes": [
|
203 |
-
{
|
204 |
-
"id": "phase1",
|
205 |
-
"label": "Phase 1 Name",
|
206 |
-
"relationship": "starts with",
|
207 |
-
"subnodes": [
|
208 |
-
{
|
209 |
-
"id": "sub1_1",
|
210 |
-
"label": "Sub Item 1",
|
211 |
-
"relationship": "includes",
|
212 |
-
"subnodes": []
|
213 |
-
}
|
214 |
-
]
|
215 |
-
}
|
216 |
-
]
|
217 |
-
}""",
|
218 |
-
"Radial Diagram": """Generate a JSON for a radial diagram with the EXACT following structure:
|
219 |
-
{
|
220 |
-
"central_node": "Central Concept",
|
221 |
-
"nodes": [
|
222 |
-
{
|
223 |
-
"id": "branch1",
|
224 |
-
"label": "Branch 1",
|
225 |
-
"relationship": "connected to",
|
226 |
-
"subnodes": [
|
227 |
-
{
|
228 |
-
"id": "item1",
|
229 |
-
"label": "Item 1",
|
230 |
-
"relationship": "example",
|
231 |
-
"subnodes": []
|
232 |
-
}
|
233 |
-
]
|
234 |
-
}
|
235 |
-
]
|
236 |
-
}""",
|
237 |
-
"Process Flow": """Generate a JSON for a process flow diagram with the EXACT following structure:
|
238 |
-
{
|
239 |
-
"start_node": "Start Process",
|
240 |
-
"nodes": [
|
241 |
-
{"id": "step1", "label": "First Step", "type": "process"},
|
242 |
-
{"id": "step2", "label": "Decision Point", "type": "decision"},
|
243 |
-
{"id": "step3", "label": "Another Step", "type": "process"},
|
244 |
-
{"id": "end", "label": "End Process", "type": "end"}
|
245 |
-
],
|
246 |
-
"connections": [
|
247 |
-
{"from": "start_node", "to": "step1", "label": "Begin"},
|
248 |
-
{"from": "step1", "to": "step2", "label": "Next"},
|
249 |
-
{"from": "step2", "to": "step3", "label": "Yes"},
|
250 |
-
{"from": "step3", "to": "end", "label": "Complete"}
|
251 |
-
]
|
252 |
-
}""",
|
253 |
-
"WBS Diagram": """Generate a JSON for a WBS diagram with the EXACT following structure:
|
254 |
-
{
|
255 |
-
"project_title": "Project Name",
|
256 |
-
"phases": [
|
257 |
-
{
|
258 |
-
"id": "phase1",
|
259 |
-
"label": "Phase 1",
|
260 |
-
"tasks": [
|
261 |
-
{
|
262 |
-
"id": "task1_1",
|
263 |
-
"label": "Task 1.1",
|
264 |
-
"subtasks": [
|
265 |
-
{
|
266 |
-
"id": "subtask1_1_1",
|
267 |
-
"label": "Subtask 1.1.1",
|
268 |
-
"sub_subtasks": []
|
269 |
-
}
|
270 |
-
]
|
271 |
-
}
|
272 |
-
]
|
273 |
-
}
|
274 |
-
]
|
275 |
-
}"""
|
276 |
-
}
|
277 |
-
|
278 |
-
# ๊ฒ์ ๊ฒฐ๊ณผ๊ฐ ์์ผ๋ฉด ์์คํ
ํ๋กฌํํธ์ ์ถ๊ฐ
|
279 |
-
search_context = ""
|
280 |
-
if use_search and search_results:
|
281 |
-
search_context = "\n\nUse the following search results to enrich the diagram content:\n"
|
282 |
-
for result in search_results[:3]:
|
283 |
-
search_context += f"- {result['title']}: {result['description']}\n"
|
284 |
-
|
285 |
-
system_prompt = f"""You are a helpful assistant that generates JSON structures for diagrams.
|
286 |
-
{json_guides.get(diagram_type, '')}
|
287 |
-
{search_context}
|
288 |
-
Important rules:
|
289 |
-
1. Generate ONLY valid JSON without any explanation or markdown formatting
|
290 |
-
2. The JSON must follow the EXACT structure shown above - do not change field names
|
291 |
-
3. Make the content relevant to the user's prompt
|
292 |
-
4. Use the user's language (Korean or English) for the content values
|
293 |
-
5. For IDs, use simple alphanumeric strings without spaces (e.g., "node1", "task1_1")
|
294 |
-
6. Ensure all connections reference existing node IDs
|
295 |
-
7. For Process Flow: 'type' can be: "process", "decision", "start", "end", "io"
|
296 |
-
8. For nested structures (Concept Map, Synoptic Chart, Radial, WBS), include empty 'subnodes' or 'subtasks' arrays when there are no children
|
297 |
-
9. Do not add any additional fields not shown in the example structure
|
298 |
-
10. If search results are provided, incorporate relevant information into the diagram content"""
|
299 |
-
|
300 |
-
payload = {
|
301 |
-
"model": "dep89a2fld32mcm",
|
302 |
-
"messages": [
|
303 |
-
{"role": "system", "content": system_prompt},
|
304 |
-
{"role": "user", "content": f"Create a {diagram_type} JSON for: {prompt}"}
|
305 |
-
],
|
306 |
-
"max_tokens": 16384,
|
307 |
-
"top_p": 0.8,
|
308 |
-
"stream": False # ๊ฐ๋จํ๊ฒ ์ฒ๋ฆฌํ๊ธฐ ์ํด stream์ False๋ก ์ค์
|
309 |
-
}
|
310 |
-
|
311 |
try:
|
312 |
-
|
|
|
313 |
|
314 |
-
|
315 |
-
|
316 |
-
return
|
317 |
|
318 |
-
|
|
|
|
|
|
|
319 |
|
320 |
-
|
321 |
-
|
322 |
-
# JSON ๋ถ๋ถ๋ง ์ถ์ถ (๋งํฌ๋ค์ด ์ฝ๋ ๋ธ๋ก ์ ๊ฑฐ)
|
323 |
-
content = content.strip()
|
324 |
-
if content.startswith("```json"):
|
325 |
-
content = content[7:]
|
326 |
-
if content.startswith("```"):
|
327 |
-
content = content[3:]
|
328 |
-
if content.endswith("```"):
|
329 |
-
content = content[:-3]
|
330 |
-
|
331 |
-
# ์ถ๊ฐ์ ์ธ ํ
์คํธ ์ ๊ฑฐ (JSON ์ธ์ ์ค๋ช
์ด ์์ ๊ฒฝ์ฐ)
|
332 |
-
content = content.strip()
|
333 |
-
# JSON ์์ ์์น ์ฐพ๊ธฐ
|
334 |
-
json_start = content.find('{')
|
335 |
-
if json_start != -1:
|
336 |
-
content = content[json_start:]
|
337 |
-
# JSON ๋ ์์น ์ฐพ๊ธฐ
|
338 |
-
bracket_count = 0
|
339 |
-
json_end = -1
|
340 |
-
for i, char in enumerate(content):
|
341 |
-
if char == '{':
|
342 |
-
bracket_count += 1
|
343 |
-
elif char == '}':
|
344 |
-
bracket_count -= 1
|
345 |
-
if bracket_count == 0:
|
346 |
-
json_end = i
|
347 |
-
break
|
348 |
-
if json_end != -1:
|
349 |
-
content = content[:json_end + 1]
|
350 |
-
|
351 |
-
return content.strip()
|
352 |
-
else:
|
353 |
-
return json.dumps({"error": "No response from LLM"})
|
354 |
-
except requests.exceptions.Timeout:
|
355 |
-
return json.dumps({"error": "Request timed out"})
|
356 |
-
except requests.exceptions.RequestException as e:
|
357 |
-
print(f"LLM API Request Error: {str(e)}")
|
358 |
-
return json.dumps({"error": f"Request failed: {str(e)}"})
|
359 |
-
except Exception as e:
|
360 |
-
print(f"LLM API Error: {str(e)}")
|
361 |
-
return json.dumps({"error": str(e)})
|
362 |
-
|
363 |
-
def generate_with_llm(prompt, diagram_type, output_format, use_search):
|
364 |
-
"""LLM์ผ๋ก JSON์ ์์ฑํ๊ณ ๋ค์ด์ด๊ทธ๋จ์ ์์ฑ"""
|
365 |
-
if not prompt:
|
366 |
-
return None, "Please enter a prompt", ""
|
367 |
-
|
368 |
-
search_results = []
|
369 |
-
search_info = ""
|
370 |
-
|
371 |
-
# ๋ธ๋ ์ด๋ธ ์์น ์ฌ์ฉ ์
|
372 |
-
if use_search:
|
373 |
-
keywords = extract_keywords(prompt, diagram_type)
|
374 |
-
if keywords:
|
375 |
-
search_info = f"๐ Searching for: {keywords}\n"
|
376 |
-
search_results = brave_search(keywords)
|
377 |
-
if search_results:
|
378 |
-
search_info += f"โ
Found {len(search_results)} relevant results\n\n"
|
379 |
-
for i, result in enumerate(search_results[:3]):
|
380 |
-
search_info += f"{i+1}. {result['title']}\n {result['description'][:100]}...\n\n"
|
381 |
-
else:
|
382 |
-
search_info += "โ No search results found\n\n"
|
383 |
-
|
384 |
-
# LLM์ผ๋ก JSON ์์ฑ (๊ฒ์ ๊ฒฐ๊ณผ ํฌํจ)
|
385 |
-
generated_json = call_llm_api(prompt, diagram_type, use_search, search_results)
|
386 |
-
|
387 |
-
try:
|
388 |
-
# JSON ์ ํจ์ฑ ๊ฒ์ฌ
|
389 |
-
json_data = json.loads(generated_json)
|
390 |
-
json_str = json.dumps(json_data, indent=2, ensure_ascii=False)
|
391 |
-
|
392 |
-
# JSON ๊ตฌ์กฐ ๊ฒ์ฆ
|
393 |
-
if diagram_type in ["Concept Map", "Synoptic Chart", "Radial Diagram"]:
|
394 |
-
if "central_node" not in json_data or "nodes" not in json_data:
|
395 |
-
return None, f"Invalid JSON structure for {diagram_type}. Missing 'central_node' or 'nodes'. Generated JSON:\n{json_str}", search_info
|
396 |
-
elif diagram_type == "Process Flow":
|
397 |
-
if "start_node" not in json_data or "nodes" not in json_data or "connections" not in json_data:
|
398 |
-
return None, f"Invalid JSON structure for Process Flow. Missing 'start_node', 'nodes', or 'connections'. Generated JSON:\n{json_str}", search_info
|
399 |
-
elif diagram_type == "WBS Diagram":
|
400 |
-
if "project_title" not in json_data or "phases" not in json_data:
|
401 |
-
return None, f"Invalid JSON structure for WBS. Missing 'project_title' or 'phases'. Generated JSON:\n{json_str}", search_info
|
402 |
|
403 |
-
#
|
404 |
try:
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
diagram = generate_synoptic_chart(json_str, output_format)
|
409 |
-
elif diagram_type == "Radial Diagram":
|
410 |
-
diagram = generate_radial_diagram(json_str, output_format)
|
411 |
-
elif diagram_type == "Process Flow":
|
412 |
-
diagram = generate_process_flow_diagram(json_str, output_format)
|
413 |
-
elif diagram_type == "WBS Diagram":
|
414 |
-
diagram = generate_wbs_diagram(json_str, output_format)
|
415 |
-
else:
|
416 |
-
return None, "Invalid diagram type", search_info
|
417 |
|
418 |
-
# ์๋ฌ ๋ฉ์์ง๊ฐ ๋ฐํ๋ ๊ฒฝ์ฐ
|
419 |
-
if isinstance(diagram, str) and diagram.startswith("Error:"):
|
420 |
-
return None, f"Diagram generation error: {diagram}\n\nGenerated JSON:\n{json_str}", search_info
|
421 |
-
|
422 |
-
return diagram, json_str, search_info
|
423 |
-
|
424 |
-
except Exception as e:
|
425 |
-
return None, f"Error generating diagram: {str(e)}\n\nGenerated JSON:\n{json_str}", search_info
|
426 |
-
|
427 |
-
except json.JSONDecodeError as e:
|
428 |
-
return None, f"Invalid JSON generated: {str(e)}\n\nGenerated content:\n{generated_json}", search_info
|
429 |
except Exception as e:
|
430 |
-
|
|
|
|
|
431 |
|
432 |
if __name__ == "__main__":
|
433 |
-
|
434 |
-
|
435 |
-
with gr.Blocks(
|
436 |
-
title="Advanced Graph Generator",
|
437 |
-
theme=gr.themes.Soft(
|
438 |
-
primary_hue="violet",
|
439 |
-
secondary_hue="purple",
|
440 |
-
),
|
441 |
-
css="""
|
442 |
-
/* ๊ทธ๋ผ๋์ธํธ ๋ฐฐ๊ฒฝ */
|
443 |
-
.gradio-container {
|
444 |
-
font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;
|
445 |
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
446 |
-
min-height: 100vh;
|
447 |
-
}
|
448 |
-
|
449 |
-
/* ๋ฉ์ธ ์ปจํ
์ด๋ ์คํ์ผ */
|
450 |
-
.main-container {
|
451 |
-
background: rgba(255, 255, 255, 0.95);
|
452 |
-
backdrop-filter: blur(10px);
|
453 |
-
border-radius: 20px;
|
454 |
-
padding: 30px;
|
455 |
-
margin: 20px auto;
|
456 |
-
max-width: 1400px;
|
457 |
-
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
|
458 |
-
}
|
459 |
-
|
460 |
-
/* ํค๋ ์คํ์ผ */
|
461 |
-
.header-section {
|
462 |
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
463 |
-
color: white;
|
464 |
-
padding: 40px;
|
465 |
-
border-radius: 15px;
|
466 |
-
margin-bottom: 30px;
|
467 |
-
text-align: center;
|
468 |
-
box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
|
469 |
-
}
|
470 |
-
|
471 |
-
.header-section h1 {
|
472 |
-
font-size: 2.5em;
|
473 |
-
font-weight: 700;
|
474 |
-
margin-bottom: 10px;
|
475 |
-
}
|
476 |
-
|
477 |
-
.header-section p {
|
478 |
-
font-size: 1.2em;
|
479 |
-
opacity: 0.9;
|
480 |
-
}
|
481 |
-
|
482 |
-
/* ํญ ์คํ์ผ */
|
483 |
-
.gr-tab-item {
|
484 |
-
padding: 15px 30px;
|
485 |
-
font-size: 1.1em;
|
486 |
-
font-weight: 600;
|
487 |
-
background: white;
|
488 |
-
border-radius: 10px 10px 0 0;
|
489 |
-
transition: all 0.3s ease;
|
490 |
-
margin-right: 5px;
|
491 |
-
}
|
492 |
-
|
493 |
-
.gr-tab-item:hover {
|
494 |
-
background: linear-gradient(135deg, #f5f7fa 0%, #e9ecef 100%);
|
495 |
-
}
|
496 |
-
|
497 |
-
.gr-tab-item.selected {
|
498 |
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
499 |
-
color: white !important;
|
500 |
-
}
|
501 |
-
|
502 |
-
/* ๋ฒํผ ์คํ์ผ */
|
503 |
-
.gr-button {
|
504 |
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
505 |
-
color: white;
|
506 |
-
border: none;
|
507 |
-
padding: 12px 30px;
|
508 |
-
font-size: 1.1em;
|
509 |
-
font-weight: 600;
|
510 |
-
border-radius: 10px;
|
511 |
-
transition: all 0.3s ease;
|
512 |
-
box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
|
513 |
-
}
|
514 |
-
|
515 |
-
.gr-button:hover {
|
516 |
-
transform: translateY(-2px);
|
517 |
-
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
|
518 |
-
}
|
519 |
-
|
520 |
-
.gr-button.primary {
|
521 |
-
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
|
522 |
-
}
|
523 |
-
|
524 |
-
/* ์
๋ ฅ ํ๋ ์คํ์ผ */
|
525 |
-
.gr-textbox, .gr-dropdown {
|
526 |
-
border: 2px solid #e9ecef;
|
527 |
-
border-radius: 10px;
|
528 |
-
padding: 12px;
|
529 |
-
font-size: 1em;
|
530 |
-
transition: all 0.3s ease;
|
531 |
-
background: white;
|
532 |
-
}
|
533 |
-
|
534 |
-
.gr-textbox:focus, .gr-dropdown:focus {
|
535 |
-
border-color: #667eea;
|
536 |
-
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
537 |
-
}
|
538 |
-
|
539 |
-
/* ํจ๋ ์คํ์ผ */
|
540 |
-
.panel-section {
|
541 |
-
background: white;
|
542 |
-
border-radius: 15px;
|
543 |
-
padding: 25px;
|
544 |
-
margin-bottom: 20px;
|
545 |
-
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
|
546 |
-
}
|
547 |
-
|
548 |
-
/* ์ด๋ฏธ์ง ์ปจํ
์ด๋ ์คํ์ผ */
|
549 |
-
.gr-image {
|
550 |
-
border-radius: 15px;
|
551 |
-
overflow: hidden;
|
552 |
-
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
553 |
-
}
|
554 |
-
|
555 |
-
/* ์์ ์ด๋ฏธ์ง ์คํ์ผ */
|
556 |
-
.example-images {
|
557 |
-
gap: 20px;
|
558 |
-
}
|
559 |
-
|
560 |
-
.example-images .gr-image {
|
561 |
-
transition: transform 0.3s ease;
|
562 |
-
}
|
563 |
-
|
564 |
-
.example-images .gr-image:hover {
|
565 |
-
transform: scale(1.02);
|
566 |
-
}
|
567 |
-
|
568 |
-
/* ๋ผ๋์ค ๋ฒํผ ์คํ์ผ */
|
569 |
-
.gr-radio {
|
570 |
-
background: white;
|
571 |
-
padding: 15px;
|
572 |
-
border-radius: 10px;
|
573 |
-
border: 2px solid #e9ecef;
|
574 |
-
}
|
575 |
-
|
576 |
-
/* LLM ํญ ํน๋ณ ์คํ์ผ */
|
577 |
-
.llm-tab {
|
578 |
-
background: linear-gradient(135deg, #f5f7fa 0%, #ffffff 100%);
|
579 |
-
padding: 30px;
|
580 |
-
border-radius: 15px;
|
581 |
-
}
|
582 |
-
|
583 |
-
.llm-input-section {
|
584 |
-
background: white;
|
585 |
-
padding: 25px;
|
586 |
-
border-radius: 15px;
|
587 |
-
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
|
588 |
-
margin-bottom: 20px;
|
589 |
-
}
|
590 |
-
|
591 |
-
/* ๊ฒ์ ์ ๋ณด ์คํ์ผ */
|
592 |
-
.search-info {
|
593 |
-
background: #f8f9fa;
|
594 |
-
padding: 15px;
|
595 |
-
border-radius: 10px;
|
596 |
-
margin-top: 10px;
|
597 |
-
border-left: 4px solid #667eea;
|
598 |
-
}
|
599 |
-
|
600 |
-
/* ์ฒดํฌ๋ฐ์ค ์คํ์ผ */
|
601 |
-
.gr-checkbox {
|
602 |
-
margin-top: 15px;
|
603 |
-
padding: 10px;
|
604 |
-
background: #f8f9fa;
|
605 |
-
border-radius: 8px;
|
606 |
-
}
|
607 |
-
|
608 |
-
/* ๋ฐ์ํ ๋์์ธ */
|
609 |
-
@media (max-width: 768px) {
|
610 |
-
.main-container {
|
611 |
-
padding: 15px;
|
612 |
-
margin: 10px;
|
613 |
-
}
|
614 |
-
|
615 |
-
.header-section h1 {
|
616 |
-
font-size: 2em;
|
617 |
-
}
|
618 |
-
|
619 |
-
.gr-tab-item {
|
620 |
-
padding: 10px 15px;
|
621 |
-
font-size: 1em;
|
622 |
-
}
|
623 |
-
}
|
624 |
-
"""
|
625 |
-
) as demo:
|
626 |
-
with gr.Column(elem_classes=["main-container"]):
|
627 |
-
with gr.Column(elem_classes=["header-section"]):
|
628 |
-
gr.Markdown(
|
629 |
-
"""
|
630 |
-
# ๐จ ChartGPT : AI-Powered Multi Diagram Generator
|
631 |
-
### based LLM 'Gemma-3-R1984-27B' Model. Powered by VIDraftโก
|
632 |
-
"""
|
633 |
-
)
|
634 |
-
|
635 |
-
with gr.Row(variant="panel", elem_classes=["panel-section"]):
|
636 |
-
output_format_radio = gr.Radio(
|
637 |
-
choices=["png", "svg"],
|
638 |
-
value="png",
|
639 |
-
label="๐ Output Format",
|
640 |
-
interactive=True
|
641 |
-
)
|
642 |
-
|
643 |
-
with gr.Tabs():
|
644 |
-
# AI ์ด์์คํดํธ ํญ (์ฒซ ๋ฒ์งธ)
|
645 |
-
with gr.TabItem("๐ค AI Assistant", elem_classes=["llm-tab"]):
|
646 |
-
gr.Markdown(
|
647 |
-
"""
|
648 |
-
### ๐ก Describe your diagram in Korean or English, and AI will create it for you!
|
649 |
-
"""
|
650 |
-
)
|
651 |
-
|
652 |
-
with gr.Row():
|
653 |
-
with gr.Column(scale=1, elem_classes=["llm-input-section"]):
|
654 |
-
prompt_input = gr.Textbox(
|
655 |
-
placeholder="์: '๋จธ์ ๋ฌ๋ ํ๋ก์ธ์ค๋ฅผ ๋ณด์ฌ์ฃผ๋ ํ๋ก์ฐ์ฐจํธ๋ฅผ ๋ง๋ค์ด์ค' or 'Create a concept map about climate change'",
|
656 |
-
label="๐ Enter your prompt",
|
657 |
-
lines=3
|
658 |
-
)
|
659 |
-
|
660 |
-
diagram_type_select = gr.Dropdown(
|
661 |
-
choices=["Concept Map", "Synoptic Chart", "Radial Diagram", "Process Flow", "WBS Diagram"],
|
662 |
-
value="Concept Map",
|
663 |
-
label="๐ Select Diagram Type",
|
664 |
-
interactive=True
|
665 |
-
)
|
666 |
-
|
667 |
-
use_search_checkbox = gr.Checkbox(
|
668 |
-
label="๐ Use Brave Search to enhance content",
|
669 |
-
value=False,
|
670 |
-
info="Search the web for relevant information to enrich your diagram"
|
671 |
-
)
|
672 |
-
|
673 |
-
generate_btn = gr.Button("โจ Generate with AI", variant="primary", size="lg")
|
674 |
-
|
675 |
-
search_info_output = gr.Textbox(
|
676 |
-
label="๐ Search Information",
|
677 |
-
lines=8,
|
678 |
-
interactive=False,
|
679 |
-
visible=False,
|
680 |
-
elem_classes=["search-info"]
|
681 |
-
)
|
682 |
-
|
683 |
-
generated_json_output = gr.Textbox(
|
684 |
-
label="๐ Generated JSON",
|
685 |
-
lines=15,
|
686 |
-
interactive=True,
|
687 |
-
visible=True
|
688 |
-
)
|
689 |
-
|
690 |
-
with gr.Column(scale=2):
|
691 |
-
ai_output_image = gr.Image(
|
692 |
-
label="๐จ Generated Diagram",
|
693 |
-
type="filepath",
|
694 |
-
show_download_button=True,
|
695 |
-
height=600
|
696 |
-
)
|
697 |
-
|
698 |
-
# ๊ฒ์ ์ฒดํฌ๋ฐ์ค ๋ณ๊ฒฝ ์ ๊ฒ์ ์ ๋ณด ์ถ๋ ฅ ํ์/์จ๊น
|
699 |
-
def toggle_search_info(use_search):
|
700 |
-
return gr.update(visible=use_search)
|
701 |
-
|
702 |
-
use_search_checkbox.change(
|
703 |
-
fn=toggle_search_info,
|
704 |
-
inputs=[use_search_checkbox],
|
705 |
-
outputs=[search_info_output]
|
706 |
-
)
|
707 |
-
|
708 |
-
generate_btn.click(
|
709 |
-
fn=generate_with_llm,
|
710 |
-
inputs=[prompt_input, diagram_type_select, output_format_radio, use_search_checkbox],
|
711 |
-
outputs=[ai_output_image, generated_json_output, search_info_output]
|
712 |
-
)
|
713 |
-
|
714 |
-
with gr.Row(elem_classes=["panel-section"]):
|
715 |
-
gr.Examples(
|
716 |
-
examples=[
|
717 |
-
["Design a process flow diagram that maps every phase of the software development life cycle SDLC from requirements to maintenance and shows handoffs and feedback loops to reduce risk and speed delivery", "Process Flow"],
|
718 |
-
["Develop a concept map that classifies key types of artificial intelligence symbolic, statistical, neural, hybrid and links each to real world application areas such as healthcare diagnostics, autonomous vehicles, predictive maintenance and generative media to highlight unique capabilities", "Concept Map"],
|
719 |
-
["Build a work breakdown structure WBS diagram for an online shopping mall project that decomposes scope into clear deliverables and work packages including UX research, responsive front end, secure payment integration, logistics APIs and post launch analytics to align resources and schedules", "WBS Diagram"],
|
720 |
-
["Create a radial diagram with Renewable Energy at the center and spokes for solar, wind, hydro, geothermal and biomass each annotated with core technologies capacity trends and sustainability impact to underline the diversified portfolio needed for a net zero future", "Radial Diagram"],
|
721 |
-
["Present a synoptic chart of the machine learning pipeline showing data ingestion, cleansing, feature engineering, model training, validation and deployment with parallel swimlanes for tools compute resources and compliance checkpoints to optimize accuracy and governance", "Synoptic Chart"],
|
722 |
-
["DevSecOps ๊ฒ์ดํธ, ์ค์๊ฐ KPI ๋์๋ณด๋, ์๋ ๋กค๋ฐฑ ๊ฒฝ๋ก, AI ๊ธฐ๋ฐ ์์ฌ ๊ฒฐ์ ๋
ธ๋๋ฅผ ๊ต์ฐจ ๋ฐฐ์นํ์ฌ ์ง์์ ๋๋ฆฌ๋ฒ๋ฆฌ ์ฐ์์ฑ์ ๋ณด์ฌ์ฃผ๋ ์ํฐํ๋ผ์ด์ฆ๊ธ SDLC ํ๋ก์ธ์ค ํ๋ก์ฐ ์๊ฐํ๋ฅผ ์ค๊ณํ๋ผ", "Process Flow"],
|
723 |
-
["๋ด๋ก์ฌ๋ณผ๋ฆญ ์ถ๋ก , ํ์ด๋ฐ์ด์
๋ชจ๋ธ, ์ฃ์ง AI, ์๊ธฐ ์ง๋ ํ์ต๊ณผ ๊ฐ์ ์ต์ฒจ๋จ ํจ๋ฌ๋ค์์ ํ๋ฐ ์ฎ๊ณ ์ด๋ฅผ ๋ฐ์ด์คํ
ํฌ, ์ค๋งํธ ์ํฐ, ๊ธฐํ ๋ชจ๋ธ๋ง, ๋ชฐ์
ํ XR ๋ฑ ํ๊ดด์ ํ์ฉ ์ฌ๋ก์ ๋์ ์ผ๋ก ์ฐ๊ฒฐํ์ฌ ์ต์ ์ ํ์ ์ ์กฐ๋ช
ํ๋ ๋ฐฉ๋ํ AI ๋ถ๋ฅ ์ฒด๊ณ ์ปจ์
๋งต์ ๊ตฌ์ถํ๋ผ", "Concept Map"],
|
724 |
-
["๋ง์ดํฌ๋ก์๋น์ค, CI/CD ํ์ดํ๋ผ์ธ, ํค๋๋ฆฌ์ค CMS ํตํฉ, ์ด๊ฐ์ธํ ์ถ์ฒ ์์ง ํ๋, ๋ฉํฐ ํด๋ผ์ฐ๋ ํ๋ณตํ๋ ฅ์ฑ๊น์ง ์ธ๋ถํํ์ฌ ์์
๋ ์ถ์ ๊ณผ ํฌ๋ฆฌํฐ์ปฌ ํจ์ค ์ํธ ์ฐ๊ณ๋ฅผ ์ฃผ์์ผ๋ก ๋ด์ ์ฐจ์ธ๋ ์ด๋์ฑ๋ e-์ปค๋จธ์ค ์ํ๊ณ๋ฅผ ์ํ ์ด์ ๋ฐ WBS๋ฅผ ์ค๊ณํ๋ผ", "WBS Diagram"],
|
725 |
-
["์ฌ์์๋์ง๋ฅผ ์ค์ฌ์ผ๋ก ๋ค์ธต ๋ฐฉ์ฌํ ๋ค์ด์ด๊ทธ๋จ์ ๊ทธ๋ฆฌ๋, ๋ฐ์ ยท์ ์ฅยท์ก๋ฐฐ์ ยท์ ์ฑ
์๋จ์ ๋์ฌ์์ผ๋ก ๋ฐฐ์นํ๊ณ , ํ๏ฟฝ๏ฟฝ๏ฟฝ๊ด PV, CSP, ํด์ ๋ฐ ์ก์ํ๋ ฅ, ์กฐ๋ ฅ, ๊ทธ๋ฆฐ ์์, ์ฐจ์ธ๋ ๋ฐฐํฐ๋ฆฌ ํํ, ์ค๋งํธ ๊ทธ๋ฆฌ๋ AI๋ฅผ ์คํฌํฌ๋ก ์ฐ๊ฒฐํ๋ฉฐ, LCOE ์ถ์ธ์ ํ์ ์์ ์๋๋ฆฌ์ค๋ฅผ ์ฃผ์์ผ๋ก ํ๊ธฐํ๋ผ", "Radial Diagram"],
|
726 |
-
["๋ฐ์ดํฐ ์ถ์ฒ, ์๋ํ๋ ํผ์ฒ ์คํ ์ด, ์ฐํฉ ํ์ต ์ค์ผ์คํธ๋ ์ด์
, ๋ชจ๋ธ ์ฑ๋ฅ ๋๋ฆฌํํธ ๊ฐ์ง, ์๋ ๋ฐฐํฌ ์บ๋๋ฆฌ, ์ค๋ฆฌ ์ค์ ๊ฐ์ฌ์ ์๊ฐ ์ถ ๋ ์ธ์ ๋ณ์นํ๊ณ , ๊ฒ์ดํธ ๊ธฐ์ค๊ณผ ๋กค๋ฐฑ ํธ๋ฆฌ๊ฑฐ๊น์ง ๊ฐ์ถ ํ๋ก๋์
๊ธ ๋จธ์ ๋ฌ๋ ํ์ดํ๋ผ์ธ์ ์ํ์ ์์ผ๋ก ๋ฌ์ฌํ ์๋ํฑ ์ฐจํธ๋ฅผ ์ ์ํ๋ผ", "Synoptic Chart"]
|
727 |
-
],
|
728 |
-
inputs=[prompt_input, diagram_type_select],
|
729 |
-
label="๐ญ Example Prompts"
|
730 |
-
)
|
731 |
-
|
732 |
-
# ๊ธฐ์กด ํญ๋ค
|
733 |
-
with gr.TabItem("๐บ๏ธ Concept Map"):
|
734 |
-
with gr.Row():
|
735 |
-
with gr.Column(scale=1, elem_classes=["panel-section"]):
|
736 |
-
json_input_cm = gr.Textbox(
|
737 |
-
value=CONCEPT_MAP_JSON,
|
738 |
-
placeholder="Paste JSON following the documented format",
|
739 |
-
label="JSON Input",
|
740 |
-
lines=20
|
741 |
-
)
|
742 |
-
submit_btn_cm = gr.Button("Generate Concept Map", variant="primary")
|
743 |
-
|
744 |
-
with gr.Column(scale=2):
|
745 |
-
output_cm = gr.Image(
|
746 |
-
label="Generated Diagram",
|
747 |
-
type="filepath",
|
748 |
-
show_download_button=True,
|
749 |
-
height=500
|
750 |
-
)
|
751 |
-
|
752 |
-
submit_btn_cm.click(
|
753 |
-
fn=generate_concept_map,
|
754 |
-
inputs=[json_input_cm, output_format_radio],
|
755 |
-
outputs=output_cm
|
756 |
-
)
|
757 |
-
|
758 |
-
gr.Markdown("## ๐ธ Examples")
|
759 |
-
with gr.Row(elem_classes=["example-images"]):
|
760 |
-
gr.Image(value="./images/cm1.svg", label="Sample 1", show_label=True, interactive=False)
|
761 |
-
gr.Image(value="./images/cm2.svg", label="Sample 2", show_label=True, interactive=False)
|
762 |
-
|
763 |
-
with gr.TabItem("๐ Synoptic Chart"):
|
764 |
-
with gr.Row():
|
765 |
-
with gr.Column(scale=1, elem_classes=["panel-section"]):
|
766 |
-
json_input_sc = gr.Textbox(
|
767 |
-
value=SYNOPTIC_CHART_JSON,
|
768 |
-
placeholder="Paste JSON following the documented format",
|
769 |
-
label="JSON Input",
|
770 |
-
lines=20
|
771 |
-
)
|
772 |
-
submit_btn_sc = gr.Button("Generate Synoptic Chart", variant="primary")
|
773 |
-
|
774 |
-
with gr.Column(scale=2):
|
775 |
-
output_sc = gr.Image(
|
776 |
-
label="Generated Diagram",
|
777 |
-
type="filepath",
|
778 |
-
show_download_button=True,
|
779 |
-
height=500
|
780 |
-
)
|
781 |
-
|
782 |
-
submit_btn_sc.click(
|
783 |
-
fn=generate_synoptic_chart,
|
784 |
-
inputs=[json_input_sc, output_format_radio],
|
785 |
-
outputs=output_sc
|
786 |
-
)
|
787 |
-
|
788 |
-
gr.Markdown("## ๐ธ Examples")
|
789 |
-
with gr.Row(elem_classes=["example-images"]):
|
790 |
-
gr.Image(value="./images/sc1.svg", label="Sample 1", show_label=True, interactive=False)
|
791 |
-
gr.Image(value="./images/sc2.svg", label="Sample 2", show_label=True, interactive=False)
|
792 |
-
|
793 |
-
with gr.TabItem("โ๏ธ Radial Diagram"):
|
794 |
-
with gr.Row():
|
795 |
-
with gr.Column(scale=1, elem_classes=["panel-section"]):
|
796 |
-
json_input_rd = gr.Textbox(
|
797 |
-
value=RADIAL_DIAGRAM_JSON,
|
798 |
-
placeholder="Paste JSON following the documented format",
|
799 |
-
label="JSON Input",
|
800 |
-
lines=20
|
801 |
-
)
|
802 |
-
submit_btn_rd = gr.Button("Generate Radial Diagram", variant="primary")
|
803 |
-
|
804 |
-
with gr.Column(scale=2):
|
805 |
-
output_rd = gr.Image(
|
806 |
-
label="Generated Diagram",
|
807 |
-
type="filepath",
|
808 |
-
show_download_button=True,
|
809 |
-
height=500
|
810 |
-
)
|
811 |
-
|
812 |
-
submit_btn_rd.click(
|
813 |
-
fn=generate_radial_diagram,
|
814 |
-
inputs=[json_input_rd, output_format_radio],
|
815 |
-
outputs=output_rd
|
816 |
-
)
|
817 |
-
|
818 |
-
gr.Markdown("## ๐ธ Examples")
|
819 |
-
with gr.Row(elem_classes=["example-images"]):
|
820 |
-
gr.Image(value="./images/rd1.svg", label="Sample 1", show_label=True, interactive=False)
|
821 |
-
gr.Image(value="./images/rd2.svg", label="Sample 2", show_label=True, interactive=False)
|
822 |
-
gr.Image(value="./images/rd3.svg", label="Sample 3", show_label=True, interactive=False)
|
823 |
-
gr.Image(value="./images/rd4.svg", label="Sample 4", show_label=True, interactive=False)
|
824 |
-
|
825 |
-
with gr.TabItem("๐ Process Flow"):
|
826 |
-
with gr.Row():
|
827 |
-
with gr.Column(scale=1, elem_classes=["panel-section"]):
|
828 |
-
json_input_pf = gr.Textbox(
|
829 |
-
value=PROCESS_FLOW_JSON,
|
830 |
-
placeholder="Paste JSON following the documented format",
|
831 |
-
label="JSON Input",
|
832 |
-
lines=20
|
833 |
-
)
|
834 |
-
submit_btn_pf = gr.Button("Generate Process Flow", variant="primary")
|
835 |
-
|
836 |
-
with gr.Column(scale=2):
|
837 |
-
output_pf = gr.Image(
|
838 |
-
label="Generated Diagram",
|
839 |
-
type="filepath",
|
840 |
-
show_download_button=True,
|
841 |
-
height=500
|
842 |
-
)
|
843 |
-
|
844 |
-
submit_btn_pf.click(
|
845 |
-
fn=generate_process_flow_diagram,
|
846 |
-
inputs=[json_input_pf, output_format_radio],
|
847 |
-
outputs=output_pf
|
848 |
-
)
|
849 |
-
|
850 |
-
gr.Markdown("## ๐ธ Examples")
|
851 |
-
with gr.Row(elem_classes=["example-images"]):
|
852 |
-
gr.Image(value="./images/pf1.svg", label="Sample 1", show_label=True, interactive=False)
|
853 |
-
gr.Image(value="./images/pf2.svg", label="Sample 2", show_label=True, interactive=False)
|
854 |
-
|
855 |
-
with gr.TabItem("๐ WBS Diagram"):
|
856 |
-
with gr.Row():
|
857 |
-
with gr.Column(scale=1, elem_classes=["panel-section"]):
|
858 |
-
json_input_wbs = gr.Textbox(
|
859 |
-
value=WBS_DIAGRAM_JSON,
|
860 |
-
placeholder="Paste JSON following the documented format",
|
861 |
-
label="JSON Input",
|
862 |
-
lines=20
|
863 |
-
)
|
864 |
-
submit_btn_wbs = gr.Button("Generate WBS Diagram", variant="primary")
|
865 |
-
|
866 |
-
with gr.Column(scale=2):
|
867 |
-
output_wbs = gr.Image(
|
868 |
-
label="Generated Diagram",
|
869 |
-
type="filepath",
|
870 |
-
show_download_button=True,
|
871 |
-
height=500
|
872 |
-
)
|
873 |
-
|
874 |
-
submit_btn_wbs.click(
|
875 |
-
fn=generate_wbs_diagram,
|
876 |
-
inputs=[json_input_wbs, output_format_radio],
|
877 |
-
outputs=output_wbs
|
878 |
-
)
|
879 |
-
|
880 |
-
gr.Markdown("## ๐ธ Examples")
|
881 |
-
with gr.Row(elem_classes=["example-images"]):
|
882 |
-
gr.Image(value="./images/wd1.svg", label="Sample 1", show_label=True, interactive=False)
|
883 |
-
gr.Image(value="./images/wd2.svg", label="Sample 2", show_label=True, interactive=False)
|
884 |
-
|
885 |
-
demo.launch(
|
886 |
-
mcp_server=True,
|
887 |
-
share=False,
|
888 |
-
server_port=7860,
|
889 |
-
server_name="0.0.0.0"
|
890 |
-
)
|
|
|
|
|
1 |
import os
|
2 |
import sys
|
3 |
+
import streamlit as st
|
4 |
+
from tempfile import NamedTemporaryFile
|
|
|
|
|
5 |
|
6 |
+
def main():
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 |
try:
|
8 |
+
# Get the code from secrets
|
9 |
+
code = os.environ.get("MAIN_CODE")
|
10 |
|
11 |
+
if not code:
|
12 |
+
st.error("โ ๏ธ The application code wasn't found in secrets. Please add the MAIN_CODE secret.")
|
13 |
+
return
|
14 |
|
15 |
+
# Create a temporary Python file
|
16 |
+
with NamedTemporaryFile(suffix='.py', delete=False, mode='w') as tmp:
|
17 |
+
tmp.write(code)
|
18 |
+
tmp_path = tmp.name
|
19 |
|
20 |
+
# Execute the code
|
21 |
+
exec(compile(code, tmp_path, 'exec'), globals())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
|
23 |
+
# Clean up the temporary file
|
24 |
try:
|
25 |
+
os.unlink(tmp_path)
|
26 |
+
except:
|
27 |
+
pass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
except Exception as e:
|
30 |
+
st.error(f"โ ๏ธ Error loading or executing the application: {str(e)}")
|
31 |
+
import traceback
|
32 |
+
st.code(traceback.format_exc())
|
33 |
|
34 |
if __name__ == "__main__":
|
35 |
+
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|