Skip to content

About

A production-ready AI voice agent for hotel services built with Django, OpenAI, Kafka, and PostgreSQL. It features real-time voice interactions, asynchronous task processing, WebSocket support, JWT authentication, Redis caching, comprehensive observability with Prometheus, Grafana, and Sentry, and is fully containerized using Docker.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

10 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🏨 Hotel Voice Agent

A production-ready AI Voice Agent for hotel services, built with Django, OpenAI, Kafka, and a full observability stack.


Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          NGINX (Load Balancer)                       β”‚
β”‚         Rate Limiting Β· TLS Termination Β· WebSocket Proxy            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”‚ HTTP                    β”‚ WebSocket (ws://)
         β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚  Gunicorn    β”‚         β”‚   Daphne (ASGI)  β”‚
         β”‚  (2 workers) β”‚         β”‚  (2 workers)     β”‚
         β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”‚                         β”‚
         β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
         β”‚              Django Application              β”‚
         β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
         β”‚  β”‚ REST API v1  β”‚   β”‚ Channels (WS)    β”‚    β”‚
         β”‚  β”‚ JWT Auth     β”‚   β”‚ Voice Sessions   β”‚    β”‚
         β”‚  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
         β”‚         β”‚                    β”‚              β”‚
         β”‚  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚
         β”‚  β”‚          Voice Agent Service          β”‚  β”‚
         β”‚  β”‚  Whisper STT β†’ GPT-4o β†’ TTS-1-HD    β”‚  β”‚
         β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚
         β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”‚            β”‚               β”‚
         β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”
         β”‚PostgreSQLβ”‚  β”‚  Redis  β”‚    β”‚  Kafka  β”‚
         β”‚(Primary/ β”‚  β”‚(Cache/  β”‚    β”‚(3 topicsβ”‚
         β”‚ Replica) β”‚  β”‚Sessions)β”‚    β”‚ + DLQ)  β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”˜
                                            β”‚
                                    β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
                                    β”‚Kafka Consumerβ”‚
                                    β”‚(2 workers)   β”‚
                                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
         β”‚              Observability                  β”‚
         β”‚  Prometheus β†’ Grafana Dashboards + Alerts   β”‚
         β”‚  Sentry Β· Structured JSON Logging           β”‚
         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Tech Stack

Layer Technology
Framework Django 5, Django REST Framework
AI / Voice OpenAI GPT-4o, Whisper STT, TTS-1-HD
Real-time Django Channels, Daphne, WebSocket
Async Tasks Celery + Redis
Event Bus Apache Kafka (confluent-kafka)
Database PostgreSQL 16 + Read Replica, optimized indexes
Cache Redis 7 (sessions, rate limiting, query cache)
Auth JWT (SimpleJWT) + Argon2 password hashing
Load Balancer Nginx (upstream round-robin + WebSocket sticky)
Rate Limiting Nginx zones + DRF throttles + custom middleware
Metrics Prometheus + Grafana (pre-built dashboards)
Error Tracking Sentry
Containers Docker + Docker Compose
Package Mgr uv (fast, lockfile-based)

Quick Start

Prerequisites

  • Docker + Docker Compose v2
  • uv (pip install uv or curl -LsSf https://astral.sh/uv/install.sh | sh)
  • OpenAI API key

1 β€” Clone & configure

git clone https://github.com/EmmanuelAdah/hotel_voice_agent.git
cd hotel_voice_agent
cp .env.example.example .env.example
# Edit .env.example β€” set DJANGO_SECRET_KEY, DB_PASSWORD, OPENAI_API_KEY at minimum

2 β€” Start with Docker Compose

docker compose up -d

The first start takes ~2 minutes. Run migrations and seed demo data:

docker compose exec web python manage.py migrate
docker compose exec web python manage.py seed_demo_data

3 β€” Access services

Service URL Credentials
API http://localhost/api/v1/ See below
Grafana http://localhost:3000 admin / $GRAFANA_PASSWORD
Prometheus http://localhost:9090 β€”
Django Admin http://localhost/admin/ admin@demo.com / Demo1234!

Demo credentials (after seed)

Guest:   guest@demo.com   / Demo1234!
Staff:   staff@demo.com   / Demo1234!
Manager: manager@demo.com / Demo1234!
Admin:   admin@demo.com   / Demo1234!

Local Development

# Install dependencies
uv sync

# Set env vars
export DJANGO_SETTINGS_MODULE=hotel_agent.settings.development
export DJANGO_SECRET_KEY=dev-secret
export DB_PASSWORD=postgres
export OPENAI_API_KEY=sk-...

# Run Django dev server
uv run manage.py runserver

# Run Celery worker
uv run celery -A hotel_agent worker --loglevel=debug

# Run Kafka consumer
uv run manage.py run_kafka_consumer

Run tests

uv run pytest                          # all tests
uv run pytest tests/unit/              # unit only
uv run pytest -k "voice" -v           # filter by name
uv run pytest --cov-report=html       # HTML coverage report

API Reference

Authentication

# Register
POST /api/v1/auth/register/
{"email": "guest@hotel.com", "password": "...", "confirm_password": "...", "first_name": "...", "last_name": "..."}

# Login β†’ JWT tokens
POST /api/v1/auth/token/
{"email": "...", "password": "..."}

# Refresh token
POST /api/v1/auth/token/refresh/
{"refresh": "<refresh_token>"}

# Logout (blacklist refresh token)
POST /api/v1/auth/token/blacklist/
{"refresh": "<refresh_token>"}

Voice Sessions

# Start session
POST /api/v1/voice/sessions/
Authorization: Bearer <token>
{"booking_id": "<uuid>"}

# Process a turn (text input)
POST /api/v1/voice/sessions/<id>/turn/
{"text": "I'd like room service please", "generate_audio": true}
# Returns: {"text": "...", "audio_base64": "...", "service_request_created": {...}?}

# Process a turn (audio upload)
POST /api/v1/voice/sessions/<id>/turn/
Content-Type: multipart/form-data
audio=<webm file>

# End session
POST /api/v1/voice/sessions/<id>/end/

# Get transcript
GET /api/v1/voice/sessions/<id>/transcript/

WebSocket (real-time voice)

ws://localhost/ws/voice/<session_id>/?token=<jwt_access_token>

# Send text turn:
{"type": "voice_input", "text": "I need extra towels", "generate_audio": true}

# Send audio turn:
{"type": "audio_input", "audio_b64": "<base64 webm>", "format": "webm"}

# Receive:
{"type": "response_text", "text": "..."}
{"type": "response_audio", "audio_b64": "...", "format": "mp3"}
{"type": "service_request_detected", "data": {"service_type": "housekeeping", ...}}
{"type": "notification", "message": "...", "data": {...}}

Service Requests

GET  /api/v1/service-requests/           # list
POST /api/v1/service-requests/           # create manually
POST /api/v1/service-requests/<id>/assign/   # staff self-assign
POST /api/v1/service-requests/<id>/complete/ # mark done

# Filters: ?status=pending&service_type=room_service&priority=high

Rooms & Bookings

GET /api/v1/rooms/                       # list available rooms
GET /api/v1/rooms/?room_type=suite       # filter by type

POST /api/v1/bookings/                   # create booking
GET  /api/v1/bookings/<id>/             # booking detail

Kafka Topics

Topic Events
hotel.voice.sessions session started/completed
hotel.service.requests request created/assigned/completed
hotel.notifications push notifications to guests/staff
hotel.analytics usage metrics
hotel.audit.log all audit events
*.dlq Dead letter queue per topic

Database Indexes

Key optimized indexes defined in scripts/init_db.sql:

  • Partial indexes β€” available rooms, open service requests, active sessions
  • Covering indexes β€” guest booking list, staff dashboard (index-only scans)
  • BRIN indexes β€” audit log timestamps (append-only table)
  • Trigram indexes β€” email and description full-text search
  • Composite indexes β€” all common filter combinations

Security Features

  • JWT with 30-min access + 7-day refresh tokens, rotation + blacklist
  • Argon2 password hashing
  • Account lockout β€” 5 failed attempts β†’ 30-min lock
  • Rate limiting β€” Nginx zones + DRF throttles + custom Redis middleware
  • HTTPS enforced β€” HSTS + strict TLS config
  • Security headers β€” CSP, X-Frame-Options, nosniff, XSS protection
  • Admin restricted β€” IP allowlist via Nginx
  • Metrics endpoint β€” Docker network only (not publicly exposed)

Production Deployment

./scripts/deploy.sh

The script:

  1. Validates required env vars
  2. Pulls infrastructure images
  3. Builds the Django image (multi-stage, non-root user)
  4. Starts Postgres, Redis, Kafka
  5. Runs database migrations
  6. Sets up Kafka topics
  7. Deploys all app services (2 replicas each)
  8. Runs a health check

Monitoring

Grafana Dashboard

Pre-provisioned dashboard at http://localhost:3000 showing:

  • Active voice sessions & sessions/min
  • Voice turn latency (p50/p95/p99)
  • OpenAI token usage
  • Service request breakdown by type
  • Kafka consumer lag
  • API error rates
  • Database query latency
  • Redis memory usage

Prometheus Alerts

Pre-configured alerts for:

  • High API error rate (>5%)
  • Slow voice turns (p95 >5s)
  • OpenAI API errors
  • Kafka consumer lag >1000
  • DB connections near limit
  • Redis memory >85%
  • Messages going to DLQ

About

A production-ready AI voice agent for hotel services built with Django, OpenAI, Kafka, and PostgreSQL. It features real-time voice interactions, asynchronous task processing, WebSocket support, JWT authentication, Redis caching, comprehensive observability with Prometheus, Grafana, and Sentry, and is fully containerized using Docker.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages