Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
6 changes: 3 additions & 3 deletions managed-inference/images/llama-cpp/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
ARG CUDA_DEV_IMAGE
ARG CUDA_RUNTIME_IMAGE

FROM ${CUDA_DEV_IMAGE} AS build

Check warning on line 7 in managed-inference/images/llama-cpp/Dockerfile

View workflow job for this annotation

GitHub Actions / Build native llama.cpp server (arm64)

Default value for global ARG results in an empty or invalid base image name

InvalidDefaultArgInFrom: Default value for ARG ${CUDA_DEV_IMAGE} results in empty or invalid base image name More info: https://docs.docker.com/go/dockerfile/rule/invalid-default-arg-in-from/

Check warning on line 7 in managed-inference/images/llama-cpp/Dockerfile

View workflow job for this annotation

GitHub Actions / Build native llama.cpp server (amd64)

Default value for global ARG results in an empty or invalid base image name

InvalidDefaultArgInFrom: Default value for ARG ${CUDA_DEV_IMAGE} results in empty or invalid base image name More info: https://docs.docker.com/go/dockerfile/rule/invalid-default-arg-in-from/

ARG LLAMA_CPP_REVISION
ARG LLAMA_CPP_ARCHIVE_SHA256
Expand All @@ -29,11 +29,11 @@
build-essential=12.10ubuntu1 \
ca-certificates=20260601~24.04.1 \
cmake=3.28.3-1build7 \
curl=8.5.0-2ubuntu10.11 \
curl=8.5.0-2ubuntu10.12 \
g++-14=14.2.0-4ubuntu2~24.04.1 \
gcc-14=14.2.0-4ubuntu2~24.04.1 \
golang-go=2:1.22~2build1 \
libcurl4-openssl-dev=8.5.0-2ubuntu10.11 \
libcurl4-openssl-dev=8.5.0-2ubuntu10.12 \
libssl-dev=3.0.13-0ubuntu3.12 \
&& rm -rf /var/lib/apt/lists/*

Expand Down Expand Up @@ -98,7 +98,7 @@
&& chmod 0555 /opt/llama.cpp/licenses/go \
&& chmod 0444 /opt/llama.cpp/licenses/go/copyright

FROM ${CUDA_RUNTIME_IMAGE} AS runtime

Check warning on line 101 in managed-inference/images/llama-cpp/Dockerfile

View workflow job for this annotation

GitHub Actions / Build native llama.cpp server (arm64)

Default value for global ARG results in an empty or invalid base image name

InvalidDefaultArgInFrom: Default value for ARG ${CUDA_RUNTIME_IMAGE} results in empty or invalid base image name More info: https://docs.docker.com/go/dockerfile/rule/invalid-default-arg-in-from/

Check warning on line 101 in managed-inference/images/llama-cpp/Dockerfile

View workflow job for this annotation

GitHub Actions / Build native llama.cpp server (amd64)

Default value for global ARG results in an empty or invalid base image name

InvalidDefaultArgInFrom: Default value for ARG ${CUDA_RUNTIME_IMAGE} results in empty or invalid base image name More info: https://docs.docker.com/go/dockerfile/rule/invalid-default-arg-in-from/

ARG CUDA_DEV_IMAGE
ARG CUDA_RUNTIME_IMAGE
Expand All @@ -123,7 +123,7 @@
RUN apt-get update \
&& DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
ca-certificates=20260601~24.04.1 \
libcurl4t64=8.5.0-2ubuntu10.11 \
libcurl4t64=8.5.0-2ubuntu10.12 \
libgomp1=14.2.0-4ubuntu2~24.04.1 \
&& groupadd --gid "${RUNTIME_GID}" nemoclaw-llama \
&& useradd --uid "${RUNTIME_UID}" --gid "${RUNTIME_GID}" \
Expand Down
11 changes: 6 additions & 5 deletions managed-inference/images/llama-cpp/image.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ spec:
- context-window
- authentication
- malformed-request
- request-body-limit
Comment thread
prekshivyas marked this conversation as resolved.
- cancellation
- client-timeout
- log-redaction
Expand All @@ -89,8 +90,8 @@ spec:

source:
repository: https://github.com/ggml-org/llama.cpp
revision: 22dc605c4ead20e36f447cc67b55ef87e523bd55
archiveSha256: sha256:975f70723e053785e894f4e1d9cf770f2f1a7bc762fd3af174ff5635014108b6
revision: 8e7f22b67ef4667b4ddd50230771287f328cfb3f
archiveSha256: sha256:45a24299e7a24410624489d19924d492bc71a120fa17d9b7cb32f6d5c4f1aed0

cuda:
developmentBase: docker.io/nvidia/cuda@sha256:ef2203909e80b8b976cfc672f7e2ae2b00bc0e25c404ee86d89e10a3802f1c52
Expand All @@ -115,11 +116,11 @@ spec:
build-essential: 12.10ubuntu1
ca-certificates: 20260601~24.04.1
cmake: 3.28.3-1build7
curl: 8.5.0-2ubuntu10.11
curl: 8.5.0-2ubuntu10.12
g++-14: 14.2.0-4ubuntu2~24.04.1
gcc-14: 14.2.0-4ubuntu2~24.04.1
golang-go: 2:1.22~2build1
libcurl4-openssl-dev: 8.5.0-2ubuntu10.11
libcurl4-openssl-dev: 8.5.0-2ubuntu10.12
libssl-dev: 3.0.13-0ubuntu3.12
cmake:
ggmlBackendDl: true
Expand Down Expand Up @@ -162,7 +163,7 @@ spec:
- /usr/bin/sh
packages:
ca-certificates: 20260601~24.04.1
libcurl4t64: 8.5.0-2ubuntu10.11
libcurl4t64: 8.5.0-2ubuntu10.12
libgomp1: 14.2.0-4ubuntu2~24.04.1
writablePaths:
- /tmp
44 changes: 23 additions & 21 deletions managed-inference/images/llama-cpp/request-guard/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,27 +97,29 @@ func requireExactCommandMarker(command []string, option string) error {

func validateSupportedCommandOptions(command []string) error {
allowed := map[string]bool{
"--alias": true,
"--api-key-file": true,
"--batch-size": true,
"--cache-type-k": true,
"--cache-type-v": true,
"--ctx-size": true,
"--flash-attn": true,
"--gpu-layers": true,
"--host": true,
"--metrics": false,
"--model": true,
"--no-agent": false,
"--no-mmproj": false,
"--no-slots": false,
"--no-ui": false,
"--n-predict": true,
"--parallel": true,
"--port": true,
"--sleep-idle-seconds": true,
"--timeout": true,
"--ubatch-size": true,
"--alias": true,
"--api-key-file": true,
"--batch-size": true,
"--cache-type-k": true,
"--cache-type-v": true,
"--chat-template-kwargs": true,
"--ctx-size": true,
"--flash-attn": true,
"--gpu-layers": true,
"--host": true,
"--jinja": false,
"--metrics": false,
"--model": true,
"--no-agent": false,
"--no-mmproj": false,
"--no-slots": false,
"--no-ui": false,
"--n-predict": true,
"--parallel": true,
"--port": true,
"--sleep-idle-seconds": true,
"--timeout": true,
"--ubatch-size": true,
}
seen := make(map[string]bool, len(allowed))
for index := 0; index < len(command); index++ {
Expand Down
2 changes: 2 additions & 0 deletions managed-inference/images/llama-cpp/request-guard/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ func TestParseConfigRequiresEveryDeclaredValue(t *testing.T) {
"--port", "8082",
"--api-key-file", llamaServerAPIKeyPath,
"--n-predict", "4096",
"--jinja",
"--chat-template-kwargs", `{"reasoning_strength":"low"}`,
"--no-ui",
"--no-slots",
"--no-mmproj",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ spec:
value: f16
speculativeDecoding: disabled
limits:
maxRequestBodyBytes: 1048576
maxRequestBodyBytes: 16384
maxRequestHeaderBytes: 32768
maxOutputTokens: 4096
requestTimeoutSeconds: 900
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ spec:
technology: llama.cpp
source:
repository: ggml-org/llama.cpp
revision: 22dc605c4ead20e36f447cc67b55ef87e523bd55
revision: 8e7f22b67ef4667b4ddd50230771287f328cfb3f

model:
id: unsloth/Nemotron-3-Nano-30B-A3B-GGUF
Expand Down Expand Up @@ -86,7 +86,7 @@ spec:
value: f16
speculativeDecoding: disabled
limits:
maxRequestBodyBytes: 1048576
maxRequestBodyBytes: 32768
maxRequestHeaderBytes: 32768
maxOutputTokens: 4096
requestTimeoutSeconds: 900
Expand Down
6 changes: 3 additions & 3 deletions scripts/checks/export-llama-cpp-image-config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -625,11 +625,11 @@ export function loadLlamaCppImageConfig(
"build-essential": "12.10ubuntu1",
"ca-certificates": "20260601~24.04.1",
cmake: "3.28.3-1build7",
curl: "8.5.0-2ubuntu10.11",
curl: "8.5.0-2ubuntu10.12",
"g++-14": "14.2.0-4ubuntu2~24.04.1",
"gcc-14": "14.2.0-4ubuntu2~24.04.1",
"golang-go": "2:1.22~2build1",
"libcurl4-openssl-dev": "8.5.0-2ubuntu10.11",
"libcurl4-openssl-dev": "8.5.0-2ubuntu10.12",
"libssl-dev": "3.0.13-0ubuntu3.12",
};
const expectedCompiler = {
Expand All @@ -639,7 +639,7 @@ export function loadLlamaCppImageConfig(
};
const expectedRuntimePackages = {
"ca-certificates": "20260601~24.04.1",
libcurl4t64: "8.5.0-2ubuntu10.11",
libcurl4t64: "8.5.0-2ubuntu10.12",
libgomp1: "14.2.0-4ubuntu2~24.04.1",
};
const expectedRequiredPaths = [
Expand Down
124 changes: 124 additions & 0 deletions scripts/checks/llama-cpp-dgx-spark-protocol-qualification.mts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import {
LLAMA_CPP_DGX_SPARK_PROTOCOL_PROBES,
LLAMA_CPP_DGX_SPARK_REJECTED_REQUEST_BODY_BYTES,
LLAMA_CPP_DGX_SPARK_REQUIRED_METRIC_SERIES,
type LlamaCppDgxSparkExecutionPlan,
type LlamaCppDgxSparkQualificationReceipt,
Expand Down Expand Up @@ -164,6 +165,51 @@ function jsonRequest(
};
}

function exactSizeChatRequest(model: string, targetBytes: number): string {
const request = {
max_tokens: 1,
messages: [{ content: "", role: "user" }],
model,
temperature: 0,
};
const emptyBody = JSON.stringify(request);
const contentBytes = targetBytes - new TextEncoder().encode(emptyBody).byteLength;
if (contentBytes < 0) throw new Error("request-body probe target is too small");
request.messages[0].content = "x".repeat(contentBytes);
const body = JSON.stringify(request);
if (new TextEncoder().encode(body).byteLength !== targetBytes) {
throw new Error("request-body probe did not construct the exact declared size");
}
return body;
}

function exactSizeJsonRequest(
authorization: string,
body: string,
timeoutMilliseconds: number,
): RequestInit {
return {
body,
headers: {
Authorization: authorization,
"Content-Type": "application/json",
},
method: "POST",
signal: requestSignal(timeoutMilliseconds),
};
}

function validateRequestBodyLimitError(value: unknown): void {
if (
!isRecord(value) ||
!isRecord(value.error) ||
value.error.code !== "request_body_too_large" ||
value.error.type !== "invalid_request_error"
) {
throw new Error("request-body limit probe did not return the declared error contract");
}
}

function usageFrom(value: unknown): ProtocolEvidence["usage"] {
if (!isRecord(value)) throw new Error("chat usage was not returned");
const promptTokens = value.prompt_tokens;
Expand Down Expand Up @@ -727,6 +773,73 @@ export async function runLlamaCppDgxSparkProtocolQualification(options: {
await expectStatus(malformedResponse, 400, bounds.maxResponseBytes, "malformed-request probe");
executedProbes.add("malformed-request");

const acceptedRequestBytes = plan.recipe.serve.limits.maxRequestBodyBytes;
const acceptedResponse = await fetchImpl(
chatUrl,
exactSizeJsonRequest(
authorization,
exactSizeChatRequest(model, acceptedRequestBytes),
timeoutMilliseconds,
),
);
await expectStatus(
acceptedResponse,
200,
bounds.maxResponseBytes,
"request-body boundary probe",
);

const rejectedResponse = await fetchImpl(
chatUrl,
exactSizeJsonRequest(
authorization,
exactSizeChatRequest(model, LLAMA_CPP_DGX_SPARK_REJECTED_REQUEST_BODY_BYTES),
timeoutMilliseconds,
),
);
validateRequestBodyLimitError(
await readJson(
rejectedResponse,
413,
bounds.maxResponseBytes,
"oversized request-body probe",
),
);

const continuationHealthResponse = await fetchImpl(`${baseUrl}/health`, {
headers: { Authorization: authorization },
signal: requestSignal(bounds.clientTimeoutMilliseconds),
});
await expectStatus(
continuationHealthResponse,
200,
bounds.maxResponseBytes,
"request-body continuation health probe",
);
const bodyLimitContinuationResponse = await fetchImpl(
chatUrl,
jsonRequest(
authorization,
{
max_tokens: bounds.maxTokens.synchronousChat,
messages: [{ content: "Return one short continuation token.", role: "user" }],
model,
temperature: 0,
},
timeoutMilliseconds,
),
);
validateChatCompletionResponse(
await readJson(
bodyLimitContinuationResponse,
200,
bounds.maxResponseBytes,
"request-body continuation completion probe",
),
model,
);
executedProbes.add("request-body-limit");

const synchronousResponse = await fetchImpl(
chatUrl,
jsonRequest(
Expand Down Expand Up @@ -914,6 +1027,17 @@ export async function runLlamaCppDgxSparkProtocolQualification(options: {
},
health: { httpStatus: 200, ok: true },
malformedRequest: { httpStatus: 400, ok: true },
requestBodyLimit: {
acceptedBytes: acceptedRequestBytes,
acceptedHttpStatus: 200,
continuationHealthHttpStatus: 200,
continuationHttpStatus: 200,
errorCode: "request_body_too_large",
errorType: "invalid_request_error",
ok: true,
rejectedBytes: LLAMA_CPP_DGX_SPARK_REJECTED_REQUEST_BODY_BYTES,
rejectedHttpStatus: 413,
},
models: { httpStatus: 200, model, ok: true },
metrics,
properties: propertiesEvidence.properties,
Expand Down
Loading
Loading