Mohibullah commited on
Commit
b8dddf7
·
1 Parent(s): e898f0a

Require review for low confidence medicine matches

Browse files
Files changed (1) hide show
  1. gradio_pharmacopilot_demo.py +44 -19
gradio_pharmacopilot_demo.py CHANGED
@@ -53,6 +53,7 @@ MODEL_ID = os.getenv("PHARMACOPILOT_MODEL_ID", "openbmb/MiniCPM-V-4_5")
53
  LIVE_GPU_OCR = os.getenv("PHARMACOPILOT_LIVE_GPU_OCR", "1").lower() not in {"0", "false", "no"}
54
  DEMO_OCR_TEXT = "Neuoxen"
55
  DEMO_PROMPT = "Read the handwritten medicine name in the image. Return only the text."
 
56
  OCR_MODEL = None
57
  OCR_TOKENIZER = None
58
 
@@ -277,7 +278,7 @@ def pipeline_html(stage: int = 0) -> str:
277
  ("Prescription", "captured"),
278
  ("MiniCPM OCR", "completed"),
279
  ("Retrieval Engine", "searched"),
280
- ("Nemotron Nano 9B", "validated"),
281
  ("Medicine Found", "ready"),
282
  ]
283
  cards = []
@@ -310,30 +311,51 @@ def medicine_details_html(
310
  display_name: str,
311
  confidence: int,
312
  ) -> str:
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  return f"""
314
  <div class="result-card">
315
  <h3>Prescription Details</h3>
316
  <dl class="details">
317
- <dt>Medicine</dt><dd>{display_name}</dd>
318
- <dt>Generic</dt><dd>{medicine.get('name', 'Unknown')}</dd>
319
- <dt>Strength</dt><dd>{first_strength(medicine.get('strength', ''))}</dd>
320
- <dt>Manufacturer</dt><dd>{medicine.get('manufacturer') or 'Not listed'}</dd>
321
  <dt>Confidence</dt><dd>{confidence}%</dd>
322
- <dt>Category</dt><dd>{medicine.get('category', 'General')}</dd>
323
- <dt>Price</dt><dd>PKR 145</dd>
324
  </dl>
325
  <div class="explain">
326
  <h4>AI Explanation</h4>
327
  <p><b>OCR detected:</b> "{ocr_text}"</p>
328
- <p><b>Retrieved:</b> {display_name}</p>
329
- <p><b>Validated using:</b> Nemotron Nano 9B</p>
330
- <p><b>Inventory:</b> Shelf {inventory['shelf']}, row {inventory['row']}</p>
331
  </div>
332
  </div>
333
  """
334
 
335
 
336
- def package_status_html(inventory: dict[str, Any]) -> str:
 
 
 
 
 
 
 
 
337
  status = "In Stock" if inventory.get("quantity", 0) > 0 else "Out of Stock"
338
  dot_class = "ok" if inventory.get("quantity", 0) > 0 else "bad"
339
  return f"""
@@ -360,11 +382,12 @@ def candidates_html(candidates: list[dict[str, Any]]) -> str:
360
  """
361
 
362
 
363
- def ocr_compare_html(medicine: dict[str, Any], ocr_text: str, display_name: str) -> str:
 
364
  return f"""
365
  <div class="compare-grid">
366
  <div><span>OCR Output</span><strong>{ocr_text}</strong></div>
367
- <div><span>AI Corrected</span><strong>{display_name}</strong></div>
368
  <div><span>Canonical</span><strong>{medicine['name']}</strong></div>
369
  </div>
370
  """
@@ -433,21 +456,23 @@ def analyze_prescription(image, progress=gr.Progress()):
433
 
434
  for pct, label in [
435
  (0.70, "Retrieval search over medicine aliases"),
436
- (0.88, "Nemotron validation"),
437
- (1.00, "Product identified"),
438
  ]:
439
  progress(pct, desc=label)
440
  time.sleep(0.25)
441
 
442
  medicine, candidates, display_name, confidence = find_medicine_from_ocr(ocr_text)
 
443
  inventory = get_inventory(medicine)
444
  image_path = resolve_asset_path(medicine.get("image_path"))
445
- package_image = str(image_path) if image_path else None
446
 
447
  state = {
448
  "medicine_id": medicine["id"],
449
  "medicine_name": medicine["name"],
450
  "display_name": display_name,
 
451
  "shelf": inventory["shelf"],
452
  "row": inventory["row"],
453
  }
@@ -458,12 +483,12 @@ def analyze_prescription(image, progress=gr.Progress()):
458
  pipeline_html(5),
459
  medicine_details_html(medicine, inventory, ocr_text, display_name, confidence),
460
  package_image,
461
- package_status_html(inventory),
462
  confidence_gauge(confidence),
463
  candidates_html(candidates),
464
- ocr_compare_html(medicine, ocr_text, display_name),
465
- gr.update(visible=True),
466
  gr.update(visible=True),
 
467
  state,
468
  )
469
 
 
53
  LIVE_GPU_OCR = os.getenv("PHARMACOPILOT_LIVE_GPU_OCR", "1").lower() not in {"0", "false", "no"}
54
  DEMO_OCR_TEXT = "Neuoxen"
55
  DEMO_PROMPT = "Read the handwritten medicine name in the image. Return only the text."
