Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
d8c4e63
feat(inference): qualify llama.cpp on NVIDIA GPUs
ericksoa Aug 8, 2026
85c23cf
docs(inference): correct llama.cpp evidence ownership
ericksoa Aug 8, 2026
349dd51
test(e2e): preserve live llama.cpp bridge identity
ericksoa Aug 8, 2026
0bdd808
test(e2e): narrow generic llama.cpp qualification
ericksoa Aug 8, 2026
9478fc2
test(e2e): use valid llama.cpp sandbox name
ericksoa Aug 8, 2026
709b526
fix(inference): match x64 readiness for llama.cpp
ericksoa Aug 8, 2026
7d54b35
fix(inference): use multiarch llama.cpp tool image
ericksoa Aug 8, 2026
e6ed849
fix(inference): honor Docker created state
ericksoa Aug 8, 2026
bdb171c
fix(inference): defer runtime port proof
ericksoa Aug 8, 2026
46388ef
fix(inference): bridge managed llama.cpp privately
ericksoa Aug 8, 2026
cc4cd9e
fix(inference): avoid bridge key-root mutation
ericksoa Aug 8, 2026
7ddae8b
test(e2e): prove llama.cpp gpu residency
ericksoa Aug 8, 2026
9063e20
fix(inference): revalidate selected llama.cpp preset
ericksoa Aug 8, 2026
90d270f
fix(inference): preserve finalized llama.cpp key authority
ericksoa Aug 8, 2026
e9b7059
test(e2e): probe protected llama.cpp auth endpoint
ericksoa Aug 8, 2026
850c3db
merge: resolve conflicts with main
github-actions[bot] Aug 8, 2026
e251392
test(e2e): run llama.cpp on exact PR head
ericksoa Aug 8, 2026
0f39c32
merge: integrate current PR head
ericksoa Aug 8, 2026
c83fdf4
fix(e2e): bind llama.cpp PR shard identity
ericksoa Aug 8, 2026
bc51dda
fix(e2e): correlate llama.cpp PR evidence
ericksoa Aug 8, 2026
c076d0c
ci: remove full E2E PR merge gate
ericksoa Aug 8, 2026
84144b4
merge(main): reconcile E2E artifact consumers
ericksoa Aug 8, 2026
8ab47a3
merge: resolve conflicts with main
ericksoa Aug 8, 2026
20d7558
merge: resolve conflicts with main
ericksoa Aug 8, 2026
e219f37
test: remove stale GPU helper imports
ericksoa Aug 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1923,6 +1923,7 @@ jobs:
E2E_JOB: "1"
E2E_TARGET_ID: "gpu-e2e"
E2E_ARTIFACT_DIR: ${{ github.workspace }}/e2e-artifacts/live/gpu-e2e
E2E_LLAMA_CPP_DEDICATED_LANE: "1"
NEMOCLAW_CLI_BIN: ${{ github.workspace }}/bin/nemoclaw.js
NEMOCLAW_RUN_LIVE_E2E: "1"
NEMOCLAW_NON_INTERACTIVE: "1"
Expand Down Expand Up @@ -1977,6 +1978,71 @@ jobs:
shell: bash
run: bash .github/scripts/docker-auth-cleanup.sh

llama-cpp-generic-gpu:
needs: generate-matrix
if: ${{ (github.event_name != 'workflow_dispatch' || (inputs.jobs == '' && inputs.targets == '')) || contains(format(',{0},', inputs.jobs), ',llama-cpp-generic-gpu,') || contains(format(',{0},', inputs.targets), ',llama-cpp-generic-gpu,') }}
runs-on: linux-amd64-gpu-rtxpro6000-latest-1

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.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Register the custom runner label with actionlint.

linux-amd64-gpu-rtxpro6000-latest-1 is a custom label. Actionlint reports it as unknown. Add the label to the repository actionlint configuration, or use a registered runner label.

🧰 Tools
🪛 actionlint (1.7.12)

