diff --git a/backend/.env.docker b/backend/.env.docker new file mode 100644 index 0000000000..4bab1bf423 --- /dev/null +++ b/backend/.env.docker @@ -0,0 +1,103 @@ +# ================================================================ +# omi Backend - Local Docker Development Environment +# ================================================================ +# Instructions: +# 1. Fill in [REQUIRED] values below +# 2. Run: bash scripts/install.sh +# ================================================================ + +# ---- Firebase / Google Cloud -------------------------------- +# [REQUIRED] Paste your Firebase service account JSON as a single line: +# Get it: Firebase Console -> Project Settings -> Service Accounts -> Generate new private key +SERVICE_ACCOUNT_JSON= +GOOGLE_APPLICATION_CREDENTIALS=google-credentials.json +FIREBASE_API_KEY= +FIREBASE_AUTH_DOMAIN= +FIREBASE_PROJECT_ID= + +# ---- Redis [pre-configured for local Docker] ---------------- +REDIS_DB_HOST=redis +REDIS_DB_PORT=6379 +REDIS_DB_PASSWORD= + +# ---- Typesense [pre-configured for local Docker] ------------ +TYPESENSE_HOST=typesense +TYPESENSE_HOST_PORT=8108 +TYPESENSE_API_KEY=local_typesense_key + +# ---- GCP Storage -------------------------------------------- +# [REQUIRED] for audio processing and file storage +BUCKET_SPEECH_PROFILES= +BUCKET_BACKUPS= +BUCKET_PLUGINS_LOGOS= + +# ---- Pinecone [REQUIRED for vector memory search] ----------- +PINECONE_API_KEY= +PINECONE_INDEX_NAME= + +# ---- Deepgram [REQUIRED for audio transcription] ------------ +DEEPGRAM_API_KEY= + +# ---- OpenAI [REQUIRED for AI features] --------------------- +OPENAI_API_KEY= + +# ---- Security ----------------------------------------------- +# [REQUIRED] Generate with: openssl rand -hex 32 +ADMIN_KEY= +ENCRYPTION_SECRET=omi_ZwB2ZNqB2HHpMK6wStk7sTpavJiPTFg7gXUHnc4tFABPU6pZ2c2DKgehtfgi4RZv + +# ---- Base URL ----------------------------------------------- +BASE_API_URL=http://localhost:8080 +API_BASE_URL=http://localhost:8080 + +# ---- Pusher (required for real-time conversation updates) --- +# Without this, conversations stay in_progress permanently +HOSTED_PUSHER_API_URL= + +# ---- HuggingFace -------------------------------------------- +HUGGINGFACE_TOKEN= + +# ---- Optional Services -------------------------------------- +GITHUB_TOKEN= +WORKFLOW_API_KEY= +HUME_API_KEY= +HUME_CALLBACK_URL= +STRIPE_API_KEY= +STRIPE_WEBHOOK_SECRET= +STRIPE_CONNECT_WEBHOOK_SECRET= +RAPID_API_HOST= +RAPID_API_KEY= +PERPLEXITY_API_KEY= + +# ---- Google OAuth ------------------------------------------- +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= + +# ---- Apple OAuth -------------------------------------------- +APPLE_CLIENT_ID= +APPLE_TEAM_ID= +APPLE_KEY_ID= +APPLE_PRIVATE_KEY= + +# ---- Third-party OAuth Integrations ------------------------- +TWITTER_CLIENT_ID= +TWITTER_CLIENT_SECRET= +NOTION_CLIENT_ID= +NOTION_CLIENT_SECRET= +WHOOP_CLIENT_ID= +WHOOP_CLIENT_SECRET= + +# ---- Twilio (Phone Calls) ----------------------------------- +TWILIO_ACCOUNT_SID= +TWILIO_AUTH_TOKEN= +TWILIO_API_KEY_SID= +TWILIO_API_KEY_SECRET= +TWILIO_TWIML_APP_SID= + +# ---- LangSmith (optional - LLM observability) --------------- +LANGSMITH_TRACING=false +LANGSMITH_API_KEY= +LANGSMITH_PROJECT=omi-backend-chat +LANGSMITH_ENDPOINT=https://api.smith.langchain.com +OMI_LANGSMITH_AGENTIC_PROMPT_NAME=omi-agentic-system +OMI_LANGSMITH_PROMPT_CACHE_TTL_SECONDS=300 diff --git a/backend/docker-compose.yaml b/backend/docker-compose.yaml new file mode 100644 index 0000000000..9ac9535cfe --- /dev/null +++ b/backend/docker-compose.yaml @@ -0,0 +1,55 @@ +version: '3.8' + +services: + backend: + build: + context: .. + dockerfile: backend/Dockerfile + ports: + - "8080:8080" + env_file: + - .env.docker + depends_on: + redis: + condition: service_healthy + typesense: + condition: service_healthy + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8080/v1/health"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 60s + restart: on-failure + + redis: + image: redis:7-alpine + ports: + - "6379:6379" + volumes: + - redis_data:/data + command: redis-server --appendonly yes + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 5s + retries: 5 + + typesense: + image: typesense/typesense:0.25.1 + ports: + - "8108:8108" + environment: + - TYPESENSE_API_KEY=local_typesense_key + - TYPESENSE_DATA_DIR=/data + volumes: + - typesense_data:/data + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8108/health"] + interval: 5s + timeout: 5s + retries: 5 + +volumes: + redis_data: + typesense_data: diff --git a/backend/scripts/README.md b/backend/scripts/README.md new file mode 100644 index 0000000000..76c8968b31 --- /dev/null +++ b/backend/scripts/README.md @@ -0,0 +1,102 @@ +# Omi Backend — One-Click Deployment + +Self-host the omi backend with a single command using Docker Compose. + +## Prerequisites + +- [Docker](https://docs.docker.com/get-docker/) 20.10+ +- Docker Compose v2 (plugin) or v1 (standalone) +- API credentials listed below + +## Required API Keys + +| Service | Purpose | Sign up | +|---------|---------|---------| +| Firebase / GCP | Auth, Firestore DB, Storage | [console.firebase.google.com](https://console.firebase.google.com) | +| OpenAI | AI chat and processing | [platform.openai.com](https://platform.openai.com) | +| Deepgram | Audio transcription | [console.deepgram.com](https://console.deepgram.com) | +| Pinecone | Vector memory search | [app.pinecone.io](https://app.pinecone.io) | + +**Redis** and **Typesense** run locally inside Docker — no external accounts needed. + +## Quick Start + +```bash +# 1. Clone the repository +git clone https://github.com/BasedHardware/omi.git +cd omi/backend + +# 2. Configure your API keys +cp .env.docker .env.docker +# Edit .env.docker — fill in SERVICE_ACCOUNT_JSON, OPENAI_API_KEY, +# DEEPGRAM_API_KEY, PINECONE_API_KEY, and GCP bucket names + +# 3. Deploy +bash scripts/install.sh +``` + +## Manual Start + +```bash +cd omi/backend +docker compose -f docker-compose.yaml up -d --build + +# Verify +curl http://localhost:8080/v1/health +# Expected: {"status":"ok"} +``` + +## Services + +| Service | Port | Notes | +|---------|------|-------| +| Backend API | 8080 | FastAPI application | +| Redis | 6379 | Persistent session cache | +| Typesense | 8108 | Full-text search index | + +## Architecture + +``` +docker compose up (run from omi/backend/) + │ + ├── backend:8080 ← FastAPI + │ ├── build context = repo root (../), dockerfile = backend/Dockerfile + │ ├── env loaded from .env.docker + │ ├── REDIS_DB_HOST=redis, REDIS_DB_PORT=6379 + │ └── TYPESENSE_HOST=typesense, TYPESENSE_HOST_PORT=8108 + ├── redis:6379 ← Persistent (appendonly yes) + └── typesense:8108 ← Search (local_typesense_key) +``` + +## Common Commands + +```bash +# View backend logs +docker compose -f docker-compose.yaml logs -f backend + +# Stop all services +docker compose -f docker-compose.yaml down + +# Full reset (removes volumes) +docker compose -f docker-compose.yaml down -v + +# Rebuild after code changes +docker compose -f docker-compose.yaml up -d --build +``` + +## Troubleshooting + +**Health check:** The correct endpoint is `/v1/health` (not `/health`): +```bash +curl http://localhost:8080/v1/health +# → {"status":"ok"} +``` + +**Build fails:** Run `docker compose` from inside `omi/backend/` — the build context +is set to `..` (repo root) which is required by `backend/Dockerfile`. + +**Backend exits on startup:** Check logs with `docker compose -f docker-compose.yaml logs backend`. +Usually a missing env var. Confirm `SERVICE_ACCOUNT_JSON` and `OPENAI_API_KEY` are set in `.env.docker`. + +**Redis not connecting:** Ensure `.env.docker` has `REDIS_DB_HOST=redis` and `REDIS_DB_PORT=6379` +(not `REDIS_URL` — that variable is not read by the omi backend). diff --git a/backend/scripts/install.sh b/backend/scripts/install.sh new file mode 100755 index 0000000000..8532eacfc4 --- /dev/null +++ b/backend/scripts/install.sh @@ -0,0 +1,93 @@ +#!/bin/bash +set -e + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m' +info() { echo -e "${GREEN}[INFO]${NC} $1"; } +warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } +error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } + +echo "==================================" +echo " Omi Backend One-Click Deployment" +echo "==================================" + +# Detect docker compose (v2 plugin first, fall back to v1 standalone) +if docker compose version &>/dev/null 2>&1; then + DOCKER_COMPOSE="docker compose" +elif docker-compose version &>/dev/null 2>&1; then + DOCKER_COMPOSE="docker-compose" +else + error "Docker Compose not found. Install Docker Desktop: https://docs.docker.com/get-docker/" +fi +info "Docker Compose: $DOCKER_COMPOSE" + +# Check Docker is running +docker info &>/dev/null 2>&1 || error "Docker daemon not running. Please start Docker." + +# Navigate to backend directory (script lives in backend/scripts/) +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +BACKEND_DIR="$(dirname "$SCRIPT_DIR")" +cd "$BACKEND_DIR" +info "Working directory: $BACKEND_DIR" + +# Create .env.docker if missing +if [ ! -f ".env.docker" ]; then + error ".env.docker not found in $BACKEND_DIR. See scripts/README.md for setup instructions." +fi + +# Generate ADMIN_KEY if not set +if grep -q "^ADMIN_KEY=$" .env.docker 2>/dev/null; then + if command -v openssl &>/dev/null; then + GENERATED_KEY=$(openssl rand -hex 32) + sed -i.bak "s/^ADMIN_KEY=$/ADMIN_KEY=$GENERATED_KEY/" .env.docker && rm -f .env.docker.bak + info "Generated secure ADMIN_KEY" + fi +fi + +# Validate critical env vars +set -a; source .env.docker; set +a + +missing=() +[ -z "${OPENAI_API_KEY:-}" ] && missing+=("OPENAI_API_KEY") +[ -z "${DEEPGRAM_API_KEY:-}" ] && missing+=("DEEPGRAM_API_KEY") +[ -z "${PINECONE_API_KEY:-}" ] && missing+=("PINECONE_API_KEY") +[ -z "${SERVICE_ACCOUNT_JSON:-}" ] && missing+=("SERVICE_ACCOUNT_JSON") + +if [ ${#missing[@]} -gt 0 ]; then + warn "The following required keys are not set in .env.docker:" + for key in "${missing[@]}"; do warn " - $key"; done + echo "" + read -p "Continue anyway? Some features will not work. [y/N]: " confirm + [[ "$confirm" =~ ^[Yy]$ ]] || exit 1 +fi + +# Build and start services +info "Building and starting services (this may take a few minutes on first run)..." +$DOCKER_COMPOSE -f docker-compose.yaml up -d --build + +# Wait for backend health +info "Waiting for backend to be ready..." +max_attempts=40 +attempt=0 +until curl -sf http://localhost:8080/v1/health &>/dev/null; do + attempt=$((attempt + 1)) + if [ $attempt -ge $max_attempts ]; then + warn "Health check timed out after $((max_attempts * 3))s. Showing logs:" + $DOCKER_COMPOSE -f docker-compose.yaml logs backend --tail=30 + exit 1 + fi + printf "." + sleep 3 +done +echo "" + +info "=========================================" +info " Omi backend is running!" +info "=========================================" +info " API: http://localhost:8080" +info " Health: http://localhost:8080/v1/health" +info " Typesense: http://localhost:8108" +info " Redis: localhost:6379" +info "" +info " Logs: $DOCKER_COMPOSE -f docker-compose.yaml logs -f" +info " Stop: $DOCKER_COMPOSE -f docker-compose.yaml down" +info "========================================="