vineelagampa commited on
Commit
4b899e5
·
verified ·
1 Parent(s): cf070a2

Update backend.py

Browse files
Files changed (1) hide show
  1. backend.py +125 -25
backend.py CHANGED
@@ -1,4 +1,12 @@
1
  import os
 
 
 
 
 
 
 
 
2
  import base64
3
  import json
4
  import re
@@ -7,33 +15,56 @@ import functools
7
  from typing import Any, Optional
8
 
9
  import google.generativeai as genai
10
- from fastapi import FastAPI, UploadFile, File, Form, HTTPException
11
- from fastapi.responses import JSONResponse
 
 
 
 
12
  from pydantic import BaseModel
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  class AnalyzeRequest(BaseModel):
15
  image_base64: str
16
- prompt: str | None = None
17
 
18
- API_KEY = None
19
- try:
20
- from api_key import GEMINI_API_KEY as API_KEY # <-- match the name in api_key.py
21
- except ImportError:
22
- API_KEY = os.getenv("GEMINI_API_KEY")
23
 
24
- if not API_KEY:
25
  raise RuntimeError(
26
- "No Google API key found. Put it in api_key.py as `GEMINI_API_KEY = '...'` or set env var GEMINI_API_KEY."
27
  )
28
 
29
- genai.configure(api_key=API_KEY)
30
 
31
  generation_config = {
32
  "temperature": 0.2,
33
  "top_p": 0.95,
34
  "top_k": 40,
35
  "max_output_tokens": 2048,
36
- "response_mime_type": "application/json",
37
  }
38
 
39
  safety_settings = [
@@ -43,21 +74,59 @@ safety_settings = [
43
  {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_MEDIUM_AND_ABOVE"},
44
  ]
45
 
 
 
 
 
 
 
 
 
 
 
 
46
  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:
47
  1. Detailed Analysis: Thoroughly analyze each image, focusing on identifying any abnormal findings that may indicate underlying medical conditions.
48
- 2. Finding Report: Document all observed anomalies or signs of disease. Clearly articulate these findings in a structured report format, ensuring accuracy and clarity.
49
- 3. 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.
50
- 4. Treatment Suggestions: Offer preliminary treatment suggestions or interventions based on the identified conditions, collaborating with the healthcare team to develop comprehensive patient care plans.
51
- 5. Output Format: Your output should be a JSON array (list) of objects, each describing one disease or medical finding using the structure below:
 
 
52
  [{"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"] } ]
53
- Important Notes: 1. Scope of Response: Only respond if the image pertains to a human health issue. 2. Clarity of Image: Ensure the image is clear and suitable for accurate analysis. 3. Disclaimer: Accompany your analysis with the disclaimer: “Consult with a doctor before making any decisions.” 4. Your Insights are Invaluable: Your insights play a crucial role in guiding clinical decisions. Please proceed with your analysis, adhering to the structured approach outlined above. """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
  # Initialize model
56
  model = genai.GenerativeModel(model_name="gemini-2.5-flash-lite")
57
 
58
- app = FastAPI()
59
-
60
-
61
  async def _call_model_blocking(request_inputs, generation_cfg, safety_cfg):
62
  """Run blocking model call in threadpool (so uvicorn's event loop isn't blocked)."""
63
  fn = functools.partial(
@@ -74,7 +143,6 @@ async def analyze_image(image_bytes: bytes, mime_type: str, prompt: Optional[str
74
  base64_img = base64.b64encode(image_bytes).decode("utf-8")
75
  text_prompt = (prompt or system_prompt).strip()
76
 
77
- # prepare request — two messages: image inline + text prompt
78
  request_inputs = [
79
  {"inline_data": {"mime_type": mime_type, "data": base64_img}},
80
  {"text": text_prompt},
@@ -85,20 +153,16 @@ async def analyze_image(image_bytes: bytes, mime_type: str, prompt: Optional[str
85
  except Exception as e:
86
  raise RuntimeError(f"Model call failed: {e}")
87
 
88
- # Try to extract textual content robustly
89
  text = getattr(response, "text", None)
90
  if not text and isinstance(response, dict):
91
- # older or alternative shapes
92
  candidates = response.get("candidates") or []
93
  if candidates:
94
  text = candidates[0].get("content") or candidates[0].get("text")
95
  if not text:
96
  text = str(response)
97
 
98
- # remove triple-backtick fences and stray code hints
99
  clean = re.sub(r"```(?:json)?", "", text).strip()
100
 
101
- # Try to parse JSON. If strict parse fails, try to extract first JSON-like block.
102
  try:
103
  parsed = json.loads(clean)
104
  return parsed
@@ -111,9 +175,34 @@ async def analyze_image(image_bytes: bytes, mime_type: str, prompt: Optional[str
111
  return {"raw_found_json": match.group(1)}
112
  return {"raw_output": clean}
113
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
115
  @app.post("/analyze")
116
  async def analyze_endpoint(file: UploadFile = File(...), prompt: str = Form(None)):
 
117
  """
118
  Upload an image file (field name `file`) and optional text `prompt`.
119
  Returns parsed JSON (or raw model output if JSON couldn't be parsed).
@@ -134,3 +223,14 @@ async def analyze_json(req: AnalyzeRequest):
134
  result = await analyze_image(image_bytes, "image/png", req.prompt)
135
  return {"result": result}
136
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import os
2
+ 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
+
10
  import base64
11
  import json
12
  import re
 
15
  from typing import Any, Optional
16
 
17
  import google.generativeai as genai
18
+ from fastapi import FastAPI, UploadFile, File, Form, HTTPException, APIRouter, Request
19
+ from fastapi.responses import JSONResponse, RedirectResponse
20
+ from fastapi.staticfiles import StaticFiles
21
+ import firebase_admin
22
+ from firebase_admin import credentials, firestore
23
+ from google.generativeai import generative_models
24
  from pydantic import BaseModel
25
+ from past_reports import router as reports_router, db_fetch_reports
26
+
27
+ from api_key import GEMINI_API_KEY
28
+
29
+ app = FastAPI()
30
+ api = APIRouter(prefix="/api")
31
+ app.include_router(api)
32
+
33
+ EXTRACTED_TEXT_CACHE = ""
34
+
35
+ app.mount("/app", StaticFiles(directory="web", html=True), name="web")
36
+ app.include_router(reports_router)
37
+
38
+ app.add_middleware(
39
+ CORSMiddleware,
40
+ allow_origins=["*"],
41
+ allow_credentials=True,
42
+ allow_methods=["*"],
43
+ allow_headers=["*"],
44
+ )
45
+
46
+ @app.get("/")
47
+ def root():
48
+ return RedirectResponse(url="/app/")
49
 
50
  class AnalyzeRequest(BaseModel):
51
  image_base64: str
52
+ prompt: Optional[str] = None
53
 
54
+ GEMINI_API_KEY = os.getenv("GEMINI_API_KEY", GEMINI_API_KEY)
 
 
 
 
55
 
56
+ if not GEMINI_API_KEY:
57
  raise RuntimeError(
58
+ "No Gemini API key found. Put it in api_key.py as `GEMINI_API_KEY = '...'` or set env var GEMINI_API_KEY."
59
  )
60
 
61
+ genai.configure(api_key=GEMINI_API_KEY)
62
 
63
  generation_config = {
64
  "temperature": 0.2,
65
  "top_p": 0.95,
66
  "top_k": 40,
67
  "max_output_tokens": 2048,
 
68
  }
69
 
70
  safety_settings = [
 
74
  {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_MEDIUM_AND_ABOVE"},
75
  ]
76
 
77
+ # --- Pydantic Models for API Endpoints ---
78
+ class ChatRequest(BaseModel):
79
+ user_id: Optional[str] = "anonymous"
80
+ question: str
81
+
82
+ class ChatResponse(BaseModel):
83
+ answer: str
84
+
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.
100
+ 3. As an AI, don't produce any output than doesn't follow this format and also remember that we need your input on the medical report for a team of renowned doctors to recheck your readings and determine AI's role in the medical industry.
101
+ 4. Your Insights are Invaluable: Your insights play a crucial role in guiding clinical decisions. Please proceed with your analysis, adhering to the structured approach outlined above.
102
+ 5. Completely UPPERCASE the main concern in the finding """
103
+
104
+ system_prompt_chat = """
105
+ *** Role: Medical Guidance Facilitator
106
+ *** Objective:
107
+ Analyze medical data, provide concise, evidence-based insights, and recommend actionable next steps for patient care. This includes suggesting local physicians or specialists within a user-specified mile radius, prioritizing in-network options when insurance information is available, and maintaining strict safety compliance with appropriate disclaimers.
108
+ *** Capabilities:
109
+ 1. Report Analysis – Review and interpret findings in uploaded medical reports.
110
+ 2. Historical Context – Compare current findings with any available previous reports.
111
+ 3. Medical Q&A – Answer specific questions about the report using trusted medical sources.
112
+ 4. Specialist Matching – Recommend relevant physician specialties for identified conditions.
113
+ 5. Local Physician Recommendations – List at least two real physician or clinic options within the user-specified mile radius (include name, specialty, address, distance from user, and contact info) based on the patient’s location and clinical need.
114
+ 6. Insurance Guidance – If insurance/network information is provided, prioritize in-network physicians.
115
+ 7. Safety Protocols – Include a brief disclaimer encouraging users to verify information, confirm insurance coverage, and consult providers directly.
116
+ *** Response Structure:
117
+ Start with a direct answer to the user’s primary question (maximum 4 concise sentences, each on a new line).
118
+ If a physician/specialist is needed, recommend at least two local providers within the requested radius (include name, specialty, address, distance, and contact info).
119
+ If insurance details are available, indicate which physicians are in-network.
120
+ End with a short safety disclaimer.
121
+ ***Input Fields:
122
+ Provided Document Text: {document_text}
123
+ User Question: {user_question}
124
+ Assistant Answer:
125
+ """
126
 
127
  # Initialize model
128
  model = genai.GenerativeModel(model_name="gemini-2.5-flash-lite")
129
 
 
 
 
130
  async def _call_model_blocking(request_inputs, generation_cfg, safety_cfg):
131
  """Run blocking model call in threadpool (so uvicorn's event loop isn't blocked)."""
132
  fn = functools.partial(
 
143
  base64_img = base64.b64encode(image_bytes).decode("utf-8")
144
  text_prompt = (prompt or system_prompt).strip()
145
 
 
146
  request_inputs = [
147
  {"inline_data": {"mime_type": mime_type, "data": base64_img}},
148
  {"text": text_prompt},
 
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 []
159
  if candidates:
160
  text = candidates[0].get("content") or candidates[0].get("text")
161
  if not text:
162
  text = str(response)
163
 
 
164
  clean = re.sub(r"```(?:json)?", "", text).strip()
165
 
 
166
  try:
167
  parsed = json.loads(clean)
168
  return parsed
 
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:
180
+ reports = db_fetch_reports(user_id=user_id, limit=10, offset=0)
181
+
182
+ history_text = ""
183
+ for report in reports:
184
+ history_text += f"Report from {report.get('report_date', 'N/A')}:\n{report.get('ocr_text', 'No OCR text found')}\n\n"
185
+ except Exception as e:
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
192
+ try:
193
+ document_text = json.dumps(result) if result else "No parsed text available"
194
+ full_prompt = system_prompt_chat.format(
195
+ document_text=document_text,
196
+ user_question=request.question
197
+ )
198
+ response = model.generate_content(full_prompt)
199
+ return ChatResponse(answer=response.text)
200
+ except Exception as e:
201
+ raise HTTPException(status_code=500, detail=f"Chat error: {e}")
202
 
203
  @app.post("/analyze")
204
  async def analyze_endpoint(file: UploadFile = File(...), prompt: str = Form(None)):
205
+ global result
206
  """
207
  Upload an image file (field name `file`) and optional text `prompt`.
208
  Returns parsed JSON (or raw model output if JSON couldn't be parsed).
 
223
  result = await analyze_image(image_bytes, "image/png", req.prompt)
224
  return {"result": result}
225
 
226
+ @app.get("/health/")
227
+ def health():
228
+ return {"response": "ok"}
229
+
230
+ @app.on_event("startup")
231
+ def _log_routes():
232
+ from fastapi.routing import APIRoute
233
+ print("Mounted routes:")
234
+ for r in app.routes:
235
+ if isinstance(r, APIRoute):
236
+ print(" ", r.path, r.methods)