openfree commited on
Commit
1be9521
ยท
verified ยท
1 Parent(s): 8f54e55

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +22 -877
app.py CHANGED
@@ -1,890 +1,35 @@
1
- import gradio as gr
2
  import os
3
  import sys
4
- import json
5
- import requests
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
- response = requests.post(url, json=payload, headers=headers, timeout=30)
 
313
 
314
- # API ์‘๋‹ต ์ƒํƒœ ํ™•์ธ
315
- if response.status_code != 200:
316
- return json.dumps({"error": f"API returned status code {response.status_code}: {response.text}"})
317
 
318
- response_data = response.json()
 
 
 
319
 
320
- if 'choices' in response_data and len(response_data['choices']) > 0:
321
- content = response_data['choices'][0]['message']['content']
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
- if diagram_type == "Concept Map":
406
- diagram = generate_concept_map(json_str, output_format)
407
- elif diagram_type == "Synoptic Chart":
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
- return None, f"Unexpected error: {str(e)}\n\nGenerated content:\n{generated_json}", search_info
 
 
431
 
432
  if __name__ == "__main__":
433
- DEFAULT_BASE_COLOR = '#19191a'
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()