Skip to content

feat(vllm): macOS/Metal support via vllm-metal (MLX) - #10489

Merged
mudler merged 7 commits into
masterfrom
feat/darwin-vllm-metal
Jun 25, 2026
Merged

feat(vllm): macOS/Metal support via vllm-metal (MLX)#10489
mudler merged 7 commits into
masterfrom
feat/darwin-vllm-metal

Conversation

@localai-bot

@localai-bot localai-bot commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds macOS (Apple Silicon / Metal) support to the vllm backend via vllm-metal (MLX). On darwin, vLLM runs through vllm-metal's platform-plugin; LocalAI's backend.py generation path is unchanged. Linux/CUDA/ROCm/Intel paths are untouched (all darwin logic is additive, gated behind uname -s = Darwin).

What changed

  • backend/python/vllm/install.sh (darwin branch): forces PYTHON_VERSION=3.12, uses LocalAI's managed venv (ensureVenv), builds vLLM from the release source tarball, then installs the vllm-metal wheel; installRequirements is skipped on darwin.
    • One pin only — VLLM_METAL_VERSION. The coupled vLLM source version is derived at build time from vllm-metal's own installer (vllm_v=) at the pinned tag (no second hardcoded value to drift; reproducible since the tag is immutable).
    • The vllm-metal wheel is fetched via a deterministic release-asset URL (tag + cp312/arm64 wheel name), not the GitHub API — avoids the unauthenticated API rate limit (60/hr) that 403s on shared CI runners.
  • .github/bump_vllm_metal.sh + bump_deps.yaml: a new autobumper job that tracks vllm-project/vllm-metal (not vllm/vllm latest — darwin can only use the vLLM vllm-metal supports) and opens a bump PR for VLLM_METAL_VERSION. Mirrors the existing bump_vllm_wheel.sh.
  • .github/backend-matrix.yml: vllm added under includeDarwin:.
  • backend/index.yaml: metal: capability + concrete metal-vllm / metal-vllm-development entries.
  • backend.py (one defensive guard): Score now returns UNIMPLEMENTED when the engine yields no usable prompt_logprobs, instead of silently scoring every candidate as 0 (see audit). No-op on Linux/CUDA.

Validated

  • CI darwin build: green — the full path runs on a macOS runner (portable cp312 → vLLM 0.23.0 built from source → vLLM version derived from the pinned tag → vllm-metal wheel installed via the deterministic URL → packaged).
  • Live on a real M4 / macOS 26.5: vllm-metal registers as a platform plugin (metal -> vllm_metal:register, MetalPlatform, MLX GPU); AsyncLLMEngine.from_engine_args transparently resolves to vLLM 0.23's v1 MLX engine; async generate produced correct output on Qwen/Qwen3-0.6B.
  • Autobumper dry-run: from a stale pin it advanced VLLM_METAL_VERSION to the latest release and left the derived line untouched.

backend.py audit (Score / Embeddings vs the v1 MLX engine)

  • Score (prompt_logprobs): not supported on Metal. Live test: vllm-metal accepts SamplingParams(prompt_logprobs=1) but returns an all-None list rather than computing it. The previous guard treated the truthy [None] as valid → silent all-zero scores. Now it returns UNIMPLEMENTED. Generation/chat is unaffected.
  • Embedding: pre-existing, platform-independent. Embedding() calls self.model.encode(), but self.model is never assigned anywhere in backend.py (identical on master) — it raises AttributeError on every platform, CUDA included. This is not a darwin regression; the vllm backend is generation-only and embeddings are served by other backends. Out of scope here.

Version coupling

vllm-metal builds against a specific vLLM release; darwin follows vllm-metal and can lag the Linux vllm pin (requirements-cublas13-after.txt, bumped independently) until vllm-metal supports a newer vLLM. The single VLLM_METAL_VERSION pin + autobumper keep this tracked and reproducible.

Assisted-by: Claude:opus-4.8 [Claude Code]

mudler added 5 commits June 24, 2026 17:17
Add an additive Apple-Silicon path to the existing vllm Python backend so
vLLM runs on macOS via vllm-metal (github.com/vllm-project/vllm-metal).

Spike outcome (proven on a real M4 / macOS 26.5, Qwen3-0.6B):
- vllm-metal registers through vLLM's platform-plugin entry point
  (metal -> vllm_metal:register); MetalPlatform activates and runs on the
  GPU through MLX.
