diff --git a/.gitignore b/.gitignore index 3da1382..5aadcb8 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,6 @@ output src/public/output_data infra/config data -.env.production \ No newline at end of file +.env.production + +notebooks/evaluations/simulated_chats \ No newline at end of file diff --git a/docs/evaluation.md b/docs/evaluation.md index 4d2e0d4..fbe0019 100644 --- a/docs/evaluation.md +++ b/docs/evaluation.md @@ -19,7 +19,7 @@ pip install -r requirements-eval.txt This is also necessary for running the [evaluation notebook](/notebooks/evaluations/evaluation.ipynb). > [!NOTE] -> To run Jupyter notebooks in this dev container, ensure Jupyter is installed (`pip install jupyter`) or use the VS Code Jupyter extension which is included in the dev container for running notebooks directly in VS Code. +> [Additional instructions](/notebooks/README.md) for running notebooks. ## Quick Start diff --git a/notebooks/README.md b/notebooks/README.md new file mode 100644 index 0000000..69a8ea6 --- /dev/null +++ b/notebooks/README.md @@ -0,0 +1,47 @@ +# Jupyter Notebooks Setup Guide + +This guide helps you set up Jupyter to run the notebooks in this repository. + +## Quick Setup (Recommended) + +1. **Activate venv** + + ```bash + source .venv/bin/activate + ``` + +1. **Install dependencies** + + ```bash + cd src + export SCENARIO=default + pip install -r requirements.txt + pip install -r requirements-eval.txt + pip install -r requirements-notebooks.txt + ``` + +1. **(optional) Set up Jupyter kernel for VS Code** + + ```bash + python -m ipykernel install --user --name "healthcare-agent-orchestrator" --display-name "Healthcare Agent Orchestrator" + +1. **az login** + ```bash + az login + ``` + +## Running Notebooks + +### VS Code + +1. Open the project in VS Code +2. Install the Jupyter extension if not already installed +3. Open any `.ipynb` file in the `notebooks/` directory +4. Select either the .venv or "Healthcare Agent Orchestrator" kernel when prompted + +## Troubleshooting + +- **ImportError**: Make sure you've installed the main project dependencies (`pip install -r src/requirements.txt`) +- **Azure Authentication**: Ensure you're logged into Azure CLI: `az login` +- **Missing .env**: Generated during infrastructure deployment +- **Python Version**: Requires Python 3.8 or later diff --git a/notebooks/end_to_end_run.ipynb b/notebooks/end_to_end_run.ipynb index c732bda..34acae5 100644 --- a/notebooks/end_to_end_run.ipynb +++ b/notebooks/end_to_end_run.ipynb @@ -47,17 +47,6 @@ "load_dotenv(os.path.join(SRC_DIR, '.env'))" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "from config import setup_logging\n", - "\n", - "setup_logging()" - ] - }, { "cell_type": "code", "execution_count": null, diff --git a/notebooks/evaluations/evaluation.ipynb b/notebooks/evaluations/evaluation.ipynb index acb6ed9..e610bb7 100644 --- a/notebooks/evaluations/evaluation.ipynb +++ b/notebooks/evaluations/evaluation.ipynb @@ -44,9 +44,7 @@ "outputs": [], "source": [ "from app import create_app_context\n", - "from config import setup_logging\n", "\n", - "setup_logging()\n", "app_ctx = create_app_context()" ] }, @@ -60,7 +58,7 @@ "SIMULATION_OUTPUT_PATH = \"./simulated_chats/patient_4\"\n", "EVALUATION_RESULTS_PATH = os.path.join(SIMULATION_OUTPUT_PATH, \"evaluation_results\")\n", "\n", - "PATIENT_TIMELINE_REFERENCE_PATH = \"./reference/\"" + "PATIENT_TIMELINE_REFERENCE_PATH = \"./references/\"" ] }, { diff --git a/notebooks/healthcare_agent_service/end_to_end.ipynb b/notebooks/healthcare_agent_service/end_to_end.ipynb index 35f1da2..6de0d23 100644 --- a/notebooks/healthcare_agent_service/end_to_end.ipynb +++ b/notebooks/healthcare_agent_service/end_to_end.ipynb @@ -33,19 +33,16 @@ "from semantic_kernel.contents.utils.author_role import AuthorRole\n", "\n", "from app import create_app_context\n", - "from config import setup_logging\n", "from data_models.chat_context import ChatContext\n", "from group_chat import create_group_chat\n", "\n", "\n", - "setup_logging(logging.WARNING)\n", + "app_ctx = create_app_context(logging.WARNING)\n", + "\n", "logging.getLogger('group_chat').setLevel(logging.INFO)\n", - "logging.getLogger('healthcare_agents').setLevel(logging.INFO)#DEBUG)\n", + "logging.getLogger('healthcare_agents').setLevel(logging.INFO)\n", "# logging.getLogger('semantic_kernel').setLevel(logging.INFO)\n", "\n", - "logger = logging.getLogger(__name__)\n", - "app_ctx = create_app_context()\n", - "\n", "print(\"Ready\")" ] }, diff --git a/notebooks/healthcare_agent_service/single.ipynb b/notebooks/healthcare_agent_service/single.ipynb index c1eee2b..dcea5a3 100644 --- a/notebooks/healthcare_agent_service/single.ipynb +++ b/notebooks/healthcare_agent_service/single.ipynb @@ -20,7 +20,6 @@ "outputs": [], "source": [ "import logging\n", - "import os\n", "import sys\n", "sys.path.append('../../src')\n", "\n", @@ -30,15 +29,9 @@ "from semantic_kernel.contents.utils.author_role import AuthorRole\n", "from azure.storage.blob.aio import BlobServiceClient\n", "\n", - "from config import setup_logging\n", "from data_models.data_access import DataAccess\n", "from data_models.chat_context import ChatContext\n", - "from healthcare_agents import HealthcareAgent\n", - "\n", - "setup_logging(log_level=logging.WARNING)\n", - "logging.getLogger(\"healthcare_agents\").setLevel(logging.DEBUG)\n", - "\n", - "logger = logging.getLogger(__name__)\n" + "from healthcare_agents import HealthcareAgent" ] }, { @@ -114,7 +107,10 @@ "from app import create_app_context\n", "\n", "\n", - "app_ctx = create_app_context()\n", + "app_ctx = create_app_context(logging.WARNING)\n", + "logging.getLogger(\"healthcare_agents\").setLevel(logging.DEBUG)\n", + "logger = logging.getLogger(__name__)\n", + "\n", "agent_name = engage_with_agent[\"agent_name\"]\n", "chat_ctx = ChatContext(f\"{agent_name}-test-chat\")\n", "healthcare_agent = HealthcareAgent(name=agent_name,\n", diff --git a/notebooks/tests/content_export.ipynb b/notebooks/tests/content_export.ipynb index 2ee77e8..8a1cff5 100644 --- a/notebooks/tests/content_export.ipynb +++ b/notebooks/tests/content_export.ipynb @@ -11,17 +11,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Added c:\\Users\\franktuan\\repos\\healthcare-agent-orchestrator\\src to sys.path\n" - ] - } - ], + "outputs": [], "source": [ "import os\n", "import sys\n", @@ -36,31 +28,18 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "True" - ] - }, - "execution_count": 2, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "from config import setup_logging\n", "from dotenv import load_dotenv\n", "\n", - "setup_logging()\n", "load_dotenv(os.path.join(SRC_DIR, '.env'))" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ @@ -79,7 +58,41 @@ "from data_models.tumor_board_summary import ClinicalTrial\n", "\n", "\n", - "doc_data = {\"patient_gender\":\"Female\",\"patient_age\":\"59\",\"medical_history\":[\"Initial chemotherapy with carboplatin and paclitaxel, followed by pembrolizumab.\",\"Completed 6 cycles of carboplatin, paclitaxel, and pembrolizumab.\",\"Maintenance therapy with pembrolizumab alone.\",\"Planned switch to KRAS-directed therapy with adagrasib due to disease progression.\"],\"social_history\":[],\"cancer_type\":\"Stage IV non-small cell lung cancer, adenocarcinoma\",\"ct_scan_findings\":[\"The calculated tumor volume is 18,014 mm³.\",\"The longest dimension of the lung mass is 919 mm.\"],\"x_ray_findings\":[\"Extensive thoracic and lumbar spinal fusion surgery is present.\",\"There is a patchy opacity in the right lower lobe.\",\"The left lung is clear.\",\"The heart is normal in size.\",\"No pneumothorax.\"],\"pathology_findings\":[\"The probability that the tumor is malignant is approximately 58.41%.\",\"The probability that the tumor is non-malignant is approximately 41.59%.\"],\"treatment_plan\":\"Start KRAS-directed treatment with adagrasib due to disease progression. Consider systemic therapy options such as docetaxel, pemetrexed, gemcitabine, or combinations if further progression occurs. Best supportive care if ECOG PS 3–4.\",\"clinical_trials\":[ClinicalTrial(title=\"NCT05780307\", summary=\"The patient is eligible for this trial. The trial requires patients to have advanced/metastatic solid tumors, an ECOG performance status of 0 or 1, and to have failed previous standard treatments. The patient meets these criteria, having completed standard first-line and second-line therapies and possessing a KRAS p.G12C mutation.\")]}" + "doc_data = {\n", + " \"patient_gender\":\"Female\",\n", + " \"patient_age\":\"59\",\n", + " \"medical_history\":[\n", + " \"Initial chemotherapy with carboplatin and paclitaxel, followed by pembrolizumab.\",\n", + " \"Completed 6 cycles of carboplatin, paclitaxel, and pembrolizumab.\",\n", + " \"Maintenance therapy with pembrolizumab alone.\",\n", + " \"Planned switch to KRAS-directed therapy with adagrasib due to disease progression.\"\n", + " ],\n", + " \"social_history\":[],\n", + " \"cancer_type\":\"Stage IV non-small cell lung cancer, adenocarcinoma\",\n", + " \"ct_scan_findings\":[\n", + " \"The calculated tumor volume is 18,014 mm³.\",\n", + " \"The longest dimension of the lung mass is 919 mm.\"\n", + " ],\n", + " \"x_ray_findings\":[\n", + " \"Extensive thoracic and lumbar spinal fusion surgery is present.\",\n", + " \"There is a patchy opacity in the right lower lobe.\",\n", + " \"The left lung is clear.\",\n", + " \"The heart is normal in size.\",\n", + " \"No pneumothorax.\"\n", + " ],\n", + " \"pathology_findings\":[\n", + " \"The probability that the tumor is malignant is approximately 58.41%.\",\n", + " \"The probability that the tumor is non-malignant is approximately 41.59%.\"\n", + " ],\n", + " \"treatment_plan\":\"Start KRAS-directed treatment with adagrasib due to disease progression. Consider systemic therapy options such as docetaxel, pemetrexed, gemcitabine, or combinations if further progression occurs. Best supportive care if ECOG PS 3–4.\",\n", + " \"clinical_trials\":[\n", + " ClinicalTrial(\n", + " title=\"NCT05780307\",\n", + " summary=\"The patient is eligible for this trial. The trial requires patients to have advanced/metastatic solid tumors, an ECOG performance status of 0 or 1, and to have failed previous standard treatments. The patient meets these criteria, having completed standard first-line and second-line therapies and possessing a KRAS p.G12C mutation.\",\n", + " url=\"https://clinicaltrials.gov/ct2/show/NCT05780307\",\n", + " )\n", + " ]\n", + "}" ] }, { diff --git a/notebooks/tests/cxr_report_gen.ipynb b/notebooks/tests/cxr_report_gen.ipynb index 63379b9..6693cc4 100644 --- a/notebooks/tests/cxr_report_gen.ipynb +++ b/notebooks/tests/cxr_report_gen.ipynb @@ -32,10 +32,8 @@ "metadata": {}, "outputs": [], "source": [ - "from config import setup_logging\n", "from dotenv import load_dotenv\n", "\n", - "setup_logging()\n", "load_dotenv(os.path.join(SRC_DIR, '.env'))" ] }, diff --git a/notebooks/tests/med_image_parse.ipynb b/notebooks/tests/med_image_parse.ipynb index fe9ea37..df4d224 100644 --- a/notebooks/tests/med_image_parse.ipynb +++ b/notebooks/tests/med_image_parse.ipynb @@ -37,10 +37,8 @@ "metadata": {}, "outputs": [], "source": [ - "from config import setup_logging\n", "from dotenv import load_dotenv\n", "\n", - "setup_logging()\n", "load_dotenv(os.path.join(SRC_DIR, '.env'))" ] }, diff --git a/src/app.py b/src/app.py index 9fc0c6b..14ed3da 100644 --- a/src/app.py +++ b/src/app.py @@ -4,9 +4,12 @@ import logging import os -from azure.identity import AzureCliCredential, ManagedIdentityCredential +from azure.identity.aio import AzureCliCredential, ManagedIdentityCredential from azure.storage.blob.aio import BlobServiceClient -from botbuilder.integration.aiohttp import CloudAdapter, ConfigurationBotFrameworkAuthentication +from botbuilder.integration.aiohttp import ( + CloudAdapter, + ConfigurationBotFrameworkAuthentication, +) from dotenv import load_dotenv from fastapi import FastAPI from fastapi.staticfiles import StaticFiles @@ -17,7 +20,12 @@ from bots import AssistantBot, MagenticBot from bots.access_control_middleware import AccessControlMiddleware from bots.show_typing_middleware import ShowTypingMiddleware -from config import DefaultConfig, load_agent_config, setup_app_insights_logging, setup_logging +from config import ( + DefaultConfig, + load_agent_config, + setup_app_insights_logging, + setup_logging, +) from data_models.app_context import AppContext from data_models.data_access import create_data_access from mcp_app import create_fast_mcp_app @@ -31,16 +39,15 @@ load_dotenv(".env") -# Setup default logging and minimum log level severity for your environment that you want to consume -log_level = logging.INFO -setup_logging(log_level=log_level) - -def create_app_context(): +def create_app_context(log_level: int = logging.INFO): '''Create the application context for commonly used object used in application.''' + # Setup default logging and minimum log level severity for your environment that you want to consume + setup_logging(log_level) + # Load agent configuration - scenario = os.getenv("SCENARIO") + scenario = os.getenv("SCENARIO", "default") agent_config = load_agent_config(scenario) # Load Azure Credential @@ -55,13 +62,21 @@ def create_app_context(): ) data_access = create_data_access(blob_service_client, credential) - return AppContext( + app_context = AppContext( all_agent_configs=agent_config, blob_service_client=blob_service_client, credential=credential, data_access=data_access, ) + # Setup Application Insights logging + setup_app_insights_logging( + credential=app_context.credential, + log_level=log_level + ) + + return app_context + def create_app( bots: dict, @@ -107,10 +122,6 @@ async def serve_react_app(full_path: str): app_context = create_app_context() -# Setup Application Insights logging -setup_app_insights_logging(credential=app_context.credential, - log_level=log_level) - # Create Teams specific objects adapters = { agent["name"]: CloudAdapter(ConfigurationBotFrameworkAuthentication( diff --git a/src/config.py b/src/config.py index 5a40653..86adc40 100644 --- a/src/config.py +++ b/src/config.py @@ -13,7 +13,6 @@ from opentelemetry.instrumentation.logging import LoggingInstrumentor import yaml -logger = logging.getLogger(__name__) formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) @@ -59,16 +58,26 @@ def setup_app_insights_logging(credential, log_level=logging.DEBUG) -> None: def setup_logging(log_level=logging.DEBUG) -> None: + root = logging.getLogger() + root.setLevel(log_level) + + # Update console handler if already exists + console_handlers = [h for h in root.handlers if isinstance(h, logging.StreamHandler)] + if console_handlers: + primary = console_handlers[0] + primary.setLevel(log_level) + primary.setFormatter(formatter) + return + # Create a logging handler to write logging records, in OTLP format, to the exporter. - console_handler = logging.StreamHandler() + console = logging.StreamHandler() # Add filters to the handler to only process records from semantic_kernel. - # console_handler.addFilter(logging.Filter("semantic_kernel")) - console_handler.setFormatter(formatter) + # console.addFilter(logging.Filter("semantic_kernel")) + console.setLevel(log_level) + console.setFormatter(formatter) - logger = logging.getLogger() - logger.addHandler(console_handler) - logger.setLevel(log_level) + root.addHandler(console) def load_agent_config(scenario: str) -> dict: diff --git a/src/evaluation/metrics/base.py b/src/evaluation/metrics/base.py index acd2255..d621a92 100644 --- a/src/evaluation/metrics/base.py +++ b/src/evaluation/metrics/base.py @@ -140,7 +140,7 @@ def load_valid_agents(scenario: str = "Orchestrator") -> list[str]: # Find config path relative to current file current_dir = Path(__file__).parent - yaml_path = current_dir.parents[2] / "scenarios" / scenario / "config" / "agents.yaml" + yaml_path = current_dir.parents[1] / "scenarios" / scenario / "config" / "agents.yaml" try: with open(yaml_path, 'r') as f: diff --git a/src/requirements-eval.txt b/src/requirements-eval.txt index 9b71f0f..4bf0705 100644 --- a/src/requirements-eval.txt +++ b/src/requirements-eval.txt @@ -1,4 +1,4 @@ evaluate==0.4.3 rouge-score==0.1.2 absl-py==2.2.2 -nltk==3.9.1 +nltk==3.9.1 \ No newline at end of file diff --git a/src/requirements-notebooks.txt b/src/requirements-notebooks.txt new file mode 100644 index 0000000..9b856d0 --- /dev/null +++ b/src/requirements-notebooks.txt @@ -0,0 +1,2 @@ +jupyter>=1.0.0 +ipykernel>=6.0.0 \ No newline at end of file diff --git a/src/requirements.txt b/src/requirements.txt index bf50ef4..14ee3d0 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,8 +1,8 @@ aiohttp==3.12.14 aiohttp-compress==0.2.1 -azure-core==1.31.0 +azure-core>=1.32.0 azure-identity==1.19.0 -azure-storage-blob==12.25.0 +azure-storage-blob>=12.26.0 botbuilder-core==4.17.0 botbuilder-dialogs==4.17.0 botbuilder-integration-aiohttp==4.17.0 @@ -16,7 +16,7 @@ docxtpl==0.19.1 autogen-core==0.4.9 autogen-agentchat==0.4.9 autogen-ext[openai]==0.4.9 -azure-keyvault-secrets==4.9.0 +azure-keyvault-secrets==4.7.0 git+https://github.com/modelcontextprotocol/python-sdk.git@58c5e7223c40b2ec682fd7674545e8ceadd7cb20 # streamable transport just landed in main, no pypi release yet uvicorn[standard]==0.34.1 uvicorn-worker==0.3.0