56
+ ACCEPTANCE_THRESHOLD = int(os.getenv("PHARMACOPILOT_ACCEPTANCE_THRESHOLD", "75"))
57
  OCR_MODEL = None
58
  OCR_TOKENIZER = None
59
 
 
278
  ("Prescription", "captured"),
279
  ("MiniCPM OCR", "completed"),
280
  ("Retrieval Engine", "searched"),
281
+ ("Validation", "checked"),
282
  ("Medicine Found", "ready"),
283
  ]
284
  cards = []
 
311
  display_name: str,
312
  confidence: int,
313
  ) -> str:
314
+ accepted = confidence >= ACCEPTANCE_THRESHOLD
315
+ medicine_label = display_name if accepted else "Needs pharmacist review"
316
+ generic_label = medicine.get("name", "Unknown") if accepted else f"Suggestion: {medicine.get('name', 'Unknown')}"
317
+ strength_label = first_strength(medicine.get("strength", "")) if accepted else "Not confirmed"
318
+ manufacturer_label = (medicine.get("manufacturer") or "Not listed") if accepted else "Not confirmed"
319
+ category_label = medicine.get("category", "General") if accepted else "Not confirmed"
320
+ price_label = "PKR 145" if accepted else "Not confirmed"
321
+ validation_label = "Pending review" if not accepted else "Accepted by retrieval threshold"
322
+ inventory_label = (
323
+ f"Shelf {inventory['shelf']}, row {inventory['row']}"
324
+ if accepted
325
+ else "Hidden until a confident medicine match is available"
326
+ )
327
  return f"""
328
  <div class="result-card">
329
  <h3>Prescription Details</h3>
330
  <dl class="details">
331
+ <dt>Medicine</dt><dd>{medicine_label}</dd>
332
+ <dt>Generic</dt><dd>{generic_label}</dd>
333
+ <dt>Strength</dt><dd>{strength_label}</dd>
334
+ <dt>Manufacturer</dt><dd>{manufacturer_label}</dd>
335
  <dt>Confidence</dt><dd>{confidence}%</dd>
336
+ <dt>Category</dt><dd>{category_label}</dd>
337
+ <dt>Price</dt><dd>{price_label}</dd>
338
  </dl>
339
  <div class="explain">
340
  <h4>AI Explanation</h4>
341
  <p><b>OCR detected:</b> "{ocr_text}"</p>
342
+ <p><b>Retrieved:</b> {display_name} ({medicine.get('name', 'Unknown')})</p>
343
+ <p><b>Validation:</b> {validation_label}</p>
344
+ <p><b>Inventory:</b> {inventory_label}</p>
345
  </div>
346
  </div>
347
  """
348
 
349
 
350
+ def package_status_html(inventory: dict[str, Any], accepted: bool = True) -> str:
351
+ if not accepted:
352
+ return """
353
+ <div class="stock-card">
354
+ <div><span>Available Stock</span><strong>-</strong></div>
355
+ <div><span>Status</span><strong class="bad">Needs Review</strong></div>
356
+ <div><span>Shelf</span><strong>Hidden</strong></div>
357
+ </div>
358
+ """
359
  status = "In Stock" if inventory.get("quantity", 0) > 0 else "Out of Stock"
360
  dot_class = "ok" if inventory.get("quantity", 0) > 0 else "bad"
361
  return f"""
 
382
  """
383
 
384
 
385
+ def ocr_compare_html(medicine: dict[str, Any], ocr_text: str, display_name: str, confidence: int) -> str:
386
+ corrected = display_name if confidence >= ACCEPTANCE_THRESHOLD else f"Needs review: {display_name}"
387
  return f"""
388
  <div class="compare-grid">
389
  <div><span>OCR Output</span><strong>{ocr_text}</strong></div>
390
+ <div><span>AI Corrected</span><strong>{corrected}</strong></div>
391
  <div><span>Canonical</span><strong>{medicine['name']}</strong></div>
392
  </div>
393
  """
 
456
 
457
  for pct, label in [
458
  (0.70, "Retrieval search over medicine aliases"),
459
+ (0.88, "Validation check"),
460
+ (1.00, "Result prepared"),
461
  ]:
462
  progress(pct, desc=label)
463
  time.sleep(0.25)
464
 
465
  medicine, candidates, display_name, confidence = find_medicine_from_ocr(ocr_text)
466
+ accepted = confidence >= ACCEPTANCE_THRESHOLD
467
  inventory = get_inventory(medicine)
468
  image_path = resolve_asset_path(medicine.get("image_path"))
469
+ package_image = str(image_path) if image_path and accepted else None
470
 
471
  state = {
472
  "medicine_id": medicine["id"],
473
  "medicine_name": medicine["name"],
474
  "display_name": display_name,
475
+ "accepted": accepted,
476
  "shelf": inventory["shelf"],
477
  "row": inventory["row"],
478
  }
 
483
  pipeline_html(5),
484
  medicine_details_html(medicine, inventory, ocr_text, display_name, confidence),
485
  package_image,
486
+ package_status_html(inventory, accepted),
487
  confidence_gauge(confidence),
488
  candidates_html(candidates),
489
+ ocr_compare_html(medicine, ocr_text, display_name, confidence),
 
490
  gr.update(visible=True),
491
+ gr.update(visible=True, interactive=accepted),
492
  state,
493
  )
494