DocUA's picture
docs: add CLAUDE.md with architecture and workflow notes
beb5a53
|
Raw
History Blame Contribute Delete
8.73 kB
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## What this is
A Gradio web app: a **Medical Assistant** chat that runs a **background spiritual-distress classifier** on every patient message, plus a **verification/review workflow** so clinicians and chaplains can grade the classifier's output and export the results. Python 3.14, Gradio 6.1.0, LLM calls to Google Gemini and Anthropic Claude.
## Commands
```bash
# Setup
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
# .env must contain GEMINI_API_KEY and/or ANTHROPIC_API_KEY
# Run β€” full interface (Chat, Verification, Model Settings, Edit Prompts, Profiles, Help)
./run.sh # port 7861, activates .venv and sets PYTHONPATH
python app.py # port 7860
# Run β€” simplified interface (Chat, Conversation Verification, Help only)
python run_simplified_interface.py
# Tests
python run_tests.py # all suites, one pytest subprocess per directory
python -m pytest tests/unit -q # one suite
python -m pytest tests/unit/test_consent_manager.py::test_name -q # one test
```
Tests import as `src.*`; pytest's rootdir insertion makes this work when run from the repo root. Run from anywhere else and you need `PYTHONPATH=.`. `direnv` (`.envrc`) does venv activation + `PYTHONPATH` + dotenv automatically.
Baseline as of this writing: `tests/unit` is **5 failed, 170 passed** β€” the 5 failures in `test_enhanced_results_display.py` are pre-existing, not caused by your change. Verify against a clean checkout before assuming you broke something. Several older files under `tests/unit` and `tests/integration` are script-style (`test_*()` returning `bool`), so they pass in pytest regardless of what they print β€” don't trust them as regression gates.
Env vars that change behavior: `LOG_PROMPTS=true` (dump every system/user prompt and response to `ai_interactions.log`), `PERF_TIMING` (per-stage `[PERF]` timing in the message path, on by default), `GRADIO_SERVER_PORT` / `GRADIO_SERVER_NAME` / `GRADIO_SHARE`, `GCP_SERVICE_ACCOUNT_B64` (base64 service-account JSON β€” enables Vertex AI instead of the Gemini API key).
## Two interfaces, one core
`src/interface/gradio_app.py` (full) and `src/interface/simplified_gradio_app.py` (simplified, built for the testing team) are **separate, largely parallel UI builders over the same `SimplifiedMedicalApp` core**. A change to shared chat/verification behavior usually has to be made or checked in both. The full app delegates to the `*_handlers.py` modules; the simplified app uses `simplified_chat_handlers.py` and its own inline wiring.
Entry points disagree on purpose: `app.py` β†’ `gradio_app.main`, `run_simplified_interface.py` β†’ `simplified_gradio_app.main`, and the README front-matter (`app_file:`) points Hugging Face Spaces at `simplified_gradio_app.py`. If you change what the deployed Space runs, update the README front-matter too.
## Message flow
`SimplifiedMedicalApp.process_message` (`src/core/simplified_medical_app.py`) is the single entry point for every patient turn. It is a state machine over `SessionSpiritualState` (`spiritual_state.py`), *not* a straight classify-then-answer:
1. **Awaiting consent** β†’ `_handle_consent_response` (patient is deciding on a chaplain referral).
2. **In triage** β†’ `_handle_triage_response` β€” continues the existing YELLOW triage; the classifier is **not** re-run mid-triage.
3. **Otherwise** β†’ `SpiritualMonitor.classify` returns GREEN / YELLOW / RED:
- GREEN β†’ normal medical reply.
- YELLOW β†’ `SoftTriageManager` asks up to 2–3 gentle clarifying questions, then resolves to GREEN or escalates to RED (`_resolve_to_green` / `_escalate_to_red`; forced decision at the question limit).
- RED β†’ crisis response + consent request + `ProviderSummaryGenerator` handoff summary.
The response returned to Gradio is prefixed with a classification badge, and every exchange is written by `ConversationLogger` to `conversation_logs/session_*.json`. `spiritual_state.last_assessment` is the shared slot the display, the logger, and the verification tab all read β€” the triage/escalation helpers update it as a side effect.
## Agent name keys
A small set of string keys threads through config, model overrides, prompt overrides, and the UI. They must stay in sync across four places:
- `AGENT_CONFIGURATIONS` in `src/config/ai_providers_config.py` β€” provider + model + temperature per agent.
- `apply_model_settings` in `src/interface/model_handlers.py` β€” the Model Settings tab writes `session.custom_models` under these keys.
- `_prompt_name_to_agent` in `src/interface/prompt_handlers.py` β€” maps emoji UI labels ("πŸ” Spiritual Monitor (Classifier)") to agent keys.
- `SimplifiedMedicalApp.set_model_overrides` / `set_prompt_overrides`, which propagate onto `AIClientManager`.
`get_agent_config` **silently falls back to Gemini 2.5 Flash for an unknown agent name** β€” a typo'd key produces a working app that quietly uses the wrong model. Note that `SoftSpiritualTriage` and `TriageResponseEvaluator` are used by the UI but are not in `AGENT_CONFIGURATIONS`, so they currently hit that fallback.
## Prompt system
Prompts are text files in `src/config/prompts/`, but resolution goes through `PromptController` (`src/config/prompt_management/prompt_controller.py`) with three tiers, highest first:
1. **Session override** β€” set from the Edit Prompts tab, scoped to one `session_id`, never touches disk.
2. **Centralized file** β€” `src/config/prompts/*.txt`, with shared components (indicators / rules / templates / categories from `prompt_management/data/*.json`) spliced into placeholders.
3. **Hardcoded default** β€” module-level `SYSTEM_PROMPT_*` constants in `spiritual_monitor.py`, `soft_triage_manager.py`, `src/config/prompts.py`.
"Promote to File" writes a session override to the real `.txt` and leaves a `*.backup.<timestamp>.txt` beside it (that's what the existing backup files in `src/config/prompts/` are). `PromptController` caches per `agent_type + session_id`; if you edit a prompt file on disk while the app is running, the cache can serve the stale copy.
## Verification subsystems
Two distinct workflows that share vocabulary but not code paths:
- **Conversation Verification** β€” replays the *current chat session*, exchange by exchange, for reviewer grading. Handlers in `src/interface/verification_handlers.py` (`_generate_conv_verification*`, `_mark_conv_correct/_incorrect`, `_export_conv_records_to_{json,csv}`). State lives in Gradio component state, not in a store. The Provider Summary is appended as the **final exchange**, and on that step the classification flag is hidden (see Gradio notes below).
- **Enhanced Verification** β€” batch testing via manual entry or CSV/XLSX upload (`enhanced_verification_interface.py`, `manual_input_interface.py`, `file_upload_interface.py`), persisted through `JSONVerificationStore` (`src/core/verification_store.py`) into `verification_sessions/`, exported to `verification_exports/`.
Feature toggles for both live in `app_config.py` (`FEATURE_FLAGS`, `ENHANCED_VERIFICATION_CONFIG`). Standard Verification is deliberately disabled there β€” its functionality was folded into Enhanced Verification; leave it off unless asked.
## Gradio 6 gotchas this codebase has already been bitten by
- A `gr.Radio` handed `""` raises (value not in choices) and fails the **entire** event response, painting "Error" across unrelated components. Normalize unselected radios to `None`.
- Handler return arity must match the `outputs=` list on **every** branch, including early returns and empty-session paths. Mismatches surface as unrelated UI breakage, not a clear traceback.
## Conventions
- Everything imports absolute from the repo root (`from src.core... import`); scripts that may be run directly re-insert the project root into `sys.path` at the top.
- Property-based tests use Hypothesis and live in `tests/verification_mode/test_properties_*.py` and `tests/chaplain_feedback/test_properties_*.py` β€” these are the meaningful correctness gates. `manual_tests/` holds scripts that hit real APIs and are run by hand, never in CI.
- Ignore `src/interface/enhanced_verification_ui_backup.py` β€” dead 1700-line copy kept around; don't edit it or use it as a reference.
- `.gitignore` excludes `docs/`, `conversation_logs/`, `exports/`, `review/`, `verification_*/`, and (oddly) `src/core/verification_store.py` β€” that last one is already tracked so edits commit normally, but `git status` will not surface new files in those paths.
- No PHI is persisted; API keys stay in `.env`.