nemoooooooooo commited on
Commit
77310a3
Β·
1 Parent(s): 30d2e0f
Files changed (1) hide show
  1. app.py +112 -49
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import os
2
  import io
3
  import base64
@@ -128,11 +129,26 @@ body, .gradio-container {
128
  border: 1px solid #ffeaa7;
129
  color: #856404;
130
  }
 
 
 
 
 
131
  .status-error {
132
  background: #f8d7da;
133
  border: 1px solid #f5c6cb;
134
  color: #721c24;
135
  }
 
 
 
 
 
 
 
 
 
 
136
  """
137
 
138
  def image_to_base64(image):
@@ -168,18 +184,18 @@ def try_connect_backend():
168
  test_client = Client("SnapwearAI/Pattern-Transfer-Backend", hf_token=HF_TOKEN)
169
  client = test_client
170
  backend_connected = True
171
- return "🟒 Model is ready!", True
172
  except Exception as e:
173
  client = None
174
  backend_connected = False
175
  error_str = str(e).lower()
176
  if "timeout" in error_str or "read operation timed out" in error_str:
177
- return "🟑 Model is starting up (this takes 5-6 minutes on first load). Please wait and try again.", False
178
  else:
179
  return f"πŸ”΄ Backend error: {str(e)}", False
180
 
181
  def call_backend_with_retry(print_image, product_image, max_retries=3):
182
- """Call the backend with proper error handling."""
183
  global client, backend_connected
184
 
185
  # Validate inputs
@@ -210,15 +226,26 @@ def call_backend_with_retry(print_image, product_image, max_retries=3):
210
 
211
  logger.info("Images converted to base64")
212
 
213
- # Make the backend call
214
  start_time = time.time()
215
- result = client.predict(
216
- print_b64,
217
- product_b64,
218
- guidance_scale,
219
- num_steps,
220
- api_name="/predict"
221
- )
 
 
 
 
 
 
 
 
 
 
 
222
 
223
  processing_time = time.time() - start_time
224
  logger.info(f"Backend call completed in {processing_time:.2f}s")
@@ -231,6 +258,9 @@ def call_backend_with_retry(print_image, product_image, max_retries=3):
231
  result_image = base64_to_image(result_b64)
232
  if result_image:
233
  logger.info("Successfully received and decoded result image")
 
 
 
234
  return result_image, status
235
  else:
236
  return None, "❌ Failed to decode result image"
@@ -245,29 +275,31 @@ def call_backend_with_retry(print_image, product_image, max_retries=3):
245
  # Backend might be starting up again
246
  backend_connected = False
247
  client = None
248
- return None, "🟑 Model timed out. It may be starting up again. Please wait 5-6 minutes and try the 'Check Status' button."
 
 
249
 
250
  logger.error(f"Backend call attempt {attempt + 1} failed: {e}")
251
  if attempt == max_retries - 1:
252
  return None, f"❌ Backend error: {str(e)}"
253
- time.sleep(2) # Wait before retry
254
 
255
  return None, "❌ All attempts failed"
256
 
257
  # ───────── Main UI ─────────
258
- with gr.Blocks(css=css, title="Pattern & Color Transfer Studio") as demo:
259
 
260
  # ──────── Hero Section ────────
261
  gr.HTML("""
262
  <div class="hero-section">
263
  <h1 style="font-size:48px;margin:0;background:linear-gradient(45deg,#fff,#f0f8ff);-webkit-background-clip:text;-webkit-text-fill-color:transparent;">
264
- 🎨 Snapwear Texture Transfer Studio
265
  </h1>
266
  <h2 style="font-size:24px;margin:10px 0;opacity:0.9;">
267
- Transfer Any Pattern onto Any Product Instantly
268
  </h2>
269
  <p style="font-size:18px;margin:15px 0;opacity:0.8;">
270
- β€’ Instant results β€’ Perfect for designers, brands & creators
271
  </p>
272
  <div class="social-links">
273
  <a href="https://snapwear.io" target="_blank">🌐 Official Website</a>
@@ -282,24 +314,33 @@ with gr.Blocks(css=css, title="Pattern & Color Transfer Studio") as demo:
282
  with gr.Column():
283
  # Initial status message
284
  if backend_connected:
285
- initial_status = '<div class="status-banner status-ready">🟒 Model is ready! You can generate pattern transfers.</div>'
286
  else:
