Skip to content

Commit 6017468

Browse files
authored
Merge branch 'master' into fix/9287-backend-autodetect
2 parents 69e482b + 8aa8e0f commit 6017468

1,110 files changed

Lines changed: 112679 additions & 35957 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/adding-backends.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,24 @@ Multi-arch backends are NOT a single matrix entry with `platforms: 'linux/amd64,
102102

103103
Entries whose `dockerfile` is `./backend/Dockerfile.{llama-cpp,ik-llama-cpp,turboquant}` must also set a `builder-base-image` field pointing at a prebuilt base from `quay.io/go-skynet/ci-cache:base-grpc-*` (CI builds these via `.github/workflows/base-images.yml`). The mapping is by `(build-type, platforms)` — see existing entries for the pattern. CI uses these prebuilt bases to skip the gRPC compile (~25–35 min cold). Local `make backends/<name>` ignores `builder-base-image` and uses the from-source path inside the Dockerfile, so you don't need quay access for local builds.
104104

105+
### Cover every OS the project supports (Linux **and** Darwin)
106+
107+
`.github/backend-matrix.yml` has two matrices, and they are the source of truth for which OS a backend ships on:
108+
109+
- `include:` — the **Linux** matrix (x86_64 + arm64; CPU and CUDA / ROCm / SYCL / Vulkan).
110+
- `includeDarwin:` — the **macOS / Apple Silicon** matrix (arm64; Metal where the engine supports it, otherwise a native arm64 CPU build).
111+
112+
**A new backend must target every OS it can build for — do not ship Linux-only by default.** A backend that appears only under `include:` is silently unavailable on macOS even when its code would run there. Most C/C++/GGML engines build on Darwin out of the box (ggml defaults `GGML_METAL=ON` on Apple, so a plain build is Metal-enabled), and many Python backends do too (CPU / MPS wheels). If a backend genuinely cannot support an OS (e.g. CUDA-only, no CPU variant), state that in the PR description instead of omitting it silently.
113+
114+
Wiring a backend into `includeDarwin:` is more than the matrix entry:
115+
116+
1. **`includeDarwin:` entry** — `tag-suffix: "-metal-darwin-arm64-<backend>"`, `build-type: "metal"`, `lang: "go"` for go+ggml backends; omit `build-type` for the bespoke C++ ones (llama-cpp / ds4 / privacy-filter). Match an existing entry of the same shape.
117+
2. **`backend/index.yaml`** — add `metal:` to the backend's `capabilities` map (main and `-development`) and concrete `metal-<backend>` / `metal-<backend>-development` image entries pointing at the `-metal-darwin-arm64-<backend>` images.
118+
3. **C/C++ backends only** — add an `inferBackendPathDarwin` case in `scripts/changed-backends.js` returning `backend/cpp/<backend>/` (the generic fallthrough assumes `backend/<lang>/`, which is wrong for a C++ source tree driven with `lang: go`), and give `run.sh` a Darwin branch that exports `DYLD_LIBRARY_PATH` instead of `LD_LIBRARY_PATH`. If the build is bespoke (single `grpc-server` + dylib bundling), model it on `scripts/build/ds4-darwin.sh` and add a `backends/<backend>-darwin` make target plus a gated step in `.github/workflows/backend_build_darwin.yml`.
119+
4. **C++ proto gotcha** — if the backend compiles the generated gRPC/protobuf in a separate CMake target (e.g. `hw_grpc_proto`), that target must link `protobuf::libprotobuf` + `gRPC::grpc++` so the Homebrew include dirs propagate; otherwise macOS fails with `google/protobuf/runtime_version.h not found` (Linux hides this because apt headers sit in `/usr/include`).
120+
121+
The CI path filter only builds a backend on a PR when a file under its directory changes, so a darwin-only YAML edit builds nothing — touch a file under `backend/<lang>/<backend>/` (a one-line comment is enough) in the same PR.
122+
105123
## 3. Add Backend Metadata to `backend/index.yaml`
106124

107125
**Step 3a: Add Meta Definition**
@@ -198,19 +216,44 @@ docker-build-backends: ... docker-build-<backend-name>
198216
- If the backend is in `backend/python/<backend-name>/` but uses `.` as context in the workflow file, use `.` context
199217
- Check similar backends to determine the correct context
200218

219+
## Documenting the backend (README + docs)
220+
221+
A backend is not "added" until it is discoverable. Update the user-facing docs:
222+
223+
- **`docs/content/features/backends.md`** - add the backend to the right
224+
category in the "LocalAI supports various types of backends" list (and add a
225+
new category if it introduces a new modality, e.g. sound classification).
226+
- If the backend introduces a **new API surface** (a new endpoint or a realtime
227+
capability), document it under `docs/content/` where its area lives (audio,
228+
vision, etc.) and follow the api-endpoints checklist in
229+
[api-endpoints-and-auth.md](api-endpoints-and-auth.md).
230+
231+
**If the backend is a native C/C++/GGML engine created and maintained by the
232+
LocalAI team** (a from-scratch port like `parakeet.cpp`, `ced.cpp`,
233+
`vibevoice.cpp`, `rf-detr.cpp`, not a wrapper around a third-party runtime), it
234+
ALSO belongs in the top-level **`README.md`** table under "native C/C++/GGML
235+
engines ... developed and maintained by the LocalAI project itself". Add a row
236+
linking the upstream engine repo with a one-line description. This is the
237+
project's showcase of its own engines; a new in-house backend that is missing
238+
from it is a documentation bug.
239+
201240
## 5. Verification Checklist
202241

203242
After adding a new backend, verify:
204243

205244
- [ ] Backend directory structure is complete with all necessary files
206245
- [ ] Build configurations added to `.github/backend-matrix.yml` for all desired platforms (per-arch entries with `platform-tag` for multi-arch; `builder-base-image` for llama-cpp / ik-llama-cpp / turboquant)
246+
- [ ] **OS coverage considered**: added to `includeDarwin:` (macOS/Apple Silicon) if the backend can build there — with the `backend/index.yaml` `metal:` capability + `metal-<backend>` image entries, a `run.sh` Darwin/DYLD branch and `inferBackendPathDarwin` case for C++ backends — or the PR explains why an OS is unsupported. Do not ship Linux-only by default.
207247
- [ ] Meta definition added to `backend/index.yaml` in the `## metas` section
208248
- [ ] Image entries added to `backend/index.yaml` for all build variants (latest + development)
209249
- [ ] Tag suffixes match between workflow file and index.yaml
210250
- [ ] Makefile updated with all 6 required changes (`.NOTPARALLEL`, `prepare-test-extra`, `test-extra`, backend definition, docker-build target eval, `docker-build-backends`)
211251
- [ ] No YAML syntax errors (check with linter)
212252
- [ ] No Makefile syntax errors (check with linter)
213253
- [ ] Follows the same pattern as similar backends (e.g., if it's a transcription backend, follow `faster-whisper` pattern)
254+
- [ ] **`Load` validates its input and refuses models it can't serve.** When a model config has no explicit `backend:`, the model loader greedily probes *every* installed backend with the model's name and binds to the first `Load` that succeeds — an accept-anything `Load` will capture arbitrary LLMs (issue #9287). Backends that load a real artefact get this for free (the load fails); backends with no artefact must gate on the name: `opus` accepts only its own name (or none), `local-store` requires the `store.NamespacePrefix` namespace marker sent by `core/backend/stores.go`.
255+
- [ ] Documented: added to the category list in `docs/content/features/backends.md` (and any new endpoint/realtime capability documented under `docs/content/`)
256+
- [ ] If it is an in-house native C/C++/GGML engine, added to the maintained-engines table in the top-level `README.md`
214257

215258
## Bundling runtime shared libraries (`package.sh`)
216259

.agents/ds4-backend.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,39 @@ maps to `DS4_THINK_HIGH`. We pass the chosen mode to `ds4_chat_append_assistant_
4444
via `ModelOptions.Options[] = "kv_cache_dir:/some/path"`. Format is **our own** -
4545
NOT bit-compatible with ds4-server's KVC files (interop is a follow-up plan).
4646

47+
## Engine options (LoadModel)
48+
49+
`LoadModel` maps `ModelOptions.Options[]` (`"key:value"`, from model-YAML
50+
`options:`) onto `ds4_engine_options` through a **declarative table**
51+
(`kEngineOptSpecs` + `apply_engine_option` in `grpc-server.cpp`). The struct is
52+
plain C with no reflection, so the field set is enumerated once in the table;
53+
adding a future engine knob is a one-line table row, not a new branch. Unknown
54+
keys are ignored (back-compat). A bare flag (`ssd_streaming` with no value)
55+
means `true`. Path-type values (`mtp_path`, `expert_profile_path`,
56+
`directional_steering_file`) resolve **relative to the model directory**, so a
57+
gallery entry can reference a companion file it downloaded by bare filename;
58+
absolute values pass through. `ds4_role` / `ds4_layers` / `ds4_listen` /
59+
`ds4_route_timeout` / `kv_cache_dir` keep their dedicated handling (validation
60+
+ coordinator wiring) and are not in the table.
61+
62+
Wired keys: `mtp_path`, `mtp_draft`, `mtp_margin`, `prefill_chunk`,
63+
`power_percent`, `warm_weights`, `quality`, `ssd_streaming`,
64+
`ssd_streaming_cold`, `ssd_streaming_preload_experts`,
65+
`ssd_streaming_cache_experts` (count or `NGB`, sets both experts+bytes via
66+
`ds4_parse_streaming_cache_experts_arg`), `simulate_used_memory` (`NGB` via
67+
`ds4_parse_gib_arg`), `expert_profile_path`, `directional_steering_file`,
68+
`directional_steering_attn`, `directional_steering_ffn`.
69+
70+
## SSD streaming (running models larger than RAM)
71+
72+
ds4's **SSD streaming** keeps non-routed weights resident and streams routed MoE
73+
experts from the GGUF on cache misses, turning "does it fit in RAM" into a speed
74+
spectrum. **Metal (Darwin) only** - it is a no-op on CUDA/CPU. Enable with
75+
`options: ["ssd_streaming"]`; size the routed-expert cache with
76+
`ssd_streaming_cache_experts:NGB` (omit for ds4's automatic 80%-of-working-set
77+
budget). Gallery entries built on this: `deepseek-v4-flash-q4-ssd` (153 GB Flash
78+
on a 128 GB Mac) and `deepseek-v4-pro-q2-ssd` (433 GB Pro, experimental).
79+
4780
## Build matrix
4881

4982
| Build | Where | Notes |

.docker/bonsai-compile.sh

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/env bash
2+
# Shared compile logic for backend/Dockerfile.bonsai.
3+
# Sourced (via bind mount) from both builder-fromsource and builder-prebuilt stages.
4+
5+
set -euxo pipefail
6+
7+
export CCACHE_DIR=/root/.ccache
8+
ccache --max-size=5G || true
9+
ccache -z || true
10+
11+
export CMAKE_ARGS="${CMAKE_ARGS:-} -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_CUDA_COMPILER_LAUNCHER=ccache"
12+
13+
if [[ -n "${CUDA_DOCKER_ARCH:-}" ]]; then
14+
CUDA_ARCH_ESC="${CUDA_DOCKER_ARCH//;/\\;}"
15+
export CMAKE_ARGS="${CMAKE_ARGS} -DCMAKE_CUDA_ARCHITECTURES=${CUDA_ARCH_ESC}"
16+
echo "CMAKE_ARGS(env) = ${CMAKE_ARGS}"
17+
rm -rf /LocalAI/backend/cpp/bonsai-*-build
18+
fi
19+
20+
cd /LocalAI/backend/cpp/bonsai
21+
22+
if [ -z "${BUILD_TYPE:-}" ]; then
23+
# Pure CPU image: one ggml CPU_ALL_VARIANTS build replaces the per-microarch binaries.
24+
# arm64: the armv9.2 SME variants need gcc-14 (gcc-13 rejects +sme).
25+
if [ "${TARGETARCH}" = "arm64" ]; then
26+
apt-get update -qq && apt-get install -y -qq gcc-14 g++-14
27+
export CC=gcc-14 CXX=g++-14
28+
fi
29+
make bonsai-cpu-all
30+
else
31+
# GPU build (cublas/hipblas/sycl/vulkan/...): single fallback CPU build, the accelerator
32+
# does the compute. Keeps the GPU compile from also building the CPU variant matrix and
33+
# avoids the gcc-14 apt step on GPU base images such as nvidia l4t.
34+
make bonsai-fallback
35+
fi
36+
make bonsai-grpc
37+
make bonsai-rpc-server
38+
39+
ccache -s || true

.docker/install-base-deps.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ if [ "${BUILD_TYPE:-}" = "vulkan" ] && [ "${SKIP_DRIVERS:-false}" = "false" ]; t
7070
git python-is-python3 bison libx11-xcb-dev liblz4-dev libzstd-dev \
7171
ocaml-core ninja-build pkg-config libxml2-dev wayland-protocols python3-jsonschema \
7272
clang-format qtbase5-dev qt6-base-dev libxcb-glx0-dev sudo xz-utils
73+
# Mesa Vulkan ICD drivers (ANV/RADV/lavapipe + Arm SoC) and their ICD
74+
# manifests. The LunarG SDK below only provides the loader and shader
75+
# tooling, not hardware drivers — without Mesa the packaged Vulkan backend
76+
# would ship a loader that finds no GPU. package-gpu-libs.sh bundles these
77+
# .so files plus their deps into the backend so it stays self-contained.
78+
apt-get install -y mesa-vulkan-drivers libdrm2
7379
if [ "amd64" = "${TARGETARCH:-}" ]; then
7480
wget "https://sdk.lunarg.com/sdk/download/1.4.335.0/linux/vulkansdk-linux-x86_64-1.4.335.0.tar.xz"
7581
tar -xf vulkansdk-linux-x86_64-1.4.335.0.tar.xz

.docker/llama-cpp-compile.sh

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,29 @@ if [[ -n "${CUDA_DOCKER_ARCH:-}" ]]; then
1717
rm -rf /LocalAI/backend/cpp/llama-cpp-*-build
1818
fi
1919

20-
if [ "${TARGETARCH}" = "arm64" ] || [ "${BUILD_TYPE}" = "hipblas" ]; then
21-
cd /LocalAI/backend/cpp/llama-cpp
22-
make llama-cpp-fallback
23-
make llama-cpp-grpc
24-
make llama-cpp-rpc-server
20+
cd /LocalAI/backend/cpp/llama-cpp
21+
if [ -z "${BUILD_TYPE:-}" ]; then
22+
# Pure CPU image (BUILD_TYPE empty): one build with ggml CPU_ALL_VARIANTS replaces the
23+
# per-microarch binaries (x86: avx/avx2/avx512/fallback; arm64: armv8.x/armv9.x). ggml
24+
# dlopens the best libggml-cpu-*.so at runtime by probing host CPU features.
25+
#
26+
# arm64: the CPU_ALL_VARIANTS table includes armv9.2 SME variants whose -march=...+sme is
27+
# rejected by the Ubuntu 24.04 default gcc-13. gcc-14 accepts it, so build the arm64
28+
# variants with it (the host never *selects* SME unless it has it, but every variant must
29+
# still compile).
30+
if [ "${TARGETARCH}" = "arm64" ]; then
31+
apt-get update -qq && apt-get install -y -qq gcc-14 g++-14
32+
export CC=gcc-14 CXX=g++-14
33+
fi
34+
make llama-cpp-cpu-all
2535
else
26-
cd /LocalAI/backend/cpp/llama-cpp
27-
make llama-cpp-avx
28-
make llama-cpp-avx2
29-
make llama-cpp-avx512
36+
# GPU build (cublas/hipblas/sycl/vulkan/...): the accelerator does the compute, so a
37+
# single fallback CPU build is enough - no per-microarch CPU variants needed. (This also
38+
# keeps the heavy GPU backend compile from also building the whole CPU variant matrix,
39+
# and avoids the gcc-14 apt step on GPU base images such as nvidia l4t.)
3040
make llama-cpp-fallback
31-
make llama-cpp-grpc
32-
make llama-cpp-rpc-server
3341
fi
42+
make llama-cpp-grpc
43+
make llama-cpp-rpc-server
3444

3545
ccache -s || true

.docker/turboquant-compile.sh

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,21 @@ fi
1919

2020
cd /LocalAI/backend/cpp/turboquant
2121

22-
if [ "${TARGETARCH}" = "arm64" ] || [ "${BUILD_TYPE}" = "hipblas" ]; then
23-
make turboquant-fallback
24-
make turboquant-grpc
25-
make turboquant-rpc-server
22+
if [ -z "${BUILD_TYPE:-}" ]; then
23+
# Pure CPU image: one ggml CPU_ALL_VARIANTS build replaces the per-microarch binaries.
24+
# arm64: the armv9.2 SME variants need gcc-14 (gcc-13 rejects +sme).
25+
if [ "${TARGETARCH}" = "arm64" ]; then
26+
apt-get update -qq && apt-get install -y -qq gcc-14 g++-14
27+
export CC=gcc-14 CXX=g++-14
28+
fi
29+
make turboquant-cpu-all
2630
else
27-
make turboquant-avx
28-
make turboquant-avx2
29-
make turboquant-avx512
31+
# GPU build (cublas/hipblas/sycl/vulkan/...): single fallback CPU build, the accelerator
32+
# does the compute. Keeps the GPU compile from also building the CPU variant matrix and
33+
# avoids the gcc-14 apt step on GPU base images such as nvidia l4t.
3034
make turboquant-fallback
31-
make turboquant-grpc
32-
make turboquant-rpc-server
3335
fi
36+
make turboquant-grpc
37+
make turboquant-rpc-server
3438

3539
ccache -s || true

.dockerignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ backend/python/**/source
3131
backend/cpp/llama-cpp/llama.cpp
3232
backend/cpp/llama-cpp-*-build
3333

34+
# privacy-filter: same in-place pattern. The Makefile fetches privacy-filter.cpp
35+
# at the pinned commit (or symlinks a PRIVACY_FILTER_SRC checkout for local dev).
36+
# A stale dir/symlink COPY'd into the image makes the clone step fail (dangling
37+
# symlink) or compile against the wrong commit, so keep host build state out.
38+
backend/cpp/privacy-filter/privacy-filter.cpp
39+
backend/cpp/privacy-filter/build
40+
backend/cpp/privacy-filter/grpc-server
41+
backend/cpp/privacy-filter/package
42+
3443
# Rust backend build output (sources are tracked; target/ is generated)
3544
backend/rust/*/target
3645

.githooks/pre-commit

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@
77
# Runs only the checks relevant to what's staged:
88
# - Go files -> make lint + make test-coverage-check
99
# - core/http/react-ui -> make test-ui-coverage-check (Playwright e2e + gate)
10-
# A commit touching neither is skipped entirely (docs/YAML/etc. can't change
11-
# lint findings, Go coverage, or the UI).
10+
# - realtime state machines / specs -> make test-realtime-conformance
11+
# (respcoord/**, turncoord/**, or formal-verification/** -- a pure .fizz
12+
# spec edit must still re-verify the design, detected separately from Go)
13+
# A commit touching none of these is skipped entirely (other docs/YAML can't
14+
# change lint findings, Go coverage, the UI, or the realtime conformance gate).
1215
#
1316
# To bypass for a single commit (e.g. a WIP checkpoint): git commit --no-verify
1417
set -eu
@@ -20,11 +23,13 @@ staged="$(git diff --cached --name-only --diff-filter=ACMRD)"
2023

2124
go_changed=0
2225
ui_changed=0
26+
rt_changed=0
2327
if echo "$staged" | grep -qE '\.go$'; then go_changed=1; fi
2428
if echo "$staged" | grep -qE '^core/http/react-ui/'; then ui_changed=1; fi
29+
if echo "$staged" | grep -qE '^(core/http/endpoints/openai/(coordinator|respcoord|turncoord|conncoord|compactcoord|ttscoord)/|formal-verification/)'; then rt_changed=1; fi
2530

26-
if [ "$go_changed" -eq 0 ] && [ "$ui_changed" -eq 0 ]; then
27-
echo "pre-commit: no Go or React UI changes staged — skipping."
31+
if [ "$go_changed" -eq 0 ] && [ "$ui_changed" -eq 0 ] && [ "$rt_changed" -eq 0 ]; then
32+
echo "pre-commit: no Go, React UI, or realtime-spec changes staged — skipping."
2833
exit 0
2934
fi
3035

@@ -57,4 +62,11 @@ if [ "$ui_changed" -eq 1 ]; then
5762
make test-ui-coverage-check
5863
fi
5964

65+
if [ "$rt_changed" -eq 1 ]; then
66+
echo "pre-commit ▶ realtime state-machine conformance (make test-realtime-conformance) —"
67+
echo " Go transition/rapid tests under -race + FizzBee model check of the"
68+
echo " authoritative specs. Fail-closed: needs FizzBee (make install-fizzbee)."
69+
make test-realtime-conformance
70+
fi
71+
6072
echo "pre-commit ✓ all relevant checks passed"

0 commit comments

Comments
 (0)