Working prototype of an internal scheduling assistant for Lavni admin/support. It reviews mock RingCentral, Twilio, and therapist/client chat data, extracts scheduling intent, checks availability/conflicts, schedules only when safe, and uses a mock Slack channel for notifications and approvals.
This is not a client-facing bot. It never messages clients or takes over conversations.
- Backend: Python FastAPI (
backend/) - Frontend: React + Vite (
client/) - Data: JSON mock files under
data/(default) - Integrations: Provider adapters (mock today → live APIs when keys are set)
- NLP: Mock AI layer by default (rule-based). Optional OpenAI/Anthropic when
USE_LLM=true+ API key.
By default no API keys are required. Copy .env.example → .env only when you want to enable live channels or LLM NLP.
| Env | Meaning |
|---|---|
USE_MOCK=true |
Force all channel providers to local JSON (interview default) |
USE_LLM=false |
Mock AI layer for intent/entities (default). Set true + OPENAI_API_KEY or ANTHROPIC_API_KEY to use an LLM |
LIVE_SLACK=true |
Use live Slack provider (needs SLACK_BOT_TOKEN) |
LIVE_RINGCENTRAL=true |
Live RingCentral conversations |
LIVE_TWILIO=true |
Live Twilio messages |
LIVE_THERAPIST_CHAT=true |
Live Lavni chat API |
LIVE_CALENDAR=true |
Live calendar / Lavni scheduling API |
Orchestrator code talks to interfaces in backend/app/providers/ and NLP via backend/app/agent/nlp.py. Mock implementations read data/; live/LLM stubs activate when keys are set. Check GET /api/health → providers.nlp.
With the venv active, from the project root (or use npm run cli -- …):
.venv/bin/python -m backend.app.cli health
.venv/bin/python -m backend.app.cli reset
.venv/bin/python -m backend.app.cli ask "…"
.venv/bin/python -m backend.app.cli slack
.venv/bin/python -m backend.app.cli appointments
.venv/bin/python -m backend.app.cli approve slack_1 --alt 0
.venv/bin/python -m backend.app.cli reject slack_1 --note "Need follow-up"
.venv/bin/python -m backend.app.cli demo # all cases, reset between each
.venv/bin/python -m backend.app.cli demo --pause
.venv/bin/python -m backend.app.cli demo --only 2Always start with health + reset. Use slack after conflict/cancel to see the approval id (slack_1, slack_2, …).
# 0) Status + clean state
.venv/bin/python -m backend.app.cli health
.venv/bin/python -m backend.app.cli reset
# 1) Clear schedule → expect schedule + Slack notification
.venv/bin/python -m backend.app.cli ask "Schedule John Smith with Carol next Tuesday at 2 PM."
.venv/bin/python -m backend.app.cli slack
.venv/bin/python -m backend.app.cli appointments
# 2) Calendar conflict → expect therapist_conflict + pending approval
.venv/bin/python -m backend.app.cli reset
.venv/bin/python -m backend.app.cli ask "Schedule Jane Doe with Carol on July 28, 2026 at 2 PM."
.venv/bin/python -m backend.app.cli slack
# 2b) Approve first alternative (0 = 9:00 AM) — do NOT use plain approve (2 PM still conflicted)
.venv/bin/python -m backend.app.cli approve slack_1 --alt 0
# 3) Ambiguous → escalate; then reject
.venv/bin/python -m backend.app.cli reset
.venv/bin/python -m backend.app.cli ask "Schedule Chris with someone tomorrow morning."
.venv/bin/python -m backend.app.cli slack
.venv/bin/python -m backend.app.cli reject slack_1 --note "Reject - client needs follow-up first"
# 4) Duplicate prevention
.venv/bin/python -m backend.app.cli reset
.venv/bin/python -m backend.app.cli ask "Jane Doe confirmed her appointment with Carol Tuesday at 10 — please schedule it."
# 5) Cancel → approval (approve or reject)
.venv/bin/python -m backend.app.cli reset
.venv/bin/python -m backend.app.cli ask "Cancel Maria Garcia appointment with David."
.venv/bin/python -m backend.app.cli slack
# Approve cancel:
.venv/bin/python -m backend.app.cli approve slack_1
# Or reject cancel:
# .venv/bin/python -m backend.app.cli reject slack_1 --note "Need follow-up first"
# 6) RingCentral scan → findings only, no auto-book
.venv/bin/python -m backend.app.cli reset
.venv/bin/python -m backend.app.cli ask "Go through my RingCentral conversations from July 20 and find anyone who needs an appointment."
# 6b) Twilio + therapist chat scan
.venv/bin/python -m backend.app.cli reset
.venv/bin/python -m backend.app.cli ask "Review today's Twilio messages and therapist-client chats and tell me who may need scheduling help."
# 7) Reserved blocked slot → calendar_blocked
.venv/bin/python -m backend.app.cli reset
.venv/bin/python -m backend.app.cli ask "Schedule John Smith with Carol on July 28, 2026 at 4 PM."
.venv/bin/python -m backend.app.cli slackYou can also hit the HTTP API with curl while npm run dev is running (see OpenAPI at /docs).
# 1) Python deps (venv)
python3 -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
# 2) Frontend deps
npm install
# 3) Run API + UI together
npm run dev- UI: http://127.0.0.1:5173
- API: http://127.0.0.1:8000
- Health: http://127.0.0.1:8000/api/health
- OpenAPI docs: http://127.0.0.1:8000/docs
Or run separately:
npm run server # FastAPI on :8000
npm run client # Vite on :5173- Accepts admin prompts in the left/center chat
- Classifies the request (direct schedule / scan / ambiguous)
- Looks up clients & therapists from mock records
- Checks working hours, blocked slots, conflicts, duplicates
- Auto-schedules only when safe; otherwise posts a Slack approval card
- On successful schedule, posts a Slack notification to
#lavni-admin-scheduling
By default, relative dates use a fixed demo “now” aligned with seed mock data:
Tuesday, July 28, 2026, 9:00 AM ET
So “next Tuesday at 2 PM” resolves to August 4, 2026 at 2:00 PM ET.
Optional: set USE_REAL_CLOCK=true in .env to use the current America/New_York time instead. If you do that, update mock appointment / conversation dates and demo prompts so they still line up. The UI pill shows Demo clock: … or Now: … from GET /api/health.
| Slot | Why |
|---|---|
| Carol 10:00 AM | Jane Doe existing appointment (duplicate demo) |
| Carol 2:00 PM | Alex Rivera booked (conflict demo) |
| Carol 4:00–5:00 PM | Reserved blocked slot — cannot auto-book |
| Carol all day July 29 | PTO block |
| David 10:00 AM July 30 | Maria Garcia (cancel/reschedule demo) |
Use the buttons in the UI, or send these via chat:
| # | Prompt | Expected |
|---|---|---|
| 1 | Schedule John Smith with Carol next Tuesday at 2 PM. |
Schedules + Slack notification |
| 2 | Schedule Jane Doe with Carol on July 28, 2026 at 2 PM. |
Conflict → Slack approval + alternatives |
| 3 | Schedule Chris with someone tomorrow morning. |
Ambiguous → escalate |
| 4 | Jane Doe confirmed her appointment with Carol Tuesday at 10 — please schedule it. |
Duplicate prevented |
| 5 | Cancel Maria Garcia appointment with David. |
Cancel requires Slack approval |
| 6 | Go through my RingCentral conversations from July 20 and find anyone who needs an appointment. |
Findings list; no auto-schedule |
| 7 | Schedule John Smith with Carol on July 28, 2026 at 4 PM. |
Reserved block → no auto-book |
Also: Review today's Twilio messages and therapist-client chats and tell me who may need scheduling help.
Use Reset demo between walkthroughs.
May schedule directly only when:
- Admin explicitly asks to schedule/book
- Client and therapist are uniquely identified
- Date/time are clear
- Therapist is available (working hours, not blocked)
- No therapist/client conflict
- No duplicate appointment
Requires admin approval when:
- Ambiguous client/therapist (e.g. multiple “Chris”)
- Missing/unclear date or time
- Calendar conflict or blocked slot
- Client not assigned to requested therapist
- Reschedule or cancellation
- Findings from conversation scans
- Low-confidence / unclear intent
No real Slack workspace is connected. The right panel is #lavni-admin-scheduling.
- Notifications after every successful schedule (direct or after approval)
- Approval cards with Approve requested / Approve alt N / Reject / Schedule custom time
Approvals call POST /api/slack/approvals/{id}.
- It only responds to admin chat in this internal UI
- Mock RingCentral / Twilio / therapist chat are read-only inputs
- There is no outbound client messaging channel in the prototype
- Session length is 1 hour
- Timezone display/storage uses ET (
-04:00) for July demo data - Therapist
workingDaysuse JS-style weekday numbers (0=Sun … 6=Sat) - Intent detection uses the mock AI layer by default (
USE_LLM=false). SetUSE_LLM=trueand addOPENAI_API_KEYorANTHROPIC_API_KEYto switch NLP to an LLM (policy/calendar stay local). - Seed appointments live in
data/appointments.json; runtime writes go todata/runtime/state.json
| Method | Path | Purpose |
|---|---|---|
| POST | /api/agent/message |
Admin prompt → agent result |
| GET | /api/slack/messages |
Mock Slack feed |
| POST | /api/slack/approvals/{id} |
Approve / reject / custom time |
| GET | /api/appointments |
Current appointments |
| POST | /api/reset |
Reset runtime state |
| GET | /api/health |
Health check |
backend/app/ FastAPI app, agent, services, routers
client/ React admin UI
data/ Seed mock data + runtime state
ARCHITECTURE.md Design notes
- Swap deterministic NLP for an LLM with structured output + confidence scores
- Real RingCentral / Twilio / Lavni chat / Google or Outlook calendar / Slack APIs
- AuthN/AuthZ, audit export, PHI encryption at rest
- Multi-timezone scheduling and “same time as last week”
- Stronger duplicate detection across nearby times / telehealth links
- Automated test suite for all policy branches
See ARCHITECTURE.md for data flow, safety, HIPAA notes, and production integration path.