Muhammadidrees commited on
Commit
db6c616
·
verified ·
1 Parent(s): c4cd035

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +97 -171
app.py CHANGED
@@ -1,9 +1,8 @@
1
- # app.py
2
  import gradio as gr
3
  from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
4
  import os
5
  import torch
6
- import re
7
 
8
  MODEL_ID = "Muhammadidrees/MedicalInsights"
9
 
@@ -12,151 +11,93 @@ MODEL_ID = "Muhammadidrees/MedicalInsights"
12
  # -----------------------
13
  tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
14
 
15
- # Try a few loading strategies so this works on GPU or CPU Spaces
16
  try:
17
- # Preferred: let HF decide device placement (works for GPU-enabled Spaces)
18
  model = AutoModelForCausalLM.from_pretrained(MODEL_ID)
19
  except Exception:
20
- # Fallback: force CPU (slower but safe)
21
- model = AutoModelForCausalLM.from_pretrained(MODEL_ID, torch_dtype=torch.float32, low_cpu_mem_usage=True)
 
 
 
 
 
 
 
 
22
 
23
- # Create pipeline
24
- pipe = pipeline("text-generation", model=model, tokenizer=tokenizer, device=0 if torch.cuda.is_available() else -1)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  # -----------------------
27
- # Helper: robust section splitter
28
  # -----------------------
29
  def split_report(text):
30
- """
31
- Split model output into left (sections 1-4) and right (sections 5-6).
32
- Accepts various markers for robustness.
33
- """
34
- # Normalize whitespace
35
  text = text.strip()
36
- # Common markers that indicate tabular/insights section
37
- markers = [
38
- "5. Tabular Mapping",
39
- "5. Tabular",
40
- "Tabular Mapping",
41
- "Tabular & AI Insights",
42
- "📊 Tabular",
43
- "## 5",
44
- ]
45
- # Find earliest marker occurrence
46
  idx = None
47
  for m in markers:
48
  pos = text.find(m)
49
- if pos != -1:
50
- if idx is None or pos < idx:
51
- idx = pos
52
  if idx is None:
53
- # fallback: try splitting at "Enhanced AI Insights" or "Enhanced AI"
54
- fallback = text.find("Enhanced AI Insights")
55
- if fallback == -1:
56
- fallback = text.find("Enhanced AI")
57
- idx = fallback if fallback != -1 else None
58
-
59
- if idx is None:
60
- # couldn't find a split marker -> put everything in left
61
  return text, ""
62
- left = text[:idx].strip()
63
- right = text[idx:].strip()
64
- return left, right
65
 
66
  # -----------------------
67
- # The analyze function
68
  # -----------------------
69
- def analyze(
70
- albumin, creatinine, glucose, crp, mcv, rdw, alp,
71
- wbc, lymph, age, gender, height, weight
72
- ):
73
- # Validate/constrain inputs
74
- try:
75
- age = int(age)
76
- except Exception:
77
- age = age
78
  try:
79
  height = float(height)
80
  weight = float(weight)
81
- bmi = round(weight / ((height / 100) ** 2), 2) if height > 0 else "N/A"
82
  except Exception:
83
  bmi = "N/A"
84
 
