mgbam commited on
Commit
c6163b1
·
verified ·
1 Parent(s): b9dbc6c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +133 -200
app.py CHANGED
@@ -1,274 +1,207 @@
1
  import streamlit as st
2
  import google.generativeai as genai
3
  import os
4
- from PIL import Image
5
- import io # Needed for handling image bytes
6
  from typing import Optional, Tuple, Any # For type hinting
7
 
8
  # --- Configuration and Initialization ---
9
 
10
- # Securely load API key
11
- # Prioritize Streamlit secrets, fall back to environment variable for flexibility
12
  GEMINI_API_KEY = st.secrets.get("GEMINI_API_KEY", os.environ.get("GEMINI_API_KEY"))
13
 
14
- # Configure Gemini Client (only if key is found)
15
  genai_client_configured = False
16
  if GEMINI_API_KEY:
17
  try:
18
  genai.configure(api_key=GEMINI_API_KEY)
19
  genai_client_configured = True
20
  except Exception as e:
21
- st.error(f"Failed to configure Google Generative AI: {e}")
22
- st.stop() # Stop execution if configuration fails
23
  else:
24
  st.error("⚠️ Gemini API Key not found. Please configure `GEMINI_API_KEY` in Streamlit secrets or environment variables.")
25
- st.stop() # Stop execution if no API key
26
 
27
- # Initialize models (only if client configured)
28
- # Using specific model versions can be good practice for reproducibility
29
- TEXT_MODEL_NAME = 'gemini-1.5-flash' # Or 'gemini-pro' if preferred
30
- VISION_MODEL_NAME = 'gemini-1.5-flash' # Or 'gemini-pro-vision' if preferred
31
 
32
  if genai_client_configured:
33
  try:
34
- model = genai.GenerativeModel(TEXT_MODEL_NAME)
35
- vision_model = genai.GenerativeModel(VISION_MODEL_NAME)
36
  except Exception as e:
37
- st.error(f"Failed to initialize Gemini models ({TEXT_MODEL_NAME}, {VISION_MODEL_NAME}): {e}")
38
  st.stop()
39
  else:
40
- # This state should technically not be reached due to earlier st.stop() calls,
41
- # but it's good defensive programming.
42
- st.error("AI Models could not be initialized due to configuration issues.")
43
  st.stop()
44
 
45
 
46
- # --- Core AI Interaction Functions ---
47
 
48
- # Define more sophisticated prompts emphasizing analysis and *potential* findings
49
- # CRITICAL: Avoid definitive "diagnosis" language. Focus on assisting clinical judgment.
50
- TEXT_ANALYSIS_PROMPT_TEMPLATE = """
51
- **Medical Information Analysis Request:**
52
 
53
- **Context:** Analyze the provided medical text (symptoms, history, or reports).
54
- **Task:**
55
- 1. **Identify Key Findings:** Extract significant symptoms, signs, or data points.
56
- 2. **Potential Considerations:** Based *only* on the provided text, list potential underlying conditions or areas of concern that *might* warrant further investigation by a qualified healthcare professional. Use cautious language (e.g., "suggests potential for," "could be consistent with," "warrants investigation into").
57
- 3. **Risk Factors (if applicable):** Mention any potential risk factors identifiable from the text.
58
- 4. **Information Gaps:** Highlight any missing information that would be crucial for a clinical assessment.
59
- 5. **Disclaimer:** Explicitly state that this analysis is AI-generated, not a diagnosis, and cannot replace professional medical evaluation.
60
 
61
- **Input Text:**
62
- ---
63
- {text_input}
64
- ---
65
 
66
- **Analysis:**
67
- """
 
 
 
 
 
 
 
 
 
 
68
 
