fix: made the macos app smaller

This commit is contained in:
2026-07-18 10:20:54 +02:00
parent c77450d497
commit 593f1646a1
8 changed files with 166 additions and 32 deletions

View File

@@ -32,6 +32,11 @@ defmodule BDS.Embeddings.Backends.Neural do
* Everywhere else — and as a fallback when EMLX is unavailable or explicitly
disabled — it runs on optimised native CPU via XLA (`compiler: EXLA`).
Packaged releases contain only the backend that can run on their platform
(`runtime:` conditional deps in mix.exs): macOS releases ship EMLX and no
EXLA, Linux/Windows ship EXLA and no EMLX (MLX is Apple-only). The selected
backend is therefore started on demand here, never assumed at boot.
The accelerator can be pinned with `config :bds, :embeddings, accelerator:`
to `:auto` (default), `:emlx`, or `:exla`.
"""
@@ -171,7 +176,20 @@ defmodule BDS.Embeddings.Backends.Neural do
@doc false
@spec current_accelerator() :: :emlx | :exla
def current_accelerator do
select_accelerator(configured_accelerator(), emlx_available?(), apple_silicon?())
case select_accelerator(
configured_accelerator(),
emlx_available?(),
exla_available?(),
apple_silicon?()
) do
:none ->
raise RuntimeError,
"no embeddings accelerator available: neither the EMLX (Apple GPU) nor the " <>
"EXLA (CPU) application could be started on this machine"
accelerator ->
accelerator
end
end
@doc """
@@ -179,22 +197,43 @@ defmodule BDS.Embeddings.Backends.Neural do
Prefer the Apple GPU (EMLX) under `:auto` only when it is both available and
running on Apple Silicon; honour an explicit `:emlx`/`:exla` request, but
degrade a forced `:emlx` to EXLA when EMLX is not loaded so a misconfigured
host still gets working CPU inference instead of crashing.
degrade to the other backend when the requested one is not available so a
misconfigured host still gets working inference instead of crashing. Only
one backend ships per platform (macOS releases exclude EXLA, Linux/Windows
exclude EMLX), so the unavailable side is the norm in production, not an
edge case.
"""
@spec select_accelerator(:auto | :emlx | :exla, boolean(), boolean()) :: :emlx | :exla
def select_accelerator(:exla, _emlx_available?, _apple_silicon?), do: :exla
def select_accelerator(:emlx, true, _apple_silicon?), do: :emlx
def select_accelerator(:emlx, false, _apple_silicon?), do: :exla
def select_accelerator(:auto, true, true), do: :emlx
def select_accelerator(:auto, _emlx_available?, _apple_silicon?), do: :exla
@spec select_accelerator(:auto | :emlx | :exla, boolean(), boolean(), boolean()) ::
:emlx | :exla | :none
def select_accelerator(_configured, false, false, _apple_silicon?), do: :none
def select_accelerator(:exla, _emlx?, true, _apple_silicon?), do: :exla
def select_accelerator(:exla, true, false, _apple_silicon?), do: :emlx
def select_accelerator(:emlx, true, _exla?, _apple_silicon?), do: :emlx
def select_accelerator(:emlx, false, true, _apple_silicon?), do: :exla
def select_accelerator(:auto, true, _exla?, true), do: :emlx
def select_accelerator(:auto, _emlx?, true, _apple_silicon?), do: :exla
def select_accelerator(:auto, true, false, _apple_silicon?), do: :emlx
defp configured_accelerator do
config() |> Keyword.get(:accelerator, @default_accelerator)
end
# The backend apps are runtime: false deps on the platforms that can't run
# them, so releases exclude one of them entirely and neither is auto-started
# in dev/test. Availability therefore means "the app actually starts here":
# ensure_all_started errors when the app is absent (or its NIF can't load).
# Starting the app is also exactly what the chosen backend needs before
# building the serving.
defp emlx_available? do
Code.ensure_loaded?(EMLX) and Code.ensure_loaded?(EMLX.Backend)
started?(:emlx) and Code.ensure_loaded?(EMLX.Backend)
end
defp exla_available? do
started?(:exla)
end
defp started?(app) do
match?({:ok, _apps}, Application.ensure_all_started(app))
end
defp apple_silicon? do