services:
  open_webui:
    image: ghcr.io/open-webui/open-webui:main
    restart: always
    ports:
      - "3000:8080"
    environment:
      OPENAI_API_BASE_URL: http://vllm:8000/v1
      OLLAMA_BASE_URL: http://ollama:11434
    volumes:
      - open_webui:/app/backend/data

  # ── Model services (activate one at a time via --profile) ───────────
  #
  # Usage:
  #   docker compose up -d                              # Open WebUI + Langfuse
  #   docker compose --profile ollama-coder up -d       # + Ollama (recommended)
  #   docker compose --profile qwen3-coder up -d        # + vLLM (fast, no tool calling)
  #   docker compose --profile qwen3-8b up -d           # + vLLM general-purpose
  #
  # Swap models (Open WebUI stays up):
  #   docker compose --profile ollama-coder stop ollama-qwen3-coder
  #   docker compose --profile qwen3-coder up -d

  # ── Ollama (recommended for tool calling) ──────────────────────────

  ollama-qwen3-coder:
    image: ollama/ollama:latest
    container_name: ollama
    profiles: [ollama-coder]
    ports:
      - "11434:11434"
    volumes:
      - ${HOME}/.ollama:/root/.ollama
    environment:
      OLLAMA_KEEP_ALIVE: "24h"
      OLLAMA_CONTEXT_LENGTH: "65536"
      OLLAMA_KV_CACHE_TYPE: q8_0
      OLLAMA_FLASH_ATTENTION: "true"
    entrypoint: ["/bin/sh", "-c"]
    command:
      - |
        /bin/ollama serve &
        sleep 5
        ollama pull qwen3-coder:30b-a3b-q4_K_M
        wait
    restart: unless-stopped
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

  # ── LiteLLM proxy (OpenAI-compatible, forwards to Ollama) ──────────
  #
  # Clients connect to http://localhost:8000
  # Traces are sent to Langfuse automatically

  litellm:
    image: ghcr.io/berriai/litellm:main-stable
    container_name: litellm
    ports:
      - "8000:4000"
    volumes:
      - ./litellm-config.yaml:/app/config.yaml
    command: ["--config", "/app/config.yaml", "--port", "4000"]
    environment:
      DATABASE_URL: postgresql://litellm:litellm@litellm-postgres:5432/litellm
      LANGFUSE_PUBLIC_KEY: ${LANGFUSE_PUBLIC_KEY:-}
      LANGFUSE_SECRET_KEY: ${LANGFUSE_SECRET_KEY:-}
      LANGFUSE_HOST: http://langfuse-web:3000
      OPENROUTER_API_KEY: ${OPENROUTER_API_KEY:-}
      DILEMMA_DASH_OPENROUTER_API_KEY: ${DILEMMA_DASH_OPENROUTER_API_KEY:-}
      DILEMMA_DASH_LANGFUSE_PUBLIC_KEY: ${DILEMMA_DASH_LANGFUSE_PUBLIC_KEY:-}
      DILEMMA_DASH_LANGFUSE_SECRET_KEY: ${DILEMMA_DASH_LANGFUSE_SECRET_KEY:-}
      PROXY_MASTER_KEY: "admin"
      LITELLM_MASTER_KEY: "admin"
    extra_hosts:
      - "host.docker.internal:host-gateway"
    depends_on:
      langfuse-web:
        condition: service_healthy
      litellm-postgres:
        condition: service_healthy
    restart: unless-stopped

  # ── Langfuse (observability UI + API) ──────────────────────────────
  #
  # Web UI: http://localhost:3001

  langfuse-web:
    image: langfuse/langfuse:3
    container_name: langfuse-web
    ports:
      - "3001:3000"
    environment:
      DATABASE_URL: postgresql://langfuse:langfuse@langfuse-postgres:5432/langfuse
      NEXTAUTH_URL: http://localhost:3001
      NEXTAUTH_SECRET: mysecret
      SALT: mysalt
      ENCRYPTION_KEY: b4fad43673c52ad1ef6e275075aaeef2add9a3ab7a41adffce1fa866a10a0cd3
      LANGFUSE_INIT_ORG_ID: my-org
      LANGFUSE_INIT_ORG_NAME: "My Org"
      LANGFUSE_INIT_PROJECT_ID: my-project
      LANGFUSE_INIT_PROJECT_NAME: "My Project"
      LANGFUSE_INIT_PROJECT_PUBLIC_KEY: pk-lf-local
      LANGFUSE_INIT_PROJECT_SECRET_KEY: sk-lf-local
      LANGFUSE_INIT_USER_EMAIL: admin@local.dev
      LANGFUSE_INIT_USER_NAME: Admin
      LANGFUSE_INIT_USER_PASSWORD: password
      CLICKHOUSE_MIGRATION_URL: clickhouse://langfuse:langfuse@langfuse-clickhouse:9000
      CLICKHOUSE_URL: http://langfuse:langfuse@langfuse-clickhouse:8123
      CLICKHOUSE_USER: langfuse
      CLICKHOUSE_PASSWORD: langfuse
      CLICKHOUSE_CLUSTER_ENABLED: "false"
      REDIS_HOST: langfuse-redis
      REDIS_PORT: "6379"
      LANGFUSE_S3_EVENT_UPLOAD_ENABLED: "true"
      LANGFUSE_S3_EVENT_UPLOAD_BUCKET: langfuse
      LANGFUSE_S3_EVENT_UPLOAD_REGION: us-east-1
      LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID: minioadmin
      LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY: minioadmin
      LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT: http://langfuse-minio:9000
      LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE: "true"
    depends_on:
      langfuse-postgres:
        condition: service_healthy
      langfuse-clickhouse:
        condition: service_healthy
      langfuse-minio:
        condition: service_started
      langfuse-redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD-SHELL", "wget -q --spider http://$(hostname):3000/api/public/health"]
      interval: 15s
      timeout: 5s
      retries: 5
      start_period: 30s
    restart: unless-stopped

  langfuse-worker:
    image: langfuse/langfuse-worker:3
    container_name: langfuse-worker
    ports:
      - "127.0.0.1:3030:3030"
    environment:
      DATABASE_URL: postgresql://langfuse:langfuse@langfuse-postgres:5432/langfuse
      SALT: mysalt
      ENCRYPTION_KEY: b4fad43673c52ad1ef6e275075aaeef2add9a3ab7a41adffce1fa866a10a0cd3
      CLICKHOUSE_MIGRATION_URL: clickhouse://langfuse:langfuse@langfuse-clickhouse:9000
      CLICKHOUSE_URL: http://langfuse:langfuse@langfuse-clickhouse:8123
      CLICKHOUSE_USER: langfuse
      CLICKHOUSE_PASSWORD: langfuse
      CLICKHOUSE_CLUSTER_ENABLED: "false"
      REDIS_HOST: langfuse-redis
      REDIS_PORT: "6379"
      LANGFUSE_S3_EVENT_UPLOAD_ENABLED: "true"
      LANGFUSE_S3_EVENT_UPLOAD_BUCKET: langfuse
      LANGFUSE_S3_EVENT_UPLOAD_REGION: us-east-1
      LANGFUSE_S3_EVENT_UPLOAD_ACCESS_KEY_ID: minioadmin
      LANGFUSE_S3_EVENT_UPLOAD_SECRET_ACCESS_KEY: minioadmin
      LANGFUSE_S3_EVENT_UPLOAD_ENDPOINT: http://langfuse-minio:9000
      LANGFUSE_S3_EVENT_UPLOAD_FORCE_PATH_STYLE: "true"
    depends_on:
      langfuse-postgres:
        condition: service_healthy
      langfuse-clickhouse:
        condition: service_healthy
      langfuse-minio:
        condition: service_started
      langfuse-redis:
        condition: service_healthy
    restart: unless-stopped

  # ── LiteLLM infrastructure ─────────────────────────────────────────

  litellm-postgres:
    image: postgres:17
    container_name: litellm-postgres
    ports:
      - "127.0.0.1:5434:5432"
    environment:
      POSTGRES_USER: litellm
      POSTGRES_PASSWORD: litellm
      POSTGRES_DB: litellm
    volumes:
      - litellm-postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U litellm"]
      interval: 5s
      timeout: 3s
      retries: 5
    restart: unless-stopped

  # ── Langfuse infrastructure ────────────────────────────────────────

  langfuse-postgres:
    image: postgres:17
    container_name: langfuse-postgres
    ports:
      - "127.0.0.1:5433:5432"
    environment:
      POSTGRES_USER: langfuse
      POSTGRES_PASSWORD: langfuse
      POSTGRES_DB: langfuse
    volumes:
      - langfuse-postgres:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U langfuse"]
      interval: 5s
      timeout: 3s
      retries: 5
    restart: unless-stopped

  langfuse-clickhouse:
    image: clickhouse/clickhouse-server
    container_name: langfuse-clickhouse
    ports:
      - "127.0.0.1:8123:8123"
      - "127.0.0.1:9001:9000"
    environment:
      CLICKHOUSE_USER: langfuse
      CLICKHOUSE_PASSWORD: langfuse
    volumes:
      - langfuse-clickhouse:/var/lib/clickhouse
      - langfuse-clickhouse-logs:/var/log/clickhouse-server
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:8123/ping"]
      interval: 5s
      timeout: 3s
      retries: 5
    restart: unless-stopped

  langfuse-minio:
    image: cgr.dev/chainguard/minio
    container_name: langfuse-minio
    ports:
      - "127.0.0.1:9090:9000"
      - "127.0.0.1:9091:9001"
    environment:
      MINIO_ROOT_USER: minioadmin
      MINIO_ROOT_PASSWORD: minioadmin
    volumes:
      - langfuse-minio:/data
    entrypoint: sh
    command: -c 'mkdir -p /data/langfuse && minio server /data --console-address ":9001"'
    restart: unless-stopped

  langfuse-redis:
    image: redis:7
    container_name: langfuse-redis
    ports:
      - "127.0.0.1:6380:6379"
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
    restart: unless-stopped

  # ── Qwen3-TTS (text-to-speech, port 9100) ──────────────────────────
  #
  # Start with LLM:  docker compose --profile tts --profile ollama-coder up -d
  # Start alone:     docker compose --profile tts up -d

  qwen3-tts:
    build: ./tts
    container_name: qwen3-tts
    profiles: [tts]
    ports:
      - "9100:9100"
    volumes:
      - ${HOME}/.cache/huggingface:/root/.cache/huggingface
      - ./voices:/data/voices
      - ./scripts:/data/scripts
    environment:
      NVIDIA_VISIBLE_DEVICES: all
      NVIDIA_DRIVER_CAPABILITIES: compute,utility
    restart: unless-stopped
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

  # ── vLLM (fast, no tool calling) ────────────────────────────────────

  vllm-qwen3-coder:
    image: vllm/vllm-openai:latest
    container_name: vllm
    profiles: [qwen3-coder]
    ports:
      - "8000:8000"
    volumes:
      - ${HOME}/.cache/huggingface:/root/.cache/huggingface
    environment:
      HF_HOME: /root/.cache/huggingface
      HF_HUB_ENABLE_HF_TRANSFER: "1"
      NVIDIA_VISIBLE_DEVICES: all
      NVIDIA_DRIVER_CAPABILITIES: compute,utility
    command:
      - QuantTrio/Qwen3-Coder-30B-A3B-Instruct-AWQ
      - --max-model-len
      - "48000"
      - --quantization
      - awq_marlin
      - --gpu-memory-utilization
      - "0.90"
      - --host
      - 0.0.0.0
      - --port
      - "8000"
    ipc: host
    restart: unless-stopped
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

  vllm-qwen3-8b:
    image: vllm/vllm-openai:latest
    container_name: vllm
    profiles: [qwen3-8b]
    ports:
      - "8000:8000"
    volumes:
      - ${HOME}/.cache/huggingface:/root/.cache/huggingface
    environment:
      HF_HOME: /root/.cache/huggingface
      HF_HUB_ENABLE_HF_TRANSFER: "1"
      NVIDIA_VISIBLE_DEVICES: all
      NVIDIA_DRIVER_CAPABILITIES: compute,utility
    command:
      - Qwen/Qwen3-8B-AWQ
      - --max-model-len
      - "48000"
      - --quantization
      - awq_marlin
      - --gpu-memory-utilization
      - "0.90"
      - --host
      - 0.0.0.0
      - --port
      - "8000"
    ipc: host
    restart: unless-stopped
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

  # One-shot helper: verify GPU is visible in Compose.
  # Run: docker compose run --rm gpu-sanity
  gpu-sanity:
    image: nvidia/cuda:12.1.0-base-ubuntu22.04
    command: ["nvidia-smi"]
    environment:
      NVIDIA_VISIBLE_DEVICES: all
      NVIDIA_DRIVER_CAPABILITIES: compute,utility
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

volumes:
  open_webui:
  langfuse-postgres:
  langfuse-clickhouse:
  litellm-postgres:
  langfuse-clickhouse-logs:
  langfuse-minio:
