Vineela Gampa commited on
Commit
fd6e070
·
unverified ·
1 Parent(s): af846ab

CHAT / ANALYZER FIXED

Browse files
Files changed (5) hide show
  1. backend.py +111 -27
  2. data/app.db +0 -0
  3. past_reports.py +2 -2
  4. requirements.txt +2 -0
  5. web/analyzer.html +1 -1
backend.py CHANGED
@@ -3,7 +3,7 @@ from ast import List
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from pydantic import BaseModel
5
  import io
6
- import fitz
7
  import traceback
8
  import pandas as pd
9
 
@@ -85,15 +85,49 @@ class ChatResponse(BaseModel):
85
  class TextRequest(BaseModel):
86
  text: str
87
 
88
- system_prompt = """ As a highly skilled medical practitioner specializing in image analysis, you are tasked with examining medical images for a renowned hospital. Your expertise is crucial in identifying any anomalies, diseases, or health issues that may be present in the images. Your responsibilities include:
89
- 1. Detailed Analysis: Thoroughly analyze each image, focusing on identifying any abnormal findings that may indicate underlying medical conditions.
90
- 2. Finding Report: Document all observed anomalies or signs of disease. Clearly articulate these findings in a structured report format, ensuring accuracy and clarity. Also include any measurement found such as trygliceride, HBa1c, and hdl levels. When presenting any found measurement, relay it in this format: {"findings": "Condition that this measurement could affect only if the value is in a risky rang: masurement type -- value with unit(current range). For example: {"findings": "Diabetes: Hba1c -- 8%(diabetic)", "ldl -- 80mg/dL(optimal)"}"
91
- At the beginning of every finding, if needed try to simplify the information given in 3 words.
92
- 3. Checking for Past: If a disease is considered family history or is a disease that the patient recovered from(anything anomoly that is historical), please mention this in severity using this format: "severity" : "severity of the anomoly (Past Anomoly but Still Under Risk)"
93
- 4. Recommendations and Next Steps: Provide detailed recommendations based on your findings. Outline the necessary follow-up actions or additional tests required to confirm diagnoses or assess treatment options.
94
- 5. Treatment Suggestions: Offer preliminary treatment suggestions or interventions based on the identified conditions, collaborating with the healthcare team to develop comprehensive patient care plans.
95
- 6. Output Format: Your output should be a JSON array (list) of objects, each describing one disease or medical finding using the structure below:
96
- [{"findings": "Description of the first disease or condition.", "severity": "MILD/SEVERE/CRITICAL", "recommendations": ["Follow-up test 1", "Follow-up test 2"], "treatment_suggestions": ["Treatment 1", "Treatment 2"], "home_care_guidance": ["Care tip 1", "Care tip 2"] }, { "findings": "Description of the second disease or condition.", "severity": "MILD/SEVERE/CRITICAL", "recommendations": ["Follow-up test A", "Follow-up test B"], "treatment_suggestions": ["Treatment A", "Treatment B"], "home_care_guidance": ["Care tip A", "Care tip B"] } ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  Important Notes:
98
  1. Scope of Response: Only respond if the image pertains to a human health issue.
99
  2. Clarity of Image: Ensure the image is clear and suitable for accurate analysis.
@@ -152,7 +186,6 @@ async def analyze_image(image_bytes: bytes, mime_type: str, prompt: Optional[str
152
  response = await _call_model_blocking(request_inputs, generation_config, safety_settings)
153
  except Exception as e:
154
  raise RuntimeError(f"Model call failed: {e}")
155
-
156
  text = getattr(response, "text", None)
157
  if not text and isinstance(response, dict):
158
  candidates = response.get("candidates") or []
@@ -162,18 +195,25 @@ async def analyze_image(image_bytes: bytes, mime_type: str, prompt: Optional[str
162
  text = str(response)
163
 
164
  clean = re.sub(r"```(?:json)?", "", text).strip()
165
-
166
  try:
167
  parsed = json.loads(clean)
168
- return parsed
 
 
 
169
  except json.JSONDecodeError:
170
  match = re.search(r"(\[.*\]|\{.*\})", clean, re.DOTALL)
171
  if match:
172
  try:
173
- return json.loads(match.group(1))
 
 
 
 
174
  except json.JSONDecodeError:
175
- return {"raw_found_json": match.group(1)}
176
- return {"raw_output": clean}
177
 
178
  def get_past_reports_from_sqllite(user_id: str):
179
  try:
@@ -186,6 +226,7 @@ def get_past_reports_from_sqllite(user_id: str):
186
  history_text = "No past reports found for this user."
187
  return history_text
188
 
 
189
  @app.post("/chat/", response_model=ChatResponse)
190
  async def chat_endpoint(request: ChatRequest):
191
  global result
@@ -195,20 +236,20 @@ async def chat_endpoint(request: ChatRequest):
195
  """
