| |
| import streamlit as st |
| import google.generativeai as genai |
| import os |
| from PIL import Image |
| import io |
| from typing import Optional, Tuple, Any |
|
|
| |
| st.set_page_config( |
| page_title="AI Clinical Support Demonstrator", |
| layout="wide", |
| initial_sidebar_state="expanded" |
| ) |
|
|
| |
| 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("---") |
|
|
| |
|
|
| |
| GEMINI_API_KEY = st.secrets.get("GEMINI_API_KEY", os.environ.get("GEMINI_API_KEY")) |
|
|
| |
| 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() |
|
|
| |
| TEXT_MODEL_NAME = 'gemini-1.5-pro-latest' |
| VISION_MODEL_NAME = 'gemini-1.5-flash' |
|
|
| 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() |
|
|
|
|
| |
|
|
| |
| 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:** |
| """ |
|
|
| |
| 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:** |
| """ |
|
|
| |
|
|
| 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) |
|
|
| |
| 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": |
| |
| return None, f"Analysis stopped prematurely. Reason: {candidate.finish_reason.name}. Input might be too long or triggered other limits." |
| else: |
| |
| 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}") |
| st.error("An error occurred during text analysis.", icon="π¨") |
| 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: |
| |
| image = Image.open(image_file) |
| if image.mode != 'RGB': |
| image = image.convert('RGB') |
| 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." |
|
|
| |
| prompt_text = IMAGE_ANALYSIS_PROMPT_TEMPLATE.format(user_prompt=user_prompt if user_prompt else "N/A") |
| model_input = [prompt_text, image] |
|
|
| response = st.session_state.vision_model.generate_content(model_input) |
|
|
| |
| 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}") |
| st.error("An error occurred during image analysis.", icon="πΌοΈ") |
| return None, "An internal error occurred during image analysis. Please try again later or contact support if the issue persists." |
|
|
|
|
| |
|
|
| def main(): |
| |
| st.title("π€ AI Clinical Support Demonstrator") |
| st.caption(f"Utilizing: Text Model ({TEXT_MODEL_NAME}), Vision Model ({VISION_MODEL_NAME})") |
|
|
| |
| 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("---") |
|
|
| |
| 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("---") |
|
|
| |
| col1, col2 = st.columns(2) |
|
|
| analysis_result = None |
| error_message = None |
| output_header = "Analysis Results" |
| analyze_button_key = None |
|
|
| |
| with col1: |
| st.header("Input Data") |
|
|
| |
| 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" |
| 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" |
| analyze_button_label = "πΌοΈ Analyze Medical Image" |
|
|
| if image_file: |
| |
| 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="βοΈ") |
|
|
| |
| with col2: |
| st.header(output_header) |
|
|
| |
| button_pressed = st.session_state.get(analyze_button_key, False) if analyze_button_key else False |
|
|
| if button_pressed: |
| |
| if error_message: |
| st.error(f"Analysis Failed: {error_message}", icon="β") |
| elif analysis_result: |
| |
| st.markdown(analysis_result, unsafe_allow_html=False) |
| |
| else: |
| |
| st.info("Analysis results will appear here after providing input and clicking the corresponding analysis button.") |
|
|
|
|
| |
| st.sidebar.markdown("---") |
| st.sidebar.header("About The Prompts") |
| with st.sidebar.expander("View Agentic Text Prompt Structure", icon="π"): |
| |
| 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="πΌοΈ"): |
| |
| 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 = "βοΈ" |
| ) |
|
|
| |
| if __name__ == "__main__": |
| |
| if st.session_state.models_initialized: |
| main() |
| else: |
| |
| |
| |
| st.info("Waiting for AI model initialization or resolving configuration issues...") |