Jin Zhu commited on
Commit
07f68c2
·
1 Parent(s): 2a268ef

Update app.py

Browse files
Files changed (1) hide show
  1. src/app.py +66 -14
src/app.py CHANGED
@@ -203,6 +203,11 @@ INTERPRETATION_TEXT = """
203
  - Not sure of the domain? Switch **Domain of the text** to *Soft Estimate* or *Estimate* — the estimation is conducted by a pretrained transformer-based classifier that infers it automatically.
204
  """
205
 
 
 
 
 
 
206
  FOOTER_TEXT = (
207
  "This tool is developed for research purposes only. The detection results are not "
208
  "100% accurate and should not be used as the sole basis for any critical decisions. "
@@ -249,35 +254,46 @@ def on_domain_mode_change(domain_mode: str):
249
  )
250
 
251
 
252
- def run_detection(text: str, domain_mode: str, manual_domain: str, alpha: float, progress=gr.Progress()):
 
 
 
 
253
  """Detect button handler: runs inference and refreshes all result widgets.
254
 
255
- `progress` is a `gr.Progress` instance (see
256
- https://www.gradio.app/guides/progress-bars) -- Gradio recognizes it by
257
- its default value and doesn't treat it as a regular input, so it's left
258
- out of `detect_btn.click(inputs=...)`. The actual scoring pass
259
- (`_run_inference`) is one blocking call with no internal hooks to report
260
- from, so the bar advances in stages around it rather than continuously.
 
 
 
 
 
261
  """
 
 
 
 
 
262
  if not text or not text.strip():
 
263
  raise gr.Error("⚠️ Please enter some text before detecting.")
264
 
265
- progress(0, desc="Validating input…")
266
  domain = resolve_domain_selection(domain_mode, manual_domain)
267
- is_auto_domain = domain in (ESTIMATE_DOMAIN, SOFTEST_DOMAIN)
268
 
269
  start_time = time.time()
270
  try:
271
- progress(0.2, desc="Estimating domain…" if is_auto_domain else "Tokenizing text…")
272
- progress(0.4, desc="Scoring text against the reference model…")
273
  crit, p_value = _run_inference(text, domain)
274
- progress(0.9, desc="Computing p-value…")
275
  except gr.Error:
 
276
  raise
277
  except Exception as e: # noqa: BLE001 — surfaced to the user via gr.Error
 
278
  raise gr.Error(f"Detection failed: {e}")
279
  elapsed_time = time.time() - start_time
280
- progress(1.0, desc="Done")
281
 
282
  stats_manager.increment_detection()
283
 
@@ -290,7 +306,7 @@ def run_detection(text: str, domain_mode: str, manual_domain: str, alpha: float,
290
  "feedback_given": False,
291
  }
292
 
293
- return (
294
  gr.update(value=format_conclusion(p_value, alpha), visible=True),
295
  gr.update(visible=True), # interpretation accordion
296
  gr.update(value=f"⏱️ Processing time: {elapsed_time:.2f} seconds", visible=True),
@@ -298,6 +314,8 @@ def run_detection(text: str, domain_mode: str, manual_domain: str, alpha: float,
298
  gr.update(visible=True), # feedback buttons row (re-shown in case a prior run hid it)
299
  gr.update(visible=False), # feedback thanks message
300
  detection_state,
 
 
301
  )
302
 
303
 
@@ -355,6 +373,27 @@ CUSTOM_CSS = """
355
  #detect-btn:hover {
356
  background-color: #fd8d3c !important;
357
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
  .conclusion-card {
359
  background-color: #f8fafc;
360
  border: 1px solid #e5e7eb;
@@ -451,6 +490,13 @@ def build_interface() -> gr.Blocks:
451
  label="Significance level (α)", scale=1,
452
  )
453
 
 
 
 
 
 
 
 
454
  domain_mode_info = gr.Markdown(DOMAIN_MODE_INFO[DEFAULT_DOMAIN_MODE])
455
  if not DOMAIN_ESTIMATOR_AVAILABLE:
456
  gr.Markdown(
@@ -493,7 +539,13 @@ def build_interface() -> gr.Blocks:
493
  outputs=[
494
  conclusion_md, interpretation_box, elapsed_caption,
495
  feedback_section, feedback_row, feedback_thanks, detection_state,
 
496
  ],
 
 
 
 
 
497
  )
498
 
499
  expected_btn.click(
 
203
  - Not sure of the domain? Switch **Domain of the text** to *Soft Estimate* or *Estimate* — the estimation is conducted by a pretrained transformer-based classifier that infers it automatically.
204
  """
205
 
206
+ DETECTING_HTML = (
207
+ '<div class="spinner"></div>'
208
+ '<span>Detecting… this can take a few seconds.</span>'
209
+ )
210
+
211
  FOOTER_TEXT = (
212
  "This tool is developed for research purposes only. The detection results are not "
213
  "100% accurate and should not be used as the sole basis for any critical decisions. "
 
254
  )
255
 
256
 
257
+ DETECT_BTN_IDLE = gr.update(value="🔍 Detect", interactive=True)
258
+ DETECT_BTN_BUSY = gr.update(value="⏳ Detecting…", interactive=False)
259
+
260
+
261
+ def run_detection(text: str, domain_mode: str, manual_domain: str, alpha: float):
262
  """Detect button handler: runs inference and refreshes all result widgets.
263
 
264
+ Written as a generator (`yield` instead of `return`) so it can push the
265
+ UI to the frontend in two steps instead of one:
266
+
267
+ 1. The instant the button is pressed — flip on the spinner + "Detecting…"
268
+ button state, before any inference has run.
269
+ 2. Once `_run_inference` returns (or raises) — turn the spinner back off
270
+ and show the result (or let the error surface via `gr.Error`).
271
+
272
+ That before/after pair is what makes it unambiguous whether detection is
273
+ still running, unlike the old staged `gr.Progress()` bar, which jumped
274
+ between a few fixed percentages and didn't clearly resolve to "done".
275
  """
276
+ # Everything except the spinner + button is left untouched on this first
277
+ # yield — any previous result stays on screen until the new one is ready.
278
+ unchanged_results = (gr.update(),) * 7
279
+ yield unchanged_results + (gr.update(visible=True), DETECT_BTN_BUSY)
280
+
281
  if not text or not text.strip():
282
+ yield unchanged_results + (gr.update(visible=False), DETECT_BTN_IDLE)
283
  raise gr.Error("⚠️ Please enter some text before detecting.")
284
 
 
285
  domain = resolve_domain_selection(domain_mode, manual_domain)
 
286
 
287
  start_time = time.time()
288
  try:
 
 
289
  crit, p_value = _run_inference(text, domain)
 
290
  except gr.Error:
291
+ yield unchanged_results + (gr.update(visible=False), DETECT_BTN_IDLE)
292
  raise
293
  except Exception as e: # noqa: BLE001 — surfaced to the user via gr.Error
294
+ yield unchanged_results + (gr.update(visible=False), DETECT_BTN_IDLE)
295
  raise gr.Error(f"Detection failed: {e}")
296
  elapsed_time = time.time() - start_time
 
297
 
298
  stats_manager.increment_detection()
299
 
 
306
  "feedback_given": False,
307
  }
