diff options
| author | historia <historiavg@proton.me> | 2026-09-02 19:13:24 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-09-02 19:13:24 -0400 |
| commit | 6804c785c6b506c47b45264398728d0a609310be (patch) | |
| tree | 0b95361d84a32d5b26e94f54530437a48f34d1c8 /app/backends | |
| parent | 268b6734b22cc251bf340882ea9debbd079b9a48 (diff) | |
| download | tts-audiobook-generator-6804c785c6b506c47b45264398728d0a609310be.tar.gz | |
feat: sglang-omni model picker on manual server launch
Diffstat (limited to 'app/backends')
| -rw-r--r-- | app/backends/servers.py | 8 | ||||
| -rw-r--r-- | app/backends/sglomni/__init__.py | 11 | ||||
| -rw-r--r-- | app/backends/sglomni/catalog.py | 20 | ||||
| -rw-r--r-- | app/backends/sglomni/models.py | 41 |
4 files changed, 75 insertions, 5 deletions
diff --git a/app/backends/servers.py b/app/backends/servers.py index 7815f30..d352717 100644 --- a/app/backends/servers.py +++ b/app/backends/servers.py @@ -54,6 +54,14 @@ _BOOT_HINTS = ( ("fp8e4nv not supported", "the server crashed compiling an FP8 MoE kernel: FP8 needs compute " "capability 8.9+ (RTX 4090/5090, Hopper) and cannot run on this GPU"), + # A model companion package missing from the backend venv (e.g. the + # sglang-omni Qwen3-TTS models need qwen-tts; a shared-cache weight + # install or a failed pip run leaves it absent). + ("ModuleNotFoundError", + "the backend venv is missing a Python module this model needs — " + "re-install the model via Configure Backends (checking the model " + "installs its companion packages), or pip-install it into the " + "backend's venv manually"), ) # Progress callback: called with an event dict. KIND is one of: diff --git a/app/backends/sglomni/__init__.py b/app/backends/sglomni/__init__.py index 688e488..be94a0e 100644 --- a/app/backends/sglomni/__init__.py +++ b/app/backends/sglomni/__init__.py @@ -38,6 +38,7 @@ from .catalog import ( entries_by_keys, entry_by_key, entry_by_repo, + extra_import_name, install_tree_families, ) from .pythonenv import ( @@ -47,9 +48,11 @@ from .pythonenv import ( ) from .models import ( delete_model_weights, + install_companions, install_model, installed_entries, installed_keys, + missing_companions, model_installed, preset_voices, repo_dir, @@ -82,12 +85,14 @@ __all__ = [ # catalog "CAPABILITY_CLONE", "CAPABILITY_DESIGN", "CAPABILITY_SPEAKER", "ENTRIES", "ModelEntry", "config_path", "entries_by_keys", - "entry_by_key", "entry_by_repo", "install_tree_families", + "entry_by_key", "entry_by_repo", "extra_import_name", + "install_tree_families", # pythonenv "env_compatible", "env_version", "prepare_env", # models - "delete_model_weights", "install_model", "installed_entries", - "installed_keys", "model_installed", "preset_voices", "repo_dir", + "delete_model_weights", "install_companions", "install_model", + "installed_entries", "installed_keys", "missing_companions", + "model_installed", "preset_voices", "repo_dir", "resolve_model", "uninstall_model", # status "build_spec", "detect", "gpu_fallback_note", "launch_config_path", diff --git a/app/backends/sglomni/catalog.py b/app/backends/sglomni/catalog.py index 5ff0c81..61cb11c 100644 --- a/app/backends/sglomni/catalog.py +++ b/app/backends/sglomni/catalog.py @@ -96,6 +96,26 @@ _DAC_EXTRAS: Tuple[Extra, ...] = ( ("descript-audiotools==0.7.2", False), ("descript-audio-codec==1.0.0", False)) +# Companion distributions whose top-level import name differs from the pip +# name's plain dash-to-underscore normalization (verified against their +# top_level.txt). Anything absent here normalizes: qwen-tts -> qwen_tts. +_EXTRA_IMPORT_OVERRIDES = { + "descript-audiotools": "audiotools", + "descript-audio-codec": "dac", +} + + +def extra_import_name(spec: str) -> str: + """The Python module an extras requirement SPEC provides. + + Takes the distribution name portion of the pip requirement (so + ``qwen-tts==0.1.1`` -> ``qwen_tts``) — the name a venv probe must + import to prove the companion is installed.""" + base = spec.split("=")[0].split("<")[0].split(">")[0].strip() + if base in _EXTRA_IMPORT_OVERRIDES: + return _EXTRA_IMPORT_OVERRIDES[base] + return base.replace("-", "_") + ENTRIES: Tuple[ModelEntry, ...] = ( ModelEntry( key="qwen3_tts_0_6b_customvoice", diff --git a/app/backends/sglomni/models.py b/app/backends/sglomni/models.py index a5211f6..607e36a 100644 --- a/app/backends/sglomni/models.py +++ b/app/backends/sglomni/models.py @@ -22,7 +22,8 @@ from pathlib import Path from typing import List, Optional from backends import common, envs -from backends.sglomni.catalog import ModelEntry, entry_by_key, entry_by_repo +from backends.sglomni.catalog import ModelEntry, entry_by_key, \ + entry_by_repo, extra_import_name from backends.sglomni.constants import SERVER_NAME, SGLOMNI_PIP_PKG from backends.sglomni.pythonenv import SGLOMNI_ENV, prepare_env @@ -129,6 +130,42 @@ def system_dep_missing(entry: ModelEntry) -> Optional[str]: return None +def missing_companions(entry: ModelEntry) -> List[Extra]: + """ENTRY's companion packages absent from the sglang-omni venv. + + One import probe per extra (its top-level module, see + ``extra_import_name``) against the venv's interpreter, so a model whose + weights landed in the shared HF cache by another route — or whose + companions failed to pip-install during setup, which install_model only + warns about — is detected before the server dies on the import. A venv + that does not exist at all yields no verdict: the start flow fails on + the missing ``sgl-omni`` executable anyway.""" + if not entry.extras or not envs.env_exists(SGLOMNI_ENV): + return [] + return [extra for extra in entry.extras + if not envs.module_available(extra_import_name(extra[0]), + SGLOMNI_ENV)] + + +def install_companions(entry: ModelEntry, *, emit=None, cancel=None) -> int: + """pip-install ENTRY's missing companion packages into the venv. + + The same recipe ``install_model`` runs (the catalog's ``--no-deps`` + flags preserved — the Qwen3-TTS companions must not replace the pinned + Transformers 5 stack), limited to what the import probe found absent, + so a start-time heal touches as little of the pinned environment as + possible. Returns the first failing exit code, 0 when all present.""" + for spec, no_deps in missing_companions(entry): + args = ["--no-deps"] if no_deps else None + rc = common.pip_install([spec], emit=emit, cancel=cancel, + env_dir=SGLOMNI_ENV, extra_args=args) + if rc != 0: + print(f"[ERROR] pip install {spec} failed (exit {rc}); " + f"install it into {SGLOMNI_ENV} manually") + return rc + return 0 + + def install_model(key: str, *, emit=None, cancel=None) -> int: """Install a catalog model: companion packages, then its weights. @@ -157,7 +194,7 @@ def install_model(key: str, *, emit=None, cancel=None) -> int: note = sg_status.gpu_fallback_note(entry) if note: print(f"[WARNING] {note}") - for spec, no_deps in entry.extras: + for spec, no_deps in missing_companions(entry): args = ["--no-deps"] if no_deps else None rc = common.pip_install([spec], emit=emit, cancel=cancel, env_dir=SGLOMNI_ENV, extra_args=args) |
