Skip to content

feat(inference): add owned llama.cpp image build - #8235

Merged
ericksoa merged 8 commits into
mainfrom
feat/llamacpp-owned-image-8231
Aug 4, 2026
Merged

feat(inference): add owned llama.cpp image build#8235
ericksoa merged 8 commits into
mainfrom
feat/llamacpp-owned-image-8231

Conversation

@ericksoa

@ericksoa ericksoa commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

NemoClaw has no repository-owned llama.cpp image build. This PR adds a declarative build manifest and pull-request checks for native amd64 and DGX Spark arm64 images. The checks do not publish an image or change the managed inference support state.

Related Issue

Part of #8231.

Changes

  • Add a ServerImageBuild YAML manifest for the image registry, pinned llama.cpp source archive, NVIDIA CUDA bases, package versions, native platform matrix, CMake settings, and runtime identity. The PR workflow consumes this manifest because the accepted managed inference design requires declarative configuration; focused contract tests reject drift between the manifest, serving recipe, Dockerfile, and workflow.
  • Add a multi-stage llama-server Dockerfile. It verifies the pinned source archive, builds the CUDA server without UI, RPC, or subprocess surfaces, preserves upstream license files, and creates a non-root runtime image. The runtime removes command shells, and PR validation inspects its exported filesystem without executing a shell.
  • Add a read-only GitHub Actions pull-request workflow for native linux/amd64 and linux/arm64 builds. It has no package-write permission or registry login. Each lane checks the exact source and base labels, server revision, runtime user, license files, disabled UI assets, and read-only root filesystem behavior.
  • Add behavior tests for the declarative manifest compiler, Dockerfile contract, native matrix, action pins, and no-publication boundary.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification: This PR adds a pull-request-only image build check. It publishes no artifact and changes no user-facing API, CLI, supported configuration, default, or support state.
  • Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)
  • Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: Accepted epic [Epic] Support llama.cpp as a managed local inference server #8144 and issue [Managed inference] Publish owned llama.cpp server image #8231 define this image boundary. This PR performs no registry write, credential operation, serving selection, default change, or support activation.
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Documentation Writer Review

  • Documentation writer subagent reviewed the completed changes
  • Result: no-docs-needed
  • Evidence: The image build is limited to pull-request validation. It publishes nothing and does not change a user-facing or supported surface. The review covered all changed comments, test titles, workflow text, and security claims; no findings remain.
  • Agent: Codex Desktop

DGX Station Hardware Evidence

  • Tested on DGX Station
  • Tested commit:
  • Station profile/scenario:
  • Result:
  • Supporting evidence:

Verification

  • PR description includes a Signed-off-by: line and every commit appears as Verified in GitHub
  • Normal pre-commit, commit-msg, and pre-push hooks passed, or npm run validate:pr passed after refreshing origin/main when hooks were skipped or unavailable
  • Targeted behavior tests pass for the current change set, or tests are marked not applicable above — command/result or justification: npx --no-install vitest run test/llama-cpp-image.test.ts test/llama-cpp-image-workflow.test.ts — 2 files and 13 tests passed.
  • Applicable broad gate passed — npm test for broad runtime/test-harness changes; npm run check for repo-wide validation/coverage changes — command/result:
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: Aaron Erickson aerickson@nvidia.com

Summary by CodeRabbit

  • New Features

    • Added a CUDA-enabled llama.cpp server image for managed inference.
    • Supports AMD64 and ARM64 platforms with pinned, reproducible source and runtime components.
    • Runs securely as a non-root user, serves requests on port 8081, and includes required runtime libraries and writable temporary storage.
  • Tests

    • Added pull-request validation covering image configuration, supported platforms, security settings, runtime behavior, metadata, and build reproducibility.

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@ericksoa ericksoa added area: inference Inference routing, serving, model selection, or outputs area: local-models Local model providers, downloads, launch, or connectivity area: providers Inference provider integrations and provider behavior feature PR adds or expands user-visible functionality platform: dgx-spark Affects DGX Spark hardware or workflows v0.0.102 labels Aug 4, 2026
@ericksoa ericksoa self-assigned this Aug 4, 2026
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 06ca9c65-8196-43c8-ba51-16a4d88ec0e3

