Spaces:
Sleeping
A newer version of the Gradio SDK is available: 6.24.0
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
# 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:
- Awaiting consent β
_handle_consent_response(patient is deciding on a chaplain referral). - In triage β
_handle_triage_responseβ continues the existing YELLOW triage; the classifier is not re-run mid-triage. - Otherwise β
SpiritualMonitor.classifyreturns GREEN / YELLOW / RED:- GREEN β normal medical reply.
- YELLOW β
SoftTriageManagerasks 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 +
ProviderSummaryGeneratorhandoff 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_CONFIGURATIONSinsrc/config/ai_providers_config.pyβ provider + model + temperature per agent.apply_model_settingsinsrc/interface/model_handlers.pyβ the Model Settings tab writessession.custom_modelsunder these keys._prompt_name_to_agentinsrc/interface/prompt_handlers.pyβ maps emoji UI labels ("π Spiritual Monitor (Classifier)") to agent keys.SimplifiedMedicalApp.set_model_overrides/set_prompt_overrides, which propagate ontoAIClientManager.
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:
- Session override β set from the Edit Prompts tab, scoped to one
session_id, never touches disk. - Centralized file β
src/config/prompts/*.txt, with shared components (indicators / rules / templates / categories fromprompt_management/data/*.json) spliced into placeholders. - Hardcoded default β module-level
SYSTEM_PROMPT_*constants inspiritual_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 throughJSONVerificationStore(src/core/verification_store.py) intoverification_sessions/, exported toverification_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.Radiohanded""raises (value not in choices) and fails the entire event response, painting "Error" across unrelated components. Normalize unselected radios toNone. - 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 intosys.pathat the top. - Property-based tests use Hypothesis and live in
tests/verification_mode/test_properties_*.pyandtests/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. .gitignoreexcludesdocs/,conversation_logs/,exports/,review/,verification_*/, and (oddly)src/core/verification_store.pyβ that last one is already tracked so edits commit normally, butgit statuswill not surface new files in those paths.- No PHI is persisted; API keys stay in
.env.