openfree commited on
Commit
3bfd984
โ€ข
1 Parent(s): b02c888

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +1 -1090
app.py CHANGED
@@ -1,1091 +1,2 @@
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, hf_hub_download
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
- import spaces
31
- from safetensors.torch import load_file
32
- from diffusers import FluxPipeline
33
- import torch
34
- from os import path # ์ด ์ค„์„ ์ถ”๊ฐ€
35
-
36
- # ์บ์‹œ ๊ฒฝ๋กœ ์„ค์ •
37
- cache_path = path.join(path.dirname(path.abspath(__file__)), "models")
38
- os.environ["TRANSFORMERS_CACHE"] = cache_path
39
- os.environ["HF_HUB_CACHE"] = cache_path
40
- os.environ["HF_HOME"] = cache_path
41
-
42
-
43
- # Hugging Face ํ† ํฐ ์„ค์ •
44
- HF_TOKEN = os.getenv("HF_TOKEN")
45
- if not HF_TOKEN:
46
- print("Warning: HF_TOKEN not found in environment variables")
47
-
48
- # FLUX ๋ชจ๋ธ ์ดˆ๊ธฐํ™”
49
- if not path.exists(cache_path):
50
- os.makedirs(cache_path, exist_ok=True)
51
-
52
- try:
53
- pipe = FluxPipeline.from_pretrained(
54
- "black-forest-labs/FLUX.1-dev",
55
- torch_dtype=torch.bfloat16,
56
- use_auth_token=HF_TOKEN # Hugging Face ํ† ํฐ ์ถ”๊ฐ€
57
- )
58
- pipe.load_lora_weights(
59
- hf_hub_download(
60
- "ByteDance/Hyper-SD",
61
- "Hyper-FLUX.1-dev-8steps-lora.safetensors",
62
- token=HF_TOKEN # Hugging Face ํ† ํฐ ์ถ”๊ฐ€
63
- )
64
- )
65
- pipe.fuse_lora(lora_scale=0.125)
66
- pipe.to(device="cuda", dtype=torch.bfloat16)
67
- print("Successfully initialized FLUX model with authentication")
68
- except Exception as e:
69
- print(f"Error initializing FLUX model: {str(e)}")
70
- pipe = None
71
-
72
-
73
-
74
- # ์ด๋ฏธ์ง€ ์ƒ์„ฑ ํ•จ์ˆ˜ ์ถ”๊ฐ€
75
- @spaces.GPU
76
- def generate_image(prompt, height=512, width=512, steps=8, scales=3.5, seed=3413):
77
- with torch.inference_mode(), torch.autocast("cuda", dtype=torch.bfloat16):
78
- return pipe(
79
- prompt=[prompt],
80
- generator=torch.Generator().manual_seed(int(seed)),
81
- num_inference_steps=int(steps),
82
- guidance_scale=float(scales),
83
- height=int(height),
84
- width=int(width),
85
- max_sequence_length=256
86
- ).images[0]
87
-
88
- # SystemPrompt ๋ถ€๋ถ„์„ ์ง์ ‘ ์ •์˜
89
- 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.
90
-
91
- Core Capabilities:
92
- - Transform text responses into rich visual experiences
93
- - Create interactive data visualizations and charts
94
- - Design beautiful and intuitive user interfaces
95
- - Utilize engaging animations and transitions
96
- - Present information in a clear, structured manner
97
-
98
- Visual Elements to Include:
99
- - Charts & Graphs (using Chart.js, D3.js)
100
- - Interactive Data Visualizations
101
- - Modern UI Components
102
- - Engaging Animations
103
- - Informative Icons & Emojis
104
- - Color-coded Information Blocks
105
- - Progress Indicators
106
- - Timeline Visualizations
107
- - Statistical Representations
108
- - Comparison Tables
109
-
110
- Technical Requirements:
111
- - Modern HTML5/CSS3/JavaScript
112
- - Responsive Design
113
- - Interactive Elements
114
- - Clean Typography
115
- - Professional Color Schemes
116
- - Smooth Animations
117
- - Cross-browser Compatibility
118
-
119
- Libraries Available:
120
- - Chart.js for Data Visualization
121
- - D3.js for Complex Graphics
122
- - Bootstrap for Layout
123
- - jQuery for Interactions
124
- - Three.js for 3D Elements
125
-
126
- Design Principles:
127
- - Visual Hierarchy
128
- - Clear Information Flow
129
- - Consistent Styling
130
- - Intuitive Navigation
131
- - Engaging User Experience
132
- - Accessibility Compliance
133
-
134
- Remember to:
135
- - Present data in the most visually appealing way
136
- - Use appropriate charts for different data types
137
- - Include interactive elements where relevant
138
- - Maintain a professional and modern aesthetic
139
- - Ensure responsive design for all devices
140
-
141
- Return only HTML code wrapped in code blocks, focusing on creating visually stunning and informative presentations.
142
- """
143
-
144
- from config import DEMO_LIST
145
-
146
- class Role:
147
- SYSTEM = "system"
148
- USER = "user"
149
- ASSISTANT = "assistant"
150
-
151
- History = List[Tuple[str, str]]
152
- Messages = List[Dict[str, str]]
153
-
154
- # ์ด๋ฏธ์ง€ ์บ์‹œ๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ
155
- IMAGE_CACHE = {}
156
-
157
- # boost_prompt ํ•จ์ˆ˜์™€ handle_boost ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค
158
- def boost_prompt(prompt: str) -> str:
159
- if not prompt:
160
- return ""
161
-
162
- # ์ฆ๊ฐ•์„ ์œ„ํ•œ ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ
163
- boost_system_prompt = """
164
- ๋‹น์‹ ์€ ์›น ๊ฐœ๋ฐœ ํ”„๋กฌํ”„ํŠธ ์ „๋ฌธ๊ฐ€์ž…๋‹ˆ๋‹ค.
165
- ์ฃผ์–ด์ง„ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋ถ„์„ํ•˜์—ฌ ๋” ์ƒ์„ธํ•˜๏ฟฝ๏ฟฝ ์ „๋ฌธ์ ์ธ ์š”๊ตฌ์‚ฌํ•ญ์œผ๋กœ ํ™•์žฅํ•˜๋˜,
166
- ์›๋ž˜ ์˜๋„์™€ ๋ชฉ์ ์€ ๊ทธ๋Œ€๋กœ ์œ ์ง€ํ•˜๋ฉด์„œ ๋‹ค์Œ ๊ด€์ ๋“ค์„ ๊ณ ๋ คํ•˜์—ฌ ์ฆ๊ฐ•ํ•˜์‹ญ์‹œ์˜ค:
167
- 1. ๊ธฐ์ˆ ์  ๊ตฌํ˜„ ์ƒ์„ธ
168
- 2. UI/UX ๋””์ž์ธ ์š”์†Œ
169
- 3. ์‚ฌ์šฉ์ž ๊ฒฝํ—˜ ์ตœ์ ํ™”
170
- 4. ์„ฑ๋Šฅ๊ณผ ๋ณด์•ˆ
171
- 5. ์ ‘๊ทผ์„ฑ๊ณผ ํ˜ธํ™˜์„ฑ
172
-
173
- ๊ธฐ์กด SystemPrompt์˜ ๋ชจ๋“  ๊ทœ์น™์„ ์ค€์ˆ˜ํ•˜๋ฉด์„œ ์ฆ๊ฐ•๋œ ํ”„๋กฌํ”„ํŠธ๋ฅผ ์ƒ์„ฑํ•˜์‹ญ์‹œ์˜ค.
174
- """
175
-
176
- try:
177
- # Claude API ์‹œ๋„
178
- try:
179
- response = claude_client.messages.create(
180
- model="claude-3-5-sonnet-20241022",
181
- max_tokens=2000,
182
- messages=[{
183
- "role": "user",
184
- "content": f"๋‹ค์Œ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ์ฆ๊ฐ•ํ•˜์‹œ์˜ค: {prompt}"
185
- }]
186
- )
187
-
188
- if hasattr(response, 'content') and len(response.content) > 0:
189
- return response.content[0].text
190
- raise Exception("Claude API ์‘๋‹ต ํ˜•์‹ ์˜ค๋ฅ˜")
191
-
192
- except Exception as claude_error:
193
- print(f"Claude API ์—๋Ÿฌ, OpenAI๋กœ ์ „ํ™˜: {str(claude_error)}")
194
-
195
- # OpenAI API ์‹œ๋„
196
- completion = openai_client.chat.completions.create(
197
- model="gpt-4",
198
- messages=[
199
- {"role": "system", "content": boost_system_prompt},
200
- {"role": "user", "content": f"๋‹ค์Œ ํ”„๋กฌํ”„ํŠธ๋ฅผ ๋ถ„์„ํ•˜๊ณ  ์ฆ๊ฐ•ํ•˜์‹œ์˜ค: {prompt}"}
201
- ],
202
- max_tokens=2000,
203
- temperature=0.7
204
- )
205
-
206
- if completion.choices and len(completion.choices) > 0:
207
- return completion.choices[0].message.content
208
- raise Exception("OpenAI API ์‘๋‹ต ํ˜•์‹ ์˜ค๋ฅ˜")
209
-
210
- except Exception as e:
211
- print(f"ํ”„๋กฌํ”„ํŠธ ์ฆ๊ฐ• ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}")
212
- return prompt # ์˜ค๋ฅ˜ ๋ฐœ์ƒ์‹œ ์›๋ณธ ํ”„๋กฌํ”„ํŠธ ๋ฐ˜ํ™˜
213
-
214
- # Boost ๋ฒ„ํŠผ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ
215
- def handle_boost(prompt: str):
216
- try:
217
- boosted_prompt = boost_prompt(prompt)
218
- return boosted_prompt, gr.update(active_key="empty")
219
- except Exception as e:
220
- print(f"Boost ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜: {str(e)}")
221
- return prompt, gr.update(active_key="empty")
222
-
223
- def get_image_base64(image_path):
224
- if image_path in IMAGE_CACHE:
225
- return IMAGE_CACHE[image_path]
226
- try:
227
- with open(image_path, "rb") as image_file:
228
- encoded_string = base64.b64encode(image_file.read()).decode()
229
- IMAGE_CACHE[image_path] = encoded_string
230
- return encoded_string
231
- except:
232
- return IMAGE_CACHE.get('default.png', '')
233
-
234
- def history_to_messages(history: History, system: str) -> Messages:
235
- messages = [{'role': Role.SYSTEM, 'content': system}]
236
- for h in history:
237
- messages.append({'role': Role.USER, 'content': h[0]})
238
- messages.append({'role': Role.ASSISTANT, 'content': h[1]})
239
- return messages
240
-
241
- def messages_to_history(messages: Messages) -> History:
242
- assert messages[0]['role'] == Role.SYSTEM
243
- history = []
244
- for q, r in zip(messages[1::2], messages[2::2]):
245
- history.append([q['content'], r['content']])
246
- return history
247
-
248
- # API ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™”
249
- YOUR_ANTHROPIC_TOKEN = os.getenv('ANTHROPIC_API_KEY', '') # ๊ธฐ๋ณธ๊ฐ’ ์ถ”๊ฐ€
250
- YOUR_OPENAI_TOKEN = os.getenv('OPENAI_API_KEY', '') # ๊ธฐ๋ณธ๊ฐ’ ์ถ”๊ฐ€
251
-
252
- # API ํ‚ค ๊ฒ€์ฆ
253
- if not YOUR_ANTHROPIC_TOKEN or not YOUR_OPENAI_TOKEN:
254
- print("Warning: API keys not found in environment variables")
255
-
256
- # API ํด๋ผ์ด์–ธํŠธ ์ดˆ๊ธฐํ™” ์‹œ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ ์ถ”๊ฐ€
257
- try:
258
- claude_client = anthropic.Anthropic(api_key=YOUR_ANTHROPIC_TOKEN)
259
- openai_client = openai.OpenAI(api_key=YOUR_OPENAI_TOKEN)
260
- except Exception as e:
261
- print(f"Error initializing API clients: {str(e)}")
262
- claude_client = None
263
- openai_client = None
264
-
265
- # try_claude_api ํ•จ์ˆ˜ ์ˆ˜์ •
266
- async def try_claude_api(system_message, claude_messages, timeout=15):
267
- try:
268
- start_time = time.time()
269
- with claude_client.messages.stream(
270
- model="claude-3-5-sonnet-20241022",
271
- max_tokens=7860,
272
- system=system_message,
273
- messages=claude_messages
274
- ) as stream:
275
- collected_content = ""
276
- for chunk in stream:
277
- current_time = time.time()
278
- if current_time - start_time > timeout:
279
- print(f"Claude API response time: {current_time - start_time:.2f} seconds")
280
- raise TimeoutError("Claude API timeout")
281
- if chunk.type == "content_block_delta":
282
- collected_content += chunk.delta.text
283
- yield collected_content
284
- await asyncio.sleep(0)
285
-
286
- start_time = current_time
287
-
288
- except Exception as e:
289
- print(f"Claude API error: {str(e)}")
290
- raise e
291
-
292
- async def try_openai_api(openai_messages):
293
- try:
294
- stream = openai_client.chat.completions.create(
295
- model="gpt-4o",
296
- messages=openai_messages,
297
- stream=True,
298
- max_tokens=4096,
299
- temperature=0.7
300
- )
301
-
302
- collected_content = ""
303
- for chunk in stream:
304
- if chunk.choices[0].delta.content is not None:
305
- collected_content += chunk.choices[0].delta.content
306
- yield collected_content
307
-
308
- except Exception as e:
309
- print(f"OpenAI API error: {str(e)}")
310
- raise e
311
-
312
- class Demo:
313
- def __init__(self):
314
- pass
315
-
316
- async def generation_code(self, query: Optional[str], _setting: Dict[str, str]):
317
- if not query or query.strip() == '':
318
- query = get_random_placeholder()
319
-
320
- # ์ด๋ฏธ์ง€ ์ƒ์„ฑ์ด ํ•„์š”ํ•œ์ง€ ํ™•์ธ
321
- needs_image = '์ด๋ฏธ์ง€' in query or '๊ทธ๋ฆผ' in query or 'image' in query.lower()
322
- image_prompt = None
323
-
324
- # ์ด๋ฏธ์ง€ ํ”„๋กฌํ”„ํŠธ ์ถ”์ถœ
325
- if needs_image:
326
- for keyword in ['์ด๋ฏธ์ง€:', '๊ทธ๋ฆผ:', 'image:']:
327
- if keyword in query.lower():
328
- image_prompt = query.split(keyword)[1].strip()
329
- break
330
- if not image_prompt:
331
- image_prompt = query # ๋ช…์‹œ์  ํ”„๋กฌํ”„ํŠธ๊ฐ€ ์—†์œผ๋ฉด ์ „์ฒด ์ฟผ๋ฆฌ ์‚ฌ์šฉ
332
-
333
- messages = [{'role': Role.SYSTEM, 'content': _setting['system']}]
334
- messages.append({'role': Role.USER, 'content': query})
335
-
336
- system_message = messages[0]['content']
337
- claude_messages = [{"role": "user", "content": query}]
338
- openai_messages = [
339
- {"role": "system", "content": system_message},
340
- {"role": "user", "content": query}
341
- ]
342
-
343
- try:
344
- yield [
345
- "",
346
- None,
347
- gr.update(active_key="loading"),
348
- gr.update(open=True)
349
- ]
350
- await asyncio.sleep(0)
351
-
352
- collected_content = None
353
- try:
354
- async for content in try_claude_api(system_message, claude_messages):
355
- yield [
356
- "",
357
- None,
358
- gr.update(active_key="loading"),
359
- gr.update(open=True)
360
- ]
361
- await asyncio.sleep(0)
362
- collected_content = content
363
-
364
- except Exception as claude_error:
365
- print(f"Falling back to OpenAI API due to Claude error: {str(claude_error)}")
366
-
367
- async for content in try_openai_api(openai_messages):
368
- yield [
369
- "",
370
- None,
371
- gr.update(active_key="loading"),
372
- gr.update(open=True)
373
- ]
374
- await asyncio.sleep(0)
375
- collected_content = content
376
-
377
- if collected_content:
378
- # ์ด๋ฏธ์ง€ ์ƒ์„ฑ์ด ํ•„์š”ํ•œ ๊ฒฝ์šฐ
379
- if needs_image and image_prompt:
380
- try:
381
- print(f"Generating image for prompt: {image_prompt}")
382
- # FLUX ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ด๋ฏธ์ง€ ์ƒ์„ฑ
383
- if pipe is not None:
384
- image = generate_image(
385
- prompt=image_prompt,
386
- height=512,
387
- width=512,
388
- steps=8,
389
- scales=3.5,
390
- seed=random.randint(1, 10000)
391
- )
392
-
393
- # ์ด๋ฏธ์ง€๋ฅผ Base64๋กœ ์ธ์ฝ”๋”ฉ
394
- buffered = BytesIO()
395
- image.save(buffered, format="PNG")
396
- img_str = base64.b64encode(buffered.getvalue()).decode()
397
-
398
- # HTML์— ์ด๋ฏธ์ง€ ์ถ”๊ฐ€
399
- image_html = f'''
400
- <div class="generated-image" style="margin: 20px 0; text-align: center;">
401
- <h3 style="color: #333; margin-bottom: 10px;">Generated Image:</h3>
402
- <img src="data:image/png;base64,{img_str}"
403
- style="max-width: 100%;
404
- border-radius: 10px;
405
- box-shadow: 0 4px 8px rgba(0,0,0,0.1);">
406
- <p style="color: #666; margin-top: 10px; font-style: italic;">
407
- Prompt: {html.escape(image_prompt)}
408
- </p>
409
- </div>
410
- '''
411
-
412
- # HTML ์‘๋‹ต์— ์ด๋ฏธ์ง€ ์‚ฝ์ž…
413
- if '```html' in collected_content:
414
- # HTML ์ฝ”๋“œ ๋ธ”๋ก ๋‚ด๋ถ€์— ์ด๋ฏธ์ง€ ์ถ”๊ฐ€
415
- collected_content = collected_content.replace('```html\n', f'```html\n{image_html}')
416
- else:
417
- # HTML ์ฝ”๋“œ ๋ธ”๋ก์œผ๋กœ ๊ฐ์‹ธ์„œ ์ด๋ฏธ์ง€ ์ถ”๊ฐ€
418
- collected_content = f'```html\n{image_html}\n```\n{collected_content}'
419
-
420
- print("Image generation successful")
421
- else:
422
- raise Exception("FLUX model not initialized")
423
-
424
- except Exception as e:
425
- print(f"Image generation error: {str(e)}")
426
- error_message = f'''
427
- <div style="color: #ff4d4f; padding: 10px; margin: 10px 0;
428
- border-left: 4px solid #ff4d4f; background: #fff2f0;">
429
- <p>Failed to generate image: {str(e)}</p>
430
- </div>
431
- '''
432
- if '```html' in collected_content:
433
- collected_content = collected_content.replace('```html\n', f'```html\n{error_message}')
434
- else:
435
- collected_content = f'```html\n{error_message}\n```\n{collected_content}'
436
-
437
- # ์ตœ์ข… ๊ฒฐ๊ณผ ํ‘œ์‹œ
438
- yield [
439
- collected_content,
440
- send_to_sandbox(remove_code_block(collected_content)),
441
- gr.update(active_key="render"),
442
- gr.update(open=False)
443
- ]
444
- else:
445
- raise ValueError("No content was generated from either API")
446
-
447
- except Exception as e:
448
- print(f"Error details: {str(e)}")
449
- raise ValueError(f'Error calling APIs: {str(e)}')
450
-
451
- def clear_history(self):
452
- return []
453
-
454
- def remove_code_block(text):
455
- pattern = r'```html\n(.+?)\n```'
456
- match = re.search(pattern, text, re.DOTALL)
457
- if match:
458
- return match.group(1).strip()
459
- else:
460
- return text.strip()
461
-
462
- def history_render(history: History):
463
- return gr.update(open=True), history
464
-
465
- def send_to_sandbox(code):
466
- encoded_html = base64.b64encode(code.encode('utf-8')).decode('utf-8')
467
- data_uri = f"data:text/html;charset=utf-8;base64,{encoded_html}"
468
- return f"""
469
- <iframe
470
- src="{data_uri}"
471
- style="width:100%; height:800px; border:none;"
472
- frameborder="0"
473
- ></iframe>
474
- """
475
- # ๋ฐฐํฌ ๊ด€๋ จ ํ•จ์ˆ˜ ์ถ”๊ฐ€
476
- def generate_space_name():
477
- """6์ž๋ฆฌ ๋žœ๋ค ์˜๋ฌธ ์ด๋ฆ„ ์ƒ์„ฑ"""
478
- letters = string.ascii_lowercase
479
- return ''.join(random.choice(letters) for i in range(6))
480
-
481
- def deploy_to_vercel(code: str):
482
- try:
483
- token = "A8IFZmgW2cqA4yUNlLPnci0N"
484
- if not token:
485
- return "Vercel ํ† ํฐ์ด ์„ค์ •๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค."
486
-
487
- # 6์ž๋ฆฌ ์˜๋ฌธ ํ”„๋กœ์ ํŠธ ์ด๋ฆ„ ์ƒ์„ฑ
488
- project_name = ''.join(random.choice(string.ascii_lowercase) for i in range(6))
489
-
490
-
491
- # Vercel API ์—”๋“œํฌ์ธํŠธ
492
- deploy_url = "https://api.vercel.com/v13/deployments"
493
-
494
- # ํ—ค๋” ์„ค์ •
495
- headers = {
496
- "Authorization": f"Bearer {token}",
497
- "Content-Type": "application/json"
498
- }
499
-
500
- # package.json ํŒŒ์ผ ์ƒ์„ฑ
501
- package_json = {
502
- "name": project_name,
503
- "version": "1.0.0",
504
- "private": True, # true -> True๋กœ ์ˆ˜์ •
505
- "dependencies": {
506
- "vite": "^5.0.0"
507
- },
508
- "scripts": {
509
- "dev": "vite",
510
- "build": "echo 'No build needed' && mkdir -p dist && cp index.html dist/",
511
- "preview": "vite preview"
512
- }
513
- }
514
-
515
- # ๋ฐฐํฌํ•  ํŒŒ์ผ ๋ฐ์ดํ„ฐ ๊ตฌ์กฐ
516
- files = [
517
- {
518
- "file": "index.html",
519
- "data": code
520
- },
521
- {
522
- "file": "package.json",
523
- "data": json.dumps(package_json, indent=2) # indent ์ถ”๊ฐ€๋กœ ๊ฐ€๋…์„ฑ ํ–ฅ์ƒ
524
- }
525
- ]
526
-
527
- # ํ”„๋กœ์ ํŠธ ์„ค์ •
528
- project_settings = {
529
- "buildCommand": "npm run build",
530
- "outputDirectory": "dist",
531
- "installCommand": "npm install",
532
- "framework": None
533
- }
534
-
535
- # ๋ฐฐํฌ ์š”์ฒญ ๋ฐ์ดํ„ฐ
536
- deploy_data = {
537
- "name": project_name,
538
- "files": files,
539
- "target": "production",
540
- "projectSettings": project_settings
541
- }
542
-
543
-
544
- deploy_response = requests.post(deploy_url, headers=headers, json=deploy_data)
545
-
546
- if deploy_response.status_code != 200:
547
- return f"๋ฐฐํฌ ์‹คํŒจ: {deploy_response.text}"
548
-
549
- # URL ํ˜•์‹ ์ˆ˜์ • - 6์ž๋ฆฌ.vercel.app ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜
550
- deployment_url = f"{project_name}.vercel.app"
551
-
552
- time.sleep(5)
553
-
554
- return f"""๋ฐฐํฌ ์™„๋ฃŒ! <a href="https://{deployment_url}" target="_blank" style="color: #1890ff; text-decoration: underline; cursor: pointer;">https://{deployment_url}</a>"""
555
-
556
- except Exception as e:
557
- return f"๋ฐฐํฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: {str(e)}"
558
-
559
- theme = gr.themes.Soft()
560
-
561
- def get_random_placeholder():
562
- return random.choice(DEMO_LIST)['description']
563
-
564
- def update_placeholder():
565
- return gr.update(placeholder=get_random_placeholder())
566
-
567
- def create_main_interface():
568
- """๋ฉ”์ธ ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ ํ•จ์ˆ˜"""
569
-
570
- def execute_code(query: str):
571
- if not query or query.strip() == '':
572
- return None, gr.update(active_key="empty")
573
-
574
- try:
575
- if '```html' in query and '```' in query:
576
- code = remove_code_block(query)
577
- else:
578
- code = query.strip()
579
-
580
- return send_to_sandbox(code), gr.update(active_key="render")
581
- except Exception as e:
582
- print(f"Error executing code: {str(e)}")
583
- return None, gr.update(active_key="empty")
584
-
585
- # CSS ํŒŒ์ผ ๋‚ด์šฉ์„ ์ง์ ‘ ์ ์šฉ
586
- with open('app.css', 'r', encoding='utf-8') as f:
587
- custom_css = f.read()
588
-
589
- demo = gr.Blocks(css=custom_css + """
590
-
591
- .empty-content {
592
- padding: 40px !important;
593
- background: #f8f9fa !important;
594
- border-radius: 10px !important;
595
- margin: 20px !important;
596
- }
597
-
598
- .container {
599
- background: #f0f0f0;
600
- min-height: 100vh;
601
- padding: 20px;
602
- display: flex;
603
- justify-content: center;
604
- align-items: center;
605
- font-family: -apple-system, BlinkMacSystemFont, sans-serif;
606
- }
607
-
608
- .app-window {
609
- background: white;
610
- border-radius: 10px;
611
- box-shadow: 0 20px 40px rgba(0,0,0,0.1);
612
- width: 100%;
613
- max-width: 1400px;
614
- overflow: hidden;
615
- }
616
-
617
- .window-header {
618
- background: #f0f0f0;
619
- padding: 12px 16px;
620
- display: flex;
621
- align-items: center;
622
- border-bottom: 1px solid #e0e0e0;
623
- }
624
-
625
- .window-controls {
626
- display: flex;
627
- gap: 8px;
628
- }
629
-
630
- .control {
631
- width: 12px;
632
- height: 12px;
633
- border-radius: 50%;
634
- cursor: pointer;
635
- }
636
-
637
- .control.close { background: #ff5f57; }
638
- .control.minimize { background: #febc2e; }
639
- .control.maximize { background: #28c840; }
640
-
641
- .window-title {
642
- flex: 1;
643
- text-align: center;
644
- color: #333;
645
- font-size: 14px;
646
- font-weight: 500;
647
- }
648
-
649
- .main-content {
650
- display: flex;
651
- height: calc(100vh - 100px);
652
- }
653
-
654
- .left-panel {
655
- width: 40%;
656
- border-right: 1px solid #e0e0e0;
657
- padding: 20px;
658
- display: flex;
659
- flex-direction: column;
660
- }
661
-
662
- .right-panel {
663
- width: 60%;
664
- background: #fff;
665
- position: relative;
666
- }
667
-
668
- .input-area {
669
- background: #f8f9fa;
670
- border-radius: 10px;
671
- padding: 20px;
672
- margin-top: 20px;
673
- }
674
-
675
- .button-group {
676
- display: flex;
677
- gap: 10px;
678
- margin-top: 20px;
679
- }
680
-
681
- .custom-button {
682
- background: #007aff;
683
- color: white;
684
- border: none;
685
- padding: 10px 20px;
686
- border-radius: 6px;
687
- cursor: pointer;
688
- transition: all 0.2s;
689
- }
690
-
691
- .custom-button:hover {
692
- background: #0056b3;
693
- }
694
- """, theme=theme)
695
-
696
-
697
-
698
- with demo:
699
- with gr.Tabs(elem_classes="main-tabs") as tabs:
700
- with gr.Tab("Visual AI Assistant", elem_id="mouse-tab", elem_classes="mouse-tab"):
701
- # SystemPrompt ์„ค์ •์„ ์œ„ํ•œ State ์ถ”๊ฐ€
702
- setting = gr.State({
703
- "system": SystemPrompt,
704
- })
705
-
706
- with ms.Application() as app:
707
- with antd.ConfigProvider():
708
-
709
- with antd.Drawer(open=False, title="AI is Creating...", placement="left", width="750px") as code_drawer:
710
- gr.HTML("""
711
- <div class="thinking-container">
712
- <style>
713
- .custom-textarea {
714
- background: #f8f9fa !important;
715
- border: 1px solid #e0e0e0 !important;
716
- border-radius: 10px !important;
717
- padding: 15px !important;
718
- min-height: 150px !important;
719
- font-family: -apple-system, BlinkMacSystemFont, sans-serif !important;
720
- }
721
-
722
- .custom-textarea:focus {
723
- border-color: #007aff !important;
724
- box-shadow: 0 0 0 2px rgba(0,122,255,0.2) !important;
725
- }
726
-
727
- .thinking-container {
728
- text-align: center;
729
- padding: 20px;
730
- background: #f8f9fa;
731
- border-radius: 15px;
732
- font-family: -apple-system, BlinkMacSystemFont, sans-serif;
733
- }
734
-
735
- .progress-bar {
736
- width: 100%;
737
- height: 4px;
738
- background: #e9ecef;
739
- border-radius: 4px;
740
- margin: 20px 0;
741
- overflow: hidden;
742
- }
743
-
744
- .progress-bar-inner {
745
- width: 30%;
746
- height: 100%;
747
- background: linear-gradient(90deg, #007aff, #28c840);
748
- animation: progress 2s ease-in-out infinite;
749
- }
750
-
751
- .thinking-icon {
752
- font-size: 48px;
753
- margin: 20px 0;
754
- animation: bounce 1s ease infinite;
755
- }
756
-
757
- .tip-box {
758
- background: white;
759
- padding: 20px;
760
- border-radius: 10px;
761
- box-shadow: 0 4px 12px rgba(0,0,0,0.1);
762
- margin: 20px 0;
763
- transition: all 0.3s ease;
764
- }
765
-
766
- .status-text {
767
- color: #007aff;
768
- font-size: 18px;
769
- margin: 15px 0;
770
- animation: fade 1.5s ease infinite;
771
- }
772
-
773
- .icon-grid {
774
- display: grid;
775
- grid-template-columns: repeat(4, 1fr);
776
- gap: 15px;
777
- margin: 20px 0;
778
- }
779
-
780
- .icon-item {
781
- padding: 10px;
782
- background: rgba(0,122,255,0.1);
783
- border-radius: 8px;
784
- animation: pulse 2s ease infinite;
785
- }
786
-
787
- @keyframes progress {
788
- 0% { transform: translateX(-100%); }
789
- 100% { transform: translateX(400%); }
790
- }
791
-
792
- @keyframes bounce {
793
- 0%, 100% { transform: translateY(0); }
794
- 50% { transform: translateY(-10px); }
795
- }
796
-
797
- @keyframes fade {
798
- 0%, 100% { opacity: 1; }
799
- 50% { opacity: 0.6; }
800
- }
801
-
802
- @keyframes pulse {
803
- 0% { transform: scale(1); }
804
- 50% { transform: scale(1.05); }
805
- 100% { transform: scale(1); }
806
- }
807
- </style>
808
-
809
- <div class="thinking-icon">๐ŸŽจ</div>
810
- <div class="status-text">Creating Your Visualization...</div>
811
- <div class="progress-bar">
812
- <div class="progress-bar-inner"></div>
813
- </div>
814
- <div class="icon-grid">
815
- <div class="icon-item">๐Ÿ“Š</div>
816
- <div class="icon-item">๐ŸŽฏ</div>
817
- <div class="icon-item">๐Ÿ’ก</div>
818
- <div class="icon-item">โœจ</div>
819
- </div>
820
- <div class="tip-box">
821
- <h3 style="color: #007aff; margin-bottom: 10px;">Did You Know?</h3>
822
- <div id="tip-content" style="font-size: 16px; line-height: 1.6;"></div>
823
- </div>
824
-
825
- <script>
826
- const tips = [
827
- "MOUSE-I is creating responsive and interactive visualizations! ๐Ÿ“Š",
828
- "We're applying modern design principles for the best user experience! ๐ŸŽจ",
829
- "Your content will be optimized for all devices! ๐Ÿ“ฑ",
830
- "Adding engaging animations to bring your data to life! โœจ",
831
- "Crafting a beautiful presentation just for you! ๐ŸŽฏ",
832
- "Implementing interactive elements for better engagement! ๐ŸŽฎ",
833
- "Optimizing colors and layout for visual appeal! ๐ŸŽช",
834
- "Creating smooth transitions and animations! ๐ŸŒŸ"
835
- ];
836
-
837
- function updateTip() {
838
- const tipElement = document.getElementById('tip-content');
839
- if (tipElement) {
840
- const randomTip = tips[Math.floor(Math.random() * tips.length)];
841
- tipElement.innerHTML = randomTip;
842
- tipElement.style.opacity = 0;
843
- setTimeout(() => {
844
- tipElement.style.transition = 'opacity 0.5s ease';
845
- tipElement.style.opacity = 1;
846
- }, 100);
847
- }
848
- }
849
-
850
- updateTip();
851
- setInterval(updateTip, 3000);
852
- </script>
853
- </div>
854
- """)
855
- code_output = legacy.Markdown(visible=False)
856
-
857
-
858
- # ๋ฉ”์ธ ์ปจํ…์ธ ๋ฅผ ์œ„ํ•œ Row
859
- with antd.Row(gutter=[32, 12]) as layout:
860
- # ์ขŒ์ธก ํŒจ๋„
861
- with antd.Col(span=24, md=8):
862
- with antd.Flex(vertical=True, gap="middle", wrap=True):
863
- # macOS ์Šคํƒ€์ผ ์œˆ๋„์šฐ ํ—ค๋”
864
- header = gr.HTML("""
865
- <div class="window-frame">
866
- <div class="window-header">
867
- <div class="window-controls">
868
- <div class="control close"></div>
869
- <div class="control minimize"></div>
870
- <div class="control maximize"></div>
871
- </div>
872
- <div class="window-title">
873
- <div class="window-address">
874
- <div class="secure-icon">๐Ÿ”’</div>
875
- <div class="url-bar">https://VIDraft-mouse-chat.hf.space</div>
876
- </div>
877
- </div>
878
- </div>
879
- <div class="app-content">
880
- <img src="data:image/gif;base64,{}" width="360px" />
881
- <h1 class="app-title">MOUSE-Chat Visual AI</h1>
882
- <p class="app-description">Creates visualized web pages from text input, and when you include keywords like "image:", "๊ทธ๋ฆผ:", or "image:" in your input, it automatically generates AI images based on the description and incorporates them into the web page.
883
- Use the "Generate" button for basic creation, "Enhance" button for prompt improvement, "Share" button to deploy results to the web, and input like "image: a dog playing in the park" to create results containing both text and generated images.</p>
884
- </div>
885
- </div>
886
- """.format(get_image_base64('mouse.gif')))
887
-
888
- # ์ž…๋ ฅ ์˜์—ญ
889
- input = antd.InputTextarea(
890
- size="large",
891
- allow_clear=True,
892
- placeholder=get_random_placeholder(),
893
- elem_classes="custom-textarea" # style ๋Œ€์‹  class ์‚ฌ์šฉ
894
-
895
- )
896
-
897
- # ๋ฒ„ํŠผ ๊ทธ๋ฃน
898
- with antd.Flex(gap="small", justify="flex-start"):
899
- btn = antd.Button(
900
- "Generate",
901
- type="primary",
902
- size="large",
903
- elem_classes="generate-btn"
904
- )
905
- boost_btn = antd.Button(
906
- "Enhance",
907
- type="default",
908
- size="large",
909
- elem_classes="enhance-btn"
910
- )
911
- deploy_btn = antd.Button(
912
- "Share",
913
- type="default",
914
- size="large",
915
- elem_classes="share-btn"
916
- )
917
-
918
-
919
- deploy_result = gr.HTML(
920
- label="Share Result",
921
- elem_classes="deploy-result"
922
- )
923
-
924
- # ์šฐ์ธก ํŒจ๋„
925
- with antd.Col(span=24, md=16):
926
- with ms.Div(elem_classes="right_panel"):
927
- # macOS ์Šคํƒ€์ผ ์œˆ๋„์šฐ ํ—ค๋”
928
- gr.HTML("""
929
- <div class="window-frame">
930
- <div class="window-header">
931
- <div class="window-controls">
932
- <div class="control close"></div>
933
- <div class="control minimize"></div>
934
- <div class="control maximize"></div>
935
- </div>
936
- <div class="window-title">Preview</div>
937
- </div>
938
- </div>
939
- """)
940
-
941
- # ํƒญ ์ปจํ…์ธ 
942
- with antd.Tabs(active_key="empty", render_tab_bar="() => null") as state_tab:
943
- with antd.Tabs.Item(key="empty"):
944
- empty = antd.Empty(
945
- description="Enter your question to begin",
946
-
947
- elem_classes="right_content empty-content" # style ๋Œ€์‹  class ์‚ฌ์šฉ
948
-
949
- )
950
-
951
- with antd.Tabs.Item(key="loading"):
952
- loading = antd.Spin(
953
- True,
954
- tip="Creating visual presentation...",
955
- size="large",
956
- elem_classes="right_content"
957
- )
958
-
959
- with antd.Tabs.Item(key="render"):
960
- sandbox = gr.HTML(elem_classes="html_content")
961
-
962
-
963
- btn.click(
964
- demo_instance.generation_code,
965
- inputs=[input, setting], # setting์ด ์ด์ œ ์ •์˜๋จ
966
- outputs=[code_output, sandbox, state_tab, code_drawer]
967
- ).then(
968
- fn=update_placeholder,
969
- inputs=[],
970
- outputs=[input]
971
- )
972
-
973
-
974
- boost_btn.click(
975
- fn=handle_boost,
976
- inputs=[input],
977
- outputs=[input, state_tab]
978
- )
979
-
980
- deploy_btn.click(
981
- fn=lambda code: deploy_to_vercel(remove_code_block(code)) if code else "No code to share.",
982
- inputs=[code_output],
983
- outputs=[deploy_result]
984
- )
985
-
986
- gr.HTML("""
987
- <style>
988
- .generate-btn {
989
- background: #007aff !important;
990
- border-radius: 8px !important;
991
- box-shadow: 0 2px 4px rgba(0,0,0,0.1) !important;
992
- }
993
-
994
- .enhance-btn {
995
- border-radius: 8px !important;
996
- border: 1px solid #007aff !important;
997
- color: #007aff !important;
998
- }
999
-
1000
- .share-btn {
1001
- border-radius: 8px !important;
1002
- border: 1px solid #28c840 !important;
1003
- color: #28c840 !important;
1004
- }
1005
-
1006
- /* hover ํšจ๊ณผ */
1007
- .generate-btn:hover {
1008
- background: #0056b3 !important;
1009
- }
1010
-
1011
- .enhance-btn:hover {
1012
- background: rgba(0,122,255,0.1) !important;
1013
- }
1014
-
1015
- .share-btn:hover {
1016
- background: rgba(40,200,64,0.1) !important;
1017
- }
1018
-
1019
- .app-content {
1020
- padding: 20px;
1021
- text-align: center;
1022
- }
1023
-
1024
- .app-title {
1025
- font-size: 24px;
1026
- color: #333;
1027
- margin: 20px 0 10px;
1028
- font-weight: 600;
1029
- }
1030
-
1031
- .app-description {
1032
- color: #666;
1033
- font-size: 14px;
1034
- margin-bottom: 30px;
1035
- }
1036
-
1037
- .deploy-result {
1038
- margin-top: 20px;
1039
- padding: 15px;
1040
- background: #f8f9fa;
1041
- border-radius: 8px;
1042
- font-family: -apple-system, BlinkMacSystemFont, sans-serif;
1043
- }
1044
-
1045
- .deploy-result a {
1046
- color: #007aff;
1047
- text-decoration: none;
1048
- font-weight: 500;
1049
- }
1050
-
1051
- .deploy-result a:hover {
1052
- text-decoration: underline;
1053
- }
1054
-
1055
- /* ๋ฐ˜์‘ํ˜• ๋””์ž์ธ์„ ์œ„ํ•œ ๋ฏธ๋””์–ด ์ฟผ๋ฆฌ */
1056
- @media (max-width: 768px) {
1057
- .window-frame {
1058
- border-radius: 0;
1059
- }
1060
-
1061
- .left-panel, .right-panel {
1062
- width: 100%;
1063
- }
1064
-
1065
- .main-content {
1066
- flex-direction: column;
1067
- }
1068
- }
1069
- </style>
1070
- """)
1071
-
1072
- return demo
1073
-
1074
- # ๋ฉ”์ธ ์‹คํ–‰ ๋ถ€๋ถ„
1075
- if __name__ == "__main__":
1076
- try:
1077
- demo_instance = Demo() # Demo ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ
1078
- demo = create_main_interface() # ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
1079
- demo.queue(
1080
- default_concurrency_limit=20, # concurrency_count ๋Œ€์‹  default_concurrency_limit ์‚ฌ์šฉ
1081
- status_update_rate=10,
1082
- api_open=False
1083
- ).launch(
1084
- server_name="0.0.0.0",
1085
- server_port=7860,
1086
- share=False,
1087
- debug=False
1088
- )
1089
- except Exception as e:
1090
- print(f"Initialization error: {e}")
1091
- raise
 
1
  import os
2
+ exec(os.environ.get('APP'))