medinsight / app.py
Muhammadidrees's picture
Create app.py
dad7343 verified
raw
history blame
9.13 kB
from fastapi import FastAPI
from pydantic import BaseModel, Field
from dotenv import load_dotenv
import google.generativeai as genai
import os
import re
import gradio as gr
from typing import Dict, Any, Union, List
# ---------------- Initialize ----------------
app = FastAPI(title="LLM Model API + Gradio UI", version="4.0")
# βœ… Fetch Gemini API Key
GEMINI_API_KEY = "AIzaSyC0XU6yLCILZFUVhKoIcqoy2k5qwQmnDsc"
if not GEMINI_API_KEY:
raise ValueError("❌ GEMINI_API_KEY not found. Please set it in your .env file.")
genai.configure(api_key=GEMINI_API_KEY)
MODEL_ID = "gemini-2.5-flash"
# ---------------- Schema ----------------
class BiomarkerRequest(BaseModel):
albumin: float = Field(default=3.2)
creatinine: float = Field(default=1.4)
glucose: float = Field(default=145)
crp: float = Field(default=12.0)
mcv: float = Field(default=88)
rdw: float = Field(default=15.5)
alp: float = Field(default=120)
wbc: float = Field(default=11.8)
lymphocytes: float = Field(default=20)
hb: float = Field(default=13.0)
pv: float = Field(default=2.1)
age: int = Field(default=52)
gender: str = Field(default="female")
height: float = Field(default=165)
weight: float = Field(default=70)
# ---------------- Utilities ----------------
def clean_json(data: Union[Dict, List, str]) -> Union[Dict, List, str]:
if isinstance(data, str):
text = re.sub(r"-{3,}", "", data)
text = re.sub(r"\s+", " ", text)
text = text.strip(" -\n\t\r")
return text
elif isinstance(data, list):
return [clean_json(i) for i in data if i and clean_json(i)]
elif isinstance(data, dict):
return {k.strip(): clean_json(v) for k, v in data.items()}
return data
# ---------------- Parser ----------------
def parse_medical_report(text: str):
def clean_line(line: str) -> str:
return re.sub(r"[\-\*\u2022]+\s*", "", line.strip())
def parse_bold_entities(block: str) -> Dict[str, str]:
entities = {}
pattern = re.compile(r"\*\*(.*?)\*\*(.*?)(?=\*\*|###|$)", re.S)
for match in pattern.finditer(block):
key = match.group(1).strip().strip(":")
val = match.group(2).strip().replace("\n", " ")
val = re.sub(r"\s+", " ", val)
if key:
entities[key] = val
return entities
data = {
"executive_summary": {"top_priorities": [], "key_strengths": []},
"system_analysis": {},
"personalized_action_plan": {},
"interaction_alerts": [],
"normal_ranges": {},
"biomarker_table": []
}
exec_match = re.search(r"###\s*Executive Summary(.*?)(?=###|$)", text, re.S | re.I)
if exec_match:
block = exec_match.group(1)
priorities = re.findall(r"\d+\.\s*(.*?)\n", block)
if priorities:
data["executive_summary"]["top_priorities"] = [clean_line(p) for p in priorities]
strengths_match = re.search(r"\*\*Key Strengths:\*\*(.*)", block, re.S)
if strengths_match:
strengths_text = strengths_match.group(1)
strengths = [clean_line(s) for s in strengths_text.splitlines() if clean_line(s)]
data["executive_summary"]["key_strengths"] = strengths
sys_match = re.search(r"###\s*System[- ]Specific Analysis(.*?)(?=###|$)", text, re.S | re.I)
if sys_match:
sys_block = sys_match.group(1)
data["system_analysis"] = parse_bold_entities(sys_block)
plan_match = re.search(r"###\s*Personalized Action Plan(.*?)(?=###|$)", text, re.S | re.I)
if plan_match:
plan_block = plan_match.group(1)
data["personalized_action_plan"] = parse_bold_entities(plan_block)
alerts_match = re.search(r"###\s*Interaction Alerts(.*?)(?=###|$)", text, re.S | re.I)
if alerts_match:
alerts_block = alerts_match.group(1)
alerts = [clean_line(a) for a in alerts_block.splitlines() if clean_line(a)]
data["interaction_alerts"] = alerts
normal_match = re.search(r"###\s*Normal Ranges(.*?)(?=###|$)", text, re.S | re.I)
if normal_match:
normal_block = normal_match.group(1)
for match in re.findall(r"-\s*([^:]+):\s*([^\n]+)", normal_block):
biomarker, rng = match
data["normal_ranges"][biomarker.strip()] = rng.strip()
table_match = re.search(r"###\s*Tabular Mapping(.*)", text, re.S | re.I)
if table_match:
table_block = table_match.group(1)
table_pattern = r"\|\s*([^|]+)\s*\|\s*([^|]+)\s*\|\s*([^|]+)\s*\|\s*([^|]+)\s*\|\s*([^|]+)\s*\|"
for biomarker, value, status, insight, ref in re.findall(table_pattern, table_block):
if not any([biomarker, value, status, insight, ref]):
continue
data["biomarker_table"].append({
"biomarker": biomarker.strip(),
"value": value.strip(),
"status": status.strip(),
"insight": insight.strip(),
"reference_range": ref.strip(),
})
return data
# ---------------- Prediction Core ----------------
def generate_report(data: BiomarkerRequest) -> str:
"""Main logic β€” uses Gemini to generate markdown medical report"""
prompt = """
You are an advanced **Medical Insight Generation AI** trained to analyze **biomarkers and lab results**.
⚠️ IMPORTANT β€” OUTPUT FORMAT INSTRUCTIONS:
Return your report in this strict markdown structure.
------------------------------
### Executive Summary
**Top 3 Health Priorities:**
1. ...
2. ...
3. ...
**Key Strengths:**
- ...
- ...
------------------------------
### System-Specific Analysis
**Cardiovascular System**
Status: Normal. Explanation: ...
**Liver Function**
Status: Elevated ALP. Explanation: ...
------------------------------
### Personalized Action Plan
**Nutrition:** ...
**Lifestyle:** ...
**Testing:** ...
**Medical Consultation:** ...
------------------------------
### Interaction Alerts
- ...
- ...
------------------------------
### Normal Ranges
- Albumin: 3.5–5.0 g/dL
- Creatinine: 0.7–1.3 mg/dL
- Glucose: 70–100 mg/dL
- CRP: 0–10 mg/L
- MCV: 80–100 fL
- RDW: 11.5–14.5 %
- ALP: 44–147 U/L
- WBC: 4.0–10.0 Γ—10^3/ΞΌL
- Lymphocytes: 20–40 %
- Hemoglobin: 13–17 g/dL
- PV: 2500–3000 mL
------------------------------
### Tabular Mapping
| Biomarker | Value | Status | Insight | Reference Range |
| Albumin | X | Normal | ... | 3.5–5.0 g/dL |
| Creatinine | X | High | ... | 0.7–1.3 mg/dL |
| Glucose | X | ... | ... | 70–100 mg/dL |
------------------------------
"""
user_message = f"""
Patient Info:
- Age: {data.age}
- Gender: {data.gender}
- Height: {data.height} cm
- Weight: {data.weight} kg
Biomarkers:
- Albumin: {data.albumin} g/dL
- Creatinine: {data.creatinine} mg/dL
- Glucose: {data.glucose} mg/dL
- CRP: {data.crp} mg/L
- MCV: {data.mcv} fL
- RDW: {data.rdw} %
- ALP: {data.alp} U/L
- WBC: {data.wbc} Γ—10^3/ΞΌL
- Lymphocytes: {data.lymphocytes} %
- Hemoglobin: {data.hb} g/dL
- Plasma Volume (PV): {data.pv} mL
"""
model = genai.GenerativeModel(MODEL_ID)
response = model.generate_content(f"{prompt}\n\n{user_message}")
if not response or not getattr(response, "text", None):
return "⚠️ Gemini returned an empty response."
return response.text.strip()
# ---------------- Gradio Interface ----------------
def gradio_interface(albumin, creatinine, glucose, crp, mcv, rdw, alp, wbc,
lymphocytes, hb, pv, age, gender, height, weight):
req = BiomarkerRequest(
albumin=albumin, creatinine=creatinine, glucose=glucose, crp=crp,
mcv=mcv, rdw=rdw, alp=alp, wbc=wbc, lymphocytes=lymphocytes,
hb=hb, pv=pv, age=int(age), gender=gender, height=height, weight=weight
)
report = generate_report(req)
return report
iface = gr.Interface(
fn=gradio_interface,
inputs=[
gr.Number(label="Albumin (g/dL)", value=3.2),
gr.Number(label="Creatinine (mg/dL)", value=1.4),
gr.Number(label="Glucose (mg/dL)", value=145),
gr.Number(label="CRP (mg/L)", value=12.0),
gr.Number(label="MCV (fL)", value=88),
gr.Number(label="RDW (%)", value=15.5),
gr.Number(label="ALP (U/L)", value=120),
gr.Number(label="WBC (Γ—10Β³/ΞΌL)", value=11.8),
gr.Number(label="Lymphocytes (%)", value=20),
gr.Number(label="Hemoglobin (g/dL)", value=13.0),
gr.Number(label="Plasma Volume (L)", value=2.1),
gr.Number(label="Age (years)", value=52),
gr.Radio(["male", "female"], label="Gender", value="female"),
gr.Number(label="Height (cm)", value=165),
gr.Number(label="Weight (kg)", value=70)
],
outputs=gr.Markdown(label="🩺 AI Medical Report"),
title="LLM Biomarker Analyzer",
description="Enter your biomarker and demographic data to generate a detailed AI-based medical report (Gemini-powered).",
theme="soft",
allow_flagging="never"
)
# ---------------- Launch ----------------
if __name__ == "__main__":
iface.launch(server_name="0.0.0.0", server_port=7860)