Skip to content

Model Catalog

The model catalog is the single source of truth for every Qwen3-TTS variant LunaVox ships with. Every other module — the downloader, the conversion pipeline, the CLI prompts — reads MODELS from this module. Adding or renaming a model means touching this file and nothing else.

The MODELS registry

from lunavox.model import MODELS, all_models, get_model

# Dict keyed by internal short name
print(list(MODELS.keys()))
# ['base_small', 'custom_small', 'base', 'custom', 'design']

# Ordered list preserving registry order
for spec in all_models():
    print(f"{spec.name:15s}  {spec.size}  mode={spec.mode}  repo={spec.repo_id}")

# Direct lookup raises ValueError on unknown
spec = get_model("base_small")

ModelSpec

ModelSpec dataclass

ModelSpec(name: str, repo: str, display_name: str, size: str, mode: str)

Catalog entry for one published Qwen3-TTS model.

MODELS module-attribute

MODELS: dict[str, ModelSpec] = {(name): spec for spec in _REGISTRY}

all_models

all_models() -> list[ModelSpec]

Iteration order matches _REGISTRY.

Source code in src/lunavox/model/config.py
def all_models() -> list[ModelSpec]:
    """Iteration order matches ``_REGISTRY``."""
    return list(_REGISTRY)

model_keys

model_keys() -> list[str]
Source code in src/lunavox/model/config.py
def model_keys() -> list[str]:
    return [spec.name for spec in _REGISTRY]

get_model

get_model(name: str) -> ModelSpec
Source code in src/lunavox/model/config.py
def get_model(name: str) -> ModelSpec:
    try:
        return MODELS[name]
    except KeyError:
        valid = ", ".join(model_keys())
        raise ValueError(f"Unknown model '{name}'. Available: {valid}") from None

get_snapshot

get_snapshot(spec_or_name: ModelSpec | str) -> Path

Resolve a cached HF snapshot directory for a model.

Previously this returned a ghost path on miss, which masked failures downstream. It now raises RuntimeError listing every path tried.

Source code in src/lunavox/model/config.py
def get_snapshot(spec_or_name: ModelSpec | str) -> Path:
    """Resolve a cached HF snapshot directory for a model.

    Previously this returned a ghost path on miss, which masked failures
    downstream. It now raises ``RuntimeError`` listing every path tried.
    """
    spec = spec_or_name if isinstance(spec_or_name, ModelSpec) else get_model(spec_or_name)
    tried: list[str] = []

    # 1. Preferred: huggingface_hub resolves the canonical snapshot dir.
    try:
        path = Path(snapshot_download(repo_id=spec.repo_id, local_files_only=True))
        log.debug("snapshot_download hit for %s: %s", spec.repo_id, path)
        return path
    except Exception as err:
        tried.append(f"snapshot_download({spec.repo_id}): {err}")

    # 2. Fallback: walk the HF cache layout directly. If the most recent
    #    snapshot subdir exists, use it.
    snap_dir = HF_HUB_ROOT / f"models--{HF_ORG}--{spec.repo}" / "snapshots"
    tried.append(str(snap_dir))
    if snap_dir.exists():
        snaps = [s for s in snap_dir.iterdir() if s.is_dir()]
        if snaps:
            # Newest-first so resuming an interrupted pull picks up the
            # latest commit instead of an abandoned partial.
            snaps.sort(key=lambda p: p.stat().st_mtime, reverse=True)
            log.debug("cache fallback hit for %s: %s", spec.repo_id, snaps[0])
            return snaps[0]

    raise RuntimeError(
        f"Model snapshot for '{spec.name}' ({spec.repo_id}) not found. Tried:\n  - "
        + "\n  - ".join(tried)
    )

ModelConfig and project-rooted view

ModelConfig dataclass

ModelConfig(name: str, spec: ModelSpec, source: Path, dest: Path)

Per-project binding of a ModelSpec to a local destination.

Models

Models(project_root: Path, names: Iterable[str] | None = None)

Project-rooted view of the model catalog.

Source code in src/lunavox/model/config.py
def __init__(self, project_root: Path, names: Iterable[str] | None = None):
    self.project_root = project_root
    keys = list(names) if names is not None else model_keys()
    self._models = [
        ModelConfig(
            name=k,
            spec=get_model(k),
            source=_probe_source(get_model(k)),
            dest=project_root / "models" / k,
        )
        for k in keys
    ]