69
- IMAGE_ANALYSIS_PROMPT_TEMPLATE = """
70
- **Medical Image Analysis Request:**
71
 
72
- **Context:** Analyze the provided medical image. User may provide additional context or questions.
73
- **Task:**
74
- 1. **Describe Visible Structures:** Briefly describe the main anatomical structures visible.
75
- 2. **Identify Potential Anomalies:** Point out any visible areas that *appear* abnormal or deviate from typical presentation (e.g., "potential opacity in the lower left lung field," "area of altered signal intensity," "possible asymmetry"). Use cautious, descriptive language.
76
- 3. **Correlate with User Prompt (if provided):** If the user asked a specific question, address it based *only* on the visual information.
77
- 4. **Limitations:** State that image quality, view, and lack of clinical context limit the analysis.
78
- 5. **Disclaimer:** Explicitly state this is an AI-based visual analysis, not a radiological interpretation or diagnosis, and requires review by a qualified radiologist or physician alongside clinical information.
79
 
80
- **User's Additional Context/Question (if any):**
 
 
81
  ---
82
- {user_prompt}
83
  ---
84
 
85
- **Image Analysis:**
86
  """
87
 
88
- def analyze_medical_text(text_input: str) -> Tuple[Optional[str], Optional[str]]:
89
  """
90
- Sends medical text to the Gemini model for analysis using a structured prompt.
91
 
92
  Args:
93
- text_input: The medical text provided by the user.
94
 
95
  Returns:
96
  A tuple containing:
97
- - The analysis text (str) if successful, None otherwise.
98
  - An error message (str) if an error occurred, None otherwise.
99
  """
100
- if not text_input:
101
  return None, "Input text cannot be empty."
102
  try:
103
- prompt = TEXT_ANALYSIS_PROMPT_TEMPLATE.format(text_input=text_input)
 
 
 
104
  response = model.generate_content(prompt)
105
- # Add safety check for response structure if needed (e.g., check for blocked content)
106
- if response.parts:
107
- return response.text, None
108
- elif response.prompt_feedback.block_reason:
109
- return None, f"Analysis blocked due to: {response.prompt_feedback.block_reason.name}. Please revise input."
110
- else:
111
- return None, "Received an empty or unexpected response from the AI."
112
-
113
- except Exception as e:
114
- st.error(f"An error occurred during text analysis: {e}") # Log for debugging
115
- return None, f"Error communicating with the AI model. Details: {e}"
116
-
117
- def analyze_medical_image(image_file: Any, user_prompt: str = "") -> Tuple[Optional[str], Optional[str]]:
118
- """
119
- Sends a medical image (and optional prompt) to the Gemini Vision model for analysis.
120
-
121
- Args:
122
- image_file: The uploaded image file object from Streamlit.
123
- user_prompt: Optional text context or specific questions from the user.
124
-
125
- Returns:
126
- A tuple containing:
127
- - The analysis text (str) if successful, None otherwise.
128
- - An error message (str) if an error occurred, None otherwise.
129
- """
130
- if not image_file:
131
- return None, "Image file cannot be empty."
132
- try:
133
- # Ensure image is opened correctly, handle potential errors
134
- try:
135
- image = Image.open(image_file)
136
- # Optional: Convert image to a supported format if needed, e.g., RGB
137
- if image.mode != 'RGB':
138
- image = image.convert('RGB')
139
- except Exception as img_e:
140
- return None, f"Error opening or processing image file: {img_e}"
141
-
142
- prompt_text = IMAGE_ANALYSIS_PROMPT_TEMPLATE.format(user_prompt=user_prompt if user_prompt else "N/A")
143
-
144
- # Prepare content for the vision model
145
- model_input = [prompt_text, image]
146
-
147
- response = vision_model.generate_content(model_input)
148
 
149
- # Add safety check for response structure
150
  if response.parts:
151
  return response.text, None
152
  elif response.prompt_feedback.block_reason:
153
- return None, f"Analysis blocked due to: {response.prompt_feedback.block_reason.name}. This might be due to sensitive content policies."
154
  else:
155
- return None, "Received an empty or unexpected response from the AI."
 
 
 
 
 
156
 
157
  except Exception as e:
158
- st.error(f"An error occurred during image analysis: {e}") # Log for debugging
159
- return None, f"Error communicating with the AI model. Details: {e}"
 
160
 
161
 
162
  # --- Streamlit User Interface ---
163
 
164
  def main():
165
- st.set_page_config(page_title="AI Medical Information Assistant", layout="wide")
 
 
 
 
166
 
