openfree commited on
Commit
4f65198
ยท
verified ยท
1 Parent(s): 2bd1620

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +593 -0
app.py ADDED
@@ -0,0 +1,593 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import re
3
+ import random
4
+ from http import HTTPStatus
5
+ from typing import Dict, List, Optional, Tuple
6
+ import base64
7
+ import anthropic
8
+ import openai
9
+ import asyncio
10
+ import time
11
+ from functools import partial
12
+ import json
13
+ import gradio as gr
14
+ import modelscope_studio.components.base as ms
15
+ import modelscope_studio.components.legacy as legacy
16
+ import modelscope_studio.components.antd as antd
17
+ import html
18
+ import urllib.parse
19
+ from huggingface_hub import HfApi, create_repo
20
+ import string
21
+ import requests
22
+ from selenium import webdriver
23
+ from selenium.webdriver.support.ui import WebDriverWait
24
+ from selenium.webdriver.support import expected_conditions as EC
25
+ from selenium.webdriver.common.by import By
26
+ from selenium.common.exceptions import WebDriverException, TimeoutException
27
+ from PIL import Image
28
+ from io import BytesIO
29
+ from datetime import datetime
30
+
31
+ # SystemPrompt ๋ถ€๋ถ„์„ ์ง์ ‘ ์ •์˜
32
+ SystemPrompt = """You are 'MOUSE-I', an advanced AI visualization expert. Your mission is to transform every response into a visually stunning and highly informative presentation.
33
+
34
+ Core Capabilities:
35
+ - Transform text responses into rich visual experiences
36
+ - Create interactive data visualizations and charts
37
+ - Design beautiful and intuitive user interfaces
38
+ - Utilize engaging animations and transitions
39
+ - Present information in a clear, structured manner
40
+
41
+ Visual Elements to Include:
42
+ - Charts & Graphs (using Chart.js, D3.js)
43
+ - Interactive Data Visualizations
44
+ - Modern UI Components
45
+ - Engaging Animations
46
+ - Informative Icons & Emojis
47
+ - Color-coded Information Blocks
48
+ - Progress Indicators
49
+ - Timeline Visualizations
50
+ - Statistical Representations
51
+ - Comparison Tables
52
+
53
+ Technical Requirements:
54
+ - Modern HTML5/CSS3/JavaScript
55
+ - Responsive Design
56
+ - Interactive Elements
57
+ - Clean Typography
58
+ - Professional Color Schemes
59
+ - Smooth Animations
60
+ - Cross-browser Compatibility
61
+
62
+ Libraries Available:
63
+ - Chart.js for Data Visualization
64
+ - D3.js for Complex Graphics
65
+ - Bootstrap for Layout
66
+ - jQuery for Interactions
67
+ - Three.js for 3D Elements
68
+
69
+ Design Principles:
70
+ - Visual Hierarchy
71
+ - Clear Information Flow
72
+ - Consistent Styling
73
+ - Intuitive Navigation
74
+ - Engaging User Experience
75
+ - Accessibility Compliance
76
+
77
+ Remember to:
78
+ - Present data in the most visually appealing way
79
+ - Use appropriate charts for different data types
80
+ - Include interactive elements where relevant
81
+ - Maintain a professional and modern aesthetic
82
+ - Ensure responsive design for all devices
83
+
84
+ Return only HTML code wrapped in code blocks, focusing on creating visually stunning and informative presentations.
85
+ """
86
+
87
+ from config import DEMO_LIST
88
+
89
+ class Role:
90
+ SYSTEM = "system"
91
+ USER = "user"
92
+ ASSISTANT = "assistant"
93
+
94
+ History = List[Tuple[str, str]]
95
+ Messages = List[Dict[str, str]]
96
+
97
+ # ์ด๋ฏธ์ง€ ์บ์‹œ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ
98
+ IMAGE_CACHE = {}
99
+
100
+ # boost_prompt ํ•จ์ˆ˜์™€ handle_boost ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค
101
+ def boost_prompt(prompt: str) -> str:
102
+ if not prompt:
103
+ return ""
104
+
105
+ # ์ฆ๊ฐ•์„ ์œ„ํ•œ ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ
106
+ boost_system_prompt = """
107
+ ๋‹น์‹ ์€ ์›น ๊ฐœ๋ฐœ ํ”„๋กฌํ”„ํŠธ ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค.
108
+ ์ฃผ์–ด์ง„ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ๋” ์ƒ์„ธํ•˜๊ณ  ์ „๋ฌธ์ ์ธ ์š”๊ตฌ์‚ฌํ•ญ์œผ๋กœ ํ™•์žฅํ•˜๋˜,
109
+ ์›๋ž˜ ์˜๋„์™€ ๋ชฉ์ ์€ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜๋ฉด์„œ ๋‹ค์Œ ๊ด€์ ๋“ค์„ ๊ณ ๋ คํ•˜์—ฌ ์ฆ๊ฐ•ํ•˜์‹ญ์‹œ์˜ค:
110
+ 1. ๊ธฐ์ˆ ์  ๊ตฌํ˜„ ์ƒ์„ธ
111
+ 2. UI/UX ๋””์ž์ธ ์š”์†Œ
112
+ 3. ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ์ตœ์ ํ™”
113
+ 4. ์„ฑ๋Šฅ๊ณผ ๋ณด์•ˆ
114
+ 5. ์ ‘๊ทผ์„ฑ๊ณผ ํ˜ธํ™˜์„ฑ
115
+
116
+ ๊ธฐ์กด SystemPrompt์˜ ๋ชจ๋“  ๊ทœ์น™์„ ์ค€์ˆ˜ํ•˜๋ฉด์„œ ์ฆ๊ฐ•๋œ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ƒ์„ฑํ•˜์‹ญ์‹œ์˜ค.
117
+ """
118
+
119
+ try:
120
+ # Claude API ์‹œ๋„
121
+ try:
122
+ response = claude_client.messages.create(
123
+ model="claude-3-5-sonnet-20241022",
124
+ max_tokens=2000,
125
+ messages=[{
126
+ "role": "user",
127
+ "content": f"๋‹ค์Œ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ์ฆ๊ฐ•ํ•˜์‹œ์˜ค: {prompt}"
128
+ }]
129
+ )
130
+
131
+ if hasattr(response, 'content') and len(response.content) > 0:
132
+ return response.content[0].text
133
+ raise Exception("Claude API ์‘๋‹ต ํ˜•์‹ ์˜ค๋ฅ˜")
134
+
135
+ except Exception as claude_error:
136
+ print(f"Claude API ์—๋Ÿฌ, OpenAI๋กœ ์ „ํ™˜: {str(claude_error)}")
137
+
138
+ # OpenAI API ์‹œ๋„
139
+ completion = openai_client.chat.completions.create(
140
+ model="gpt-4",
141
+ messages=[
142
+ {"role": "system", "content": boost_system_prompt},
143
+ {"role": "user", "content": f"๋‹ค์Œ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ์ฆ๊ฐ•ํ•˜์‹œ์˜ค: {prompt}"}
144
+ ],
145
+ max_tokens=2000,
146
+ temperature=0.7
147
+ )
148
+
149
+ if completion.choices and len(completion.choices) > 0:
150
+ return completion.choices[0].message.content
151
+ raise Exception("OpenAI API ์‘๋‹ต ํ˜•์‹ ์˜ค๋ฅ˜")
152
+
153
+ except Exception as e:
154
+ print(f"ํ”„๋กฌํ”„ํŠธ ์ฆ๊ฐ• ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
155
+ return prompt # ์˜ค๋ฅ˜ ๋ฐœ์ƒ์‹œ ์›๋ณธ ํ”„๋กฌํ”„ํŠธ ๋ฐ˜ํ™˜
156
+
157
+ # Boost ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
158
+ def handle_boost(prompt: str):
159
+ try:
160
+ boosted_prompt = boost_prompt(prompt)
161
+ return boosted_prompt, gr.update(active_key="empty")
162
+ except Exception as e:
163
+ print(f"Boost ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜: {str(e)}")
164
+ return prompt, gr.update(active_key="empty")
165
+
166
+ def get_image_base64(image_path):
167
+ if image_path in IMAGE_CACHE:
168
+ return IMAGE_CACHE[image_path]
169
+ try:
170
+ with open(image_path, "rb") as image_file:
171
+ encoded_string = base64.b64encode(image_file.read()).decode()
172
+ IMAGE_CACHE[image_path] = encoded_string
173
+ return encoded_string
174
+ except:
175
+ return IMAGE_CACHE.get('default.png', '')
176
+
177
+ def history_to_messages(history: History, system: str) -> Messages:
178
+ messages = [{'role': Role.SYSTEM, 'content': system}]
179
+ for h in history:
180
+ messages.append({'role': Role.USER, 'content': h[0]})
181
+ messages.append({'role': Role.ASSISTANT, 'content': h[1]})
182
+ return messages
183
+
184
+ def messages_to_history(messages: Messages) -> History:
185
+ assert messages[0]['role'] == Role.SYSTEM
186
+ history = []
187
+ for q, r in zip(messages[1::2], messages[2::2]):
188
+ history.append([q['content'], r['content']])
189
+ return history
190
+
191
+ # API ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
192
+ YOUR_ANTHROPIC_TOKEN = os.getenv('ANTHROPIC_API_KEY', '') # ๊ธฐ๋ณธ๊ฐ’ ์ถ”๊ฐ€
193
+ YOUR_OPENAI_TOKEN = os.getenv('OPENAI_API_KEY', '') # ๊ธฐ๋ณธ๊ฐ’ ์ถ”๊ฐ€
194
+
195
+ # API ํ‚ค ๊ฒ€์ฆ
196
+ if not YOUR_ANTHROPIC_TOKEN or not YOUR_OPENAI_TOKEN:
197
+ print("Warning: API keys not found in environment variables")
198
+
199
+ # API ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์‹œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ถ”๊ฐ€
200
+ try:
201
+ claude_client = anthropic.Anthropic(api_key=YOUR_ANTHROPIC_TOKEN)
202
+ openai_client = openai.OpenAI(api_key=YOUR_OPENAI_TOKEN)
203
+ except Exception as e:
204
+ print(f"Error initializing API clients: {str(e)}")
205
+ claude_client = None
206
+ openai_client = None
207
+
208
+ # try_claude_api ํ•จ์ˆ˜ ์ˆ˜์ •
209
+ async def try_claude_api(system_message, claude_messages, timeout=15):
210
+ try:
211
+ start_time = time.time()
212
+ with claude_client.messages.stream(
213
+ model="claude-3-5-sonnet-20241022",
214
+ max_tokens=7800,
215
+ system=system_message,
216
+ messages=claude_messages
217
+ ) as stream:
218
+ collected_content = ""
219
+ for chunk in stream:
220
+ current_time = time.time()
221
+ if current_time - start_time > timeout:
222
+ print(f"Claude API response time: {current_time - start_time:.2f} seconds")
223
+ raise TimeoutError("Claude API timeout")
224
+ if chunk.type == "content_block_delta":
225
+ collected_content += chunk.delta.text
226
+ yield collected_content
227
+ await asyncio.sleep(0)
228
+
229
+ start_time = current_time
230
+
231
+ except Exception as e:
232
+ print(f"Claude API error: {str(e)}")
233
+ raise e
234
+
235
+ async def try_openai_api(openai_messages):
236
+ try:
237
+ stream = openai_client.chat.completions.create(
238
+ model="gpt-4o",
239
+ messages=openai_messages,
240
+ stream=True,
241
+ max_tokens=4096,
242
+ temperature=0.7
243
+ )
244
+
245
+ collected_content = ""
246
+ for chunk in stream:
247
+ if chunk.choices[0].delta.content is not None:
248
+ collected_content += chunk.choices[0].delta.content
249
+ yield collected_content
250
+
251
+ except Exception as e:
252
+ print(f"OpenAI API error: {str(e)}")
253
+ raise e
254
+
255
+ class Demo:
256
+ def __init__(self):
257
+ pass
258
+
259
+ async def generation_code(self, query: Optional[str], _setting: Dict[str, str], _history: Optional[History]):
260
+ if not query or query.strip() == '':
261
+ query = random.choice(DEMO_LIST)['description']
262
+
263
+ if _history is None:
264
+ _history = []
265
+
266
+ messages = history_to_messages(_history, _setting['system'])
267
+ system_message = messages[0]['content']
268
+
269
+ claude_messages = [
270
+ {"role": msg["role"] if msg["role"] != "system" else "user", "content": msg["content"]}
271
+ for msg in messages[1:] + [{'role': Role.USER, 'content': query}]
272
+ if msg["content"].strip() != ''
273
+ ]
274
+
275
+ openai_messages = [{"role": "system", "content": system_message}]
276
+ for msg in messages[1:]:
277
+ openai_messages.append({
278
+ "role": msg["role"],
279
+ "content": msg["content"]
280
+ })
281
+ openai_messages.append({"role": "user", "content": query})
282
+
283
+ try:
284
+ yield [
285
+ "Generating code...",
286
+ _history,
287
+ None,
288
+ gr.update(active_key="loading"),
289
+ gr.update(open=True)
290
+ ]
291
+ await asyncio.sleep(0)
292
+
293
+ collected_content = None
294
+ try:
295
+ async for content in try_claude_api(system_message, claude_messages):
296
+ yield [
297
+ content,
298
+ _history,
299
+ None,
300
+ gr.update(active_key="loading"),
301
+ gr.update(open=True)
302
+ ]
303
+ await asyncio.sleep(0)
304
+ collected_content = content
305
+
306
+ except Exception as claude_error:
307
+ print(f"Falling back to OpenAI API due to Claude error: {str(claude_error)}")
308
+
309
+ async for content in try_openai_api(openai_messages):
310
+ yield [
311
+ content,
312
+ _history,
313
+ None,
314
+ gr.update(active_key="loading"),
315
+ gr.update(open=True)
316
+ ]
317
+ await asyncio.sleep(0)
318
+ collected_content = content
319
+
320
+ if collected_content:
321
+ _history = messages_to_history([
322
+ {'role': Role.SYSTEM, 'content': system_message}
323
+ ] + claude_messages + [{
324
+ 'role': Role.ASSISTANT,
325
+ 'content': collected_content
326
+ }])
327
+
328
+ # code_drawer๋ฅผ ๋‹ซ๋„๋ก ์ˆ˜์ •
329
+ yield [
330
+ collected_content,
331
+ _history,
332
+ send_to_sandbox(remove_code_block(collected_content)),
333
+ gr.update(active_key="render"),
334
+ gr.update(open=False) # code_drawer๋ฅผ ๋‹ซ์Œ
335
+ ]
336
+
337
+ else:
338
+ raise ValueError("No content was generated from either API")
339
+
340
+ except Exception as e:
341
+ print(f"Error details: {str(e)}")
342
+ raise ValueError(f'Error calling APIs: {str(e)}')
343
+
344
+ def clear_history(self):
345
+ return []
346
+
347
+ def remove_code_block(text):
348
+ pattern = r'```html\n(.+?)\n```'
349
+ match = re.search(pattern, text, re.DOTALL)
350
+ if match:
351
+ return match.group(1).strip()
352
+ else:
353
+ return text.strip()
354
+
355
+ def history_render(history: History):
356
+ return gr.update(open=True), history
357
+
358
+ def send_to_sandbox(code):
359
+ encoded_html = base64.b64encode(code.encode('utf-8')).decode('utf-8')
360
+ data_uri = f"data:text/html;charset=utf-8;base64,{encoded_html}"
361
+ return f"""
362
+ <iframe
363
+ src="{data_uri}"
364
+ style="width:100%; height:800px; border:none;"
365
+ frameborder="0"
366
+ ></iframe>
367
+ """
368
+ # ๋ฐฐํฌ ๊ด€๋ จ ํ•จ์ˆ˜ ์ถ”๊ฐ€
369
+ def generate_space_name():
370
+ """6์ž๋ฆฌ ๋žœ๋ค ์˜๋ฌธ ์ด๋ฆ„ ์ƒ์„ฑ"""
371
+ letters = string.ascii_lowercase
372
+ return ''.join(random.choice(letters) for i in range(6))
373
+
374
+ def deploy_to_vercel(code: str):
375
+ try:
376
+ token = "A8IFZmgW2cqA4yUNlLPnci0N"
377
+ if not token:
378
+ return "Vercel ํ† ํฐ์ด ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."
379
+
380
+ # 6์ž๋ฆฌ ์˜๋ฌธ ํ”„๋กœ์ ํŠธ ์ด๋ฆ„ ์ƒ์„ฑ
381
+ project_name = ''.join(random.choice(string.ascii_lowercase) for i in range(6))
382
+
383
+
384
+ # Vercel API ์—”๋“œํฌ์ธํŠธ
385
+ deploy_url = "https://api.vercel.com/v13/deployments"
386
+
387
+ # ํ—ค๋” ์„ค์ •
388
+ headers = {
389
+ "Authorization": f"Bearer {token}",
390
+ "Content-Type": "application/json"
391
+ }
392
+
393
+ # package.json ํŒŒ์ผ ์ƒ์„ฑ
394
+ package_json = {
395
+ "name": project_name,
396
+ "version": "1.0.0",
397
+ "private": True, # true -> True๋กœ ์ˆ˜์ •
398
+ "dependencies": {
399
+ "vite": "^5.0.0"
400
+ },
401
+ "scripts": {
402
+ "dev": "vite",
403
+ "build": "echo 'No build needed' && mkdir -p dist && cp index.html dist/",
404
+ "preview": "vite preview"
405
+ }
406
+ }
407
+
408
+ # ๋ฐฐํฌํ•  ํŒŒ์ผ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ
409
+ files = [
410
+ {
411
+ "file": "index.html",
412
+ "data": code
413
+ },
414
+ {
415
+ "file": "package.json",
416
+ "data": json.dumps(package_json, indent=2) # indent ์ถ”๊ฐ€๋กœ ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ
417
+ }
418
+ ]
419
+
420
+ # ํ”„๋กœ์ ํŠธ ์„ค์ •
421
+ project_settings = {
422
+ "buildCommand": "npm run build",
423
+ "outputDirectory": "dist",
424
+ "installCommand": "npm install",
425
+ "framework": None
426
+ }
427
+
428
+ # ๋ฐฐํฌ ์š”์ฒญ ๋ฐ์ดํ„ฐ
429
+ deploy_data = {
430
+ "name": project_name,
431
+ "files": files,
432
+ "target": "production",
433
+ "projectSettings": project_settings
434
+ }
435
+
436
+
437
+ deploy_response = requests.post(deploy_url, headers=headers, json=deploy_data)
438
+
439
+ if deploy_response.status_code != 200:
440
+ return f"๋ฐฐํฌ ์‹คํŒจ: {deploy_response.text}"
441
+
442
+ # URL ํ˜•์‹ ์ˆ˜์ • - 6์ž๋ฆฌ.vercel.app ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜
443
+ deployment_url = f"{project_name}.vercel.app"
444
+
445
+ time.sleep(5)
446
+
447
+ return f"""๋ฐฐํฌ ์™„๋ฃŒ! <a href="https://{deployment_url}" target="_blank" style="color: #1890ff; text-decoration: underline; cursor: pointer;">https://{deployment_url}</a>"""
448
+
449
+ except Exception as e:
450
+ return f"๋ฐฐํฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
451
+
452
+ theme = gr.themes.Soft()
453
+
454
+ def create_main_interface():
455
+ """๋ฉ”์ธ ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ ํ•จ์ˆ˜"""
456
+
457
+ def execute_code(query: str):
458
+ if not query or query.strip() == '':
459
+ return None, gr.update(active_key="empty")
460
+
461
+ try:
462
+ if '```html' in query and '```' in query:
463
+ code = remove_code_block(query)
464
+ else:
465
+ code = query.strip()
466
+
467
+ return send_to_sandbox(code), gr.update(active_key="render")
468
+ except Exception as e:
469
+ print(f"Error executing code: {str(e)}")
470
+ return None, gr.update(active_key="empty")
471
+
472
+ # CSS ํŒŒ์ผ ๋‚ด์šฉ์„ ์ง์ ‘ ์ ์šฉ
473
+ with open('app.css', 'r', encoding='utf-8') as f:
474
+ custom_css = f.read()
475
+
476
+ demo = gr.Blocks(css=custom_css, theme=theme)
477
+
478
+ with demo:
479
+ with gr.Tabs(elem_classes="main-tabs") as tabs:
480
+ # MOUSE ํƒญ
481
+ with gr.Tab("VisualAI", elem_id="mouse-tab", elem_classes="mouse-tab"):
482
+ history = gr.State([])
483
+ setting = gr.State({
484
+ "system": SystemPrompt,
485
+ })
486
+
487
+ with ms.Application() as app:
488
+ with antd.ConfigProvider():
489
+ # Drawer ์ปดํฌ๋„ŒํŠธ๋“ค
490
+ with antd.Drawer(open=False, title="Thinking", placement="left", width="750px") as code_drawer:
491
+ code_output = legacy.Markdown()
492
+
493
+ with antd.Drawer(open=False, title="history", placement="left", width="900px") as history_drawer:
494
+ history_output = legacy.Chatbot(show_label=False, flushing=False, height=960, elem_classes="history_chatbot")
495
+
496
+ # ๋ฉ”์ธ ์ปจํ…์ธ ๋ฅผ ์œ„ํ•œ Row
497
+ with antd.Row(gutter=[32, 12]) as layout:
498
+ # ์ขŒ์ธก ํŒจ๋„
499
+ with antd.Col(span=24, md=8):
500
+ with antd.Flex(vertical=True, gap="middle", wrap=True):
501
+ # ํ—ค๋” ๋ถ€๋ถ„
502
+ header = gr.HTML(f"""
503
+ <div class="left_header">
504
+ <img src="data:image/gif;base64,{get_image_base64('mouse.gif')}" width="360px" />
505
+ <h1 style="font-size: 18px;">MOUSE-Chat: Visual AI Assistant</h1>
506
+ <h1 style="font-size: 10px;">Transform your questions into stunning visual presentations. Every response is crafted with beautiful graphics, charts, and interactive elements.</h1>
507
+ </div>
508
+ """)
509
+
510
+
511
+
512
+ # ์ž…๋ ฅ ์˜์—ญ
513
+ input = antd.InputTextarea(
514
+ size="large",
515
+ allow_clear=True,
516
+ placeholder=random.choice(DEMO_LIST)['description']
517
+ )
518
+
519
+ # ๋ฒ„ํŠผ ๊ทธ๋ฃน
520
+ with antd.Flex(gap="small", justify="space-between"):
521
+ btn = antd.Button("Send", type="primary", size="large")
522
+ boost_btn = antd.Button("Boost", type="default", size="large")
523
+ deploy_btn = antd.Button("Share", type="default", size="large")
524
+ clear_btn = antd.Button("Clear", type="default", size="large")
525
+
526
+ deploy_result = gr.HTML(label="๋ฐฐํฌ ๊ฒฐ๊ณผ")
527
+
528
+ # ์šฐ์ธก ํŒจ๋„
529
+ with antd.Col(span=24, md=16):
530
+ with ms.Div(elem_classes="right_panel"):
531
+ # ์ƒ๋‹จ ๋ฒ„ํŠผ๋“ค
532
+ with antd.Flex(gap="small", elem_classes="setting-buttons"):
533
+ historyBtn = antd.Button("๐Ÿ“œ ํžˆ์Šคํ† ๋ฆฌ", type="default")
534
+
535
+ gr.HTML('<div class="render_header"><span class="header_btn"></span><span class="header_btn"></span><span class="header_btn"></span></div>')
536
+
537
+ # ํƒญ ์ปจํ…์ธ 
538
+ with antd.Tabs(active_key="empty", render_tab_bar="() => null") as state_tab:
539
+ with antd.Tabs.Item(key="empty"):
540
+ empty = antd.Empty(description="empty input", elem_classes="right_content")
541
+ with antd.Tabs.Item(key="loading"):
542
+ loading = antd.Spin(True, tip="Generating visual response...", size="large", elem_classes="right_content")
543
+ with antd.Tabs.Item(key="render"):
544
+ sandbox = gr.HTML(elem_classes="html_content")
545
+
546
+ # ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์—ฐ๊ฒฐ
547
+ historyBtn.click(
548
+ history_render,
549
+ inputs=[history],
550
+ outputs=[history_drawer, history_output]
551
+ )
552
+
553
+ history_drawer.close(
554
+ lambda: gr.update(open=False),
555
+ inputs=[],
556
+ outputs=[history_drawer]
557
+ )
558
+ btn.click(
559
+ demo_instance.generation_code,
560
+ inputs=[input, setting, history],
561
+ outputs=[code_output, history, sandbox, state_tab, code_drawer]
562
+ )
563
+
564
+
565
+ clear_btn.click(
566
+ demo_instance.clear_history,
567
+ inputs=[],
568
+ outputs=[history]
569
+ )
570
+
571
+ boost_btn.click(
572
+ fn=handle_boost,
573
+ inputs=[input],
574
+ outputs=[input, state_tab]
575
+ )
576
+
577
+ deploy_btn.click(
578
+ fn=lambda code: deploy_to_vercel(remove_code_block(code)) if code else "์ฝ”๋“œ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.",
579
+ inputs=[code_output],
580
+ outputs=[deploy_result]
581
+ )
582
+
583
+ return demo
584
+
585
+ # ๋ฉ”์ธ ์‹คํ–‰ ๋ถ€๋ถ„
586
+ if __name__ == "__main__":
587
+ try:
588
+ demo_instance = Demo() # Demo ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
589
+ demo = create_main_interface() # ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
590
+ demo.queue(default_concurrency_limit=20).launch(server_name="0.0.0.0", server_port=7860) # ์„œ๋ฒ„ ์„ค์ • ์ถ”๊ฐ€
591
+ except Exception as e:
592
+ print(f"Initialization error: {e}")
593
+ raise