Muhammadidrees commited on
Commit
a725bee
Β·
verified Β·
1 Parent(s): 9506f55

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +173 -125
app.py CHANGED
@@ -3,80 +3,78 @@ import gradio as gr
3
  from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
4
  import os
5
  import torch
 
6
 
7
  MODEL_ID = "Muhammadidrees/MedicalInsights"
8
 
9
  # -----------------------
10
- # Load tokenizer + model
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
- device = 0 if torch.cuda.is_available() else -1
22
- pipe = pipeline("text-generation", model=model, tokenizer=tokenizer, device=device)
23
 
24
  # -----------------------
25
- # Reference ranges lookup
26
  # -----------------------
27
- REFERENCE_RANGES = {
28
- "Albumin": (3.5, 5.5),
29
- "Creatinine": {"Male": (0.7, 1.3), "Female": (0.6, 1.1)},
30
- "Glucose": (70, 100),
31
- "CRP": (0.3, 10),
32
- "MCV": (80, 100),
33
- "RDW": (11, 15),
34
- "WBC": (4, 11), # K/uL
35
- "Lymphocytes": (20, 40),
36
- "ALP": (44, 147),
37
- }
38
-
39
- def classify_biomarker(name, value, gender=None):
40
  """
41
- Returns Low / Normal / High for a biomarker using REFERENCE_RANGES.
42
- Safe: won't crash if value or range missing.
43
  """
44
- try:
45
- if value is None:
46
- return "Unknown"
47
- ref = REFERENCE_RANGES.get(name)
48
- if ref is None:
49
- return "Unknown"
50
-
51
- if isinstance(ref, dict) and gender:
52
- low, high = ref.get(gender, (None, None))
53
- else:
54
- low, high = ref
55
-
56
- if low is None or high is None:
57
- return "Unknown"
58
-
59
- if value < low:
60
- return "Low"
61
- elif value > high:
62
- return "High"
63
- else:
64
- return "Normal"
65
- except Exception as e:
66
- return f"Error: {str(e)}"
 
 
 
 
 
 
 
 
67
 
68
  # -----------------------
69
  # The analyze function
70
  # -----------------------
71
- def analyze(albumin, creatinine, glucose, crp, mcv, rdw, alp,
72
- wbc, lymph, age, gender, height, weight):
73
-
74
- # Safe conversions
 
75
  try:
76
  age = int(age)
77
  except Exception:
78
- age = "N/A"
79
-
80
  try:
81
  height = float(height)
82
  weight = float(weight)
