dots.tts / app.py
hflm's picture
Deploy via pip-installed dots.tts + thin ZeroGPU AOTI patch
1c7476a
Raw History Blame Contribute Delete
7.14 kB
"""Hugging Face ZeroGPU entry point for dots.tts.
This Space runs an *unmodified*, pip-installed ``dots.tts`` package. All ZeroGPU
specifics live in the local :mod:`dots_tts_zerogpu` patch layer:
* the model source is never forked -- AOTI acceleration is applied by
intercepting upstream's single compile seam;
* generation buckets are warmed via upstream's own ``run_warmup``.
Startup flow:
1. Apply the AOTI compile-seam patches (before any runtime is built).
2. Preload the runtime on the root process with warmup suppressed (root has only
CUDA emulation, so no real compile can happen here).
3. Inside a ``@spaces.GPU`` call, run warmup under an AOTI capture session so
every reachable step module is exported + AOTI-compiled, then hand the
artifacts back and install them into the serving process.
4. Wrap ``service.generate`` with ``@spaces.GPU`` and launch Gradio.
"""
from __future__ import annotations
import os
import sys
from pathlib import Path
from typing import Any, Callable
REPO_ROOT = Path(__file__).resolve().parent
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
class _SpacesFallback:
"""No-op ``spaces`` shim for local runs outside Hugging Face Spaces."""
@staticmethod
def GPU(*decorator_args, **_decorator_kwargs):
if decorator_args and callable(decorator_args[0]):
return decorator_args[0]
def decorate(fn: Callable[..., Any]) -> Callable[..., Any]:
return fn
return decorate
try:
import spaces # type: ignore
except Exception: # pragma: no cover - only used outside Hugging Face Spaces.
spaces = _SpacesFallback() # type: ignore
def _env_bool(name: str, default: bool) -> bool:
value = os.environ.get(name)
if value is None:
return default
return value.strip().lower() in {"1", "true", "yes", "on"}
def _env_int(name: str, default: int) -> int:
value = os.environ.get(name)
if value is None or not value.strip():
return default
return int(value)
def _configure_zero_gpu_environment() -> None:
# optimize=True on CUDA would otherwise select the flex-attention backend,
# which relies on module-level torch.compile that ZeroGPU cannot run.
os.environ.setdefault("DOTS_TTS_DELAYED_DIT_BACKEND", "sdpa")
os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
def _suppress_root_warmup() -> None:
"""Force ``DotsTtsRuntime.from_pretrained`` to skip the in-process warmup.
The service loads the runtime lazily via ``from_pretrained`` and does not
expose ``warmup_on_optimize``. On the root process there is no real GPU
(only CUDA emulation), so any warmup here would run eagerly and slowly --
real compilation is done separately inside ``@spaces.GPU``.
"""
from dots_tts.runtime import DotsTtsRuntime
original_from_pretrained = DotsTtsRuntime.from_pretrained.__func__
def _from_pretrained(cls, *args, **kwargs):
kwargs.setdefault("warmup_on_optimize", False)
return original_from_pretrained(cls, *args, **kwargs)
DotsTtsRuntime.from_pretrained = classmethod(_from_pretrained)
def _preload_runtime(app_service, app_config):
runtime, resolved_model_name_or_path = app_service._get_runtime( # noqa: SLF001
app_config.default_model_name_or_path,
)
return runtime, resolved_model_name_or_path
def main() -> None:
_configure_zero_gpu_environment()
from loguru import logger
from dots_tts_zerogpu import apply_zero_gpu_patches
from dots_tts_zerogpu.aoti import AOTI_REGISTRY
from dots_tts_zerogpu.warmup import compile_runtime_aoti
# Patch the compile seam before any runtime (and thus any step module) exists.
apply_zero_gpu_patches()
_suppress_root_warmup()
import gradio as gr
from apps.gradio.app import PLAYGROUND_CSS, build_demo, build_playground_theme
from apps.gradio.service import GradioAppService, build_gradio_app_config
from dots_tts.utils.logging import configure_logging
host = os.environ.get("DOTS_TTS_HOST", "0.0.0.0")
port = _env_int("DOTS_TTS_PORT", 7860)
model_name_or_path = os.environ.get(
"DOTS_TTS_MODEL_NAME_OR_PATH",
"dots-studio/dots.tts-soar",
)
model_revision = os.environ.get("DOTS_TTS_MODEL_REVISION") or None
precision = os.environ.get("DOTS_TTS_PRECISION", "bfloat16")
execution_mode = os.environ.get("DOTS_TTS_EXECUTION_MODE", "generate")
max_generate_length = _env_int("DOTS_TTS_MAX_GENERATE_LENGTH", 500)
default_num_steps = _env_int("DOTS_TTS_DEFAULT_NUM_STEPS", 16)
optimize = _env_bool("DOTS_TTS_OPTIMIZE", True)
enable_aoti = _env_bool("DOTS_TTS_ENABLE_AOTI", True)
startup_compile = _env_bool("DOTS_TTS_AOTI_COMPILE_ON_STARTUP", True)
generation_duration = _env_int("DOTS_TTS_ZERO_GPU_DURATION", 60)
compile_duration = _env_int("DOTS_TTS_ZERO_GPU_COMPILE_DURATION", 1500)
output_dir = Path(os.environ.get("DOTS_TTS_OUTPUT_DIR", "/data/generated"))
log_file = Path(os.environ.get("DOTS_TTS_LOG_FILE", "/tmp/dots_tts_gradio.log"))
configure_logging(log_file=log_file)
logger.info(
"Space app starting: model={} execution_mode={} precision={} optimize={} "
"enable_aoti={} startup_compile={} max_generate_length={}",
model_name_or_path,
execution_mode,
precision,
optimize,
enable_aoti,
startup_compile,
max_generate_length,
)
app_config = build_gradio_app_config(
host=host,
port=port,
execution_mode=execution_mode,
precision=precision,
optimize=optimize,
model_name_or_path=model_name_or_path,
output_dir=output_dir,
max_generate_length=max_generate_length,
default_num_steps=default_num_steps,
default_max_generate_length=max_generate_length,
repo_root=REPO_ROOT,
model_revision=model_revision,
)
app_service = GradioAppService(app_config)
runtime, resolved_model_name_or_path = _preload_runtime(app_service, app_config)
if enable_aoti and startup_compile and optimize:
@spaces.GPU(duration=compile_duration)
def compile_aoti_cache():
child_runtime, _ = _preload_runtime(app_service, app_config)
return compile_runtime_aoti(child_runtime)
compiled_models = compile_aoti_cache()
installed = AOTI_REGISTRY.install(compiled_models or {})
logger.info("AOTI startup compile completed: installed_target_count={}", installed)
app_service.generate = spaces.GPU(duration=generation_duration)(app_service.generate)
demo = build_demo(gr, app_config, app_service)
logger.info(
"Space app ready: host={} port={} resolved_model={} aoti_target_count={}",
app_config.host,
app_config.port,
resolved_model_name_or_path,
len(AOTI_REGISTRY),
)
demo.launch(
server_name=app_config.host,
server_port=app_config.port,
theme=build_playground_theme(gr),
css=PLAYGROUND_CSS,
)
if __name__ == "__main__":
main()