Update app.py
Browse files
app.py
CHANGED
|
@@ -677,6 +677,99 @@ async def get_history(user_id: str):
|
|
| 677 |
history = [s for s in screenings_db.values() if s.get("user_id") == user_id]
|
| 678 |
return {"screenings": history}
|
| 679 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 680 |
# -----------------------
|
| 681 |
# Main processing pipeline
|
| 682 |
# -----------------------
|
|
|
|
| 677 |
history = [s for s in screenings_db.values() if s.get("user_id") == user_id]
|
| 678 |
return {"screenings": history}
|
| 679 |
|
| 680 |
+
# -----------------------
|
| 681 |
+
# Immediate VLM -> LLM routes (return vitals in one call)
|
| 682 |
+
# -----------------------
|
| 683 |
+
@app.post("/api/v1/get-vitals")
|
| 684 |
+
async def get_vitals_from_upload(
|
| 685 |
+
face_image: UploadFile = File(...),
|
| 686 |
+
eye_image: UploadFile = File(...)
|
| 687 |
+
):
|
| 688 |
+
"""
|
| 689 |
+
Run VLM -> LLM pipeline synchronously (but off the event loop) and return:
|
| 690 |
+
{ vlm_features, vlm_raw, structured_risk }
|
| 691 |
+
"""
|
| 692 |
+
if not GRADIO_AVAILABLE:
|
| 693 |
+
raise HTTPException(status_code=500, detail="VLM/LLM client not available in this deployment.")
|
| 694 |
+
|
| 695 |
+
# save files to a temp directory
|
| 696 |
+
try:
|
| 697 |
+
tmp_dir = "/tmp/elderly_healthwatch"
|
| 698 |
+
os.makedirs(tmp_dir, exist_ok=True)
|
| 699 |
+
uid = str(uuid.uuid4())
|
| 700 |
+
face_path = os.path.join(tmp_dir, f"{uid}_face.jpg")
|
| 701 |
+
eye_path = os.path.join(tmp_dir, f"{uid}_eye.jpg")
|
| 702 |
+
face_bytes = await face_image.read()
|
| 703 |
+
eye_bytes = await eye_image.read()
|
| 704 |
+
with open(face_path, "wb") as f:
|
| 705 |
+
f.write(face_bytes)
|
| 706 |
+
with open(eye_path, "wb") as f:
|
| 707 |
+
f.write(eye_bytes)
|
| 708 |
+
except Exception as e:
|
| 709 |
+
logger.exception("Failed saving uploaded images")
|
| 710 |
+
raise HTTPException(status_code=500, detail=f"Failed saving images: {e}")
|
| 711 |
+
|
| 712 |
+
try:
|
| 713 |
+
# Run VLM (off the event loop)
|
| 714 |
+
vlm_features, vlm_raw = await asyncio.to_thread(run_vlm_and_get_features, face_path, eye_path)
|
| 715 |
+
|
| 716 |
+
# Prefer sending raw vlm text to LLM (same behavior as process_screening)
|
| 717 |
+
llm_input = vlm_raw if vlm_raw else (vlm_features if vlm_features else "{}")
|
| 718 |
+
|
| 719 |
+
# Run LLM (off the event loop)
|
| 720 |
+
structured_risk = await asyncio.to_thread(run_llm_on_vlm, llm_input)
|
| 721 |
+
|
| 722 |
+
# Return merged result
|
| 723 |
+
return {
|
| 724 |
+
"vlm_features": vlm_features,
|
| 725 |
+
"vlm_raw": vlm_raw,
|
| 726 |
+
"structured_risk": structured_risk
|
| 727 |
+
}
|
| 728 |
+
except Exception as e:
|
| 729 |
+
logger.exception("get_vitals_from_upload pipeline failed")
|
| 730 |
+
raise HTTPException(status_code=500, detail=f"Pipeline failed: {e}")
|
| 731 |
+
|
| 732 |
+
@app.post("/api/v1/get-vitals/{screening_id}")
|
| 733 |
+
async def get_vitals_for_screening(screening_id: str):
|
| 734 |
+
"""
|
| 735 |
+
Re-run VLM->LLM on images already stored for `screening_id` in screenings_db.
|
| 736 |
+
Useful for re-processing or debugging.
|
| 737 |
+
"""
|
| 738 |
+
if screening_id not in screenings_db:
|
| 739 |
+
raise HTTPException(status_code=404, detail="Screening not found")
|
| 740 |
+
|
| 741 |
+
entry = screenings_db[screening_id]
|
| 742 |
+
face_path = entry.get("face_image_path")
|
| 743 |
+
eye_path = entry.get("eye_image_path")
|
| 744 |
+
if not (face_path and os.path.exists(face_path) and eye_path and os.path.exists(eye_path)):
|
| 745 |
+
raise HTTPException(status_code=400, detail="Stored images missing for this screening")
|
| 746 |
+
|
| 747 |
+
try:
|
| 748 |
+
# Run VLM off the event loop
|
| 749 |
+
vlm_features, vlm_raw = await asyncio.to_thread(run_vlm_and_get_features, face_path, eye_path)
|
| 750 |
+
|
| 751 |
+
llm_input = vlm_raw if vlm_raw else (vlm_features if vlm_features else "{}")
|
| 752 |
+
structured_risk = await asyncio.to_thread(run_llm_on_vlm, llm_input)
|
| 753 |
+
|
| 754 |
+
# Optionally store this run's outputs back into the DB for inspection
|
| 755 |
+
entry.setdefault("ai_results", {})
|
| 756 |
+
entry["ai_results"].update({
|
| 757 |
+
"vlm_features": vlm_features,
|
| 758 |
+
"vlm_raw": vlm_raw,
|
| 759 |
+
"structured_risk": structured_risk,
|
| 760 |
+
"last_vitals_run": datetime.utcnow().isoformat() + "Z"
|
| 761 |
+
})
|
| 762 |
+
|
| 763 |
+
return {
|
| 764 |
+
"screening_id": screening_id,
|
| 765 |
+
"vlm_features": vlm_features,
|
| 766 |
+
"vlm_raw": vlm_raw,
|
| 767 |
+
"structured_risk": structured_risk
|
| 768 |
+
}
|
| 769 |
+
except Exception as e:
|
| 770 |
+
logger.exception("get_vitals_for_screening pipeline failed")
|
| 771 |
+
raise HTTPException(status_code=500, detail=f"Pipeline failed: {e}")
|
| 772 |
+
|
| 773 |
# -----------------------
|
| 774 |
# Main processing pipeline
|
| 775 |
# -----------------------
|