@@ -84,86 +82,139 @@ def analyze(albumin, creatinine, glucose, crp, mcv, rdw, alp,
84
  except Exception:
85
  bmi = "N/A"
86
 
87
- # Classify each biomarker
88
- statuses = {
89
- "Albumin": classify_biomarker("Albumin", albumin, gender),
90
- "Creatinine": classify_biomarker("Creatinine", creatinine, gender),
91
- "Glucose": classify_biomarker("Glucose", glucose),
92
- "CRP": classify_biomarker("CRP", crp),
93
- "MCV": classify_biomarker("MCV", mcv),
94
- "RDW": classify_biomarker("RDW", rdw),
95
- "WBC": classify_biomarker("WBC", wbc),
96
- "Lymphocytes": classify_biomarker("Lymphocytes", lymph),
97
- "ALP": classify_biomarker("ALP", alp),
98
- }
99
-
100
- # Build structured patient input for LLM
101
- patient_input = (
102
- f"Patient Profile:\n"
103
- f"- Age: {age}\n"
104
- f"- Gender: {gender}\n"
105
- f"- Height: {height} cm\n"
106
- f"- Weight: {weight} kg\n"
107
- f"- BMI: {bmi}\n\n"
108
- "Biomarker Results:\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
- patient_input += f"- {biomarker}: {value} ({statuses[biomarker]})\n"
122
-
123
- # System prompt
124
  system_prompt = (
125
- "You are 'Medical Insights AI', a trusted medical assistant.\n"
126
- "The biomarker classifications (Low / Normal / High) are already provided.\n"
127
- "Do not recompute them β€” just use them to generate:\n\n"
128
- "*Executive Summary*\n"
129
- "- Top Priority Issues\n"
130
- "- Key Strengths\n\n"
131
- "*System-Specific Analysis*\n"
132
- "- Blood Health\n"
133
- "- Protein & Liver Health\n"
134
- "- Kidney Health\n"
135
- "- Metabolic Health\n"
136
- "- Anthropometrics\n"
137
- "- Other Systems\n\n"
138
- "*Personalized Action Plan*\n"
139
- "- Medical, Nutrition, Lifestyle, Testing\n\n"
140
- "*Interaction Alerts*\n\n"
141
- "*Tabular Mapping*\n"
142
- "- Table with Biomarker | Reference Range | Value | Status | Insight\n\n"
143
- "*Enhanced AI Insights & Longitudinal Risk*\n"
144
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
  prompt = system_prompt + "\n" + patient_input
147
 
148
- gen = pipe(
149
- prompt,
150
- max_new_tokens=1200,
151
- do_sample=False,
152
- temperature=0.0,
153
- top_p=1.0,
154
- repetition_penalty=1.0,
155
- early_stopping=True,
156
- return_full_text=False
157
- )
158
 
 
159
  generated = gen[0].get("generated_text") or gen[0].get("text") or str(gen[0])
160
- return generated, generated # left and right panels (split optional)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
  # -----------------------
163
  # Build Gradio app
164
  # -----------------------
165
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
166
  gr.Markdown("# πŸ₯ AI Medical Biomarker Dashboard")
 
167
 
168
  with gr.Row():
169
  with gr.Column(scale=1):
@@ -203,10 +254,7 @@ with gr.Blocks(theme=gr.themes.Soft()) as demo:
203
  outputs=[left_output, right_output]
204
  )
205
 
 
 
206
  if __name__ == "__main__":
207
- demo.launch(
208
- server_name="0.0.0.0",
209
- server_port=int(os.environ.get("PORT", 7860)),
210
- show_error=True, # Show traceback on Hugging Face
211
- debug=True
212
- )
 
3
  from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
4
  import os
5
  import torch
6
+ import re
7
 
8
  MODEL_ID = "Muhammadidrees/MedicalInsights"
9
 
10
  # -----------------------
11
+ # Load tokenizer + model safely (GPU or CPU)
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)
 
82
  except Exception:
83
  bmi = "N/A"
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  system_prompt = (
86
+ "You are a professional AI Medical Assistant.\n"
87
+ "You are analyzing patient demographics (age, height, weight) and Levine biomarker panel values.\n\n"
88
+
89
+ "The Levine biomarker panel includes:\n"
90
+ "- Albumin\n"
91
+ "- Creatinine\n"
92
+ "- Glucose\n"
93
+ "- C-reactive protein (CRP)\n"
94
+ "- Mean Cell Volume (MCV)\n"
95
+ "- Red Cell Distribution Width (RDW)\n"
96
+ "- Alkaline Phosphatase (ALP)\n"
97
+ "- White Blood Cell count (WBC)\n"
98
+ "- Lymphocyte percentage\n\n"
99
+
100
+ "STRICT RULES:\n"
101
+ "- Use ONLY the 9 biomarkers above + age, height, weight.\n"
102
+ "- DO NOT use or invent other lab results (e.g., cholesterol, vitamin D, ferritin, ALT, AST, urine results).\n"
103
+ "- Use the reference ranges provided in the Patient Summary for all biomarker interpretations.\n"
104
+ "- Status (Low/Normal/High) must be defined strictly by whether the value falls below, within, or above the provided reference ranges.\n"
105
+ "- Analysis must always explain deviations with respect to these reference ranges.\n"
106
+ "- If a section cannot be addressed with available data, explicitly state: 'Not available from current biomarkers.'\n"
107
+ "- Do not give absolute longevity scores. Instead, summarize trends (e.g., 'No major abnormalities suggesting elevated short-term risk.').\n"
108
+ "- Nutrient status (Iron, B12, Folate) can only be suggested as possible IF supported by MCV + RDW patterns, but never stated as confirmed.\n"
109
+ "- Interpret ALP cautiously: mention bone vs liver as possible sources, but highlight that more tests would be required to confirm.\n"
110
+ "- Always highlight limitations where applicable.\n\n"
111
+
112
+ "OUTPUT FORMAT (strict, structured, and professional):\n\n"
113
+
114
+ "1. Executive Summary\n"
115
+ " - Top Priority Issues (based only on provided biomarkers and their ranges)\n"
116
+ " - Key Strengths\n\n"
117
+
118
+ "2. System-Specific Analysis\n"
119
+ " - Blood Health (MCV, RDW, Lymphocytes, WBC)\n"
120
+ " - Protein & Liver Health (Albumin, ALP)\n"
121
+ " - Kidney Health (Creatinine)\n"
122
+ " - Metabolic Health (Glucose, CRP)\n"
123
+ " - Anthropometrics (Age, Height, Weight, BMI)\n"
124
+ " - Other systems: Always state 'Not available from current biomarkers.' if data missing\n\n"
125
+
126
+ "3. Personalized Action Plan\n"
127
+ " - Medical (tests/consults related only to biomarkers β€” e.g., repeat CBC, iron studies if anemia suspected)\n"
128
+ " - Nutrition (diet & supplements grounded ONLY in biomarker findings β€” e.g., protein intake if albumin low, anti-inflammatory foods if CRP elevated)\n"
129
+ " - Lifestyle (hydration, exercise, sleep β€” general guidance contextualized by BMI and biomarkers)\n"
130
+ " - Testing (only mention ferritin, B12, folate, GGT, etc. as follow-up β€” but clarify these are NOT part of current data)\n\n"
131
+
132
+ "4. Interaction Alerts\n"
133
+ " - Describe ONLY interactions among provided biomarkers (e.g., RDW with MCV for anemia trends, ALP bone/liver origin, WBC with CRP for infection/inflammation)\n\n"
134
+
135
+ "5. Tabular Mapping\n"
136
+ " - This section must always include a Markdown table.\n"
137
+ " - The table must contain exactly five columns:\n"
138
+ " | Biomarker | Value | Status (Low/Normal/High) | Reference Range | AI-Inferred Insight |\n"
139
+ " - The Reference Range column must be populated with the ranges given in the Patient Summary.\n"
140
+ " - The first row after the header must begin directly with 'Albumin'.\n"
141
+ " - Do NOT add any index numbers (0,1,2...) or empty rows.\n"
142
+ " - Each biomarker must appear exactly once as a separate row.\n\n"
143
+
144
+ "6. Enhanced AI Insights & Longitudinal Risk\n"
145
+ " - Subclinical nutrient predictions ONLY if patterns (MCV + RDW) suggest it β€” state as possible, not confirmed.\n"
146
+ " - ALP interpretation limited to bone vs liver origin (uncertain without further tests).\n"
147
+ " - WBC & lymphocyte balance for immunity.\n"
148
+ " - Risk framing: Highlight if biomarkers suggest resilience or potential stress, but avoid absolute longevity claims.\n\n"
149
+
150
+ "STYLE REQUIREMENTS:\n"
151
+ "- Use clear section headings and bullet points.\n"
152
+ "- Keep language professional, concise, and client-friendly.\n"
153
+ "- Format tables cleanly in Markdown.\n"
154
+ "- Present output beautifully, like a polished medical summary.\n"
155
+ )
156
+
157
+
158
+ patient_input =(
159
+ f"Patient Profile:\n"
160
+ f"- Age: {age}\n"
161
+ f"- Gender: {gender}\n"
162
+ f"- Height: {height} cm\n"
163
+ f"- Weight: {weight} kg\n"
164
+ f"- BMI: {bmi}\n\n"
165
+ "Lab Values (with Normal Ranges):\n"
166
+ f"- Albumin: {albumin} g/dL (Normal: 3.5 – 5.0 g/dL)\n"
167
+ f"- Creatinine: {creatinine} mg/dL (Normal: 0.6 – 1.2 mg/dL)\n"
168
+ f"- Glucose: {glucose} mg/dL (Normal: 70 – 99 mg/dL, fasting)\n"
169
+ f"- CRP: {crp} mg/L (Normal: < 3 mg/L)\n"
170
+ f"- MCV: {mcv} fL (Normal: 80 – 100 fL)\n"
171
+ f"- RDW: {rdw} % (Normal: 11.5 – 14.5 %)\n"
172
+ f"- ALP: {alp} U/L (Normal: 44 – 147 U/L)\n"
173
+ f"- WBC: {wbc} K/uL (Normal: 4.0 – 11.0 K/uL)\n"
174
+ f"- Lymphocytes: {lymph} % (Normal: 20 – 40 %)\n"
175
+ )
176
 
177
  prompt = system_prompt + "\n" + patient_input
178
 
179
+ # Generate
180
+ # Keep generation parameters conservative for Spaces
181
+ gen = pipe(prompt,
182
+ max_new_tokens=2500,
183
+ do_sample=True,
184
+ temperature=0.001,
185
+ top_p=0.9,
186
+ return_full_text=False)
 
 
187
 
188
+ # Extract generated text
189
  generated = gen[0].get("generated_text") or gen[0].get("text") or str(gen[0])
190
+ generated = generated.strip()
191
+
192
+ # Clean: some models repeat prompt β€” attempt to strip prompt if present
193
+ # Remove leading prompt echo if it appears
194
+ if patient_input.strip() in generated:
195
+ generated = generated.split(patient_input.strip())[-1].strip()
196
+ # Also remove repeated instructions
197
+ if system_prompt.strip() in generated:
198
+ generated = generated.split(system_prompt.strip())[-1].strip()
199
+
200
+ # Split into left/right panels
201
+ left_md, right_md = split_report(generated)
202
+
203
+ # If the model output is empty or too short, return a helpful fallback
204
+ if len(left_md) < 50 and len(right_md) < 50:
205
+ fallback = (
206
+ "⚠️ The model returned an unexpectedly short response. Try re-running the report.\n\n"
207
+ "**Patient Profile:**\n" + patient_input
208
+ )
209
+ return fallback, ""
210
+ return left_md, right_md
211
 
212
  # -----------------------
213
  # Build Gradio app
214
  # -----------------------
215
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
216
  gr.Markdown("# πŸ₯ AI Medical Biomarker Dashboard")
217
+ gr.Markdown("Enter lab values and demographics β€” Report is generated in two panels (Summary & Table/Insights).")
218
 
219
  with gr.Row():
220
  with gr.Column(scale=1):
 
254
  outputs=[left_output, right_output]
255
  )
256
 
257
+
258
+ # Launch (HF Spaces expects this pattern)
259
  if __name__ == "__main__":
260
+ demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))