backend(audio-cpp): register the backend with CI and the gallery

Adds the five Linux matrix entries (cpu amd64/arm64 sharing a tag-suffix so the
manifest merge fires, cuda 12, cuda 13, vulkan), the path-filter case that keeps
later PRs touching backend/cpp/audio-cpp/ from getting zero CI jobs, the
bump-bot entry pointing at the AUDIO_CPP_VERSION pin in the backend Makefile,
the gallery meta plus its -development variant and the image entries for every
variant, and the Makefile docker-build wiring.

The matrix entries carry base-image only, with no builder-base-image, unlike
the llama-cpp and privacy-filter blocks they sit next to. The prebuilt
quay.io/go-skynet/ci-cache:base-grpc-* images ship a from-source gRPC whose
protobuf v26 depends on abseil, and this backend's sentencepiece is built with
SPM_PROTOBUF_PROVIDER=package, so it sees real abseil's absl::lts_20240116::
internal alongside its own vendored plain absl::internal and every
absl::internal:: reference becomes ambiguous. Building against base-grpc-amd64
fails at sentencepiece-static.dir/error.cc.o with "reference to 'internal' is
ambiguous". Dockerfile.audio-cpp installs apt's gRPC/protobuf 3.21.12 itself,
which is also the pair every unit and end-to-end run of this backend has been
verified against, and the CUDA toolkit therefore has to come from base-image.

No Darwin matrix entry and no metal gallery entries: the Metal build needs
scripts/build/audio-cpp-darwin.sh, a backends/audio-cpp-darwin make target and
a routing step in backend_build_darwin.yml, none of which exist yet, so an
entry added now would be routed to build-darwin-go-backend and look for
backend/go/audio-cpp/. The inferBackendPathDarwin case and the
DARWIN_BESPOKE_BUILDERS membership are in place, inert, so that adding the
entry later is a one-line change that cannot be claimed by the generic Go path.

Assisted-by: Claude:claude-opus-5 [Claude Code]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
Ettore Di Giacinto
2026-07-27 03:30:22 +00:00
parent 3b2ef6e69f
commit ea93697686
6 changed files with 253 additions and 4 deletions

View File

