`backend/api/routers/profiles.py` imports two pure-stdlib regex helpers from `omnivoice.utils.voice_design`. That import pulled in `omnivoice/__init__`, which eagerly imported `omnivoice.models.omnivoice` — torch, torchaudio, transformers, flex_attention, the whole model definition — including a top-level `from transformers import HiggsAudioV2TokenizerModel`. transformers exposes that class through its lazy module and gates it on the torchaudio backend, so the *attribute access* raises when torchaudio is missing, ABI-mismatched, or installed without discoverable distribution metadata — Colab's system Python, an interrupted `uv pip install`. It raised during `backend/main.py`'s module import, before FastAPI existed: TTS, dubbing, ASR and Settings all dead, the user left with a uvicorn traceback and "Backend did not become healthy within 5 minutes". Two changes, both structural rather than Colab-specific: - `omnivoice/__init__` resolves its model exports lazily (PEP 562). Importing `omnivoice.utils.*` no longer costs — or risks — the model stack. `from omnivoice import OmniVoice` is unchanged; only the timing moves. `backend.spec` already lists `omnivoice.models.omnivoice` as a hidden import, so the frozen build is unaffected. - `HiggsAudioV2TokenizerModel` resolves at its single use site in `from_pretrained`, and a failure there raises an ImportError naming torchaudio and the reinstall. Deferred into a request, `core.failure .classify()` maps it to TRANSFORMERS_IMPORT and attaches a repair hint — whose text now names torchaudio too, instead of only transformers + an ASR workaround irrelevant to this path. Colab notebook: cell 2's sanity check imports the model stack (and prints torchaudio/transformers versions), so a broken env fails there with the real error instead of as a health timeout two cells later. Regression test: tests/test_omnivoice_lazy_model_import.py pins that the utils import loads no heavy module, the lazy exports still resolve, and the deferred failure is actionable and classified. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
50 lines
1.8 KiB
Python
50 lines
1.8 KiB
Python
import warnings
|
|
from importlib.metadata import PackageNotFoundError, version
|
|
|
|
warnings.filterwarnings("ignore", module="torchaudio")
|
|
warnings.filterwarnings(
|
|
"ignore",
|
|
category=SyntaxWarning,
|
|
message="invalid escape sequence",
|
|
module="pydub.utils",
|
|
)
|
|
warnings.filterwarnings(
|
|
"ignore",
|
|
category=FutureWarning,
|
|
module="torch.distributed.algorithms.ddp_comm_hooks",
|
|
)
|
|
|
|
try:
|
|
__version__ = version("omnivoice")
|
|
except PackageNotFoundError:
|
|
__version__ = "0.0.0"
|
|
|
|
__all__ = ["OmniVoice", "OmniVoiceConfig", "OmniVoiceGenerationConfig"]
|
|
|
|
# The model exports are resolved lazily (PEP 562). Importing them here made
|
|
# `omnivoice` an all-or-nothing package: `backend/api/routers/profiles.py` asks
|
|
# only for two pure-stdlib helpers from `omnivoice.utils.voice_design`, and got
|
|
# torch + torchaudio + transformers + the full model definition as a side
|
|
# effect. Any breakage in that stack — a torchaudio transformers can't detect,
|
|
# a flex_attention symbol a torch version doesn't have — then killed the entire
|
|
# backend at import time, before FastAPI existed to classify the error: TTS,
|
|
# dubbing, ASR and Settings all dead, with only a uvicorn traceback to go on
|
|
# (#1229). Deferred, the same breakage surfaces inside the request that
|
|
# actually needs a model, where `core.failure.classify()` attaches a repair
|
|
# hint and everything else keeps working.
|
|
#
|
|
# `from omnivoice import OmniVoice` and `omnivoice.OmniVoice` behave exactly as
|
|
# before; only the *timing* of the heavy import changes.
|
|
|
|
|
|
def __getattr__(name):
|
|
if name in __all__:
|
|
from omnivoice.models import omnivoice as _m
|
|
|
|
return getattr(_m, name)
|
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
|
|
|
|
def __dir__():
|
|
return sorted([*globals(), *__all__])
|