Files
VoiceStudio/omnivoice/__init__.py
T
debpalashandClaude Opus 4.8 b6d92637e7 fix(startup): don't let one optional transformers symbol kill the backend (#1229)
`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>
2026-07-23 00:28:42 +05:30

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__])