📥 Commits

Reviewing files that changed from the base of the PR and between 09e9466 and 8ca6b8c.

📒 Files selected for processing (3)
  • .github/workflows/llama-cpp-image.yaml
  • test/llama-cpp-image-workflow.test.ts
  • test/llama-cpp-image.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/llama-cpp-image.test.ts
  • test/llama-cpp-image-workflow.test.ts

📝 Walkthrough

Walkthrough

Adds a pinned, multi-platform CUDA llama.cpp server image with non-root runtime settings. Adds manifest validation, pull-request image builds, isolated runtime checks, and Vitest coverage for the image and workflow contracts.

Changes

llama.cpp CUDA image

Layer / File(s) Summary
Image manifest and configuration export
managed-inference/images/llama-cpp/image.yaml, scripts/checks/export-llama-cpp-image-config.mts
Declares pinned source, CUDA development and runtime bases, amd64/arm64 platforms, build dependencies, CMake options, runtime identity, port, required packages, and forbidden paths. Validates manifest structure and exports normalized GitHub Actions build arguments and matrix values.
CUDA image build and runtime setup
managed-inference/images/llama-cpp/Dockerfile
Builds llama-server from a checksum-verified archive with CUDA and selected features. Packages the binary, required libraries, and licenses. Creates a labeled non-root runtime image with shell binaries removed and configured cache and library paths.
Pull-request build and image validation
.github/workflows/llama-cpp-image.yaml
Builds unpublished native images for each target architecture. Validates build arguments, image metadata, entrypoint, runtime identity, revision labels, CUDA base image references, required files, forbidden paths, and restricted execution with disabled networking and read-only storage.
Manifest, workflow, and Dockerfile tests
test/llama-cpp-image.test.ts, test/llama-cpp-image-workflow.test.ts
Tests validate manifest bindings to serving recipes, pinned inputs, platform configurations, exporter outputs, Dockerfile build flags and targets, workflow permissions and triggers, action pin accuracy, declarative build argument wiring, and runtime security properties.

Estimated code review effort: 4 (Complex) | ~50 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PullRequest
  participant ConfigJob as Config Job
  participant Buildx
  participant LlamaImage as Llama Image
  PullRequest->>ConfigJob: trigger validation workflow
  ConfigJob->>ConfigJob: load and validate image manifest
  ConfigJob->>Buildx: provide matrix and build arguments
  Buildx->>LlamaImage: build unpublished native image
  ConfigJob->>LlamaImage: run metadata and restricted-runtime checks
  LlamaImage-->>ConfigJob: return validation results
Loading

Possibly related issues

Possibly related PRs

  • NVIDIA/NemoClaw#8224 — Adds related serving domain support, preset, and catalog schema for llama.cpp.

Suggested labels: area: packaging, area: ci, platform: container

Suggested reviewers: apurvvkumaria, brandonpelfrey

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: adding a repository-owned llama.cpp image build for inference.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/llamacpp-owned-image-8231

Comment @coderabbitai help to get the list of available commands.

@github-code-quality

github-code-quality Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in commit 8ca6b8c in the feat/llamacpp-owned-... branch remains at 96%, unchanged from commit 8683777 in the main branch.

TypeScript / code-coverage/cli

The overall coverage in commit 8ca6b8c in the feat/llamacpp-owned-... branch remains at 81%, unchanged from commit 8683777 in the main branch.

Show a code coverage summary of the most impacted files.
File main 8683777 feat/llamacpp-owned-... 8ca6b8c +/-
src/lib/policy/...ne-exclusion.ts 96% 92% -4%
src/lib/private-networks.ts 93% 90% -3%
src/lib/credentials/store.ts 56% 55% -1%
src/lib/sandbox...rce-identity.ts 87% 87% 0%

Updated August 04, 2026 18:02 UTC

Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
Signed-off-by: Aaron Erickson <aerickson@nvidia.com>
@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — No blocking findings reported

