From a5a23ad57d72b511d94817e9ece3eb83c05fe87b Mon Sep 17 00:00:00 2001 From: AndrewVFranco <129307231+AndrewVFranco@users.noreply.github.com> Date: Tue, 21 Apr 2026 01:12:20 -0700 Subject: [PATCH 1/2] Update ragas pipeline --- configs/settings.yaml | 0 notebooks/ragas.ipynb | 206 +++++++++++++++--------------------------- 2 files changed, 75 insertions(+), 131 deletions(-) delete mode 100644 configs/settings.yaml diff --git a/configs/settings.yaml b/configs/settings.yaml deleted file mode 100644 index e69de29..0000000 diff --git a/notebooks/ragas.ipynb b/notebooks/ragas.ipynb index d520c49..821bde1 100644 --- a/notebooks/ragas.ipynb +++ b/notebooks/ragas.ipynb @@ -6,8 +6,8 @@ "metadata": { "collapsed": true, "ExecuteTime": { - "end_time": "2026-04-18T21:21:51.862906Z", - "start_time": "2026-04-18T21:21:51.841683Z" + "end_time": "2026-04-21T06:42:11.922770Z", + "start_time": "2026-04-21T06:41:51.670197Z" } }, "source": [ @@ -27,48 +27,38 @@ "name": "stderr", "output_type": "stream", "text": [ - "/var/folders/n1/2qvnq4v53p51hj0p1nsg6ldc0000gn/T/ipykernel_20608/770387039.py:3: DeprecationWarning: Importing Faithfulness from 'ragas.metrics' is deprecated and will be removed in v1.0. Please use 'ragas.metrics.collections' instead. Example: from ragas.metrics.collections import Faithfulness\n", + "/var/folders/n1/2qvnq4v53p51hj0p1nsg6ldc0000gn/T/ipykernel_67716/770387039.py:3: DeprecationWarning: Importing Faithfulness from 'ragas.metrics' is deprecated and will be removed in v1.0. Please use 'ragas.metrics.collections' instead. Example: from ragas.metrics.collections import Faithfulness\n", " from ragas.metrics import Faithfulness, ResponseRelevancy, LLMContextPrecisionWithoutReference\n", - "/var/folders/n1/2qvnq4v53p51hj0p1nsg6ldc0000gn/T/ipykernel_20608/770387039.py:3: DeprecationWarning: Importing ResponseRelevancy from 'ragas.metrics' is deprecated and will be removed in v1.0. Please use 'ragas.metrics.collections' instead. Example: from ragas.metrics.collections import ResponseRelevancy\n", + "/var/folders/n1/2qvnq4v53p51hj0p1nsg6ldc0000gn/T/ipykernel_67716/770387039.py:3: DeprecationWarning: Importing ResponseRelevancy from 'ragas.metrics' is deprecated and will be removed in v1.0. Please use 'ragas.metrics.collections' instead. Example: from ragas.metrics.collections import ResponseRelevancy\n", " from ragas.metrics import Faithfulness, ResponseRelevancy, LLMContextPrecisionWithoutReference\n", - "/var/folders/n1/2qvnq4v53p51hj0p1nsg6ldc0000gn/T/ipykernel_20608/770387039.py:3: DeprecationWarning: Importing LLMContextPrecisionWithoutReference from 'ragas.metrics' is deprecated and will be removed in v1.0. Please use 'ragas.metrics.collections' instead. Example: from ragas.metrics.collections import LLMContextPrecisionWithoutReference\n", - " from ragas.metrics import Faithfulness, ResponseRelevancy, LLMContextPrecisionWithoutReference\n" + "/var/folders/n1/2qvnq4v53p51hj0p1nsg6ldc0000gn/T/ipykernel_67716/770387039.py:3: DeprecationWarning: Importing LLMContextPrecisionWithoutReference from 'ragas.metrics' is deprecated and will be removed in v1.0. Please use 'ragas.metrics.collections' instead. Example: from ragas.metrics.collections import LLMContextPrecisionWithoutReference\n", + " from ragas.metrics import Faithfulness, ResponseRelevancy, LLMContextPrecisionWithoutReference\n", + "Loading weights: 100%|██████████| 199/199 [00:00<00:00, 31582.66it/s]\n", + "\u001B[1mBertModel LOAD REPORT\u001B[0m from: pritamdeka/BioBERT-mnli-snli-scinli-scitail-mednli-stsb\n", + "Key | Status | | \n", + "------------------------+------------+--+-\n", + "embeddings.position_ids | UNEXPECTED | | \n", + "\n", + "Notes:\n", + "- UNEXPECTED:\tcan be ignored when loading from different task/architecture; not ok if you expect identical arch.\n", + "Loading weights: 100%|██████████| 106/106 [00:00<00:00, 12492.52it/s]\n", + "\u001B[1mDebertaV2ForSequenceClassification LOAD REPORT\u001B[0m from: cross-encoder/nli-deberta-v3-small\n", + "Key | Status | | \n", + "--------------------------------+------------+--+-\n", + "deberta.embeddings.position_ids | UNEXPECTED | | \n", + "\n", + "Notes:\n", + "- UNEXPECTED:\tcan be ignored when loading from different task/architecture; not ok if you expect identical arch.\n" ] } ], - "execution_count": 46 - }, - { - "metadata": { - "ExecuteTime": { - "end_time": "2026-04-18T07:12:07.417548Z", - "start_time": "2026-04-18T07:12:07.386039Z" - } - }, - "cell_type": "code", - "source": [ - "mlflow.set_tracking_uri(settings.MLFLOW_TRACKING_URI)\n", - "client = mlflow.tracking.MlflowClient()\n", - "\n", - "# Get all runs from the experiment\n", - "experiment = client.get_experiment_by_name(\"SentinelMD\")\n", - "runs = client.search_runs(experiment.experiment_id)\n", - "\n", - "test_queries = []\n", - "\n", - "# Pull run metrics and params\n", - "for run in runs:\n", - " test_queries.append(run.data.params[\"query\"])" - ], - "id": "772eb338dd0b64ef", - "outputs": [], "execution_count": 5 }, { "metadata": { "ExecuteTime": { - "end_time": "2026-04-18T20:43:25.277644Z", - "start_time": "2026-04-18T20:40:24.458935Z" + "end_time": "2026-04-21T06:52:47.418461Z", + "start_time": "2026-04-21T06:43:28.266430Z" } }, "cell_type": "code", @@ -96,16 +86,23 @@ "# Run test queries through the agent\n", "for query in test_queries:\n", " result = agent.invoke({\n", - " \"query\": query,\n", + " \"api_key\": None,\n", + " \"query\": query,\n", " \"search_query\": None,\n", - " \"cache_hit\": False,\n", " \"abstracts\": [],\n", " \"llm_response\": None,\n", " \"claims\": None,\n", " \"scored_claims\": None,\n", " \"confidence_score\": None,\n", - " \"final_response\": None\n", - " })\n", + " \"final_response\": None,\n", + " \"has_drug_query\": False,\n", + " \"drug_names\": None,\n", + " \"drug_labels\": None,\n", + " \"has_fhir\": None,\n", + " \"fhir_resource_type\": None,\n", + " \"fhir_resource_id\": None,\n", + " \"fhir_output\": None\n", + " })\n", " final = result[\"final_response\"]\n", " data[\"question\"].append(query)\n", " data[\"answer\"].append(final[\"response\"])\n", @@ -115,40 +112,38 @@ ], "id": "30b5d6b164540f67", "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/andrewfranco/Documents/JetBrains/Pycharm/SentinelMD/.venv/lib/python3.11/site-packages/mlflow/tracking/_tracking_service/utils.py:184: FutureWarning: The filesystem tracking backend (e.g., './mlruns') is deprecated as of February 2026. Consider transitioning to a database backend (e.g., 'sqlite:///mlflow.db') to take advantage of the latest MLflow features. See https://mlflow.org/docs/latest/self-hosting/migrate-from-file-store for migration guidance.\n", + " return FileStore(store_uri, store_uri)\n", + "2026/04/20 23:44:30 INFO mlflow.tracking.fluent: Experiment with name 'SentinelMD' does not exist. Creating a new experiment.\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "🏃 View run unruly-ape-73 at: http://127.0.0.1:5000/#/experiments/1/runs/dd1333ed7021490798d4723b5efd4f1d\n", - "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/1\n", - "🏃 View run valuable-robin-400 at: http://127.0.0.1:5000/#/experiments/1/runs/a563915a4c2a48fd8d109ef6aa510366\n", - "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/1\n", - "🏃 View run skittish-asp-34 at: http://127.0.0.1:5000/#/experiments/1/runs/1f128b94e3114cdda86909099e0048cd\n", - "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/1\n", - "🏃 View run sneaky-wolf-664 at: http://127.0.0.1:5000/#/experiments/1/runs/79f13b68586e452ca2c64e8d325e1d35\n", - "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/1\n", - "🏃 View run delicate-crab-530 at: http://127.0.0.1:5000/#/experiments/1/runs/26893882688f41e38bf1f3a594775465\n", - "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/1\n", - "🏃 View run nebulous-wolf-873 at: http://127.0.0.1:5000/#/experiments/1/runs/12beb84b994440c3ad7617a8dd9f8dd2\n", - "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/1\n", - "🏃 View run silent-skink-435 at: http://127.0.0.1:5000/#/experiments/1/runs/f9df316513224df2879a7dd90376d233\n", - "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/1\n", - "🏃 View run amusing-turtle-851 at: http://127.0.0.1:5000/#/experiments/1/runs/d644ebdb052549beb9c6416ae1c448b1\n", - "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/1\n", - "🏃 View run bright-skink-570 at: http://127.0.0.1:5000/#/experiments/1/runs/8a4b9f22fcb04d21bdd44881d0b02b86\n", - "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/1\n", - "🏃 View run victorious-tern-393 at: http://127.0.0.1:5000/#/experiments/1/runs/7e616d4a2ec54b0291e7d2086f1ba447\n", - "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/1\n" + "FDA lookup failed for PD-1: 404 Client Error: Not Found for url: https://api.fda.gov/drug/label.json?search=openfda.brand_name:%22PD-1%22&limit=1\n", + "FDA lookup failed for CTLA-4: 404 Client Error: Not Found for url: https://api.fda.gov/drug/label.json?search=openfda.brand_name:%22CTLA-4%22&limit=1\n", + "FDA lookup failed for LAG-3: 404 Client Error: Not Found for url: https://api.fda.gov/drug/label.json?search=openfda.brand_name:%22LAG-3%22&limit=1\n", + "FDA lookup failed for EGFR-TKI: 404 Client Error: Not Found for url: https://api.fda.gov/drug/label.json?search=openfda.brand_name:%22EGFR-TKI%22&limit=1\n", + "FDA lookup failed for EGFR: 404 Client Error: Not Found for url: https://api.fda.gov/drug/label.json?search=openfda.brand_name:%22EGFR%22&limit=1\n", + "FDA lookup failed for ALK: 404 Client Error: Not Found for url: https://api.fda.gov/drug/label.json?search=openfda.brand_name:%22ALK%22&limit=1\n", + "FDA lookup failed for KRAS: 404 Client Error: Not Found for url: https://api.fda.gov/drug/label.json?search=openfda.brand_name:%22KRAS%22&limit=1\n", + "FDA lookup failed for BRAF: 404 Client Error: Not Found for url: https://api.fda.gov/drug/label.json?search=openfda.brand_name:%22BRAF%22&limit=1\n", + "FDA lookup failed for NTRK: 404 Client Error: Not Found for url: https://api.fda.gov/drug/label.json?search=openfda.brand_name:%22NTRK%22&limit=1\n" ] } ], - "execution_count": 23 + "execution_count": 7 }, { "metadata": { "ExecuteTime": { - "end_time": "2026-04-18T21:21:55.056589Z", - "start_time": "2026-04-18T21:21:54.485221Z" + "end_time": "2026-04-21T07:24:33.944655Z", + "start_time": "2026-04-21T06:53:11.073522Z" } }, "cell_type": "code", @@ -181,77 +176,35 @@ "name": "stderr", "output_type": "stream", "text": [ - "/var/folders/n1/2qvnq4v53p51hj0p1nsg6ldc0000gn/T/ipykernel_20608/2330226121.py:7: DeprecationWarning: LangchainLLMWrapper is deprecated and will be removed in a future version. Use llm_factory instead: from openai import OpenAI; from ragas.llms import llm_factory; llm = llm_factory('gpt-4o-mini', client=OpenAI(api_key='...'))\n", + "/var/folders/n1/2qvnq4v53p51hj0p1nsg6ldc0000gn/T/ipykernel_67716/2330226121.py:7: DeprecationWarning: LangchainLLMWrapper is deprecated and will be removed in a future version. Use llm_factory instead: from openai import OpenAI; from ragas.llms import llm_factory; llm = llm_factory('gpt-4o-mini', client=OpenAI(api_key='...'))\n", " llm = LangchainLLMWrapper(ChatGoogleGenerativeAI(model=\"gemma-3-27b-it\", google_api_key=settings.GEMINI_API_KEY))\n", - "/var/folders/n1/2qvnq4v53p51hj0p1nsg6ldc0000gn/T/ipykernel_20608/2330226121.py:8: DeprecationWarning: LangchainEmbeddingsWrapper is deprecated and will be removed in a future version. Use the modern embedding providers instead: embedding_factory('openai', model='text-embedding-3-small', client=openai_client) or from ragas.embeddings import OpenAIEmbeddings, GoogleEmbeddings, HuggingFaceEmbeddings\n", + "/var/folders/n1/2qvnq4v53p51hj0p1nsg6ldc0000gn/T/ipykernel_67716/2330226121.py:8: DeprecationWarning: LangchainEmbeddingsWrapper is deprecated and will be removed in a future version. Use the modern embedding providers instead: embedding_factory('openai', model='text-embedding-3-small', client=openai_client) or from ragas.embeddings import OpenAIEmbeddings, GoogleEmbeddings, HuggingFaceEmbeddings\n", " embeddings = LangchainEmbeddingsWrapper(GoogleGenerativeAIEmbeddings(model=\"gemini-embedding-001\", google_api_key=settings.GEMINI_API_KEY))\n", - "Evaluating: 0%| | 0/30 [00:00 \u001B[39m\u001B[32m10\u001B[39m results = \u001B[43mevaluate\u001B[49m\u001B[43m(\u001B[49m\n\u001B[32m 11\u001B[39m \u001B[43m \u001B[49m\u001B[43mdataset\u001B[49m\u001B[43m=\u001B[49m\u001B[43mdataset\u001B[49m\u001B[43m,\u001B[49m\n\u001B[32m 12\u001B[39m \u001B[43m \u001B[49m\u001B[43mmetrics\u001B[49m\u001B[43m=\u001B[49m\u001B[43m[\u001B[49m\n\u001B[32m 13\u001B[39m \u001B[43m \u001B[49m\u001B[43mFaithfulness\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\u001B[43m,\u001B[49m\n\u001B[32m 14\u001B[39m \u001B[43m \u001B[49m\u001B[43mResponseRelevancy\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\u001B[43m,\u001B[49m\n\u001B[32m 15\u001B[39m \u001B[43m \u001B[49m\u001B[43mLLMContextPrecisionWithoutReference\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n\u001B[32m 16\u001B[39m \u001B[43m \u001B[49m\u001B[43m]\u001B[49m\u001B[43m,\u001B[49m\n\u001B[32m 17\u001B[39m \u001B[43m \u001B[49m\u001B[43mllm\u001B[49m\u001B[43m=\u001B[49m\u001B[43mllm\u001B[49m\u001B[43m,\u001B[49m\n\u001B[32m 18\u001B[39m \u001B[43m \u001B[49m\u001B[43membeddings\u001B[49m\u001B[43m=\u001B[49m\u001B[43membeddings\u001B[49m\u001B[43m,\u001B[49m\n\u001B[32m 19\u001B[39m \u001B[43m \u001B[49m\u001B[43mrun_config\u001B[49m\u001B[43m=\u001B[49m\u001B[43mcustom_run_config\u001B[49m\n\u001B[32m 20\u001B[39m \u001B[43m)\u001B[49m\n\u001B[32m 21\u001B[39m \u001B[38;5;28mprint\u001B[39m(results)\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Documents/JetBrains/Pycharm/SentinelMD/.venv/lib/python3.11/site-packages/ragas/_analytics.py:278\u001B[39m, in \u001B[36mtrack_was_completed..wrapper\u001B[39m\u001B[34m(*args, **kwargs)\u001B[39m\n\u001B[32m 275\u001B[39m \u001B[38;5;129m@wraps\u001B[39m(func)\n\u001B[32m 276\u001B[39m \u001B[38;5;28;01mdef\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[34mwrapper\u001B[39m(*args: P.args, **kwargs: P.kwargs) -> T:\n\u001B[32m 277\u001B[39m track(IsCompleteEvent(event_type=func.\u001B[34m__name__\u001B[39m, is_completed=\u001B[38;5;28;01mFalse\u001B[39;00m))\n\u001B[32m--> \u001B[39m\u001B[32m278\u001B[39m result = \u001B[43mfunc\u001B[49m\u001B[43m(\u001B[49m\u001B[43m*\u001B[49m\u001B[43margs\u001B[49m\u001B[43m,\u001B[49m\u001B[43m \u001B[49m\u001B[43m*\u001B[49m\u001B[43m*\u001B[49m\u001B[43mkwargs\u001B[49m\u001B[43m)\u001B[49m\n\u001B[32m 279\u001B[39m track(IsCompleteEvent(event_type=func.\u001B[34m__name__\u001B[39m, is_completed=\u001B[38;5;28;01mTrue\u001B[39;00m))\n\u001B[32m 281\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m result\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Documents/JetBrains/Pycharm/SentinelMD/.venv/lib/python3.11/site-packages/ragas/evaluation.py:484\u001B[39m, in \u001B[36mevaluate\u001B[39m\u001B[34m(dataset, metrics, llm, embeddings, experiment_name, callbacks, run_config, token_usage_parser, raise_exceptions, column_map, show_progress, batch_size, _run_id, _pbar, return_executor, allow_nest_asyncio)\u001B[39m\n\u001B[32m 480\u001B[39m \u001B[38;5;28;01melse\u001B[39;00m:\n\u001B[32m 481\u001B[39m \u001B[38;5;66;03m# Default behavior: use nest_asyncio for backward compatibility (Jupyter notebooks)\u001B[39;00m\n\u001B[32m 482\u001B[39m \u001B[38;5;28;01mfrom\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[34;01mragas\u001B[39;00m\u001B[34;01m.\u001B[39;00m\u001B[34;01masync_utils\u001B[39;00m\u001B[38;5;250m \u001B[39m\u001B[38;5;28;01mimport\u001B[39;00m run\n\u001B[32m--> \u001B[39m\u001B[32m484\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mrun\u001B[49m\u001B[43m(\u001B[49m\u001B[43m_async_wrapper\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\u001B[43m)\u001B[49m\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Documents/JetBrains/Pycharm/SentinelMD/.venv/lib/python3.11/site-packages/ragas/async_utils.py:156\u001B[39m, in \u001B[36mrun\u001B[39m\u001B[34m(async_func, allow_nest_asyncio)\u001B[39m\n\u001B[32m 148\u001B[39m loop_type = \u001B[38;5;28mtype\u001B[39m(loop).\u001B[34m__name__\u001B[39m\n\u001B[32m 149\u001B[39m \u001B[38;5;28;01mraise\u001B[39;00m \u001B[38;5;167;01mRuntimeError\u001B[39;00m(\n\u001B[32m 150\u001B[39m \u001B[33mf\u001B[39m\u001B[33m\"\u001B[39m\u001B[33mCannot execute nested async code with \u001B[39m\u001B[38;5;132;01m{\u001B[39;00mloop_type\u001B[38;5;132;01m}\u001B[39;00m\u001B[33m. \u001B[39m\u001B[33m\"\u001B[39m\n\u001B[32m 151\u001B[39m \u001B[33mf\u001B[39m\u001B[33m\"\u001B[39m\u001B[33muvloop does not support nested event loop execution. \u001B[39m\u001B[33m\"\u001B[39m\n\u001B[32m 152\u001B[39m \u001B[33mf\u001B[39m\u001B[33m\"\u001B[39m\u001B[33mPlease use asyncio\u001B[39m\u001B[33m'\u001B[39m\u001B[33ms standard event loop in Jupyter environments, \u001B[39m\u001B[33m\"\u001B[39m\n\u001B[32m 153\u001B[39m \u001B[33mf\u001B[39m\u001B[33m\"\u001B[39m\u001B[33mor refactor your code to avoid nested async calls.\u001B[39m\u001B[33m\"\u001B[39m\n\u001B[32m 154\u001B[39m )\n\u001B[32m--> \u001B[39m\u001B[32m156\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43masyncio\u001B[49m\u001B[43m.\u001B[49m\u001B[43mrun\u001B[49m\u001B[43m(\u001B[49m\u001B[43mcoro\u001B[49m\u001B[43m)\u001B[49m\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Documents/JetBrains/Pycharm/SentinelMD/.venv/lib/python3.11/site-packages/nest_asyncio.py:30\u001B[39m, in \u001B[36m_patch_asyncio..run\u001B[39m\u001B[34m(main, debug)\u001B[39m\n\u001B[32m 28\u001B[39m task = asyncio.ensure_future(main)\n\u001B[32m 29\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m---> \u001B[39m\u001B[32m30\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m \u001B[43mloop\u001B[49m\u001B[43m.\u001B[49m\u001B[43mrun_until_complete\u001B[49m\u001B[43m(\u001B[49m\u001B[43mtask\u001B[49m\u001B[43m)\u001B[49m\n\u001B[32m 31\u001B[39m \u001B[38;5;28;01mfinally\u001B[39;00m:\n\u001B[32m 32\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m task.done():\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Documents/JetBrains/Pycharm/SentinelMD/.venv/lib/python3.11/site-packages/nest_asyncio.py:92\u001B[39m, in \u001B[36m_patch_loop..run_until_complete\u001B[39m\u001B[34m(self, future)\u001B[39m\n\u001B[32m 90\u001B[39m f._log_destroy_pending = \u001B[38;5;28;01mFalse\u001B[39;00m\n\u001B[32m 91\u001B[39m \u001B[38;5;28;01mwhile\u001B[39;00m \u001B[38;5;129;01mnot\u001B[39;00m f.done():\n\u001B[32m---> \u001B[39m\u001B[32m92\u001B[39m \u001B[38;5;28;43mself\u001B[39;49m\u001B[43m.\u001B[49m\u001B[43m_run_once\u001B[49m\u001B[43m(\u001B[49m\u001B[43m)\u001B[49m\n\u001B[32m 93\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m \u001B[38;5;28mself\u001B[39m._stopping:\n\u001B[32m 94\u001B[39m \u001B[38;5;28;01mbreak\u001B[39;00m\n", - "\u001B[36mFile \u001B[39m\u001B[32m~/Documents/JetBrains/Pycharm/SentinelMD/.venv/lib/python3.11/site-packages/nest_asyncio.py:115\u001B[39m, in \u001B[36m_patch_loop.._run_once\u001B[39m\u001B[34m(self)\u001B[39m\n\u001B[32m 108\u001B[39m heappop(scheduled)\n\u001B[32m 110\u001B[39m timeout = (\n\u001B[32m 111\u001B[39m \u001B[32m0\u001B[39m \u001B[38;5;28;01mif\u001B[39;00m ready \u001B[38;5;129;01mor\u001B[39;00m \u001B[38;5;28mself\u001B[39m._stopping\n\u001B[32m 112\u001B[39m \u001B[38;5;28;01melse\u001B[39;00m \u001B[38;5;28mmin\u001B[39m(\u001B[38;5;28mmax\u001B[39m(\n\u001B[32m 113\u001B[39m scheduled[\u001B[32m0\u001B[39m]._when - \u001B[38;5;28mself\u001B[39m.time(), \u001B[32m0\u001B[39m), \u001B[32m86400\u001B[39m) \u001B[38;5;28;01mif\u001B[39;00m scheduled\n\u001B[32m 114\u001B[39m \u001B[38;5;28;01melse\u001B[39;00m \u001B[38;5;28;01mNone\u001B[39;00m)\n\u001B[32m--> \u001B[39m\u001B[32m115\u001B[39m event_list = \u001B[38;5;28;43mself\u001B[39;49m\u001B[43m.\u001B[49m\u001B[43m_selector\u001B[49m\u001B[43m.\u001B[49m\u001B[43mselect\u001B[49m\u001B[43m(\u001B[49m\u001B[43mtimeout\u001B[49m\u001B[43m)\u001B[49m\n\u001B[32m 116\u001B[39m \u001B[38;5;28mself\u001B[39m._process_events(event_list)\n\u001B[32m 118\u001B[39m end_time = \u001B[38;5;28mself\u001B[39m.time() + \u001B[38;5;28mself\u001B[39m._clock_resolution\n", - "\u001B[36mFile \u001B[39m\u001B[32m/Library/Frameworks/Python.framework/Versions/3.11/lib/python3.11/selectors.py:566\u001B[39m, in \u001B[36mKqueueSelector.select\u001B[39m\u001B[34m(self, timeout)\u001B[39m\n\u001B[32m 564\u001B[39m ready = []\n\u001B[32m 565\u001B[39m \u001B[38;5;28;01mtry\u001B[39;00m:\n\u001B[32m--> \u001B[39m\u001B[32m566\u001B[39m kev_list = \u001B[38;5;28mself\u001B[39m._selector.control(\u001B[38;5;28;01mNone\u001B[39;00m, max_ev, timeout)\n\u001B[32m 567\u001B[39m \u001B[38;5;28;01mexcept\u001B[39;00m \u001B[38;5;167;01mInterruptedError\u001B[39;00m:\n\u001B[32m 568\u001B[39m \u001B[38;5;28;01mreturn\u001B[39;00m ready\n", - "\u001B[31mKeyboardInterrupt\u001B[39m: " - ] - }, - { - "name": "stderr", + "name": "stdout", "output_type": "stream", "text": [ - "Exception raised in Job[0]: AssertionError(llm must be set to compute score)\n", - "Exception raised in Job[1]: AssertionError(LLM is not set)\n", - "Exception raised in Job[2]: AssertionError(LLM is not set)\n", - "Exception raised in Job[3]: AssertionError(LLM is not set)\n", - "Exception raised in Job[4]: AssertionError(LLM is not set)\n", - "Exception raised in Job[5]: AssertionError(LLM is not set)\n", - "Exception raised in Job[6]: AssertionError(LLM is not set)\n", - "Exception raised in Job[7]: AssertionError(LLM is not set)\n", - "Exception raised in Job[8]: AssertionError(LLM is not set)\n", - "Exception raised in Job[9]: AssertionError(LLM is not set)\n", - "Exception raised in Job[10]: AssertionError(LLM is not set)\n", - "Exception raised in Job[11]: AssertionError(LLM is not set)\n", - "Exception raised in Job[12]: AssertionError(LLM is not set)\n", - "Exception raised in Job[13]: AssertionError(LLM is not set)\n", - "Exception raised in Job[14]: AssertionError(LLM is not set)\n", - "Exception raised in Job[15]: AssertionError(LLM is not set)\n", - "Exception raised in Job[16]: AssertionError(LLM is not set)\n", - "Exception raised in Job[17]: AssertionError(LLM is not set)\n", - "Exception raised in Job[18]: AssertionError(LLM is not set)\n", - "Exception raised in Job[19]: AssertionError(LLM is not set)\n", - "Exception raised in Job[20]: AssertionError(LLM is not set)\n", - "Exception raised in Job[21]: AssertionError(LLM is not set)\n", - "Exception raised in Job[22]: AssertionError(LLM is not set)\n", - "Exception raised in Job[23]: AssertionError(LLM is not set)\n", - "Exception raised in Job[24]: AssertionError(LLM is not set)\n", - "Exception raised in Job[25]: AssertionError(LLM is not set)\n", - "Exception raised in Job[26]: AssertionError(LLM is not set)\n", - "Exception raised in Job[27]: AssertionError(LLM is not set)\n", - "Exception raised in Job[28]: AssertionError(LLM is not set)\n", - "Exception raised in Job[29]: AssertionError(LLM is not set)\n" + "{'faithfulness': 0.7735, 'answer_relevancy': nan, 'llm_context_precision_without_reference': 0.5609}\n" ] } ], - "execution_count": 47 + "execution_count": 8 }, { - "metadata": { - "ExecuteTime": { - "end_time": "2026-04-18T21:15:44.222004Z", - "start_time": "2026-04-18T21:15:44.151229Z" - } - }, + "metadata": {}, "cell_type": "code", "source": [ "with mlflow.start_run(run_name=\"ragas_evaluation\"):\n", @@ -263,17 +216,8 @@ " mlflow.log_param(\"retrieval_k\", 5)" ], "id": "ff8138554e213bcb", - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "🏃 View run ragas_evaluation at: http://127.0.0.1:5000/#/experiments/1/runs/249cd5b390b74077b671731e90a0795d\n", - "🧪 View experiment at: http://127.0.0.1:5000/#/experiments/1\n" - ] - } - ], - "execution_count": 43 + "outputs": [], + "execution_count": null } ], "metadata": { From 158e53d772e99117adb63caf48f686795ad0a17d Mon Sep 17 00:00:00 2001 From: AndrewVFranco <129307231+AndrewVFranco@users.noreply.github.com> Date: Tue, 21 Apr 2026 01:12:39 -0700 Subject: [PATCH 2/2] Add mobile support for frontend --- .env.example | 1 + frontend/src/App.css | 104 +++++++++++++++++++++++++- frontend/src/App.js | 41 +++++++--- frontend/src/components/ChatWindow.js | 14 ++-- 4 files changed, 144 insertions(+), 16 deletions(-) diff --git a/.env.example b/.env.example index e3b8af5..287d886 100644 --- a/.env.example +++ b/.env.example @@ -10,5 +10,6 @@ MLFLOW_TRACKING_URI=http://localhost:5000 MLFLOW_ARTIFACT_LOCATION=logs/mlflow FASTAPI_HOST=0.0.0.0 FASTAPI_PORT=8000 +REACT_APP_API_URL=http://127.0.0.1:8000 ENV=development DEBUG=True \ No newline at end of file diff --git a/frontend/src/App.css b/frontend/src/App.css index 605d14d..201bb9f 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -110,12 +110,20 @@ header.header { overflow: hidden; } -/* SIDEBAR */ -aside.sidebar { +/* SIDEBAR CONTAINER (New wrapper for mobile logic) */ +.sidebar-container { width: var(--sidebar); flex-shrink: 0; + height: 100%; background: var(--surface); border-right: 1px solid var(--border); + z-index: 50; + transition: left 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +/* SIDEBAR */ +aside.sidebar { + width: 100%; display: flex; flex-direction: column; overflow-y: auto; @@ -522,3 +530,95 @@ aside.sidebar { text-align: right; font-family: 'DM Mono', monospace; } + +/* --- MOBILE RESPONSIVE RULES --- */ + +/* Hide the hamburger menu and overlay on desktop */ +.mobile-menu-toggle { + display: none; +} + +.mobile-overlay { + display: block; + position: absolute; + top: var(--header); /* Start below the fixed header */ + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.4); + z-index: 40; + backdrop-filter: blur(2px); + } + +@media (max-width: 768px) { + header.header { + padding: 0 16px; + } + + /* Show hamburger menu */ + .mobile-menu-toggle { + display: block; + } + + /* Make layout relative for absolute positioning of sidebar */ + .layout { + position: relative; + flex-direction: column; + } + + /* Turn Sidebar into a slide-out drawer */ + .sidebar-container { + position: absolute; + top: var(--header); /* Start below the fixed header */ + bottom: 0; /* Stretch to the bottom */ + height: auto; /* Override the desktop 100% height */ + left: -100%; + box-shadow: 4px 0 24px rgba(0, 0, 0, 0.15); + } + + /* Slide in when 'open' */ + .sidebar-container.open { + left: 0; + } + + /* Stronger shadow in dark mode */ + body.dark-mode .sidebar-container.open { + box-shadow: 4px 0 24px rgba(0, 0, 0, 0.6); + } + + /* Chat window fills screen */ + .chat-window { + width: 100%; + } + + /* Show the darkening overlay */ + .mobile-overlay { + display: block; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.4); + z-index: 40; + backdrop-filter: blur(2px); + } + + /* Adjust message padding and widths */ + .messages { + padding: 16px; + } + + .message-user .bubble { + max-width: 90%; /* Let bubbles be wider on small screens */ + } + + .input-area { + padding: 12px 16px; + } + + /* Hide the "Enter to send" hint on mobile */ + .input-hint { + display: none; + } +} \ No newline at end of file diff --git a/frontend/src/App.js b/frontend/src/App.js index f8947d2..4e3bc1d 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -1,5 +1,5 @@ -import React, {useLayoutEffect, useState} from 'react'; -import { Sun, Moon } from 'lucide-react'; +import React, { useLayoutEffect, useState } from 'react'; +import { Sun, Moon, Menu, X } from 'lucide-react'; import Sidebar from './components/Sidebar'; import ChatWindow from './components/ChatWindow'; import './App.css'; @@ -12,6 +12,7 @@ function App() { const [messages, setMessages] = useState([]); const [loading, setLoading] = useState(false); const [result, setResult] = useState(null); + const [isSidebarOpen, setIsSidebarOpen] = useState(false); const [darkMode, setDarkMode] = useState(() => { const saved = localStorage.getItem('theme'); return saved ? saved === 'dark' : true; @@ -29,9 +30,14 @@ function App() { }); }; + const closeSidebar = () => setIsSidebarOpen(false); + const handleSubmit = async (fhirData = {}) => { if ((!query.trim() && !fhirData.has_fhir) || loading) return; + // Close the sidebar automatically on mobile when a query is sent + closeSidebar(); + const userMessage = { role: 'user', text: query || `FHIR ${fhirData.fhir_resource_type}/${fhirData.fhir_resource_id}` @@ -78,22 +84,39 @@ function App() { return (
-
- SentinelMD +
+ + +
+ SentinelMD +
-
- +
+ +
+ + {isSidebarOpen && ( +
+ )} + { // Stop the carousel if a message has already been sent if (messages.length > 0) return; const timer = setInterval(() => { setPlaceholderIndex((prev) => (prev + 1) % SAMPLE_PLACEHOLDERS.length); - }, 5000); // 5 seconds + }, 5000); return () => clearInterval(timer); }, [messages.length]); @@ -159,7 +159,9 @@ function ChatWindow({ messages, query, onQueryChange, onSubmit, loading }) { · HAPI R4 Server
-
+ + {/* Added flexWrap: 'wrap' here so the inputs stack cleanly on small mobile screens */} +