CareRelay turns fragmented caregiver updates into a shared care timeline, flags schedule conflicts, and generates a traceable handoff summary for the next caregiver.
When several people look after one person, the important details end up scattered across chat apps, voice notes, photos and memory. The next caregiver starts their shift not knowing what has already been done. CareRelay collects those updates in one place, keeps them structured, and turns the handoff into a single click.
Type or dictate what happened — "Dad ate half a bowl of porridge at 8:30" — and the AI turns it into a structured care record: category, time, title, details. Nothing is guessed silently: it shows you what it understood and waits for Confirm before saving anything.
Works in Chinese and mixed language, and keeps the caregiver's original sentence so the record stays auditable.
When an update is missing something important, the assistant asks one short question instead of inventing a value. Report a symptom and it walks through a short intake — what, where, how severe, how long, how it started — before it records anything.
Say "Dad has physiotherapy tomorrow at 3 PM" and CareRelay creates the appointment. If it overlaps something already booked, you get a conflict card with three choices: Keep both, Replace existing, Discard new. The overlap check is plain Python, not an AI judgement — it is deterministic and explainable.
Each caregiver has their own chat thread, but everyone writes to the same care board. Switch caregiver in the top bar and the conversation switches with it. When you come back, a short note tells you what the others recorded while you were away.
The Care Board shows the daily timeline, upcoming tasks with pending/completed toggles, overdue highlighting, open conflicts, and any photos attached to an event.
Generate handoff produces a summary for the next caregiver: what was completed, what still needs attention, what is coming up. Every line carries a source chip — click it to jump to the original timeline entry, hover it to read the caregiver's exact words. The summary can only reference records that exist in the database.
- Voice input — browser dictation (Chrome / Edge)
- Photo attachment — attach an image as supporting evidence; it appears on the timeline
- Manual entry — + Add task for anything you would rather type into a form than a chat
- Offline demo mode — the whole app runs with no backend at all, on built-in demo data
What it deliberately does not do: diagnose, recommend medication, or claim a task was missed. An empty record means not recorded, not not done.
React + Vite (frontend/)
│ Chat · Care Board · landing page
│ falls back to in-browser demo data if the API is unreachable
▼
FastAPI (backend/)
│ validation · approval store · conflict engine · attachments
▼
Azure AI Foundry (agent-framework + FoundryChatClient)
│ Conversation Agent → structured output
│ Handoff Agent → summarises database records only
▼
SQLite
The principle: Foundry interprets language. Python owns every write, every time comparison, every conflict check and the audit trail. AI-proposed writes always pass through:
model output → preview card → Confirm / Reject → database write
Pending approvals expire after 15 minutes and are held in memory, so restarting the backend discards them.
You need Node.js 20+. For the real backend you also need Python 3.11+ (3.13 recommended) and an Azure login.
Fastest way to see it. No Python, no Azure, no internet.
cd care-relay\frontend
Copy-Item .env.example .env.local
# put VITE_FORCE_MOCK=true in .env.local
npm install # first time only, ~15 s
npm run devOpen http://localhost:5173. The badge top-right reads Demo data — everything is clickable and runs on built-in sample data.
Two terminals.
Terminal 1 — backend
cd care-relay\backend
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt
.\.venv\Scripts\python.exe -m uvicorn app.main:app --reloadTerminal 2 — frontend
cd care-relay\frontend
npm run devOpen http://localhost:5173. The badge should now read Live backend.
Check http://localhost:8000/health — you want:
{"status":"ok","foundry_configured":true}foundry_configured: false means the AI is not connected; the app silently uses a small keyword parser and still looks like it works. Fix the configuration below rather than trusting the UI.
Copy care-relay/.env.example to care-relay/.env and set:
FOUNDRY_PROJECT_ENDPOINT=https://<resource>.services.ai.azure.com/api/projects/<project>
FOUNDRY_MODEL=<your-deployment-name>Two things people get wrong here: the endpoint is the services.ai.azure.com one (not cognitiveservices.azure.com), and FOUNDRY_MODEL is the name of your deployment, not the model family.
Authentication uses DefaultAzureCredential — no API key is stored anywhere. Sign in once:
az loginThe backend calls az behind the scenes, so start uvicorn from a terminal that can find it. A terminal opened before you installed the Azure CLI will fail with a credential error that looks like a login problem.
Full setup notes, including data resets and troubleshooting: care-relay/HOW_TO_RUN.txt.
- Chat tab. Pick a caregiver in the top bar — say Amy.
- Send
Dad ate half a bowl of porridge at 8:30.→ an "AI understood" card appears. Press Confirm (or Edit to correct it first). - Send
Dad has physiotherapy tomorrow at 3 PM.→ Confirm. It clashes with the physio already on the schedule, so the conflict card appears with Keep both / Replace existing / Discard new. - Care Board tab. → your events are on the daily timeline, the appointment is under Upcoming tasks.
- Press Generate handoff to Ben.
- In the summary, click a small source chip. → it jumps to the original entry and highlights it. Hover it to see Amy's exact sentence.
Try Chinese too: 爸爸中午喝了一杯水,看起来比平常累。 — the assistant replies in Chinese and, because tiredness reads as a symptom, walks through a short intake before recording.
Switch the caregiver in the top bar to see the separate-threads model: Ben's chat is his own, but the board is shared.
Live AI takes roughly 2-5 seconds per message. That is normal.
The Plant demo conflict button on the Care Board (demo mode only) recreates step 3's clash without typing.
Two independent stores — clear both for a clean run.
In the browser (F12 → Console):
localStorage.removeItem('careRelay.threads');
localStorage.removeItem('careRelay.caregiverId');
location.reload();In the backend — stop uvicorn first, then delete backend/care_relay.db and the files in backend/uploads/ (keep .gitkeep). It re-creates and re-seeds on the next start.
care-relay/
├── frontend/ React 18 + Vite 5, plain CSS
│ └── src/
│ ├── pages/ LandingPage · ChatPage · CareBoardPage
│ ├── components/ Timeline, ScheduleList, ConflictAlert, HandoffCard, AddTaskForm, …
│ ├── services/ api.js (live) · mockBackend.js (offline demo)
│ └── lib/care.js date and formatting helpers
├── backend/
│ └── app/
│ ├── main.py app setup, CORS, /uploads, /health
│ ├── models.py CareEvent · ScheduleItem · Caregiver · Attachment
│ ├── schemas.py Pydantic contracts incl. ConversationOutput
│ ├── routes/ bootstrap · chat · conflicts · events · schedules ·
│ │ handoffs · attachments · care_board
│ ├── services/ foundry_workflow_client · handoff_agent_client ·
│ │ conflict_engine · approval_service · conflict_store ·
│ │ handoff_service
│ └── tools/ create_care_event · create_schedule_item ·
│ check_schedule_conflict · get_handoff_context
├── foundry/ agent instructions and output schemas
├── demo/ seed data and demo script
└── docs/ collaboration notes
| Method | Path | Purpose |
|---|---|---|
| GET | /api/bootstrap |
Caregivers, care recipient, current shift |
| POST | /api/chat |
Send a caregiver message → structured result |
| POST | /api/chat/confirm |
Approve or reject a proposed write |
| POST | /api/events |
Create an event manually |
| PATCH | /api/events/{event_id} |
Edit an event |
| POST | /api/schedules |
Create a schedule item (returns conflicts) |
| PATCH | /api/schedules/{schedule_id} |
Edit / mark completed |
| POST | /api/conflicts/resolve |
keep_both · replace_existing · discard_new |
| GET | /api/care-board |
Timeline, schedule, conflicts, shift |
| POST | /api/handoffs/generate |
Handoff summary for a time range |
| POST | /api/attachments |
Image / voice upload |
| GET | /health |
Liveness and Foundry configuration flag |
Interactive docs while the backend is running: http://localhost:8000/docs
| Symptom | Fix |
|---|---|
'npm' is not recognized |
Install Node from nodejs.org, then open a new terminal |
| Port 5173 in use | npm run dev -- --port 5174 |
| Port 8000 in use | An old backend is still running — close it, or find the PID with netstat -ano | findstr :8000 |
| Badge stuck on "Demo data" | Backend not answering. Check http://localhost:8000/health |
foundry_configured: false |
care-relay/.env is missing or its values are blank |
| Credential error on startup | The terminal cannot find az. Open a new one, or run az login |
| Mic button greyed out | Browser dictation is Chrome / Edge only — type instead |
| Photo does not appear | Uploading needs the backend. In demo mode the image lives only in the browser and is lost on refresh |
| Page is blank | F12 → Console → read the first red error |
| Changes not showing | Hard reload with Ctrl+Shift+R |
| Area | Owner |
|---|---|
| Azure AI Foundry agents | Member 1 |
| Backend and database | Member 2 |
| Chat frontend | Member 3 |
| Care Board and integration | Member 4 |
Working notes: care-relay/docs/COLLABORATION.md
CareRelay records and summarises information that caregivers provide. It does not diagnose conditions, recommend medication, or assert that an unrecorded task was missed. Database writes and conflict resolutions always require human confirmation.