167
- st.title("🤖 AI Medical Information Assistant")
168
- st.caption(f"Powered by Google Gemini ({TEXT_MODEL_NAME} / {VISION_MODEL_NAME})")
 
 
169
 
170
  # --- CRITICAL DISCLAIMER ---
171
  st.warning(
172
  """
173
- **IMPORTANT DISCLAIMER:**
174
- * This tool uses AI to analyze information but **DOES NOT PROVIDE MEDICAL ADVICE OR DIAGNOSIS.**
175
- * The analysis is based solely on the input provided and may be incomplete, inaccurate, or lack clinical context.
176
- * **ALWAYS consult a qualified healthcare professional** for any health concerns, diagnosis, or treatment decisions.
177
- * Do not rely on this tool for medical decisions. It is for informational and educational purposes only, potentially assisting clinicians by highlighting areas for review.
178
- * **Do not upload identifiable patient information** unless you have explicit consent and comply with all privacy regulations (e.g., HIPAA).
179
- """
 
180
  )
 
 
 
 
 
181
 
182
- st.sidebar.header("Input Options")
183
- input_method = st.sidebar.radio(
184
- "Select Input Type:",
185
- ("Text Description", "Medical Image"),
186
- help="Choose whether to analyze text-based medical information or a medical image."
187
  )
188
 
189
- st.sidebar.markdown("---") # Visual separator
190
-
191
- col1, col2 = st.columns(2)
192
-
193
- with col1:
194
- st.header("Input")
195
- if input_method == "Text Description":
196
- st.subheader("Enter Medical Text")
197
- text_input = st.text_area(
198
- "Paste symptoms, patient history excerpt, or non-identifiable report sections:",
199
- height=300,
200
- placeholder="Example: 55-year-old male presents with intermittent chest pain, worse on exertion. History of hypertension. Non-smoker...",
201
- key="text_input_area" # Add key for potential state management
202
- )
203
- analyze_button = st.button("Analyze Text", key="analyze_text_button", type="primary")
204
-
205
- if analyze_button and text_input:
206
- with st.spinner("🧠 Analyzing Text... Please wait."):
207
- analysis_result, error_message = analyze_medical_text(text_input)
208
-
209
- if error_message:
210
- with col2:
211
- st.error(f"Analysis Failed: {error_message}")
212
- elif analysis_result:
213
- with col2:
214
- st.header("Analysis Results")
215
- st.markdown(analysis_result) # Use markdown for better formatting
216
- else:
217
- # Handle unexpected case where both are None
218
- with col2:
219
- st.error("An unknown error occurred during analysis.")
220
-
221
- elif analyze_button and not text_input:
222
- st.warning("Please enter some text to analyze.")
223
-
224
-
225
- elif input_method == "Medical Image":
226
- st.subheader("Upload Medical Image")
227
- image_file = st.file_uploader(
228
- "Upload an image (e.g., X-ray, CT slice, pathology slide). Supported: PNG, JPG, JPEG.",
229
- type=["png", "jpg", "jpeg"],
230
- key="image_uploader"
231
- )
232
- user_image_prompt = st.text_input(
233
- "Optional: Add context or specific questions for the image analysis:",
234
- placeholder="Example: 'Check for abnormalities in the left lung apex' or 'Patient context: Follow-up scan after treatment'",
235
- key="image_prompt_input"
236
- )
237
- analyze_button = st.button("Analyze Image", key="analyze_image_button", type="primary")
238
-
239
- if analyze_button and image_file:
240
- # Display uploaded image immediately for confirmation
241
- st.image(image_file, caption="Uploaded Image Preview", use_column_width=True)
242
- with st.spinner("🖼️ Analyzing Image... Please wait."):
243
- analysis_result, error_message = analyze_medical_image(image_file, user_image_prompt)
244
-
245
- if error_message:
246
- with col2:
247
- st.error(f"Analysis Failed: {error_message}")
248
- elif analysis_result:
249
- with col2:
250
- st.header("Image Analysis Results")
251
- st.markdown(analysis_result)
252
- else:
253
- with col2:
254
- st.error("An unknown error occurred during analysis.")
255
-
256
- elif analyze_button and not image_file:
257
- st.warning("Please upload an image file to analyze.")
258
-
259
-
260
- # Display placeholder in result column if nothing submitted yet
261
- # Check if 'analysis_result' or 'error_message' exists in session state if needed for persistence,
262
- # but for this simpler flow, checking button presses is often sufficient.
263
- # A simple way is to check if the button was pressed in the current run.
264
- button_pressed = st.session_state.get('analyze_text_button', False) or st.session_state.get('analyze_image_button', False)
265
- if not button_pressed:
266
- with col2:
267
- st.info("Analysis results will appear here once input is submitted.")
268
-
269
-
270
- st.sidebar.markdown("---")
271
- st.sidebar.info("Remember: This AI tool is an assistant, not a substitute for professional medical expertise.")
272
 