196
 
197
  #history_text = get_past_reports_from_firestore(request.user_id)
198
- full_document_text = get_past_reports_from_sqllite(request.user_id)
199
-
200
- #full_document_text = +"\n\n" + "PAST REPORTS:\n" + history_text
201
-
202
  if not full_document_text:
203
  raise HTTPException(status_code=400, detail="No past reports or current data exists for this user")
204
 
205
 
206
  try:
207
- document_text = json.dumps(full_document_text)
208
  full_prompt = system_prompt_chat.format(
209
- document_text=document_text,
210
  user_question=request.question
211
  )
 
212
 
213
  response = model.generate_content(full_prompt)
214
  return ChatResponse(answer=response.text)
@@ -223,15 +264,23 @@ async def analyze_endpoint(file: UploadFile = File(...), prompt: str = Form(None
223
  Returns parsed JSON (or raw model output if JSON couldn't be parsed).
224
  """
225
 
226
- global result
 
 
 
227
  contents = await file.read() # <-- this gets the uploaded file bytes
228
  mime = file.content_type or "image/png"
229
- result = await analyze_image(contents, mime, prompt)
 
230
  try:
231
- result = await analyze_image(contents, mime, prompt)
 
232
  except Exception as e:
233
  raise HTTPException(status_code=500, detail=str(e))
234
- return JSONResponse(content={"Detected_Anomolies": result})
 
 
 
235
 
236
  @app.post("/analyze_json")
237
  async def analyze_json(req: AnalyzeRequest):
@@ -250,4 +299,39 @@ def _log_routes():
250
  print("Mounted routes:")
251
  for r in app.routes:
252
  if isinstance(r, APIRoute):
253
- print(" ", r.path, r.methods)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  from fastapi.middleware.cors import CORSMiddleware
4
  from pydantic import BaseModel
5
  import io
6
+ #import fitz
7
  import traceback
8
  import pandas as pd
9
 
 
85
  class TextRequest(BaseModel):
86
  text: str
87
 
88
+ system_prompt = """ You are a highly skilled medical practitioner specializing in medical image and document analysis. You will be given either a medical image or a PDF.
89
+
90
+ Your responsibilities are:
91
+
92
+ 1. **Extract Text**: If the input is a PDF or image, first extract all the text content (lab values, notes, measurements, etc.). Do not summarize keep the extracted text verbatim.
93
+
94
+ 2. **Detailed Analysis**: Use both the extracted text and the visual features of the image to identify any anomalies, diseases, or health issues.
95
+
96
+ 3. **Finding Report**: Document all observed anomalies or signs of disease.
97
+ - Include any measurements (e.g., triglycerides, HBa1c, HDL) in the format:
98
+ `{"findings": "Condition only if risky: measurement type -- value with unit(current range)"}`
99
+ - Simplify the finding in **3 words** at the beginning when helpful.
100
+
101
+ 4. **Checking for Past**: If a disease is family history or previously recovered, mark severity as:
102
+ `"severity": "severity of anomaly (Past Anomaly but Still Under Risk)"`
103
+
104
+ 5. **Recommendations and Next Steps**: Provide detailed recommendations (tests, follow-ups, consultations).
105
+
106
+ 6. **Treatment Suggestions**: Offer preliminary treatments or interventions.
107
+
108
+ 7. **Output Format**: Always return a JSON object containing both the raw extracted text and the structured analysis, like this:
109
+
110
+ ```json
111
+ {
112
+ "ocr_text": "<<<FULL VERBATIM TEXT FROM THE PDF/IMAGE>>>",
113
+ "analysis": [
114
+ {
115
+ "findings": "UPPERCASE MAIN CONCERN. Description of the first disease or condition.",
116
+ "severity": "MILD/SEVERE/CRITICAL",
117
+ "recommendations": ["Follow-up test 1", "Follow-up test 2"],
118
+ "treatment_suggestions": ["Treatment 1", "Treatment 2"],
119
+ "home_care_guidance": ["Care tip 1", "Care tip 2"]
120
+ },
121
+ {
122
+ "findings": "UPPERCASE MAIN CONCERN. Description of the second disease or condition.",
123
+ "severity": "MILD/SEVERE/CRITICAL",
124
+ "recommendations": ["Follow-up test A", "Follow-up test B"],
125
+ "treatment_suggestions": ["Treatment A", "Treatment B"],
126
+ "home_care_guidance": ["Care tip A", "Care tip B"]
127
+ }
128
+ ]
129
+ }
130
+
131
  Important Notes:
132
  1. Scope of Response: Only respond if the image pertains to a human health issue.
133
  2. Clarity of Image: Ensure the image is clear and suitable for accurate analysis.
 
186
  response = await _call_model_blocking(request_inputs, generation_config, safety_settings)
187
  except Exception as e:
188
  raise RuntimeError(f"Model call failed: {e}")
 
189
  text = getattr(response, "text", None)
190
  if not text and isinstance(response, dict):
191
  candidates = response.get("candidates") or []
 
195
  text = str(response)
196
 
197
  clean = re.sub(r"```(?:json)?", "", text).strip()
198
+ print(f"Cleaned text: {clean}")
199
  try:
200
  parsed = json.loads(clean)
201
+ ocr_text = parsed["ocr_text"]
202
+ analysis = parsed["analysis"]
203
+ print(f"Parsed JSON: {parsed}")
204
+ return analysis,ocr_text
205
  except json.JSONDecodeError:
206
  match = re.search(r"(\[.*\]|\{.*\})", clean, re.DOTALL)
207
  if match:
208
  try:
209
+
210
+ parsed = json.loads(match.group(1)), None
211
+ ocr_text = parsed["ocr_text"]
212
+ analysis = parsed["analysis"]
213
+ return analysis, ocr_text
214
  except json.JSONDecodeError:
215
+ return {"raw_found_json": match.group(1)}, None
216
+ return {"raw_output": clean}, None
217
 
218
  def get_past_reports_from_sqllite(user_id: str):
219
  try:
 
226
  history_text = "No past reports found for this user."
227
  return history_text
228
 
229
+
230
  @app.post("/chat/", response_model=ChatResponse)
231
  async def chat_endpoint(request: ChatRequest):
232
  global result
 
236
  """
237
 
238
  #history_text = get_past_reports_from_firestore(request.user_id)
239
+ full_document_text = get_past_reports_from_sqllite(request.user_id.strip())
240
+
241
+ full_document_text = EXTRACTED_TEXT_CACHE+"\n\n" + "PAST REPORTS:\n" + full_document_text
242
+ print(f"Full document text: {full_document_text}")
243
  if not full_document_text:
244
  raise HTTPException(status_code=400, detail="No past reports or current data exists for this user")
245
 
246
 
247
  try:
 
248
  full_prompt = system_prompt_chat.format(
249
+ document_text=full_document_text,
250
  user_question=request.question
251
  )
252
+ print(f"Full prompt: {full_prompt}")
253
 
254
  response = model.generate_content(full_prompt)
255
  return ChatResponse(answer=response.text)
 
264
  Returns parsed JSON (or raw model output if JSON couldn't be parsed).
265
  """
266
 
267
+ global result,EXTRACTED_TEXT_CACHE
268
+
269
+ filename = file.filename.lower()
270
+ print(f"Received analyze request for file {filename}")
271
  contents = await file.read() # <-- this gets the uploaded file bytes
272
  mime = file.content_type or "image/png"
273
+
274
+ #result = await analyze_image(contents, mime, prompt)
275
  try:
276
+ result, ocr_text = await analyze_image(contents, mime, prompt)
277
+ EXTRACTED_TEXT_CACHE = ocr_text
278
  except Exception as e:
279
  raise HTTPException(status_code=500, detail=str(e))
280
+ return JSONResponse(content={
281
+ "ocr_text": ocr_text,
282
+ "Detected_Anomolies": result
283
+ })
284
 
285
  @app.post("/analyze_json")
286
  async def analyze_json(req: AnalyzeRequest):
 
299
  print("Mounted routes:")
300
  for r in app.routes:
301
  if isinstance(r, APIRoute):
302
+ print(" ", r.path, r.methods)
303
+
304
+
305
+
306
+ def main():
307
+ """Run the application."""
308
+ try:
309
+ logger.info(f"Starting server on 8000")
310
+ logger.info(f"Debug mode: true")
311
+
312
+ if Config.DEBUG:
313
+ # Use import string for reload mode
314
+ uvicorn.run(
315
+ "main:app",
316
+ host="localhost",
317
+ port="8000",
318
+ reload=True,
319
+ log_level="debug"
320
+ )
321
+ else:
322
+ # Use app instance for production
323
+ uvicorn.run(
324
+ app,
325
+ host="localhost",
326
+ port="8000",
327
+ reload=False,
328
+ log_level="info"
329
+ )
330
+
331
+ except Exception as e:
332
+ logger.error(f"Failed to start server: {e}")
333
+ raise
334
+
335
+
336
+ if __name__ == "__main__":
337
+ main()
data/app.db CHANGED
Binary files a/data/app.db and b/data/app.db differ
 
past_reports.py CHANGED
@@ -77,7 +77,7 @@ def _safe_parse_json(text):
77
  return None # or return s if you prefer to surface the raw text
78
 
79
  def _row_to_dict(row: sqlite3.Row) -> dict:
80
- return {
81
  "id": row[0],
82
  "user_id": row[1],
83
  "report_date": row[2],
@@ -200,4 +200,4 @@ def delete_report(report_id: int):
200
  if __name__ == "__main__":
201
  #req = ReportIn(user_id="[email protected]", report_date="2025-09-08", ocr_text="Medical Report - Cancer Patient Name: Carol Davis Age: 55 Gender: Female Clinical History: Recent biopsy confirms breast cancer (invasive ductal carcinoma). No lymph node involvement. PET scan negative for metastasis.", anomalies=[{"findings":"BREAST CANCER(DETECTED AS HISTORICAL CONDITION, BUT STILL UNDER RISK.)","severity":"Severe Risk","recommendations":["Consult a doctor."],"treatment_suggestions":"Consult a specialist: General Practitioner","home_care_guidance":[],"info_link":"https://www.webmd.com/"},{"findings":"CANCER(DETECTED AS HISTORICAL CONDITION, BUT STILL UNDER RISK.)","severity":"Severe Risk","recommendations":["Consult a doctor."],"treatment_suggestions":"Consult a specialist: General Practitioner","home_care_guidance":[],"info_link":"https://www.webmd.com/"},{"findings":"BRAIN CANCER(DETECTED AS HISTORICAL CONDITION, BUT STILL UNDER RISK.)","severity":"Severe Risk","recommendations":["Consult a doctor."],"treatment_suggestions":"Consult a specialist: General Practitioner","home_care_guidance":[],"info_link":"https://www.webmd.com/"}], measurements=[])
202
  #save_report(req)
203
- print(db_fetch_reports(user_id="test.hd@gmail.com"))
 
77
  return None # or return s if you prefer to surface the raw text
78
 
79
  def _row_to_dict(row: sqlite3.Row) -> dict:
80
+ return {
81
  "id": row[0],
82
  "user_id": row[1],
83
  "report_date": row[2],
 
200
  if __name__ == "__main__":
201
  #req = ReportIn(user_id="[email protected]", report_date="2025-09-08", ocr_text="Medical Report - Cancer Patient Name: Carol Davis Age: 55 Gender: Female Clinical History: Recent biopsy confirms breast cancer (invasive ductal carcinoma). No lymph node involvement. PET scan negative for metastasis.", anomalies=[{"findings":"BREAST CANCER(DETECTED AS HISTORICAL CONDITION, BUT STILL UNDER RISK.)","severity":"Severe Risk","recommendations":["Consult a doctor."],"treatment_suggestions":"Consult a specialist: General Practitioner","home_care_guidance":[],"info_link":"https://www.webmd.com/"},{"findings":"CANCER(DETECTED AS HISTORICAL CONDITION, BUT STILL UNDER RISK.)","severity":"Severe Risk","recommendations":["Consult a doctor."],"treatment_suggestions":"Consult a specialist: General Practitioner","home_care_guidance":[],"info_link":"https://www.webmd.com/"},{"findings":"BRAIN CANCER(DETECTED AS HISTORICAL CONDITION, BUT STILL UNDER RISK.)","severity":"Severe Risk","recommendations":["Consult a doctor."],"treatment_suggestions":"Consult a specialist: General Practitioner","home_care_guidance":[],"info_link":"https://www.webmd.com/"}], measurements=[])
202
  #save_report(req)
203
+ print(db_fetch_reports(user_id="vineela.local@lowes.com"))
requirements.txt CHANGED
@@ -32,3 +32,5 @@ numpy==1.25.2
32
  # --- Cloud / APIs ---
33
  firebase-admin==5.1.0
34
  google-generativeai==0.3.1
 
 
 
32
  # --- Cloud / APIs ---
33
  firebase-admin==5.1.0
34
  google-generativeai==0.3.1
35
+
36
+ PyMuPDF==1.22.5 #fitz
web/analyzer.html CHANGED
@@ -401,7 +401,7 @@
401
  headers: { "Content-Type": "application/json" },
402
  body: JSON.stringify({
403
  question: q,
404
- user_id: currentUser ? currentUser.uid : "anonymous",
405
  }),
406
  });
407
  if (!res.ok) throw new Error(await res.text());
 
401
  headers: { "Content-Type": "application/json" },
402
  body: JSON.stringify({
403
  question: q,
404
+ user_id: currentUser ? currentUser.email : "anonymous",
405
  }),
406
  });
407
  if (!res.ok) throw new Error(await res.text());