Advisor assessment: No blocking advisor findings reported
Next action: Review the warnings below.
Findings: 0 blockers · 2 warnings · 0 suggestions

Model lanes

  • GPT-5.6 Terra (primary): Completed · high confidence · 0 blockers · 2 warnings · 0 suggestions
  • Nemotron 3 Ultra (second opinion): Completed · high confidence · 0 blockers · 0 warnings · 0 suggestions
  • Model comparison: normalized findings differ; normalized terminology decisions differ; normalized E2E selections differ; Nemotron reported the same number of blockers, 2 fewer warnings, the same number of suggestions.
6 terminology differences from the second opinion

Advisory only. These are normalized differences from the primary terminology receipt.

  • inference-server at managed-inference/images/llama-cpp/Dockerfile:137: selected only by the second-opinion lane as justified.
  • managed-inference at managed-inference/images/llama-cpp/image.yaml:4: selected only by the second-opinion lane as established.
  • DGX Spark at test/llama-cpp-image.test.ts:84: selected only by the second-opinion lane as established.
  • llama.cpp at managed-inference/images/llama-cpp/Dockerfile:43: selected only by the second-opinion lane as established.
  • ServerImageBuild at managed-inference/images/llama-cpp/image.yaml:5: selected only by the second-opinion lane as justified.
  • NemoClaw-owned at managed-inference/images/llama-cpp/Dockerfile:136: selected only by the second-opinion lane as established.
5 additional E2E selections from the second opinion

Advisory only. The primary lane did not select these E2E jobs or targets.

  • spark-install: The completed second-opinion lane identified E2E coverage that the primary lane omitted.
  • gpu-e2e: The completed second-opinion lane identified E2E coverage that the primary lane omitted.
  • ubuntu-repo-docker-post-reboot-recovery: The completed second-opinion lane identified E2E coverage that the primary lane omitted.
  • ubuntu-repo-cloud-openclaw: The completed second-opinion lane identified E2E coverage that the primary lane omitted.
  • ubuntu-policy-custom-missing-presets-negative: The completed second-opinion lane identified E2E coverage that the primary lane omitted.

Second-opinion terminology and E2E selections are advisory. They do not change the primary assessment or E2E / PR Gate.

2 semantic terminology decisions

Terminology decisions are advisory. They affect the assessment only when a separate finding identifies concrete semantic impact.

  • justified — native PR image at .github/workflows/llama-cpp-image.yaml:110: Keep the modifier because it identifies architecture-specific build evidence.
  • justified — declarative image configuration at .github/workflows/llama-cpp-image.yaml:25: Keep the phrase where it distinguishes manifest-derived inputs from fixed workflow literals.

E2E guidance

Advisory only. E2E / PR Gate selects and runs jobs independently.

Recommended E2E: cloud-onboard, inference-routing, network-policy

2 warnings · 0 suggestions

Warnings

Warnings do not block.

PRA-1 Warning — Probe the built server through its declared port

  • Location: .github/workflows/llama-cpp-image.yaml:138
  • Category: tests
  • Problem: The PR image contract validates labels, filesystem contents, and `llama-server --version`, but it does not start the server or make a request to port 8081.
  • Impact: An image whose server cannot bind or answer a request can pass the pull-request image gate, leaving the declared serving contract unverified.
  • Recommendation: After the isolated version check, start the image with its normal entrypoint and add a bounded local request that confirms the server answers on port 8081.
  • Verification: Read the validation script at lines 138-207 and confirm it contains no container start plus request against port 8081.
  • Test coverage: Add a workflow contract test that requires the validation script to start the built image and verify a successful local request to its declared port.
  • Evidence: .github/workflows/llama-cpp-image.yaml:171-177 runs the image only as `llama-server --version` with no published port or HTTP request. managed-inference/images/llama-cpp/image.yaml:71 declares runtime port 8081. test/llama-cpp-image-workflow.test.ts:164-181 asserts the isolated version and filesystem checks but has no assertion for starting the server and probing port 8081.

