DocUA's picture
docs: add CLAUDE.md with architecture and workflow notes
beb5a53
|
Raw
History Blame Contribute Delete
8.73 kB

A newer version of the Gradio SDK is available: 6.24.0

Upgrade

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:

  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.