287
- initial_status = '<div class="status-banner status-starting">🟑 Model may be starting up. Click "Check Status" to verify.</div>'
288
 
289
  status_display = gr.HTML(value=initial_status)
290
 
291
  # Status check button
292
- check_status_btn = gr.Button("πŸ”„ Check Status", size="sm")
 
 
 
 
 
 
 
 
293
 
294
  # ──────── Info Box ────────
295
  gr.HTML("""
296
  <div style="background:#e8f4fd;border:1px solid #bee5eb;border-radius:12px;padding:20px;margin:20px 0;">
297
- <h3 style="color:#0c5460;margin:0 0 10px 0;">ℹ️ First Time Here?</h3>
298
- <p style="color:#0c5460;margin:0;">
299
- Our AI model takes 5-6 minutes to start up on first visit after being idle.
300
- Use the "Check Status" button above to see when the system is ready.
301
- Once ready, pattern transfers complete in 30-60 seconds!
302
- </p>
 
303
  </div>
304
  """)
305
 
@@ -307,16 +348,20 @@ with gr.Blocks(css=css, title="Pattern & Color Transfer Studio") as demo:
307
  gr.HTML("""
308
  <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:20px;margin:30px 0;">
309
  <div class="feature-box">
310
- <h3>πŸš€ Instant Transfer</h3>
311
- <p>Apply any pattern to any product in 30-60 seconds with professional results</p>
312
  </div>
313
  <div class="feature-box">
314
  <h3>🎯 Perfect Mapping</h3>
315
- <p>Preserve product shape, lighting, and texture for realistic results</p>
 
 
 
 
316
  </div>
317
  <div class="feature-box">
318
- <h3>🎨 Endless Possibilities</h3>
319
- <p>Transfer prints, patterns, textures, and colors across any product type</p>
320
  </div>
321
  </div>
322
  """)
@@ -339,16 +384,16 @@ with gr.Blocks(css=css, title="Pattern & Color Transfer Studio") as demo:
339
  type="pil",
340
  height=400,
341
  )
342
- gr.HTML('<p style="text-align:center;color:#666;font-size:14px;">Upload any color, print, or design you want to transfer</p>')
343
 
344
  # Print examples
345
  if os.path.exists("Assets/print"):
346
- print_examples = [os.path.join("Assets/print", f) for f in os.listdir("Assets/print")][:10]
347
  if print_examples:
348
  gr.Examples(
349
  label="✨ Example Patterns",
350
  inputs=print_image,
351
- examples_per_page=10,
352
  examples=print_examples,
353
  )
354
 
@@ -363,12 +408,12 @@ with gr.Blocks(css=css, title="Pattern & Color Transfer Studio") as demo:
363
 
364
  # Product examples
365
  if os.path.exists("Assets/product"):
366
- product_examples = [os.path.join("Assets/product", f) for f in os.listdir("Assets/product")][:12]
367
  if product_examples:
368
  gr.Examples(
369
  label="πŸ“¦ Example Products",
370
  inputs=product_image,
371
- examples_per_page=12,
372
  examples=product_examples,
373
  )
374
 
@@ -380,7 +425,7 @@ with gr.Blocks(css=css, title="Pattern & Color Transfer Studio") as demo:
380
  height=400
381
  )
382
 
383
- # Status display
384
  status_text = gr.Text(
385
  label="Generation Status",
386
  interactive=False,
@@ -394,6 +439,13 @@ with gr.Blocks(css=css, title="Pattern & Color Transfer Studio") as demo:
394
  size="lg",
395
  variant="primary"
396
  )
 
 
 
 
 
 
 
397
 
398
  # ──────── Showcase Examples ────────