PRA-2 Warning — Trigger the image gate when its build-argument guard changes

  • Location: .github/workflows/llama-cpp-image.yaml:10
  • Category: workflow
  • Problem: The pull-request path filter omits `scripts/checks/check-production-build-args.sh`, although the build job executes that script before building the image.
  • Impact: A change that weakens or breaks the build-argument guard can merge without the llama.cpp image workflow running to validate the changed guard.
  • Recommendation: Add `scripts/checks/check-production-build-args.sh` to `pull_request.paths` and include it in the workflow contract test's expected path list.
  • Verification: Compare the workflow path filter at lines 10-17 with the executed guard at line 97.
  • Test coverage: Extend `test/llama-cpp-image-workflow.test.ts` to require `scripts/checks/check-production-build-args.sh` in the workflow pull-request path filter.
  • Evidence: .github/workflows/llama-cpp-image.yaml:10-17 lists the workflow path filters and omits `scripts/checks/check-production-build-args.sh`. .github/workflows/llama-cpp-image.yaml:97 invokes `scripts/checks/check-production-build-args.sh`. test/llama-cpp-image-workflow.test.ts:75-84 asserts selected path filters but does not require the guard script path.

Workflow run details

This automated review informs maintainers. Warnings and suggestions do not require a response. A maintainer decides whether to merge.

@cjagwani cjagwani left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head b5e18c7937cb52ba7ed598b44c04efa3687032f6 against accepted epic #8144 and delivery issue #8231. The staged PR-only build is in scope: #8231 explicitly requires both native architectures to build/validate without registry writes before publication. The lack of a current owned-image consumer is therefore expected at this intermediate step and is not itself my blocker.

Changes requested for the following contract failures:

  1. The final runtime image deliberately retains and invokes /bin/sh in .github/workflows/llama-cpp-image.yaml, but #8231's image contract explicitly says the runtime must not include shell tools. The validation currently proves the opposite of the accepted boundary. Remove the runtime shell/tool surface (or obtain and link an explicit maintainer decision changing that contract), and validate user/license/UI properties without executing a shell in the image—for example via image metadata and an exported filesystem inspection.

  2. The manifest compiler is described and tested as fail-closed, but digestReference accepts any digest-pinned path under docker.io or ghcr.io, including non-NVIDIA CUDA bases, and the schema permits unexpected outer fields. #8231 requires NVIDIA CUDA bases and fail-closed handling of mutable/changed dependencies and descriptors. Tighten the exact allowlist/shape validation and add table-driven rejection coverage for unauthorized base registries, invalid runners, malformed digests, duplicate platforms, and unexpected fixed-contract fields. The primary advisor's PRA-2 identifies the same gap.

  3. Exact-head codebase-growth-guardrails fails because test/llama-cpp-image-workflow.test.ts adds two conditional statements. Keep the tests linear by moving setup validation into a named non-test helper or collecting action references and asserting them without conditional branches. This is deterministic and should not be retried unchanged.

Please retain the current no-publication permissions boundary while addressing these items. The native image jobs and CodeRabbit review are still running; any findings there remain additional exact-head evidence.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (4)
test/llama-cpp-image.test.ts (1)

60-67: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

split("=", 2) discards the tail of a value that contains =.

String.prototype.split with a limit does not keep the remainder. It returns only the first two segments. For matrix={"include":[...]} the current values contain no =, so the parse happens to be correct today. If any emitted value gains an =, this helper silently truncates it and the matrix assertion on line 136 fails as a JSON parse error rather than as a clear mismatch.

Split once on the first delimiter.