85
- system_prompt = (
86
- "You are 'Medical Insights AI', a trusted medical assistant.\n"
87
- "You analyze patient demographics and biomarkers using two knowledge sources:\n"
88
- "1. Internal medical expertise from pretraining.\n"
89
- "2. The Knowledge Base of biomarker reference ranges provided below.\n\n"
90
-
91
- "KNOWLEDGE BASE (STRICT PRIORITY):\n"
92
- "- Albumin: 3.5 5.5 g/dL (Adults)\n"
93
- "- Creatinine: 0.7 1.3 mg/dL (Adult Male), 0.6 – 1.1 mg/dL (Adult Female)\n"
94
- "- Glucose (Fasting): 70 – 100 mg/dL (Adults)\n"
95
- "- CRP: 0.3 10 mg/L (Adults)\n"
96
- "- MCV: 80 – 100 fL (Adults)\n"
97
- "- RDW: 11 – 15% (Adults)\n"
98
- "- WBC: 4,000 11,000 /µL (Adults)\n"
99
- "- Lymphocytes: 20 – 40% (Adults)\n"
100
- "- ALP: 44 – 147 U/L (Adults)\n"
101
-
102
- "RULES:\n"
103
- "- For each biomarker, compare the patient’s value strictly against the ranges above.\n"
104
- "- Always classify as Low / Normal / High.\n"
105
- "- Provide 1–3 lines of interpretation (clinical meaning).\n"
106
- "- Cite the source: Knowledge Base or Internal Knowledge.\n"
107
- "- Do NOT invent biomarkers or repeat sections.\n"
108
- "- Always integrate biomarker findings into system-level context.\n"
109
- "- Provide actionable but non-prescriptive recommendations (nutrition, lifestyle, monitoring).\n\n"
110
-
111
- "OUTPUT FORMAT (strict, mandatory):\n\n"
112
-
113
- "*Executive Summary*\n"
114
- "- Top Priority Issues: [List actual abnormalities, else 'None detected']\n"
115
- "- Key Strengths: [Highlight normal/protective findings]\n\n"
116
-
117
- "*Biomarker-by-Biomarker Analysis*\n"
118
- "For each biomarker provided, follow this structure:\n"
119
- "1. *Biomarker:* [Name]\n"
120
- " - *Patient Value:* [Value + Units]\n"
121
- " - *Reference Range:* [Range + Units + Population]\n"
122
- " - *Status:* [Low / Normal / High]\n"
123
- " - *Interpretation:* [Clear clinical meaning]\n"
124
- " - *Source:* [Knowledge Base / Internal Knowledge]\n\n"
125
-
126
- "*System-Specific Analysis*\n"
127
- "- Blood Health (MCV, RDW, WBC, Lymphocytes)\n"
128
- "- Protein & Liver Health (Albumin, ALP)\n"
129
- "- Kidney Health (Creatinine)\n"
130
- "- Metabolic Health (Glucose, CRP)\n"
131
- "- Anthropometrics (Age, Height, Weight, BMI)\n"
132
- "- Other Systems: 'Not available from current biomarkers.'\n\n"
133
-
134
- "*Personalized Action Plan*\n"
135
- "- Medical: [Follow-up tests if abnormalities present]\n"
136
- "- Nutrition: [Dietary guidance based on results, e.g., protein intake if albumin low, anti-inflammatory foods if CRP high]\n"
137
- "- Lifestyle: [Exercise, hydration, stress, sleep tailored to BMI & biomarkers]\n"
138
- "- Testing: [Mention ferritin, B12, folate, etc. ONLY if abnormalities suggest it]\n\n"
139
-
140
- "*Interaction Alerts*\n"
141
- "- Highlight important biomarker interactions (e.g., MCV+RDW for anemia, CRP+WBC for inflammation).\n\n"
142
-
143
- "*Tabular Mapping*\n"
144
- "- Markdown table with columns: | Biomarker |Reference range | Value | Status | AI-Inferred Insight |\n"
145
- "- Include all available biomarkers in order.\n\n"
146
-
147
- "*Enhanced AI Insights & Longitudinal Risk*\n"
148
-
149
-
150
- "STYLE:\n"
151
- "- Professional, concise, medically accurate.\n"
152
- "- Never output free-floating paragraphs, always follow structured format.\n"
153
- )
154
-
155
-
156
-
157
-
158
-
159
-
160
  patient_input = (
161
  f"Patient Profile:\n"
162
  f"- Age: {age}\n"
@@ -164,60 +105,46 @@ def analyze(
164
  f"- Height: {height} cm\n"
165
  f"- Weight: {weight} kg\n"
166
  f"- BMI: {bmi}\n\n"
167
- "Biomarker Results:\n"
168
- f"- Albumin: {albumin} g/dL\n"
169
- f"- Creatinine: {creatinine} mg/dL\n"
170
- f"- Glucose: {glucose} mg/dL\n"
171
- f"- CRP: {crp} mg/L\n"
172
- f"- MCV: {mcv} fL\n"
173
- f"- RDW: {rdw} %\n"
174
- f"- ALP: {alp} U/L\n"
175
- f"- WBC: {wbc} K/uL\n"
176
- f"- Lymphocytes: {lymph} %\n"
 
 
 
 
 
 
 
 
 
 
 
 
177
  )
178
 
179
  prompt = system_prompt + "\n" + patient_input
180
 
181
- # Generate
182
- # Keep generation parameters conservative for Spaces
183
  gen = pipe(
184
  prompt,
185
- max_new_tokens=2500, # enough for executive summary
186
- do_sample=False, # greedy decoding (no randomness)
187
- temperature=0.0, # fully deterministic
188
- top_p=1.0, # consider full probability mass
189
- repetition_penalty=1.0, # neutral repetition handling
190
- early_stopping=True, # stop cleanly once done
191
- return_full_text=False # only assistant output
192
  )
193
 
194
-
195
- # Extract generated text
196
- generated = gen[0].get("generated_text") or gen[0].get("text") or str(gen[0])
197
- generated = generated.strip()
198
-
199
- # Clean: some models repeat prompt — attempt to strip prompt if present
200
- # Remove leading prompt echo if it appears
201
- if patient_input.strip() in generated:
202
- generated = generated.split(patient_input.strip())[-1].strip()
203
- # Also remove repeated instructions
204
- if system_prompt.strip() in generated:
205
- generated = generated.split(system_prompt.strip())[-1].strip()
206
-
207
- # Split into left/right panels
208
- left_md, right_md = split_report(generated)
209
-
210
- # If the model output is empty or too short, return a helpful fallback
211
- if len(left_md) < 50 and len(right_md) < 50:
212
- fallback = (
213
- "⚠️ The model returned an unexpectedly short response. Try re-running the report.\n\n"
214
- "**Patient Profile:**\n" + patient_input
215
- )
216
- return fallback, ""
217
- return left_md, right_md
218
 
219
  # -----------------------
220
- # Build Gradio app
221
  # -----------------------
222
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
223
  gr.Markdown("# 🏥 AI Medical Biomarker Dashboard")
@@ -261,7 +188,6 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
261
  outputs=[left_output, right_output]
262
  )