@@ -3037,6 +3037,100 @@ include:
dockerfile: "./backend/Dockerfile.privacy-filter"
context: "./"
ubuntu-version: '2404'
# audio-cpp: 0xShug0/audio.cpp, a multi-family ggml audio engine (TTS, ASR,
# VAD, diarization, source separation, music generation).
#
# These entries deliberately carry NO builder-base-image, unlike the
# privacy-filter and llama-cpp blocks above. The prebuilt
# quay.io/go-skynet/ci-cache:base-grpc-* images ship a from-source gRPC whose
# protobuf is v26, and protobuf has depended on abseil since v22. audio.cpp
# links sentencepiece with SPM_PROTOBUF_PROVIDER=package (needed to stop
# sentencepiece's vendored protobuf 3.14 from colliding with the 3.21 the
# generated backend.pb.cc is built against, which broke every nested-message
# parse), so sentencepiece then sees real abseil's
# `absl::lts_20240116::internal` alongside its own vendored plain
# `absl::internal` and every `absl::internal::` reference becomes ambiguous.
# Verified, not theorised: building against base-grpc-amd64 fails at
# sentencepiece-static.dir/error.cc.o with "reference to 'internal' is
# ambiguous". Dockerfile.audio-cpp therefore installs Ubuntu Noble's apt
# gRPC/protobuf 3.21.12 itself and has a single `builder` stage, so the
# BUILDER_BASE_IMAGE / BUILDER_TARGET / SKIP_DRIVERS build-args are never
# consumed. Same reason CUDA needs its toolkit in base-image rather than in a
# builder image: this is the ds4 shape, not the llama-cpp one.
#
# No ROCm entry: upstream has no HIP configuration. No CUDA arm64 or L4T
# entry: upstream documents and validates CUDA on x86 only. Darwin/Metal
# lands with scripts/build/audio-cpp-darwin.sh, which does not exist yet.
- build-type: ''
cuda-major-version: ""
cuda-minor-version: ""
platforms: 'linux/amd64'
platform-tag: 'amd64'
tag-latest: 'auto'
tag-suffix: '-cpu-audio-cpp'
runs-on: 'ubuntu-latest'
base-image: "ubuntu:24.04"
skip-drivers: 'true'
backend: "audio-cpp"
dockerfile: "./backend/Dockerfile.audio-cpp"
context: "./"
ubuntu-version: '2404'
- build-type: ''
cuda-major-version: ""
cuda-minor-version: ""
platforms: 'linux/arm64'
platform-tag: 'arm64'
tag-latest: 'auto'
tag-suffix: '-cpu-audio-cpp'
runs-on: 'ubuntu-24.04-arm'
base-image: "ubuntu:24.04"
skip-drivers: 'true'
backend: "audio-cpp"
dockerfile: "./backend/Dockerfile.audio-cpp"
context: "./"
ubuntu-version: '2404'
- build-type: 'cublas'
cuda-major-version: "12"
cuda-minor-version: "8"
platforms: 'linux/amd64'
tag-latest: 'auto'
tag-suffix: '-gpu-nvidia-cuda-12-audio-cpp'
runs-on: 'ubuntu-latest'
base-image: "nvidia/cuda:12.8.1-devel-ubuntu24.04"
skip-drivers: 'true'
backend: "audio-cpp"
dockerfile: "./backend/Dockerfile.audio-cpp"
context: "./"
ubuntu-version: '2404'
- build-type: 'cublas'
cuda-major-version: "13"
cuda-minor-version: "0"
platforms: 'linux/amd64'
tag-latest: 'auto'
tag-suffix: '-gpu-nvidia-cuda-13-audio-cpp'
runs-on: 'ubuntu-latest'
base-image: "nvidia/cuda:13.0.0-devel-ubuntu24.04"
skip-drivers: 'true'
backend: "audio-cpp"
dockerfile: "./backend/Dockerfile.audio-cpp"
context: "./"
ubuntu-version: '2404'
# Vulkan: ubuntu:24.04 plus apt's libvulkan-dev + glslc, installed by
# Dockerfile.audio-cpp when BUILD_TYPE=vulkan. arm64 vulkan is a one-line add
# once amd64 is proven in CI.
- build-type: 'vulkan'
cuda-major-version: ""
cuda-minor-version: ""
platforms: 'linux/amd64'
tag-latest: 'auto'
tag-suffix: '-gpu-vulkan-audio-cpp'
runs-on: 'ubuntu-latest'
base-image: "ubuntu:24.04"
skip-drivers: 'false'
backend: "audio-cpp"
dockerfile: "./backend/Dockerfile.audio-cpp"
context: "./"
ubuntu-version: '2404'
- build-type: ''
cuda-major-version: ""
cuda-minor-version: ""

View File

@@ -30,6 +30,10 @@ jobs:
variable: "DS4_VERSION"
branch: "main"
file: "backend/cpp/ds4/Makefile"
- repository: "0xShug0/audio.cpp"
variable: "AUDIO_CPP_VERSION"
branch: "main"
file: "backend/cpp/audio-cpp/Makefile"
- repository: "meituan-longcat/LongCat-Video"
variable: "LONGCAT_VIDEO_VERSION"
branch: "main"

View File