273
  if __name__ == "__main__":
274
  main()
 
1
  import streamlit as st
2
  import google.generativeai as genai
3
  import os
4
+ # from PIL import Image # Removing image focus for this agentic text example
5
+ import io
6
  from typing import Optional, Tuple, Any # For type hinting
7
 
8
  # --- Configuration and Initialization ---
9
 
10
+ # Securely load API key (Prioritize Secrets, then Env Vars)
 
11
  GEMINI_API_KEY = st.secrets.get("GEMINI_API_KEY", os.environ.get("GEMINI_API_KEY"))
12
 
13
+ # Configure Gemini Client
14
  genai_client_configured = False
15
  if GEMINI_API_KEY:
16
  try:
17
  genai.configure(api_key=GEMINI_API_KEY)
18
  genai_client_configured = True
19
  except Exception as e:
20
+ st.error(f"Fatal Error: Failed to configure Google Generative AI. Check API Key and permissions. Details: {e}")
21
+ st.stop()
22
  else:
23
  st.error("⚠️ Gemini API Key not found. Please configure `GEMINI_API_KEY` in Streamlit secrets or environment variables.")
24
+ st.stop()
25
 
26
+ # Initialize models (Using specific models for consistency)
27
+ # Using a potentially more powerful model like 1.5 Pro might be beneficial for complex reasoning
28
+ MODEL_NAME = 'gemini-1.5-pro-latest' # Or 'gemini-1.5-flash' if speed is critical
 
29
 
30
  if genai_client_configured:
31
  try:
32
+ model = genai.GenerativeModel(MODEL_NAME)
33
+ # vision_model = genai.GenerativeModel('gemini-pro-vision') # Keep commented unless adding image capability back
34
  except Exception as e:
35
+ st.error(f"Fatal Error: Failed to initialize Gemini model ({MODEL_NAME}). Details: {e}")
36
  st.stop()
37
  else:
38
+ st.error("AI Model could not be initialized due to configuration issues.")
 
 
39
  st.stop()
40
 
41
 
42
+ # --- Core Agentic AI Simulation Function ---
43
 