[error] 1984-1984: label "linux-amd64-gpu-rtxpro6000-latest-1" is unknown. available labels are "windows-latest", "windows-latest-8-cores", "windows-2025", "windows-2025-vs2026", "windows-2022", "windows-11-arm", "ubuntu-slim", "ubuntu-latest", "ubuntu-latest-4-cores", "ubuntu-latest-8-cores", "ubuntu-latest-16-cores", "ubuntu-24.04", "ubuntu-24.04-arm", "ubuntu-22.04", "ubuntu-22.04-arm", "macos-latest", "macos-latest-xlarge", "macos-latest-large", "macos-26-intel", "macos-26-xlarge", "macos-26-large", "macos-26", "macos-15-intel", "macos-15-xlarge", "macos-15-large", "macos-15", "macos-14-xlarge", "macos-14-large", "macos-14", "self-hosted", "x64", "arm", "arm64", "linux", "macos", "windows". if it is a custom label for self-hosted runner, set list of labels in actionlint.yaml config file

(runner-label)

🤖 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/e2e.yaml at line 1984, The workflow uses the unregistered
custom runner label linux-amd64-gpu-rtxpro6000-latest-1. Register this label in
the repository’s actionlint configuration, or replace the runs-on value with an
already registered runner label while preserving the required GPU runner
selection.

Source: Linters/SAST tools

timeout-minutes: 120
env:
E2E_JOB: "1"
E2E_TARGET_ID: "llama-cpp-generic-gpu"
E2E_ARTIFACT_DIR: ${{ github.workspace }}/e2e-artifacts/live/llama-cpp-generic-gpu
NEMOCLAW_CLI_BIN: ${{ github.workspace }}/bin/nemoclaw.js
NEMOCLAW_RUN_LIVE_E2E: "1"
NEMOCLAW_E2E_EXPECTED_SHA: ${{ inputs.checkout_sha || github.sha }}
NEMOCLAW_NON_INTERACTIVE: "1"
NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1"
NEMOCLAW_PROVIDER: "install-llama-cpp"
NEMOCLAW_LLAMACPP_RECIPE: "llama-cpp.nemotron-3-nano-30b-a3b.spark-single.v1"
NEMOCLAW_SANDBOX_NAME: "e2e-llamacpp-gpu"
OPENSHELL_GATEWAY: "nemoclaw"
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: ${{ inputs.checkout_repository || github.repository }}
ref: ${{ inputs.checkout_sha || github.sha }}
persist-credentials: false

- *dockerhub-auth

- name: Prepare E2E workspace
uses: NVIDIA/NemoClaw/.github/actions/prepare-e2e@f6304bc25fc35bfaa441c8c2fbfee38f72805a75
with:
build-cli: "false"

- name: Restore exact-commit CLI artifact
uses: NVIDIA/NemoClaw/.github/actions/restore-e2e-cli-artifact@c246409193a31133cab10c8a3589001cc0d59eb3
with:
provenance-json: ${{ needs.generate-matrix.outputs.cli_artifact_provenance }}

- name: Install OpenShell CLI
run: bash scripts/install-openshell.sh

- name: Run generic NVIDIA GPU llama.cpp live test
run: |
set -euo pipefail
export PATH="$HOME/.local/bin:$HOME/.npm-global/bin:$PATH"
if command -v openshell >/dev/null 2>&1; then
OPENSHELL_BIN="$(command -v openshell)"
elif [ -x "$HOME/.local/bin/openshell" ]; then
OPENSHELL_BIN="$HOME/.local/bin/openshell"
else
echo "::error::OpenShell CLI not found after install"
exit 1
fi
export OPENSHELL_BIN
"$OPENSHELL_BIN" --version
npx tsx tools/e2e/live-vitest-invocation.mts run --test-path test/e2e/live/llama-cpp-generic-gpu.test.ts

- name: Upload generic NVIDIA GPU llama.cpp artifacts
if: always()
uses: NVIDIA/NemoClaw/.github/actions/upload-e2e-artifacts@7768e15eb90d3ee2d33432f481dfe8747e4f6d57

- name: Clean up Docker auth
if: always()
shell: bash
run: bash .github/scripts/docker-auth-cleanup.sh

