milliyin commited on
Commit
3cfe471
Β·
verified Β·
1 Parent(s): 998ac7d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +81 -12
app.py CHANGED
@@ -17,6 +17,8 @@ logger = logging.getLogger(__name__)
17
  PREDICT_TIMEOUT = 600
18
  GPU_WARM_WINDOW = 900
19
  MAX_RETRIES = 1
 
 
20
 
21
  # ───────── Backend connection ─────────
22
  HF_TOKEN = os.getenv("HF_TOKEN")
@@ -76,10 +78,19 @@ def base64_to_image(b64: str) -> Image.Image | None:
76
  logger.error(f"Failed to decode base64 β†’ image: {e}")
77
  return None
78
 
 
 
 
 
 
 
 
 
 
 
79
  # ───────── UI ↔ Backend bridge ─────────
80
  def call_backend_with_retry(input_image: Image.Image, category: str, gender: str, *, max_retries: int = MAX_RETRIES):
81
-
82
- """Single‑shot call (no more than `max_retries` times)."""
83
 
84
  if input_image is None:
85
  return None, None, "❌ Please upload an image.", gr.update(interactive=True)
@@ -90,21 +101,24 @@ def call_backend_with_retry(input_image: Image.Image, category: str, gender: str
90
  return None, None, msg, gr.update(interactive=True)
91
 
92
  client: Client = backend_status["client"]
93
- # print(Client.view_api())
94
  img_b64 = image_to_base64(input_image)
95
 
 
 
 
 
96
  for attempt in range(max_retries):
97
  try:
98
  logger.info(f"Backend call #{attempt+1}")
99
- start = time.time()
 
100
  result = client.predict(
101
  img_b64,
102
  category,
103
  gender,
104
  api_name="/predict",
105
-
106
  )
107
- dt = time.time() - start
108
 
109
  if not result or len(result) < 4:
110
  raise ValueError("Invalid response structure from backend")
@@ -118,7 +132,9 @@ def call_backend_with_retry(input_image: Image.Image, category: str, gender: str
118
 
119
  if not status.startswith("βœ…"):
120
  status = "βœ… " + status
121
- status += f" (⏱ {dt:.1f}s)"
 
 
122
  return overlay_img, bg_img, status, gr.update(interactive=True)
123
 
124
  except Exception as e:
@@ -132,6 +148,10 @@ def call_backend_with_retry(input_image: Image.Image, category: str, gender: str
132
  def disable_button():
133
  return gr.update(interactive=False)
134
 
 
 
 
 
135
  # ───────── CSS ─────────
136
  custom_css = """
137
  .gradio-container {
@@ -147,6 +167,24 @@ custom_css = """
147
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
148
  backdrop-filter: blur(10px);
149
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
150
  .title-container {
151
  text-align: center;
152
  margin-bottom: 25px;
@@ -284,14 +322,24 @@ with gr.Blocks(css=custom_css, title="Jewellery Photography Preview") as demo:
284
 
285
  # Status banner
286
  status_html = gr.HTML()
 
 
 
287
 
288
  def _update_status():
289
  ok, msg = check_backend_connection()
290
  cls = "status-ready" if ok else ("status-starting" if "🟑" in msg else "status-error")
291
  return f'<div class="status-banner {cls}">{msg}</div>'
292
 
 
 
 
293
  status_html.value = _update_status()
294
- gr.Button("πŸ”„ Check Status").click(fn=_update_status, outputs=status_html)
 
 
 
 
295
 
296
  with gr.Column():
297
  with gr.Row():
@@ -334,6 +382,15 @@ with gr.Blocks(css=custom_css, title="Jewellery Photography Preview") as demo:
334
 
335
 
336
  out_status = gr.Text(label="Status", interactive=False)
 
 
 
 
 
 
 
 
 
337
  # ──────── Footer ────────
338
  gr.HTML("""
339
  <div style="text-align:center;padding:40px 20px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:16px;margin:30px 0;">
@@ -352,20 +409,32 @@ with gr.Blocks(css=custom_css, title="Jewellery Photography Preview") as demo:
352
  </div>
353
  """)
354
 
355
- # wire button β†’ backend
356
  run_btn.click(
357
  fn=disable_button,
358
  inputs=None,
359
  outputs=run_btn
 
 
 
 
360
  ).then(
361
  fn=call_backend_with_retry,
362
  inputs=[input_img, category, gender],
363
  outputs=[out_overlay, out_bg, out_status, run_btn],
364
- concurrency_limit=1,
365
  show_progress=True,
366
  )
367
 
368
 
369
- # ───────── Launch ─────────
370
  if __name__ == "__main__":
371
- demo.queue(max_size=20, default_concurrency_limit=1).launch(share=False)
 
 
 
 
 
 
 
 
 
17
  PREDICT_TIMEOUT = 600
18
  GPU_WARM_WINDOW = 900
19
  MAX_RETRIES = 1
20
+ MAX_QUEUE_SIZE = 50 # Maximum number of requests in queue
21
+ DEFAULT_CONCURRENCY_LIMIT = 3 # Number of concurrent backend calls
22
 
23
  # ───────── Backend connection ─────────
24
  HF_TOKEN = os.getenv("HF_TOKEN")
 
78
  logger.error(f"Failed to decode base64 β†’ image: {e}")
79
  return None
80
 
81
+ # ───────── Queue Status ─────────
82
+ def get_queue_status():
83
+ """Get current queue status for display"""
84
+ try:
85
+ # This would need to be implemented based on your queue system
86
+ # For now, return a placeholder
87
+ return "πŸ“Š Queue: Ready for requests"
88
+ except Exception as e:
89
+ return f"πŸ“Š Queue status unavailable: {e}"
90
+
91
  # ───────── UI ↔ Backend bridge ─────────
92
  def call_backend_with_retry(input_image: Image.Image, category: str, gender: str, *, max_retries: int = MAX_RETRIES):
93
+ """Single‑shot call (no more than `max_retries` times) with queue handling."""
 
94
 
95
  if input_image is None:
96
  return None, None, "❌ Please upload an image.", gr.update(interactive=True)
 
101
  return None, None, msg, gr.update(interactive=True)
102
 
103
  client: Client = backend_status["client"]
 
104
  img_b64 = image_to_base64(input_image)
105
 
106
+ # Add queue position info
107
+ start_time = time.time()
108
+ logger.info(f"Request queued at {time.strftime('%H:%M:%S')}")
109
+
110
  for attempt in range(max_retries):
111
  try:
112
  logger.info(f"Backend call #{attempt+1}")
113
+
114
+ # Add timeout to prevent hanging
115
  result = client.predict(
116
  img_b64,
117
  category,
118
  gender,
119
  api_name="/predict",
 
120
  )
121
+ dt = time.time() - start_time
122
 
123
  if not result or len(result) < 4:
124
  raise ValueError("Invalid response structure from backend")
 
132
 
133
  if not status.startswith("βœ…"):
134
  status = "βœ… " + status
135
+ status += f" (⏱ Total: {dt:.1f}s)"
136
+
137
+ logger.info(f"Request completed successfully in {dt:.1f}s")
138
  return overlay_img, bg_img, status, gr.update(interactive=True)
139
 
140
  except Exception as e:
 
148
  def disable_button():
149
  return gr.update(interactive=False)
150
 
151
+ def show_queue_position():
152
+ """Show current position in queue"""
153
+ return "⏳ Request added to queue..."
154
+
155
  # ───────── CSS ─────────
156
  custom_css = """
157
  .gradio-container {
 
167
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
168
  backdrop-filter: blur(10px);
169
  }
170
+ .queue-status {
171
+ background: linear-gradient(135deg, #4CAF50, #45a049);
172
+ color: white;
173
+ padding: 10px;
174
+ border-radius: 8px;
175
+ text-align: center;
176
+ margin: 10px 0;
177
+ font-weight: 500;
178
+ }
179
+ .queue-warning {
180
+ background: linear-gradient(135deg, #ff9800, #f57c00);
181
+ color: white;
182
+ padding: 10px;
183
+ border-radius: 8px;
184
+ text-align: center;
185
+ margin: 10px 0;
186
+ font-weight: 500;
187
+ }
188
  .title-container {
189
  text-align: center;
190
  margin-bottom: 25px;
 
322
 
323
  # Status banner
324
  status_html = gr.HTML()
325
+
326
+ # Queue status display
327
+ queue_status = gr.HTML()
328
 
329
  def _update_status():
330
  ok, msg = check_backend_connection()
331
  cls = "status-ready" if ok else ("status-starting" if "🟑" in msg else "status-error")
332
  return f'<div class="status-banner {cls}">{msg}</div>'
333
 
334
+ def _update_queue_status():
335
+ return f'<div class="queue-status">{get_queue_status()}</div>'
336
+
337
  status_html.value = _update_status()
338
+ queue_status.value = _update_queue_status()
339
+
340
+ with gr.Row():
341
+ gr.Button("πŸ”„ Check Status").click(fn=_update_status, outputs=status_html)
342
+ gr.Button("πŸ“Š Queue Status").click(fn=_update_queue_status, outputs=queue_status)
343
 
344
  with gr.Column():
345
  with gr.Row():
 
382
 
383
 
384
  out_status = gr.Text(label="Status", interactive=False)
385
+
386
+ # Queue information
387
+ gr.HTML("""
388
+ <div class="tip-box">
389
+ <strong>⏳ Queue Information:</strong> Requests are processed in order.
390
+ During high traffic, you may experience wait times. The button will be disabled while processing.
391
+ </div>
392
+ """)
393
+
394
  # ──────── Footer ────────
395
  gr.HTML("""
396
  <div style="text-align:center;padding:40px 20px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:16px;margin:30px 0;">
 
409
  </div>
410
  """)
411
 
412
+ # Wire button β†’ backend with queue status updates
413
  run_btn.click(
414
  fn=disable_button,
415
  inputs=None,
416
  outputs=run_btn
417
+ ).then(
418
+ fn=show_queue_position,
419
+ inputs=None,
420
+ outputs=out_status
421
  ).then(
422
  fn=call_backend_with_retry,
423
  inputs=[input_img, category, gender],
424
  outputs=[out_overlay, out_bg, out_status, run_btn],
425
+ concurrency_limit=DEFAULT_CONCURRENCY_LIMIT, # Limit concurrent backend calls
426
  show_progress=True,
427
  )
428
 
429
 
430
+ # ───────── Launch with Queue ─────────
431
  if __name__ == "__main__":
432
+ demo.queue(
433
+ max_size=MAX_QUEUE_SIZE, # Maximum requests in queue
434
+ default_concurrency_limit=DEFAULT_CONCURRENCY_LIMIT, # Concurrent processing limit
435
+ ).launch(
436
+ share=False,
437
+ server_name="0.0.0.0",
438
+ server_port=7860,
439
+ show_error=True
440
+ )