openfree commited on
Commit
0d65984
ยท
verified ยท
1 Parent(s): 36ec0c3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +22 -567
app.py CHANGED
@@ -1,580 +1,35 @@
1
- import gradio as gr
2
- import replicate
3
  import os
4
- from PIL import Image
5
- import requests
6
- from io import BytesIO
7
- import time
8
- import base64
9
- import json
10
 
11
- # Set up API keys from environment variables
12
- os.environ['REPLICATE_API_TOKEN'] = os.getenv('REPLICATE_API_TOKEN')
13
- FIREWORKS_API_KEY = os.getenv('FIREWORKS_API_KEY', '')
14
- BRAVE_API_KEY = os.getenv('BRAVE_API_KEY', '') # Add Brave API key
15
-
16
- def search_design_trends(product_type):
17
- """
18
- Brave Search API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ตœ์‹  ๋””์ž์ธ ํŠธ๋ Œ๋“œ์™€ ๊ธฐ์ˆ  ๊ฒ€์ƒ‰
19
- """
20
- if not BRAVE_API_KEY:
21
- return "๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์—†์Œ (API ํ‚ค ๋ฏธ์„ค์ •)"
22
-
23
- try:
24
- # ์ œํ’ˆ ๋””์ž์ธ์„ ์œ„ํ•œ ๊ฒ€์ƒ‰ ์ฟผ๋ฆฌ ๊ตฌ์„ฑ
25
- query = f"{product_type} design trends 2024 2025 materials technology innovation"
26
-
27
- url = "https://api.search.brave.com/res/v1/web/search"
28
- headers = {
29
- "Accept": "application/json",
30
- "X-Subscription-Token": BRAVE_API_KEY
31
- }
32
- params = {
33
- "q": query,
34
- "count": 5
35
- }
36
-
37
- response = requests.get(url, headers=headers, params=params, timeout=10)
38
-
39
- if response.status_code == 200:
40
- data = response.json()
41
- results = []
42
-
43
- if 'web' in data and 'results' in data['web']:
44
- for item in data['web']['results'][:3]:
45
- results.append(f"- {item.get('title', '')}: {item.get('description', '')[:150]}")
46
-
47
- if results:
48
- return "\n".join(results)
49
- else:
50
- return "์ œํ•œ๋œ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ"
51
- else:
52
- return "ํ˜„์žฌ ๊ฒ€์ƒ‰ ๋ถˆ๊ฐ€"
53
-
54
- except Exception as e:
55
- print(f"Brave ๊ฒ€์ƒ‰ ์˜ค๋ฅ˜: {str(e)}")
56
- return "๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์ด์šฉ ๋ถˆ๊ฐ€"
57
-
58
- def enhance_prompt_with_team(user_input):
59
- """
60
- ์‹ค์ œ ์›น ๊ฒ€์ƒ‰์„ ํ†ตํ•œ ํŒ€ ํ˜‘์—… ๋ฐฉ์‹์˜ ํ–ฅ์ƒ๋œ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ
61
- """
62
- if not FIREWORKS_API_KEY:
63
- # ๊ธฐ๋ณธ ํ–ฅ์ƒ์œผ๋กœ ํด๋ฐฑ
64
- return create_basic_design_prompt(user_input), "API ํ‚ค๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์Œ"
65
-
66
- try:
67
- # ๋จผ์ € ์—”์ง€๋‹ˆ์–ด๋ฅผ ์œ„ํ•œ ์‹ค์ œ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ํš๋“
68
- search_results = search_design_trends(user_input)
69
-
70
- url = "https://api.fireworks.ai/inference/v1/chat/completions"
71
-
72
- # ๊ฒ€์ƒ‰ ํ†ตํ•ฉ๋œ ํ–ฅ์ƒ๋œ ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ
73
- system_prompt = """๋‹น์‹ ์€ ์„ธ ๊ฐ€์ง€ ์ „๋ฌธ ์—ญํ• ๋กœ ๊ตฌ์„ฑ๋œ ์ œํ’ˆ ๋””์ž์ธ ํŒ€์ž…๋‹ˆ๋‹ค. ๋ชจ๋“  ์‘๋‹ต์€ ํ•œ๊ตญ์–ด๋กœ ์ž‘์„ฑํ•˜์„ธ์š”:
74
-
75
- ## ํŒ€ ์—ญํ• :
76
-
77
- ### 1. ๊ฐ๋… (ํฌ๋ฆฌ์—์ดํ‹ฐ๋ธŒ ๋น„์ „ ๋ฆฌ๋”)
78
- - ์‚ฌ์šฉ์ž ์š”๊ตฌ์‚ฌํ•ญ๊ณผ ์‹œ์žฅ ํฌ์ง€์…”๋‹ ๋ถ„์„
79
- - ๋””์ž์ธ ์ฒ ํ•™๊ณผ ์ œ์•ฝ ์กฐ๊ฑด ์„ค์ •
80
- - ๊ธฐ๋Šฅ์— ๋Œ€ํ•œ ์ตœ์ข… ๊ฒฐ์ •
81
-
82
- ### 2. ์—”์ง€๋‹ˆ์–ด (์—ฐ๊ตฌ ๋ฐ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•œ ๊ธฐ์ˆ  ์ „๋ฌธ๊ฐ€)
83
- - ์ œ๊ณต๋œ ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ๋ฅผ ํ™œ์šฉํ•œ ์˜์‚ฌ๊ฒฐ์ •
84
- - ์ œ์กฐ ๊ฐ€๋Šฅ์„ฑ ํ‰๊ฐ€
85
- - ์žฌ๋ฃŒ์™€ ๊ธฐ์ˆ  ์ถ”์ฒœ
86
- - ํŠธ๋ Œ๋“œ ๊ธฐ๋ฐ˜ ํ˜์‹  ๊ธฐ๋Šฅ ์ œ์•ˆ
87
-
88
- ### 3. ๋””์ž์ด๋„ˆ (๋น„์ฃผ์–ผ ์ „๋ฌธ๊ฐ€)
89
- - ์ƒ์„ธํ•œ ์‹œ๊ฐ์  ์‚ฌ์–‘ ์ž‘์„ฑ
90
- - ์ •ํ™•ํ•œ ์žฌ๋ฃŒ, ๋งˆ๊ฐ, ์ƒ‰์ƒ ์ง€์ •
91
- - ๋ Œ๋”๋ง ์Šคํƒ€์ผ๊ณผ ๋ถ„์œ„๊ธฐ ์ •์˜
92
- - ํ…์ŠคํŠธ ๋ ˆ์ด๋ธ”๊ณผ ์ฃผ์„ ํฌํ•จ ํ•„์ˆ˜
93
-
94
- ## ์ถœ๋ ฅ ํ˜•์‹:
95
-
96
- **๊ฐ๋…์˜ ๋น„์ „:**
97
- [๊ฐ„๋žตํ•œ ๋น„์ „ ์„ค๋ช… - 2-3๋ฌธ์žฅ]
98
-
99
- **์—”์ง€๋‹ˆ์–ด์˜ ๊ธฐ์ˆ  ํ‰๊ฐ€:**
100
- [๊ฒ€์ƒ‰ ๋ฐ์ดํ„ฐ ๊ธฐ๋ฐ˜ 3-4๊ฐœ ๊ธฐ์ˆ  ๊ถŒ์žฅ์‚ฌํ•ญ]
101
-
102
- **๋””์ž์ด๋„ˆ์˜ ์ตœ์ข… ํ”„๋กฌํ”„ํŠธ:**
103
- [ํ…์ŠคํŠธ ๋ ˆ์ด๋ธ”๊ณผ ์ฃผ์„์„ ํฌํ•จํ•œ ์ƒ์„ธํ•œ ์‹œ๊ฐ์  ์„ค๋ช…. ๋ฐ˜๋“œ์‹œ ํฌํ•จ:
104
- - ์ œํ’ˆ๋ช…/๋ชจ๋ธ์„ ํƒ€์ดํ‹€ ํ…์ŠคํŠธ๋กœ
105
- - ํ™”์‚ดํ‘œ ์ฃผ์„๊ณผ ํ•จ๊ป˜ ์ฃผ์š” ๊ธฐ๋Šฅ ์„ค๋ช…
106
- - ์˜ค๋ฒ„๋ ˆ์ด ํ…์ŠคํŠธ๋กœ ๊ธฐ์ˆ  ์‚ฌ์–‘
107
- - ์žฌ๋ฃŒ/๊ตฌ์„ฑ์š”์†Œ ๋ ˆ์ด๋ธ”
108
- ์˜ˆ์‹œ: "์ƒ๋‹จ์— 'MODEL X-500' ํ…์ŠคํŠธ ์˜ค๋ฒ„๋ ˆ์ด, 'ํ‹ฐํƒ€๋Š„ ํ”„๋ ˆ์ž„', 'OLED ๋””์Šคํ”Œ๋ ˆ์ด', '5000mAh ๋ฐฐํ„ฐ๋ฆฌ'๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ํ™”์‚ดํ‘œ์™€ ๊ธฐ๋Šฅ ์„ค๋ช…"]
109
-
110
- **์ตœ์ข… ๋””์ž์ธ ๋ณด๊ณ ์„œ:**
111
- [ํ•œ๊ตญ์–ด๋กœ ์ž‘์„ฑ๋œ ์ œํ’ˆ์˜ ํ•ต์‹ฌ ํŠน์ง•, ํ˜์‹  ํฌ์ธํŠธ, ํƒ€๊ฒŸ ์‹œ์žฅ, ์˜ˆ์ƒ ์ œ์กฐ ๋น„์šฉ ๋“ฑ์„ ํฌํ•จํ•œ ์ข…ํ•ฉ ๋ณด๊ณ ์„œ - 3-4๋ฌธ์žฅ]
112
-
113
- ๊ฐ„๊ฒฐํ•˜์ง€๋งŒ ๊ตฌ์ฒด์ ์œผ๋กœ ์ž‘์„ฑ. ๋ฌผ๋ฆฌ์  ์ œํ’ˆ์—๋งŒ ์ง‘์ค‘ํ•˜๋ฉฐ ํ…์ŠคํŠธ ์ฃผ์„ ํฌํ•จ."""
114
-
115
- # ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ์™€ ํ•จ๊ป˜ ํ˜‘์—… ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ
116
- user_prompt = f"""์ œํ’ˆ ์ปจ์…‰: {user_input}
117
-
118
- ์—”์ง€๋‹ˆ์–ด์˜ ์—ฐ๊ตฌ ๋ฐ์ดํ„ฐ:
119
- {search_results}
120
-
121
- ์œ„ ์ž…๋ ฅ๊ณผ ์—ฐ๊ตฌ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ํŒ€ ํ˜‘์—… ์‹คํ–‰:
122
- 1. ๊ฐ๋…์ด ๋น„์ „ ์ˆ˜๋ฆฝ (๊ฐ„๋žตํžˆ)
123
- 2. ์—”์ง€๋‹ˆ์–ด๊ฐ€ ์œ„ ์—ฐ๊ตฌ ๋ฐ์ดํ„ฐ๋ฅผ ํ™œ์šฉํ•ด ๊ธฐ๋Šฅ ์ถ”์ฒœ
124
- 3. ๋””์ž์ด๋„ˆ๊ฐ€ ํ…์ŠคํŠธ ๋ ˆ์ด๋ธ”๊ณผ ์ฃผ์„์ด ํฌํ•จ๋œ ์ƒ์„ธํ•œ ์‹œ๊ฐ์  ํ”„๋กฌํ”„ํŠธ ์ž‘์„ฑ
125
- 4. ์ตœ์ข… ๋””์ž์ธ ๋ณด๊ณ ์„œ ์ž‘์„ฑ (ํ•œ๊ตญ์–ด)
126
-
127
- ์ œ์กฐ ๊ฐ€๋Šฅํ•˜๊ณ  ํ˜์‹ ์ ์ธ ์ œํ’ˆ ๋””์ž์ธ ์ฐฝ์ถœ์— ์ง‘์ค‘ํ•˜๋ฉฐ, ๋ช…ํ™•ํ•œ ํ…์ŠคํŠธ ์˜ค๋ฒ„๋ ˆ์ด๋กœ ์ฃผ์š” ๊ธฐ๋Šฅ, ์‚ฌ์–‘, ๊ตฌ์„ฑ์š”์†Œ ๋ ˆ์ด๋ธ” ํ‘œ์‹œ."""
128
-
129
- payload = {
130
- "model": "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507", # Original model maintained
131
- "max_tokens": 600,
132
- "top_p": 0.9,
133
- "temperature": 0.7,
134
- "messages": [
135
- {
136
- "role": "system",
137
- "content": system_prompt
138
- },
139
- {
140
- "role": "user",
141
- "content": user_prompt
142
- }
143
- ]
144
- }
145
-
146
- headers = {
147
- "Accept": "application/json",
148
- "Content-Type": "application/json",
149
- "Authorization": f"Bearer {FIREWORKS_API_KEY}"
150
- }
151
-
152
- response = requests.post(url, headers=headers, data=json.dumps(payload), timeout=30)
153
-
154
- if response.status_code == 200:
155
- result = response.json()
156
- enhanced = result.get('choices', [{}])[0].get('message', {}).get('content', '')
157
- if enhanced:
158
- # ๋””์ž์ด๋„ˆ์˜ ์ตœ์ข… ํ”„๋กฌํ”„ํŠธ ์ถ”์ถœ
159
- if "๋””์ž์ด๋„ˆ์˜ ์ตœ์ข… ํ”„๋กฌํ”„ํŠธ:" in enhanced or "DESIGNER'S FINAL PROMPT:" in enhanced:
160
- parts = enhanced.split("๋””์ž์ด๋„ˆ์˜ ์ตœ์ข… ํ”„๋กฌํ”„ํŠธ:" if "๋””์ž์ด๋„ˆ์˜ ์ตœ์ข… ํ”„๋กฌํ”„ํŠธ:" in enhanced else "DESIGNER'S FINAL PROMPT:")
161
- if len(parts) > 1:
162
- # ์ตœ์ข… ํ”„๋กฌํ”„ํŠธ์™€ ์ „์ฒด ํ† ๋ก  ๋ชจ๋‘ ๋ฐ˜ํ™˜
163
- final_prompt = parts[1].split("**")[0].strip()
164
- return final_prompt, enhanced
165
- return enhanced, enhanced
166
-
167
- # API ์‹คํŒจ ์‹œ ํด๋ฐฑ
168
- return create_basic_design_prompt(user_input), "ํŒ€ ํ˜‘์—… ๋ถˆ๊ฐ€ - ๊ธฐ๋ณธ ํ”„๋กฌํ”„ํŠธ ์‚ฌ์šฉ"
169
-
170
- except requests.exceptions.Timeout:
171
- print("Fireworks API ํƒ€์ž„์•„์›ƒ - ํด๋ฐฑ ์‚ฌ์šฉ")
172
- return create_basic_design_prompt(user_input), "API ํƒ€์ž„์•„์›ƒ - ํด๋ฐฑ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ ์‚ฌ์šฉ"
173
- except Exception as e:
174
- print(f"ํŒ€ ํ˜‘์—… ์˜ค๋ฅ˜: {str(e)}")
175
- return create_basic_design_prompt(user_input), f"์˜ค๋ฅ˜: {str(e)[:100]}"
176
-
177
- def create_basic_design_prompt(user_input):
178
- """
179
- ํ…์ŠคํŠธ ์ฃผ์„์ด ํฌํ•จ๋œ ๊ธฐ๋ณธ ๋””์ž์ธ ํ”„๋กฌํ”„ํŠธ ์ƒ์„ฑ์„ ์œ„ํ•œ ํด๋ฐฑ ํ•จ์ˆ˜
180
- """
181
- product_name = user_input.upper().replace(" ", "-")
182
- return f"""์ƒ์„ธํ•œ ํ…์ŠคํŠธ ์ฃผ์„์ด ํฌํ•จ๋œ {user_input}์˜ ์ „๋ฌธ ์ œํ’ˆ ๋””์ž์ธ ์ปจ์…‰:
183
- - ์ƒ๋‹จ์— ๊ตต์€ ๋ชจ๋˜ ํฐํŠธ๋กœ ์ œํ’ˆ ํƒ€์ดํ‹€ ํ…์ŠคํŠธ ์˜ค๋ฒ„๋ ˆ์ด: "{product_name} PRO 2025"
184
- - ์ฃผ์š” ๊ตฌ์„ฑ์š”์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” ํ™”์‚ดํ‘œ์™€ ํ•จ๊ป˜ ๊ธฐ๋Šฅ ์„ค๋ช… ๋ ˆ์ด๋ธ”:
185
- * "ํ”„๋ฆฌ๋ฏธ์—„ ์†Œ์žฌ" โ†’ ๋ณธ์ฒด
186
- * "์ธ์ฒด๊ณตํ•™์  ๋””์ž์ธ" โ†’ ํผ ํŒฉํ„ฐ
187
- * "์Šค๋งˆํŠธ ํ†ตํ•ฉ" โ†’ ์ œ์–ด ํŒจ๋„
188
- * "์ง€์†๊ฐ€๋Šฅํ•œ ์ œ์กฐ" โ†’ ์นœํ™˜๊ฒฝ ์š”์†Œ
189
- - ๋ชจ์„œ๋ฆฌ์— ๊ธฐ์ˆ  ์‚ฌ์–‘ ํ…์ŠคํŠธ ์˜ค๋ฒ„๋ ˆ์ด:
190
- * ์น˜์ˆ˜, ๋ฌด๊ฒŒ, ์ „๋ ฅ ์‚ฌ์–‘
191
- - ์ฃผ์„์ด ํฌํ•จ๋œ ๊น”๋”ํ•œ ์‚ฐ์—… ๋””์ž์ธ
192
- - ์ŠคํŠœ๋””์˜ค ์กฐ๋ช…๊ณผ ํ•จ๊ป˜ ํฌํ† ๋ฆฌ์–ผ๋ฆฌ์Šคํ‹ฑ ๋ Œ๋”๋ง
193
- - ์ธํฌ๊ทธ๋ž˜ํ”ฝ ์š”์†Œ๊ฐ€ ํฌํ•จ๋œ ์ „๋ฌธ ์ œํ’ˆ ์‚ฌ์ง„ ์Šคํƒ€์ผ
194
- - ๊น”๋”ํ•œ ์‚ฐ์„ธ๋ฆฌํ”„ ํฐํŠธ(Helvetica/Arial ์Šคํƒ€์ผ)์˜ ํ…์ŠคํŠธ ๋ ˆ์ด๋ธ”
195
- - ๊ฐ€๋…์„ฑ์„ ์œ„ํ•œ ๋†’์€ ๋Œ€๋น„ ํ…์ŠคํŠธ
196
- - ๋ชจ๋“  ๋ ˆ์ด๋ธ”์ด ๋ช…ํ™•ํžˆ ๋ณด์ด๋Š” 3/4 ๊ด€์ ์—์„œ ํ‘œ์‹œ"""
197
-
198
- def upload_image_to_hosting(image):
199
- """
200
- ํ˜ธ์ŠคํŒ… ์„œ๋น„์Šค์— ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ
201
- """
202
  try:
203
- # imgbb ๋จผ์ € ์‹œ๋„
204
- buffered = BytesIO()
205
- image.save(buffered, format="PNG")
206
- buffered.seek(0)
207
- img_base64 = base64.b64encode(buffered.getvalue()).decode()
208
 
209
- response = requests.post(
210
- "https://api.imgbb.com/1/upload",
211
- data={
212
- 'key': '6d207e02198a847aa98d0a2a901485a5',
213
- 'image': img_base64,
214
- },
215
- timeout=10
216
- )
217
 
218
- if response.status_code == 200:
219
- data = response.json()
220
- if data.get('success'):
221
- return data['data']['url']
222
- except:
223
- pass
224
-
225
- # base64๋กœ ํด๋ฐฑ
226
- buffered = BytesIO()
227
- image.save(buffered, format="PNG")
228
- buffered.seek(0)
229
- img_base64 = base64.b64encode(buffered.getvalue()).decode()
230
- return f"data:image/png;base64,{img_base64}"
231
-
232
- def process_product_design(prompt, enhance_prompt_flag, image1, image2=None):
233
- """
234
- ํŒ€ ํ˜‘์—…์„ ํ†ตํ•œ ์ œํ’ˆ ๋””์ž์ธ ์ฒ˜๋ฆฌ
235
- """
236
- if not os.getenv('REPLICATE_API_TOKEN'):
237
- return None, prompt, "โš ๏ธ REPLICATE_API_TOKEN์„ ์„ค์ •ํ•˜์„ธ์š”", ""
238
-
239
- try:
240
- team_discussion = ""
241
- final_prompt = prompt
242
 
243
- # ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ ํŒ€ ํ˜‘์—… ์ ์šฉ
244
- if enhance_prompt_flag:
245
- result = enhance_prompt_with_team(prompt)
246
- if isinstance(result, tuple):
247
- final_prompt, team_discussion = result
248
- else:
249
- final_prompt = result
250
- team_discussion = "ํŒ€ ํ˜‘์—… ์™„๋ฃŒ"
251
-
252
- # ํ‘œ์‹œ๋ฅผ ์œ„ํ•œ ํŒ€ ํ† ๋ก  ํ˜•์‹ํ™”
253
- if "๊ฐ๋…์˜ ๋น„์ „:" in team_discussion or "DIRECTOR'S VISION:" in team_discussion:
254
- team_discussion = f"### ๐ŸŽฌ ํŒ€ ํ˜‘์—… ํ”„๋กœ์„ธ์Šค\n\n{team_discussion}"
255
-
256
- # ๋ชจ๋ธ ์ž…๋ ฅ ์ค€๋น„
257
- model_input = {
258
- "prompt": final_prompt
259
- }
260
-
261
- # ์ œ๊ณต๋œ ๊ฒฝ์šฐ ์ฐธ์กฐ ์ด๋ฏธ์ง€ ์ถ”๊ฐ€
262
- if image1 or image2:
263
- image_urls = []
264
-
265
- if image1:
266
- url1 = upload_image_to_hosting(image1)
267
- image_urls.append(url1)
268
-
269
- if image2:
270
- url2 = upload_image_to_hosting(image2)
271
- image_urls.append(url2)
272
-
273
- model_input["image_input"] = image_urls
274
- status_msg = "โœ… ์Šคํƒ€์ผ ์ฐธ์กฐ์™€ ํ•จ๊ป˜ ์ œํ’ˆ ๋””์ž์ธ ์ƒ์„ฑ ์™„๋ฃŒ!"
275
- else:
276
- status_msg = "โœ… ์ œํ’ˆ ๋””์ž์ธ์ด ์„ฑ๊ณต์ ์œผ๋กœ ์ƒ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค!"
277
 
278
- # Replicate๋กœ ์ด๋ฏธ์ง€ ์ƒ์„ฑ
279
  try:
280
- # ํ”„๋กฌํ”„ํŠธ์— ํ…์ŠคํŠธ ์ฃผ์„ ๊ฐ•์กฐ ์ถ”๊ฐ€
281
- annotated_prompt = f"{final_prompt}. Include clear text labels, feature callouts with arrows, product name/model as title, technical specifications overlay, component annotations in clean modern typography"
282
-
283
- output = replicate.run(
284
- "black-forest-labs/flux-schnell", # Alternative model that works better
285
- input={
286
- "prompt": annotated_prompt,
287
- "num_outputs": 1,
288
- "aspect_ratio": "1:1",
289
- "output_format": "webp",
290
- "output_quality": 90
291
- }
292
- )
293
  except:
294
- # ํ…์ŠคํŠธ ์ฃผ์„๊ณผ ํ•จ๊ป˜ ์›๋ณธ ๋ชจ๋ธ๋กœ ํด๋ฐฑ
295
- annotated_prompt = f"{final_prompt}. With text overlays showing product name, key features with arrow callouts, specifications, and component labels in professional typography"
296
 
297
- output = replicate.run(
298
- "stability-ai/sdxl:39ed52f2a78e934b3ba6e2a89f5b1c712de7dfea535525255b1aa35c5565e08b",
299
- input={
300
- "prompt": annotated_prompt,
301
- "negative_prompt": "low quality, blurry, distorted, unreadable text, messy labels",
302
- "width": 1024,
303
- "height": 1024,
304
- "num_outputs": 1
305
- }
306
- )
307
-
308
- if output is None:
309
- return None, final_prompt, "โŒ ๋ชจ๋ธ๋กœ๋ถ€ํ„ฐ ์ถœ๋ ฅ์„ ๋ฐ›์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค", team_discussion
310
-
311
- # ์ถœ๋ ฅ ์ฒ˜๋ฆฌ
312
- output_url = None
313
- if isinstance(output, str):
314
- output_url = output
315
- elif isinstance(output, list) and len(output) > 0:
316
- output_url = output[0]
317
- elif hasattr(output, 'url'):
318
- output_url = output.url
319
-
320
- if output_url:
321
- response = requests.get(output_url, timeout=30)
322
- if response.status_code == 200:
323
- img = Image.open(BytesIO(response.content))
324
- return img, final_prompt, status_msg, team_discussion
325
-
326
- return None, final_prompt, "โŒ ์ถœ๋ ฅ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค", team_discussion
327
-
328
  except Exception as e:
