openfree commited on
Commit
b8790f1
ยท
verified ยท
1 Parent(s): 3238c20

Create app-backup.py

Browse files
Files changed (1) hide show
  1. app-backup.py +743 -0
app-backup.py ADDED
@@ -0,0 +1,743 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import os
3
+ import sys
4
+ import json
5
+ import requests
6
+
7
+ # ํ˜„์žฌ ๋””๋ ‰ํ† ๋ฆฌ์™€ ํฐํŠธ ํŒŒ์ผ ๊ฒฝ๋กœ ์„ค์ •
8
+ CURRENT_DIR = os.path.dirname(os.path.abspath(__file__))
9
+ FONT_PATH = os.path.join(CURRENT_DIR, 'NanumGothic-Regular.ttf')
10
+ FONTS_CONF_PATH = os.path.join(CURRENT_DIR, 'fonts.conf')
11
+
12
+ # fonts.conf ํŒŒ์ผ ์ƒ์„ฑ ํ•จ์ˆ˜
13
+ def create_fonts_conf():
14
+ """Graphviz๊ฐ€ ๋กœ์ปฌ ํฐํŠธ๋ฅผ ์ธ์‹ํ•  ์ˆ˜ ์žˆ๋„๋ก fonts.conf ํŒŒ์ผ ์ƒ์„ฑ"""
15
+ fonts_conf_content = f"""<?xml version="1.0"?>
16
+ <!DOCTYPE fontconfig SYSTEM "fonts.dtd">
17
+ <fontconfig>
18
+ <!-- ๋กœ์ปฌ ํฐํŠธ ๋””๋ ‰ํ† ๋ฆฌ ์ถ”๊ฐ€ -->
19
+ <dir>{CURRENT_DIR}</dir>
20
+
21
+ <!-- NanumGothic ํฐํŠธ ๋ณ„์นญ ์„ค์ • -->
22
+ <alias>
23
+ <family>NanumGothic</family>
24
+ <prefer>
25
+ <family>NanumGothic-Regular</family>
26
+ </prefer>
27
+ </alias>
28
+
29
+ <alias>
30
+ <family>NanumGothic-Regular</family>
31
+ <default>
32
+ <family>NanumGothic-Regular</family>
33
+ </default>
34
+ </alias>
35
+
36
+ <!-- ํ•œ๊ธ€ ํฐํŠธ ๋งคํ•‘ -->
37
+ <match target="pattern">
38
+ <test name="family">
39
+ <string>NanumGothic</string>
40
+ </test>
41
+ <edit name="family" mode="assign">
42
+ <string>NanumGothic-Regular</string>
43
+ </edit>
44
+ </match>
45
+ </fontconfig>
46
+ """
47
+
48
+ with open(FONTS_CONF_PATH, 'w', encoding='utf-8') as f:
49
+ f.write(fonts_conf_content)
50
+ print(f"fonts.conf ํŒŒ์ผ ์ƒ์„ฑ๋จ: {FONTS_CONF_PATH}")
51
+
52
+ # ํฐํŠธ ์„ค์ •
53
+ if os.path.exists(FONT_PATH):
54
+ print(f"ํ•œ๊ธ€ ํฐํŠธ ํŒŒ์ผ ๋ฐœ๊ฒฌ: {FONT_PATH}")
55
+
56
+ # fonts.conf ํŒŒ์ผ ์ƒ์„ฑ
57
+ create_fonts_conf()
58
+
59
+ # Graphviz๊ฐ€ ํฐํŠธ๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ๋„๋ก ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ์„ค์ •
60
+ os.environ['GDFONTPATH'] = CURRENT_DIR
61
+ os.environ['FONTCONFIG_PATH'] = CURRENT_DIR
62
+ os.environ['FONTCONFIG_FILE'] = FONTS_CONF_PATH
63
+
64
+ print(f"GDFONTPATH ์„ค์ •: {CURRENT_DIR}")
65
+ print(f"FONTCONFIG_FILE ์„ค์ •: {FONTS_CONF_PATH}")
66
+ else:
67
+ print(f"๊ฒฝ๊ณ : ํ•œ๊ธ€ ํฐํŠธ ํŒŒ์ผ์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค: {FONT_PATH}")
68
+
69
+ # ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ํฐํŠธ ๊ฒฝ๋กœ ์ €์žฅ (generator๋“ค์ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก)
70
+ os.environ['KOREAN_FONT_PATH'] = FONT_PATH
71
+
72
+ from concept_map_generator import generate_concept_map
73
+ from synoptic_chart_generator import generate_synoptic_chart
74
+ from radial_diagram_generator import generate_radial_diagram
75
+ from process_flow_generator import generate_process_flow_diagram
76
+ from wbs_diagram_generator import generate_wbs_diagram
77
+
78
+ from sample_data import CONCEPT_MAP_JSON, SYNOPTIC_CHART_JSON, RADIAL_DIAGRAM_JSON, PROCESS_FLOW_JSON, WBS_DIAGRAM_JSON
79
+
80
+ # LLM API ํ•จ์ˆ˜
81
+ def call_llm_api(prompt, diagram_type):
82
+ """Friendli AI API๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ JSON ์ƒ์„ฑ"""
83
+ token = os.environ.get("FRIENDLI_TOKEN") or "YOUR_FRIENDLI_TOKEN"
84
+ url = "https://api.friendli.ai/dedicated/v1/chat/completions"
85
+ headers = {
86
+ "Authorization": "Bearer " + token,
87
+ "Content-Type": "application/json"
88
+ }
89
+
90
+ # ๋‹ค์ด์–ด๊ทธ๋žจ ํƒ€์ž…๋ณ„ JSON ๊ตฌ์กฐ ๊ฐ€์ด๋“œ - ์‹ค์ œ ์ƒ์„ฑ๊ธฐ๊ฐ€ ์š”๊ตฌํ•˜๋Š” ์ •ํ™•ํ•œ ํ˜•์‹
91
+ json_guides = {
92
+ "Concept Map": """Generate a JSON for a concept map with the EXACT following structure:
93
+ {
94
+ "central_node": "Main Topic",
95
+ "nodes": [
96
+ {
97
+ "id": "node1",
98
+ "label": "First Concept",
99
+ "relationship": "is part of",
100
+ "subnodes": [
101
+ {
102
+ "id": "node1_1",
103
+ "label": "Sub Concept 1",
104
+ "relationship": "includes",
105
+ "subnodes": []
106
+ }
107
+ ]
108
+ },
109
+ {
110
+ "id": "node2",
111
+ "label": "Second Concept",
112
+ "relationship": "relates to",
113
+ "subnodes": []
114
+ }
115
+ ]
116
+ }""",
117
+ "Synoptic Chart": """Generate a JSON for a synoptic chart with the EXACT following structure:
118
+ {
119
+ "central_node": "Chart Title",
120
+ "nodes": [
121
+ {
122
+ "id": "phase1",
123
+ "label": "Phase 1 Name",
124
+ "relationship": "starts with",
125
+ "subnodes": [
126
+ {
127
+ "id": "sub1_1",
128
+ "label": "Sub Item 1",
129
+ "relationship": "includes",
130
+ "subnodes": []
131
+ }
132
+ ]
133
+ }
134
+ ]
135
+ }""",
136
+ "Radial Diagram": """Generate a JSON for a radial diagram with the EXACT following structure:
137
+ {
138
+ "central_node": "Central Concept",
139
+ "nodes": [
140
+ {
141
+ "id": "branch1",
142
+ "label": "Branch 1",
143
+ "relationship": "connected to",
144
+ "subnodes": [
145
+ {
146
+ "id": "item1",
147
+ "label": "Item 1",
148
+ "relationship": "example",
149
+ "subnodes": []
150
+ }
151
+ ]
152
+ }
153
+ ]
154
+ }""",
155
+ "Process Flow": """Generate a JSON for a process flow diagram with the EXACT following structure:
156
+ {
157
+ "start_node": "Start Process",
158
+ "nodes": [
159
+ {"id": "step1", "label": "First Step", "type": "process"},
160
+ {"id": "step2", "label": "Decision Point", "type": "decision"},
161
+ {"id": "step3", "label": "Another Step", "type": "process"},
162
+ {"id": "end", "label": "End Process", "type": "end"}
163
+ ],
164
+ "connections": [
165
+ {"from": "start_node", "to": "step1", "label": "Begin"},
166
+ {"from": "step1", "to": "step2", "label": "Next"},
167
+ {"from": "step2", "to": "step3", "label": "Yes"},
168
+ {"from": "step3", "to": "end", "label": "Complete"}
169
+ ]
170
+ }""",
171
+ "WBS Diagram": """Generate a JSON for a WBS diagram with the EXACT following structure:
172
+ {
173
+ "project_title": "Project Name",
174
+ "phases": [
175
+ {
176
+ "id": "phase1",
177
+ "label": "Phase 1",
178
+ "tasks": [
179
+ {
180
+ "id": "task1_1",
181
+ "label": "Task 1.1",
182
+ "subtasks": [
183
+ {
184
+ "id": "subtask1_1_1",
185
+ "label": "Subtask 1.1.1",
186
+ "sub_subtasks": []
187
+ }
188
+ ]
189
+ }
190
+ ]
191
+ }
192
+ ]
193
+ }"""
194
+ }
195
+
196
+ system_prompt = f"""You are a helpful assistant that generates JSON structures for diagrams.
197
+ {json_guides.get(diagram_type, '')}
198
+
199
+ Important rules:
200
+ 1. Generate ONLY valid JSON without any explanation or markdown formatting
201
+ 2. The JSON must follow the EXACT structure shown above - do not change field names
202
+ 3. Make the content relevant to the user's prompt
203
+ 4. Use the user's language (Korean or English) for the content values
204
+ 5. For IDs, use simple alphanumeric strings without spaces (e.g., "node1", "task1_1")
205
+ 6. Ensure all connections reference existing node IDs
206
+ 7. For Process Flow: 'type' can be: "process", "decision", "start", "end", "io"
207
+ 8. For nested structures (Concept Map, Synoptic Chart, Radial, WBS), include empty 'subnodes' or 'subtasks' arrays when there are no children
208
+ 9. Do not add any additional fields not shown in the example structure"""
209
+
210
+ payload = {
211
+ "model": "dep89a2fld32mcm",
212
+ "messages": [
213
+ {"role": "system", "content": system_prompt},
214
+ {"role": "user", "content": f"Create a {diagram_type} JSON for: {prompt}"}
215
+ ],
216
+ "max_tokens": 16384,
217
+ "top_p": 0.8,
218
+ "stream": False # ๊ฐ„๋‹จํ•˜๊ฒŒ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด stream์„ False๋กœ ์„ค์ •
219
+ }
220
+
221
+ try:
222
+ response = requests.post(url, json=payload, headers=headers, timeout=30)
223
+
224
+ # API ์‘๋‹ต ์ƒํƒœ ํ™•์ธ
225
+ if response.status_code != 200:
226
+ return json.dumps({"error": f"API returned status code {response.status_code}: {response.text}"})
227
+
228
+ response_data = response.json()
229
+
230
+ if 'choices' in response_data and len(response_data['choices']) > 0:
231
+ content = response_data['choices'][0]['message']['content']
232
+ # JSON ๋ถ€๋ถ„๋งŒ ์ถ”์ถœ (๋งˆํฌ๋‹ค์šด ์ฝ”๋“œ ๋ธ”๋ก ์ œ๊ฑฐ)
233
+ content = content.strip()
234
+ if content.startswith("```json"):
235
+ content = content[7:]
236
+ if content.startswith("```"):
237
+ content = content[3:]
238
+ if content.endswith("```"):
239
+ content = content[:-3]
240
+
241
+ # ์ถ”๊ฐ€์ ์ธ ํ…์ŠคํŠธ ์ œ๊ฑฐ (JSON ์™ธ์˜ ์„ค๋ช…์ด ์žˆ์„ ๊ฒฝ์šฐ)
242
+ content = content.strip()
243
+ # JSON ์‹œ์ž‘ ์œ„์น˜ ์ฐพ๊ธฐ
244
+ json_start = content.find('{')
245
+ if json_start != -1:
246
+ content = content[json_start:]
247
+ # JSON ๋ ์œ„์น˜ ์ฐพ๊ธฐ
248
+ bracket_count = 0
249
+ json_end = -1
250
+ for i, char in enumerate(content):
251
+ if char == '{':
252
+ bracket_count += 1
253
+ elif char == '}':
254
+ bracket_count -= 1
255
+ if bracket_count == 0:
256
+ json_end = i
257
+ break
258
+ if json_end != -1:
259
+ content = content[:json_end + 1]
260
+
261
+ return content.strip()
262
+ else:
263
+ return json.dumps({"error": "No response from LLM"})
264
+ except requests.exceptions.Timeout:
265
+ return json.dumps({"error": "Request timed out"})
266
+ except requests.exceptions.RequestException as e:
267
+ print(f"LLM API Request Error: {str(e)}")
268
+ return json.dumps({"error": f"Request failed: {str(e)}"})
269
+ except Exception as e:
270
+ print(f"LLM API Error: {str(e)}")
271
+ return json.dumps({"error": str(e)})
272
+
273
+ def generate_with_llm(prompt, diagram_type, output_format):
274
+ """LLM์œผ๋กœ JSON์„ ์ƒ์„ฑํ•˜๊ณ  ๋‹ค์ด์–ด๊ทธ๋žจ์„ ์ƒ์„ฑ"""
275
+ if not prompt:
276
+ return None, "Please enter a prompt"
277
+
278
+ # LLM์œผ๋กœ JSON ์ƒ์„ฑ
279
+ generated_json = call_llm_api(prompt, diagram_type)
280
+
281
+ try:
282
+ # JSON ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ
283
+ json_data = json.loads(generated_json)
284
+ json_str = json.dumps(json_data, indent=2, ensure_ascii=False)
285
+
286
+ # JSON ๊ตฌ์กฐ ๊ฒ€์ฆ
287
+ if diagram_type in ["Concept Map", "Synoptic Chart", "Radial Diagram"]:
288
+ if "central_node" not in json_data or "nodes" not in json_data:
289
+ return None, f"Invalid JSON structure for {diagram_type}. Missing 'central_node' or 'nodes'. Generated JSON:\n{json_str}"
290
+ elif diagram_type == "Process Flow":
291
+ if "start_node" not in json_data or "nodes" not in json_data or "connections" not in json_data:
292
+ return None, f"Invalid JSON structure for Process Flow. Missing 'start_node', 'nodes', or 'connections'. Generated JSON:\n{json_str}"
293
+ elif diagram_type == "WBS Diagram":
294
+ if "project_title" not in json_data or "phases" not in json_data:
295
+ return None, f"Invalid JSON structure for WBS. Missing 'project_title' or 'phases'. Generated JSON:\n{json_str}"
296
+
297
+ # ๋‹ค์ด์–ด๊ทธ๋žจ ์ƒ์„ฑ
298
+ try:
299
+ if diagram_type == "Concept Map":
300
+ diagram = generate_concept_map(json_str, output_format)
301
+ elif diagram_type == "Synoptic Chart":
302
+ diagram = generate_synoptic_chart(json_str, output_format)
303
+ elif diagram_type == "Radial Diagram":
304
+ diagram = generate_radial_diagram(json_str, output_format)
305
+ elif diagram_type == "Process Flow":
306
+ diagram = generate_process_flow_diagram(json_str, output_format)
307
+ elif diagram_type == "WBS Diagram":
308
+ diagram = generate_wbs_diagram(json_str, output_format)
309
+ else:
310
+ return None, "Invalid diagram type"
311
+
312
+ # ์—๋Ÿฌ ๋ฉ”์‹œ์ง€๊ฐ€ ๋ฐ˜ํ™˜๋œ ๊ฒฝ์šฐ
313
+ if isinstance(diagram, str) and diagram.startswith("Error:"):
314
+ return None, f"Diagram generation error: {diagram}\n\nGenerated JSON:\n{json_str}"
315
+
316
+ return diagram, json_str
317
+
318
+ except Exception as e:
319
+ return None, f"Error generating diagram: {str(e)}\n\nGenerated JSON:\n{json_str}"
320
+
321
+ except json.JSONDecodeError as e:
322
+ return None, f"Invalid JSON generated: {str(e)}\n\nGenerated content:\n{generated_json}"
323
+ except Exception as e:
324
+ return None, f"Unexpected error: {str(e)}\n\nGenerated content:\n{generated_json}"
325
+
326
+ if __name__ == "__main__":
327
+ DEFAULT_BASE_COLOR = '#19191a'
328
+
329
+ with gr.Blocks(
330
+ title="Advanced Graph Generator",
331
+ theme=gr.themes.Soft(
332
+ primary_hue="violet",
333
+ secondary_hue="purple",
334
+ ),
335
+ css="""
336
+ /* ๊ทธ๋ผ๋””์–ธํŠธ ๋ฐฐ๊ฒฝ */
337
+ .gradio-container {
338
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif !important;
339
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
340
+ min-height: 100vh;
341
+ }
342
+
343
+ /* ๋ฉ”์ธ ์ปจํ…Œ์ด๋„ˆ ์Šคํƒ€์ผ */
344
+ .main-container {
345
+ background: rgba(255, 255, 255, 0.95);
346
+ backdrop-filter: blur(10px);
347
+ border-radius: 20px;
348
+ padding: 30px;
349
+ margin: 20px auto;
350
+ max-width: 1400px;
351
+ box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
352
+ }
353
+
354
+ /* ํ—ค๋” ์Šคํƒ€์ผ */
355
+ .header-section {
356
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
357
+ color: white;
358
+ padding: 40px;
359
+ border-radius: 15px;
360
+ margin-bottom: 30px;
361
+ text-align: center;
362
+ box-shadow: 0 10px 30px rgba(102, 126, 234, 0.3);
363
+ }
364
+
365
+ .header-section h1 {
366
+ font-size: 2.5em;
367
+ font-weight: 700;
368
+ margin-bottom: 10px;
369
+ }
370
+
371
+ .header-section p {
372
+ font-size: 1.2em;
373
+ opacity: 0.9;
374
+ }
375
+
376
+ /* ํƒญ ์Šคํƒ€์ผ */
377
+ .gr-tab-item {
378
+ padding: 15px 30px;
379
+ font-size: 1.1em;
380
+ font-weight: 600;
381
+ background: white;
382
+ border-radius: 10px 10px 0 0;
383
+ transition: all 0.3s ease;
384
+ margin-right: 5px;
385
+ }
386
+
387
+ .gr-tab-item:hover {
388
+ background: linear-gradient(135deg, #f5f7fa 0%, #e9ecef 100%);
389
+ }
390
+
391
+ .gr-tab-item.selected {
392
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
393
+ color: white !important;
394
+ }
395
+
396
+ /* ๋ฒ„ํŠผ ์Šคํƒ€์ผ */
397
+ .gr-button {
398
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
399
+ color: white;
400
+ border: none;
401
+ padding: 12px 30px;
402
+ font-size: 1.1em;
403
+ font-weight: 600;
404
+ border-radius: 10px;
405
+ transition: all 0.3s ease;
406
+ box-shadow: 0 4px 15px rgba(102, 126, 234, 0.3);
407
+ }
408
+
409
+ .gr-button:hover {
410
+ transform: translateY(-2px);
411
+ box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
412
+ }
413
+
414
+ .gr-button.primary {
415
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
416
+ }
417
+
418
+ /* ์ž…๋ ฅ ํ•„๋“œ ์Šคํƒ€์ผ */
419
+ .gr-textbox, .gr-dropdown {
420
+ border: 2px solid #e9ecef;
421
+ border-radius: 10px;
422
+ padding: 12px;
423
+ font-size: 1em;
424
+ transition: all 0.3s ease;
425
+ background: white;
426
+ }
427
+
428
+ .gr-textbox:focus, .gr-dropdown:focus {
429
+ border-color: #667eea;
430
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
431
+ }
432
+
433
+ /* ํŒจ๋„ ์Šคํƒ€์ผ */
434
+ .panel-section {
435
+ background: white;
436
+ border-radius: 15px;
437
+ padding: 25px;
438
+ margin-bottom: 20px;
439
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
440
+ }
441
+
442
+ /* ์ด๋ฏธ์ง€ ์ปจํ…Œ์ด๋„ˆ ์Šคํƒ€์ผ */
443
+ .gr-image {
444
+ border-radius: 15px;
445
+ overflow: hidden;
446
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
447
+ }
448
+
449
+ /* ์˜ˆ์ œ ์ด๋ฏธ์ง€ ์Šคํƒ€์ผ */
450
+ .example-images {
451
+ gap: 20px;
452
+ }
453
+
454
+ .example-images .gr-image {
455
+ transition: transform 0.3s ease;
456
+ }
457
+
458
+ .example-images .gr-image:hover {
459
+ transform: scale(1.02);
460
+ }
461
+
462
+ /* ๋ผ๋””์˜ค ๋ฒ„ํŠผ ์Šคํƒ€์ผ */
463
+ .gr-radio {
464
+ background: white;
465
+ padding: 15px;
466
+ border-radius: 10px;
467
+ border: 2px solid #e9ecef;
468
+ }
469
+
470
+ /* LLM ํƒญ ํŠน๋ณ„ ์Šคํƒ€์ผ */
471
+ .llm-tab {
472
+ background: linear-gradient(135deg, #f5f7fa 0%, #ffffff 100%);
473
+ padding: 30px;
474
+ border-radius: 15px;
475
+ }
476
+
477
+ .llm-input-section {
478
+ background: white;
479
+ padding: 25px;
480
+ border-radius: 15px;
481
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05);
482
+ margin-bottom: 20px;
483
+ }
484
+
485
+ /* ๋ฐ˜์‘ํ˜• ๋””์ž์ธ */
486
+ @media (max-width: 768px) {
487
+ .main-container {
488
+ padding: 15px;
489
+ margin: 10px;
490
+ }
491
+
492
+ .header-section h1 {
493
+ font-size: 2em;
494
+ }
495
+
496
+ .gr-tab-item {
497
+ padding: 10px 15px;
498
+ font-size: 1em;
499
+ }
500
+ }
501
+ """
502
+ ) as demo:
503
+ with gr.Column(elem_classes=["main-container"]):
504
+ with gr.Column(elem_classes=["header-section"]):
505
+ gr.Markdown(
506
+ """
507
+ # ๐ŸŽจ ChartGPT : AI-Powered Multi Diagram Generator
508
+ ### based LLM 'Gemma-3-R1984-27B' Model. Powered by VIDraftโšก
509
+ """
510
+ )
511
+
512
+ with gr.Row(variant="panel", elem_classes=["panel-section"]):
513
+ output_format_radio = gr.Radio(
514
+ choices=["png", "svg"],
515
+ value="png",
516
+ label="๐Ÿ“ Output Format",
517
+ interactive=True
518
+ )
519
+
520
+ with gr.Tabs():
521
+ # AI ์–ด์‹œ์Šคํ„ดํŠธ ํƒญ (์ฒซ ๋ฒˆ์งธ)
522
+ with gr.TabItem("๐Ÿค– AI Assistant", elem_classes=["llm-tab"]):
523
+ gr.Markdown(
524
+ """
525
+ ### ๐Ÿ’ก Describe your diagram in Korean or English, and AI will create it for you!
526
+ """
527
+ )
528
+
529
+ with gr.Row():
530
+ with gr.Column(scale=1, elem_classes=["llm-input-section"]):
531
+ prompt_input = gr.Textbox(
532
+ placeholder="์˜ˆ: '๋จธ์‹ ๋Ÿฌ๋‹ ํ”„๋กœ์„ธ์Šค๋ฅผ ๋ณด์—ฌ์ฃผ๋Š” ํ”Œ๋กœ์šฐ์ฐจํŠธ๋ฅผ ๋งŒ๋“ค์–ด์ค˜' or 'Create a concept map about climate change'",
533
+ label="๐Ÿ“ Enter your prompt",
534
+ lines=3
535
+ )
536
+
537
+ diagram_type_select = gr.Dropdown(
538
+ choices=["Concept Map", "Synoptic Chart", "Radial Diagram", "Process Flow", "WBS Diagram"],
539
+ value="Concept Map",
540
+ label="๐Ÿ“Š Select Diagram Type",
541
+ interactive=True
542
+ )
543
+
544
+ generate_btn = gr.Button("โœจ Generate with AI", variant="primary", size="lg")
545
+
546
+ generated_json_output = gr.Textbox(
547
+ label="๐Ÿ“„ Generated JSON",
548
+ lines=15,
549
+ interactive=True,
550
+ visible=True
551
+ )
552
+
553
+ with gr.Column(scale=2):
554
+ ai_output_image = gr.Image(
555
+ label="๐ŸŽจ Generated Diagram",
556
+ type="filepath",
557
+ show_download_button=True,
558
+ height=600
559
+ )
560
+
561
+ generate_btn.click(
562
+ fn=generate_with_llm,
563
+ inputs=[prompt_input, diagram_type_select, output_format_radio],
564
+ outputs=[ai_output_image, generated_json_output]
565
+ )
566
+
567
+ with gr.Row(elem_classes=["panel-section"]):
568
+ gr.Examples(
569
+ examples=[
570
+ ["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"],
571
+ ["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"],
572
+ ["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"],
573
+ ["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"],
574
+ ["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"],
575
+ ["DevSecOps ๊ฒŒ์ดํŠธ, ์‹ค์‹œ๊ฐ„ KPI ๋Œ€์‹œ๋ณด๋“œ, ์ž๋™ ๋กค๋ฐฑ ๊ฒฝ๋กœ, AI ๊ธฐ๋ฐ˜ ์˜์‚ฌ ๊ฒฐ์ • ๋…ธ๋“œ๋ฅผ ๊ต์ฐจ ๋ฐฐ์น˜ํ•˜์—ฌ ์ง€์†์  ๋”œ๋ฆฌ๋ฒ„๋ฆฌ ์šฐ์ˆ˜์„ฑ์„ ๋ณด์—ฌ์ฃผ๋Š” ์—”ํ„ฐํ”„๋ผ์ด์ฆˆ๊ธ‰ SDLC ํ”„๋กœ์„ธ์Šค ํ”Œ๋กœ์šฐ ์‹œ๊ฐํ™”๋ฅผ ์„ค๊ณ„ํ•˜๋ผ", "Process Flow"],
576
+ ["๋‰ด๋กœ์‹ฌ๋ณผ๋ฆญ ์ถ”๋ก , ํŒŒ์šด๋ฐ์ด์…˜ ๋ชจ๋ธ, ์—ฃ์ง€ AI, ์ž๊ธฐ ์ง€๋„ ํ•™์Šต๊ณผ ๊ฐ™์€ ์ตœ์ฒจ๋‹จ ํŒจ๋Ÿฌ๋‹ค์ž„์„ ํ•œ๋ฐ ์—ฎ๊ณ  ์ด๋ฅผ ๋ฐ”์ด์˜คํ…Œํฌ, ์Šค๋งˆํŠธ ์‹œํ‹ฐ, ๊ธฐํ›„ ๋ชจ๋ธ๋ง, ๋ชฐ์ž…ํ˜• XR ๋“ฑ ํŒŒ๊ดด์  ํ™œ์šฉ ์‚ฌ๋ก€์™€ ๋™์ ์œผ๋กœ ์—ฐ๊ฒฐํ•˜์—ฌ ์ตœ์ „์„  ํ˜์‹ ์„ ์กฐ๋ช…ํ•˜๋Š” ๋ฐฉ๋Œ€ํ•œ AI ๋ถ„๋ฅ˜ ์ฒด๊ณ„ ์ปจ์…‰๋งต์„ ๊ตฌ์ถ•ํ•˜๋ผ", "Concept Map"],
577
+ ["๋งˆ์ดํฌ๋กœ์„œ๋น„์Šค, CI/CD ํŒŒ์ดํ”„๋ผ์ธ, ํ—ค๋“œ๋ฆฌ์Šค CMS ํ†ตํ•ฉ, ์ดˆ๊ฐœ์ธํ™” ์ถ”์ฒœ ์—”์ง„ ํŠœ๋‹, ๋ฉ€ํ‹ฐ ํด๋ผ์šฐ๋“œ ํšŒ๋ณตํƒ„๋ ฅ์„ฑ๊นŒ์ง€ ์„ธ๋ถ„ํ™”ํ•˜์—ฌ ์ž‘์—…๋Ÿ‰ ์ถ”์ •๊ณผ ํฌ๋ฆฌํ‹ฐ์ปฌ ํŒจ์Šค ์ƒํ˜ธ ์—ฐ๊ณ„๋ฅผ ์ฃผ์„์œผ๋กœ ๋‹ด์€ ์ฐจ์„ธ๋Œ€ ์˜ด๋‹ˆ์ฑ„๋„ e-์ปค๋จธ์Šค ์ƒํƒœ๊ณ„๋ฅผ ์œ„ํ•œ ์ดˆ์ •๋ฐ€ WBS๋ฅผ ์„ค๊ณ„ํ•˜๋ผ", "WBS Diagram"],
578
+ ["์žฌ์ƒ์—๋„ˆ์ง€๋ฅผ ์ค‘์‹ฌ์œผ๋กœ ๋‹ค์ธต ๋ฐฉ์‚ฌํ˜• ๋‹ค์ด์–ด๊ทธ๋žจ์„ ๊ทธ๋ฆฌ๋˜, ๋ฐœ์ „ยท์ €์žฅยท์†ก๋ฐฐ์ „ยท์ •์ฑ… ์ˆ˜๋‹จ์„ ๋™์‹ฌ์›์œผ๋กœ ๋ฐฐ์น˜ํ•˜๊ณ , ํƒœ์–‘๊ด‘ PV, CSP, ํ•ด์ƒ ๋ฐ ์œก์ƒํ’๋ ฅ, ์กฐ๋ ฅ, ๊ทธ๋ฆฐ ์ˆ˜์†Œ, ์ฐจ์„ธ๋Œ€ ๋ฐฐํ„ฐ๋ฆฌ ํ™”ํ•™, ์Šค๋งˆํŠธ ๊ทธ๋ฆฌ๋“œ AI๋ฅผ ์Šคํฌํฌ๋กœ ์—ฐ๊ฒฐํ•˜๋ฉฐ, LCOE ์ถ”์„ธ์™€ ํƒ„์†Œ ์ƒ์‡„ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ ์ฃผ์„์œผ๋กœ ํ‘œ๊ธฐํ•˜๋ผ", "Radial Diagram"],
579
+ ["๋ฐ์ดํ„ฐ ์ถœ์ฒ˜, ์ž๋™ํ™”๋œ ํ”ผ์ฒ˜ ์Šคํ† ์–ด, ์—ฐํ•ฉ ํ•™์Šต ์˜ค์ผ€์ŠคํŠธ๋ ˆ์ด์…˜, ๋ชจ๋ธ ์„ฑ๋Šฅ ๋“œ๋ฆฌํ”„ํŠธ ๊ฐ์ง€, ์„€๋„ ๋ฐฐํฌ ์บ๋„ˆ๋ฆฌ, ์œค๋ฆฌ ์ค€์ˆ˜ ๊ฐ์‚ฌ์˜ ์‹œ๊ฐ„ ์ถ• ๋ ˆ์ธ์„ ๋ณ‘์น˜ํ•˜๊ณ , ๊ฒŒ์ดํŠธ ๊ธฐ์ค€๊ณผ ๋กค๋ฐฑ ํŠธ๋ฆฌ๊ฑฐ๊นŒ์ง€ ๊ฐ–์ถ˜ ํ”„๋กœ๋•์…˜๊ธ‰ ๋จธ์‹ ๋Ÿฌ๋‹ ํŒŒ์ดํ”„๋ผ์ธ์„ ์˜ํ™”์  ์‹œ์•ผ๋กœ ๋ฌ˜์‚ฌํ•œ ์‹œ๋†‰ํ‹ฑ ์ฐจํŠธ๋ฅผ ์ œ์ž‘ํ•˜๋ผ", "Synoptic Chart"]
580
+ ],
581
+ inputs=[prompt_input, diagram_type_select],
582
+ label="๐Ÿ’ญ Example Prompts"
583
+ )
584
+
585
+ # ๊ธฐ์กด ํƒญ๋“ค
586
+ with gr.TabItem("๐Ÿ—บ๏ธ Concept Map"):
587
+ with gr.Row():
588
+ with gr.Column(scale=1, elem_classes=["panel-section"]):
589
+ json_input_cm = gr.Textbox(
590
+ value=CONCEPT_MAP_JSON,
591
+ placeholder="Paste JSON following the documented format",
592
+ label="JSON Input",
593
+ lines=20
594
+ )
595
+ submit_btn_cm = gr.Button("Generate Concept Map", variant="primary")
596
+
597
+ with gr.Column(scale=2):
598
+ output_cm = gr.Image(
599
+ label="Generated Diagram",
600
+ type="filepath",
601
+ show_download_button=True,
602
+ height=500
603
+ )
604
+
605
+ submit_btn_cm.click(
606
+ fn=generate_concept_map,
607
+ inputs=[json_input_cm, output_format_radio],
608
+ outputs=output_cm
609
+ )
610
+
611
+ gr.Markdown("## ๐Ÿ“ธ Examples")
612
+ with gr.Row(elem_classes=["example-images"]):
613
+ gr.Image(value="./images/cm1.svg", label="Sample 1", show_label=True, interactive=False)
614
+ gr.Image(value="./images/cm2.svg", label="Sample 2", show_label=True, interactive=False)
615
+
616
+ with gr.TabItem("๐Ÿ“Š Synoptic Chart"):
617
+ with gr.Row():
618
+ with gr.Column(scale=1, elem_classes=["panel-section"]):
619
+ json_input_sc = gr.Textbox(
620
+ value=SYNOPTIC_CHART_JSON,
621
+ placeholder="Paste JSON following the documented format",
622
+ label="JSON Input",
623
+ lines=20
624
+ )
625
+ submit_btn_sc = gr.Button("Generate Synoptic Chart", variant="primary")
626
+
627
+ with gr.Column(scale=2):
628
+ output_sc = gr.Image(
629
+ label="Generated Diagram",
630
+ type="filepath",
631
+ show_download_button=True,
632
+ height=500
633
+ )
634
+
635
+ submit_btn_sc.click(
636
+ fn=generate_synoptic_chart,
637
+ inputs=[json_input_sc, output_format_radio],
638
+ outputs=output_sc
639
+ )
640
+
641
+ gr.Markdown("## ๐Ÿ“ธ Examples")
642
+ with gr.Row(elem_classes=["example-images"]):
643
+ gr.Image(value="./images/sc1.svg", label="Sample 1", show_label=True, interactive=False)
644
+ gr.Image(value="./images/sc2.svg", label="Sample 2", show_label=True, interactive=False)
645
+
646
+ with gr.TabItem("โ˜€๏ธ Radial Diagram"):
647
+ with gr.Row():
648
+ with gr.Column(scale=1, elem_classes=["panel-section"]):
649
+ json_input_rd = gr.Textbox(
650
+ value=RADIAL_DIAGRAM_JSON,
651
+ placeholder="Paste JSON following the documented format",
652
+ label="JSON Input",
653
+ lines=20
654
+ )
655
+ submit_btn_rd = gr.Button("Generate Radial Diagram", variant="primary")
656
+
657
+ with gr.Column(scale=2):
658
+ output_rd = gr.Image(
659
+ label="Generated Diagram",
660
+ type="filepath",
661
+ show_download_button=True,
662
+ height=500
663
+ )
664
+
665
+ submit_btn_rd.click(
666
+ fn=generate_radial_diagram,
667
+ inputs=[json_input_rd, output_format_radio],
668
+ outputs=output_rd
669
+ )
670
+
671
+ gr.Markdown("## ๐Ÿ“ธ Examples")
672
+ with gr.Row(elem_classes=["example-images"]):
673
+ gr.Image(value="./images/rd1.svg", label="Sample 1", show_label=True, interactive=False)
674
+ gr.Image(value="./images/rd2.svg", label="Sample 2", show_label=True, interactive=False)
675
+ gr.Image(value="./images/rd3.svg", label="Sample 3", show_label=True, interactive=False)
676
+ gr.Image(value="./images/rd4.svg", label="Sample 4", show_label=True, interactive=False)
677
+
678
+ with gr.TabItem("๐Ÿ”„ Process Flow"):
679
+ with gr.Row():
680
+ with gr.Column(scale=1, elem_classes=["panel-section"]):
681
+ json_input_pf = gr.Textbox(
682
+ value=PROCESS_FLOW_JSON,
683
+ placeholder="Paste JSON following the documented format",
684
+ label="JSON Input",
685
+ lines=20
686
+ )
687
+ submit_btn_pf = gr.Button("Generate Process Flow", variant="primary")
688
+
689
+ with gr.Column(scale=2):
690
+ output_pf = gr.Image(
691
+ label="Generated Diagram",
692
+ type="filepath",
693
+ show_download_button=True,
694
+ height=500
695
+ )
696
+
697
+ submit_btn_pf.click(
698
+ fn=generate_process_flow_diagram,
699
+ inputs=[json_input_pf, output_format_radio],
700
+ outputs=output_pf
701
+ )
702
+
703
+ gr.Markdown("## ๐Ÿ“ธ Examples")
704
+ with gr.Row(elem_classes=["example-images"]):
705
+ gr.Image(value="./images/pf1.svg", label="Sample 1", show_label=True, interactive=False)
706
+ gr.Image(value="./images/pf2.svg", label="Sample 2", show_label=True, interactive=False)
707
+
708
+ with gr.TabItem("๐Ÿ“‹ WBS Diagram"):
709
+ with gr.Row():
710
+ with gr.Column(scale=1, elem_classes=["panel-section"]):
711
+ json_input_wbs = gr.Textbox(
712
+ value=WBS_DIAGRAM_JSON,
713
+ placeholder="Paste JSON following the documented format",
714
+ label="JSON Input",
715
+ lines=20
716
+ )
717
+ submit_btn_wbs = gr.Button("Generate WBS Diagram", variant="primary")
718
+
719
+ with gr.Column(scale=2):
720
+ output_wbs = gr.Image(
721
+ label="Generated Diagram",
722
+ type="filepath",
723
+ show_download_button=True,
724
+ height=500
725
+ )
726
+
727
+ submit_btn_wbs.click(
728
+ fn=generate_wbs_diagram,
729
+ inputs=[json_input_wbs, output_format_radio],
730
+ outputs=output_wbs
731
+ )
732
+
733
+ gr.Markdown("## ๐Ÿ“ธ Examples")
734
+ with gr.Row(elem_classes=["example-images"]):
735
+ gr.Image(value="./images/wd1.svg", label="Sample 1", show_label=True, interactive=False)
736
+ gr.Image(value="./images/wd2.svg", label="Sample 2", show_label=True, interactive=False)
737
+
738
+ demo.launch(
739
+ mcp_server=True,
740
+ share=False,
741
+ server_port=7860,
742
+ server_name="0.0.0.0"
743
+ )