308
 
309
+ yield (
310
  gr.update(value=format_conclusion(p_value, alpha), visible=True),
311
  gr.update(visible=True), # interpretation accordion
312
  gr.update(value=f"⏱️ Processing time: {elapsed_time:.2f} seconds", visible=True),
 
314
  gr.update(visible=True), # feedback buttons row (re-shown in case a prior run hid it)
315
  gr.update(visible=False), # feedback thanks message
316
  detection_state,
317
+ gr.update(visible=False), # detecting indicator — inference is done
318
+ DETECT_BTN_IDLE,
319
  )
320
 
321
 
 
373
  #detect-btn:hover {
374
  background-color: #fd8d3c !important;
375
  }
376
+ #detecting-indicator {
377
+ display: flex;
378
+ align-items: center;
379
+ gap: 0.5rem;
380
+ margin: -0.3rem 0 0.5rem;
381
+ color: #b45309;
382
+ font-size: 0.92rem;
383
+ font-weight: 500;
384
+ }
385
+ #detecting-indicator .spinner {
386
+ flex: none;
387
+ width: 1rem;
388
+ height: 1rem;
389
+ border: 2.5px solid #fde3c8;
390
+ border-top-color: #fd8d3c;
391
+ border-radius: 50%;
392
+ animation: detecting-spin 0.7s linear infinite;
393
+ }
394
+ @keyframes detecting-spin {
395
+ to { transform: rotate(360deg); }
396
+ }
397
  .conclusion-card {
398
  background-color: #f8fafc;
399
  border: 1px solid #e5e7eb;
 
490
  label="Significance level (α)", scale=1,
491
  )
492
 
493
+ # Hidden by default; `run_detection` toggles it on for the duration of
494
+ # each detection run so there's an unmistakable, animated "still
495
+ # running" cue right under the button the user just pressed.
496
+ detecting_indicator = gr.HTML(
497
+ DETECTING_HTML, visible=False, elem_id="detecting-indicator",
498
+ )
499
+
500
  domain_mode_info = gr.Markdown(DOMAIN_MODE_INFO[DEFAULT_DOMAIN_MODE])
501
  if not DOMAIN_ESTIMATOR_AVAILABLE:
502
  gr.Markdown(
 
539
  outputs=[
540
  conclusion_md, interpretation_box, elapsed_caption,
541
  feedback_section, feedback_row, feedback_thanks, detection_state,
542
+ detecting_indicator, detect_btn,
543
  ],
544
+ # We drive the "still running" indicator ourselves (spinner +
545
+ # busy button label, both toggled by `run_detection`'s two
546
+ # yields), so suppress Gradio's own progress overlay to avoid
547
+ # showing two different busy indicators at once.
548
+ show_progress="hidden",
549
  )
550
 
551
  expected_btn.click(