Update app.py
Browse files
app.py
CHANGED
|
@@ -13,7 +13,7 @@ Notes:
|
|
| 13 |
- This version includes a robust regex-based extractor that finds the outermost {...} block
|
| 14 |
in the LLM output, extracts numeric values for the required keys, and always returns
|
| 15 |
numeric defaults (no NaN) so frontends will not receive null/None for numeric fields.
|
| 16 |
-
- This variant logs raw LLM
|
| 17 |
"""
|
| 18 |
|
| 19 |
import io
|
|
@@ -276,7 +276,8 @@ def run_vlm_and_get_features(face_path: str, eye_path: str, prompt: Optional[str
|
|
| 276 |
(parsed_features_dict_or_None, raw_text_response_str)
|
| 277 |
|
| 278 |
We attempt to parse JSON as before, but always return the original raw text so it can be
|
| 279 |
-
forwarded verbatim to the LLM if desired.
|
|
|
|
| 280 |
"""
|
| 281 |
prompt = prompt or DEFAULT_VLM_PROMPT
|
| 282 |
if not os.path.exists(face_path) or not os.path.exists(eye_path):
|
|
@@ -312,6 +313,12 @@ def run_vlm_and_get_features(face_path: str, eye_path: str, prompt: Optional[str
|
|
| 312 |
if not text_out:
|
| 313 |
text_out = json.dumps(out)
|
| 314 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 315 |
# Try to parse JSON but remember raw text always
|
| 316 |
parsed_features = None
|
| 317 |
try:
|
|
@@ -333,6 +340,15 @@ def run_vlm_and_get_features(face_path: str, eye_path: str, prompt: Optional[str
|
|
| 333 |
except Exception:
|
| 334 |
parsed_features = None
|
| 335 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 336 |
return parsed_features, text_out
|
| 337 |
|
| 338 |
# -----------------------
|
|
@@ -735,6 +751,10 @@ async def get_vitals_from_upload(
|
|
| 735 |
# Run VLM (off the event loop)
|
| 736 |
vlm_features, vlm_raw = await asyncio.to_thread(run_vlm_and_get_features, face_path, eye_path)
|
| 737 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 738 |
# Prefer sending raw vlm text to LLM (same behavior as process_screening)
|
| 739 |
llm_input = vlm_raw if vlm_raw else (vlm_features if vlm_features else "{}")
|
| 740 |
|
|
@@ -770,6 +790,10 @@ async def get_vitals_for_screening(screening_id: str):
|
|
| 770 |
# Run VLM off the event loop
|
| 771 |
vlm_features, vlm_raw = await asyncio.to_thread(run_vlm_and_get_features, face_path, eye_path)
|
| 772 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 773 |
llm_input = vlm_raw if vlm_raw else (vlm_features if vlm_features else "{}")
|
| 774 |
structured_risk = await asyncio.to_thread(run_llm_on_vlm, llm_input)
|
| 775 |
|
|
@@ -901,6 +925,10 @@ async def process_screening(screening_id: str):
|
|
| 901 |
vlm_features = None
|
| 902 |
vlm_raw = None
|
| 903 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 904 |
# --------------------------
|
| 905 |
# RUN LLM on vlm_raw (preferred) or vlm_features -> structured risk JSON
|
| 906 |
# --------------------------
|
|
|
|
| 13 |
- This version includes a robust regex-based extractor that finds the outermost {...} block
|
| 14 |
in the LLM output, extracts numeric values for the required keys, and always returns
|
| 15 |
numeric defaults (no NaN) so frontends will not receive null/None for numeric fields.
|
| 16 |
+
- This variant logs raw VLM & LLM outputs and the parsed JSON using Python logging.
|
| 17 |
"""
|
| 18 |
|
| 19 |
import io
|
|
|
|
| 276 |
(parsed_features_dict_or_None, raw_text_response_str)
|
| 277 |
|
| 278 |
We attempt to parse JSON as before, but always return the original raw text so it can be
|
| 279 |
+
forwarded verbatim to the LLM if desired. This function now logs the raw VLM output and
|
| 280 |
+
the parsed features for debugging.
|
| 281 |
"""
|
| 282 |
prompt = prompt or DEFAULT_VLM_PROMPT
|
| 283 |
if not os.path.exists(face_path) or not os.path.exists(eye_path):
|
|
|
|
| 313 |
if not text_out:
|
| 314 |
text_out = json.dumps(out)
|
| 315 |
|
| 316 |
+
# Log raw VLM output for debugging/auditing
|
| 317 |
+
try:
|
| 318 |
+
logger.info("VLM raw output:\n%s", text_out)
|
| 319 |
+
except Exception:
|
| 320 |
+
logger.info("VLM raw output (could not pretty print)")
|
| 321 |
+
|
| 322 |
# Try to parse JSON but remember raw text always
|
| 323 |
parsed_features = None
|
| 324 |
try:
|
|
|
|
| 340 |
except Exception:
|
| 341 |
parsed_features = None
|
| 342 |
|
| 343 |
+
# Log parsed features if available
|
| 344 |
+
if parsed_features is not None:
|
| 345 |
+
try:
|
| 346 |
+
logger.info("VLM parsed features:\n%s", json.dumps(parsed_features, indent=2, ensure_ascii=False))
|
| 347 |
+
except Exception:
|
| 348 |
+
logger.info("VLM parsed features (raw): %s", str(parsed_features))
|
| 349 |
+
else:
|
| 350 |
+
logger.info("VLM parsed features: None (raw output kept)")
|
| 351 |
+
|
| 352 |
return parsed_features, text_out
|
| 353 |
|
| 354 |
# -----------------------
|
|
|
|
| 751 |
# Run VLM (off the event loop)
|
| 752 |
vlm_features, vlm_raw = await asyncio.to_thread(run_vlm_and_get_features, face_path, eye_path)
|
| 753 |
|
| 754 |
+
# Log VLM outputs (already logged inside run_vlm..., but log again with context)
|
| 755 |
+
logger.info("get_vitals_from_upload - VLM raw (snippet): %s", (vlm_raw[:100] + "...") if vlm_raw else "None")
|
| 756 |
+
logger.info("get_vitals_from_upload - VLM parsed features: %s", vlm_features if vlm_features is not None else "None")
|
| 757 |
+
|
| 758 |
# Prefer sending raw vlm text to LLM (same behavior as process_screening)
|
| 759 |
llm_input = vlm_raw if vlm_raw else (vlm_features if vlm_features else "{}")
|
| 760 |
|
|
|
|
| 790 |
# Run VLM off the event loop
|
| 791 |
vlm_features, vlm_raw = await asyncio.to_thread(run_vlm_and_get_features, face_path, eye_path)
|
| 792 |
|
| 793 |
+
# Log VLM outputs
|
| 794 |
+
logger.info("get_vitals_for_screening(%s) - VLM raw (snippet): %s", screening_id, (vlm_raw[:100] + "...") if vlm_raw else "None")
|
| 795 |
+
logger.info("get_vitals_for_screening(%s) - VLM parsed features: %s", screening_id, vlm_features if vlm_features is not None else "None")
|
| 796 |
+
|
| 797 |
llm_input = vlm_raw if vlm_raw else (vlm_features if vlm_features else "{}")
|
| 798 |
structured_risk = await asyncio.to_thread(run_llm_on_vlm, llm_input)
|
| 799 |
|
|
|
|
| 925 |
vlm_features = None
|
| 926 |
vlm_raw = None
|
| 927 |
|
| 928 |
+
# Log VLM outputs in pipeline context
|
| 929 |
+
logger.info("process_screening(%s) - VLM raw (snippet): %s", screening_id, (vlm_raw[:100] + "...") if vlm_raw else "None")
|
| 930 |
+
logger.info("process_screening(%s) - VLM parsed features: %s", screening_id, vlm_features if vlm_features is not None else "None")
|
| 931 |
+
|
| 932 |
# --------------------------
|
| 933 |
# RUN LLM on vlm_raw (preferred) or vlm_features -> structured risk JSON
|
| 934 |
# --------------------------
|