@@ -1,5 +1,5 @@
# Disable parallel execution for backend builds
.NOTPARALLEL: backends/diffusers backends/llama-cpp backends/turboquant backends/bonsai backends/outetts backends/piper backends/stablediffusion-ggml backends/whisper backends/crispasr backends/parakeet-cpp backends/moss-transcribe-cpp backends/faster-whisper backends/silero-vad backends/local-store backends/cloud-proxy backends/huggingface backends/rfdetr backends/rfdetr-cpp backends/insightface backends/speaker-recognition backends/kitten-tts backends/kokoro backends/chatterbox backends/llama-cpp-darwin backends/neutts build-darwin-python-backend build-darwin-go-backend backends/mlx backends/diffuser-darwin backends/mlx-vlm backends/mlx-audio backends/mlx-distributed backends/stablediffusion-ggml-darwin backends/vllm backends/vllm-omni backends/longcat-video backends/sglang backends/moonshine backends/pocket-tts backends/qwen-tts backends/faster-qwen3-tts backends/qwen-asr backends/nemo backends/voxcpm backends/whisperx backends/ace-step backends/acestep-cpp backends/fish-speech backends/voxtral backends/opus backends/trl backends/llama-cpp-quantization backends/kokoros backends/sam3-cpp backends/qwen3-tts-cpp backends/moss-tts-cpp backends/magpie-tts-cpp backends/omnivoice-cpp backends/vibevoice-cpp backends/localvqe backends/tinygrad backends/sherpa-onnx backends/ds4 backends/ds4-darwin backends/liquid-audio backends/supertonic backends/depth-anything-cpp backends/privacy-filter backends/privacy-filter-darwin
.NOTPARALLEL: backends/diffusers backends/llama-cpp backends/turboquant backends/bonsai backends/outetts backends/piper backends/stablediffusion-ggml backends/whisper backends/crispasr backends/parakeet-cpp backends/moss-transcribe-cpp backends/faster-whisper backends/silero-vad backends/local-store backends/cloud-proxy backends/huggingface backends/rfdetr backends/rfdetr-cpp backends/insightface backends/speaker-recognition backends/kitten-tts backends/kokoro backends/chatterbox backends/llama-cpp-darwin backends/neutts build-darwin-python-backend build-darwin-go-backend backends/mlx backends/diffuser-darwin backends/mlx-vlm backends/mlx-audio backends/mlx-distributed backends/stablediffusion-ggml-darwin backends/vllm backends/vllm-omni backends/longcat-video backends/sglang backends/moonshine backends/pocket-tts backends/qwen-tts backends/faster-qwen3-tts backends/qwen-asr backends/nemo backends/voxcpm backends/whisperx backends/ace-step backends/acestep-cpp backends/fish-speech backends/voxtral backends/opus backends/trl backends/llama-cpp-quantization backends/kokoros backends/sam3-cpp backends/qwen3-tts-cpp backends/moss-tts-cpp backends/magpie-tts-cpp backends/omnivoice-cpp backends/vibevoice-cpp backends/localvqe backends/tinygrad backends/sherpa-onnx backends/ds4 backends/ds4-darwin backends/liquid-audio backends/supertonic backends/depth-anything-cpp backends/privacy-filter backends/privacy-filter-darwin backends/audio-cpp
GOCMD=go
GOTEST=$(GOCMD) test
@@ -1240,6 +1240,11 @@ BACKEND_DS4 = ds4|ds4|.|false|false
# openai-privacy-filter PII/NER token classifier) — the TokenClassify RPC for
# the PII redactor tier, on stock ggml with no llama.cpp carry-patches.
BACKEND_PRIVACY_FILTER = privacy-filter|privacy-filter|.|false|false
# audio-cpp wraps 0xShug0/audio.cpp, a multi-family ggml audio inference engine
# (TTS, ASR, VAD, diarization, source separation, music generation). Builds
# against apt gRPC/protobuf rather than a prebuilt base-grpc image; the reason
# is on the audio-cpp block in .github/backend-matrix.yml.
BACKEND_AUDIO_CPP = audio-cpp|audio-cpp|.|false|false
# Golang backends
BACKEND_PIPER = piper|golang|.|false|true
@@ -1340,6 +1345,7 @@ $(eval $(call generate-docker-build-target,$(BACKEND_TURBOQUANT)))
$(eval $(call generate-docker-build-target,$(BACKEND_BONSAI)))
$(eval $(call generate-docker-build-target,$(BACKEND_DS4)))
$(eval $(call generate-docker-build-target,$(BACKEND_PRIVACY_FILTER)))
$(eval $(call generate-docker-build-target,$(BACKEND_AUDIO_CPP)))
$(eval $(call generate-docker-build-target,$(BACKEND_PIPER)))
$(eval $(call generate-docker-build-target,$(BACKEND_LOCAL_STORE)))
$(eval $(call generate-docker-build-target,$(BACKEND_CLOUD_PROXY)))
@@ -1405,7 +1411,7 @@ $(eval $(call generate-docker-build-target,$(BACKEND_SUPERTONIC)))
docker-save-%: backend-images
docker save local-ai-backend:$* -o backend-images/$*.tar
docker-build-backends: docker-build-llama-cpp docker-build-ik-llama-cpp docker-build-turboquant docker-build-bonsai docker-build-ds4 docker-build-rerankers docker-build-vllm docker-build-vllm-omni docker-build-longcat-video docker-build-sglang docker-build-transformers docker-build-outetts docker-build-diffusers docker-build-kokoro docker-build-faster-whisper docker-build-crispasr docker-build-coqui docker-build-chatterbox docker-build-vibevoice docker-build-liquid-audio docker-build-moonshine docker-build-pocket-tts docker-build-qwen-tts docker-build-fish-speech docker-build-faster-qwen3-tts docker-build-qwen-asr docker-build-nemo docker-build-voxcpm docker-build-whisperx docker-build-ace-step docker-build-acestep-cpp docker-build-voxtral docker-build-mlx-distributed docker-build-trl docker-build-llama-cpp-quantization docker-build-tinygrad docker-build-kokoros docker-build-sam3-cpp docker-build-rfdetr-cpp docker-build-qwen3-tts-cpp docker-build-moss-tts-cpp docker-build-magpie-tts-cpp docker-build-omnivoice-cpp docker-build-vibevoice-cpp docker-build-localvqe docker-build-insightface docker-build-speaker-recognition docker-build-sherpa-onnx docker-build-cloud-proxy docker-build-supertonic docker-build-depth-anything-cpp docker-build-moss-transcribe-cpp docker-build-privacy-filter
docker-build-backends: docker-build-llama-cpp docker-build-ik-llama-cpp docker-build-turboquant docker-build-bonsai docker-build-ds4 docker-build-rerankers docker-build-vllm docker-build-vllm-omni docker-build-longcat-video docker-build-sglang docker-build-transformers docker-build-outetts docker-build-diffusers docker-build-kokoro docker-build-faster-whisper docker-build-crispasr docker-build-coqui docker-build-chatterbox docker-build-vibevoice docker-build-liquid-audio docker-build-moonshine docker-build-pocket-tts docker-build-qwen-tts docker-build-fish-speech docker-build-faster-qwen3-tts docker-build-qwen-asr docker-build-nemo docker-build-voxcpm docker-build-whisperx docker-build-ace-step docker-build-acestep-cpp docker-build-voxtral docker-build-mlx-distributed docker-build-trl docker-build-llama-cpp-quantization docker-build-tinygrad docker-build-kokoros docker-build-sam3-cpp docker-build-rfdetr-cpp docker-build-qwen3-tts-cpp docker-build-moss-tts-cpp docker-build-magpie-tts-cpp docker-build-omnivoice-cpp docker-build-vibevoice-cpp docker-build-localvqe docker-build-insightface docker-build-speaker-recognition docker-build-sherpa-onnx docker-build-cloud-proxy docker-build-supertonic docker-build-depth-anything-cpp docker-build-moss-transcribe-cpp docker-build-privacy-filter docker-build-audio-cpp
########################################################
### Mock Backend for E2E Tests

View File

@@ -129,6 +129,35 @@
nvidia-l4t-cuda-13: "cuda13-nvidia-l4t-arm64-ds4"
metal: "metal-ds4"
metal-darwin-arm64: "metal-ds4"
- &audiocpp
name: "audio-cpp"
alias: "audio-cpp"
license: apache-2.0
description: |
0xShug0/audio.cpp - a ggml audio inference framework covering text to
speech, voice cloning, transcription, forced alignment, voice activity
detection, speaker diarization, source separation, voice conversion and
music generation, across 30+ model families. Consumes audio.cpp-native
GGUF packages from huggingface.co/audio-cpp/audio.cpp-gguf.
urls:
- https://github.com/0xShug0/audio.cpp
- https://huggingface.co/audio-cpp/audio.cpp-gguf
tags:
- TTS
- text-to-speech
- audio-transcription
- CPU
- CUDA
- Vulkan
# No metal/metal-darwin-arm64 key: there is no Darwin matrix entry for
# audio-cpp yet, and pointing a capability at a tag CI never builds hands the
# user a pull failure instead of the CPU fallback below.
capabilities:
default: "cpu-audio-cpp"
nvidia: "cuda12-audio-cpp"
nvidia-cuda-12: "cuda12-audio-cpp"
nvidia-cuda-13: "cuda13-audio-cpp"
vulkan: "vulkan-audio-cpp"
- &whispercpp
name: "whisper"
alias: "whisper"
@@ -1919,6 +1948,14 @@
nvidia-l4t-cuda-13: "cuda13-nvidia-l4t-arm64-ds4-development"
metal: "metal-ds4-development"
metal-darwin-arm64: "metal-ds4-development"
- !!merge <<: *audiocpp
name: "audio-cpp-development"
capabilities:
default: "cpu-audio-cpp-development"
nvidia: "cuda12-audio-cpp-development"
nvidia-cuda-12: "cuda12-audio-cpp-development"
nvidia-cuda-13: "cuda13-audio-cpp-development"
vulkan: "vulkan-audio-cpp-development"
- !!merge <<: *stablediffusionggml
name: "stablediffusion-ggml-development"
capabilities:
@@ -6573,3 +6610,44 @@
uri: "quay.io/go-skynet/local-ai-backends:master-metal-darwin-arm64-supertonic"
mirrors:
- localai/localai-backends:master-metal-darwin-arm64-supertonic
## audio-cpp
- !!merge <<: *audiocpp
name: "cpu-audio-cpp"
uri: "quay.io/go-skynet/local-ai-backends:latest-cpu-audio-cpp"
mirrors:
- localai/localai-backends:latest-cpu-audio-cpp
- !!merge <<: *audiocpp
name: "cpu-audio-cpp-development"
uri: "quay.io/go-skynet/local-ai-backends:master-cpu-audio-cpp"
mirrors:
- localai/localai-backends:master-cpu-audio-cpp
- !!merge <<: *audiocpp
name: "cuda12-audio-cpp"
uri: "quay.io/go-skynet/local-ai-backends:latest-gpu-nvidia-cuda-12-audio-cpp"
mirrors:
- localai/localai-backends:latest-gpu-nvidia-cuda-12-audio-cpp
- !!merge <<: *audiocpp
name: "cuda12-audio-cpp-development"
uri: "quay.io/go-skynet/local-ai-backends:master-gpu-nvidia-cuda-12-audio-cpp"
mirrors:
- localai/localai-backends:master-gpu-nvidia-cuda-12-audio-cpp
- !!merge <<: *audiocpp
name: "cuda13-audio-cpp"
uri: "quay.io/go-skynet/local-ai-backends:latest-gpu-nvidia-cuda-13-audio-cpp"
mirrors:
- localai/localai-backends:latest-gpu-nvidia-cuda-13-audio-cpp
- !!merge <<: *audiocpp
name: "cuda13-audio-cpp-development"
uri: "quay.io/go-skynet/local-ai-backends:master-gpu-nvidia-cuda-13-audio-cpp"
mirrors:
- localai/localai-backends:master-gpu-nvidia-cuda-13-audio-cpp
- !!merge <<: *audiocpp
name: "vulkan-audio-cpp"
uri: "quay.io/go-skynet/local-ai-backends:latest-gpu-vulkan-audio-cpp"
mirrors:
- localai/localai-backends:latest-gpu-vulkan-audio-cpp
- !!merge <<: *audiocpp
name: "vulkan-audio-cpp-development"
uri: "quay.io/go-skynet/local-ai-backends:master-gpu-vulkan-audio-cpp"
mirrors:
- localai/localai-backends:master-gpu-vulkan-audio-cpp

View File

@@ -75,6 +75,9 @@ export function inferBackendPath(item) {
if (item.dockerfile.endsWith("ds4")) {
return `backend/cpp/ds4/`;
}
if (item.dockerfile.endsWith("audio-cpp")) {
return `backend/cpp/audio-cpp/`;
}
if (item.dockerfile.endsWith("llama-cpp")) {
return `backend/cpp/llama-cpp/`;
}
@@ -98,6 +101,16 @@ export function inferBackendPathDarwin(item) {
if (item.backend === "privacy-filter") {
return `backend/cpp/privacy-filter/`;
}
// audio-cpp is C++ too. There is no Darwin matrix entry for it yet: the
// Metal build needs scripts/build/audio-cpp-darwin.sh and a
// `backends/audio-cpp-darwin` make target, neither of which exists, so this
// case (and the DARWIN_BESPOKE_BUILDERS membership below) is inert today. It
// is here so that adding the entry is a one-line change that cannot
// accidentally route a C++ backend through the generic
// build-darwin-go-backend path, which would look for backend/go/audio-cpp/.
if (item.backend === "audio-cpp") {
return `backend/cpp/audio-cpp/`;
}
if (!item.lang) {
return `backend/python/${item.backend}/`;
}
@@ -139,7 +152,17 @@ const isDarwinPython = item => !item.lang;
// backend_build_darwin.yml routes llama-cpp, ds4 and privacy-filter to their
// own bespoke make targets; every other lang=go entry goes through
// `make build-darwin-go-backend` -> scripts/build/golang-darwin.sh.
const DARWIN_BESPOKE_BUILDERS = new Set(["llama-cpp", "ds4", "privacy-filter"]);
//
// audio-cpp is listed ahead of its Darwin matrix entry (see
// inferBackendPathDarwin): it is a C++/ggml backend that will need the same
// bespoke treatment, and listing it here now means the entry, when it lands,
// cannot be silently claimed by the generic Go path.
const DARWIN_BESPOKE_BUILDERS = new Set([
"llama-cpp",
"ds4",
"privacy-filter",
"audio-cpp",
]);
const isDarwinGenericGo = item =>
!!item.lang && !DARWIN_BESPOKE_BUILDERS.has(item.backend);

View File

@@ -8,7 +8,7 @@
import test from "node:test";
import assert from "node:assert/strict";
import { filterMatrix } from "./backend-filter.mjs";
import { filterMatrix, inferBackendPathDarwin } from "./backend-filter.mjs";
// A representative slice of .github/backend-matrix.yml: one entry per build
// path that the filter treats differently. Kept inline rather than parsed from
@@ -51,6 +51,12 @@ const includes = [
"tag-suffix": "-turboquant",
"base-image": "ubuntu:24.04",
},
{
backend: "audio-cpp",
dockerfile: "./backend/Dockerfile.audio-cpp",
"tag-suffix": "-cpu-audio-cpp",
"base-image": "ubuntu:24.04",
},
];
const includesDarwin = [
@@ -194,6 +200,44 @@ test("turboquant still retriggers on llama-cpp source changes", () => {
assert.deepEqual(names(filtered), ["llama-cpp", "turboquant"]);
});
// ---------------------------------------------------------------------------
// audio-cpp: a C++ backend whose Dockerfile suffix is its only Linux marker
// ---------------------------------------------------------------------------
test("audio-cpp source changes rebuild audio-cpp and nothing else", () => {
// Without an inferBackendPath case, Dockerfile.audio-cpp resolves to null and
// filterMatrix drops every audio-cpp entry, so a PR touching only this
// backend would get zero CI jobs and look green for the wrong reason.
const { filtered, filteredDarwin, changedBackends } = run([
"backend/cpp/audio-cpp/grpc-server.cpp",
]);
assert.deepEqual(names(filtered), ["audio-cpp"]);
assert.deepEqual(filteredDarwin, []);
assert.deepEqual([...changedBackends], ["audio-cpp"]);
});
test("Dockerfile.audio-cpp rebuilds only audio-cpp", () => {
const { filtered } = run(["backend/Dockerfile.audio-cpp"]);
assert.deepEqual(names(filtered), ["audio-cpp"]);
});
test("audio-cpp resolves to its C++ sources on Darwin, not backend/go", () => {
// There is no Darwin matrix entry yet (the Metal build script does not
// exist), so this pins the function directly. lang=go on a Darwin entry only
// selects the runner and toolchain; routing audio-cpp by that field would
// point at backend/go/audio-cpp/, which does not exist.
assert.equal(
inferBackendPathDarwin({
backend: "audio-cpp",
lang: "go",
"tag-suffix": "-metal-darwin-arm64-audio-cpp",
}),
"backend/cpp/audio-cpp/",
);
});
// ---------------------------------------------------------------------------
// pkg/ subtrees linked into every Go backend binary
// ---------------------------------------------------------------------------