diff --git a/backend/requirements-mlx.txt b/backend/requirements-mlx.txt index b1a82d5d9..6699e5888 100644 --- a/backend/requirements-mlx.txt +++ b/backend/requirements-mlx.txt @@ -16,7 +16,8 @@ miniaudio>=1.59 # mlx_audio.stt.load) works fine on transformers 4.57.x in practice. # # Install it via `pip install --no-deps mlx-audio==0.4.1` after this file -# (see .github/workflows/release.yml). Most other mlx-audio runtime deps +# (see .github/workflows/release.yml and the setup-python recipe in the +# justfile). Most other mlx-audio runtime deps # (huggingface_hub, librosa, mlx-lm, numba, numpy, protobuf, pyloudnorm, # sounddevice, tqdm) are already in requirements.txt or pulled in by # other engines. diff --git a/backend/tests/test_mlx_smoke.py b/backend/tests/test_mlx_smoke.py new file mode 100644 index 000000000..13c4b6165 --- /dev/null +++ b/backend/tests/test_mlx_smoke.py @@ -0,0 +1,55 @@ +""" +Smoke test for the MLX backend dependencies on Apple Silicon. + +Guards the `--no-deps` install of mlx-audio/mlx-lm done by `just setup-python` +and release.yml: those packages skip their declared dependencies (transformers +>=5.x conflict), so a missing transitive dep only surfaces at import time. +This test fails fast if the MLX STT/TTS entry points the backend uses stop +importing (e.g. the `miniaudio` regression from issue #505). + +Usage: + python -m pytest backend/tests/test_mlx_smoke.py -v +""" + +import platform +import sys + +import pytest + +pytestmark = pytest.mark.skipif( + not (sys.platform == "darwin" and platform.machine() == "arm64"), + reason="MLX packages are only installed on Apple Silicon macOS", +) + + +def test_mlx_core_runs(): + """The MLX runtime itself works (Metal array op).""" + import mlx.core as mx + + assert mx.array([1, 2]).sum().item() == 3 + + +def test_mlx_audio_tts_entry_point(): + """`from mlx_audio.tts import load` — used by MLXBackend.load_model_async.""" + from mlx_audio.tts import load + + assert callable(load) + + +def test_mlx_audio_stt_entry_point(): + """`from mlx_audio.stt import load` — used by the Whisper MLX STT path. + + Importing mlx_audio.stt also pulls in miniaudio, so this catches the + ModuleNotFoundError from issue #505 on fresh installs. + """ + from mlx_audio.stt import load + + assert callable(load) + + +def test_mlx_lm_entry_points(): + """`mlx_lm.load` / `mlx_lm.generate` — used by qwen_llm_backend.""" + from mlx_lm import generate, load + + assert callable(load) + assert callable(generate) diff --git a/justfile b/justfile index 156806d88..e1f50f859 100644 --- a/justfile +++ b/justfile @@ -72,6 +72,12 @@ setup-python: if [ "$(uname -m)" = "arm64" ] && [ "$(uname)" = "Darwin" ]; then echo "Detected Apple Silicon — installing MLX dependencies..." {{ pip }} install -r {{ backend_dir }}/requirements-mlx.txt + # mlx-lm and mlx-audio declare transformers>=5.x, which conflicts with + # our transformers<=4.57.x cap, so install them --no-deps (their other + # runtime deps are covered by requirements.txt / requirements-mlx.txt — + # see the note in requirements-mlx.txt and .github/workflows/release.yml) + {{ pip }} install --no-deps mlx-lm==0.31.1 + {{ pip }} install --no-deps mlx-audio==0.4.1 fi {{ pip }} install git+https://github.com/QwenLM/Qwen3-TTS.git {{ pip }} install pyinstaller ruff pytest pytest-asyncio -q