BehavioralHealth App is a full-stack project with:
- an Expo / React Native frontend
- a FastAPI backend
- SQLite persistence
- automated backend and frontend tests
- an optional Docker workflow for integrated environment testing
Current capabilities include:
- registering and logging in
- capturing and editing a health intake profile during and after registration
- guiding first-time users through a live in-app tutorial across the main screens
- progressing through lessons in order, with later lessons locked until earlier ones are finished
- creating and listing conversations
- sending messages
- generating an assistant reply from the backend
- viewing saved chat history in a read-only conversation screen with per-message timestamps
- navigating between dedicated app screens with Expo Router
- tracking coach state from conversation updates
- generating session report memory for later assistant replies
Install the following before you begin:
- Node.js LTS
- npm
- Python 3.13 recommended
- Docker Desktop optional for the Docker workflows
Recommended download sources:
- Node.js:
https://nodejs.org/ - Python:
https://www.python.org/downloads/ - Docker Desktop:
https://www.docker.com/products/docker-desktop/
Confirm your tools are available:
node -v
npm -v
py -0
docker --version
docker compose versionNotes:
- On Windows,
py -0should show a Python version such as3.13. - If PowerShell blocks
npm, usenpm.cmdinstead ofnpm. - Docker is optional unless you want the Docker setup paths below.
Use this section once on a new machine before following any Quickstart path.
cd backend
py -3.13 -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip setuptools wheel
python -m pip install -r requirements-dev.txt
cd ..
cd frontend
npm.cmd install
cd ..This frontend install step picks up all current Expo/mobile dependencies, including the tutorial spotlight package react-native-svg.
Optional env files:
Copy-Item backend\.env.example backend\.env
Copy-Item frontend\.env.example frontend\.envcd backend
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip setuptools wheel
python -m pip install -r requirements-dev.txt
cd ..
cd frontend
npm install
cd ..This frontend install step picks up all current Expo/mobile dependencies, including the tutorial spotlight package react-native-svg.
Optional env files:
cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.envOptional OpenAI setup:
- The app runs without OpenAI access. If you skip this, the backend stays in safe local test mode and chat replies use the built-in stub behavior.
- To use OpenAI for real assistant replies, update
backend/.envwith:
BHA_ASSISTANT_TEST_MODE=false
OPENAI_API_KEY=YOUR_OPENAI_API_KEY
BHA_ASSISTANT_MODEL_NAME=gpt-4.1-miniPhone-testing extras:
- Install Expo Go on your phone.
Use this section for the three main ways to run the project, in priority order after completing First-Time Setup.
Use this when you want the primary phone-testing workflow for the app.
- Start the backend in Docker:
docker compose up --build backend- In a second terminal, start the Expo frontend for phone mode:
npm.cmd run check:frontend:phone
npm.cmd run frontend:phone- Open Expo Go on your phone and scan the QR code.
What to expect:
- the backend is available on port
8000 - the frontend syncs
frontend/.envto your current LAN IP - the frontend preflight checks only the frontend requirements for this Docker-backed flow
- Expo starts in LAN mode for phone testing
- your phone should be able to reach
http://YOUR_LOCAL_IP:8000/health
Use this when you want the quickest reproducible integrated environment on one machine.
docker compose up --buildThen open:
http://127.0.0.1:8080for the frontendhttp://127.0.0.1:8000/docsfor backend Swagger
Important:
- this Docker stack serves the frontend as a web app for integrated testing
- the mobile app workflow still uses Expo locally
Use this when you want both the backend and frontend running locally.
Windows PowerShell:
npm.cmd run check
npm.cmd run appmacOS / Linux / Git Bash:
npm run check
npm run appWhat to expect:
- the backend starts on
http://127.0.0.1:8000 - Expo starts in a second process for the frontend
- the app opens to the login screen, then routes to Home, Lessons, Chat, History, and Profile after authentication
- first-time users are guided through a live tutorial on the actual app screens after sign-in or registration
- lessons unlock in sequence, and each lesson page includes a
Finish Lessonaction to unlock the next one
The detailed sections below follow this same order: phone workflow first, integrated Docker second, and fully local development third.
Use this when testing the mobile app with Expo Go.
Use this path when both your phone and computer are on the same Wi-Fi network.
- Install Expo Go on your phone.
- From the repo root, run:
npm.cmd run check:phone
npm.cmd run app:phoneWhat these commands do:
- detect your local network IP address
- update
frontend/.envto use that IP forEXPO_PUBLIC_API_URL - warn if the selected phone API URL looks like a virtual or low-confidence adapter
- start the backend on
0.0.0.0 - start Expo in LAN mode
- Scan the Expo QR code with your phone.
If Expo LAN mode fails on your network, try:
cd frontend
npm.cmd start -- --tunnelUse this when you want the backend containerized but still want the real mobile app flow through Expo Go.
- Start the backend container:
docker compose up --build backend- In a second terminal:
npm.cmd run check:frontend:phone
npm.cmd run frontend:phoneThis path uses the frontend-only phone preflight because the backend is already running in Docker.
- On your phone browser, open:
http://YOUR_LOCAL_IP:8000/health
Expected result:
{"status":"ok"}- Scan the Expo QR code with Expo Go.
Why this split is recommended:
- Docker is a strong fit for the backend and persisted services
- Expo mobile development works better on the host machine than inside containers
- this gives you a reliable phone workflow without sacrificing the backend Docker environment
If you prefer helper scripts for the phone workflow:
Windows PowerShell:
docker compose up --build backendThen in a second terminal:
.\run_frontend.ps1 -PhonemacOS / Linux / Git Bash:
docker compose up --build backendThen in a second terminal:
./run_frontend.sh --phoneUse this when you want the simplest integrated environment with minimal local Python setup.
The integrated Docker environment includes:
backend: FastAPI API servicefrontend: browser-served Expo web export with a reverse proxy to the backendbackend_data: Docker volume for persisted SQLite data
This Docker workflow is for:
- integrated environment testing
- demos
- deployment-style validation
The mobile app remains the primary workflow outside Docker.
Start the full stack:
docker compose up --buildExpected result:
- the backend starts on
http://127.0.0.1:8000 - the frontend starts on
http://127.0.0.1:8080 http://127.0.0.1:8080/api/healthproxies from the frontend container to the backend container- the backend database persists in the
backend_dataDocker volume
Stop the stack:
docker compose downRemove the persisted Docker database volume too:
docker compose down -vIf you only want the backend container:
docker compose up --build backendThat starts only the backend on port 8000 with persisted SQLite storage.
To run the integrated Docker environment instead of the local frontend/backend workflow:
docker compose up --buildThen verify:
- frontend:
http://127.0.0.1:8080 - backend docs:
http://127.0.0.1:8000/docs - proxy health check:
http://127.0.0.1:8080/api/health
Detailed runbooks:
docs/runbooks/docker-stack.mddocs/runbooks/backend-docker.md
This is the standard day-to-day development workflow when you want both frontend and backend running locally.
Windows PowerShell:
cd backend
py -3.13 -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip setuptools wheel
python -m pip install -r requirements-dev.txtmacOS / Linux / Git Bash:
cd backend
python3 -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip setuptools wheel
python -m pip install -r requirements-dev.txtOptional backend env file:
Windows PowerShell:
Copy-Item .env.example .envmacOS / Linux / Git Bash:
cp .env.example .envThe backend .env file controls the chat-agent service configuration.
It is loaded from backend/.env even when you start the app from the repo root with npm run app or npm run app:phone.
If you do not configure OpenAI, the app still runs in local test mode with stub chat replies.
OpenAI notes:
OPENAI_API_KEYworks directly in this backend as a convenience alias.BHA_ASSISTANT_LLM_API_KEYalso works if you prefer the explicit backend-prefixed setting.- If you turn test mode off and keep the old stub defaults, the backend automatically switches to
https://api.openai.comandgpt-4.1-mini. - A blank
BHA_ASSISTANT_LLM_BASE_URLis also treated as the default OpenAI path when test mode is off and an API key is present. - You can still point
BHA_ASSISTANT_LLM_BASE_URLat any other OpenAI-compatible service.
SQLite details:
- data is stored in SQLite
- the database path is controlled by
BHA_SQLITE_DB_PATH - the default path is
data/behavioral_health.sqlite3insidebackend
Install dependencies from the frontend folder:
cd frontend
npm.cmd installIf you pull new frontend changes later, run npm.cmd install in frontend again so any newly added Expo/native packages are installed before starting the app.
Optional frontend env file:
Windows PowerShell:
Copy-Item .env.example .envmacOS / Linux / Git Bash:
cp .env.example .envRoot tooling behavior:
npm run check,npm run frontend, andnpm run appkeepfrontend/.envin localhost modenpm run check:phone,npm run frontend:phone, andnpm run app:phonesyncfrontend/.envto your current LAN IP for the full local phone workflownpm run check:frontend:phoneis the frontend-only preflight for the Docker-backend phone workflownpm run check:phoneandnpm run check:frontend:phoneprint the selected interface and warn, but do not fail, if the detected phone API URL looks suspicious
Tutorial behavior:
- new accounts receive a first-time guided tutorial that walks through the real app screens
- the tutorial completion state is stored per account, so returning users are not shown it again
- in development builds, the Home screen includes a
Replay Tutorialbutton for retesting the walkthrough
The frontend uses Expo Router:
- runtime entry point:
expo-router/entry - route files live in
frontend/app/
From the repo root:
npm.cmd run check
npm.cmd run appThese root commands do:
npm run check: verifies the backend virtual environment, frontend Expo dependencies, and syncs the frontend backend URL to localhostnpm run app: starts backend and frontend togethernpm run check:frontend:phone: verifies frontend dependencies only and syncs the frontend backend URL to your LAN IP
If you prefer helper scripts:
Windows PowerShell:
.\run_backend.ps1
.\run_frontend.ps1
.\run_app.ps1macOS / Linux / Git Bash:
./run_backend.sh
./run_frontend.sh
./run_app.shRun only the backend:
cd backend
.\.venv\Scripts\Activate.ps1
python -m uvicorn app.main:app --reloadRun only the frontend:
cd frontend
npm.cmd start -- --host localhostFor the browser:
cd frontend
npm.cmd run webIf Expo appears stale after a routing change:
cd frontend
npm.cmd start -- --clearFrom the repo root in Windows PowerShell:
cd backend
.\.venv\Scripts\Activate.ps1
python -m pytest -qExpected result:
- all backend tests pass
To run only the standalone SQLite persistence unit tests:
cd backend
.\.venv\Scripts\Activate.ps1
python -m pytest -q tests/test_sqlite_persistence.pyFrom the repo root in Windows PowerShell:
cd frontend
npm.cmd testThese tests verify that:
- the frontend calls the backend API client correctly
- backend responses are mapped into frontend data correctly
- registration and health-profile API flows map correctly
- lesson progression, lesson completion, and locked-lesson behavior map correctly
- the routed app UI updates after login, sending a message, opening history, and viewing a saved read-only transcript
- message timestamps render on the live chat screen and the saved history transcript
- tutorial replay, skip-confirmation flow, and step progression work
- tutorial spotlight geometry and popup placement logic stay within expected bounds
From the repo root in Windows PowerShell:
cd frontend
npm.cmd run typecheckexpo is not recognized
- Run
npm.cmd installin thefrontendfolder first on Windows, ornpm installon macOS/Linux.
npm.ps1 cannot be loaded in PowerShell
- PowerShell is blocking the npm script shim on this machine.
- Use
npm.cmd install,npm.cmd start -- --host localhost,npm.cmd test, ornpm.cmd run typecheck.
"node" is not recognized
- Close and reopen the terminal after installing Node.js.
- Make sure Node.js was installed with PATH enabled.
- Check with
node -vandnpm -v.
Network request failed in Expo
- Make sure the backend is running.
- Make sure
EXPO_PUBLIC_API_URLpoints to the correct backend address. - If you are using a real phone, start the backend on
0.0.0.0. - Make sure the phone and computer are on the same network.
- Prefer
npm run check:phoneandnpm run app:phonefor the full local phone workflow. - Prefer
npm run check:frontend:phoneandnpm run frontend:phonewhen the backend is already running in Docker. - If
npm run check:phonewarns about a suspicious adapter IP, prefer the Wi-Fi interface it reports for phone testing. - If you previously used phone mode,
frontend/.envmay still point to your LAN IP. Switch it back tohttp://127.0.0.1:8000for same-machine testing if needed.
Unsupported platform: 312 while installing backend dependencies
- This usually means the wrong Python installation is being used.
- Prefer a standard Python install from
python.org. - Python
3.13is the safest choice for this project.
docker: command not found
- Docker Desktop is not installed or not available on PATH.
- Install Docker Desktop and reopen the terminal.
Bind for 0.0.0.0:8000 failed: port is already allocated
- Another process is already using port
8000. - Stop the local backend or change the published port in
docker-compose.yml.
Bind for 0.0.0.0:8080 failed: port is already allocated
- Another process is already using port
8080. - Stop the conflicting process or change the frontend port mapping in
docker-compose.yml.
failed to start tunnel
- Expo tunnel issues are often temporary.
- Try
npm.cmd start -- --host localhostfirst on Windows, ornpm start -- --host localhoston macOS/Linux. - Use
npm run app:phonefor the normal LAN workflow.
Key directories:
frontend: Expo / React Native appbackend: FastAPI backenddocs/runbooks: operational runbooks, including Docker setup notes
Key files:
frontend/app/: Expo Router screens and layouts for the mobile/web appfrontend/App.tsx: test wrapper that renders the routed app in Jestfrontend/lib/api.ts: frontend API clientfrontend/lib/session.tsx: frontend authentication/session state for routed screensfrontend/Dockerfile: frontend container image definition for the Docker test stackfrontend/docker/nginx.conf: frontend reverse-proxy config for/apitraffic to the backend containerbackend/app/main.py: backend API routesbackend/Dockerfile: backend container image definitiondocker-compose.yml: Docker Compose entry for the integrated Docker stackbackend/app/assistant_agent.py: backend assistant reply logicbackend/app/services/chatbox/chat_agent.py: migrated chat-agent service layerbackend/app/services/chatbox/extractor_agent.py: migrated extractor and session report logicbackend/app/services/chatbox/state_tracker.py: migrated coach-state tracker logic
- The frontend communicates with the backend through
frontend/lib/api.ts. - Authentication routes are
POST /auth/registerandPOST /auth/login. - Authentication responses include a first-time tutorial flag for the frontend walkthrough.
- The tutorial completion route is
POST /auth/tutorial/complete. - Lesson routes are
GET /lessons,GET /lessons/{lesson_id}, andPOST /lessons/{lesson_id}/complete. - Lessons are unlocked sequentially per user; locked lesson detail and completion attempts are rejected by the backend.
- The backend assistant reply route is
POST /conversations/{conversation_id}/assistant-reply. GET /conversationspowers the saved session list, andGET /conversations/{conversation_id}/historypowers the read-only conversation transcript view.- Message payloads include
created_at, which the frontend shows as a small timestamp under each message in chat and history views. - Backend routes are defined in
backend/app/main.py. - Conversation and message data are persisted in SQLite instead of the old in-memory store.
- The assistant reply route uses a migrated chat-agent service under
backend/app/services/chatbox/. - The backend also runs a migrated extractor/state-tracker flow when assistant replies are generated.
- Session report memory is stored and reused in later assistant replies.
- Conversation, history, coach-state, and session-report routes require a bearer token.
- Debug inspection routes are
GET /conversations/{conversation_id}/coach-stateandGET /conversations/{conversation_id}/session-reports.
Use Swagger when you want to inspect backend state directly.
- Start the backend:
cd backend
.\.venv\Scripts\Activate.ps1
python -m uvicorn app.main:app --reload- Open:
http://127.0.0.1:8000/docs
- Register or log in first:
- call
POST /auth/registerorPOST /auth/login - copy the returned
access_token - click Authorize in Swagger and paste
Bearer YOUR_TOKEN
- Create a conversation with
POST /conversations:
{
"title": "Debug Test Session"
}-
Copy the returned conversation ID, such as
conv-1. -
Send a user message with
POST /conversations/{conversation_id}/messages:
{
"role": "user",
"content": "I am too tired after work to exercise."
}-
Generate an assistant reply with
POST /conversations/{conversation_id}/assistant-reply. -
Inspect the generated coach state:
GET /conversations/{conversation_id}/coach-state
- Inspect the generated session report memory:
GET /conversations/{conversation_id}/session-reports
If those two routes return data after the assistant reply call, then the migrated extractor, state tracker, and session report flow are all working.
Use this test to confirm that conversations and messages stay saved after the backend server restarts.
- Start the backend:
cd backend
.\.venv\Scripts\Activate.ps1
python -m uvicorn app.main:app --reload-
Open Swagger at
http://127.0.0.1:8000/docs. -
Register or log in and authorize.
-
Create a conversation with:
{
"title": "SQLite Restart Test"
}- Add a message with:
{
"role": "user",
"content": "This message should still exist after restart."
}- Confirm the message exists with:
GET /conversations/{conversation_id}/history
-
Stop the backend with
Ctrl + C. -
Start the backend again:
python -m uvicorn app.main:app --reload- Log in again if needed and confirm:
GET /conversations
GET /conversations/{conversation_id}/history
If the conversation and message are still returned after restart, SQLite persistence is working.
Detailed operational docs:
docs/runbooks/docker-stack.mddocs/runbooks/backend-docker.md