399
  gr.HTML("""
@@ -414,13 +466,12 @@ with gr.Blocks(css=css, title="Pattern & Color Transfer Studio") as demo:
414
  [os.path.join("Assets/examples", "1_product.jpg"), os.path.join("Assets/examples", "1_print.jpg"), os.path.join("Assets/examples", "1_result.jpg")],
415
  [os.path.join("Assets/examples", "2_product.jpg"), os.path.join("Assets/examples", "2_print.jpg"), os.path.join("Assets/examples", "2_result.jpg")],
416
  [os.path.join("Assets/examples", "3_product.jpg"), os.path.join("Assets/examples", "3_print.jpg"), os.path.join("Assets/examples", "3_result.jpg")],
417
- [os.path.join("Assets/examples", "4_product.jpg"), os.path.join("Assets/examples", "4_print.jpg"), os.path.join("Assets/examples", "4_result.jpg")],
418
  ]
419
  pattern_showcase = gr.Examples(
420
  examples=showcase_examples,
421
  inputs=[product_image, print_image, result_img],
422
  label="Pattern Transfer Examples - Click any example to try it yourself!",
423
- examples_per_page=4,
424
  )
425
  except:
426
  gr.HTML("<p style='text-align:center;color:#666;'>Pattern transfer examples will appear here once example files are added to Assets/examples/</p>")
@@ -430,20 +481,19 @@ with gr.Blocks(css=css, title="Pattern & Color Transfer Studio") as demo:
430
  gr.HTML('<h3 style="text-align:center;color:#764ba2;margin:20px 0;">🌈 Color Transfer Showcase</h3>')
431
 
432
  try:
433
- if os.path.exists("Assets/examples/color"):
434
  color_examples = [
435
- [os.path.join("Assets/examples/color", "1_product.jpg"), os.path.join("Assets/examples/color", "1_print.jpg"), os.path.join("Assets/examples/color", "1_result.jpg")],
436
- [os.path.join("Assets/examples/color", "2_product.jpg"), os.path.join("Assets/examples/color", "2_print.jpg"), os.path.join("Assets/examples/color", "2_result.jpg")],
437
- [os.path.join("Assets/examples/color", "3_product.jpg"), os.path.join("Assets/examples/color", "3_print.jpg"), os.path.join("Assets/examples/color", "3_result.jpg")],
438
  ]
439
  color_showcase = gr.Examples(
440
  examples=color_examples,
441
  inputs=[product_image, print_image, result_img],
442
  label="Color Transfer Examples - Perfect for recoloring products!",
443
- examples_per_page=3,
444
  )
445
  except:
446
- gr.HTML("<p style='text-align:center;color:#666;'>Color transfer examples will appear here once example files are added to Assets/examples/color/</p>")
447
 
448
  # ──────── Use Cases ────────
449
  gr.HTML("""
@@ -470,6 +520,19 @@ with gr.Blocks(css=css, title="Pattern & Color Transfer Studio") as demo:
470
  </div>
471
  """)
472
 
 
 
 
 
 
 
 
 
 
 
 
 
 
473
  # ──────── Event Handlers ────────
474
  def update_status_display():
475
  """Check backend status and update display"""
@@ -491,13 +554,13 @@ with gr.Blocks(css=css, title="Pattern & Color Transfer Studio") as demo:
491
  outputs=[status_display]
492
  )
493
 
494
- # Generate button click
495
  generate_btn.click(
496
  fn=call_backend_with_retry,
497
  inputs=[print_image, product_image],
498
  outputs=[result_img, status_text],
499
  show_progress="full",
500
- concurrency_limit=8,
501
  )
502
 
503
  # ──────── Footer ────────
@@ -506,7 +569,7 @@ with gr.Blocks(css=css, title="Pattern & Color Transfer Studio") as demo:
506
  <h3 style="color:#333;">πŸš€ Powered by Snapwear AI</h3>
507
  <p style="color:#666;">
508
  Transform your creative vision with professional-grade AI technology.<br/>
509
- Join thousands of designers, brands, and creators already using our tools.
510
  </p>
511
  <div class="social-links">
512
  <a href="https://snapwear.io" target="_blank">🌐 Website</a>
@@ -522,7 +585,7 @@ with gr.Blocks(css=css, title="Pattern & Color Transfer Studio") as demo:
522
  if __name__ == "__main__":
523
  demo.queue(
524
  max_size=20,
525
- default_concurrency_limit=8,
526
  api_open=False
527
  ).launch(
528
  server_name="0.0.0.0",
 
1
+
2
  import os
3
  import io
4
  import base64
 
129
  border: 1px solid #ffeaa7;
130
  color: #856404;
131
  }
132
+ .status-processing {
133
+ background: #cce5ff;
134
+ border: 1px solid #99ccff;
135
+ color: #004085;
136
+ }
137
  .status-error {
138
  background: #f8d7da;
139
  border: 1px solid #f5c6cb;
140
  color: #721c24;
141
  }
142
+ .queue-info {
143
+ background: #e8f4fd;
144
+ border: 1px solid #bee5eb;
145
+ padding: 12px;
146
+ border-radius: 8px;
147
+ margin: 10px 0;
148
+ text-align: center;
149
+ font-size: 14px;
150
+ color: #0c5460;
151
+ }
152
  """
153
 
154
  def image_to_base64(image):
 
184
  test_client = Client("SnapwearAI/Pattern-Transfer-Backend", hf_token=HF_TOKEN)
185
  client = test_client
186
  backend_connected = True
187
+ return "🟒 Backend is ready! You can now generate pattern transfers.", True
188
  except Exception as e:
189
  client = None
190
  backend_connected = False
191
  error_str = str(e).lower()
192
  if "timeout" in error_str or "read operation timed out" in error_str:
193
+ return "🟑 Backend is starting up (this takes 5-6 minutes on first load). Please wait and try again.", False
194
  else:
195
  return f"πŸ”΄ Backend error: {str(e)}", False
196
 
197
  def call_backend_with_retry(print_image, product_image, max_retries=3):
198
+ """Call the backend with proper error handling and queue awareness."""
199
  global client, backend_connected
200
 
201
  # Validate inputs
 
226
 
227
  logger.info("Images converted to base64")
228
 
229
+ # Make the backend call with progress tracking
230
  start_time = time.time()
231
+
232
+ # Add queue position info if available
233
+ try:
234
+ result = client.predict(
235
+ print_b64,
236
+ product_b64,
237
+ guidance_scale,
238
+ num_steps,
239
+ api_name="/predict"
240
+ )
241
+ except Exception as prediction_error:
242
+ # Handle queue-related messages in error
243
+ error_str = str(prediction_error).lower()
244
+ if "queue" in error_str or "position" in error_str:
245
+ # Extract queue info if present
246
+ return None, f"πŸ“‹ Request queued. {str(prediction_error)}"
247
+ else:
248
+ raise prediction_error
249
 
250
  processing_time = time.time() - start_time
251
  logger.info(f"Backend call completed in {processing_time:.2f}s")
 
258
  result_image = base64_to_image(result_b64)
259
  if result_image:
260
  logger.info("Successfully received and decoded result image")
261
+ # Add processing time to status if not already present
262
+ if "Generated in" not in status:
263
+ status = f"{status} (Total time: {processing_time:.1f}s)"
264
  return result_image, status
265
  else:
266
  return None, "❌ Failed to decode result image"
 
275
  # Backend might be starting up again
276
  backend_connected = False
277
  client = None
278
+ return None, "🟑 Backend timed out. It may be starting up or busy with other requests. Please try again in a few moments."
279
+ elif "queue" in error_str or "busy" in error_str:
280
+ return None, f"πŸ“‹ Server is busy processing other requests. Please wait and try again. {str(e)}"
281
 
282
  logger.error(f"Backend call attempt {attempt + 1} failed: {e}")
283
  if attempt == max_retries - 1:
284
  return None, f"❌ Backend error: {str(e)}"
285
+ time.sleep(3) # Wait before retry
286
 
287
  return None, "❌ All attempts failed"
288
 
289
  # ───────── Main UI ─────────
290
+ with gr.Blocks(css=css, title="AI Style Transfer Studio - Pattern & Color Transfer") as demo:
291
 
292
  # ──────── Hero Section ────────
293
  gr.HTML("""
294
  <div class="hero-section">
295
  <h1 style="font-size:48px;margin:0;background:linear-gradient(45deg,#fff,#f0f8ff);-webkit-background-clip:text;-webkit-text-fill-color:transparent;">
296
+ 🎨 AI Style Transfer Studio
297
  </h1>
298
  <h2 style="font-size:24px;margin:10px 0;opacity:0.9;">
299
+ Transform Any Pattern onto Any Product Instantly
300
  </h2>
301
  <p style="font-size:18px;margin:15px 0;opacity:0.8;">
302
+ Professional-grade AI pattern transfer β€’ Instant results β€’ Perfect for designers, brands & creators
303
  </p>
304
  <div class="social-links">
305
  <a href="https://snapwear.io" target="_blank">🌐 Official Website</a>
 
314
  with gr.Column():
315
  # Initial status message
316
  if backend_connected:
317
+ initial_status = '<div class="status-banner status-ready">🟒 Backend is ready! You can generate pattern transfers.</div>'
318
  else:
319
+ initial_status = '<div class="status-banner status-starting">🟑 Backend may be starting up. Click "Check Status" to verify.</div>'
320
 
321
  status_display = gr.HTML(value=initial_status)
322
 
323
  # Status check button
324
+ check_status_btn = gr.Button("πŸ”„ Check Backend Status", size="sm")
325
+
326
+ # ──────── Queue Info Section ────────
327
+ gr.HTML("""
328
+ <div class="queue-info">
329
+ <strong>ℹ️ Multiple Users:</strong> If others are using the system, your request will be queued.
330
+ Processing time is 30-60 seconds per request. You'll see queue position updates during processing.
331
+ </div>
332
+ """)
333
 
334
  # ──────── Info Box ────────
335
  gr.HTML("""
336
  <div style="background:#e8f4fd;border:1px solid #bee5eb;border-radius:12px;padding:20px;margin:20px 0;">
337
+ <h3 style="color:#0c5460;margin:0 0 10px 0;">ℹ️ How It Works</h3>
338
+ <div style="color:#0c5460;margin:0;">
339
+ <p><strong>First Time:</strong> Backend takes 5-6 minutes to start up after being idle.</p>
340
+ <p><strong>Multiple Users:</strong> Requests are processed one at a time to ensure quality. You'll be queued if others are using the system.</p>
341
+ <p><strong>Processing Time:</strong> 30-60 seconds per request once processing begins.</p>
342
+ <p><strong>Queue Updates:</strong> You'll see your position and estimated wait time.</p>
343
+ </div>
344
  </div>
345
  """)
346
 
 
348
  gr.HTML("""
349
  <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:20px;margin:30px 0;">
350
  <div class="feature-box">
351
+ <h3>πŸš€ Smart Queuing</h3>
352
+ <p>Automatic request queuing ensures stable results even with multiple simultaneous users</p>
353
  </div>
354
  <div class="feature-box">
355
  <h3>🎯 Perfect Mapping</h3>
356
+ <p>AI preserves product shape, lighting, and texture for realistic results</p>
357
+ </div>
358
+ <div class="feature-box">
359
+ <h3>πŸ’Ž Professional Quality</h3>
360
+ <p>Exhibition-grade 4K/8K outputs ready for commercial use</p>
361
  </div>
362
  <div class="feature-box">
363
+ <h3>🎨 Reliable Processing</h3>
364
+ <p>Single-threaded processing prevents errors and ensures consistent quality</p>
365
  </div>
366
  </div>
367
  """)
 
384
  type="pil",
385
  height=400,
386
  )
387
+ gr.HTML('<p style="text-align:center;color:#666;font-size:14px;">Upload any pattern, print, texture, or design you want to transfer</p>')
388
 
389
  # Print examples
390
  if os.path.exists("Assets/print"):
391
+ print_examples = [os.path.join("Assets/print", f) for f in os.listdir("Assets/print")][:6]
392
  if print_examples:
393
  gr.Examples(
394
  label="✨ Example Patterns",
395
  inputs=print_image,
396
+ examples_per_page=6,
397
  examples=print_examples,
398
  )
399
 
 
408
 
409
  # Product examples
410
  if os.path.exists("Assets/product"):
411
+ product_examples = [os.path.join("Assets/product", f) for f in os.listdir("Assets/product")][:6]
412
  if product_examples:
413
  gr.Examples(
414
  label="πŸ“¦ Example Products",
415
  inputs=product_image,
416
+ examples_per_page=6,
417
  examples=product_examples,
418
  )
419
 
 
425
  height=400
426
  )
427
 
428
+ # Status display with queue info
429
  status_text = gr.Text(
430
  label="Generation Status",
431
  interactive=False,
 
439
  size="lg",
440
  variant="primary"
441
  )
442
+
443
+ # Queue status info
444
+ gr.HTML("""
445
+ <div style="font-size:12px;color:#666;text-align:center;margin-top:10px;">
446
+ πŸ’‘ If busy, you'll be automatically queued and see position updates
447
+ </div>
448
+ """)
449
 
450
  # ──────── Showcase Examples ────────
451
  gr.HTML("""
 
466
  [os.path.join("Assets/examples", "1_product.jpg"), os.path.join("Assets/examples", "1_print.jpg"), os.path.join("Assets/examples", "1_result.jpg")],
467
  [os.path.join("Assets/examples", "2_product.jpg"), os.path.join("Assets/examples", "2_print.jpg"), os.path.join("Assets/examples", "2_result.jpg")],
468
  [os.path.join("Assets/examples", "3_product.jpg"), os.path.join("Assets/examples", "3_print.jpg"), os.path.join("Assets/examples", "3_result.jpg")],
 
469
  ]
470
  pattern_showcase = gr.Examples(
471
  examples=showcase_examples,
472
  inputs=[product_image, print_image, result_img],
473
  label="Pattern Transfer Examples - Click any example to try it yourself!",
474
+ examples_per_page=3,
475
  )
476
  except:
477
  gr.HTML("<p style='text-align:center;color:#666;'>Pattern transfer examples will appear here once example files are added to Assets/examples/</p>")
 
481
  gr.HTML('<h3 style="text-align:center;color:#764ba2;margin:20px 0;">🌈 Color Transfer Showcase</h3>')
482
 
483
  try:
484
+ if os.path.exists("Assets/color_examples"):
485
  color_examples = [
486
+ [os.path.join("Assets/color_examples", "1_product.jpg"), os.path.join("Assets/color_examples", "1_color.jpg"), os.path.join("Assets/color_examples", "1_result.jpg")],
487
+ [os.path.join("Assets/color_examples", "2_product.jpg"), os.path.join("Assets/color_examples", "2_color.jpg"), os.path.join("Assets/color_examples", "2_result.jpg")],
 
488
  ]
489
  color_showcase = gr.Examples(
490
  examples=color_examples,
491
  inputs=[product_image, print_image, result_img],
492
  label="Color Transfer Examples - Perfect for recoloring products!",
493
+ examples_per_page=2,
494
  )
495
  except:
496
+ gr.HTML("<p style='text-align:center;color:#666;'>Color transfer examples will appear here once example files are added to Assets/color_examples/</p>")
497
 
498
  # ──────── Use Cases ────────
499
  gr.HTML("""
 
520
  </div>
521
  """)
522
 
523
+ # ──────── Technical Details ────────
524
+ gr.HTML("""
525
+ <div style="background:#f8fafc;border:1px solid #e2e8f0;padding:20px;border-radius:12px;margin:20px 0;">
526
+ <h3 style="color:#333;text-align:center;margin-bottom:15px;">πŸ”§ Technical Details</h3>
527
+ <div style="font-size:14px;color:#666;">
528
+ <p><strong>Queue System:</strong> Requests are processed sequentially to prevent GPU memory conflicts and ensure stable results.</p>
529
+ <p><strong>Concurrency Protection:</strong> Single-worker processing eliminates the "index out of bounds" errors that occur with parallel processing.</p>
530
+ <p><strong>Timeout Protection:</strong> Requests timeout after 10 minutes to prevent infinite waiting.</p>
531
+ <p><strong>State Isolation:</strong> Each request gets its own random seed and generator to prevent interference.</p>
532
+ </div>
533
+ </div>
534
+ """)
535
+
536
  # ──────── Event Handlers ────────
537
  def update_status_display():
538
  """Check backend status and update display"""
 
554
  outputs=[status_display]
555
  )
556
 
557
+ # Generate button click with enhanced progress tracking
558
  generate_btn.click(
559
  fn=call_backend_with_retry,
560
  inputs=[print_image, product_image],
561
  outputs=[result_img, status_text],
562
  show_progress="full",
563
+ concurrency_limit=1, # Ensure only one generation at a time on frontend too
564
  )
565
 
566
  # ──────── Footer ────────
 
569
  <h3 style="color:#333;">πŸš€ Powered by Snapwear AI</h3>
570
  <p style="color:#666;">
571
  Transform your creative vision with professional-grade AI technology.<br/>
572
+ Now with smart queuing for reliable multi-user support.
573
  </p>
574
  <div class="social-links">
575
  <a href="https://snapwear.io" target="_blank">🌐 Website</a>
 
585
  if __name__ == "__main__":
586
  demo.queue(
587
  max_size=20,
588
+ default_concurrency_limit=1, # Single concurrent request to match backend
589
  api_open=False
590
  ).launch(
591
  server_name="0.0.0.0",