Files
VoiceStudio/tests/backend/test_models_yaml_engines.py
Palash Debnath 3008f89919 feat(catalogue): engine list + detail, weights under their engine
The engine matrix (five columns, three-line rows, every chip on every row)
becomes a shadcn table with three columns — Engine · Runs on · Status — and
one primary action per row (Use / Install). Everything else lives in a
detail panel for the selected row: GPU compatibility chips, isolation,
hints and reasons, health and self-test probes, one-click install
progress, setup snippet, disk usage, docs, license, the curated-model
picker, and now the engine's downloadable WEIGHTS.

Weights belong to their engine: every models.yaml entry names the backend
ids that load it (`engines:`), the detail panel lists and installs them
(EngineWeights, on the model store's install/cancel/remove flow via the
extracted useModelDownloads hook), and the sherpa-onnx engine shows its
dictation-model picker there. The page's "Downloaded weights" list and
recommendation card are gone; only weights no engine owns (speaker
diarisation) remain in a small "Other weights" list. A backend test pins
the mapping: every entry has an `engines` list and every id is a real
backend.

- useEngineInventory: the matrix's state machines extracted verbatim
  (shared/local fetch, residency, health/self-test cooldowns, install
  poller with overlap guard + epoch, disk-usage generations, license).
- Row status phrases: GPU active / CPU fallback / CPU / Available /
  Needs setup / Installing… / failed; routing "unavailable" never reads
  Ready. Group captions keep "Ready to use" / "Add more engines".
- Engine titles read "Engines" (each locale's own word); backend
  "Model Catalogue → Engines/Models" messages and docs updated to the
  new structure.
- Dead matrix CSS (phone-tier grid) removed; scopeReco and RecoBanner gone.
2026-09-10 07:59:47 -07:00

60 lines
2.1 KiB
Python

"""Every catalog model names the engines that load it.
The Model Catalogue lists downloadable weights UNDER their engine (the
`engines:` list in ``config/models.yaml``); a row with no owner falls into a
small "other weights" list instead. That only works if the mapping stays
complete and points at real backend ids, so this pins both: each entry
carries an explicit ``engines`` list (empty is allowed, and means "pipeline
weights no single engine owns"), and every id it names is a backend the
engines router would list for TTS or ASR.
"""
from __future__ import annotations
from pathlib import Path
import pytest
import yaml
YAML_PATH = Path(__file__).resolve().parents[2] / "backend" / "config" / "models.yaml"
def _catalog() -> list[dict]:
with open(YAML_PATH, encoding="utf-8") as f:
data = yaml.safe_load(f)
models = data.get("models") if isinstance(data, dict) else data
assert isinstance(models, list) and models, "models.yaml has no model list"
return models
@pytest.fixture(scope="module")
def known_engine_ids() -> set[str]:
from services import asr_backend, tts_backend
ids = {b["id"] for b in tts_backend.list_backends(include_hidden=True)}
ids |= {b["id"] for b in asr_backend.list_backends()}
return ids
def test_every_model_declares_its_engines():
missing = [m["repo_id"] for m in _catalog() if not isinstance(m.get("engines"), list)]
assert not missing, (
"models.yaml entries without an `engines:` list (use `engines: []` for "
f"pipeline weights no engine owns): {missing}"
)
def test_engine_ids_in_models_yaml_exist(known_engine_ids):
unknown = {
(m["repo_id"], e)
for m in _catalog()
for e in m.get("engines") or []
if e not in known_engine_ids
}
assert not unknown, f"models.yaml names engine ids the registry does not have: {sorted(unknown)}"
def test_sherpa_dictation_models_belong_to_the_sherpa_engine():
for m in _catalog():
if m.get("engine") == "sherpa-onnx":
assert m.get("engines") == ["sherpa-onnx-asr"], m["repo_id"]