♻️ Proposed fix
 function parseOutput(value: string): Record<string, string> {
   return Object.fromEntries(
     value
       .trim()
       .split("\n")
-      .map((line) => line.split("=", 2) as [string, string]),
+      .map((line) => {
+        const separator = line.indexOf("=");
+        return [line.slice(0, separator), line.slice(separator + 1)] as [string, string];
+      }),
   );
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/llama-cpp-image.test.ts` around lines 60 - 67, Update parseOutput so
each line is split only at the first "=" while preserving the entire remainder
as the value. Keep the existing Record<string, string> output and
Object.fromEntries behavior unchanged.
.github/workflows/llama-cpp-image.yaml (2)

50-57: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Add a concurrency group for this long build.

pr-build has timeout-minutes: 120 and builds two platforms from source with no cache. Without a concurrency group, each new push to a pull request starts another pair of two-hour CUDA builds while the previous pair still runs. The queue on the ubuntu-24.04-arm runner pool grows quickly.

Cancel superseded runs at the workflow level.

♻️ Proposed change
 permissions:
   contents: read

+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
+  cancel-in-progress: true
+
 jobs:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/llama-cpp-image.yaml around lines 50 - 57, Add a
workflow-level concurrency group for the pr-build job, keyed to the pull request
or workflow context, and enable cancellation of in-progress runs so superseded
pushes do not continue occupying runners. Preserve the existing matrix, timeout,
and fail-fast settings.

67-87: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider a build cache for the CUDA compile step.

This step compiles llama.cpp with CUDA for three device architectures on amd64. It declares no cache-from or cache-to, so every run rebuilds from scratch against a 120-minute timeout. .github/workflows/managed-images.yaml line 179 uses a registry build cache for the same reason.

A registry cache needs credentials, which this read-only workflow must not have. Use the GitHub Actions cache backend instead, which works with the ambient runtime token and keeps the no-publication boundary intact.

♻️ Proposed change
           provenance: false
           sbom: false
+          cache-from: type=gha,scope=llama-cpp-${{ matrix.arch }}
+          cache-to: type=gha,mode=max,scope=llama-cpp-${{ matrix.arch }}

Confirm the runtime measured for a cold build before you rely on timeout-minutes: 120.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/llama-cpp-image.yaml around lines 67 - 87, Add GitHub
Actions build-cache configuration to the “Build native PR image without
publishing” step using docker/build-push-action’s gha cache backend, with a
stable scope shared appropriately across runs. Keep the workflow read-only by
avoiding registry credentials and preserve the existing load, push, provenance,
and SBOM settings; also verify the cold-build duration remains within the
120-minute timeout.
scripts/checks/export-llama-cpp-image-config.mts (1)

79-121: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Consider validating shape instead of mirroring every manifest value.

expectedCmake, expectedBuildPackages, and expectedRuntimePackages restate the full contents of image.yaml lines 32-54 and 61-64. The exporter then rejects the manifest unless it matches that copy exactly. This inverts the intended direction: image.yaml is the declared source of truth, but any change to it fails the exporter until an engineer edits this file too. The same values also appear a third time as literals in managed-inference/images/llama-cpp/Dockerfile.

Keep the fail-closed checks that the manifest cannot express (digestReference, fullRevision, sha256, the runner and platform pairing, the runtime ID range). For the cmake flags and package pins, validate structure and let test/llama-cpp-image.test.ts continue to assert manifest-to-Dockerfile agreement.

♻️ Suggested direction for the package pins
-  const expectedBuildPackages = {
-    "build-essential": "12.10ubuntu1",
-    "ca-certificates": "20260601~24.04.1",
-    cmake: "3.28.3-1build7",
-    curl: "8.5.0-2ubuntu10.11",
-    "libcurl4-openssl-dev": "8.5.0-2ubuntu10.11",
-    "libssl-dev": "3.0.13-0ubuntu3.12",
-  };
-  const expectedRuntimePackages = {
-    "ca-certificates": "20260601~24.04.1",
-    libcurl4t64: "8.5.0-2ubuntu10.11",
-    libgomp1: "14.2.0-4ubuntu2~24.04.1",
-  };
+  const pinnedPackages = (value: unknown): boolean =>
+    typeof value === "object" &&
+    value !== null &&
+    Object.keys(value).length > 0 &&
+    Object.entries(value as Record<string, unknown>).every(
+      ([name, version]) =>
+        /^[a-z0-9][a-z0-9+.-]*$/u.test(name) &&
+        typeof version === "string" &&
+        /^[0-9][A-Za-z0-9.+:~-]*$/u.test(version),
+    );

Then replace the two matchesExactRecord calls for packages with pinnedPackages(...) checks.

Based on the path instruction for scripts/checks/**: "Derive inventories and limits from a canonical source where possible; flag duplicated lists that can silently drift."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/checks/export-llama-cpp-image-config.mts` around lines 79 - 121,
Update the validation in the exporter around expectedCmake,
expectedBuildPackages, and expectedRuntimePackages to stop mirroring manifest
values. Retain fail-closed checks for digestReference, fullRevision, sha256,
runner/platform pairing, and runtime ID limits; validate only the required cmake
shape, and replace both exact package-record comparisons with
pinnedPackages(...) checks. Remove duplicated package and flag inventories while
preserving the existing llama.cpp contract checks and relying on the
manifest/Dockerfile agreement test for exact values.

Source: Path instructions

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@managed-inference/images/llama-cpp/Dockerfile`:
- Line 99: Update the license directory COPY instruction to use a traversable
permission mode, changing --chmod=0444 to --chmod=0555 so the non-root workflow
can access /usr/local/share/licenses/llama.cpp/ while preserving license
readability.
- Around line 59-62: Update the Dockerfile’s CMake configuration to set
GGML_BACKEND_DIR to /opt/llama.cpp/lib when building with GGML_BACKEND_DL=ON,
matching the destination used by the backend modules. Add validation after
packaging that confirms the CUDA backend module is present and loadable by
llama-server, failing the build if CUDA backend availability is not proven.

In `@test/llama-cpp-image-workflow.test.ts`:
- Around line 40-43: Update required<T> to use a nullish-coalescing fallback
that throws the existing error, avoiding an if statement and treating null as
absent. In the action-pin verification loop around step.uses, collect all
declared uses values first, assert the collection is non-empty, then verify
every entry matches fullShaAction without conditional assertions.
- Around line 68-69: Replace the ineffective JSON substring assertion for
"packages:write" with an assertion against the parsed workflow permissions
object, verifying that package publishing permission is absent or not granted.
Keep the existing "docker/login-action" assertion unchanged, and update only the
no-publication-path checks in the workflow test.

In `@test/llama-cpp-image.test.ts`:
- Around line 15-20: Update the paths filter in the llama-cpp-image workflow to
include
managed-inference/recipes/llama-cpp.nemotron-3-nano-30b-a3b.spark-single.v1.yaml,
matching the recipePath used by the llama-cpp image test so recipe changes
trigger the workflow.

---

Nitpick comments:
In @.github/workflows/llama-cpp-image.yaml:
- Around line 50-57: Add a workflow-level concurrency group for the pr-build
job, keyed to the pull request or workflow context, and enable cancellation of
in-progress runs so superseded pushes do not continue occupying runners.
Preserve the existing matrix, timeout, and fail-fast settings.
- Around line 67-87: Add GitHub Actions build-cache configuration to the “Build
native PR image without publishing” step using docker/build-push-action’s gha
cache backend, with a stable scope shared appropriately across runs. Keep the
workflow read-only by avoiding registry credentials and preserve the existing
load, push, provenance, and SBOM settings; also verify the cold-build duration
remains within the 120-minute timeout.

In `@scripts/checks/export-llama-cpp-image-config.mts`:
- Around line 79-121: Update the validation in the exporter around
expectedCmake, expectedBuildPackages, and expectedRuntimePackages to stop
mirroring manifest values. Retain fail-closed checks for digestReference,
fullRevision, sha256, runner/platform pairing, and runtime ID limits; validate
only the required cmake shape, and replace both exact package-record comparisons
with pinnedPackages(...) checks. Remove duplicated package and flag inventories
while preserving the existing llama.cpp contract checks and relying on the
manifest/Dockerfile agreement test for exact values.

In `@test/llama-cpp-image.test.ts`:
- Around line 60-67: Update parseOutput so each line is split only at the first
"=" while preserving the entire remainder as the value. Keep the existing
Record<string, string> output and Object.fromEntries behavior unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: d8f2d6fb-c4a6-40a2-b7a6-a7605d800f50

📥 Commits

Reviewing files that changed from the base of the PR and between 3a7899f and b5e18c7.

📒 Files selected for processing (6)
  • .github/workflows/llama-cpp-image.yaml
  • managed-inference/images/llama-cpp/Dockerfile
  • managed-inference/images/llama-cpp/image.yaml
  • scripts/checks/export-llama-cpp-image-config.mts
  • test/llama-cpp-image-workflow.test.ts
  • test/llama-cpp-image.test.ts

Comment thread managed-inference/images/llama-cpp/Dockerfile
Comment thread managed-inference/images/llama-cpp/Dockerfile Outdated
Comment on lines +40 to +43
function required<T>(value: T | undefined, message: string): T {
if (value === undefined) throw new Error(message);
return value;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Remove the two if statements to clear the growth guardrail, and make the action-pin loop non-vacuous.

CI fails on this file: "this changed test file adds 2 if statements, up from 0 at the base. Keep test bodies linear." The two statements are line 41 and line 112.

Line 112 is also a vacuous assertion. if (step.uses) expect(...) asserts nothing when no step declares uses. The test then passes while proving no pin at all. Collect the uses values first, assert the collection is not empty, then assert each entry matches fullShaAction.

Line 41 can express the same guard without a statement.

🐛 Proposed fix
 function required<T>(value: T | undefined, message: string): T {
-  if (value === undefined) throw new Error(message);
-  return value;
+  return (
+    value ??
+    (() => {
+      throw new Error(message);
+    })()
+  );
 }
   it("pins actions and validates the native non-root read-only image (`#8231`)", () => {
-    for (const job of Object.values(workflow.jobs ?? {})) {
-      for (const step of job.steps ?? []) {
-        if (step.uses) expect(step.uses).toMatch(fullShaAction);
-      }
-    }
+    const actionReferences = Object.values(workflow.jobs ?? {})
+      .flatMap((job) => job.steps ?? [])
+      .map((step) => step.uses)
+      .filter((uses): uses is string => uses !== undefined);
+    expect(actionReferences.length).toBeGreaterThan(0);
+    expect(actionReferences.filter((uses) => !fullShaAction.test(uses))).toEqual([]);

Note that ?? treats null as absent while === undefined does not. YAML.parse produces null for an empty key, so the ?? form is the safer guard for this input.

Also applies to: 110-114

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/llama-cpp-image-workflow.test.ts` around lines 40 - 43, Update
required<T> to use a nullish-coalescing fallback that throws the existing error,
avoiding an if statement and treating null as absent. In the action-pin
verification loop around step.uses, collect all declared uses values first,
assert the collection is non-empty, then verify every entry matches
fullShaAction without conditional assertions.

Sources: Path instructions, Pipeline failures

Comment thread test/llama-cpp-image-workflow.test.ts Outdated
Comment thread test/llama-cpp-image.test.ts
@ericksoa

ericksoa commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the exact-head review findings in 09e94660ed48f563c7349f69a67adb2593f4def2:

  1. Removed command shells from the runtime image. Required and forbidden filesystem paths now live in the YAML contract, and PR CI inspects docker export output without executing a shell in the image.
  2. Restricted both CUDA bases to digest-pinned docker.io/nvidia/cuda references, added exact key-shape validation at every fixed manifest layer, and added table-driven rejection coverage for non-NVIDIA bases, runner/platform mismatch, malformed digests, duplicate platforms, and unexpected fields.
  3. Linearized the workflow tests; the exact conditional-growth scan passes.
  4. Fixed additional old-head findings: GCC 14 is selected declaratively for the Arm SME variants, GGML_BACKEND_DIR points at the packaged module directory, the CUDA module is required in the exported filesystem, license directories/files retain traversable/read-only modes, the recipe is a workflow trigger, permissions are tested structurally, and long builds now use per-architecture GHA caches plus superseded-run cancellation.

Focused local evidence: 2 files / 13 tests passed; CLI typecheck, repository checks, test-title/size/conditional guards, Biome, Prettier, Hadolint, git diff --check, and normal commit/push hooks passed. The workflow retains contents: read, no registry login, and push: false. Native exact-head CI is now running.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/llama-cpp-image-workflow.test.ts`:
- Around line 74-78: Update the permission validation in the workflow test to
handle both scalar and map forms for workflow and job permissions without
relying on Object.values for strings. Ensure scalar job permissions such as
write-all are detected and rejected, and add a job-level permissions: write-all
fixture to verify this case.

In `@test/llama-cpp-image.test.ts`:
- Around line 186-188: Update the duplicate-platform test case to change the
runner alongside the platform replacement, using a runner compatible with
linux/amd64 so validation reaches the duplicate-platform uniqueness check. Keep
the test focused on exercising duplicate-platform rejection rather than runner
validation.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 8209efef-38ad-438e-bf57-2c03b0f90a52

📥 Commits

Reviewing files that changed from the base of the PR and between b5e18c7 and 09e9466.

📒 Files selected for processing (6)
  • .github/workflows/llama-cpp-image.yaml
  • managed-inference/images/llama-cpp/Dockerfile
  • managed-inference/images/llama-cpp/image.yaml
  • scripts/checks/export-llama-cpp-image-config.mts
  • test/llama-cpp-image-workflow.test.ts
  • test/llama-cpp-image.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • managed-inference/images/llama-cpp/image.yaml
  • .github/workflows/llama-cpp-image.yaml
  • managed-inference/images/llama-cpp/Dockerfile

Comment thread test/llama-cpp-image-workflow.test.ts Outdated
Comment thread test/llama-cpp-image.test.ts

@cjagwani cjagwani left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head 09e94660ed48f563c7349f69a67adb2593f4def2. The follow-up resolves my original blockers: the final image removes the shell paths, the workflow validates via exported filesystem/image metadata rather than invoking a shell, the compiler now enforces exact manifest shapes and NVIDIA-owned digest-pinned bases, the rejection matrix covers the requested cases, and the guardrail is green.

Two current fail-closed test gaps remain, matching CodeRabbit's exact-head findings:

  1. test/llama-cpp-image-workflow.test.ts types permissions only as maps and flattens them with Object.values. GitHub Actions also accepts scalar permission forms such as write-all; Object.values("write-all") yields characters, so this no-publication assertion would not detect a job-level scalar grant. Normalize/reject both scalar and map forms and add a mutated fixture proving permissions: write-all fails.

  2. The duplicate-platform mutation changes linux/arm64 to linux/amd64 but leaves the ARM runner. Validation therefore rejects the runner mismatch before reaching the uniqueness check, so the test does not prove duplicate platforms are rejected. Change the runner in that candidate as well and assert the duplicate-platform error path.

Please retain the current read-only/no-publication boundary and native amd64/arm64 validation while fixing these two tests. The unrelated CLI shard failure and native image jobs are separate exact-head receipts and do not waive these deterministic gaps.

@ericksoa

ericksoa commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Exact-head remediation update for 8ca6b8cf03d5f59ac730ab3047398341499ed453:

  • permission checks now normalize both scalar and map forms, with a hostile job-level permissions: write-all fixture
  • the duplicate-platform mutation now also selects the compatible runner, so it reaches and proves the uniqueness rejection path
  • the native image build now runs the repository production build-argument guard before docker/build-push-action; the exact dependency-policy regression and focused image tests pass
  • final-head native image validation is green on arm64 (2m25s) and amd64 (3m29s); CodeRabbit and both review-advisor lanes completed successfully, with 0 blocking advisor findings

This remains a read-only PR build: no publication, registry write, support activation, or default-provider change.

@ericksoa
ericksoa merged commit 3f7097b into main Aug 4, 2026
85 of 88 checks passed
@ericksoa
ericksoa deleted the feat/llamacpp-owned-image-8231 branch August 4, 2026 18:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: inference Inference routing, serving, model selection, or outputs area: local-models Local model providers, downloads, launch, or connectivity area: providers Inference provider integrations and provider behavior feature PR adds or expands user-visible functionality platform: dgx-spark Affects DGX Spark hardware or workflows

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants