# Zonos-v0.1 (Zyphra's expressive multilingual open-weight TTS) served
# via the OFFICIAL Zyphra/Zonos repo's Gradio interface.
#
# Why this stack exists alongside the other TTS:
#   * Apache-2.0, 44 kHz output, zero-shot voice cloning from a
#     10-30 s sample, and explicit emotion/conditioning sliders —
#     a different control surface from tag-based engines.
#   * Multilingual (EN/JA/ZH/FR/DE), trained on 200k+ hours.
#   * Cheap to run (~6 GB VRAM, transformer variant ~3.6 GB weights).
#
# CAVEAT — this is an AUDITION/EVAL surface, not a skaldsong-pluggable
# engine yet: the official repo ships a Gradio WebUI + Python SDK, NOT
# an OpenAI-compatible /v1/audio/speech endpoint. To wire Zonos into
# skaldsong's router we'd need the community FastAPI fork (Zyphra/Zonos
# PR #73) or a thin adapter. Bench it by ear first; promote later if it
# earns a slot.
#
# Image is built locally from the upstream repo's Dockerfile via docker
# buildx git-context. All tunables live in .env — edit that, not this.

services:
  zonos:
    image: local/zonos:${ZONOS_TAG}
    build:
      # Single Dockerfile at repo root; bundles espeak-ng (required for
      # Zonos's eSpeak phonemization).
      context: https://github.com/Zyphra/Zonos.git#${ZONOS_SHA}
      dockerfile: Dockerfile
    container_name: zonos
    restart: unless-stopped
    runtime: nvidia
    # Upstream's Dockerfile sets NO CMD — it launches the app from its own
    # compose instead. Without this the NVIDIA entrypoint prints the CUDA
    # banner and exits 0, looping forever (nothing binds 7860). Mirror
    # upstream: python3 gradio_interface.py (demo.launch binds 0.0.0.0:7860).
    command: ["python3", "gradio_interface.py"]
    ports:
      - "${ZONOS_BIND:-0.0.0.0}:${ZONOS_PORT}:7860"
    environment:
      - NVIDIA_VISIBLE_DEVICES=${ZONOS_GPU_DEVICES:-0}
      - NVIDIA_DRIVER_CAPABILITIES=compute,utility
      # Make Gradio bind all interfaces inside the container so the host
      # port-map reaches it (Gradio otherwise may bind 127.0.0.1 only).
      - GRADIO_SERVER_NAME=0.0.0.0
      # Explicit: gradio_interface.py reads GRADIO_SHARE (defaults False).
      # Pin it off so a missing default never tries a public share tunnel
      # (irv-ml1 has no egress for that).
      - GRADIO_SHARE=False
      - HF_HOME=/app/hf_cache
    volumes:
      - ${ZONOS_CACHE_DIR}:/app/hf_cache
    healthcheck:
      # No JSON status route on the Gradio app — just probe the root
      # page for liveness. start_period covers the first-boot model pull.
      test: ["CMD-SHELL", "python3 -c \"import urllib.request,sys; urllib.request.urlopen('http://127.0.0.1:7860/', timeout=5); sys.exit(0)\""]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 600s
    labels:
      - homepage.group=AI Systems
      - homepage.name=Zonos
      - homepage.icon=mdi-waveform
      - homepage.description=Expressive multilingual TTS + cloning, 44kHz (Gradio eval, irv-ml1)
      - homepage.href=http://10.100.79.3:${ZONOS_PORT}

  # OpenAI-ish REST adapter (POST /v1/audio/speech) that asset-engine
  # routes to — upstream Zonos has no REST surface, only Gradio + SDK.
  # Built FROM the gradio image above (reuses torch/CUDA/SDK); loads its
  # own copy of the model, so it adds ~6 GB VRAM on top of the gradio
  # service. Drop the gradio service once Zonos earns a permanent slot.
  zonos-api:
    image: local/zonos-api:${ZONOS_TAG}
    build:
      context: ./adapter
      dockerfile: Dockerfile
      args:
        ZONOS_BASE: local/zonos:${ZONOS_TAG}
    container_name: zonos-api
    restart: unless-stopped
    runtime: nvidia
    depends_on:
      - zonos
    ports:
      - "${ZONOS_BIND:-0.0.0.0}:${ZONOS_API_PORT}:8000"
    environment:
      - NVIDIA_VISIBLE_DEVICES=${ZONOS_GPU_DEVICES:-0}
      - NVIDIA_DRIVER_CAPABILITIES=compute,utility
      - HF_HOME=/app/hf_cache
      - ZONOS_MODEL=${ZONOS_MODEL:-Zyphra/Zonos-v0.1-transformer}
      - ZONOS_VOICES_DIR=/app/voices
    volumes:
      - ${ZONOS_CACHE_DIR}:/app/hf_cache
      # Reference clips for zero-shot cloning, selected by the `voice`
      # request field (filename under this dir).
      - ${ZONOS_VOICES_DIR}:/app/voices:ro
    healthcheck:
      test: ["CMD-SHELL", "python3 -c \"import urllib.request,sys; urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=5); sys.exit(0)\""]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 600s
