diff --git a/.agents/ci-caching.md b/.agents/ci-caching.md index 9f28233a4..1c8c65470 100644 --- a/.agents/ci-caching.md +++ b/.agents/ci-caching.md @@ -186,6 +186,22 @@ source touched), `changed-backends.js` adds one canary backend matrix entry per (lang × build-type × arch × cuda × ubuntu) tuple to the filtered matrix so each base flavour gets exercised. +### Existing language tiers + +| Tier (lang) | Recipe | Consumer Dockerfile(s) | Distinct stems | +|---|---|---|---| +| `python` | `.docker/bases/Dockerfile.python` | `backend/Dockerfile.python` | 9 | +| `golang` | `.docker/bases/Dockerfile.golang` | `backend/Dockerfile.golang` | 8 | +| `cpp` | `.docker/bases/Dockerfile.cpp` (apt + GPU + protoc + cmake + GRPC) | `backend/Dockerfile.{llama-cpp,ik-llama-cpp,turboquant}` | 8 | +| `rust` | `.docker/bases/Dockerfile.rust` | `backend/Dockerfile.rust` | 1 | + +The C++ trio share a single `cpp` base because they only differ in their +per-backend `make` targets. `langOf()` in `scripts/changed-backends.js` +remaps `Dockerfile.{llama-cpp,ik-llama-cpp,turboquant}` → `cpp` so dedup +works across the trio. If a future C++ consumer needs a *different* base +(e.g. without GRPC, or with a different protoc version), give it its own +`Dockerfile.` recipe and remove it from the cpp remap. + ### Adding a new (accel × arch × cuda × lang) flavour Just add the matrix entry to `.github/backend-matrix.yaml` for the new @@ -193,17 +209,24 @@ flavour. The bases matrix and the per-entry `base-image-prebuilt` are derived automatically by `scripts/changed-backends.js`. Nothing else to change. -### Adding a new language tier (e.g. golang) +### Adding a new language tier -1. Create `.docker/bases/Dockerfile.` mirroring `Dockerfile.python` +1. Create `.docker/bases/Dockerfile.` mirroring an existing tier (apt + accel install + lang-specific toolchain). 2. Slim `backend/Dockerfile.` to `FROM ${BASE_IMAGE_PREBUILT}` plus the per-backend source COPY + build (no inline accel install). +3. Add the new recipe to `baseTriggerFiles` in + `scripts/changed-backends.js` so PRs touching it fan out to canaries. +4. Add `: (item) => item.dockerfile.endsWith("")` to + `langTriggerSelector` in the same file. +5. Add a `LOCAL_BASE__TAG`, a `docker-build--base` target, + and a clause in `local-base-tag` / `local-base-target` in `Makefile`. The `langsWithBase` set in `scripts/changed-backends.js` is auto-detected from the `.docker/bases/` directory at script startup, so step 1 alone is enough for the script to start emitting bases (and annotating matrix -entries with `base-image-prebuilt`) for that lang. +entries with `base-image-prebuilt`) for that lang. Steps 3–5 plug it +into the canary fan-out and the local-build path. ### Why not just rely on `mode=max` cache? @@ -216,18 +239,22 @@ it — that's the actual cross-matrix dedup. ### Local builds -`backend/Dockerfile.python` requires `BASE_IMAGE_PREBUILT` (no inline -fallback). For local development: +All `backend/Dockerfile.{python,golang,cpp,rust}` consumers require +`BASE_IMAGE_PREBUILT` (no inline fallback). The Makefile wires the right +`docker-build--base` as a prerequisite for each backend's +`docker-build-` target, so: ```bash -# Build a base flavour locally -make backend-image-base BUILD_TYPE=cublas CUDA_MAJOR_VERSION=12 CUDA_MINOR_VERSION=9 - -# Build a backend on top of it -make backend-image BACKEND=mlx-vlm BUILD_TYPE=cublas CUDA_MAJOR_VERSION=12 CUDA_MINOR_VERSION=9 +# Build any backend; the matching base is built first if needed. +make docker-build-vllm BUILD_TYPE=cublas CUDA_MAJOR_VERSION=12 CUDA_MINOR_VERSION=8 +make docker-build-llama-cpp BUILD_TYPE=cublas CUDA_MAJOR_VERSION=13 CUDA_MINOR_VERSION=0 +make docker-build-rerankers # golang +make docker-build-kokoros # rust ``` -Or pull a pre-built base from quay if it exists for your target tuple. +Or build a base directly: `make docker-build-{python,golang,cpp,rust}-base +BUILD_TYPE=...`. Or pull a pre-built one from quay if it exists for your +target tuple. ## Touching the cache pipeline diff --git a/.docker/bases/Dockerfile.cpp b/.docker/bases/Dockerfile.cpp new file mode 100644 index 000000000..e7ab763bb --- /dev/null +++ b/.docker/bases/Dockerfile.cpp @@ -0,0 +1,259 @@ +# Shared C++ + accelerator base image for the llama-cpp / ik-llama-cpp / +# turboquant trio. They differ only in their Makefile targets at build +# time; the apt + GPU SDK + protoc + cmake + GRPC install is identical. +# +# Built once per (build-type, arch, ubuntu-version, cuda-version) combination +# by .github/workflows/base_images.yml and pushed to +# quay.io/go-skynet/localai-base:[-pr]. Consumed by +# backend/Dockerfile.{llama-cpp,ik-llama-cpp,turboquant} via the +# BASE_IMAGE_PREBUILT build-arg. See .agents/ci-caching.md. + +ARG BASE_IMAGE=ubuntu:24.04 +ARG APT_MIRROR="" +ARG APT_PORTS_MIRROR="" + +FROM ${BASE_IMAGE} AS grpc + +ARG GRPC_MAKEFLAGS="-j4 -Otarget" +ARG GRPC_VERSION=v1.65.0 +ARG CMAKE_FROM_SOURCE=false +# CUDA Toolkit 13.x compatibility: CMake 3.31.9+ fixes toolchain detection/arch table issues +ARG CMAKE_VERSION=3.31.10 +ARG APT_MIRROR +ARG APT_PORTS_MIRROR + +ENV MAKEFLAGS=${GRPC_MAKEFLAGS} + +WORKDIR /build + +RUN --mount=type=bind,source=.docker/apt-mirror.sh,target=/usr/local/sbin/apt-mirror \ + APT_MIRROR="${APT_MIRROR}" APT_PORTS_MIRROR="${APT_PORTS_MIRROR}" sh /usr/local/sbin/apt-mirror && \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + ca-certificates \ + build-essential curl libssl-dev \ + git wget && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +RUN </dev/null || ls /opt/rocm*/lib64/rocblas/library/Kernels* 2>/dev/null) | grep -oP 'gfx[0-9a-z+-]+' | sort -u || \ + echo "WARNING: No rocBLAS kernel data found" \ + ; fi + +# Install protoc (the version in 22.04 is too old, and grpc's bundled protoc +# would pull in a newer absl that breaks stablediffusion). +RUN <[-pr]. Consumed by +# backend/Dockerfile.golang via the BASE_IMAGE_PREBUILT build-arg. +# +# Mirrors the GPU stack stanzas in Dockerfile.python; the language-specific +# tail at the bottom installs Go + grpc tooling. See .agents/ci-caching.md. + +ARG BASE_IMAGE=ubuntu:24.04 +ARG APT_MIRROR="" +ARG APT_PORTS_MIRROR="" + +FROM ${BASE_IMAGE} + +ARG BUILD_TYPE +ENV BUILD_TYPE=${BUILD_TYPE} +ARG CUDA_MAJOR_VERSION +ARG CUDA_MINOR_VERSION +ARG SKIP_DRIVERS=false +ENV CUDA_MAJOR_VERSION=${CUDA_MAJOR_VERSION} +ENV CUDA_MINOR_VERSION=${CUDA_MINOR_VERSION} +ENV DEBIAN_FRONTEND=noninteractive +ARG TARGETARCH +ARG TARGETVARIANT +ARG GO_VERSION=1.25.4 +ARG UBUNTU_VERSION=2404 +ARG APT_MIRROR +ARG APT_PORTS_MIRROR + +LABEL org.opencontainers.image.source="https://github.com/mudler/LocalAI" +LABEL org.opencontainers.image.description="LocalAI Go+accelerator base image" +LABEL org.localai.base.lang="golang" + +# gcc-14 is the default on noble (ubuntu:24.04) but absent from jammy +# (the L4T jetpack r36.4.0 base). LocalVQE needs it; the other Go backends +# compile with the default gcc shipped via build-essential. Try gcc-14 +# from the configured repos and fall back gracefully when it's missing. +RUN --mount=type=bind,source=.docker/apt-mirror.sh,target=/usr/local/sbin/apt-mirror \ + APT_MIRROR="${APT_MIRROR}" APT_PORTS_MIRROR="${APT_PORTS_MIRROR}" sh /usr/local/sbin/apt-mirror && \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + build-essential \ + git ccache \ + ca-certificates \ + make cmake wget libopenblas-dev \ + curl unzip \ + libssl-dev && \ + if apt-cache show gcc-14 >/dev/null 2>&1 && apt-cache show g++-14 >/dev/null 2>&1; then \ + apt-get install -y --no-install-recommends gcc-14 g++-14 && \ + update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100 \ + --slave /usr/bin/g++ g++ /usr/bin/g++-14 \ + --slave /usr/bin/gcov gcov /usr/bin/gcov-14; \ + fi && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Cuda +ENV PATH=/usr/local/cuda/bin:${PATH} + +# HipBLAS requirements +ENV PATH=/opt/rocm/bin:${PATH} + +# Vulkan requirements +RUN <[-pr]. The current +# rust matrix is CPU-only, so this base skips the GPU SDK stanzas; if a +# future rust backend needs cublas/rocm/etc., promote this recipe to mirror +# Dockerfile.python's GPU stack. See .agents/ci-caching.md. + +ARG BASE_IMAGE=ubuntu:24.04 +ARG APT_MIRROR="" +ARG APT_PORTS_MIRROR="" + +FROM ${BASE_IMAGE} + +ENV DEBIAN_FRONTEND=noninteractive +ARG TARGETARCH +ARG TARGETVARIANT +ARG UBUNTU_VERSION=2404 +ARG APT_MIRROR +ARG APT_PORTS_MIRROR + +LABEL org.opencontainers.image.source="https://github.com/mudler/LocalAI" +LABEL org.opencontainers.image.description="LocalAI Rust base image" +LABEL org.localai.base.lang="rust" + +RUN --mount=type=bind,source=.docker/apt-mirror.sh,target=/usr/local/sbin/apt-mirror \ + APT_MIRROR="${APT_MIRROR}" APT_PORTS_MIRROR="${APT_PORTS_MIRROR}" sh /usr/local/sbin/apt-mirror && \ + apt-get update && \ + apt-get install -y --no-install-recommends \ + build-essential \ + git ccache \ + ca-certificates \ + make cmake wget \ + curl unzip \ + clang \ + pkg-config \ + libssl-dev \ + espeak-ng libespeak-ng-dev \ + libsonic-dev libpcaudio-dev \ + libopus-dev \ + protobuf-compiler && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Install Rust +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" diff --git a/Makefile b/Makefile index 15a9b7af3..6bc2d37ca 100644 --- a/Makefile +++ b/Makefile @@ -1094,7 +1094,7 @@ BACKEND_KOKOROS = kokoros|rust|.|false|true # C++ backends (Go wrapper with purego) BACKEND_SAM3_CPP = sam3-cpp|golang|.|false|true -# Tag stem for the local prebuilt base image. Mirrors tagStem() in +# Tag stem for the local prebuilt base images. Mirrors tagStem() in # scripts/changed-backends.js and the inline expression in # .github/workflows/backend.yml, so a `make docker-build-X` produces the # same FROM ref shape that CI uses. @@ -1102,10 +1102,15 @@ LOCAL_BASE_BUILD_TYPE := $(or $(BUILD_TYPE),cpu) LOCAL_BASE_UBUNTU_VERSION := $(or $(UBUNTU_VERSION),2404) LOCAL_BASE_CUDA_SUFFIX := $(if $(filter cublas l4t,$(BUILD_TYPE)),-cuda$(CUDA_MAJOR_VERSION).$(CUDA_MINOR_VERSION)) LOCAL_BASE_PYTHON_TAG := localai-base:python-$(LOCAL_BASE_BUILD_TYPE)-$(LOCAL_BASE_UBUNTU_VERSION)$(LOCAL_BASE_CUDA_SUFFIX) +LOCAL_BASE_GOLANG_TAG := localai-base:golang-$(LOCAL_BASE_BUILD_TYPE)-$(LOCAL_BASE_UBUNTU_VERSION)$(LOCAL_BASE_CUDA_SUFFIX) +LOCAL_BASE_CPP_TAG := localai-base:cpp-$(LOCAL_BASE_BUILD_TYPE)-$(LOCAL_BASE_UBUNTU_VERSION)$(LOCAL_BASE_CUDA_SUFFIX) +LOCAL_BASE_RUST_TAG := localai-base:rust-$(LOCAL_BASE_BUILD_TYPE)-$(LOCAL_BASE_UBUNTU_VERSION) + +# Per-(lang) base image build targets. Each backend's docker-build-X target +# depends on the matching base via generate-docker-build-target below. +# PHONY so docker handles its own layer caching. +.PHONY: docker-build-python-base docker-build-golang-base docker-build-cpp-base docker-build-rust-base -# Build the Python+accelerator base image locally. Backend builds depend on -# this; PHONY so docker handles its own layer caching. -.PHONY: docker-build-python-base docker-build-python-base: docker build \ --build-arg BUILD_TYPE=$(BUILD_TYPE) \ @@ -1120,6 +1125,59 @@ docker-build-python-base: -f .docker/bases/Dockerfile.python \ . +docker-build-golang-base: + docker build \ + --build-arg BUILD_TYPE=$(BUILD_TYPE) \ + --build-arg BASE_IMAGE=$(or $(BASE_IMAGE),ubuntu:24.04) \ + --build-arg CUDA_MAJOR_VERSION=$(CUDA_MAJOR_VERSION) \ + --build-arg CUDA_MINOR_VERSION=$(CUDA_MINOR_VERSION) \ + --build-arg UBUNTU_VERSION=$(LOCAL_BASE_UBUNTU_VERSION) \ + --build-arg APT_MIRROR=$(APT_MIRROR) \ + --build-arg APT_PORTS_MIRROR=$(APT_PORTS_MIRROR) \ + $(if $(SKIP_DRIVERS),--build-arg SKIP_DRIVERS=$(SKIP_DRIVERS)) \ + -t $(LOCAL_BASE_GOLANG_TAG) \ + -f .docker/bases/Dockerfile.golang \ + . + +docker-build-cpp-base: + docker build \ + --build-arg BUILD_TYPE=$(BUILD_TYPE) \ + --build-arg BASE_IMAGE=$(or $(BASE_IMAGE),ubuntu:24.04) \ + --build-arg CUDA_MAJOR_VERSION=$(CUDA_MAJOR_VERSION) \ + --build-arg CUDA_MINOR_VERSION=$(CUDA_MINOR_VERSION) \ + --build-arg UBUNTU_VERSION=$(LOCAL_BASE_UBUNTU_VERSION) \ + --build-arg APT_MIRROR=$(APT_MIRROR) \ + --build-arg APT_PORTS_MIRROR=$(APT_PORTS_MIRROR) \ + $(if $(SKIP_DRIVERS),--build-arg SKIP_DRIVERS=$(SKIP_DRIVERS)) \ + -t $(LOCAL_BASE_CPP_TAG) \ + -f .docker/bases/Dockerfile.cpp \ + . + +docker-build-rust-base: + docker build \ + --build-arg BASE_IMAGE=$(or $(BASE_IMAGE),ubuntu:24.04) \ + --build-arg UBUNTU_VERSION=$(LOCAL_BASE_UBUNTU_VERSION) \ + --build-arg APT_MIRROR=$(APT_MIRROR) \ + --build-arg APT_PORTS_MIRROR=$(APT_PORTS_MIRROR) \ + -t $(LOCAL_BASE_RUST_TAG) \ + -f .docker/bases/Dockerfile.rust \ + . + +# Map a consumer dockerfile-type to the base-image tag it should consume. +# Mirrors langOf() in scripts/changed-backends.js: the C++ trio +# (llama-cpp/ik-llama-cpp/turboquant) all consume the shared cpp base. +local-base-tag = $(strip \ + $(if $(filter python,$(1)),$(LOCAL_BASE_PYTHON_TAG), \ + $(if $(filter golang,$(1)),$(LOCAL_BASE_GOLANG_TAG), \ + $(if $(filter llama-cpp ik-llama-cpp turboquant,$(1)),$(LOCAL_BASE_CPP_TAG), \ + $(if $(filter rust,$(1)),$(LOCAL_BASE_RUST_TAG)))))) + +local-base-target = $(strip \ + $(if $(filter python,$(1)),docker-build-python-base, \ + $(if $(filter golang,$(1)),docker-build-golang-base, \ + $(if $(filter llama-cpp ik-llama-cpp turboquant,$(1)),docker-build-cpp-base, \ + $(if $(filter rust,$(1)),docker-build-rust-base))))) + # Helper function to build docker image for a backend # Usage: $(call docker-build-backend,BACKEND_NAME,DOCKERFILE_TYPE,BUILD_CONTEXT,PROGRESS_FLAG,NEEDS_BACKEND_ARG) define docker-build-backend @@ -1132,19 +1190,18 @@ define docker-build-backend --build-arg UBUNTU_CODENAME=$(UBUNTU_CODENAME) \ --build-arg APT_MIRROR=$(APT_MIRROR) \ --build-arg APT_PORTS_MIRROR=$(APT_PORTS_MIRROR) \ - $(if $(filter python,$(2)),--build-arg BASE_IMAGE_PREBUILT=$(LOCAL_BASE_PYTHON_TAG)) \ + $(if $(call local-base-tag,$(2)),--build-arg BASE_IMAGE_PREBUILT=$(call local-base-tag,$(2))) \ $(if $(FROM_SOURCE),--build-arg FROM_SOURCE=$(FROM_SOURCE)) \ $(if $(AMDGPU_TARGETS),--build-arg AMDGPU_TARGETS=$(AMDGPU_TARGETS)) \ $(if $(filter true,$(5)),--build-arg BACKEND=$(1)) \ -t local-ai-backend:$(1) -f backend/Dockerfile.$(2) $(3) endef -# Generate docker-build targets from backend definitions. Python backends -# get docker-build-python-base as a prerequisite so the layered base is -# always present locally. Other dockerfile types still build their own -# inline bootstrap from their respective Dockerfile.. +# Generate docker-build targets from backend definitions. Each consumer +# gets the matching layered base as a prerequisite so the FROM in the +# slimmed Dockerfile resolves locally. The map lives in local-base-target. define generate-docker-build-target -docker-build-$(word 1,$(subst |, ,$(1))): $(if $(filter python,$(word 2,$(subst |, ,$(1)))),docker-build-python-base) +docker-build-$(word 1,$(subst |, ,$(1))): $(call local-base-target,$(word 2,$(subst |, ,$(1)))) $$(call docker-build-backend,$(word 1,$(subst |, ,$(1))),$(word 2,$(subst |, ,$(1))),$(word 3,$(subst |, ,$(1))),$(word 4,$(subst |, ,$(1))),$(word 5,$(subst |, ,$(1)))) endef diff --git a/backend/Dockerfile.golang b/backend/Dockerfile.golang index af704d375..f01e86e56 100644 --- a/backend/Dockerfile.golang +++ b/backend/Dockerfile.golang @@ -1,210 +1,37 @@ -ARG BASE_IMAGE=ubuntu:24.04 -ARG APT_MIRROR="" -ARG APT_PORTS_MIRROR="" +# Builds a single Go backend on top of the shared +# .docker/bases/Dockerfile.golang base. The base bakes in apt + GPU SDK + +# Go toolchain + protoc + grpc tooling, so this stage only carries the +# per-backend opus-dev install + COPY + `make build`. +# +# CI orchestration (.github/workflows/backend.yml + backend_pr.yml) builds +# the right base flavour automatically via scripts/changed-backends.js +# and passes BASE_IMAGE_PREBUILT here. For local builds, run: +# make backend-image-base LANG=golang BUILD_TYPE=<...> +# make backend-image BACKEND=<...> BUILD_TYPE=<...> +# See .agents/ci-caching.md. + +ARG BASE_IMAGE_PREBUILT + +FROM ${BASE_IMAGE_PREBUILT} AS builder -FROM ${BASE_IMAGE} AS builder ARG BACKEND=rerankers ARG BUILD_TYPE ENV BUILD_TYPE=${BUILD_TYPE} ARG CUDA_MAJOR_VERSION ARG CUDA_MINOR_VERSION -ARG SKIP_DRIVERS=false ENV CUDA_MAJOR_VERSION=${CUDA_MAJOR_VERSION} ENV CUDA_MINOR_VERSION=${CUDA_MINOR_VERSION} -ENV DEBIAN_FRONTEND=noninteractive ARG TARGETARCH ARG TARGETVARIANT -ARG GO_VERSION=1.25.4 -ARG UBUNTU_VERSION=2404 ARG AMDGPU_TARGETS ENV AMDGPU_TARGETS=${AMDGPU_TARGETS} -ARG APT_MIRROR -ARG APT_PORTS_MIRROR - -# gcc-14 is the default on noble (ubuntu:24.04) but absent from jammy -# (the L4T jetpack r36.4.0 base). LocalVQE specifically needs it; the -# other Go backends compile fine with the default gcc shipped via -# build-essential. So: try gcc-14 from the configured repos, fall back -# gracefully when it's not available so jammy-based builds don't fail -# at the apt step. -RUN --mount=type=bind,source=.docker/apt-mirror.sh,target=/usr/local/sbin/apt-mirror \ - APT_MIRROR="${APT_MIRROR}" APT_PORTS_MIRROR="${APT_PORTS_MIRROR}" sh /usr/local/sbin/apt-mirror && \ - apt-get update && \ - apt-get install -y --no-install-recommends \ - build-essential \ - git ccache \ - ca-certificates \ - make cmake wget libopenblas-dev \ - curl unzip \ - libssl-dev && \ - if apt-cache show gcc-14 >/dev/null 2>&1 && apt-cache show g++-14 >/dev/null 2>&1; then \ - apt-get install -y --no-install-recommends gcc-14 g++-14 && \ - update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-14 100 \ - --slave /usr/bin/g++ g++ /usr/bin/g++-14 \ - --slave /usr/bin/gcov gcov /usr/bin/gcov-14; \ - fi && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* - - -# Cuda -ENV PATH=/usr/local/cuda/bin:${PATH} - -# HipBLAS requirements -ENV PATH=/opt/rocm/bin:${PATH} - - -# Vulkan requirements -RUN </dev/null || ls /opt/rocm*/lib64/rocblas/library/Kernels* 2>/dev/null) | grep -oP 'gfx[0-9a-z+-]+' | sort -u || \ - echo "WARNING: No rocBLAS kernel data found" \ - ; fi - -RUN echo "TARGETARCH: $TARGETARCH" - -# We need protoc installed, and the version in 22.04 is too old. We will create one as part installing the GRPC build below -# but that will also being in a newer version of absl which stablediffusion cannot compile with. This version of protoc is only -# here so that we can generate the grpc code for the stablediffusion build -RUN </dev/null || ls /opt/rocm*/lib64/rocblas/library/Kernels* 2>/dev/null) | grep -oP 'gfx[0-9a-z+-]+' | sort -u || \ - echo "WARNING: No rocBLAS kernel data found" \ - ; fi - -RUN echo "TARGETARCH: $TARGETARCH" - -# We need protoc installed, and the version in 22.04 is too old. We will create one as part installing the GRPC build below -# but that will also being in a newer version of absl which stablediffusion cannot compile with. This version of protoc is only -# here so that we can generate the grpc code for the stablediffusion build -RUN <) trigger only their own lang; the +// shared scaffolding entries trigger every lang. const baseTriggerFiles = new Set([ ".docker/bases/Dockerfile.python", + ".docker/bases/Dockerfile.golang", + ".docker/bases/Dockerfile.cpp", + ".docker/bases/Dockerfile.rust", ".docker/apt-mirror.sh", ".github/workflows/base_images.yml", ".github/actions/configure-apt-mirror/action.yml", "scripts/changed-backends.js", ]); +// Maps a base lang back to the consumer Dockerfiles that build on top of +// it. The cpp base is shared by the llama-cpp / ik-llama-cpp / turboquant +// trio; everything else is 1:1 with the file suffix. const langTriggerSelector = { python: (item) => item.dockerfile && item.dockerfile.endsWith("python"), + golang: (item) => item.dockerfile && item.dockerfile.endsWith("golang"), + rust: (item) => item.dockerfile && item.dockerfile.endsWith("rust"), + cpp: (item) => + !!item.dockerfile && /Dockerfile\.(llama-cpp|ik-llama-cpp|turboquant)$/.test(item.dockerfile), }; // ---------- helpers ---------- @@ -65,7 +77,13 @@ function langOf(item) { if (!item.dockerfile) return null; // dockerfile is like "./backend/Dockerfile.python" const m = item.dockerfile.match(/Dockerfile\.([\w-]+)$/); - return m ? m[1] : null; + if (!m) return null; + // The C++ trio (llama-cpp, ik-llama-cpp, turboquant) consume a shared + // cpp base image — they only differ in their per-backend make targets. + if (m[1] === "llama-cpp" || m[1] === "ik-llama-cpp" || m[1] === "turboquant") { + return "cpp"; + } + return m[1]; } function inferBackendPath(item) {