Fix ZeroGPU model re-download loop causing browser freeze
Browse filesRoot cause: indic_text.py and indic_tts.py called .to("cuda").eval() as
part of the module-scope assignment (e.g. _model = Model.from_pretrained().to("cuda")).
On ZeroGPU, after startup the runtime deletes 17GB of raw HF cache blobs and
keeps only its packed tensor format. When these assignments failed (because
the .to("cuda") chain prevents the variable from being set), _model/_mms_model
stayed None. At inference time, from_pretrained() tried to load from the
deleted HF cache, triggering a full re-download inside the GPU window -> hang.
Fix: load both NLLB-200 and MMS-TTS-Kan to CPU at module scope (no .to("cuda")),
so ZeroGPU packs their tensors along with the main models. Inside @spaces.GPU
functions, call model.to("cuda") so ZeroGPU transfers the pre-packed tensors
to GPU without any re-download.
Also reduce voice-cloning sentence cap from 10 to 6 (6 x ~15s = 90s, well
within the 180s GPU budget).
Co-Authored-By: Codex <noreply@codex.ai>
- app.py +2 -2
- indic_text.py +16 -3
- indic_tts.py +16 -9
|
@@ -1151,9 +1151,9 @@ def generate_tts_cloned_gpu(text: str, ref_wav: str | None, mood: str = "calming
|
|
| 1151 |
sentences = [text.strip() or "Sweet dreams."]
|
| 1152 |
|
| 1153 |
has_ref = bool(ref_wav and os.path.exists(str(ref_wav)))
|
| 1154 |
-
# Voice cloning is
|
| 1155 |
if has_ref:
|
| 1156 |
-
sentences = sentences[:
|
| 1157 |
|
| 1158 |
silence = np.zeros(int(0.65 * sr), dtype=np.float32)
|
| 1159 |
pieces = []
|
|
|
|
| 1151 |
sentences = [text.strip() or "Sweet dreams."]
|
| 1152 |
|
| 1153 |
has_ref = bool(ref_wav and os.path.exists(str(ref_wav)))
|
| 1154 |
+
# Voice cloning is ~10-15s per sentence β cap at 6 to stay within 180s budget
|
| 1155 |
if has_ref:
|
| 1156 |
+
sentences = sentences[:6]
|
| 1157 |
|
| 1158 |
silence = np.zeros(int(0.65 * sr), dtype=np.float32)
|
| 1159 |
pieces = []
|
|
@@ -1,4 +1,9 @@
|
|
| 1 |
-
"""English β Kannada translation via facebook/nllb-200-distilled-600M (non-gated).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
from __future__ import annotations
|
| 3 |
import re
|
| 4 |
import torch
|
|
@@ -15,10 +20,14 @@ def _get_model():
|
|
| 15 |
if _model is None:
|
| 16 |
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
|
| 17 |
_tok = AutoTokenizer.from_pretrained(_HUB_ID, src_lang=_SRC)
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
| 19 |
return _model, _tok
|
| 20 |
|
| 21 |
|
|
|
|
| 22 |
try:
|
| 23 |
_get_model()
|
| 24 |
except Exception:
|
|
@@ -32,6 +41,10 @@ def translate_to_kannada(en_text: str) -> str:
|
|
| 32 |
raise ValueError("Nothing to translate.")
|
| 33 |
|
| 34 |
model, tok = _get_model()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
tgt_id = tok.lang_code_to_id[_TGT]
|
| 36 |
|
| 37 |
sentences = [s.strip() for s in re.split(r"(?<=[.!?])\s+", text) if s.strip()]
|
|
@@ -40,7 +53,7 @@ def translate_to_kannada(en_text: str) -> str:
|
|
| 40 |
|
| 41 |
parts = []
|
| 42 |
for sent in sentences:
|
| 43 |
-
inputs = tok(sent, return_tensors="pt", padding=True).to(
|
| 44 |
with torch.no_grad():
|
| 45 |
out = model.generate(**inputs, forced_bos_token_id=tgt_id, max_length=512)
|
| 46 |
parts.append(tok.decode(out[0], skip_special_tokens=True))
|
|
|
|
| 1 |
+
"""English β Kannada translation via facebook/nllb-200-distilled-600M (non-gated).
|
| 2 |
+
|
| 3 |
+
ZeroGPU pattern: model loaded to CPU at module scope so ZeroGPU can pack its
|
| 4 |
+
tensors. Inside @spaces.GPU functions, .to("cuda") is called and ZeroGPU
|
| 5 |
+
transfers the packed tensors efficiently β no re-download needed.
|
| 6 |
+
"""
|
| 7 |
from __future__ import annotations
|
| 8 |
import re
|
| 9 |
import torch
|
|
|
|
| 20 |
if _model is None:
|
| 21 |
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
|
| 22 |
_tok = AutoTokenizer.from_pretrained(_HUB_ID, src_lang=_SRC)
|
| 23 |
+
# Load to CPU only β ZeroGPU packs these tensors at startup.
|
| 24 |
+
# .to("cuda") is called inside translate_to_kannada() which runs
|
| 25 |
+
# inside @spaces.GPU, where ZeroGPU intercepts the call.
|
| 26 |
+
_model = AutoModelForSeq2SeqLM.from_pretrained(_HUB_ID)
|
| 27 |
return _model, _tok
|
| 28 |
|
| 29 |
|
| 30 |
+
# Pre-load to CPU at module scope so ZeroGPU packs the tensors.
|
| 31 |
try:
|
| 32 |
_get_model()
|
| 33 |
except Exception:
|
|
|
|
| 41 |
raise ValueError("Nothing to translate.")
|
| 42 |
|
| 43 |
model, tok = _get_model()
|
| 44 |
+
# Move to GPU β ZeroGPU intercepts this inside @spaces.GPU and uses the
|
| 45 |
+
# pre-packed tensors, so no re-download is needed.
|
| 46 |
+
model = model.to("cuda")
|
| 47 |
+
|
| 48 |
tgt_id = tok.lang_code_to_id[_TGT]
|
| 49 |
|
| 50 |
sentences = [s.strip() for s in re.split(r"(?<=[.!?])\s+", text) if s.strip()]
|
|
|
|
| 53 |
|
| 54 |
parts = []
|
| 55 |
for sent in sentences:
|
| 56 |
+
inputs = tok(sent, return_tensors="pt", padding=True).to(model.device)
|
| 57 |
with torch.no_grad():
|
| 58 |
out = model.generate(**inputs, forced_bos_token_id=tgt_id, max_length=512)
|
| 59 |
parts.append(tok.decode(out[0], skip_special_tokens=True))
|
|
@@ -1,5 +1,9 @@
|
|
| 1 |
"""Kannada TTS β primary: sush0401/IndicF5-Kannada-Bedtime-v2 (fine-tuned, non-gated).
|
| 2 |
Fallback: facebook/mms-tts-kan (VITS, 16kHz, no voice cloning).
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
"""
|
| 4 |
from __future__ import annotations
|
| 5 |
import os
|
|
@@ -21,9 +25,10 @@ MMS_SR = 16_000
|
|
| 21 |
def _load_indic():
|
| 22 |
global _indic_model, _use_indic
|
| 23 |
from transformers import AutoModel
|
|
|
|
| 24 |
_indic_model = AutoModel.from_pretrained(
|
| 25 |
_FINETUNE_HUB, trust_remote_code=True,
|
| 26 |
-
)
|
| 27 |
_use_indic = True
|
| 28 |
|
| 29 |
|
|
@@ -31,7 +36,8 @@ def _load_mms():
|
|
| 31 |
global _mms_model, _mms_tok
|
| 32 |
from transformers import VitsModel, AutoTokenizer
|
| 33 |
_mms_tok = AutoTokenizer.from_pretrained(_FALLBACK_HUB)
|
| 34 |
-
|
|
|
|
| 35 |
|
| 36 |
|
| 37 |
def _get_model():
|
|
@@ -40,12 +46,10 @@ def _get_model():
|
|
| 40 |
_load_indic()
|
| 41 |
except Exception:
|
| 42 |
_load_mms()
|
| 43 |
-
elif not _use_indic:
|
| 44 |
-
pass # already loaded MMS
|
| 45 |
return _use_indic
|
| 46 |
|
| 47 |
|
| 48 |
-
# Pre-load at module scope
|
| 49 |
try:
|
| 50 |
_get_model()
|
| 51 |
except Exception:
|
|
@@ -68,8 +72,9 @@ def _split(text: str, max_chars: int = 200):
|
|
| 68 |
return out or [text.strip()]
|
| 69 |
|
| 70 |
|
| 71 |
-
def _narrate_indic(ref_wav: str, kannada_text: str) -> np.ndarray:
|
| 72 |
-
|
|
|
|
| 73 |
silence_sr = 24_000
|
| 74 |
silence = np.zeros(int(0.55 * silence_sr), dtype=np.float32)
|
| 75 |
chunks = []
|
|
@@ -86,12 +91,14 @@ def _narrate_indic(ref_wav: str, kannada_text: str) -> np.ndarray:
|
|
| 86 |
|
| 87 |
|
| 88 |
def _narrate_mms(kannada_text: str) -> tuple[np.ndarray, int]:
|
|
|
|
|
|
|
| 89 |
silence = np.zeros(int(0.55 * MMS_SR), dtype=np.float32)
|
| 90 |
chunks = []
|
| 91 |
for sent in _split(kannada_text):
|
| 92 |
-
inputs = _mms_tok(sent, return_tensors="pt").to(
|
| 93 |
with torch.no_grad():
|
| 94 |
-
wav =
|
| 95 |
audio = wav.squeeze().cpu().float().numpy()
|
| 96 |
if audio.size:
|
| 97 |
chunks.append(audio)
|
|
|
|
| 1 |
"""Kannada TTS β primary: sush0401/IndicF5-Kannada-Bedtime-v2 (fine-tuned, non-gated).
|
| 2 |
Fallback: facebook/mms-tts-kan (VITS, 16kHz, no voice cloning).
|
| 3 |
+
|
| 4 |
+
ZeroGPU pattern: models loaded to CPU at module scope so ZeroGPU packs their
|
| 5 |
+
tensors. Inside inference functions (called from @spaces.GPU), .to("cuda") is
|
| 6 |
+
called and ZeroGPU transfers packed tensors to GPU β no re-download needed.
|
| 7 |
"""
|
| 8 |
from __future__ import annotations
|
| 9 |
import os
|
|
|
|
| 25 |
def _load_indic():
|
| 26 |
global _indic_model, _use_indic
|
| 27 |
from transformers import AutoModel
|
| 28 |
+
# Load to CPU β ZeroGPU packs these tensors.
|
| 29 |
_indic_model = AutoModel.from_pretrained(
|
| 30 |
_FINETUNE_HUB, trust_remote_code=True,
|
| 31 |
+
)
|
| 32 |
_use_indic = True
|
| 33 |
|
| 34 |
|
|
|
|
| 36 |
global _mms_model, _mms_tok
|
| 37 |
from transformers import VitsModel, AutoTokenizer
|
| 38 |
_mms_tok = AutoTokenizer.from_pretrained(_FALLBACK_HUB)
|
| 39 |
+
# Load to CPU β ZeroGPU packs these tensors.
|
| 40 |
+
_mms_model = VitsModel.from_pretrained(_FALLBACK_HUB)
|
| 41 |
|
| 42 |
|
| 43 |
def _get_model():
|
|
|
|
| 46 |
_load_indic()
|
| 47 |
except Exception:
|
| 48 |
_load_mms()
|
|
|
|
|
|
|
| 49 |
return _use_indic
|
| 50 |
|
| 51 |
|
| 52 |
+
# Pre-load to CPU at module scope so ZeroGPU packs the tensors.
|
| 53 |
try:
|
| 54 |
_get_model()
|
| 55 |
except Exception:
|
|
|
|
| 72 |
return out or [text.strip()]
|
| 73 |
|
| 74 |
|
| 75 |
+
def _narrate_indic(ref_wav: str, kannada_text: str) -> tuple[np.ndarray, int]:
|
| 76 |
+
# Move to GPU β ZeroGPU intercepts this inside @spaces.GPU.
|
| 77 |
+
model = _indic_model.to("cuda")
|
| 78 |
silence_sr = 24_000
|
| 79 |
silence = np.zeros(int(0.55 * silence_sr), dtype=np.float32)
|
| 80 |
chunks = []
|
|
|
|
| 91 |
|
| 92 |
|
| 93 |
def _narrate_mms(kannada_text: str) -> tuple[np.ndarray, int]:
|
| 94 |
+
# Move to GPU β ZeroGPU intercepts this inside @spaces.GPU.
|
| 95 |
+
model = _mms_model.to("cuda")
|
| 96 |
silence = np.zeros(int(0.55 * MMS_SR), dtype=np.float32)
|
| 97 |
chunks = []
|
| 98 |
for sent in _split(kannada_text):
|
| 99 |
+
inputs = _mms_tok(sent, return_tensors="pt").to(model.device)
|
| 100 |
with torch.no_grad():
|
| 101 |
+
wav = model(**inputs).waveform
|
| 102 |
audio = wav.squeeze().cpu().float().numpy()
|
| 103 |
if audio.size:
|
| 104 |
chunks.append(audio)
|