Codex Codex commited on
Commit
782f6e2
Β·
1 Parent(s): 82a3f36

Fix ZeroGPU model re-download loop causing browser freeze

Browse files

Root 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>

Files changed (3) hide show
  1. app.py +2 -2
  2. indic_text.py +16 -3
  3. indic_tts.py +16 -9
app.py CHANGED
@@ -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 slower per sentence β€” cap at 10 to stay within GPU budget
1155
  if has_ref:
1156
- sentences = sentences[:10]
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 = []
indic_text.py CHANGED
@@ -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
- _model = AutoModelForSeq2SeqLM.from_pretrained(_HUB_ID).to("cuda").eval()
 
 
 
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("cuda")
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))
indic_tts.py CHANGED
@@ -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
- ).to("cuda").eval()
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
- _mms_model = VitsModel.from_pretrained(_FALLBACK_HUB).to("cuda").eval()
 
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 on ZeroGPU
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
- model = _indic_model
 
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("cuda")
93
  with torch.no_grad():
94
- wav = _mms_model(**inputs).waveform
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)