263
 
264
-
265
- # Launch (HF Spaces expects this pattern)
266
  if __name__ == "__main__":
267
- demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
 
 
1
+ ```python
2
  import gradio as gr
3
  from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
4
  import os
5
  import torch
 
6
 
7
  MODEL_ID = "Muhammadidrees/MedicalInsights"
8
 
 
11
  # -----------------------
12
  tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
13
 
 
14
  try:
 
15
  model = AutoModelForCausalLM.from_pretrained(MODEL_ID)
16
  except Exception:
17
+ model = AutoModelForCausalLM.from_pretrained(
18
+ MODEL_ID, torch_dtype=torch.float32, low_cpu_mem_usage=True
19
+ )
20
+
21
+ pipe = pipeline(
22
+ "text-generation",
23
+ model=model,
24
+ tokenizer=tokenizer,
25
+ device=0 if torch.cuda.is_available() else -1,
26
+ )
27
 
28
+ # -----------------------
29
+ # Lookup Table for Biomarkers
30
+ # -----------------------
31
+ REFERENCE_RANGES = {
32
+ "Albumin": {"low": 3.5, "high": 5.5, "unit": "g/dL"},
33
+ "Creatinine_Male": {"low": 0.7, "high": 1.3, "unit": "mg/dL"},
34
+ "Creatinine_Female": {"low": 0.6, "high": 1.1, "unit": "mg/dL"},
35
+ "Glucose": {"low": 70, "high": 100, "unit": "mg/dL"},
36
+ "CRP": {"low": 0.3, "high": 10, "unit": "mg/L"},
37
+ "MCV": {"low": 80, "high": 100, "unit": "fL"},
38
+ "RDW": {"low": 11, "high": 15, "unit": "%"},
39
+ "WBC": {"low": 4000, "high": 11000, "unit": "/µL"},
40
+ "Lymphocytes": {"low": 20, "high": 40, "unit": "%"},
41
+ "ALP": {"low": 44, "high": 147, "unit": "U/L"},
42
+ }
43
+
44
+ def classify(value, biomarker, gender="Male"):
45
+ """Classify biomarker as Low, Normal, or High using lookup table."""
46
+ if biomarker == "Creatinine":
47
+ ref = REFERENCE_RANGES[f"Creatinine_{gender}"]
48
+ else:
49
+ ref = REFERENCE_RANGES[biomarker]
50
+
51
+ if value < ref["low"]:
52
+ return "Low"
53
+ elif value > ref["high"]:
54
+ return "High"
55
+ else:
56
+ return "Normal"
57
 
58
  # -----------------------
59
+ # Splitter helper (unchanged)
60
  # -----------------------
61
  def split_report(text):
 
 
 
 
 
62
  text = text.strip()
63
+ markers = ["5. Tabular Mapping", "Tabular Mapping", "📊 Tabular", "## 5"]
 
 
 
 
 
 
 
 
 
64
  idx = None
65
  for m in markers:
66
  pos = text.find(m)
67
+ if pos != -1 and (idx is None or pos < idx):
68
+ idx = pos
 
69
  if idx is None:
 
 
 
 
 
 
 
 
70
  return text, ""
71
+ return text[:idx].strip(), text[idx:].strip()
 
 
72
 
73
  # -----------------------
74
+ # Analyze Function
75
  # -----------------------
76
+ def analyze(albumin, creatinine, glucose, crp, mcv, rdw, alp,
77
+ wbc, lymph, age, gender, height, weight):
78
+
79
+ # Calculate BMI
 
 
 
 
 
80
  try:
81
  height = float(height)
82
  weight = float(weight)
83
+ bmi = round(weight / ((height / 100) ** 2), 2)
84
  except Exception:
85
  bmi = "N/A"
86
 
87
+ # Classify biomarkers via lookup
88
+ statuses = {
89
+ "Albumin": classify(albumin, "Albumin", gender),
90
+ "Creatinine": classify(creatinine, "Creatinine", gender),
91
+ "Glucose": classify(glucose, "Glucose", gender),
92
+ "CRP": classify(crp, "CRP", gender),
93
+ "MCV": classify(mcv, "MCV", gender),
94
+ "RDW": classify(rdw, "RDW", gender),
95
+ "WBC": classify(wbc * 1000, "WBC", gender), # convert K/uL to /µL
96
+ "Lymphocytes": classify(lymph, "Lymphocytes", gender),
97
+ "ALP": classify(alp, "ALP", gender),
98
+ }
99
+
100
+ # Structured context for LLM
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
101
  patient_input = (
102
  f"Patient Profile:\n"
103
  f"- Age: {age}\n"
 
105
  f"- Height: {height} cm\n"
106
  f"- Weight: {weight} kg\n"
107
  f"- BMI: {bmi}\n\n"
108
+ "Biomarker Results (Pre-classified):\n"
109
+ )
110
+ for biomarker, value in {
111
+ "Albumin": albumin,
112
+ "Creatinine": creatinine,
113
+ "Glucose": glucose,
114
+ "CRP": crp,
115
+ "MCV": mcv,
116
+ "RDW": rdw,
117
+ "ALP": alp,
118
+ "WBC": wbc,
119
+ "Lymphocytes": lymph,
120
+ }.items():
121
+ unit = REFERENCE_RANGES["Creatinine_Male"]["unit"] if biomarker == "Creatinine" else REFERENCE_RANGES[biomarker]["unit"]
122
+ patient_input += f"- {biomarker}: {value} {unit} → {statuses[biomarker]}\n"
123
+
124
+ system_prompt = (
125
+ "You are 'Medical Insights AI'.\n"
126
+ "The biomarker statuses (Low/Normal/High) are pre-computed using strict ranges.\n"
127
+ "Your role: generate detailed structured medical analysis, system-specific context, and recommendations.\n"
128
+ "Do NOT recalculate statuses — use the provided ones.\n"
129
+ "Follow the Executive Summary format strictly.\n"
130
  )
131
 
132
  prompt = system_prompt + "\n" + patient_input
133
 
 
 
134
  gen = pipe(
135
  prompt,
136
+ max_new_tokens=2000,
137
+ do_sample=False,
138
+ temperature=0.0,
139
+ return_full_text=False,
 
 
 
140
  )
141
 
142
+ generated = gen[0]["generated_text"].strip()
143
+ left, right = split_report(generated)
144
+ return left, right
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
  # -----------------------
147
+ # Gradio UI (unchanged)
148
  # -----------------------
149
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
150
  gr.Markdown("# 🏥 AI Medical Biomarker Dashboard")
 
188
  outputs=[left_output, right_output]
189
  )
190
 
 
 
191
  if __name__ == "__main__":
192
+ demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))
193
+ ```