- LocalAI's backend.py is UNCHANGED: AsyncEngineArgs(...) ->
  AsyncLLMEngine.from_engine_args transparently resolves to vLLM 0.23's v1
  AsyncLLM MLX engine, and async generate produced correct output.
- backend.py is NOT touched: its only empty_cache() call is CUDA-only
  (guarded by torch.cuda.is_available()), so the benign shutdown-only
  "Allocator for mps is not a DeviceAllocator" noise comes from vLLM's
  internal EngineCore teardown, not from our code.

Changes (all gated behind a darwin condition; Linux/CUDA/ROCm/Intel paths
are byte-for-byte unchanged):
- install.sh: darwin branch forces PYTHON_VERSION=3.12 (vllm-metal
  requirement), creates/activates LocalAI's managed venv via ensureVenv,
  then reproduces vllm-metal's installer INTO that venv (build vLLM 0.23.0
  from the release source tarball against requirements/cpu.txt, then install
  the prebuilt vllm-metal wheel from its latest GitHub release), and runs
  runProtogen. installRequirements is skipped on darwin.
- backend-matrix.yml: add a vllm includeDarwin entry (mps, python).
- index.yaml: add metal capability + concrete metal-vllm /
  metal-vllm-development child entries mirroring the metal-kitten-tts
  template.

Version coupling: vllm-metal pins vLLM 0.23.0, equal to LocalAI's current
vllm pin. Bumping vllm must be coordinated with a supporting vllm-metal
release; documented in install.sh and requirements-cublas13-after.txt.

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Assisted-by: Claude:opus-4.8 [Claude Code]
The Apple Silicon build pinned vLLM 0.23.0 as a hidden string in install.sh
while floating the vllm-metal wheel on releases/latest - the two could drift
apart silently. Make both a tracked, reproducible pair (VLLM_METAL_VERSION +
VLLM_VERSION), fetch the wheel by tag, and add .github/bump_vllm_metal.sh wired
into bump_deps.yaml. It tracks vllm-project/vllm-metal (not vllm/vllm latest),
reading the coupled vLLM source version from vllm-metal's own installer, and
opens a bump PR - mirroring the existing bump_vllm_wheel.sh for the cu130 wheel.

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Assisted-by: Claude:opus-4.8 [Claude Code]
Follow-up: VLLM_VERSION was still a hardcoded string duplicating what
VLLM_METAL_VERSION already determines. Derive it at install time from
vllm-metal's own installer (vllm_v=) at the pinned tag - one source of truth,
no second value to drift. The bumper now touches only VLLM_METAL_VERSION;
the derivation is immutable per tag, so builds stay reproducible.

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Assisted-by: Claude:opus-4.8 [Claude Code]
The darwin build resolved the wheel URL via api.github.com, whose
unauthenticated rate limit (60/hr per IP) 403s on shared macOS runners
(observed after the 9-min vLLM source build). Construct the release-asset
download URL deterministically from the pinned tag and the cp312/arm64 wheel
name instead - no API call, no rate limit. Verified the URL resolves (200).

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Assisted-by: Claude:opus-4.8 [Claude Code]
Audit of the Score path against vllm-metal (MLX on macOS): the engine accepts
SamplingParams(prompt_logprobs=1) but returns an all-None prompt_logprobs list
rather than computing it, so scoring is not supported there. The old guard
treated the truthy [None] list as valid and silently scored every candidate as
0. Detect the all-None case and return UNIMPLEMENTED instead. No-op on
Linux/CUDA, which populate real entries.

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Assisted-by: Claude:opus-4.8 [Claude Code]
@localai-bot
localai-bot marked this pull request as ready for review June 24, 2026 21:32
mudler added 2 commits June 24, 2026 22:15
Resolve includeDarwin conflict in backend-matrix.yml: keep both the vllm and
the newly-merged liquid-audio darwin entries (additive).

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Assisted-by: Claude:opus-4.8 [Claude Code]
Resolve includeDarwin conflict: keep the vllm and merged trl darwin entries.

Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Assisted-by: Claude:opus-4.8 [Claude Code]
@mudler
mudler merged commit 3a87d9e into master Jun 25, 2026
76 of 77 checks passed
@mudler
mudler deleted the feat/darwin-vllm-metal branch June 25, 2026 13:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants