File size: 6,385 Bytes
0e99f05
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#!/usr/bin/env bash
# Sourced by start_llm.sh / start_stt.sh / start_tts.sh. Not run directly.

SUDO=""
if [ "$(id -u)" -ne 0 ]; then SUDO="sudo"; fi

# Models (~30G for granite+cohere+moss) will not fit next to two CUDA envs on a
# 32G overlay. Keep the HF cache on /dev/shm (RAM-backed; this box has ~2Ti).
ensure_hf_home () {
  local dest="/dev/shm/hf_home"
  local ws="${WORKSPACE:-/workspace}"
  mkdir -p "$dest"
  if [ -d "${ws}/.hf_home" ] && [ ! -L "${ws}/.hf_home" ]; then
    # Migrate any leftover overlay cache once.
    cp -a "${ws}/.hf_home/." "$dest/" 2>/dev/null || true
    rm -rf "${ws}/.hf_home"
  fi
  ln -sfn "$dest" "${ws}/.hf_home"
  export HF_HOME="$dest"
  export HUGGINGFACE_HUB_CACHE="$dest/hub"
  # Persist for login shells / supervisor
  if [ -d "$ws" ] && ! grep -q '^HF_HOME=' "${ws}/.env" 2>/dev/null; then
    printf 'HF_HOME="%s"\nHUGGINGFACE_HUB_CACHE="%s/hub"\n' "$dest" "$dest" >> "${ws}/.env"
  fi
}

# Idempotent: only installs anything if nvcc or its matching g++ is actually missing.
# Vast.ai templates vary a lot -- some ship a complete matched toolchain, some don't.
ensure_toolchain () {
  if ! command -v nvcc >/dev/null 2>&1; then
    echo "[bootstrap] nvcc not found -- installing CUDA toolkit (this can take a few minutes)"
    $SUDO apt-get update -y
    wget -q https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb -O /tmp/cuda-keyring.deb
    $SUDO dpkg -i /tmp/cuda-keyring.deb
    $SUDO apt-get update -y
    $SUDO apt-get install -y cuda-toolkit-13-0
  fi

  # nvcc shells out to plain `gcc`, whose version can silently differ from
  # whatever g++ you have -- each gcc major version ships its own cc1plus.
  local gcc_major
  gcc_major="$(gcc -dumpversion | cut -d. -f1)"
  local cc1plus_path
  cc1plus_path="$(gcc -print-prog-name=cc1plus)"
  if [ ! -f "$cc1plus_path" ]; then
    echo "[bootstrap] cc1plus missing for gcc-$gcc_major -- installing g++-$gcc_major"
    $SUDO apt-get update -y
    $SUDO apt-get install -y "g++-${gcc_major}" build-essential
  fi
}

# ensure_venv <name> <pip-install-args...>
# Creates ~/<name>-env if missing, then pip installs the given packages inside it.
# Safe to call every run -- pip install is a no-op if already satisfied.
ensure_venv () {
  local name="$1"; shift
  local venv_dir="$HOME/${name}-env"
  if [ ! -f "$venv_dir/bin/activate" ]; then
    echo "[bootstrap] creating venv: $venv_dir"
    python3 -m venv "$venv_dir"
  fi
  # shellcheck disable=SC1090
  source "$venv_dir/bin/activate"
  # 32G container disks fill fast with 3 CUDA stacks + pip cache.
  export PIP_NO_CACHE_DIR=1
  export UV_NO_CACHE=1
  pip install --upgrade pip -q
  if [ "$#" -gt 0 ]; then
    echo "[bootstrap] pip install: $*"
    pip install "$@" -q
  fi
}

# Shared env for LLM + TTS. sglang-omni is not on PyPI and pins sglang==0.5.12.post1;
# plain pip hits a protobuf conflict (descript-audiotools vs s3prl) that upstream
# solves with uv override-dependencies.
ensure_sglang_omni () {
  local venv_dir="$HOME/sglang-env"
  local src="/dev/shm/sglang-omni-src"
  local overrides="/tmp/sgl-omni-overrides.txt"

  ensure_hf_home
  if [ ! -f "$venv_dir/bin/activate" ]; then
    echo "[bootstrap] creating venv: $venv_dir"
    python3 -m venv "$venv_dir"
  fi
  # shellcheck disable=SC1090
  source "$venv_dir/bin/activate"
  export PIP_NO_CACHE_DIR=1
  export UV_NO_CACHE=1

  if [ ! -d "$src/.git" ]; then
    echo "[bootstrap] cloning sglang-omni -> $src"
    rm -rf "$src"
    git clone --depth 1 https://github.com/sgl-project/sglang-omni.git "$src"
  fi

  # Blackwell (sm_120): force SDPA in Moss vocoder + audio tokenizer.
  _patch_moss_blackwell "$src"

  if command -v sgl-omni >/dev/null 2>&1 && python3 -c "import sglang" 2>/dev/null; then
    return 0
  fi

  # Matches [tool.uv] override-dependencies in upstream pyproject.toml
  printf 'protobuf>=6.31.1,<7.0.0\n' > "$overrides"

  echo "[bootstrap] uv pip install -e sglang-omni (protobuf override)"
  uv pip install --python "$venv_dir/bin/python" -e "$src" --override "$overrides"
}

_patch_moss_blackwell () {
  local src="$1"
  local vocoder="$src/sglang_omni/models/moss_tts_local/vocoder_decoder.py"
  local tokenizer="$src/sglang_omni/models/moss_tts_local/audio_tokenizer.py"

  if [ -f "$vocoder" ] && ! grep -q 'major >= 10' "$vocoder" 2>/dev/null; then
    python3 - "$vocoder" <<'PY'
from pathlib import Path
import sys
path = Path(sys.argv[1])
text = path.read_text()
old = """try:
    from sglang.jit_kernel.flash_attention import flash_attn_varlen_func
except ImportError:
    flash_attn_varlen_func = None
"""
new = """try:
    from sglang.jit_kernel.flash_attention import flash_attn_varlen_func

    # FA3 (sgl-kernel) rejects Blackwell (sm_120). FA4 cute varlen is not API-
    # compatible with the FA2 call site used when FA3 is disabled. Fall back to
    # the dense SDPA path in resolve_attention_implementation().
    if torch.cuda.is_available():
        _major, _ = torch.cuda.get_device_capability()
        if _major >= 10:
            flash_attn_varlen_func = None
except ImportError:
    flash_attn_varlen_func = None
"""
if old not in text:
    raise SystemExit(f"patch target not found in {path}")
path.write_text(text.replace(old, new, 1))
print(f"[bootstrap] patched Moss vocoder for Blackwell: {path}")
PY
  fi

  if [ -f "$tokenizer" ] && ! grep -q 'Blackwell: flash-attn' "$tokenizer" 2>/dev/null; then
    python3 - "$tokenizer" <<'PY'
from pathlib import Path
import sys
path = Path(sys.argv[1])
text = path.read_text()
needle = "    model.eval()\n    model.to(device)\n    return MossTTSLocalAudioTokenizer("
insert = """    model.eval()
    model.to(device)
    # Blackwell: flash-attn FA2/FA3/FA4 paths are broken for this codec; force SDPA.
    if hasattr(model, "set_attention_implementation"):
        model.set_attention_implementation("sdpa")
    for module in model.modules():
        if hasattr(module, "attn_implementation"):
            module.attn_implementation = "sdpa"
        if hasattr(module, "attention_implementation"):
            module.attention_implementation = "sdpa"
    return MossTTSLocalAudioTokenizer("""
if needle not in text:
    raise SystemExit(f"patch target not found in {path}")
path.write_text(text.replace(needle, insert, 1))
print(f"[bootstrap] patched Moss audio tokenizer for Blackwell: {path}")
PY
  fi
}