fix(moss): align accelerator detection and routing status

This commit is contained in:
Palash Debnath
2026-09-07 11:13:33 +05:30
parent a671004cb7
commit cd58bbded0
34 changed files with 135 additions and 27 deletions
+2
View File
@@ -20,6 +20,8 @@ the frozen-backend fallback mirror it for their toolchains.
### Fixed ### Fixed
- MOSS accelerator routing now matches runtime device selection, including native XPU and registered NPU detection (#1830) — thanks @li-lizhe!
## [0.5.2] — 2026-09-02 ## [0.5.2] — 2026-09-02
+1 -1
View File
@@ -150,7 +150,7 @@ def _compute_device_state() -> dict:
caps = device_caps.detect_host_caps() caps = device_caps.detect_host_caps()
env_pin = (os.environ.get("OMNIVOICE_DEVICE") or "").strip().lower() env_pin = (os.environ.get("OMNIVOICE_DEVICE") or "").strip().lower()
auto_family = next( auto_family = next(
(f for f in ("cuda", "rocm", "xpu", "mps") if f in caps.available_families), (f for f in device_caps.ACCELERATOR_PRIORITY if f in caps.available_families),
"cpu", "cpu",
) )
value = device_caps.requested_device_override() value = device_caps.requested_device_override()
+22 -4
View File
@@ -35,7 +35,8 @@ import sys
from dataclasses import dataclass from dataclasses import dataclass
from typing import Literal from typing import Literal
DeviceFamily = Literal["cuda", "rocm", "mps", "xpu", "cpu"] DeviceFamily = Literal["cuda", "rocm", "mps", "xpu", "npu", "cpu"]
ACCELERATOR_PRIORITY = ("cuda", "rocm", "xpu", "npu", "mps")
# Stable substring stamped onto notes that represent a real kernel-launch risk # Stable substring stamped onto notes that represent a real kernel-launch risk
# (arch/driver mismatch) — as opposed to advisory notes (multi-GPU, VRAM query # (arch/driver mismatch) — as opposed to advisory notes (multi-GPU, VRAM query
@@ -533,9 +534,12 @@ def _probe() -> HostCaps:
# is the whole truth in that case (CodeRabbit, #1425). # is the whole truth in that case (CodeRabbit, #1425).
notes.extend(why_no_gpu(torch)) notes.extend(why_no_gpu(torch))
# ── Intel XPU via IPEX ─────────────────────────────────────────────── # Older builds register XPU through IPEX; modern torch exposes it directly.
try: try:
import intel_extension_for_pytorch # noqa: F401 import intel_extension_for_pytorch # noqa: F401
except Exception:
pass
try:
if hasattr(torch, "xpu") and torch.xpu.is_available(): if hasattr(torch, "xpu") and torch.xpu.is_available():
detected.append("xpu") detected.append("xpu")
if not device_name: if not device_name:
@@ -546,7 +550,21 @@ def _probe() -> HostCaps:
pass pass
notes.append("XPU VRAM not queried (unreliable across IPEX versions)") notes.append("XPU VRAM not queried (unreliable across IPEX versions)")
except Exception: except Exception:
# IPEX absent or XPU probe failed — no XPU on this host. # XPU probe failed — no usable XPU on this host.
pass
# Vendor extensions may register an NPU with torch. Probe only an already
# registered backend; never install or import an optional vendor package.
try:
if hasattr(torch, "npu") and torch.npu.is_available():
detected.append("npu")
if not device_name:
try:
device_name = torch.npu.get_device_name(0)
except Exception:
pass
notes.append("NPU VRAM not queried")
except Exception:
pass pass
# ── Apple Silicon MPS ──────────────────────────────────────────────── # ── Apple Silicon MPS ────────────────────────────────────────────────
@@ -579,7 +597,7 @@ def _probe() -> HostCaps:
# Preferred family by priority; cpu when nothing accelerated was detected. # Preferred family by priority; cpu when nothing accelerated was detected.
family: DeviceFamily = "cpu" family: DeviceFamily = "cpu"
for pref in ("cuda", "rocm", "xpu", "mps"): for pref in ACCELERATOR_PRIORITY:
if pref in detected: if pref in detected:
family = pref # type: ignore[assignment] family = pref # type: ignore[assignment]
break break
+6 -10
View File
@@ -29,15 +29,11 @@ Do NOT import ``main.py`` from the parent process — it runs under a
different venv (``transformers==5.0.0``) and importing it in-process would different venv (``transformers==5.0.0``) and importing it in-process would
re-introduce the exact conflict this isolation exists to avoid. re-introduce the exact conflict this isolation exists to avoid.
Hardware honesty (cross-platform rule): MOSS-TTS-v1.5's upstream documents Hardware routing follows the sidecar's runtime-available PyTorch accelerator:
only CUDA and CPU. There is **no documented or tested MPS path** — the CUDA/ROCm, XPU, or a registered NPU. MPS remains excluded; CPU is the fallback.
custom ``trust_remote_code`` modelling code and the separate audio XPU/NPU routing is covered with mocked device contracts, not physical-hardware
tokenizer are unverified on Apple Silicon. We therefore advertise synthesis certification; users need a compatible torch/vendor runtime in the
``gpu_compat = ("cuda", "cpu")`` and the sidecar selects ``cuda`` when isolated engine venv.
present else ``cpu`` — it never silently routes to MPS where it might
crash. On Apple Silicon the engine honestly resolves to CPU (slow but
correct), and the engine is opt-in regardless, so it never becomes a
broken default on any platform.
""" """
from __future__ import annotations from __future__ import annotations
@@ -91,7 +87,7 @@ class MossTTSV15Backend(SubprocessBackend):
_DEFAULT_SAMPLE_RATE = 24000 _DEFAULT_SAMPLE_RATE = 24000
# Honest hardware surface: upstream documents CUDA + CPU only. MPS is # Honest hardware surface: upstream documents CUDA + CPU only. MPS is
# undocumented / untested, so we do NOT claim it (cross-platform rule). # undocumented / untested, so we do NOT claim it (cross-platform rule).
gpu_compat = ("cuda", "cpu") gpu_compat = ("cuda", "rocm", "xpu", "npu", "cpu")
# ── availability ─────────────────────────────────────────────────────── # ── availability ───────────────────────────────────────────────────────
+1 -1
View File
@@ -2371,7 +2371,7 @@ def list_backends() -> list[dict]:
"one_click_install": bool, # services.sidecar_install can provision it in-app "one_click_install": bool, # services.sidecar_install can provision it in-app
"last_error": Optional[str], # cached most-recent failure "last_error": Optional[str], # cached most-recent failure
"isolation_mode": "in-process" | "subprocess", "isolation_mode": "in-process" | "subprocess",
"gpu_compat": list[str], # subset of {cuda, rocm, mps, xpu, cpu} "gpu_compat": list[str], # subset of {cuda, rocm, mps, xpu, npu, cpu}
"supports_cloning": Optional[bool], # True/False from the class attr; None when "supports_cloning": Optional[bool], # True/False from the class attr; None when
# model-dependent (property, e.g. mlx-audio) # model-dependent (property, e.g. mlx-audio)
"effective_device": str, # device this engine uses on THIS host "effective_device": str, # device this engine uses on THIS host
+5 -4
View File
@@ -23,10 +23,11 @@ interpreter, so MOSS runs behind
8 GB GPUs when quantized; the bf16 Transformers path used here is ~16 GB 8 GB GPUs when quantized; the bf16 Transformers path used here is ~16 GB
of weights, so a 16 GB+ GPU is the realistic CUDA target. It also runs on of weights, so a 16 GB+ GPU is the realistic CUDA target. It also runs on
**CPU** (fp32) — correct but slow. **CPU** (fp32) — correct but slow.
- **Device:** CUDA when present, else CPU. **There is no MPS path** - **Device:** the sidecar uses a runtime-available PyTorch CUDA/ROCm, XPU,
upstream documents only CUDA/CPU and the custom modelling code is or registered NPU backend, otherwise CPU. The isolated engine venv needs the
untested on Apple Silicon, so VoiceStudio never routes MOSS to MPS. On a matching torch/vendor integration. MPS still uses CPU. XPU/NPU routing is
Mac it runs on CPU. covered by mocked loader tests; physical-device synthesis has not been
validated by this change.
## Install ## Install
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "تعذّر تحميل إعداد الجهاز", "device_load_failed": "تعذّر تحميل إعداد الجهاز",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "Geräteeinstellung konnte nicht geladen werden", "device_load_failed": "Geräteeinstellung konnte nicht geladen werden",
+1
View File
@@ -934,6 +934,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "Failed to load device setting", "device_load_failed": "Failed to load device setting",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "No se pudo cargar el ajuste del dispositivo", "device_load_failed": "No se pudo cargar el ajuste del dispositivo",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "Impossible de charger le réglage du périphérique", "device_load_failed": "Impossible de charger le réglage du périphérique",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "डिवाइस सेटिंग लोड नहीं हो सकी", "device_load_failed": "डिवाइस सेटिंग लोड नहीं हो सकी",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "Gagal memuat pengaturan perangkat", "device_load_failed": "Gagal memuat pengaturan perangkat",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "Impossibile caricare l'impostazione del dispositivo", "device_load_failed": "Impossibile caricare l'impostazione del dispositivo",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "デバイス設定を読み込めませんでした", "device_load_failed": "デバイス設定を読み込めませんでした",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "장치 설정을 불러오지 못했습니다", "device_load_failed": "장치 설정을 불러오지 못했습니다",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "Apparaatinstelling kon niet worden geladen", "device_load_failed": "Apparaatinstelling kon niet worden geladen",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "Nie udało się wczytać ustawienia urządzenia", "device_load_failed": "Nie udało się wczytać ustawienia urządzenia",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "Falha ao carregar a configuração do dispositivo", "device_load_failed": "Falha ao carregar a configuração do dispositivo",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "Не удалось загрузить настройку устройства", "device_load_failed": "Не удалось загрузить настройку устройства",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "Kunde inte läsa in enhetsinställningen", "device_load_failed": "Kunde inte läsa in enhetsinställningen",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "โหลดการตั้งค่าอุปกรณ์ไม่สำเร็จ", "device_load_failed": "โหลดการตั้งค่าอุปกรณ์ไม่สำเร็จ",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "Aygıt ayarı yüklenemedi", "device_load_failed": "Aygıt ayarı yüklenemedi",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "Не вдалося завантажити налаштування пристрою", "device_load_failed": "Не вдалося завантажити налаштування пристрою",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "Không tải được cài đặt thiết bị", "device_load_failed": "Không tải được cài đặt thiết bị",
+1
View File
@@ -586,6 +586,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "无法加载设备设置", "device_load_failed": "无法加载设备设置",
+1
View File
@@ -334,6 +334,7 @@
"device_family_cuda": "NVIDIA GPU (CUDA)", "device_family_cuda": "NVIDIA GPU (CUDA)",
"device_family_rocm": "AMD GPU (ROCm)", "device_family_rocm": "AMD GPU (ROCm)",
"device_family_xpu": "Intel GPU (XPU)", "device_family_xpu": "Intel GPU (XPU)",
"device_family_npu": "NPU",
"device_family_mps": "Apple GPU (MPS)", "device_family_mps": "Apple GPU (MPS)",
"device_family_cpu": "CPU", "device_family_cpu": "CPU",
"device_load_failed": "無法載入裝置設定", "device_load_failed": "無法載入裝置設定",
@@ -82,7 +82,7 @@ _REQUIRED_KEYS = {
"effective_device", "routing_status", "routing_reason", "effective_device", "routing_status", "routing_reason",
} }
_TTS_ASR_STATUSES = {"accelerated", "cpu_fallback", "cpu_only", "unavailable"} _TTS_ASR_STATUSES = {"accelerated", "cpu_fallback", "cpu_only", "unavailable"}
_VALID_FAMILIES = {"cuda", "rocm", "mps", "xpu", "cpu"} _VALID_FAMILIES = {"cuda", "rocm", "mps", "xpu", "npu", "cpu"}
def test_engines_response_includes_new_fields(fresh_app): def test_engines_response_includes_new_fields(fresh_app):
@@ -109,3 +109,14 @@ def test_env_pin_is_reported_and_wins(fresh_app, monkeypatch):
r = c.put("/api/settings/compute-device", json={"value": "auto"}) r = c.put("/api/settings/compute-device", json={"value": "auto"})
assert r.status_code == 200 assert r.status_code == 200
assert r.json()["value"] == "cpu" assert r.json()["value"] == "cpu"
def test_auto_summary_reports_registered_npu(fresh_app, monkeypatch):
from core import device_caps
monkeypatch.setattr(device_caps, 'detect_host_caps', lambda: device_caps.HostCaps(
family='npu', available_families=('npu', 'cpu'),
))
body = _client(fresh_app).get('/api/settings/compute-device').json()
assert body['auto_family'] == body['effective_family'] == 'npu'
# Detection does not globally opt unrelated engines into NPU execution.
assert 'npu' not in body['choices']
+1 -1
View File
@@ -34,7 +34,7 @@ _EXPECTED = {
# is_available — e.g. parakeet-mlx gates on Apple Silicon via mlx_supported()). # is_available — e.g. parakeet-mlx gates on Apple Silicon via mlx_supported()).
_GPU_ONLY: set[str] = {"parakeet-mlx"} _GPU_ONLY: set[str] = {"parakeet-mlx"}
_VALID = {"cuda", "rocm", "mps", "xpu", "cpu"} _VALID = {"cuda", "rocm", "mps", "xpu", "npu", "cpu"}
def _cls(engine_id): def _cls(engine_id):
+21
View File
@@ -235,3 +235,24 @@ def test_result_is_cached_until_refresh():
def teardown_module(_module): def teardown_module(_module):
# Drop any cached mock-derived result so other test modules re-probe clean. # Drop any cached mock-derived result so other test modules re-probe clean.
device_caps.detect_host_caps.cache_clear() device_caps.detect_host_caps.cache_clear()
def test_builtin_xpu_does_not_require_ipex():
caps = _probe_with({'torch': _torch_mock(xpu_available=True), 'intel_extension_for_pytorch': None})
assert caps.family == 'xpu'
def test_registered_npu_is_reported_without_importing_vendor_packages():
torch = _torch_mock()
torch.npu = types.SimpleNamespace(is_available=lambda: True, get_device_name=lambda i: 'Ascend')
caps = _probe_with({'torch': torch, 'intel_extension_for_pytorch': None})
assert caps.family == 'npu'
assert caps.available_families == ('npu', 'cpu')
assert caps.device_name == 'Ascend'
def test_unavailable_npu_does_not_claim_acceleration():
torch = _torch_mock()
torch.npu = types.SimpleNamespace(is_available=lambda: False)
caps = _probe_with({'torch': torch, 'intel_extension_for_pytorch': None})
assert caps.family == 'cpu'
+1 -1
View File
@@ -132,7 +132,7 @@ def test_empty_compat_is_defensive_cpu_only():
# ── Contract guarantees ─────────────────────────────────────────────────── # ── Contract guarantees ───────────────────────────────────────────────────
def test_never_emits_n_a(): def test_never_emits_n_a():
for fam in ("cuda", "rocm", "mps", "xpu", "cpu"): for fam in ("cuda", "rocm", "mps", "xpu", "npu", "cpu"):
for compat in ((), ("cpu",), ("cuda",), ("cuda", "cpu"), ("mps", "cpu")): for compat in ((), ("cpu",), ("cuda",), ("cuda", "cpu"), ("mps", "cpu")):
assert resolve_routing(compat, _caps(fam))["routing_status"] != "n/a" assert resolve_routing(compat, _caps(fam))["routing_status"] != "n/a"
+41 -3
View File
@@ -106,12 +106,12 @@ def test_audited_custom_remote_code_requires_both_opt_ins(monkeypatch):
# ── hardware honesty (cross-platform rule) ───────────────────────────────── # ── hardware honesty (cross-platform rule) ─────────────────────────────────
def test_gpu_compat_cuda_cpu_no_mps(): def test_gpu_compat_matches_accelerator_paths_without_mps():
"""MPS is undocumented/untested upstream — we must not claim it.""" """MPS is undocumented/untested upstream — we must not claim it."""
from engines.moss_tts_v15 import MossTTSV15Backend from engines.moss_tts_v15 import MossTTSV15Backend
assert MossTTSV15Backend.gpu_compat == ("cuda", "cpu"), ( assert MossTTSV15Backend.gpu_compat == ("cuda", "rocm", "xpu", "npu", "cpu"), (
f"expected ('cuda', 'cpu'), got {MossTTSV15Backend.gpu_compat!r}" f"unexpected device targets: {MossTTSV15Backend.gpu_compat!r}"
) )
@@ -199,3 +199,41 @@ def test_generate_without_ref_audio_omits_reference(monkeypatch):
MossTTSV15Backend().generate("just text") MossTTSV15Backend().generate("just text")
assert "ref_audio" not in captured assert "ref_audio" not in captured
assert "tokens" not in captured assert "tokens" not in captured
@pytest.mark.parametrize('family', [None, 'cuda', 'xpu', 'npu', 'mps'])
def test_loader_device_matches_routing(monkeypatch, family):
"""Exercise model + tokenizer placement without importing optional weights."""
import io
from types import SimpleNamespace
from unittest.mock import Mock
from engines.moss_tts_v15 import main, MossTTSV15Backend
from core.device_caps import HostCaps
from services.engine_routing import resolve_routing
expected = family if family not in (None, 'mps') else 'cpu'
accelerator = Mock(return_value=SimpleNamespace(type=family) if family else None)
monkeypatch.setitem(sys.modules, 'torch', SimpleNamespace(
accelerator=SimpleNamespace(current_accelerator=accelerator),
bfloat16='bf16', float32='fp32',
))
processor = Mock()
processor.model_config.sampling_rate = 24000
tokenizer = processor.audio_tokenizer
model = Mock()
model.to.return_value = model
factory = Mock()
factory.from_pretrained.return_value = model
monkeypatch.setitem(sys.modules, 'transformers', SimpleNamespace(
AutoModel=factory,
AutoProcessor=SimpleNamespace(from_pretrained=lambda *a, **kw: processor),
))
monkeypatch.setattr(main, '_state', None)
monkeypatch.setattr(main, '_model_source', lambda: ('local-fixture', 'a' * 40))
state = main._load_model(io.BytesIO())
accelerator.assert_called_once_with(check_available=True)
model.to.assert_called_once_with(expected)
tokenizer.to.assert_called_once_with(expected)
assert factory.from_pretrained.call_args.kwargs['torch_dtype'] == ('fp32' if expected == 'cpu' else 'bf16')
caps = HostCaps(family=family or 'cpu', available_families=(family, 'cpu') if family else ('cpu',))
assert resolve_routing(MossTTSV15Backend.gpu_compat, caps)['effective_device'] == state[2]
+1 -1
View File
@@ -145,7 +145,7 @@ def test_preflight_device_summary(client):
assert d["gpu_backend"] in {"cuda", "rocm", "mps", "cpu"} assert d["gpu_backend"] in {"cuda", "rocm", "mps", "cpu"}
assert d["gpu_vendor"] in {"nvidia", "amd", "apple", "intel", "unknown", "none"} assert d["gpu_vendor"] in {"nvidia", "amd", "apple", "intel", "unknown", "none"}
# #21: canonical-probe family + VRAM joined the device summary. # #21: canonical-probe family + VRAM joined the device summary.
assert d["gpu_family"] in {"cuda", "rocm", "mps", "xpu", "cpu"} assert d["gpu_family"] in {"cuda", "rocm", "mps", "xpu", "npu", "cpu"}
assert isinstance(d["vram_gb"], (int, float)) assert isinstance(d["vram_gb"], (int, float))