Update app.py
Browse files
app.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
import streamlit as st
|
| 2 |
import google.generativeai as genai
|
| 3 |
import os
|
|
@@ -17,7 +18,7 @@ st.markdown(
|
|
| 17 |
"""
|
| 18 |
### Welcome to the AI Clinical Support Demonstrator
|
| 19 |
This application demonstrates how Generative AI (Google Gemini) can be guided to assist with analyzing clinical information.
|
| 20 |
-
- **Agentic Text Analysis:** Simulates a structured reasoning process on clinical text
|
| 21 |
- **Medical Image Analysis:** Provides descriptive observations of potential anomalies in medical images.
|
| 22 |
|
| 23 |
**Crucially, this tool is for demonstration purposes ONLY. It does NOT provide medical advice or diagnosis.**
|
|
@@ -43,11 +44,10 @@ else:
|
|
| 43 |
st.error("โ ๏ธ Gemini API Key not found. Please configure `GEMINI_API_KEY` in Streamlit secrets or environment variables.", icon="๐")
|
| 44 |
st.stop()
|
| 45 |
|
| 46 |
-
# Initialize models
|
| 47 |
TEXT_MODEL_NAME = 'gemini-1.5-pro-latest' # For agentic text reasoning
|
| 48 |
-
VISION_MODEL_NAME = 'gemini-1.5-flash' # For image analysis
|
| 49 |
|
| 50 |
-
# Use session state to initialize models only once
|
| 51 |
if 'models_initialized' not in st.session_state:
|
| 52 |
st.session_state.models_initialized = False
|
| 53 |
st.session_state.text_model = None
|
|
@@ -58,20 +58,17 @@ if genai_client_configured and not st.session_state.models_initialized:
|
|
| 58 |
st.session_state.text_model = genai.GenerativeModel(TEXT_MODEL_NAME)
|
| 59 |
st.session_state.vision_model = genai.GenerativeModel(VISION_MODEL_NAME)
|
| 60 |
st.session_state.models_initialized = True
|
| 61 |
-
# Display success message subtly in sidebar perhaps, or remove if too noisy
|
| 62 |
-
# st.sidebar.success("AI Models Ready.", icon="โ
")
|
| 63 |
except Exception as e:
|
| 64 |
st.error(f"Fatal Error: Failed to initialize Gemini models. Text: {TEXT_MODEL_NAME}, Vision: {VISION_MODEL_NAME}. Details: {e}", icon="๐ฅ")
|
| 65 |
st.stop()
|
| 66 |
elif not genai_client_configured:
|
| 67 |
-
# Should have stopped already, but defensive check
|
| 68 |
st.error("AI Models could not be initialized due to configuration issues.", icon="๐ซ")
|
| 69 |
st.stop()
|
| 70 |
|
| 71 |
|
| 72 |
# --- Core AI Interaction Functions ---
|
| 73 |
|
| 74 |
-
#
|
| 75 |
AGENTIC_TEXT_ANALYSIS_PROMPT_TEMPLATE = """
|
| 76 |
**Simulated Clinical Reasoning Agent Task:**
|
| 77 |
|
|
@@ -83,26 +80,26 @@ AGENTIC_TEXT_ANALYSIS_PROMPT_TEMPLATE = """
|
|
| 83 |
|
| 84 |
**Simulated Agentic Steps (Perform sequentially):**
|
| 85 |
|
| 86 |
-
1. **Information Extraction & Structuring:**
|
| 87 |
* Key Demographics (Age, Sex if provided).
|
| 88 |
* Primary Symptoms/Signs.
|
| 89 |
* Relevant Medical History.
|
| 90 |
* Pertinent Negatives (if mentioned).
|
| 91 |
|
| 92 |
-
2. **Differential Considerations Generation:**
|
| 93 |
* Based *only* on Step 1, list **potential differential considerations** (possible conditions).
|
| 94 |
* **Use cautious language:** "could be consistent with," "warrants consideration," "less likely but possible." **AVOID definitive statements.**
|
| 95 |
* Briefly justify each consideration based on findings.
|
| 96 |
|
| 97 |
-
3. **Information Gap Analysis:**
|
| 98 |
* Identify critical missing information (e.g., lab results, imaging, exam specifics, duration/onset).
|
| 99 |
|
| 100 |
-
4. **Suggested Next Steps for Investigation (for Clinician):**
|
| 101 |
* Propose logical next steps a **healthcare professional might consider**.
|
| 102 |
* Categorize (e.g., Further History, Exam Points, Labs, Imaging).
|
| 103 |
* Frame as *suggestions* (e.g., "Consider ordering...", "Assessment of X may be informative").
|
| 104 |
|
| 105 |
-
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."
|
| 106 |
|
| 107 |
**Input Clinical Information:**
|
| 108 |
---
|
|
@@ -112,21 +109,46 @@ AGENTIC_TEXT_ANALYSIS_PROMPT_TEMPLATE = """
|
|
| 112 |
**Agentic Analysis:**
|
| 113 |
"""
|
| 114 |
|
| 115 |
-
#
|
| 116 |
IMAGE_ANALYSIS_PROMPT_TEMPLATE = """
|
| 117 |
**Medical Image Analysis Request:**
|
| 118 |
|
| 119 |
-
**Context:** Analyze the provided medical image. User may provide additional context or questions.
|
| 120 |
|
| 121 |
-
**Output Format:**
|
| 122 |
|
| 123 |
**Task:**
|
| 124 |
|
| 125 |
-
1. **
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
|
| 131 |
**User's Additional Context/Question (if any):**
|
| 132 |
---
|
|
@@ -136,68 +158,76 @@ IMAGE_ANALYSIS_PROMPT_TEMPLATE = """
|
|
| 136 |
**Image Analysis:**
|
| 137 |
"""
|
| 138 |
|
|
|
|
|
|
|
| 139 |
def run_agentic_text_analysis(text_input: str) -> Tuple[Optional[str], Optional[str]]:
|
| 140 |
"""Sends clinical text to the configured text model for simulated agentic analysis."""
|
| 141 |
if not text_input or not text_input.strip():
|
| 142 |
return None, "Input text cannot be empty."
|
| 143 |
if not st.session_state.models_initialized or not st.session_state.text_model:
|
| 144 |
-
return None, "Text analysis model not initialized."
|
| 145 |
|
| 146 |
try:
|
| 147 |
prompt = AGENTIC_TEXT_ANALYSIS_PROMPT_TEMPLATE.format(text_input=text_input)
|
| 148 |
-
response = st.session_state.text_model.generate_content(prompt)
|
| 149 |
|
|
|
|
| 150 |
if response.parts:
|
| 151 |
return response.text, None
|
| 152 |
elif response.prompt_feedback.block_reason:
|
| 153 |
-
return None, f"Analysis blocked by safety filters: {response.prompt_feedback.block_reason.name}.
|
| 154 |
else:
|
| 155 |
candidate = response.candidates[0] if response.candidates else None
|
| 156 |
if candidate and candidate.finish_reason != "STOP":
|
| 157 |
-
|
|
|
|
| 158 |
else:
|
| 159 |
-
|
|
|
|
| 160 |
|
| 161 |
except Exception as e:
|
| 162 |
-
print(f"ERROR in run_agentic_text_analysis: {e}") #
|
| 163 |
-
st.error("An error occurred during text analysis.", icon="๐จ") # User-facing error
|
| 164 |
-
return None, "An internal error occurred during text analysis. Please try again later."
|
| 165 |
|
| 166 |
def analyze_medical_image(image_file: Any, user_prompt: str = "") -> Tuple[Optional[str], Optional[str]]:
|
| 167 |
-
"""Sends a medical image to the configured vision model for analysis."""
|
| 168 |
if not image_file:
|
| 169 |
return None, "Image file cannot be empty."
|
| 170 |
if not st.session_state.models_initialized or not st.session_state.vision_model:
|
| 171 |
-
return None, "Image analysis model not initialized."
|
| 172 |
|
| 173 |
try:
|
| 174 |
try:
|
|
|
|
| 175 |
image = Image.open(image_file)
|
| 176 |
-
# Convert to RGB ensure compatibility, common requirement for vision models
|
| 177 |
if image.mode != 'RGB':
|
| 178 |
-
image = image.convert('RGB')
|
| 179 |
except Exception as img_e:
|
| 180 |
-
return None, f"Error opening or processing the uploaded image file: {img_e}"
|
| 181 |
|
|
|
|
| 182 |
prompt_text = IMAGE_ANALYSIS_PROMPT_TEMPLATE.format(user_prompt=user_prompt if user_prompt else "N/A")
|
| 183 |
-
model_input = [prompt_text, image]
|
| 184 |
-
|
|
|
|
| 185 |
|
|
|
|
| 186 |
if response.parts:
|
| 187 |
return response.text, None
|
| 188 |
elif response.prompt_feedback.block_reason:
|
| 189 |
-
return None, f"Image analysis blocked by safety filters: {response.prompt_feedback.block_reason.name}. This
|
| 190 |
else:
|
| 191 |
candidate = response.candidates[0] if response.candidates else None
|
| 192 |
if candidate and candidate.finish_reason != "STOP":
|
| 193 |
-
return None, f"Image analysis stopped prematurely. Reason: {candidate.finish_reason.name}."
|
| 194 |
else:
|
| 195 |
return None, "Received an empty or unexpected response from the AI model for image analysis."
|
| 196 |
|
| 197 |
except Exception as e:
|
| 198 |
-
print(f"ERROR in analyze_medical_image: {e}") #
|
| 199 |
-
st.error("An error occurred during image analysis.", icon="๐ผ๏ธ") # User-facing error
|
| 200 |
-
return None, "An internal error occurred during image analysis. Please try again later."
|
| 201 |
|
| 202 |
|
| 203 |
# --- Streamlit User Interface ---
|
|
@@ -208,7 +238,6 @@ def main():
|
|
| 208 |
st.caption(f"Utilizing: Text Model ({TEXT_MODEL_NAME}), Vision Model ({VISION_MODEL_NAME})")
|
| 209 |
|
| 210 |
# --- CRITICAL DISCLAIMER ---
|
| 211 |
-
# Positioned prominently after title/intro
|
| 212 |
st.warning(
|
| 213 |
"""
|
| 214 |
**๐ด IMPORTANT SAFETY & USE DISCLAIMER ๐ด**
|
|
@@ -236,9 +265,10 @@ def main():
|
|
| 236 |
# --- Main Area Layout (Input and Output Columns) ---
|
| 237 |
col1, col2 = st.columns(2)
|
| 238 |
|
| 239 |
-
analysis_result = None #
|
| 240 |
error_message = None
|
| 241 |
output_header = "Analysis Results" # Default header
|
|
|
|
| 242 |
|
| 243 |
# --- Column 1: Input Area ---
|
| 244 |
with col1:
|
|
@@ -254,12 +284,12 @@ def main():
|
|
| 254 |
placeholder="Example: 68yo male, sudden SOB & pleuritic chest pain post-flight. HR 110, SpO2 92% RA. No known cardiac hx...",
|
| 255 |
key="text_input_area"
|
| 256 |
)
|
| 257 |
-
analyze_button_key = "analyze_text_button"
|
| 258 |
analyze_button_label = "โถ๏ธ Run Agentic Text Analysis"
|
| 259 |
|
| 260 |
if st.button(analyze_button_label, key=analyze_button_key, type="primary"):
|
| 261 |
if text_input:
|
| 262 |
-
with st.spinner("๐ง Simulating agentic reasoning..."):
|
| 263 |
analysis_result, error_message = run_agentic_text_analysis(text_input)
|
| 264 |
output_header = "Simulated Agentic Analysis Output"
|
| 265 |
else:
|
|
@@ -278,7 +308,7 @@ def main():
|
|
| 278 |
placeholder="Example: 'Describe findings in the lung fields' or 'Any visible fractures?'",
|
| 279 |
key="image_prompt_input"
|
| 280 |
)
|
| 281 |
-
analyze_button_key = "analyze_image_button"
|
| 282 |
analyze_button_label = "๐ผ๏ธ Analyze Medical Image"
|
| 283 |
|
| 284 |
if image_file:
|
|
@@ -287,7 +317,7 @@ def main():
|
|
| 287 |
|
| 288 |
if st.button(analyze_button_label, key=analyze_button_key, type="primary"):
|
| 289 |
if image_file:
|
| 290 |
-
with st.spinner("๐๏ธ Analyzing image..."):
|
| 291 |
analysis_result, error_message = analyze_medical_image(image_file, user_image_prompt)
|
| 292 |
output_header = "Medical Image Analysis Output"
|
| 293 |
else:
|
|
@@ -297,30 +327,33 @@ def main():
|
|
| 297 |
with col2:
|
| 298 |
st.header(output_header)
|
| 299 |
|
| 300 |
-
#
|
| 301 |
-
|
| 302 |
-
button_pressed = st.session_state.get(analyze_button_key, False) if 'analyze_button_key' in locals() else False
|
| 303 |
|
| 304 |
-
if button_pressed
|
| 305 |
-
|
|
|
|
| 306 |
st.error(f"Analysis Failed: {error_message}", icon="โ")
|
| 307 |
-
|
| 308 |
-
# Use st.markdown to render potential formatting from AI
|
| 309 |
-
st.markdown(analysis_result)
|
| 310 |
-
|
|
|
|
|
|
|
| 311 |
st.info("Analysis results will appear here after providing input and clicking the corresponding analysis button.")
|
| 312 |
-
# Add a condition for button pressed but no input provided (already handled by st.warning in col1)
|
| 313 |
|
| 314 |
|
| 315 |
# --- Sidebar Explanations ---
|
| 316 |
st.sidebar.markdown("---")
|
| 317 |
st.sidebar.header("About The Prompts")
|
| 318 |
with st.sidebar.expander("View Agentic Text Prompt Structure", icon="๐"):
|
| 319 |
-
|
|
|
|
| 320 |
st.caption("Guides the AI through structured reasoning steps for text.")
|
| 321 |
with st.sidebar.expander("View Image Analysis Prompt Structure", icon="๐ผ๏ธ"):
|
| 322 |
-
|
| 323 |
-
st.
|
|
|
|
| 324 |
|
| 325 |
st.sidebar.markdown("---")
|
| 326 |
st.sidebar.error(
|
|
@@ -334,7 +367,7 @@ if __name__ == "__main__":
|
|
| 334 |
if st.session_state.models_initialized:
|
| 335 |
main()
|
| 336 |
else:
|
| 337 |
-
#
|
| 338 |
-
#
|
| 339 |
-
|
| 340 |
-
|
|
|
|
| 1 |
+
# -*- coding: utf-8 -*-
|
| 2 |
import streamlit as st
|
| 3 |
import google.generativeai as genai
|
| 4 |
import os
|
|
|
|
| 18 |
"""
|
| 19 |
### Welcome to the AI Clinical Support Demonstrator
|
| 20 |
This application demonstrates how Generative AI (Google Gemini) can be guided to assist with analyzing clinical information.
|
| 21 |
+
- **Agentic Text Analysis:** Simulates a structured reasoning process on clinical text.
|
| 22 |
- **Medical Image Analysis:** Provides descriptive observations of potential anomalies in medical images.
|
| 23 |
|
| 24 |
**Crucially, this tool is for demonstration purposes ONLY. It does NOT provide medical advice or diagnosis.**
|
|
|
|
| 44 |
st.error("โ ๏ธ Gemini API Key not found. Please configure `GEMINI_API_KEY` in Streamlit secrets or environment variables.", icon="๐")
|
| 45 |
st.stop()
|
| 46 |
|
| 47 |
+
# Initialize models using Session State for persistence across reruns
|
| 48 |
TEXT_MODEL_NAME = 'gemini-1.5-pro-latest' # For agentic text reasoning
|
| 49 |
+
VISION_MODEL_NAME = 'gemini-1.5-flash' # For image analysis (or 'gemini-pro-vision')
|
| 50 |
|
|
|
|
| 51 |
if 'models_initialized' not in st.session_state:
|
| 52 |
st.session_state.models_initialized = False
|
| 53 |
st.session_state.text_model = None
|
|
|
|
| 58 |
st.session_state.text_model = genai.GenerativeModel(TEXT_MODEL_NAME)
|
| 59 |
st.session_state.vision_model = genai.GenerativeModel(VISION_MODEL_NAME)
|
| 60 |
st.session_state.models_initialized = True
|
|
|
|
|
|
|
| 61 |
except Exception as e:
|
| 62 |
st.error(f"Fatal Error: Failed to initialize Gemini models. Text: {TEXT_MODEL_NAME}, Vision: {VISION_MODEL_NAME}. Details: {e}", icon="๐ฅ")
|
| 63 |
st.stop()
|
| 64 |
elif not genai_client_configured:
|
|
|
|
| 65 |
st.error("AI Models could not be initialized due to configuration issues.", icon="๐ซ")
|
| 66 |
st.stop()
|
| 67 |
|
| 68 |
|
| 69 |
# --- Core AI Interaction Functions ---
|
| 70 |
|
| 71 |
+
# AGENTIC prompt for Text Analysis (Remains the same as previous good version)
|
| 72 |
AGENTIC_TEXT_ANALYSIS_PROMPT_TEMPLATE = """
|
| 73 |
**Simulated Clinical Reasoning Agent Task:**
|
| 74 |
|
|
|
|
| 80 |
|
| 81 |
**Simulated Agentic Steps (Perform sequentially):**
|
| 82 |
|
| 83 |
+
1. **## 1. Information Extraction & Structuring:**
|
| 84 |
* Key Demographics (Age, Sex if provided).
|
| 85 |
* Primary Symptoms/Signs.
|
| 86 |
* Relevant Medical History.
|
| 87 |
* Pertinent Negatives (if mentioned).
|
| 88 |
|
| 89 |
+
2. **## 2. Differential Considerations Generation:**
|
| 90 |
* Based *only* on Step 1, list **potential differential considerations** (possible conditions).
|
| 91 |
* **Use cautious language:** "could be consistent with," "warrants consideration," "less likely but possible." **AVOID definitive statements.**
|
| 92 |
* Briefly justify each consideration based on findings.
|
| 93 |
|
| 94 |
+
3. **## 3. Information Gap Analysis:**
|
| 95 |
* Identify critical missing information (e.g., lab results, imaging, exam specifics, duration/onset).
|
| 96 |
|
| 97 |
+
4. **## 4. Suggested Next Steps for Investigation (for Clinician):**
|
| 98 |
* Propose logical next steps a **healthcare professional might consider**.
|
| 99 |
* Categorize (e.g., Further History, Exam Points, Labs, Imaging).
|
| 100 |
* Frame as *suggestions* (e.g., "Consider ordering...", "Assessment of X may be informative").
|
| 101 |
|
| 102 |
+
5. **## 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."
|
| 103 |
|
| 104 |
**Input Clinical Information:**
|
| 105 |
---
|
|
|
|
| 109 |
**Agentic Analysis:**
|
| 110 |
"""
|
| 111 |
|
| 112 |
+
# REFINED prompt for Image Analysis - More explicit instructions based on good example
|
| 113 |
IMAGE_ANALYSIS_PROMPT_TEMPLATE = """
|
| 114 |
**Medical Image Analysis Request:**
|
| 115 |
|
| 116 |
+
**Context:** Analyze the provided medical image objectively based *only* on visual information. User may provide additional context or questions.
|
| 117 |
|
| 118 |
+
**Output Format:** Structure your response precisely using the following Markdown headings. Be factual and descriptive.
|
| 119 |
|
| 120 |
**Task:**
|
| 121 |
|
| 122 |
+
1. **## 1. Visible Structures:**
|
| 123 |
+
* Identify the likely imaging modality and view (e.g., PA Chest Radiograph, Axial CT slice of the abdomen).
|
| 124 |
+
* Briefly list the main anatomical structures clearly visible (e.g., ribs, heart silhouette, lung fields, diaphragm).
|
| 125 |
+
|
| 126 |
+
2. **## 2. Identify Potential Anomalies / Key Findings:**
|
| 127 |
+
* Carefully examine the image for any areas that *appear* abnormal or deviate significantly from typical presentation.
|
| 128 |
+
* **Use extremely cautious, descriptive language.** Describe *what* you see (e.g., "area of increased opacity," "region of lucency," "asymmetry observed in X," "potential contour abnormality," "patchy distribution").
|
| 129 |
+
* **Specify location accurately** using standard anatomical terms (e.g., "right lower lung zone," "left hilum," "hepatic flexure region").
|
| 130 |
+
* **Crucially, AVOID interpretive or diagnostic terms** (DO NOT use words like "pneumonia," "tumor," "fracture," "infection," "inflammation"). Stick strictly to visual observation.
|
| 131 |
+
* If relevant and clearly discernible, mention the **absence** of certain major expected abnormalities (pertinent negatives, e.g., "No obvious large pneumothorax identified," "Bowel gas pattern appears unremarkable in visualized areas").
|
| 132 |
+
* Compare sides if applicable and relevant differences are seen (e.g., "Left lung field demonstrates greater transparency compared to the right").
|
| 133 |
+
|
| 134 |
+
3. **## 3. Correlate with User Prompt (if provided):**
|
| 135 |
+
* Address specific user questions based *strictly* on the visual information identifiable in the image.
|
| 136 |
+
* If the image cannot visually answer the question (e.g., requires clinical context, different view), state that clearly.
|
| 137 |
+
* If no user prompt was provided, state "N/A".
|
| 138 |
+
|
| 139 |
+
4. **## 4. Limitations of this AI Analysis:**
|
| 140 |
+
* **Explicitly list the following limitations inherent to this analysis:**
|
| 141 |
+
* Dependency on the **quality, resolution, and potential artifacts** of the provided image.
|
| 142 |
+
* Analysis is restricted to the **single view/slice(s)** provided; other areas are not assessed.
|
| 143 |
+
* **Complete lack of clinical context:** Patient history, symptoms, physical exam findings, and laboratory results are unknown and not considered.
|
| 144 |
+
* **Absence of prior imaging studies:** Comparisons over time are not possible, which is often crucial for interpretation.
|
| 145 |
+
* The AI functions purely on **visual pattern recognition**; it does not perform clinical reasoning or differential diagnosis.
|
| 146 |
+
|
| 147 |
+
5. **## 5. Mandatory Disclaimer:**
|
| 148 |
+
* State clearly: This is an AI-generated visual analysis intended for informational and demonstration purposes **ONLY**.
|
| 149 |
+
* It is **NOT** a radiological interpretation or medical diagnosis.
|
| 150 |
+
* It **CANNOT** substitute for a comprehensive evaluation and interpretation by a qualified radiologist or physician integrating full clinical information.
|
| 151 |
+
* Any potential observations noted herein **MUST** be correlated with clinical findings and reviewed/confirmed by qualified healthcare professionals.
|
| 152 |
|
| 153 |
**User's Additional Context/Question (if any):**
|
| 154 |
---
|
|
|
|
| 158 |
**Image Analysis:**
|
| 159 |
"""
|
| 160 |
|
| 161 |
+
# --- Backend Functions ---
|
| 162 |
+
|
| 163 |
def run_agentic_text_analysis(text_input: str) -> Tuple[Optional[str], Optional[str]]:
|
| 164 |
"""Sends clinical text to the configured text model for simulated agentic analysis."""
|
| 165 |
if not text_input or not text_input.strip():
|
| 166 |
return None, "Input text cannot be empty."
|
| 167 |
if not st.session_state.models_initialized or not st.session_state.text_model:
|
| 168 |
+
return None, "Text analysis model not initialized. Please refresh or check configuration."
|
| 169 |
|
| 170 |
try:
|
| 171 |
prompt = AGENTIC_TEXT_ANALYSIS_PROMPT_TEMPLATE.format(text_input=text_input)
|
| 172 |
+
response = st.session_state.text_model.generate_content(prompt) # Use model from session state
|
| 173 |
|
| 174 |
+
# Handle response variations
|
| 175 |
if response.parts:
|
| 176 |
return response.text, None
|
| 177 |
elif response.prompt_feedback.block_reason:
|
| 178 |
+
return None, f"Analysis blocked by safety filters: {response.prompt_feedback.block_reason.name}. Please review or revise input."
|
| 179 |
else:
|
| 180 |
candidate = response.candidates[0] if response.candidates else None
|
| 181 |
if candidate and candidate.finish_reason != "STOP":
|
| 182 |
+
# If stopped for reasons other than normal completion (e.g., length, safety)
|
| 183 |
+
return None, f"Analysis stopped prematurely. Reason: {candidate.finish_reason.name}. Input might be too long or triggered other limits."
|
| 184 |
else:
|
| 185 |
+
# General case for empty or unexpected response
|
| 186 |
+
return None, "Received an empty or unexpected response from the AI model for text analysis."
|
| 187 |
|
| 188 |
except Exception as e:
|
| 189 |
+
print(f"ERROR in run_agentic_text_analysis: {e}") # Log for server/dev console
|
| 190 |
+
st.error("An error occurred during text analysis.", icon="๐จ") # User-facing generic error
|
| 191 |
+
return None, "An internal error occurred during text analysis. Please try again later or contact support if the issue persists."
|
| 192 |
|
| 193 |
def analyze_medical_image(image_file: Any, user_prompt: str = "") -> Tuple[Optional[str], Optional[str]]:
|
| 194 |
+
"""Sends a medical image to the configured vision model for analysis using the refined prompt."""
|
| 195 |
if not image_file:
|
| 196 |
return None, "Image file cannot be empty."
|
| 197 |
if not st.session_state.models_initialized or not st.session_state.vision_model:
|
| 198 |
+
return None, "Image analysis model not initialized. Please refresh or check configuration."
|
| 199 |
|
| 200 |
try:
|
| 201 |
try:
|
| 202 |
+
# Open and prepare the image
|
| 203 |
image = Image.open(image_file)
|
|
|
|
| 204 |
if image.mode != 'RGB':
|
| 205 |
+
image = image.convert('RGB') # Ensure RGB format
|
| 206 |
except Exception as img_e:
|
| 207 |
+
return None, f"Error opening or processing the uploaded image file: {img_e}. Please ensure it's a valid image."
|
| 208 |
|
| 209 |
+
# Prepare the prompt using the refined template
|
| 210 |
prompt_text = IMAGE_ANALYSIS_PROMPT_TEMPLATE.format(user_prompt=user_prompt if user_prompt else "N/A")
|
| 211 |
+
model_input = [prompt_text, image] # Combine text prompt and image data
|
| 212 |
+
|
| 213 |
+
response = st.session_state.vision_model.generate_content(model_input) # Use model from session state
|
| 214 |
|
| 215 |
+
# Handle response variations
|
| 216 |
if response.parts:
|
| 217 |
return response.text, None
|
| 218 |
elif response.prompt_feedback.block_reason:
|
| 219 |
+
return None, f"Image analysis blocked by safety filters: {response.prompt_feedback.block_reason.name}. This might relate to sensitive content policies regarding medical images."
|
| 220 |
else:
|
| 221 |
candidate = response.candidates[0] if response.candidates else None
|
| 222 |
if candidate and candidate.finish_reason != "STOP":
|
| 223 |
+
return None, f"Image analysis stopped prematurely. Reason: {candidate.finish_reason.name}. Input might be too complex or triggered other limits."
|
| 224 |
else:
|
| 225 |
return None, "Received an empty or unexpected response from the AI model for image analysis."
|
| 226 |
|
| 227 |
except Exception as e:
|
| 228 |
+
print(f"ERROR in analyze_medical_image: {e}") # Log for server/dev console
|
| 229 |
+
st.error("An error occurred during image analysis.", icon="๐ผ๏ธ") # User-facing generic error
|
| 230 |
+
return None, "An internal error occurred during image analysis. Please try again later or contact support if the issue persists."
|
| 231 |
|
| 232 |
|
| 233 |
# --- Streamlit User Interface ---
|
|
|
|
| 238 |
st.caption(f"Utilizing: Text Model ({TEXT_MODEL_NAME}), Vision Model ({VISION_MODEL_NAME})")
|
| 239 |
|
| 240 |
# --- CRITICAL DISCLAIMER ---
|
|
|
|
| 241 |
st.warning(
|
| 242 |
"""
|
| 243 |
**๐ด IMPORTANT SAFETY & USE DISCLAIMER ๐ด**
|
|
|
|
| 265 |
# --- Main Area Layout (Input and Output Columns) ---
|
| 266 |
col1, col2 = st.columns(2)
|
| 267 |
|
| 268 |
+
analysis_result = None # Reset results variables for this run
|
| 269 |
error_message = None
|
| 270 |
output_header = "Analysis Results" # Default header
|
| 271 |
+
analyze_button_key = None # Initialize key variable
|
| 272 |
|
| 273 |
# --- Column 1: Input Area ---
|
| 274 |
with col1:
|
|
|
|
| 284 |
placeholder="Example: 68yo male, sudden SOB & pleuritic chest pain post-flight. HR 110, SpO2 92% RA. No known cardiac hx...",
|
| 285 |
key="text_input_area"
|
| 286 |
)
|
| 287 |
+
analyze_button_key = "analyze_text_button" # Set key for this branch
|
| 288 |
analyze_button_label = "โถ๏ธ Run Agentic Text Analysis"
|
| 289 |
|
| 290 |
if st.button(analyze_button_label, key=analyze_button_key, type="primary"):
|
| 291 |
if text_input:
|
| 292 |
+
with st.spinner("๐ง Simulating agentic reasoning... Please wait."):
|
| 293 |
analysis_result, error_message = run_agentic_text_analysis(text_input)
|
| 294 |
output_header = "Simulated Agentic Analysis Output"
|
| 295 |
else:
|
|
|
|
| 308 |
placeholder="Example: 'Describe findings in the lung fields' or 'Any visible fractures?'",
|
| 309 |
key="image_prompt_input"
|
| 310 |
)
|
| 311 |
+
analyze_button_key = "analyze_image_button" # Set key for this branch
|
| 312 |
analyze_button_label = "๐ผ๏ธ Analyze Medical Image"
|
| 313 |
|
| 314 |
if image_file:
|
|
|
|
| 317 |
|
| 318 |
if st.button(analyze_button_label, key=analyze_button_key, type="primary"):
|
| 319 |
if image_file:
|
| 320 |
+
with st.spinner("๐๏ธ Analyzing image... Please wait."):
|
| 321 |
analysis_result, error_message = analyze_medical_image(image_file, user_image_prompt)
|
| 322 |
output_header = "Medical Image Analysis Output"
|
| 323 |
else:
|
|
|
|
| 327 |
with col2:
|
| 328 |
st.header(output_header)
|
| 329 |
|
| 330 |
+
# Determine if analysis was attempted in this run using the correct button key
|
| 331 |
+
button_pressed = st.session_state.get(analyze_button_key, False) if analyze_button_key else False
|
|
|
|
| 332 |
|
| 333 |
+
if button_pressed:
|
| 334 |
+
# Only display results if the corresponding button was pressed AND generated output/error
|
| 335 |
+
if error_message:
|
| 336 |
st.error(f"Analysis Failed: {error_message}", icon="โ")
|
| 337 |
+
elif analysis_result:
|
| 338 |
+
# Use st.markdown to render potential formatting (like headings) from AI
|
| 339 |
+
st.markdown(analysis_result, unsafe_allow_html=False) # Keep unsafe_allow_html=False for security
|
| 340 |
+
# No explicit 'else' needed here; if button pressed but no result/error, likely handled by input validation warning
|
| 341 |
+
else:
|
| 342 |
+
# Default placeholder message if no analysis attempted yet in this run
|
| 343 |
st.info("Analysis results will appear here after providing input and clicking the corresponding analysis button.")
|
|
|
|
| 344 |
|
| 345 |
|
| 346 |
# --- Sidebar Explanations ---
|
| 347 |
st.sidebar.markdown("---")
|
| 348 |
st.sidebar.header("About The Prompts")
|
| 349 |
with st.sidebar.expander("View Agentic Text Prompt Structure", icon="๐"):
|
| 350 |
+
# Show only the instructive part of the prompt template
|
| 351 |
+
st.code(AGENTIC_TEXT_ANALYSIS_PROMPT_TEMPLATE.split('---')[0] + "...", language='markdown')
|
| 352 |
st.caption("Guides the AI through structured reasoning steps for text.")
|
| 353 |
with st.sidebar.expander("View Image Analysis Prompt Structure", icon="๐ผ๏ธ"):
|
| 354 |
+
# Show only the instructive part of the prompt template
|
| 355 |
+
st.code(IMAGE_ANALYSIS_PROMPT_TEMPLATE.split('---')[0] + "...", language='markdown')
|
| 356 |
+
st.caption("Guides the AI to provide cautious, descriptive visual observations for images.")
|
| 357 |
|
| 358 |
st.sidebar.markdown("---")
|
| 359 |
st.sidebar.error(
|
|
|
|
| 367 |
if st.session_state.models_initialized:
|
| 368 |
main()
|
| 369 |
else:
|
| 370 |
+
# Errors during initialization should have been shown and stopped execution.
|
| 371 |
+
# This path might be reached if initialization is still pending in an async setup (not the case here)
|
| 372 |
+
# or if there was a non-fatal init issue not caught by st.stop().
|
| 373 |
+
st.info("Waiting for AI model initialization or resolving configuration issues...")
|