# Every main push runs this lane. Manual PR qualification also requires the
# exact candidate activation contract.
managed-image-multiarch-startup:
Expand Down Expand Up @@ -7261,6 +7327,7 @@ jobs:
inference-routing,
cloud-inference,
gpu-e2e,
llama-cpp-generic-gpu,
managed-image-multiarch-startup,
llama-cpp-dgx-spark-plan,
llama-cpp-dgx-spark-qualification,
Expand Down
103 changes: 103 additions & 0 deletions .github/workflows/pr-self-hosted.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,109 @@ jobs:
- id: get-pr-info
uses: nv-gha-runners/get-pr-info@090577647b8ddc4e06e809e264f7881650ecdccf # main

select-llama-cpp-generic-gpu:
needs: get-pr-info
runs-on: ubuntu-latest
outputs:
selected: ${{ steps.changed.outputs.selected }}
steps:
- id: changed
name: Select llama.cpp generic GPU E2E from PR files
env:
GH_TOKEN: ${{ github.token }}
PR_INFO: ${{ needs.get-pr-info.outputs.pr-info }}
shell: bash
run: |
set -euo pipefail
pr_number="$(jq -er '.number | select(type == "number" and . > 0)' <<<"$PR_INFO")"
head_sha="$(jq -er '.head.sha | select(test("^[a-f0-9]{40}$"))' <<<"$PR_INFO")"
[[ "$head_sha" == "$GITHUB_SHA" ]] || {
echo "::error::Copied PR branch SHA does not match the current PR head" >&2
exit 1
}
if gh api --paginate --slurp \
"repos/$GITHUB_REPOSITORY/pulls/$pr_number/files?per_page=100" \
| jq -e '
flatten
| any(
.filename == ".github/workflows/e2e.yaml"
or .filename == ".github/workflows/pr-self-hosted.yaml"
or .filename == "test/e2e/live/llama-cpp-generic-gpu.test.ts"
or .filename == "test/e2e/live/gpu-e2e-helpers.ts"
or .filename == "test/e2e/mock-parity.json"
or .filename == "tools/e2e/cli-artifact-workflow-boundary.mts"
or .filename == "tools/e2e/workflow-boundary.mts"
or (.filename | startswith("managed-inference/presets/llama-cpp."))
or (.filename | startswith("managed-inference/recipes/llama-cpp."))
or (.filename | startswith("src/lib/inference/llama-cpp/"))
or (.filename | startswith("src/lib/onboard/runtime-provider/docker-llama-cpp"))
)
' >/dev/null; then
selected=true
else
selected=false
fi
printf 'selected=%s\n' "$selected" >>"$GITHUB_OUTPUT"

llama-cpp-generic-gpu:
name: llama.cpp on generic NVIDIA GPU
needs:
- get-pr-info
- select-llama-cpp-generic-gpu
if: ${{ needs.select-llama-cpp-generic-gpu.outputs.selected == 'true' }}
runs-on: linux-amd64-gpu-rtxpro6000-latest-1
timeout-minutes: 120
env:
E2E_ARTIFACT_DIR: ${{ github.workspace }}/e2e-artifacts/live/llama-cpp-generic-gpu
E2E_JOB: "1"
E2E_TARGET_ID: llama-cpp-generic-gpu
NEMOCLAW_ACCEPT_THIRD_PARTY_SOFTWARE: "1"
NEMOCLAW_CLI_BIN: ${{ github.workspace }}/bin/nemoclaw.js
NEMOCLAW_E2E_EXPECTED_SHA: ${{ fromJSON(needs.get-pr-info.outputs.pr-info).head.sha }}
NEMOCLAW_E2E_SHARD: default
NEMOCLAW_LLAMACPP_RECIPE: llama-cpp.nemotron-3-nano-30b-a3b.spark-single.v1
NEMOCLAW_NON_INTERACTIVE: "1"
NEMOCLAW_PROVIDER: install-llama-cpp
NEMOCLAW_RUN_LIVE_E2E: "1"
NEMOCLAW_SANDBOX_NAME: e2e-llamacpp-gpu
OPENSHELL_GATEWAY: nemoclaw
steps:
- name: Checkout exact PR head
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
ref: ${{ fromJSON(needs.get-pr-info.outputs.pr-info).head.sha }}

- name: Prepare E2E workspace
uses: NVIDIA/NemoClaw/.github/actions/prepare-e2e@f6304bc25fc35bfaa441c8c2fbfee38f72805a75

