File size: 20,173 Bytes
5b4c3e2 bdb09c8 b296b05 b9dbc6c bdb09c8 9059159 81dc1a4 5b4c3e2 81dc1a4 b9dbc6c bdb09c8 81dc1a4 b9dbc6c 81dc1a4 b9dbc6c bdb09c8 b9dbc6c 81dc1a4 b9dbc6c 81dc1a4 5b4c3e2 81dc1a4 5b4c3e2 b9dbc6c 81dc1a4 b9dbc6c 81dc1a4 b9dbc6c 81dc1a4 b9dbc6c 81dc1a4 b9dbc6c 81dc1a4 bdb09c8 b9dbc6c b296b05 b9dbc6c 5b4c3e2 97685a7 c6163b1 97685a7 81dc1a4 97685a7 5b4c3e2 97685a7 5b4c3e2 97685a7 5b4c3e2 97685a7 c6163b1 5b4c3e2 97685a7 5b4c3e2 97685a7 b296b05 b9dbc6c 97685a7 b296b05 b9dbc6c 5b4c3e2 b296b05 b9dbc6c 5b4c3e2 81dc1a4 5b4c3e2 81dc1a4 b296b05 81dc1a4 5b4c3e2 c6163b1 b296b05 b9dbc6c b296b05 b9dbc6c b296b05 b9dbc6c 5b4c3e2 97685a7 81dc1a4 97685a7 b9dbc6c 81dc1a4 5b4c3e2 81dc1a4 bdb09c8 97685a7 5b4c3e2 97685a7 5b4c3e2 b296b05 5b4c3e2 b296b05 97685a7 5b4c3e2 97685a7 5b4c3e2 b296b05 5b4c3e2 b296b05 5b4c3e2 b296b05 81dc1a4 5b4c3e2 81dc1a4 b296b05 5b4c3e2 b296b05 5b4c3e2 b296b05 5b4c3e2 b296b05 5b4c3e2 b296b05 5b4c3e2 b9dbc6c 5b4c3e2 b9dbc6c 5b4c3e2 b9dbc6c 97685a7 5b4c3e2 97685a7 b9dbc6c bdb09c8 5b4c3e2 b9dbc6c bdb09c8 81dc1a4 97685a7 81dc1a4 b9dbc6c 97685a7 81dc1a4 97685a7 b9dbc6c 97685a7 81dc1a4 97685a7 b296b05 97685a7 b9dbc6c b296b05 81dc1a4 b296b05 5b4c3e2 81dc1a4 5b4c3e2 81dc1a4 b296b05 97685a7 81dc1a4 97685a7 81dc1a4 b296b05 81dc1a4 97685a7 b296b05 5b4c3e2 81dc1a4 b296b05 81dc1a4 97685a7 5b4c3e2 97685a7 b296b05 97685a7 81dc1a4 b296b05 81dc1a4 b296b05 97685a7 b296b05 5b4c3e2 81dc1a4 97685a7 81dc1a4 97685a7 5b4c3e2 97685a7 81dc1a4 97685a7 81dc1a4 5b4c3e2 81dc1a4 5b4c3e2 97685a7 5b4c3e2 97685a7 b296b05 97685a7 81dc1a4 5b4c3e2 97685a7 81dc1a4 5b4c3e2 97685a7 b296b05 97685a7 81dc1a4 97685a7 bdb09c8 9059159 bdb09c8 81dc1a4 5b4c3e2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 |
# -*- coding: utf-8 -*-
import streamlit as st
import google.generativeai as genai
import os
from PIL import Image
import io # Needed for handling image bytes
from typing import Optional, Tuple, Any # For type hinting
# --- Page Configuration (MUST BE THE FIRST STREAMLIT COMMAND) ---
st.set_page_config(
page_title="AI Clinical Support Demonstrator",
layout="wide",
initial_sidebar_state="expanded"
)
# --- Introductory Explanation ---
st.markdown(
"""
### Welcome to the AI Clinical Support Demonstrator
This application demonstrates how Generative AI (Google Gemini) can be guided to assist with analyzing clinical information.
- **Agentic Text Analysis:** Simulates a structured reasoning process on clinical text.
- **Medical Image Analysis:** Provides descriptive observations of potential anomalies in medical images.
**Crucially, this tool is for demonstration purposes ONLY. It does NOT provide medical advice or diagnosis.**
"""
)
st.markdown("---") # Visual separator
# --- Configuration and Initialization ---
# Securely load API key (Secrets > Env Var)
GEMINI_API_KEY = st.secrets.get("GEMINI_API_KEY", os.environ.get("GEMINI_API_KEY"))
# Configure Gemini Client
genai_client_configured = False
if GEMINI_API_KEY:
try:
genai.configure(api_key=GEMINI_API_KEY)
genai_client_configured = True
except Exception as e:
st.error(f"Fatal Error: Failed to configure Google Generative AI. Check API Key. Details: {e}", icon="π¨")
st.stop()
else:
st.error("β οΈ Gemini API Key not found. Please configure `GEMINI_API_KEY` in Streamlit secrets or environment variables.", icon="π")
st.stop()
# Initialize models using Session State for persistence across reruns
TEXT_MODEL_NAME = 'gemini-1.5-pro-latest' # For agentic text reasoning
VISION_MODEL_NAME = 'gemini-1.5-flash' # For image analysis (or 'gemini-pro-vision')
if 'models_initialized' not in st.session_state:
st.session_state.models_initialized = False
st.session_state.text_model = None
st.session_state.vision_model = None
if genai_client_configured and not st.session_state.models_initialized:
try:
st.session_state.text_model = genai.GenerativeModel(TEXT_MODEL_NAME)
st.session_state.vision_model = genai.GenerativeModel(VISION_MODEL_NAME)
st.session_state.models_initialized = True
except Exception as e:
st.error(f"Fatal Error: Failed to initialize Gemini models. Text: {TEXT_MODEL_NAME}, Vision: {VISION_MODEL_NAME}. Details: {e}", icon="π₯")
st.stop()
elif not genai_client_configured:
st.error("AI Models could not be initialized due to configuration issues.", icon="π«")
st.stop()
# --- Core AI Interaction Functions ---
# AGENTIC prompt for Text Analysis (Remains the same as previous good version)
AGENTIC_TEXT_ANALYSIS_PROMPT_TEMPLATE = """
**Simulated Clinical Reasoning Agent Task:**
**Role:** AI assistant simulating an agentic clinical reasoning process to support a healthcare professional by structuring information, generating possibilities, and suggesting investigation pathways based *strictly* on the provided text. **This is NOT a diagnosis.**
**Input Data:** Unstructured clinical information (e.g., symptoms, history, basic findings).
**Output Format:** Please structure your response using Markdown headings for each step (e.g., `## 1. Information Extraction`).
**Simulated Agentic Steps (Perform sequentially):**
1. **## 1. Information Extraction & Structuring:**
* Key Demographics (Age, Sex if provided).
* Primary Symptoms/Signs.
* Relevant Medical History.
* Pertinent Negatives (if mentioned).
2. **## 2. Differential Considerations Generation:**
* Based *only* on Step 1, list **potential differential considerations** (possible conditions).
* **Use cautious language:** "could be consistent with," "warrants consideration," "less likely but possible." **AVOID definitive statements.**
* Briefly justify each consideration based on findings.
3. **## 3. Information Gap Analysis:**
* Identify critical missing information (e.g., lab results, imaging, exam specifics, duration/onset).
4. **## 4. Suggested Next Steps for Investigation (for Clinician):**
* Propose logical next steps a **healthcare professional might consider**.
* Categorize (e.g., Further History, Exam Points, Labs, Imaging).
* Frame as *suggestions* (e.g., "Consider ordering...", "Assessment of X may be informative").
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."
**Input Clinical Information:**
---
{text_input}
---
**Agentic Analysis:**
"""
# REFINED prompt for Image Analysis - More explicit instructions based on good example
IMAGE_ANALYSIS_PROMPT_TEMPLATE = """
**Medical Image Analysis Request:**
**Context:** Analyze the provided medical image objectively based *only* on visual information. User may provide additional context or questions.
**Output Format:** Structure your response precisely using the following Markdown headings. Be factual and descriptive.
**Task:**
1. **## 1. Visible Structures:**
* Identify the likely imaging modality and view (e.g., PA Chest Radiograph, Axial CT slice of the abdomen).
* Briefly list the main anatomical structures clearly visible (e.g., ribs, heart silhouette, lung fields, diaphragm).
2. **## 2. Identify Potential Anomalies / Key Findings:**
* Carefully examine the image for any areas that *appear* abnormal or deviate significantly from typical presentation.
* **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").
* **Specify location accurately** using standard anatomical terms (e.g., "right lower lung zone," "left hilum," "hepatic flexure region").
* **Crucially, AVOID interpretive or diagnostic terms** (DO NOT use words like "pneumonia," "tumor," "fracture," "infection," "inflammation"). Stick strictly to visual observation.
* 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").
* Compare sides if applicable and relevant differences are seen (e.g., "Left lung field demonstrates greater transparency compared to the right").
3. **## 3. Correlate with User Prompt (if provided):**
* Address specific user questions based *strictly* on the visual information identifiable in the image.
* If the image cannot visually answer the question (e.g., requires clinical context, different view), state that clearly.
* If no user prompt was provided, state "N/A".
4. **## 4. Limitations of this AI Analysis:**
* **Explicitly list the following limitations inherent to this analysis:**
* Dependency on the **quality, resolution, and potential artifacts** of the provided image.
* Analysis is restricted to the **single view/slice(s)** provided; other areas are not assessed.
* **Complete lack of clinical context:** Patient history, symptoms, physical exam findings, and laboratory results are unknown and not considered.
* **Absence of prior imaging studies:** Comparisons over time are not possible, which is often crucial for interpretation.
* The AI functions purely on **visual pattern recognition**; it does not perform clinical reasoning or differential diagnosis.
5. **## 5. Mandatory Disclaimer:**
* State clearly: This is an AI-generated visual analysis intended for informational and demonstration purposes **ONLY**.
* It is **NOT** a radiological interpretation or medical diagnosis.
* It **CANNOT** substitute for a comprehensive evaluation and interpretation by a qualified radiologist or physician integrating full clinical information.
* Any potential observations noted herein **MUST** be correlated with clinical findings and reviewed/confirmed by qualified healthcare professionals.
**User's Additional Context/Question (if any):**
---
{user_prompt}
---
**Image Analysis:**
"""
# --- Backend Functions ---
def run_agentic_text_analysis(text_input: str) -> Tuple[Optional[str], Optional[str]]:
"""Sends clinical text to the configured text model for simulated agentic analysis."""
if not text_input or not text_input.strip():
return None, "Input text cannot be empty."
if not st.session_state.models_initialized or not st.session_state.text_model:
return None, "Text analysis model not initialized. Please refresh or check configuration."
try:
prompt = AGENTIC_TEXT_ANALYSIS_PROMPT_TEMPLATE.format(text_input=text_input)
response = st.session_state.text_model.generate_content(prompt) # Use model from session state
# Handle response variations
if response.parts:
return response.text, None
elif response.prompt_feedback.block_reason:
return None, f"Analysis blocked by safety filters: {response.prompt_feedback.block_reason.name}. Please review or revise input."
else:
candidate = response.candidates[0] if response.candidates else None
if candidate and candidate.finish_reason != "STOP":
# If stopped for reasons other than normal completion (e.g., length, safety)
return None, f"Analysis stopped prematurely. Reason: {candidate.finish_reason.name}. Input might be too long or triggered other limits."
else:
# General case for empty or unexpected response
return None, "Received an empty or unexpected response from the AI model for text analysis."
except Exception as e:
print(f"ERROR in run_agentic_text_analysis: {e}") # Log for server/dev console
st.error("An error occurred during text analysis.", icon="π¨") # User-facing generic error
return None, "An internal error occurred during text analysis. Please try again later or contact support if the issue persists."
def analyze_medical_image(image_file: Any, user_prompt: str = "") -> Tuple[Optional[str], Optional[str]]:
"""Sends a medical image to the configured vision model for analysis using the refined prompt."""
if not image_file:
return None, "Image file cannot be empty."
if not st.session_state.models_initialized or not st.session_state.vision_model:
return None, "Image analysis model not initialized. Please refresh or check configuration."
try:
try:
# Open and prepare the image
image = Image.open(image_file)
if image.mode != 'RGB':
image = image.convert('RGB') # Ensure RGB format
except Exception as img_e:
return None, f"Error opening or processing the uploaded image file: {img_e}. Please ensure it's a valid image."
# Prepare the prompt using the refined template
prompt_text = IMAGE_ANALYSIS_PROMPT_TEMPLATE.format(user_prompt=user_prompt if user_prompt else "N/A")
model_input = [prompt_text, image] # Combine text prompt and image data
response = st.session_state.vision_model.generate_content(model_input) # Use model from session state
# Handle response variations
if response.parts:
return response.text, None
elif response.prompt_feedback.block_reason:
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."
else:
candidate = response.candidates[0] if response.candidates else None
if candidate and candidate.finish_reason != "STOP":
return None, f"Image analysis stopped prematurely. Reason: {candidate.finish_reason.name}. Input might be too complex or triggered other limits."
else:
return None, "Received an empty or unexpected response from the AI model for image analysis."
except Exception as e:
print(f"ERROR in analyze_medical_image: {e}") # Log for server/dev console
st.error("An error occurred during image analysis.", icon="πΌοΈ") # User-facing generic error
return None, "An internal error occurred during image analysis. Please try again later or contact support if the issue persists."
# --- Streamlit User Interface ---
def main():
# Page title and model info
st.title("π€ AI Clinical Support Demonstrator")
st.caption(f"Utilizing: Text Model ({TEXT_MODEL_NAME}), Vision Model ({VISION_MODEL_NAME})")
# --- CRITICAL DISCLAIMER ---
st.warning(
"""
**π΄ IMPORTANT SAFETY & USE DISCLAIMER π΄**
* This tool **DEMONSTRATES** AI capabilities. It **DOES NOT** provide medical advice or diagnosis.
* **Agentic Text Analysis:** Simulates reasoning on text input. Output is illustrative, not diagnostic.
* **Image Analysis:** Provides observations on images. Output is **NOT** a radiological interpretation.
* AI analysis lacks full clinical context, may be inaccurate, and **CANNOT** replace professional judgment.
* **ALWAYS consult qualified healthcare professionals** for diagnosis and treatment.
* **PRIVACY:** Do **NOT** upload identifiable patient information (PHI) without explicit consent and adherence to all privacy laws (e.g., HIPAA). You are responsible for the data you input.
""",
icon="β οΈ"
)
st.markdown("---")
# --- Sidebar Controls ---
st.sidebar.header("Analysis Options")
input_method = st.sidebar.radio(
"Select Analysis Type:",
("Agentic Text Analysis", "Medical Image Analysis"),
key="input_method_radio",
help="Choose 'Agentic Text Analysis' for reasoning simulation on clinical text, or 'Medical Image Analysis' for visual observations on images."
)
st.sidebar.markdown("---") # Visual separator
# --- Main Area Layout (Input and Output Columns) ---
col1, col2 = st.columns(2)
analysis_result = None # Reset results variables for this run
error_message = None
output_header = "Analysis Results" # Default header
analyze_button_key = None # Initialize key variable
# --- Column 1: Input Area ---
with col1:
st.header("Input Data")
# Conditional Input UI based on selection
if input_method == "Agentic Text Analysis":
st.subheader("Clinical Text for Agentic Analysis")
st.caption("Please ensure data is de-identified before pasting.")
text_input = st.text_area(
"Paste clinical information:",
height=350,
placeholder="Example: 68yo male, sudden SOB & pleuritic chest pain post-flight. HR 110, SpO2 92% RA. No known cardiac hx...",
key="text_input_area"
)
analyze_button_key = "analyze_text_button" # Set key for this branch
analyze_button_label = "βΆοΈ Run Agentic Text Analysis"
if st.button(analyze_button_label, key=analyze_button_key, type="primary"):
if text_input:
with st.spinner("π§ Simulating agentic reasoning... Please wait."):
analysis_result, error_message = run_agentic_text_analysis(text_input)
output_header = "Simulated Agentic Analysis Output"
else:
st.warning("Please enter clinical text to analyze.", icon="βοΈ")
elif input_method == "Medical Image Analysis":
st.subheader("Medical Image for Analysis")
st.caption("Upload a de-identified medical image (PNG, JPG, JPEG).")
image_file = st.file_uploader(
"Choose an image file:",
type=["png", "jpg", "jpeg"],
key="image_uploader"
)
user_image_prompt = st.text_input(
"Optional: Add context or specific question for image analysis:",
placeholder="Example: 'Describe findings in the lung fields' or 'Any visible fractures?'",
key="image_prompt_input"
)
analyze_button_key = "analyze_image_button" # Set key for this branch
analyze_button_label = "πΌοΈ Analyze Medical Image"
if image_file:
# Display preview immediately after upload inside the input column
st.image(image_file, caption="Uploaded Image Preview", use_column_width=True)
if st.button(analyze_button_label, key=analyze_button_key, type="primary"):
if image_file:
with st.spinner("ποΈ Analyzing image... Please wait."):
analysis_result, error_message = analyze_medical_image(image_file, user_image_prompt)
output_header = "Medical Image Analysis Output"
else:
st.warning("Please upload an image file to analyze.", icon="βοΈ")
# --- Column 2: Output Area ---
with col2:
st.header(output_header)
# Determine if analysis was attempted in this run using the correct button key
button_pressed = st.session_state.get(analyze_button_key, False) if analyze_button_key else False
if button_pressed:
# Only display results if the corresponding button was pressed AND generated output/error
if error_message:
st.error(f"Analysis Failed: {error_message}", icon="β")
elif analysis_result:
# Use st.markdown to render potential formatting (like headings) from AI
st.markdown(analysis_result, unsafe_allow_html=False) # Keep unsafe_allow_html=False for security
# No explicit 'else' needed here; if button pressed but no result/error, likely handled by input validation warning
else:
# Default placeholder message if no analysis attempted yet in this run
st.info("Analysis results will appear here after providing input and clicking the corresponding analysis button.")
# --- Sidebar Explanations ---
st.sidebar.markdown("---")
st.sidebar.header("About The Prompts")
with st.sidebar.expander("View Agentic Text Prompt Structure", icon="π"):
# Show only the instructive part of the prompt template
st.code(AGENTIC_TEXT_ANALYSIS_PROMPT_TEMPLATE.split('---')[0] + "...", language='markdown')
st.caption("Guides the AI through structured reasoning steps for text.")
with st.sidebar.expander("View Image Analysis Prompt Structure", icon="πΌοΈ"):
# Show only the instructive part of the prompt template
st.code(IMAGE_ANALYSIS_PROMPT_TEMPLATE.split('---')[0] + "...", language='markdown')
st.caption("Guides the AI to provide cautious, descriptive visual observations for images.")
st.sidebar.markdown("---")
st.sidebar.error(
"**Ethical Use Reminder:** AI in medicine requires extreme caution. This tool is for demonstration and education, not clinical practice. Verify all information and rely on professional expertise.",
icon = "βοΈ"
)
# --- Main Execution Guard ---
if __name__ == "__main__":
# Check if models are initialized before running the main UI components
if st.session_state.models_initialized:
main()
else:
# Errors during initialization should have been shown and stopped execution.
# This path might be reached if initialization is still pending in an async setup (not the case here)
# or if there was a non-fatal init issue not caught by st.stop().
st.info("Waiting for AI model initialization or resolving configuration issues...") |