Skip to main content

Overview

The Unmute backend is a FastAPI application that orchestrates real-time voice conversations by coordinating between the frontend, STT, LLM, and TTS services. Technology Stack:
  • Framework: FastAPI (async Python web framework)
  • WebSocket: Native FastAPI WebSocket support
  • Async: Python asyncio for concurrent operations
  • Serialization: Pydantic for message validation, MessagePack for STT/TTS
  • Monitoring: Prometheus metrics
  • Audio: sphn (Opus codec), NumPy (processing)

Application Structure

Main File: unmute/main_websocket.py

Request Lifecycle

HTTP Endpoints

WebSocket Endpoint

Concurrency Control

File: main_websocket.py:69
Why Limit Concurrency:
  • Python GIL limits true parallelism
  • Better to scale horizontally (more backend instances)
  • Prevents resource exhaustion

WebSocket Protocol

Subprotocol Negotiation

File: main_websocket.py:314
Purpose: OpenAI Realtime API compatibility. Client specifies supported protocols, server selects one.

Message Validation

File: main_websocket.py:79
Benefits:
  • Type safety
  • Automatic validation
  • Clear error messages

Two-Loop Architecture

File: main_websocket.py:380 The backend uses two concurrent loops:
  1. Receive Loop: Handle incoming messages from client
  2. Emit Loop: Send messages to client
TaskGroup Benefits:
  • All tasks cancelled if one fails
  • Automatic exception propagation
  • Clean shutdown

Receive Loop

File: main_websocket.py:406

Opus Decoding

File: main_websocket.py:461
Why asyncio.to_thread:
  • Opus decoding is CPU-bound
  • Run in thread pool to avoid blocking event loop
  • Other connections can process concurrently

Reconnection Handling

File: main_websocket.py:462
Problem: Browser sometimes sends old Opus packets on reconnect Solution: Wait for packet marked as “first” before processing

Emit Loop

File: main_websocket.py:512

Opus Encoding

File: main_websocket.py:550
Buffering: Opus encoder buffers internally, doesn’t always output on every input.

Error Handling

Exception Reporter

File: main_websocket.py:334

CORS Error Handling

File: main_websocket.py:594
Why Important: Without CORS headers on errors, browser shows confusing CORS error instead of actual error.

Health Checks

File: main_websocket.py:137
Features:
  • Parallel health checks (TaskGroup)
  • Cached for 0.5s (avoid hammering services)
  • Used before accepting WebSocket connections

CORS Configuration

File: main_websocket.py:84
Production: In production (Docker Swarm), Traefik handles CORS.

Configuration

File: unmute/kyutai_constants.py

Deployment

Docker Compose

File: docker-compose.yml:32

Dockerfile

File: Dockerfile

Running Locally

Monitoring

Prometheus Metrics

File: main_websocket.py:74
Metrics Available:
  • /metrics endpoint
  • HTTP request duration, status codes, etc.
  • Custom metrics from unmute/metrics.py

Grafana Dashboard

File: services/grafana/dashboards/unmute-monitoring-*.json Pre-configured dashboard for:
  • Active sessions
  • Latency percentiles (STT TTFT, TTS TTFT, VLLM TTFT)
  • Error rates
  • Throughput (words/sec)

Next Steps