329
- error_msg = str(e)
330
- return None, prompt, f"โŒ ์˜ค๋ฅ˜: {error_msg[:200]}", ""
331
-
332
- # ์ „๋ฌธ ์ œํ’ˆ ๋””์ž์ธ CSS
333
- css = """
334
- .gradio-container {
335
- background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
336
- font-family: 'Pretendard', 'Noto Sans KR', -apple-system, BlinkMacSystemFont, sans-serif;
337
- min-height: 100vh;
338
- }
339
- .header-container {
340
- background: linear-gradient(135deg, #2d2d2d 0%, #0f0f0f 100%);
341
- padding: 2rem;
342
- border-radius: 20px;
343
- margin-bottom: 2rem;
344
- box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
345
- border: 1px solid rgba(255, 215, 0, 0.2);
346
- }
347
- .logo-text {
348
- font-size: 2.8rem;
349
- font-weight: 800;
350
- color: #ffd700;
351
- text-align: center;
352
- margin: 0;
353
- letter-spacing: -1px;
354
- text-shadow: 2px 2px 4px rgba(0,0,0,0.8);
355
- }
356
- .subtitle {
357
- color: #b0b0b0;
358
- text-align: center;
359
- font-size: 1rem;
360
- margin-top: 0.5rem;
361
- font-weight: 400;
362
- letter-spacing: 0.5px;
363
- }
364
- .mode-indicator {
365
- background: rgba(255, 215, 0, 0.1);
366
- border: 1px solid rgba(255, 215, 0, 0.3);
367
- border-radius: 10px;
368
- padding: 0.6rem 1.2rem;
369
- margin: 1rem auto;
370
- text-align: center;
371
- font-weight: 500;
372
- color: #ffd700;
373
- max-width: 600px;
374
- font-size: 0.9rem;
375
- }
376
- .main-content {
377
- background: rgba(255, 255, 255, 0.98);
378
- border-radius: 20px;
379
- padding: 2rem;
380
- box-shadow: 0 5px 20px rgba(0, 0, 0, 0.3);
381
- }
382
- .gr-button-primary {
383
- background: linear-gradient(135deg, #2d2d2d 0%, #0f0f0f 100%) !important;
384
- border: 1px solid #ffd700 !important;
385
- color: #ffd700 !important;
386
- font-weight: 600 !important;
387
- font-size: 1rem !important;
388
- padding: 1rem 2rem !important;
389
- border-radius: 10px !important;
390
- transition: all 0.3s ease !important;
391
- text-transform: uppercase;
392
- letter-spacing: 0.5px;
393
- width: 100%;
394
- margin-top: 1rem !important;
395
- }
396
- .gr-button-primary:hover {
397
- transform: translateY(-2px) !important;
398
- box-shadow: 0 8px 25px rgba(255, 215, 0, 0.25) !important;
399
- background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%) !important;
400
- color: #000 !important;
401
- }
402
- .info-box {
403
- background: linear-gradient(135deg, #fff3cd 0%, #ffeaa7 100%);
404
- border-radius: 10px;
405
- padding: 1rem;
406
- margin-bottom: 1.5rem;
407
- border-left: 4px solid #ffd700;
408
- color: #333;
409
- font-size: 0.9rem;
410
- }
411
- .team-discussion {
412
- background: #f8f9fa;
413
- border-radius: 10px;
414
- padding: 1.2rem;
415
- margin-top: 1rem;
416
- border: 1px solid #dee2e6;
417
- font-family: 'Pretendard', 'Noto Sans KR', monospace;
418
- font-size: 0.85rem;
419
- max-height: 400px;
420
- overflow-y: auto;
421
- white-space: pre-wrap;
422
- line-height: 1.6;
423
- }
424
- .enhanced-prompt-box {
425
- background: #f0f0f0;
426
- border-radius: 10px;
427
- padding: 1rem;
428
- margin-top: 0.5rem;
429
- border-left: 3px solid #ffd700;
430
- font-size: 0.9rem;
431
- }
432
- .gr-input, .gr-textarea {
433
- background: #ffffff !important;
434
- border: 1px solid #d0d0d0 !important;
435
- border-radius: 8px !important;
436
- font-size: 0.95rem !important;
437
- padding: 0.7rem !important;
438
- }
439
- .gr-input:focus, .gr-textarea:focus {
440
- border-color: #ffd700 !important;
441
- box-shadow: 0 0 0 3px rgba(255, 215, 0, 0.1) !important;
442
- }
443
- .image-container {
444
- border-radius: 10px !important;
445
- overflow: hidden;
446
- border: 1px solid #e0e0e0 !important;
447
- }
448
- footer {
449
- display: none !important;
450
- }
451
- """
452
-
453
- # Gradio ์ธํ„ฐํŽ˜์ด์Šค ์ƒ์„ฑ
454
- with gr.Blocks(css=css, theme=gr.themes.Base()) as demo:
455
- with gr.Column(elem_classes="header-container"):
456
- gr.HTML("""
457
- <h1 class="logo-text">๐Ÿญ ์ œํ’ˆ ๋””์ž์ธ ์ŠคํŠœ๋””์˜ค</h1>
458
- <p class="subtitle">AI ๊ธฐ๋ฐ˜ ์‚ฐ์—… ๋””์ž์ธ ์‹œ์Šคํ…œ - ์›น ๋ฆฌ์„œ์น˜์™€ ์ฃผ์„ ๊ธฐ๋Šฅ ํฌํ•จ</p>
459
- <div class="mode-indicator">
460
- ๐Ÿ‘ฅ ๊ฐ๋… โ†’ ๐Ÿ” ์—”์ง€๋‹ˆ์–ด (Brave ๊ฒ€์ƒ‰) โ†’ ๐ŸŽจ ๋””์ž์ด๋„ˆ (ํ…์ŠคํŠธ ์ฃผ์„ ํฌํ•จ)
461
- </div>
462
- """)
463
-
464
- with gr.Column(elem_classes="main-content"):
465
- gr.HTML("""
466
- <div class="info-box">
467
- <strong>๐ŸŽฏ ์ „๋ฌธ ์ œํ’ˆ ๋””์ž์ธ ํ”„๋กœ์„ธ์Šค (์ฃผ์„ ํฌํ•จ):</strong><br>
468
- โ€ข <b>๊ฐ๋…:</b> ๋น„์ „๊ณผ ์š”๊ตฌ์‚ฌํ•ญ ์ˆ˜๋ฆฝ<br>
469
- โ€ข <b>์—”์ง€๋‹ˆ์–ด:</b> Brave API๋กœ ์ตœ์‹  ํŠธ๋ Œ๋“œ์™€ ๊ธฐ์ˆ  ๊ฒ€์ƒ‰<br>
470
- โ€ข <b>๋””์ž์ด๋„ˆ:</b> <b>ํ…์ŠคํŠธ ๋ ˆ์ด๋ธ”๊ณผ ๊ธฐ๋Šฅ ์„ค๋ช…</b>์ด ํฌํ•จ๋œ ์‹œ๊ฐ ์‚ฌ์–‘ ์ž‘์„ฑ<br>
471
- โ€ข <b>๊ฒฐ๊ณผ๋ฌผ:</b> ์ฃผ์š” ๊ธฐ๋Šฅ์ด ๊ฐ•์กฐ๋œ ์ฃผ์„ ํฌํ•จ ์ œํ’ˆ ๋””์ž์ธ<br>
472
- โ€ข <b>์ง‘์ค‘ ๋ถ„์•ผ:</b> ์ธํฌ๊ทธ๋ž˜ํ”ฝ ์Šคํƒ€์ผ ์ฃผ์„์ด ํฌํ•จ๋œ ๋ฌผ๋ฆฌ์  ์ œํ’ˆ
473
- </div>
474
- """)
475
-
476
- with gr.Row(equal_height=True):
477
- with gr.Column(scale=1):
478
- prompt = gr.Textbox(
479
- label="๐Ÿ“ ์ œํ’ˆ ์ปจ์…‰",
480
- placeholder="์ œํ’ˆ ์œ ํ˜• ์ž…๋ ฅ: '์Šค๋งˆํŠธ ์šด๋™ํ™”', '๋ฏธ๋ž˜ํ˜• ์ฃผ๋ฐฉ์šฉํ’ˆ', '์นœํ™˜๊ฒฝ ๊ฐ€๋ฐฉ', '๋ชจ๋“ˆํ˜• ๊ฐ€๊ตฌ'...",
481
- lines=2,
482
- value="์ฐจ์„ธ๋Œ€ ๋ฌด์„  ์ด์–ดํฐ",
483
- elem_classes="prompt-input"
484
- )
485
-
486
- enhance_prompt_checkbox = gr.Checkbox(
487
- label="๐Ÿš€ ์›น ๋ฆฌ์„œ์น˜๋ฅผ ํ†ตํ•œ ํŒ€ ํ˜‘์—… ํ™œ์„ฑํ™”",
488
- value=True,
489
- info="๊ฐ๋… + ์—”์ง€๋‹ˆ์–ด(Brave ๊ฒ€์ƒ‰) + ๋””์ž์ด๋„ˆ ํ”„๋กœ์„ธ์Šค ํ™œ์„ฑํ™”"
490
- )
491
-
492
- with gr.Row():
493
- image1 = gr.Image(
494
- label="์Šคํƒ€์ผ ์ฐธ์กฐ 1 (์„ ํƒ์‚ฌํ•ญ)",
495
- type="pil",
496
- height=180
497
- )
498
- image2 = gr.Image(
499
- label="์Šคํƒ€์ผ ์ฐธ์กฐ 2 (์„ ํƒ์‚ฌํ•ญ)",
500
- type="pil",
501
- height=180
502
- )
503
-
504
- generate_btn = gr.Button(
505
- "๐ŸŽจ ์ œํ’ˆ ๋””์ž์ธ ์ƒ์„ฑ",
506
- variant="primary",
507
- size="lg"
508
- )
509
-
510
- with gr.Column(scale=1):
511
- output_image = gr.Image(
512
- label="์ƒ์„ฑ๋œ ์ œํ’ˆ ๋””์ž์ธ",
513
- type="pil",
514
- height=400,
515
- elem_classes="image-container"
516
- )
517
-
518
- enhanced_prompt_display = gr.Textbox(
519
- label="๐Ÿ“‹ ์ตœ์ข… ๋””์ž์ธ ์‚ฌ์–‘",
520
- interactive=False,
521
- lines=3,
522
- elem_classes="enhanced-prompt-box"
523
- )
524
-
525
- status = gr.Textbox(
526
- label="์ƒํƒœ",
527
- interactive=False,
528
- elem_classes="status-text",
529
- value="๋””์ž์ธ ์ค€๋น„ ์ค‘..."
530
- )
531
-
532
- with gr.Row():
533
- team_discussion_display = gr.Markdown(
534
- value="",
535
- elem_classes="team-discussion"
536
- )
537
-
538
- # ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ์—ฐ๊ฒฐ
539
- generate_btn.click(
540
- fn=process_product_design,
541
- inputs=[prompt, enhance_prompt_checkbox, image1, image2],
542
- outputs=[output_image, enhanced_prompt_display, status, team_discussion_display]
543
- )
544
-
545
- # ์ฐฝ์˜์ ์ธ ํ•œ๊ธ€ ์ œํ’ˆ ์˜ˆ์‹œ
546
- gr.Examples(
547
- examples=[
548
- ["๊ณต์ค‘ ๋ถ€์–‘ ์Šคํ”ผ์ปค", True, None, None],
549
- ["๋ณ€ํ˜• ๊ฐ€๋Šฅํ•œ ๋‹ค๋ชฉ์  ๊ฐ€๊ตฌ", True, None, None],
550
- ["์ž๊ฐ€ ์ถฉ์ „ ์Šค๋งˆํŠธ ์‹ ๋ฐœ", True, None, None],
551
- ["ํ™€๋กœ๊ทธ๋žจ ๋””์Šคํ”Œ๋ ˆ์ด ์‹œ๊ณ„", True, None, None],
552
- ["์ ‘์ด์‹ ์ „๊ธฐ ์Šค์ฟ ํ„ฐ ํ—ฌ๋ฉง", True, None, None],
553
- ["AI ์š”๋ฆฌ ๋„์šฐ๋ฏธ ๋กœ๋ด‡", True, None, None],
554
- ["ํˆฌ๋ช… OLED ๋…ธํŠธ๋ถ", True, None, None],
555
- ["์ƒ์ฒด์ธ์‹ ์Šค๋งˆํŠธ ๋„์–ด๋ฝ", True, None, None],
556
- ["๋ชจ๋“ˆํ˜• ์บ ํ•‘ ์žฅ๋น„ ์‹œ์Šคํ…œ", True, None, None],
557
- ["์›จ์–ด๋Ÿฌ๋ธ” ๊ณต๊ธฐ์ฒญ์ •๊ธฐ", True, None, None],
558
- ["์ž๋™ ์˜จ๋„์กฐ์ ˆ ํ…€๋ธ”๋Ÿฌ", True, None, None],
559
- ["๋ฏธ๋ž˜ํ˜• ๋ฐ˜๋ ค๋™๋ฌผ ์ผ€์–ด ์Šคํ…Œ์ด์…˜", True, None, None],
560
- ],
561
- inputs=[prompt, enhance_prompt_checkbox, image1, image2],
562
- label="๐Ÿ’ก ์ฐฝ์˜์ ์ธ ์ œํ’ˆ ๋””์ž์ธ ์˜ˆ์‹œ"
563
- )
564
 