- name: Bind E2E correlation identity
shell: bash
run: |
set -euo pipefail
correlation_id="$(node --input-type=module -e \
'import { randomUUID } from "node:crypto"; console.log(randomUUID())')"
[[ "$correlation_id" =~ ^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$ ]]
printf 'NEMOCLAW_E2E_CORRELATION_ID=%s\n' "$correlation_id" >>"$GITHUB_ENV"

- name: Install OpenShell CLI
run: bash scripts/install-openshell.sh

- name: Run llama.cpp generic NVIDIA GPU live test
shell: bash
run: |
set -euo pipefail
export PATH="$HOME/.local/bin:$HOME/.npm-global/bin:$PATH"
OPENSHELL_BIN="$(command -v openshell)"
export OPENSHELL_BIN
"$OPENSHELL_BIN" --version
npx tsx tools/e2e/live-vitest-invocation.mts run \
--test-path test/e2e/live/llama-cpp-generic-gpu.test.ts

- name: Upload llama.cpp generic NVIDIA GPU artifacts
if: always()
uses: NVIDIA/NemoClaw/.github/actions/upload-e2e-artifacts@7768e15eb90d3ee2d33432f481dfe8747e4f6d57

build-sandbox-images:
runs-on: linux-amd64-cpu4
timeout-minutes: 45
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

apiVersion: nemoclaw.nvidia.com/managed-inference/v1
kind: ServingPreset

metadata:
id: llama-cpp.linux-amd64-nvidia.single.nemotron-3-nano-30b-a3b
displayName: NVIDIA Nemotron 3 Nano 30B-A3B on one Linux x86_64 NVIDIA GPU
supportState: experimental

spec:
selection: explicit-only
priority: 440

requirements:
all:
- readiness:
scope: everyNode
kind: capability
id: host.platform.supported
state: present
- readiness:
scope: everyNode
kind: capability
id: host.docker.available
state: present
- readiness:
scope: everyNode
kind: capability
id: host.docker.daemon_reachable
state: present
- readiness:
scope: everyNode
kind: capability
id: host.docker.runtime_supported
state: present
- readiness:
scope: everyNode
kind: capability
id: host.docker.storage_compatible
state: present
- readiness:
scope: everyNode
kind: capability
id: host.gpu.nvidia_available
state: present
- readiness:
scope: everyNode
kind: capability
id: host.gpu.container_toolkit_available
state: present
- readiness:
scope: everyNode
kind: capability
id: host.gpu.cdi_healthy
state: present
- readiness:
scope: everyNode
kind: observation
id: host.os.platform
comparison:
operator: equals
value: linux
- readiness:
scope: everyNode
kind: observation
id: host.os.architecture
comparison:
operator: equals
value: x64
- readiness:
scope: everyNode
kind: observation
id: host.docker.runtime
comparison:
operator: equals
value: docker
- readiness:
scope: everyNode
kind: observation
id: host.gpu.count
comparison:
operator: at-least
value: 1
- readiness:
scope: everyNode
kind: observation
id: host.gpu.driver_version
comparison:
operator: version-at-least
value: 580.65.06