44
+ # This prompt instructs the LLM to simulate a structured, agent-like reasoning process
45
+ # focused on differential diagnosis support.
46
+ AGENTIC_ANALYSIS_PROMPT_TEMPLATE = """
47
+ **Simulated Clinical Reasoning Agent Task:**
48
 
49
+ **Role:** You are an AI assistant simulating an agentic clinical reasoning process to support a healthcare professional. Your goal is NOT to diagnose, but to structure information, generate possibilities, and suggest logical next steps based *strictly* on the provided information.
 
 
 
 
 
 
50
 
51
+ **Input Data:** You will receive unstructured clinical information (e.g., symptoms, history, basic findings). Assume this is the *only* information available unless stated otherwise.
 
 
 
52
 
53
+ **Simulated Agentic Steps (Perform these sequentially in your response):**
54
+
55
+ 1. **Information Extraction & Structuring:**
56
+ * Identify and list the key patient demographics (age, sex, if provided).
57
+ * List the primary symptoms and signs presented.
58
+ * Summarize relevant medical history points.
59
+ * Note any explicitly mentioned negative findings (pertinent negatives).
60
+
61
+ 2. **Differential Considerations Generation:**
62
+ * Based *only* on the structured information from Step 1, generate a list of **potential differential considerations** (possible conditions that *could* explain the findings).
63
+ * **Use cautious and probabilistic language:** "could be consistent with," "warrants consideration," "less likely but possible," "should be ruled out." **AVOID definitive statements.**
64
+ * Briefly state the primary rationale linking each consideration to the key findings.
65
 
66
+ 3. **Information Gap Analysis:**
67
+ * Identify critical pieces of information typically needed for assessment that are missing from the input (e.g., specific lab results, imaging details, physical exam specifics, duration/onset details).
68
 
69
+ 4. **Suggested Next Steps for Investigation (for the Clinician):**
70
+ * Propose logical next steps a **healthcare professional might consider** to narrow down the possibilities or gather missing information.
71
+ * Categorize suggestions (e.g., Further History Taking, Physical Examination Points, Laboratory Tests, Imaging Studies).
72
+ * Frame these as *suggestions* for the clinician's judgment (e.g., "Consider ordering...", "Assessment of X may be informative", "Further questioning about Y could clarify...").
 
 
 
73
 
74
+ 5. **Mandatory Disclaimer:** Conclude with: "This AI-generated analysis is for informational support only. It is **NOT** a diagnosis and cannot replace the judgment of a qualified healthcare professional who must consider the full clinical context, conduct necessary examinations, and interpret investigations."
75
+
76
+ **Input Clinical Information:**
77
  ---
78
+ {text_input}
79
  ---
80
 
81
+ **Agentic Analysis:**
82
  """
83
 
84
+ def run_agentic_reasoning(text_input: str) -> Tuple[Optional[str], Optional[str]]:
85
  """
86
+ Simulates an agentic reasoning process on clinical text using the Gemini model.
87
 
88
  Args:
89
+ text_input: The clinical information provided by the user.
90
 
91
  Returns:
92
  A tuple containing:
93
+ - The structured analysis text (str) if successful, None otherwise.
94
  - An error message (str) if an error occurred, None otherwise.
95
  """
96
+ if not text_input or not text_input.strip():
97
  return None, "Input text cannot be empty."
98
  try:
99
+ prompt = AGENTIC_ANALYSIS_PROMPT_TEMPLATE.format(text_input=text_input)
100
+ # Consider adding safety settings if dealing with potentially sensitive outputs
101
+ # safety_settings = [...]
102
+ # response = model.generate_content(prompt, safety_settings=safety_settings)
103
  response = model.generate_content(prompt)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
 
105
+ # Check for blocked content or other issues
106
  if response.parts:
107
  return response.text, None
108
  elif response.prompt_feedback.block_reason:
109
+ return None, f"Analysis blocked by safety filters: {response.prompt_feedback.block_reason.name}. Please review input for potentially harmful content or adjust safety settings if appropriate."
110
  else:
111
+ # Handle cases where the response might be empty but not explicitly blocked
112
+ candidate = response.candidates[0] if response.candidates else None
113
+ if candidate and candidate.finish_reason != "STOP":
114
+ return None, f"Analysis stopped prematurely. Reason: {candidate.finish_reason.name}. The input might be too complex or ambiguous."
115
+ else:
116
+ return None, "Received an empty or unexpected response from the AI model. The model may not have been able to process the request."
117
 
118
  except Exception as e:
119
+ st.error(f"Critical Error during AI Analysis: {e}", icon="🚨") # Log for debugging
120
+ # Provide a user-friendly error message
121
+ return None, f"An error occurred while communicating with the AI model. Please try again later or check the input. Details: {e}"
122
 
123
 
124
  # --- Streamlit User Interface ---
125
 
126
  def main():
127
+ st.set_page_config(
128
+ page_title="Agentic AI Clinical Reasoning Support",
129
+ layout="wide",
130
+ initial_sidebar_state="expanded"
131
+ )
132
 
133
+ # --- Header ---
134
+ st.title("🤖 Agentic AI: Clinical Reasoning Support Tool")
135
+ st.caption(f"Powered by Google Gemini ({MODEL_NAME})")
136
+ st.markdown("---")
137
 
138
  # --- CRITICAL DISCLAIMER ---
139
  st.warning(
140
  """
141
+ **🔴 EXTREMELY IMPORTANT DISCLAIMER 🔴**
142
+ * This tool **SIMULATES** an AI reasoning process. It **DOES NOT DIAGNOSE** diseases or provide medical advice.
143
+ * Outputs are based **SOLELY** on the text input and the AI's internal knowledge, which may be incomplete or contain inaccuracies. **It lacks real-world clinical context.**
144
+ * **NEVER** use this tool for actual patient diagnosis, treatment decisions, or clinical management. It is intended for **educational and conceptual purposes ONLY**, potentially aiding clinicians in organizing thoughts or exploring possibilities.
145
+ * **ALWAYS rely on the expertise and judgment of qualified healthcare professionals.**
146
+ * **PRIVACY ALERT:** Do **NOT** enter identifiable patient information (PHI) unless you comply with all legal and ethical requirements (e.g., HIPAA, GDPR, patient consent). You are responsible for the data you input.
147
+ """,
148
+ icon="⚠️"
149
  )
150
+ st.markdown("---")
151
+
152
+ # --- Input Area ---
153
+ st.header("Clinical Information Input")
154
+ st.markdown("Enter de-identified clinical information below (e.g., symptoms, brief history, key findings). The AI will attempt a structured analysis.")
155
 
156
+ text_input = st.text_area(
157
+ "Enter Clinical Data:",
158
+ height=300,
159
+ placeholder="Example: A 68-year-old male presents with sudden onset shortness of breath and pleuritic chest pain. History of recent long-haul flight. Vitals show tachycardia (HR 110) and mild hypoxia (SpO2 92% on room air). No significant cardiac history mentioned...",
160
+ key="clinical_text_input"
161
  )
162
 
163
+ # --- Action Button ---
164
+ analyze_button = st.button("▶️ Run Agentic Analysis", key="analyze_button", type="primary", help="Click to start the simulated reasoning process.")
165
+
166
+ st.markdown("---") # Separator before results
167
+
168
+ # --- Output Area ---
169
+ st.header("Analysis Results")
170
+
171
+ if analyze_button:
172
+ if text_input:
173
+ with st.spinner("🧠 Simulating agentic reasoning... Please wait."):
174
+ analysis_result, error_message = run_agentic_reasoning(text_input)
175
+
176
+ if error_message:
177
+ st.error(f"Analysis Failed: {error_message}", icon="")
178
+ elif analysis_result:
179
+ st.markdown("**Simulated Agent Analysis Output:**")
180
+ st.markdown(analysis_result) # Use markdown for better formatting
181
+ else:
182
+ st.error("An unexpected issue occurred. No analysis was returned.", icon="❓")
183
+ else:
184
+ st.warning("Please enter clinical information in the text area above before analyzing.", icon="☝️")
185
+ else:
186
+ st.info("Analysis results will appear here after you enter information and click 'Run Agentic Analysis'.")
187
+
188
+ # --- Sidebar for Explanation/Prompt ---
189
+ st.sidebar.header("About This Tool")
190
+ st.sidebar.info(
191
+ "This application demonstrates how an AI model (Gemini) can be prompted to simulate "
192
+ "a structured, agent-like approach to analyzing clinical information. It focuses on "
193
+ "differential considerations and suggesting investigation pathways for **clinician review**."
194
+ )
195
+ with st.sidebar.expander("View the Agentic Prompt Structure"):
196
+ st.markdown(f"```plaintext\n{AGENTIC_ANALYSIS_PROMPT_TEMPLATE.split('---')[0]} ... [Input Text] ...\n```")
197
+ st.caption("The prompt guides the AI to break down the task into logical steps.")
198
+
199
+ st.sidebar.header("Ethical Considerations")
200
+ st.sidebar.error(
201
+ "**Crucial Reminder:** This AI is a tool, not a clinician. It has limitations and biases. "
202
+ "Clinical decisions require human expertise, empathy, and comprehensive patient assessment. "
203
+ "Misuse can lead to harm."
204
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
 
206
  if __name__ == "__main__":
207
  main()