Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ A running list of projects, each self-contained in its own folder under `project

| Project | What it is | Built by |
|---|---|---|
| [meridian](projects/meridian) | A 5-assistant hotel + airline concierge squad built with only native Vapi primitives (code tools, transfers, evals), zero hosting required | [Justin Crowe](https://github.com/justincrowe-hub) |


## Browsing / running a project
Expand Down
29 changes: 29 additions & 0 deletions projects/meridian/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Copy to .env and fill in your own values. Never commit real keys.

# ── Required ─────────────────────────────────────────────────────────────────
# Vapi dashboard → Org Settings → API Keys (use a test/sandbox key)
VAPI_API_KEY=

# Supabase → Project Settings → API. The code tools carry these to Vapi's
# runtime so reservations/service requests persist with zero hosting.
SUPABASE_URL=
SUPABASE_SERVICE_ROLE_KEY=

# Supabase → Project Settings → Database → Connection string (URI).
# Only needed once, for `npm run migrate`.
SUPABASE_DB_URL=

# ── Optional ─────────────────────────────────────────────────────────────────
# File id printed by `npm run kb:upload` (uploads assets/hotel-knowledge-base.txt
# to your Vapi org). Unset = hotel assistant is created without the KB tool.
HOTEL_KB_FILE_ID=

# Callback number the outbound assistant offers if a member declines, written
# the way it should be read aloud. Unset = a generic "call us back" line.
CALLBACK_NUMBER=

# Only for the optional self-hosted webhook server (any string you invent).
VAPI_WEBHOOK_SECRET=

# Only for the optional PostHog call-analytics sink in the webhook server.
POSTHOG_API_KEY=
88 changes: 88 additions & 0 deletions projects/meridian/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Meridian: a 5-assistant Vapi concierge squad

> 🧪 **This is a showcase demo, not an officially supported Vapi product.** Built by Justin Crowe (GTM at Vapi) to explore how far a multi-assistant squad can go using only native Vapi primitives: code tools, query tools, transfers, evals, and structured-data analysis. No third-party integrations, no self-hosted webhook required. It is meant to inspire, not to be production ready.

## What it does

Meridian is a fictional membership travel brand (think closed-loop hotel + airline concierge). Call one number and a squad of five assistants handles the whole journey:

| Assistant | Voice | Role |
|---|---|---|
| Concierge "Aria" | asteria | Front door. Greets, identifies the member, routes to hotel or flight. |
| Hotel Concierge "Jack" | orion | Reservation lookup, service requests, hotel knowledge base. |
| Flight Triage "Maya" | luna | Flight status. Escalates cancellations and 2h+ delays to Rebooking. |
| Rebooking "Marcus" | arcas | Finds alternative flights, confirms the rebook. |
| Upsell & Recovery "Sophie" | stella | Room upgrades (priced, pending on folio) and travel-credit requests. |

A sixth, standalone assistant ("Outbound Disruption") proactively calls members when their flight is cancelled or badly delayed, rebooks them in one call, and files a travel-credit request.

## How it works

- **Every business tool is a Vapi code tool** that calls the Supabase REST API directly, so the tools run on Vapi's infrastructure. There is no webhook, tunnel, or server to host, yet data persists: a new caller gets a randomly invented reservation that is written to Supabase and returned consistently on every later call.
- **Routing lives in the squad**, not in prompts: members declare `transferCall` tools with `assistantName` destinations, and the squad wires `assistantDestinations` with silent, rolling-history transfers so context carries across handoffs.
- **Distinct persona voices** per member (Deepgram), with `membersOverrides` used only for uniform settings (transcriber, barge-in). Pinning a voice in `membersOverrides` would silently collapse every member to one voice.
- **Barge-in is tuned for PSTN**: a shared `stopSpeakingPlan` (`numWords: 10`, the API maximum, plus `acknowledgementPhrases` / `interruptionPhrases`) so background noise and backchannels never interrupt, but "stop" or "wait" always does.
- **TTS hygiene everywhere**: every string a tool returns for speech is spoken words only. Flight numbers, times, dates, and reference codes are spelled out so the voice never reads raw codes or ISO timestamps.
- **Evals**: 15 `chat.mockConversation` simulations cover routing, tool behavior, and guardrails (for example: Sophie must treat an upgrade ask as an upgrade, never lead with credits, and never quote a credit dollar amount).
- An optional Express webhook server (`src/server.ts`) adds end-of-call analytics (call logs to Supabase, events to PostHog). The voice demo works fully without it.

## Setup

### Prerequisites

- Node 20+
- A Vapi account and API key (use a test key)
- A free [Supabase](https://supabase.com) project (gives the code tools a hosted place to persist reservations)

### Steps

1. `cp .env.example .env` and fill in `VAPI_API_KEY`, `SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY`, and `SUPABASE_DB_URL`
2. `npm install`
3. `npm run migrate`: applies `src/db/schema.sql` (5 tables) to your Supabase project
4. Optional: `npm run kb:upload`: uploads `assets/hotel-knowledge-base.txt` to your Vapi org; put the printed id in `.env` as `HOTEL_KB_FILE_ID` so Jack gets the knowledge-base tool
5. Create the five assistants (order does not matter; names are the routing contract, so leave them as-is):
```
npm run assistant:concierge
npm run assistant:hotel
npm run assistant:triage
npm run assistant:rebooking
npm run assistant:upsell
```
6. `npm run squad`: wires them into the Meridian Concierge Squad
7. In the Vapi dashboard, point a phone number at the squad, then call it

Re-running an assistant script creates a new assistant. To update one in place, pass its id: `ASSISTANT_ID=<id> npm run assistant:hotel`. Keep assistant names unique in your org or the squad script will refuse to wire (by design).

### Outbound disruption campaign (optional)

```
npm run assistant:outbound
npm run seed # adds a demo member
TEST_PHONE=+1XXXXXXXXXX FLIGHT_NUMBER=UA482 npm run campaign
```

`TEST_PHONE` overrides the member's phone so the call goes to you. You also need a Vapi phone number to place the call from.

## Verify and test

| Command | What it checks | Needs |
|---|---|---|
| `npm run typecheck` | TypeScript across the project | nothing |
| `npm run check` | env vars + Supabase/Vapi connectivity | .env |
| `npm run test:synth` | executes the exact shipped lookup code against your Supabase | .env |
| `npm run test:flight` | flight status/rebook/compensation code tools | .env |
| `npm run test:audit` | all 8 code-tool bodies across a 40+ case matrix, plus a TTS-hygiene scan of every spoken message | .env |
| `npm run test:verify` | live squad config: membership, distinct voices, barge-in, transferCall wiring (read-only) | assistants + squad created |
| `npm run simulations` | upserts the 15 evals (`RUN=1` to also execute them) | assistants created |

## Known limitations

- Flight status is **synthesized** (weighted toward disruptions so the demo is interesting). No real flight-data provider is wired in.
- Free-tier Supabase projects pause after about a week of inactivity; unpause in the Supabase dashboard if lookups start failing with DNS errors.
- Eval runs (`RUN=1 npm run simulations`) have been observed to sit in `queued` on some orgs; the audit suite (`npm run test:audit`) covers the same tool behavior offline.
- Voices are Deepgram-specific; swapping providers means re-tuning the barge-in plan.
- Built and tested on macOS with Node 20/24.

## Built by

Justin Crowe ([justincrowe-hub](https://github.com/justincrowe-hub)), GTM at Vapi.
14 changes: 14 additions & 0 deletions projects/meridian/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Vercel serverless entry point — wraps the Meridian Express app.
* All routes (/webhook, /health) are handled by the Express router.
* Env vars come from Vercel's environment (not .env files).
*/
import express from "express";
import { webhookRouter } from "../src/routes/webhook.js";

const app = express();
app.use(express.json({ limit: "5mb" }));
app.use(webhookRouter);
app.get("/health", (_req, res) => res.json({ ok: true, service: "meridian" }));

export default app;
191 changes: 191 additions & 0 deletions projects/meridian/assets/hotel-knowledge-base.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
MERIDIAN GRAND HOTEL — KNOWLEDGE BASE
Last updated: 2025

==================================================
PROPERTY OVERVIEW
==================================================
Property name: Meridian Grand Hotel
Address: 1200 Harbor Boulevard, Miami, FL 33101
Main phone: +1 (305) 555-0100
Check-in time: 4:00 PM
Late check-out: Available until 2:00 PM, subject to availability, $50 fee. Complimentary for Gold and Platinum members.
Check-out time: 11:00 AM
Early check-in: Available from 12:00 PM, subject to availability, $75 fee. Complimentary for Platinum members.

==================================================
ROOM TYPES
==================================================
Standard King — 1 king bed, city view, 380 sq ft
Deluxe King — 1 king bed, ocean view, 420 sq ft
Standard Double — 2 queen beds, city view, 400 sq ft
Deluxe Double — 2 queen beds, ocean view, 440 sq ft
Junior Suite — 1 king bed, separate living area, ocean view, 650 sq ft
Executive Suite — 1 king bed, full living room, ocean view, 950 sq ft
Presidential Suite — 2 bedrooms, full kitchen, wraparound terrace, 1800 sq ft

All rooms include: free WiFi, 65-inch smart TV, Nespresso machine, minibar, in-room safe, blackout curtains.

==================================================
AMENITIES & HOURS
==================================================

POOL
Rooftop pool: Open daily 7:00 AM – 10:00 PM
Heated lap pool: Open daily 6:00 AM – 9:00 PM
Pool towels available at the pool deck, no charge.
Cabana rentals: $150/day, reservations recommended, call front desk.

FITNESS CENTER
Location: Level 3
Hours: Open 24 hours, 7 days a week
Equipment: Peloton bikes, treadmills, free weights, cable machines, stretching area
Personal training: Available by appointment, $120/session, call front desk to book.

SPA — MERIDIAN WELLNESS
Location: Level 2
Hours: Monday–Friday 9:00 AM – 8:00 PM, Saturday–Sunday 8:00 AM – 9:00 PM
Services: Swedish massage, deep tissue massage, hot stone massage, facials, couples treatments
50-minute massage: $180
80-minute massage: $240
Facial (60 min): $160
Couples suite: $420 for 80-minute session
Booking: Guests should call the front desk or visit Level 2 directly. 24-hour cancellation policy.

RESTAURANTS & BARS

Harbor Kitchen (all-day dining)
Location: Level 1, lobby level
Hours: Breakfast 6:30 AM – 11:00 AM, Lunch 12:00 PM – 3:00 PM, Dinner 6:00 PM – 10:30 PM
Cuisine: Contemporary American
Dress code: Smart casual
Reservations: Recommended for dinner, walk-ins welcome for breakfast and lunch

Azul Rooftop Bar
Location: Level 22
Hours: Sunday–Thursday 4:00 PM – 12:00 AM, Friday–Saturday 4:00 PM – 2:00 AM
Cuisine: Small plates, craft cocktails
Dress code: Smart casual, no flip flops or beachwear after 6:00 PM
Reservations: Not required but recommended on weekends

The Lobby Lounge
Location: Level 1
Hours: Daily 11:00 AM – 1:00 AM
Cuisine: Light bites, coffee, cocktails, afternoon tea (2:00 PM – 5:00 PM daily)
No reservations needed

Room service: Available 24 hours. 20-minute delivery guarantee or the order is complimentary.

BUSINESS CENTER
Location: Level 2
Hours: Open 24 hours
Services: Printing, scanning, fax, private meeting pods (bookable at front desk, $50/hour)

==================================================
SERVICES
==================================================

VALET & PARKING
Valet parking: $55/night, available 24 hours
Self-parking garage: $35/night, accessed via Harbor Street entrance
Electric vehicle charging: 4 stalls available in self-parking, first-come first-served, no extra charge

CONCIERGE SERVICES
Hours: Daily 7:00 AM – 11:00 PM
Services: Restaurant reservations, show tickets, transportation, tours, dry cleaning coordination

DRY CLEANING & LAUNDRY
Same-day service if items received before 9:00 AM
Next-day service for items received after 9:00 AM
Pickup: Call housekeeping or leave bag on door handle with request card

HOUSEKEEPING
Standard service: Daily between 9:00 AM – 4:00 PM
Turn-down service: Available on request, 6:00 PM – 9:00 PM
Extra towels, pillows, toiletries: Call housekeeping, delivered within 20 minutes
Do not disturb: Hang card on door or press DND button on room phone

TRANSPORTATION
Airport shuttle: $35 per person, runs every 90 minutes 5:00 AM – 11:00 PM, book at front desk
Taxi/rideshare: Doorman can assist, pickup zone is Harbor Boulevard entrance
Car rental desk: Located in lobby, open daily 8:00 AM – 6:00 PM

PET POLICY
Pets allowed: Dogs and cats only, max 2 pets per room
Pet fee: $75 per stay (non-refundable)
Max weight: 50 lbs per pet
Pet-friendly rooms: Ground floor rooms only, must request at booking
Dog walking service: Available through concierge, $25 per 30-minute walk

==================================================
LOYALTY PROGRAM — MERIDIAN REWARDS
==================================================
Standard: Base earn rate, 10 points per $1 spent
Silver: 25+ nights/year, 12 points per $1, free room upgrade when available
Gold: 50+ nights/year, 15 points per $1, complimentary late checkout, free breakfast daily
Platinum: 75+ nights/year, 20 points per $1, complimentary early check-in and late checkout, suite upgrades when available, dedicated phone line

Points redemption: 1,000 points = $10 credit toward room rate or hotel services
Points never expire for members with activity in past 24 months

==================================================
POLICIES
==================================================

CANCELLATION
Standard rate: Free cancellation up to 48 hours before check-in
Non-refundable rate: No refund after booking
Group bookings (5+ rooms): 7-day cancellation policy

SMOKING
Property is entirely non-smoking including all rooms, balconies, and rooftop areas
Smoking permitted in designated outdoor area only — located at Harbor Street side entrance
Violation fee: $350 deep cleaning charge

NOISE & QUIET HOURS
Quiet hours: 10:00 PM – 8:00 AM
Noise complaints: Call front desk, security will respond within 10 minutes

VISITORS
Guests may have visitors in common areas at any time
Visitors in guest rooms: Permitted until 11:00 PM, must be registered at front desk
Overnight visitors: $50/night, must be added to reservation

ACCESSIBILITY
Accessible rooms available on every floor, roll-in showers available on request
Hearing loop available at front desk
Service animals welcome at no charge
Accessible parking: 6 spaces on Level 1 of self-parking garage

==================================================
FREQUENTLY ASKED QUESTIONS
==================================================

Q: Can I get a room with a guaranteed ocean view?
A: Deluxe King, Deluxe Double, Junior Suite, Executive Suite, and Presidential Suite all have ocean views. Standard rooms have city views. We cannot guarantee a specific floor but can note your preference.

Q: Is breakfast included?
A: Breakfast is not included in standard rates. Gold and Platinum members receive complimentary breakfast at Harbor Kitchen for up to 2 guests per room. A breakfast package can be added at booking for $38 per person per day.

Q: What is the WiFi password?
A: WiFi is complimentary. Network name: MeridianGuest. Password is provided on your key card envelope at check-in.

Q: Can I store luggage before check-in or after check-out?
A: Yes, luggage storage is complimentary and available at the bell desk 24 hours a day.

Q: Is there a fee to use the pool or fitness center?
A: Both are complimentary for all registered guests.

Q: Do you have a gift shop?
A: Yes, located in the lobby, open daily 7:00 AM – 10:00 PM. Stocks sundries, snacks, beverages, and Meridian-branded items.

Q: Can I request a specific room or floor?
A: We accept room preferences and do our best to accommodate, but cannot guarantee specific rooms. Platinum members receive priority room assignments.

Q: What time is the pool bar open?
A: The rooftop Azul Bar overlooks the pool and opens at 4:00 PM daily. Non-alcoholic beverages and light snacks can be ordered poolside from 10:00 AM through a QR code menu.

Q: Is the hotel family-friendly?
A: Yes. We offer rollaway beds ($35/night), cribs (complimentary, request in advance), and a kids menu at Harbor Kitchen. Children under 12 stay free when sharing a room with parents.

Q: Do you have EV charging?
A: Yes, 4 EV charging stalls are available in the self-parking garage at no extra charge, first-come first-served.
Loading