eaysu Claude Sonnet 4.6 commited on
Commit
12b3247
Β·
1 Parent(s): 749bf48

Convert to CPU-compatible Space: remove ZeroGPU dependency

Browse files

- Remove spaces import and @spaces.GPU decorator
- Auto-detect device (CUDA > MPS > CPU) instead of hardcoded "cuda"
- Use float32 dtype on CPU, bfloat16 on CUDA/MPS
- Pass DEVICE variable to tts_model.inference() instead of inline check
- Remove spaces package from requirements.txt
- Update README title/description for CPU variant

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Files changed (3) hide show
  1. README.md +2 -2
  2. app.py +21 -21
  3. requirements.txt +0 -1
README.md CHANGED
@@ -1,5 +1,5 @@
1
  ---
2
- title: MOSS-TTS-Nano
3
  emoji: πŸŽ™οΈ
4
  colorFrom: blue
5
  colorTo: purple
@@ -7,7 +7,7 @@ sdk: gradio
7
  sdk_version: 6.5.1
8
  app_file: app.py
9
  pinned: false
10
- short_description: 0.1B multilingual TTS with voice cloning
11
  ---
12
 
13
  # MOSS-TTS-Nano
 
1
  ---
2
+ title: MOSS-TTS-Nano CPU
3
  emoji: πŸŽ™οΈ
4
  colorFrom: blue
5
  colorTo: purple
 
7
  sdk_version: 6.5.1
8
  app_file: app.py
9
  pinned: false
10
+ short_description: 0.1B multilingual TTS with voice cloning (CPU)
11
  ---
12
 
13
  # MOSS-TTS-Nano
app.py CHANGED
@@ -8,20 +8,22 @@ from pathlib import Path
8
  import gradio as gr
9
  import torch
10
 
11
- try:
12
- import spaces
13
- except ImportError:
14
- class _SpacesFallback:
15
- @staticmethod
16
- def GPU(*args, **kwargs):
17
- def decorator(func):
18
- return func
19
- return decorator
20
- spaces = _SpacesFallback()
21
-
22
  logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s")
23
  logger = logging.getLogger(__name__)
24
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  MODEL_ID = "OpenMOSS-Team/MOSS-TTS-Nano-100M"
26
  AUDIO_TOKENIZER_ID = "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano"
27
  OUTPUT_DIR = Path("/tmp/moss-tts-output")
@@ -29,18 +31,18 @@ SAMPLE_DIR = Path("/tmp/moss-tts-samples")
29
  OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
30
  SAMPLE_DIR.mkdir(parents=True, exist_ok=True)
31
 
32
- # ─── Load models at module level (ZeroGPU packs them at startup) ──────────────
33
  from transformers import AutoModelForCausalLM, AutoModel, AutoTokenizer
34
 
35
  logger.info("Loading TTS model: %s", MODEL_ID)
36
  tts_model = AutoModelForCausalLM.from_pretrained(
37
  MODEL_ID,
38
  trust_remote_code=True,
39
- dtype=torch.bfloat16,
40
  low_cpu_mem_usage=True,
41
  )
42
  tts_model.eval()
43
- tts_model._set_attention_implementation("sdpa") # flash_attn not installed; sdpa is always available
44
 
45
  logger.info("Loading audio tokenizer: %s", AUDIO_TOKENIZER_ID)
