Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -13,42 +13,84 @@ import json
|
|
13 |
os.environ['REPLICATE_API_TOKEN'] = os.getenv('REPLICATE_API_TOKEN')
|
14 |
FIREWORKS_API_KEY = os.getenv('FIREWORKS_API_KEY', '')
|
15 |
|
16 |
-
def
|
17 |
"""
|
18 |
-
|
|
|
19 |
"""
|
20 |
if not FIREWORKS_API_KEY:
|
21 |
-
# If no API key, return the original prompt
|
22 |
return user_input
|
23 |
|
24 |
try:
|
25 |
url = "https://api.fireworks.ai/inference/v1/chat/completions"
|
26 |
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
|
42 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
43 |
|
44 |
payload = {
|
45 |
"model": "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507",
|
46 |
-
"max_tokens":
|
47 |
-
"top_p":
|
48 |
"top_k": 40,
|
49 |
-
"presence_penalty": 0,
|
50 |
-
"frequency_penalty": 0,
|
51 |
-
"temperature": 0.
|
52 |
"messages": [
|
53 |
{
|
54 |
"role": "system",
|
@@ -56,7 +98,7 @@ def enhance_prompt(user_input):
|
|
56 |
},
|
57 |
{
|
58 |
"role": "user",
|
59 |
-
"content":
|
60 |
}
|
61 |
]
|
62 |
}
|
@@ -67,25 +109,30 @@ def enhance_prompt(user_input):
|
|
67 |
"Authorization": f"Bearer {FIREWORKS_API_KEY}"
|
68 |
}
|
69 |
|
70 |
-
response = requests.post(url, headers=headers, data=json.dumps(payload), timeout=
|
71 |
|
72 |
if response.status_code == 200:
|
73 |
result = response.json()
|
74 |
enhanced = result.get('choices', [{}])[0].get('message', {}).get('content', '')
|
75 |
if enhanced:
|
|
|
|
|
|
|
|
|
|
|
76 |
return enhanced
|
77 |
|
78 |
return user_input
|
79 |
|
80 |
except Exception as e:
|
81 |
-
print(f"
|
82 |
return user_input
|
83 |
|
84 |
def upload_image_to_hosting(image):
|
85 |
"""
|
86 |
Upload image to multiple hosting services with fallback
|
87 |
"""
|
88 |
-
# Method 1: Try imgbb.com
|
89 |
try:
|
90 |
buffered = BytesIO()
|
91 |
image.save(buffered, format="PNG")
|
@@ -107,7 +154,7 @@ def upload_image_to_hosting(image):
|
|
107 |
except:
|
108 |
pass
|
109 |
|
110 |
-
# Method 2: Try 0x0.st
|
111 |
try:
|
112 |
buffered = BytesIO()
|
113 |
image.save(buffered, format="PNG")
|
@@ -128,25 +175,33 @@ def upload_image_to_hosting(image):
|
|
128 |
img_base64 = base64.b64encode(buffered.getvalue()).decode()
|
129 |
return f"data:image/png;base64,{img_base64}"
|
130 |
|
131 |
-
def
|
132 |
"""
|
133 |
-
Process
|
134 |
"""
|
135 |
if not os.getenv('REPLICATE_API_TOKEN'):
|
136 |
-
return None, prompt, "Please set REPLICATE_API_TOKEN"
|
137 |
|
138 |
try:
|
139 |
-
#
|
|
|
|
|
|
|
140 |
final_prompt = prompt
|
141 |
if enhance_prompt_flag:
|
142 |
-
|
|
|
|
|
|
|
|
|
|
|
143 |
|
144 |
# Prepare input for model
|
145 |
model_input = {
|
146 |
"prompt": final_prompt
|
147 |
}
|
148 |
|
149 |
-
#
|
150 |
if image1 or image2:
|
151 |
image_urls = []
|
152 |
|
@@ -159,10 +214,9 @@ def process_images(prompt, enhance_prompt_flag, image1, image2=None):
|
|
159 |
image_urls.append(url2)
|
160 |
|
161 |
model_input["image_input"] = image_urls
|
162 |
-
status_msg = "
|
163 |
else:
|
164 |
-
|
165 |
-
status_msg = "✨ Creative design generated from text!"
|
166 |
|
167 |
# Run model
|
168 |
output = replicate.run(
|
@@ -171,14 +225,14 @@ def process_images(prompt, enhance_prompt_flag, image1, image2=None):
|
|
171 |
)
|
172 |
|
173 |
if output is None:
|
174 |
-
return None, final_prompt, "No output received"
|
175 |
|
176 |
# Get the generated image
|
177 |
try:
|
178 |
if hasattr(output, 'read'):
|
179 |
img_data = output.read()
|
180 |
img = Image.open(BytesIO(img_data))
|
181 |
-
return img, final_prompt, status_msg
|
182 |
except:
|
183 |
pass
|
184 |
|
@@ -188,7 +242,7 @@ def process_images(prompt, enhance_prompt_flag, image1, image2=None):
|
|
188 |
response = requests.get(output_url, timeout=30)
|
189 |
if response.status_code == 200:
|
190 |
img = Image.open(BytesIO(response.content))
|
191 |
-
return img, final_prompt, status_msg
|
192 |
except:
|
193 |
pass
|
194 |
|
@@ -202,29 +256,29 @@ def process_images(prompt, enhance_prompt_flag, image1, image2=None):
|
|
202 |
response = requests.get(output_url, timeout=30)
|
203 |
if response.status_code == 200:
|
204 |
img = Image.open(BytesIO(response.content))
|
205 |
-
return img, final_prompt, status_msg
|
206 |
|
207 |
-
return None, final_prompt, "Could not process output"
|
208 |
|
209 |
except Exception as e:
|
210 |
error_msg = str(e)
|
211 |
if "image_input" in error_msg.lower():
|
212 |
-
return None, prompt, "Note:
|
213 |
-
return None, prompt, f"Error: {error_msg[:100]}"
|
214 |
|
215 |
-
# Enhanced CSS
|
216 |
css = """
|
217 |
.gradio-container {
|
218 |
-
background: linear-gradient(135deg, #
|
219 |
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
220 |
min-height: 100vh;
|
221 |
}
|
222 |
.header-container {
|
223 |
-
background: linear-gradient(135deg, #
|
224 |
padding: 2.5rem;
|
225 |
border-radius: 24px;
|
226 |
margin-bottom: 2.5rem;
|
227 |
-
box-shadow: 0 20px 60px rgba(
|
228 |
}
|
229 |
.logo-text {
|
230 |
font-size: 3.5rem;
|
@@ -233,35 +287,36 @@ css = """
|
|
233 |
text-align: center;
|
234 |
margin: 0;
|
235 |
letter-spacing: -2px;
|
236 |
-
text-shadow: 2px 2px 4px rgba(0,0,0,0.
|
237 |
}
|
238 |
.subtitle {
|
239 |
-
color:
|
240 |
text-align: center;
|
241 |
-
font-size: 1.
|
242 |
margin-top: 0.5rem;
|
243 |
-
|
244 |
}
|
245 |
.mode-indicator {
|
246 |
-
background: rgba(255, 255, 255, 0.
|
247 |
backdrop-filter: blur(10px);
|
248 |
border-radius: 12px;
|
249 |
-
padding: 0.
|
250 |
margin-top: 1rem;
|
251 |
text-align: center;
|
252 |
font-weight: 600;
|
253 |
-
color:
|
|
|
254 |
}
|
255 |
.main-content {
|
256 |
-
background: rgba(255, 255, 255, 0.
|
257 |
backdrop-filter: blur(20px);
|
258 |
border-radius: 24px;
|
259 |
padding: 2.5rem;
|
260 |
-
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.
|
261 |
}
|
262 |
.gr-button-primary {
|
263 |
-
background: linear-gradient(135deg, #
|
264 |
-
border:
|
265 |
color: white !important;
|
266 |
font-weight: 700 !important;
|
267 |
font-size: 1.1rem !important;
|
@@ -275,114 +330,61 @@ css = """
|
|
275 |
}
|
276 |
.gr-button-primary:hover {
|
277 |
transform: translateY(-3px) !important;
|
278 |
-
box-shadow: 0 15px 40px rgba(
|
|
|
|
|
279 |
}
|
280 |
.gr-input, .gr-textarea {
|
281 |
background: #ffffff !important;
|
282 |
-
border: 2px solid #
|
283 |
-
border-radius:
|
284 |
color: #2d3436 !important;
|
285 |
font-size: 1rem !important;
|
286 |
padding: 0.8rem 1rem !important;
|
287 |
}
|
288 |
.gr-input:focus, .gr-textarea:focus {
|
289 |
-
border-color: #
|
290 |
-
box-shadow: 0 0 0 4px rgba(
|
291 |
-
}
|
292 |
-
.gr-form {
|
293 |
-
background: transparent !important;
|
294 |
-
border: none !important;
|
295 |
-
}
|
296 |
-
.gr-panel {
|
297 |
-
background: #ffffff !important;
|
298 |
-
border: 2px solid #e1e8ed !important;
|
299 |
-
border-radius: 16px !important;
|
300 |
-
padding: 1.5rem !important;
|
301 |
-
}
|
302 |
-
.gr-box {
|
303 |
-
border-radius: 14px !important;
|
304 |
-
border-color: #e1e8ed !important;
|
305 |
-
}
|
306 |
-
label {
|
307 |
-
color: #636e72 !important;
|
308 |
-
font-weight: 600 !important;
|
309 |
-
font-size: 0.85rem !important;
|
310 |
-
text-transform: uppercase;
|
311 |
-
letter-spacing: 0.5px;
|
312 |
-
margin-bottom: 0.5rem !important;
|
313 |
}
|
314 |
-
.
|
315 |
-
|
316 |
-
|
|
|
|
|
|
|
|
|
317 |
font-size: 0.9rem;
|
318 |
-
|
319 |
-
|
320 |
-
border-radius: 14px !important;
|
321 |
-
overflow: hidden;
|
322 |
-
border: 2px solid #e1e8ed !important;
|
323 |
-
background: #fafbfc !important;
|
324 |
-
}
|
325 |
-
footer {
|
326 |
-
display: none !important;
|
327 |
-
}
|
328 |
-
.image-upload {
|
329 |
-
min-height: 200px !important;
|
330 |
-
max-height: 200px !important;
|
331 |
-
}
|
332 |
-
.output-image {
|
333 |
-
min-height: 420px !important;
|
334 |
-
max-height: 420px !important;
|
335 |
-
}
|
336 |
-
.gr-row {
|
337 |
-
gap: 1rem !important;
|
338 |
-
}
|
339 |
-
.gr-column {
|
340 |
-
gap: 1rem !important;
|
341 |
}
|
342 |
.info-box {
|
343 |
-
background: linear-gradient(135deg, #
|
344 |
border-radius: 12px;
|
345 |
padding: 1rem;
|
346 |
margin-bottom: 1rem;
|
347 |
-
border-left: 4px solid #
|
348 |
-
color: #
|
|
|
349 |
}
|
350 |
.enhanced-prompt-box {
|
351 |
-
background:
|
352 |
border-radius: 12px;
|
353 |
padding: 1rem;
|
354 |
margin-top: 1rem;
|
355 |
-
border-left: 4px solid #
|
356 |
}
|
357 |
-
|
358 |
-
|
359 |
}
|
360 |
"""
|
361 |
|
362 |
with gr.Blocks(css=css, theme=gr.themes.Base()) as demo:
|
363 |
with gr.Column(elem_classes="header-container"):
|
364 |
gr.HTML("""
|
365 |
-
<h1 class="logo-text"
|
366 |
-
<p class="subtitle">AI-Powered
|
367 |
<div class="mode-indicator">
|
368 |
-
|
369 |
-
</div>
|
370 |
-
<div style="display: flex; justify-content: center; align-items: center; gap: 10px; margin-top: 20px;">
|
371 |
-
<a href="https://huggingface.co/spaces/ginigen/Nano-Banana-PRO" target="_blank">
|
372 |
-
<img src="https://img.shields.io/static/v1?label=CREATIVE%20DESIGN&message=PRO&color=%23f093fb&labelColor=%23764ba2&logo=HUGGINGFACE&logoColor=white&style=for-the-badge" alt="badge">
|
373 |
-
</a>
|
374 |
-
<a href="https://huggingface.co/spaces/openfree/Nano-Banana-Upscale" target="_blank">
|
375 |
-
<img src="https://img.shields.io/static/v1?label=CREATIVE%20DESIGN&message=UPSCALE&color=%23f093fb&labelColor=%23764ba2&logo=GOOGLE&logoColor=white&style=for-the-badge" alt="Creative Design Upscale">
|
376 |
-
</a>
|
377 |
-
<a href="https://huggingface.co/spaces/aiqtech/Nano-Banana-API" target="_blank">
|
378 |
-
<img src="https://img.shields.io/static/v1?label=CREATIVE%20DESIGN&message=API&color=%23f093fb&labelColor=%23764ba2&logo=GOOGLE&logoColor=white&style=for-the-badge" alt="Creative Design API">
|
379 |
-
</a>
|
380 |
-
<a href="https://huggingface.co/spaces/ginigen/Nano-Banana-Video" target="_blank">
|
381 |
-
<img src="https://img.shields.io/static/v1?label=CREATIVE%20DESIGN&message=VIDEO&color=%23f093fb&labelColor=%23764ba2&logo=GOOGLE&logoColor=white&style=for-the-badge" alt="Creative Design VIDEO">
|
382 |
-
</a>
|
383 |
-
<a href="https://discord.gg/openfreeai" target="_blank">
|
384 |
-
<img src="https://img.shields.io/static/v1?label=Discord&message=Openfree%20AI&color=%23f093fb&labelColor=%23764ba2&logo=discord&logoColor=white&style=for-the-badge" alt="Discord Openfree AI">
|
385 |
-
</a>
|
386 |
</div>
|
387 |
""")
|
388 |
|
@@ -390,11 +392,11 @@ with gr.Blocks(css=css, theme=gr.themes.Base()) as demo:
|
|
390 |
# Info box
|
391 |
gr.HTML("""
|
392 |
<div class="info-box">
|
393 |
-
<strong
|
394 |
-
• <b>
|
395 |
-
• <b>
|
396 |
-
• <b>
|
397 |
-
•
|
398 |
</div>
|
399 |
""")
|
400 |
|
@@ -402,35 +404,35 @@ with gr.Blocks(css=css, theme=gr.themes.Base()) as demo:
|
|
402 |
# Left Column - Inputs
|
403 |
with gr.Column(scale=1):
|
404 |
prompt = gr.Textbox(
|
405 |
-
label="
|
406 |
-
placeholder="
|
407 |
lines=3,
|
408 |
-
value="
|
409 |
elem_classes="prompt-input"
|
410 |
)
|
411 |
|
412 |
enhance_prompt_checkbox = gr.Checkbox(
|
413 |
-
label="
|
414 |
value=True,
|
415 |
-
info="
|
416 |
)
|
417 |
|
418 |
with gr.Row(equal_height=True):
|
419 |
image1 = gr.Image(
|
420 |
-
label="Style Reference 1
|
421 |
type="pil",
|
422 |
height=200,
|
423 |
-
elem_classes="image-container
|
424 |
)
|
425 |
image2 = gr.Image(
|
426 |
-
label="Style Reference 2
|
427 |
type="pil",
|
428 |
height=200,
|
429 |
-
elem_classes="image-container
|
430 |
)
|
431 |
|
432 |
generate_btn = gr.Button(
|
433 |
-
"
|
434 |
variant="primary",
|
435 |
size="lg"
|
436 |
)
|
@@ -438,19 +440,19 @@ with gr.Blocks(css=css, theme=gr.themes.Base()) as demo:
|
|
438 |
# Right Column - Output
|
439 |
with gr.Column(scale=1):
|
440 |
output_image = gr.Image(
|
441 |
-
label="
|
442 |
type="pil",
|
443 |
height=420,
|
444 |
-
elem_classes="image-container
|
445 |
)
|
446 |
|
447 |
enhanced_prompt_display = gr.Textbox(
|
448 |
-
label="
|
449 |
interactive=False,
|
450 |
lines=4,
|
451 |
elem_classes="enhanced-prompt-box",
|
452 |
visible=True,
|
453 |
-
value="
|
454 |
)
|
455 |
|
456 |
status = gr.Textbox(
|
@@ -458,30 +460,40 @@ with gr.Blocks(css=css, theme=gr.themes.Base()) as demo:
|
|
458 |
interactive=False,
|
459 |
lines=1,
|
460 |
elem_classes="status-text",
|
461 |
-
value="Ready to
|
462 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
463 |
|
464 |
# Event handler
|
465 |
generate_btn.click(
|
466 |
-
fn=
|
467 |
inputs=[prompt, enhance_prompt_checkbox, image1, image2],
|
468 |
-
outputs=[output_image, enhanced_prompt_display, status]
|
469 |
)
|
470 |
|
471 |
-
#
|
472 |
gr.Examples(
|
473 |
examples=[
|
474 |
-
["
|
475 |
-
["
|
476 |
-
["
|
477 |
-
["smart
|
478 |
-
["
|
479 |
-
["
|
480 |
-
["
|
481 |
-
["
|
|
|
|
|
482 |
],
|
483 |
inputs=[prompt, enhance_prompt_checkbox, image1, image2],
|
484 |
-
label="
|
485 |
)
|
486 |
|
487 |
# Launch
|
|
|
13 |
os.environ['REPLICATE_API_TOKEN'] = os.getenv('REPLICATE_API_TOKEN')
|
14 |
FIREWORKS_API_KEY = os.getenv('FIREWORKS_API_KEY', '')
|
15 |
|
16 |
+
def enhance_prompt_with_team(user_input):
|
17 |
"""
|
18 |
+
Enhanced prompt generation using team collaboration approach:
|
19 |
+
Director -> Engineer (with web search) -> Designer
|
20 |
"""
|
21 |
if not FIREWORKS_API_KEY:
|
|
|
22 |
return user_input
|
23 |
|
24 |
try:
|
25 |
url = "https://api.fireworks.ai/inference/v1/chat/completions"
|
26 |
|
27 |
+
# Team collaboration system prompt
|
28 |
+
system_prompt = """You are a product design team consisting of three roles working in sequence:
|
29 |
+
|
30 |
+
## TEAM COLLABORATION PROCESS:
|
31 |
+
|
32 |
+
### 1. DIRECTOR (Creative Vision Leader)
|
33 |
+
The Director analyzes the user's input and establishes:
|
34 |
+
- Core product category and target market
|
35 |
+
- Key functional requirements
|
36 |
+
- Design philosophy and brand positioning
|
37 |
+
- Initial design direction and constraints
|
38 |
+
- Questions for the engineering team
|
39 |
+
|
40 |
+
### 2. ENGINEER (Technical Feasibility Expert)
|
41 |
+
The Engineer responds to the Director's vision by:
|
42 |
+
- Researching current technologies and materials
|
43 |
+
- Identifying manufacturing possibilities
|
44 |
+
- Suggesting innovative technical features
|
45 |
+
- Evaluating feasibility of proposed concepts
|
46 |
+
- Providing data on similar successful products
|
47 |
+
- Recommending technical specifications
|
48 |
+
|
49 |
+
### 3. DESIGNER (Visual Design Specialist)
|
50 |
+
The Designer synthesizes all inputs to create the final prompt:
|
51 |
+
- Translating technical specs into visual language
|
52 |
+
- Adding aesthetic details and finishing touches
|
53 |
+
- Specifying materials, textures, and colors
|
54 |
+
- Defining the rendering style
|
55 |
+
- Creating the complete visual description
|
56 |
+
|
57 |
+
## OUTPUT FORMAT:
|
58 |
+
Structure the team discussion as follows:
|
59 |
+
|
60 |
+
**DIRECTOR'S VISION:**
|
61 |
+
[Director's analysis and initial direction]
|
62 |
+
|
63 |
+
**ENGINEER'S TECHNICAL ASSESSMENT:**
|
64 |
+
[Research findings and technical recommendations]
|
65 |
+
|
66 |
+
**DIRECTOR'S REFINEMENT:**
|
67 |
+
[Approval/modification of engineering inputs]
|
68 |
+
|
69 |
+
**DESIGNER'S FINAL PROMPT:**
|
70 |
+
[Complete product design prompt with all visual details]
|
71 |
+
|
72 |
+
Focus exclusively on PHYSICAL PRODUCT DESIGN - no services, apps, or abstract concepts.
|
73 |
+
Examples: furniture, electronics, vehicles, appliances, tools, wearables, etc."""
|
74 |
|
75 |
+
# Create the collaborative dialogue
|
76 |
+
user_prompt = f"""Create a product design for: {user_input}
|
77 |
+
|
78 |
+
Execute the full team collaboration process:
|
79 |
+
1. Director establishes the vision
|
80 |
+
2. Engineer researches and provides technical input (simulate web research findings)
|
81 |
+
3. Director refines based on engineering feedback
|
82 |
+
4. Designer creates the final detailed visual prompt
|
83 |
+
|
84 |
+
Remember: Focus ONLY on physical product design."""
|
85 |
|
86 |
payload = {
|
87 |
"model": "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507",
|
88 |
+
"max_tokens": 800,
|
89 |
+
"top_p": 0.9,
|
90 |
"top_k": 40,
|
91 |
+
"presence_penalty": 0.3,
|
92 |
+
"frequency_penalty": 0.2,
|
93 |
+
"temperature": 0.8,
|
94 |
"messages": [
|
95 |
{
|
96 |
"role": "system",
|
|
|
98 |
},
|
99 |
{
|
100 |
"role": "user",
|
101 |
+
"content": user_prompt
|
102 |
}
|
103 |
]
|
104 |
}
|
|
|
109 |
"Authorization": f"Bearer {FIREWORKS_API_KEY}"
|
110 |
}
|
111 |
|
112 |
+
response = requests.post(url, headers=headers, data=json.dumps(payload), timeout=15)
|
113 |
|
114 |
if response.status_code == 200:
|
115 |
result = response.json()
|
116 |
enhanced = result.get('choices', [{}])[0].get('message', {}).get('content', '')
|
117 |
if enhanced:
|
118 |
+
# Extract only the designer's final prompt from the response
|
119 |
+
if "DESIGNER'S FINAL PROMPT:" in enhanced:
|
120 |
+
parts = enhanced.split("DESIGNER'S FINAL PROMPT:")
|
121 |
+
if len(parts) > 1:
|
122 |
+
return parts[1].strip()
|
123 |
return enhanced
|
124 |
|
125 |
return user_input
|
126 |
|
127 |
except Exception as e:
|
128 |
+
print(f"Team collaboration error: {str(e)}")
|
129 |
return user_input
|
130 |
|
131 |
def upload_image_to_hosting(image):
|
132 |
"""
|
133 |
Upload image to multiple hosting services with fallback
|
134 |
"""
|
135 |
+
# Method 1: Try imgbb.com
|
136 |
try:
|
137 |
buffered = BytesIO()
|
138 |
image.save(buffered, format="PNG")
|
|
|
154 |
except:
|
155 |
pass
|
156 |
|
157 |
+
# Method 2: Try 0x0.st
|
158 |
try:
|
159 |
buffered = BytesIO()
|
160 |
image.save(buffered, format="PNG")
|
|
|
175 |
img_base64 = base64.b64encode(buffered.getvalue()).decode()
|
176 |
return f"data:image/png;base64,{img_base64}"
|
177 |
|
178 |
+
def process_product_design(prompt, enhance_prompt_flag, image1, image2=None):
|
179 |
"""
|
180 |
+
Process product design with team collaboration approach
|
181 |
"""
|
182 |
if not os.getenv('REPLICATE_API_TOKEN'):
|
183 |
+
return None, prompt, "Please set REPLICATE_API_TOKEN", ""
|
184 |
|
185 |
try:
|
186 |
+
# Store team discussion for display
|
187 |
+
team_discussion = ""
|
188 |
+
|
189 |
+
# Enhance prompt with team collaboration if requested
|
190 |
final_prompt = prompt
|
191 |
if enhance_prompt_flag:
|
192 |
+
# This will contain the full team discussion
|
193 |
+
full_response = enhance_prompt_with_team(prompt)
|
194 |
+
final_prompt = full_response
|
195 |
+
|
196 |
+
# Store the discussion for display
|
197 |
+
team_discussion = f"🎬 **Director → Engineer → Designer Collaboration**\n\n{full_response}"
|
198 |
|
199 |
# Prepare input for model
|
200 |
model_input = {
|
201 |
"prompt": final_prompt
|
202 |
}
|
203 |
|
204 |
+
# Add style reference images if provided
|
205 |
if image1 or image2:
|
206 |
image_urls = []
|
207 |
|
|
|
214 |
image_urls.append(url2)
|
215 |
|
216 |
model_input["image_input"] = image_urls
|
217 |
+
status_msg = "🏭 Product design generated with style references!"
|
218 |
else:
|
219 |
+
status_msg = "🏭 Product design generated from concept!"
|
|
|
220 |
|
221 |
# Run model
|
222 |
output = replicate.run(
|
|
|
225 |
)
|
226 |
|
227 |
if output is None:
|
228 |
+
return None, final_prompt, "No output received", team_discussion
|
229 |
|
230 |
# Get the generated image
|
231 |
try:
|
232 |
if hasattr(output, 'read'):
|
233 |
img_data = output.read()
|
234 |
img = Image.open(BytesIO(img_data))
|
235 |
+
return img, final_prompt, status_msg, team_discussion
|
236 |
except:
|
237 |
pass
|
238 |
|
|
|
242 |
response = requests.get(output_url, timeout=30)
|
243 |
if response.status_code == 200:
|
244 |
img = Image.open(BytesIO(response.content))
|
245 |
+
return img, final_prompt, status_msg, team_discussion
|
246 |
except:
|
247 |
pass
|
248 |
|
|
|
256 |
response = requests.get(output_url, timeout=30)
|
257 |
if response.status_code == 200:
|
258 |
img = Image.open(BytesIO(response.content))
|
259 |
+
return img, final_prompt, status_msg, team_discussion
|
260 |
|
261 |
+
return None, final_prompt, "Could not process output", team_discussion
|
262 |
|
263 |
except Exception as e:
|
264 |
error_msg = str(e)
|
265 |
if "image_input" in error_msg.lower():
|
266 |
+
return None, prompt, "Note: Model may require at least one image.", ""
|
267 |
+
return None, prompt, f"Error: {error_msg[:100]}", ""
|
268 |
|
269 |
+
# Enhanced CSS for Product Design focus
|
270 |
css = """
|
271 |
.gradio-container {
|
272 |
+
background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
|
273 |
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
274 |
min-height: 100vh;
|
275 |
}
|
276 |
.header-container {
|
277 |
+
background: linear-gradient(135deg, #434343 0%, #000000 100%);
|
278 |
padding: 2.5rem;
|
279 |
border-radius: 24px;
|
280 |
margin-bottom: 2.5rem;
|
281 |
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.4);
|
282 |
}
|
283 |
.logo-text {
|
284 |
font-size: 3.5rem;
|
|
|
287 |
text-align: center;
|
288 |
margin: 0;
|
289 |
letter-spacing: -2px;
|
290 |
+
text-shadow: 2px 2px 4px rgba(0,0,0,0.5);
|
291 |
}
|
292 |
.subtitle {
|
293 |
+
color: #e0e0e0;
|
294 |
text-align: center;
|
295 |
+
font-size: 1.2rem;
|
296 |
margin-top: 0.5rem;
|
297 |
+
font-weight: 300;
|
298 |
}
|
299 |
.mode-indicator {
|
300 |
+
background: rgba(255, 255, 255, 0.1);
|
301 |
backdrop-filter: blur(10px);
|
302 |
border-radius: 12px;
|
303 |
+
padding: 0.8rem 1.5rem;
|
304 |
margin-top: 1rem;
|
305 |
text-align: center;
|
306 |
font-weight: 600;
|
307 |
+
color: #ffd700;
|
308 |
+
border: 1px solid rgba(255, 215, 0, 0.3);
|
309 |
}
|
310 |
.main-content {
|
311 |
+
background: rgba(255, 255, 255, 0.98);
|
312 |
backdrop-filter: blur(20px);
|
313 |
border-radius: 24px;
|
314 |
padding: 2.5rem;
|
315 |
+
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
|
316 |
}
|
317 |
.gr-button-primary {
|
318 |
+
background: linear-gradient(135deg, #434343 0%, #000000 100%) !important;
|
319 |
+
border: 2px solid #ffd700 !important;
|
320 |
color: white !important;
|
321 |
font-weight: 700 !important;
|
322 |
font-size: 1.1rem !important;
|
|
|
330 |
}
|
331 |
.gr-button-primary:hover {
|
332 |
transform: translateY(-3px) !important;
|
333 |
+
box-shadow: 0 15px 40px rgba(255, 215, 0, 0.3) !important;
|
334 |
+
background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%) !important;
|
335 |
+
color: #000 !important;
|
336 |
}
|
337 |
.gr-input, .gr-textarea {
|
338 |
background: #ffffff !important;
|
339 |
+
border: 2px solid #d0d0d0 !important;
|
340 |
+
border-radius: 12px !important;
|
341 |
color: #2d3436 !important;
|
342 |
font-size: 1rem !important;
|
343 |
padding: 0.8rem 1rem !important;
|
344 |
}
|
345 |
.gr-input:focus, .gr-textarea:focus {
|
346 |
+
border-color: #434343 !important;
|
347 |
+
box-shadow: 0 0 0 4px rgba(67, 67, 67, 0.1) !important;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
348 |
}
|
349 |
+
.team-discussion {
|
350 |
+
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
351 |
+
border-radius: 12px;
|
352 |
+
padding: 1.5rem;
|
353 |
+
margin-top: 1rem;
|
354 |
+
border-left: 4px solid #434343;
|
355 |
+
font-family: 'Monaco', 'Courier New', monospace;
|
356 |
font-size: 0.9rem;
|
357 |
+
max-height: 400px;
|
358 |
+
overflow-y: auto;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
359 |
}
|
360 |
.info-box {
|
361 |
+
background: linear-gradient(135deg, #ffd700 0%, #ffed4e 100%);
|
362 |
border-radius: 12px;
|
363 |
padding: 1rem;
|
364 |
margin-bottom: 1rem;
|
365 |
+
border-left: 4px solid #000;
|
366 |
+
color: #000;
|
367 |
+
font-weight: 500;
|
368 |
}
|
369 |
.enhanced-prompt-box {
|
370 |
+
background: #f0f0f0;
|
371 |
border-radius: 12px;
|
372 |
padding: 1rem;
|
373 |
margin-top: 1rem;
|
374 |
+
border-left: 4px solid #ffd700;
|
375 |
}
|
376 |
+
footer {
|
377 |
+
display: none !important;
|
378 |
}
|
379 |
"""
|
380 |
|
381 |
with gr.Blocks(css=css, theme=gr.themes.Base()) as demo:
|
382 |
with gr.Column(elem_classes="header-container"):
|
383 |
gr.HTML("""
|
384 |
+
<h1 class="logo-text">🏭 PRODUCT DESIGN STUDIO</h1>
|
385 |
+
<p class="subtitle">AI-Powered Industrial & Product Design System</p>
|
386 |
<div class="mode-indicator">
|
387 |
+
👥 Team Collaboration: Director → Engineer → Designer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
388 |
</div>
|
389 |
""")
|
390 |
|
|
|
392 |
# Info box
|
393 |
gr.HTML("""
|
394 |
<div class="info-box">
|
395 |
+
<strong>🎯 Professional Product Design Process:</strong><br>
|
396 |
+
• <b>Director:</b> Establishes design vision and requirements<br>
|
397 |
+
• <b>Engineer:</b> Researches technology and feasibility<br>
|
398 |
+
• <b>Designer:</b> Creates detailed visual specifications<br>
|
399 |
+
• Focus: Physical products only (electronics, furniture, vehicles, tools, etc.)
|
400 |
</div>
|
401 |
""")
|
402 |
|
|
|
404 |
# Left Column - Inputs
|
405 |
with gr.Column(scale=1):
|
406 |
prompt = gr.Textbox(
|
407 |
+
label="Product Concept",
|
408 |
+
placeholder="Examples: 'ergonomic office chair', 'smart home speaker', 'electric bicycle', 'modular kitchen appliance'...",
|
409 |
lines=3,
|
410 |
+
value="professional mechanical keyboard",
|
411 |
elem_classes="prompt-input"
|
412 |
)
|
413 |
|
414 |
enhance_prompt_checkbox = gr.Checkbox(
|
415 |
+
label="👥 Enable Team Collaboration (Director + Engineer + Designer)",
|
416 |
value=True,
|
417 |
+
info="Activates multi-role design process with technical research"
|
418 |
)
|
419 |
|
420 |
with gr.Row(equal_height=True):
|
421 |
image1 = gr.Image(
|
422 |
+
label="Style/Material Reference 1",
|
423 |
type="pil",
|
424 |
height=200,
|
425 |
+
elem_classes="image-container"
|
426 |
)
|
427 |
image2 = gr.Image(
|
428 |
+
label="Style/Material Reference 2",
|
429 |
type="pil",
|
430 |
height=200,
|
431 |
+
elem_classes="image-container"
|
432 |
)
|
433 |
|
434 |
generate_btn = gr.Button(
|
435 |
+
"Generate Product Design 🏭",
|
436 |
variant="primary",
|
437 |
size="lg"
|
438 |
)
|
|
|
440 |
# Right Column - Output
|
441 |
with gr.Column(scale=1):
|
442 |
output_image = gr.Image(
|
443 |
+
label="Product Design Visualization",
|
444 |
type="pil",
|
445 |
height=420,
|
446 |
+
elem_classes="image-container"
|
447 |
)
|
448 |
|
449 |
enhanced_prompt_display = gr.Textbox(
|
450 |
+
label="Final Design Specifications",
|
451 |
interactive=False,
|
452 |
lines=4,
|
453 |
elem_classes="enhanced-prompt-box",
|
454 |
visible=True,
|
455 |
+
value="Design specifications will appear here..."
|
456 |
)
|
457 |
|
458 |
status = gr.Textbox(
|
|
|
460 |
interactive=False,
|
461 |
lines=1,
|
462 |
elem_classes="status-text",
|
463 |
+
value="Ready to design products..."
|
464 |
)
|
465 |
+
|
466 |
+
# Team Discussion Display
|
467 |
+
with gr.Row():
|
468 |
+
team_discussion_display = gr.Markdown(
|
469 |
+
label="Team Collaboration Process",
|
470 |
+
value="",
|
471 |
+
elem_classes="team-discussion"
|
472 |
+
)
|
473 |
|
474 |
# Event handler
|
475 |
generate_btn.click(
|
476 |
+
fn=process_product_design,
|
477 |
inputs=[prompt, enhance_prompt_checkbox, image1, image2],
|
478 |
+
outputs=[output_image, enhanced_prompt_display, status, team_discussion_display]
|
479 |
)
|
480 |
|
481 |
+
# Product Design Examples
|
482 |
gr.Examples(
|
483 |
examples=[
|
484 |
+
["ergonomic gaming mouse", True, None, None],
|
485 |
+
["portable coffee maker", True, None, None],
|
486 |
+
["modular storage system", True, None, None],
|
487 |
+
["smart bicycle helmet", True, None, None],
|
488 |
+
["industrial power tool", True, None, None],
|
489 |
+
["luxury watch design", True, None, None],
|
490 |
+
["compact home gym equipment", True, None, None],
|
491 |
+
["sustainable packaging solution", True, None, None],
|
492 |
+
["professional camera lens", True, None, None],
|
493 |
+
["electric vehicle charging station", True, None, None],
|
494 |
],
|
495 |
inputs=[prompt, enhance_prompt_checkbox, image1, image2],
|
496 |
+
label="Product Design Examples (Team will collaborate on these concepts)"
|
497 |
)
|
498 |
|
499 |
# Launch
|