plan:
backend: install-llama-cpp
recipeRef: llama-cpp.nemotron-3-nano-30b-a3b.spark-single.v1
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ spec:
license: NVIDIA-Open-Model-License
acquisition:
ref: hugging-face-exact-file/v1
downloaderImage: nvcr.io/nvidia/vllm@sha256:9204569b17ee4c0eff75194b8e6e458479c8aee18953b5ab9cf359fcdac659e2
downloaderImage: nvcr.io/nvidia/vllm@sha256:94e21552f644e0c1627464ba89d2f7a4ce7442e196f72afa0bb5d7fba23cbb03
authentication:
mode: optional
environment: HF_TOKEN
Expand Down Expand Up @@ -92,7 +92,7 @@ spec:
contractRef: llama-cpp.server-readiness/v1
timeoutSeconds: 1800
expectedModel: nvidia-nemotron-3-nano-30b-a3b
probeImage: nvcr.io/nvidia/vllm@sha256:9204569b17ee4c0eff75194b8e6e458479c8aee18953b5ab9cf359fcdac659e2
probeImage: nvcr.io/nvidia/vllm@sha256:94e21552f644e0c1627464ba89d2f7a4ce7442e196f72afa0bb5d7fba23cbb03
probes:
models: true
health: true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export const LLAMA_CPP_DGX_SPARK_CUDA_DEVELOPMENT_BASE =
export const LLAMA_CPP_DGX_SPARK_CUDA_RUNTIME_BASE =
"docker.io/nvidia/cuda@sha256:789e629e49401647e22b7054ae9c6c4f6427dba68010ba428deb4cc6b063676e" as const;
export const LLAMA_CPP_DGX_SPARK_TOOL_IMAGE =
"nvcr.io/nvidia/vllm@sha256:9204569b17ee4c0eff75194b8e6e458479c8aee18953b5ab9cf359fcdac659e2" as const;
"nvcr.io/nvidia/vllm@sha256:94e21552f644e0c1627464ba89d2f7a4ce7442e196f72afa0bb5d7fba23cbb03" as const;
export const LLAMA_CPP_DGX_SPARK_MINIMUM_DRIVER_VERSION = "580.65.06" as const;
export const LLAMA_CPP_DGX_SPARK_PROTOCOL_PROBES = [
"health",
Expand Down
8 changes: 3 additions & 5 deletions src/lib/inference/llama-cpp/host-local-runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ describe("llama.cpp host-local runtime materializer", () => {
`type=bind,source=${runtime.model.hostPath},target=/models/${input.model.file.path},readonly`,
`type=bind,source=${runtime.apiKeyHostPath},target=/run/secrets/llama-cpp-api-key,readonly`,
]);
expect(valuesAfter(argv, "--publish")).toEqual(["127.0.0.1::8081"]);
expect(valuesAfter(argv, "--publish")).toEqual([]);
expect(valuesAfter(argv, "--gpus")).toEqual(["driver=nvidia,count=1"]);
expect(valuesAfter(argv, "--gpu-layers")).toEqual(["all"]);
expect(valuesAfter(argv, "--ctx-size")).toEqual([String(input.serve.contextSize)]);
Expand All @@ -160,13 +160,13 @@ describe("llama.cpp host-local runtime materializer", () => {
expect(argv.join("\n")).not.toContain("huggingface.co");
});

it("publishes the fixed loopback host port when the bindings pin one", () => {
it("leaves fixed host-port bridging to the Docker lifecycle provider", () => {
const argv = buildLlamaCppHostLocalDockerArgv(contract(), {
...bindings(),
hostPort: LLAMA_CPP_PORT,
});

expect(valuesAfter(argv, "--publish")).toEqual([`127.0.0.1:${String(LLAMA_CPP_PORT)}:8081`]);
expect(valuesAfter(argv, "--publish")).toEqual([]);
});

it("takes launch settings from the declared contract instead of code defaults (#8144)", () => {
Expand Down Expand Up @@ -206,8 +206,6 @@ describe("llama.cpp host-local runtime materializer", () => {
"unless-stopped",
"--user",
"1001:1001",
"--publish",
"127.0.0.1::8081",
"--read-only",
"--cap-drop",
"ALL",
Expand Down
4 changes: 1 addition & 3 deletions src/lib/inference/llama-cpp/host-local-runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export interface LlamaCppHostLocalRuntimeBindings {
readonly apiKeyHostPath: string;
readonly containerName: string;
readonly imageReference: string;
/** Fixed loopback host port for product installs; omitted only by isolated qualification. */
/** Fixed host bridge port for product installs; omitted only by isolated qualification. */
readonly hostPort?: number;
readonly model: VerifiedLocalModelArtifact;
/** The caller must create this named Docker network with `--internal` before launch. */
Expand Down Expand Up @@ -286,8 +286,6 @@ export function buildLlamaCppHostLocalDockerArgv(
contract.runtime.restartPolicy,
"--user",
runtimeIdentity,
"--publish",
`127.0.0.1:${bindings.hostPort === undefined ? "" : String(bindings.hostPort)}:${String(serve.port)}`,
"--read-only",
"--cap-drop",
"ALL",
Expand Down
Loading
Loading