# Deploy Stable Audio Open 1.0 (Stability AI diffusion SFX generator)
# to irv-ml1.
#
# Builds a small custom image from server.py + Dockerfile (no upstream
# Docker exists). ~5-10 min cold build (pytorch base + diffusers stack).
# First start pulls the model (~6 GB) from HF into the bind-mounted
# cache, then loads to VRAM (~30-60 s).
#
# Pre-req (user runs once):
#   1. Visit https://huggingface.co/stabilityai/stable-audio-open-1.0
#      and accept the Stability AI Community License (one click).
#   2. Generate a read token at https://huggingface.co/settings/tokens.
#   3. Put it in /opt/docker/compose/stable-audio-open/.env as
#      SAO_HF_TOKEN=hf_xxx (the playbook seeds .env from .env.example
#      with this field blank; the model gate fails closed without it).
#   4. ssh -t irv-ml1 'sudo mkdir -p \
#         /worktank/stable-audio-open/{hf_cache,outputs} \
#         /opt/docker/compose/stable-audio-open && \
#         sudo chown -R lkraven:lkraven \
#         /worktank/stable-audio-open /opt/docker/compose/stable-audio-open'
#
# Usage:
#   scripts/elway irv-ml1 --playbook playbooks/deploy-stable-audio-open.yaml
#
# Idempotent — every step is creates-/when-gated; rerun is safe.

vars:
  compose_dir: /opt/docker/compose/stable-audio-open
  worktank_root: /worktank/stable-audio-open
  host_port: "8211"

steps:
  # ── sanity checks (dirs were created by user-side sudo prep) ────────

  - name: Verify /worktank/stable-audio-open exists and is writable
    shell: test -w {{ worktank_root }}
    changed_when: "false"

  - name: Verify compose dir exists and is writable
    shell: test -w {{ compose_dir }}
    changed_when: "false"

  # ── upload build context (compose + Dockerfile + server.py) ─────────

  - name: Upload compose.yaml
    upload:
      src: stacks/stable-audio-open/compose.yaml
      dest: "{{ compose_dir }}/compose.yaml"
      mode: "0644"

  - name: Upload Dockerfile
    upload:
      src: stacks/stable-audio-open/Dockerfile
      dest: "{{ compose_dir }}/Dockerfile"
      mode: "0644"

  - name: Upload server.py
    upload:
      src: stacks/stable-audio-open/server.py
      dest: "{{ compose_dir }}/server.py"
      mode: "0644"

  - name: Seed .env from template (only if absent — REMEMBER TO SET SAO_HF_TOKEN)
    upload:
      src: stacks/stable-audio-open/.env.example
      dest: "{{ compose_dir }}/.env"
      mode: "0644"
    when: "[ ! -f {{ compose_dir }}/.env ]"

  # Fail loud + early if the HF token is still empty — the model is
  # gated and the container will crashloop on a 401 if we let it boot
  # without one. Better to bail here than to wait for the healthcheck
  # deadline to expire.
  - name: Verify SAO_HF_TOKEN is set (model is gated, 401s without it)
    shell: |
      set -e
      grep -q '^SAO_HF_TOKEN=hf_' {{ compose_dir }}/.env || {
        echo "ERROR: SAO_HF_TOKEN is empty or invalid in {{ compose_dir }}/.env" >&2
        echo "  1. Accept license at https://huggingface.co/stabilityai/stable-audio-open-1.0" >&2
        echo "  2. Generate token at https://huggingface.co/settings/tokens" >&2
        echo "  3. Put hf_xxx token into {{ compose_dir }}/.env" >&2
        exit 1
      }
    changed_when: "false"

  # ── build + bring up ────────────────────────────────────────────────

  - name: docker compose build (~5-10 min first time; cached after)
    shell: |
      set -o pipefail
      cd {{ compose_dir }} && docker compose build 2>&1 \
        | grep -vE '^#[0-9]+ |^ => |^=> |Collecting|Downloading|Requirement|Using cached|Installing collected|Successfully (installed|built)|━'

  - name: docker compose up -d
    shell: cd {{ compose_dir }} && docker compose up -d

  - name: Wait for /health to respond (allow ~10 min for first model download + load)
    shell: |
      for i in $(seq 1 120); do
        curl -sf -o /dev/null --max-time 3 http://localhost:{{ host_port }}/health && exit 0
        sleep 5
      done
      exit 1
    changed_when: "false"

verify:
  - name: /health returns 200 and reports model loaded
    shell: |
      out=$(curl -sf --max-time 5 http://localhost:{{ host_port }}/health)
      echo "$out" | grep -q '"loaded":true' || { echo "model not loaded: $out" >&2; exit 1; }
    changed_when: "false"

  - name: /v1/audio/sfx returns a real WAV (cheap 1s clip, 10 steps)
    shell: |
      out=$(mktemp --suffix=.wav)
      curl -sf -X POST http://localhost:{{ host_port }}/v1/audio/sfx \
        -H 'Content-Type: application/json' \
        -d '{"prompt":"a single soft bell chime","duration":1,"steps":10}' \
        -o "$out" --max-time 60
      file -b "$out" | grep -q '^RIFF.*WAVE'
      rm -f "$out"
    changed_when: "false"

  - name: Container is running
    shell: docker inspect stable-audio-open --format '{{.State.Status}}' | grep -q running
    changed_when: "false"