46
  audio_tokenizer = AutoModel.from_pretrained(
@@ -52,9 +54,9 @@ audio_tokenizer.eval()
52
  logger.info("Loading text tokenizer")
53
  text_tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
54
 
55
- logger.info("Moving models to CUDA (ZeroGPU packing) …")
56
- tts_model.to("cuda")
57
- audio_tokenizer.to("cuda")
58
  logger.info("All models ready.")
59
 
60
  # ─── Sample audio files ───────────────────────────────────────────────────────
@@ -108,7 +110,6 @@ LANG_TO_SAMPLE_AUDIO: dict[str, str | None] = {
108
 
109
 
110
  # ─── Inference ────────────────────────────────────────────────────────────────
111
- @spaces.GPU(duration=120)
112
  def generate_speech(
113
  text: str,
114
  reference_audio: str | None,
@@ -124,10 +125,9 @@ def generate_speech(
124
  seed_int = int(seed) if seed else 0
125
  if seed_int != 0:
126
  torch.manual_seed(seed_int)
127
- if torch.cuda.is_available():
128
  torch.cuda.manual_seed(seed_int)
129
 
130
- device = "cuda" if torch.cuda.is_available() else "cpu"
131
  output_path = str(OUTPUT_DIR / f"output_{os.getpid()}_{abs(hash(text)) % 1_000_000}.wav")
132
 
133
  try:
@@ -139,7 +139,7 @@ def generate_speech(
139
  text_tokenizer=text_tokenizer,
140
  audio_tokenizer=audio_tokenizer,
141
  audio_tokenizer_type="moss-audio-tokenizer-nano",
142
- device=device,
143
  max_new_frames=int(max_new_frames),
144
  do_sample=bool(do_sample),
145
  use_kv_cache=True,
 
8
  import gradio as gr
9
  import torch
10
 
 
 
 
 
 
 
 
 
 
 
 
11
  logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s")
12
  logger = logging.getLogger(__name__)
13
 
14
+ # ─── Device detection ─────────────────────────────────────────────────────────
15
+ def _get_device() -> str:
16
+ if torch.cuda.is_available():
17
+ return "cuda"
18
+ if torch.backends.mps.is_available():
19
+ return "mps"
20
+ return "cpu"
21
+
22
+ DEVICE = _get_device()
23
+ # bfloat16 works on CUDA/MPS; use float32 on CPU for stability
24
+ DTYPE = torch.bfloat16 if DEVICE in ("cuda", "mps") else torch.float32
25
+ logger.info("Running on device: %s, dtype: %s", DEVICE, DTYPE)
26
+
27
  MODEL_ID = "OpenMOSS-Team/MOSS-TTS-Nano-100M"
28
  AUDIO_TOKENIZER_ID = "OpenMOSS-Team/MOSS-Audio-Tokenizer-Nano"
29
  OUTPUT_DIR = Path("/tmp/moss-tts-output")
 
31
  OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
32
  SAMPLE_DIR.mkdir(parents=True, exist_ok=True)
33
 
34
+ # ─── Load models ──────────────────────────────────────────────────────────────
35
  from transformers import AutoModelForCausalLM, AutoModel, AutoTokenizer
36
 
37
  logger.info("Loading TTS model: %s", MODEL_ID)
38
  tts_model = AutoModelForCausalLM.from_pretrained(
39
  MODEL_ID,
40
  trust_remote_code=True,
41
+ torch_dtype=DTYPE,
42
  low_cpu_mem_usage=True,
43
  )
44
  tts_model.eval()
45
+ tts_model._set_attention_implementation("sdpa")
46
 
47
  logger.info("Loading audio tokenizer: %s", AUDIO_TOKENIZER_ID)
48
  audio_tokenizer = AutoModel.from_pretrained(
 
54
  logger.info("Loading text tokenizer")
55
  text_tokenizer = AutoTokenizer.from_pretrained(MODEL_ID, trust_remote_code=True)
56
 
57
+ logger.info("Moving models to %s …", DEVICE)
58
+ tts_model.to(DEVICE)
59
+ audio_tokenizer.to(DEVICE)
60
  logger.info("All models ready.")
61
 
62
  # ─── Sample audio files ───────────────────────────────────────────────────────
 
110
 
111
 
112
  # ─── Inference ────────────────────────────────────────────────────────────────
 
113
  def generate_speech(
114
  text: str,
115
  reference_audio: str | None,
 
125
  seed_int = int(seed) if seed else 0
126
  if seed_int != 0:
127
  torch.manual_seed(seed_int)
128
+ if DEVICE == "cuda":
129
  torch.cuda.manual_seed(seed_int)
130
 
 
131
  output_path = str(OUTPUT_DIR / f"output_{os.getpid()}_{abs(hash(text)) % 1_000_000}.wav")
132
 
133
  try:
 
139
  text_tokenizer=text_tokenizer,
140
  audio_tokenizer=audio_tokenizer,
141
  audio_tokenizer_type="moss-audio-tokenizer-nano",
142
+ device=DEVICE,
143
  max_new_frames=int(max_new_frames),
144
  do_sample=bool(do_sample),
145
  use_kv_cache=True,
requirements.txt CHANGED
@@ -5,5 +5,4 @@ safetensors>=0.4.3
5
  soundfile>=0.13.1
6
  torchaudio==2.7.1
7
  gradio==6.5.1
8
- spaces
9
  accelerate>=0.30.0
 
5
  soundfile>=0.13.1
6
  torchaudio==2.7.1
7
  gradio==6.5.1
 
8
  accelerate>=0.30.0