565
- # ์‹คํ–‰ ์„ค์ •
566
  if __name__ == "__main__":
567
- # ํ•„์š”ํ•œ API ํ‚ค ํ™•์ธ
568
- if not os.getenv('REPLICATE_API_TOKEN'):
569
- print("โš ๏ธ ๊ฒฝ๊ณ : REPLICATE_API_TOKEN์ด ์„ค์ •๋˜์ง€ ์•Š์Œ")
570
- if not os.getenv('FIREWORKS_API_KEY'):
571
- print("โš ๏ธ ๊ฒฝ๊ณ : FIREWORKS_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์Œ - ํด๋ฐฑ ํ”„๋กฌํ”„ํŠธ ์‚ฌ์šฉ")
572
- if not os.getenv('BRAVE_API_KEY'):
573
- print("โš ๏ธ ๊ฒฝ๊ณ : BRAVE_API_KEY๊ฐ€ ์„ค์ •๋˜์ง€ ์•Š์Œ - ์›น ๊ฒ€์ƒ‰ ๋ถˆ๊ฐ€")
574
-
575
- demo.launch(
576
- share=False, # Hugging Face Spaces์šฉ์œผ๋กœ False ์„ค์ •
577
- server_name="0.0.0.0",
578
- server_port=7860,
579
- ssr_mode=False # ๊ฒฝ๊ณ  ๋ฐฉ์ง€๋ฅผ ์œ„ํ•ด SSR ๋น„ํ™œ์„ฑํ™”
580
- )
 
 
 
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()