Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
32 changes: 32 additions & 0 deletions .github/buildcheck/natives.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,37 @@ def check_readme(natives, readme_text):
return failures + compare("README.md all-backends fat jars", fatjar_targets(natives), fatjar_names(readme_text))


JBANG_EXAMPLE = "examples/jbang/Chat.java"


def check_jbang_example(natives, script_text, readme_text):
"""The one-file JBang example runs without a checkout, so it cannot take `llama-platform`: JBang
treats a `pom` dependency as a BOM and puts nothing of it on the classpath (measured). It therefore
names the classes jar and the platform=yes natives jars itself -- exactly those, at the version of
the README's install snippet, which the version bump moves by hand."""
deps = [line.split()[1] for line in script_text.splitlines() if line.startswith("//DEPS ")]
coords = [d.split(":") for d in deps]
good = [c for c in coords if c[:2] == ["net.ladenthin", "llama"] and len(c) in (3, 4)]
failures = [f"{JBANG_EXAMPLE}: `{d}` is not net.ladenthin:llama:<version>[:<classifier>]"
for d, c in zip(deps, coords) if c not in good]
match = re.search(r"<artifactId>llama-platform</artifactId>\s*<version>([^<]+)</version>", readme_text)
expected = match.group(1) if match else None
versions = {c[2] for c in good}
if versions != {expected}:
failures.append(f"{JBANG_EXAMPLE}: DEPS version(s) {sorted(versions)} must be the README install "
f"snippet's {expected}")
if not any(len(c) == 3 for c in good):
failures.append(f"{JBANG_EXAMPLE}: the classes jar net.ladenthin:llama:<version> is missing")
named = [c[3] for c in good if len(c) == 4]
platform = [r["classifier"] for r in natives if r["platform"] == "yes"]
failures += [f"{JBANG_EXAMPLE}: missing the platform natives jar {c}" for c in platform if c not in named]
failures += [f"{JBANG_EXAMPLE}: names {c}, which is not a platform=yes row of .github/natives.csv"
for c in named if c not in platform]
if len(set(named)) != len(named):
failures.append(f"{JBANG_EXAMPLE}: a natives jar is named twice")
return failures


def check_agent_class_path(natives, agent_pom_text):
"""`java -jar` on the agent jar finds the core through its manifest Class-Path, which names
every all-backends fat jar."""
Expand Down Expand Up @@ -284,5 +315,6 @@ def check(root):
+ check_cmake(natives, read(root, "llama/CMakeLists.txt"))
+ check_dependency_allowlist(natives, nativedeps.ALLOWED)
+ check_readme(natives, read(root, "README.md"))
+ check_jbang_example(natives, read(root, JBANG_EXAMPLE), read(root, "README.md"))
+ check_agent_class_path(natives, read(root, "llama-atmosphere-agent/pom.xml"))
+ check_agent_version(read(root, "pom.xml"), read(root, "llama-atmosphere-agent/pom.xml")))
21 changes: 21 additions & 0 deletions .github/buildcheck/tests/test_natives.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,27 @@ def test_readme(self):
failures = natives.check_readme(ROWS, text.replace("`cpu-linux-s390x`", "cpu-linux-s390x"))
self.assertEqual(failures, ["README.md does not document the natives jar `cpu-linux-s390x`"])

def test_jbang_example(self):
readme = "<artifactId>llama-platform</artifactId>\n <version>5.2.0</version>\n</dependency>"
platform = [r["classifier"] for r in ROWS if r["platform"] == "yes"]
lines = ["//DEPS net.ladenthin:llama:5.2.0"] + [f"//DEPS net.ladenthin:llama:5.2.0:{c}" for c in platform]
script = "///usr/bin/env jbang\n" + "\n".join(lines) + "\n// a comment naming net.ladenthin:llama:5.2.0:cuda13-linux-x86-64 is not a dependency\n"
self.assertEqual(natives.check_jbang_example(ROWS, script, readme), [])
# a platform jar missing, a GPU jar named instead
broken = script.replace(":cpu-linux-s390x", ":cuda13-linux-x86-64")
self.assertEqual(natives.check_jbang_example(ROWS, broken, readme),
["examples/jbang/Chat.java: missing the platform natives jar cpu-linux-s390x",
"examples/jbang/Chat.java: names cuda13-linux-x86-64, which is not a platform=yes row of .github/natives.csv"])
# the classes jar missing
failures = natives.check_jbang_example(ROWS, script.replace("//DEPS net.ladenthin:llama:5.2.0\n", ""), readme)
self.assertEqual(failures, ["examples/jbang/Chat.java: the classes jar net.ladenthin:llama:<version> is missing"])
# a version the README has moved past (the bump edits both by hand)
failures = natives.check_jbang_example(ROWS, script, readme.replace("5.2.0", "5.3.0"))
self.assertEqual(failures, ["examples/jbang/Chat.java: DEPS version(s) ['5.2.0'] must be the README install snippet's 5.3.0"])
# another artifact, or JBang's BOM-only pom form
failures = natives.check_jbang_example(ROWS, script + "//DEPS net.ladenthin:llama-platform:5.2.0@pom\n", readme)
self.assertEqual(failures, ["examples/jbang/Chat.java: `net.ladenthin:llama-platform:5.2.0@pom` is not net.ladenthin:llama:<version>[:<classifier>]"])

def test_agent_class_path(self):
good = "<Class-Path>llama-${v}-all-linux-x86-64-jar-with-dependencies.jar llama-${v}-jar-with-dependencies.jar</Class-Path>"
self.assertEqual(natives.check_agent_class_path(ROWS, good), [])
Expand Down
53 changes: 38 additions & 15 deletions .github/verify-test-counts.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,33 +14,39 @@
# The shape is what defeats the obvious guard: a CLASS-LEVEL assumption failure makes Surefire
# record tests="0" errors="0" failures="0" skipped="0" for that class. It contributes NO test
# entries at all, so "did this run skip anything?" is structurally blind to it — a skipped test is
# still a reported test. Two checks catch it:
# still a reported test. Three checks catch it:
#
# 1. Any testsuite reporting tests="0". This is the exact signature above, it is precise, and it
# is platform-independent: a class that runs nowhere is a bug on every OS.
# 2. A floor on the total number of tests executed. The backstop for a whole class file going
# missing from the run rather than reporting zero.
# 2. A floor on the number of tests EXECUTED (run minus skipped). This is the other shape of the
# same failure: a METHOD-LEVEL assumption (Assume.assumeTrue inside a test) does report the
# test, as skipped. Measured on a green run at b11529, every test-java-* job runs 1869 tests
# and skips 4 (Linux), 11 (macOS, all three jobs) or 13 (Windows, Ninja and MSVC), i.e. it
# executes 1856 to 1865; the same tree in a checkout WITHOUT the models runs 1869 and skips
# 280, i.e. executes 1589. The jobs pass --min-executed 1800: that catches the model set going
# missing (or a resolver regression) with a margin of ~200 either way, and tolerates ordinary
# test removals of up to ~56 tests before someone has to lower it, deliberately.
# 3. A floor on the total (--min-total), the backstop for a whole class file going missing from
# the run rather than reporting zero. Implied by check 2 when both are given.
#
# Check 1 is the sensitive one; check 2 is deliberately slack so it never fails spuriously on a
# platform that legitimately runs fewer tests. Tighten --min-total once real per-platform numbers
# are known from a green run.
#
# Usage: .github/verify-test-counts.sh <surefire-reports-dir> [--min-total N]
# Usage: .github/verify-test-counts.sh <surefire-reports-dir> [--min-total N] [--min-executed N]
# Exit codes: 0 all good, 1 a check failed, 2 nothing to scan.

set -euo pipefail

REPORT_DIR="${1:-}"
MIN_TOTAL=0
MIN_EXECUTED=0
shift || true
while [ $# -gt 0 ]; do
case "$1" in
--min-total) MIN_TOTAL="$2"; shift 2 ;;
--min-executed) MIN_EXECUTED="$2"; shift 2 ;;
*) echo "unknown argument: $1" >&2; exit 1 ;;
esac
done

[ -n "$REPORT_DIR" ] || { echo "usage: $0 <surefire-reports-dir> [--min-total N]" >&2; exit 1; }
[ -n "$REPORT_DIR" ] || { echo "usage: $0 <surefire-reports-dir> [--min-total N] [--min-executed N]" >&2; exit 1; }
[ -d "$REPORT_DIR" ] || { echo "ERROR: no such directory: $REPORT_DIR" >&2; exit 2; }

shopt -s nullglob
Expand All @@ -54,18 +60,26 @@ if [ "${#reports[@]}" -eq 0 ]; then
exit 2
fi

# An attribute of the <testsuite> element; the first match, so a nested element carrying the same
# attribute name cannot shift the number (<testcase> has neither, a skipped test is a child element).
suite_attr() {
local n
n="$(grep -o "$2=\"[0-9]*\"" "$1" | head -1 | grep -o '[0-9]*' || true)"
echo "${n:-0}"
}

total=0
skipped=0
empty_suites=()
for f in "${reports[@]}"; do
# The count lives on the <testsuite> element; take the first match so a nested element
# carrying the same attribute name cannot shift the number.
n="$(grep -o 'tests="[0-9]*"' "$f" | head -1 | grep -o '[0-9]*' || true)"
[ -n "$n" ] || n=0
n="$(suite_attr "$f" tests)"
total=$((total + n))
skipped=$((skipped + $(suite_attr "$f" skipped)))
if [ "$n" -eq 0 ]; then
empty_suites+=("$(basename "$f")")
fi
done
executed=$((total - skipped))

status=0

Expand All @@ -79,11 +93,20 @@ if [ "${#empty_suites[@]}" -gt 0 ]; then
fi

if [ "$total" -lt "$MIN_TOTAL" ]; then
echo "ERROR: only $total test(s) executed, below the floor of $MIN_TOTAL" >&2
echo "ERROR: only $total test(s) run, below the floor of $MIN_TOTAL" >&2
status=1
fi

if [ "$executed" -lt "$MIN_EXECUTED" ]; then
echo "ERROR: only $executed test(s) executed ($total run, $skipped skipped), below the floor of $MIN_EXECUTED" >&2
echo " Method-level assumptions skipping in bulk look like this: a model file the tests expect is" >&2
echo " missing or resolves to the wrong directory (TestConstants.resolveModelPath), or a native" >&2
echo " library failed to load. If the suite legitimately shrank, lower --min-executed in the" >&2
echo " workflow deliberately." >&2
status=1
fi

if [ "$status" -eq 0 ]; then
echo "test counts verified: $total test(s) across ${#reports[@]} class(es), none empty (floor $MIN_TOTAL)"
echo "test counts verified: $total test(s) run, $skipped skipped, $executed executed across ${#reports[@]} class(es), none empty (floors: total $MIN_TOTAL, executed $MIN_EXECUTED)"
fi
exit "$status"
2 changes: 1 addition & 1 deletion .github/workflows/java-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ jobs:
# zero check is the sensitive half.
- name: Verify tests actually ran
shell: bash
run: .github/verify-test-counts.sh llama/target/surefire-reports --min-total 1500
run: .github/verify-test-counts.sh llama/target/surefire-reports --min-executed 1800
- name: Memory after tests
if: always() && runner.os == 'macOS'
run: vm_stat && sysctl hw.memsize hw.physmem
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2329,7 +2329,7 @@ jobs:
# muted on every test-java-* job for months. The floor is deliberately slack; the per-class
# zero check is the sensitive half.
- name: Verify tests actually ran
run: .github/verify-test-counts.sh llama/target/surefire-reports --min-total 1500
run: .github/verify-test-counts.sh llama/target/surefire-reports --min-executed 1800
- uses: actions/upload-artifact@v7
if: success()
with:
Expand Down
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ from version 5.0.0 onward. Pre-fork releases (`1.x`–`4.2.0`) were authored by
## [Unreleased]

### Added
- **`examples/jbang/Chat.java`**, a one-file console chat that [JBang](https://www.jbang.dev) runs without a
checkout or a build (`jbang https://github.com/bernardladenthin/java-llama.cpp/blob/main/examples/jbang/Chat.java
model.gguf`). Its `//DEPS` lines name the classes jar and the CPU natives jar of every desktop platform
themselves -- JBang treats a `pom` dependency such as `llama-platform` as a BOM and puts nothing of it on the
classpath -- and `check-natives.py` holds them to the `platform=yes` rows of `natives.csv` and to the README's
release version.
- **`ModelParameters.setMoeCacheMib(int)`** (`--moe-cache-mib`, llama.cpp b11480, EXPERIMENTAL upstream): a
GPU cache for the MoE expert weights a `setCpuMoeLayers` / `--cpu-moe` setup keeps in host memory,
split among several GPUs like the layers; `0` (the default) disables it.
Expand Down Expand Up @@ -57,6 +63,20 @@ from version 5.0.0 onward. Pre-fork releases (`1.x`–`4.2.0`) were authored by
in `build.sh` opts back in). With CUDA 13.4 and sccache 0.18.0 the nvcc launcher failed on the first `.cu`
files in two confirmed runs (`Missing "cubin" file output`, `Compiler killed by signal 126`), and the
uncached retry that followed made those jobs slower than a cold build; the C/C++ objects stay cached.
- **Upgraded the pinned llama.cpp from b11529 to b11534**, in 3 reviewed steps, each ending at a tag. No
carried patch needed a refresh, and every drop-check still finds its defect. The one step with project
code is b11531, **#30210 "chat : refactor API"**: the prompt and the parser state of a chat generation now
live in a `common_chat_session` that `oaicompat_chat_params_parse` fills and the server task applies,
replacing the `chat_format` / `chat_parser` / `generation_prompt` / `parse_tool_calls` request fields
(none was a `RequestField`, so the Java wire surface is unchanged); `jllama.cpp` threads a session through
its four chat entry points and the C++ tests follow the new signatures. Also in the range: OpenCL kernels
that compile on Adreno A6x (#30176, the `opencl-android-aarch64` jar and the `llama-android-opencl` AAR on
that family), exact GELU for ModernBERT encoders (#30108), and no redundant CUDA copies after `SSM_SCAN`
(#29807). Per-step record: `docs/history/llama-cpp-breaking-changes.md`.
- **CI: every `test-java-*` job now requires at least 1800 tests *executed*** (`verify-test-counts.sh
--min-executed`, run minus skipped), where it required 1500 *run* before. The run count cannot see the
other shape of the muted-suite failure -- method-level assumptions skipping in bulk -- while the executed
count does: the jobs execute 1856 (Windows) to 1865 (Linux) tests, a checkout without the models 1589.
- **Upgraded the pinned llama.cpp from b11512 to b11529**, in 5 reviewed steps, each ending at a tag. No
carried patch needed a refresh, and every drop-check still finds its defect. In the range: the server's
default port moves from 8080 to 9931 (#30159, the `NativeServer` entry above); CUDA top-k selection is
Expand Down Expand Up @@ -255,6 +275,11 @@ from version 5.0.0 onward. Pre-fork releases (`1.x`–`4.2.0`) were authored by
4.4.7 (none of the files the agent's carried JLine fixes touch changed between 4.4.6 and 4.4.7).

### Fixed
- **`LlamaLoader` prints its two diagnostic lines to stderr**, `[jllama] using native backend '…'` and
`[jllama] extracted '…'`, where they went to stdout before. A router worker JVM
(`NativeServer.setWorkerCommand`) printed them onto the router's command pipe, which since llama.cpp b11401
the router reports as `unexpected output on the command pipe` (harmless, misleading); and the stdout of a
`java -jar` server is otherwise the upstream server's. The CI smokes read both streams already.
- **`llama/patches/0017`** fixes two defects in the four `_mm_prefetch` calls of ggml's x86
`quants.c` (the SSSE3-without-AVX branch of `ggml_vec_dot_q4_0_q8_0`, and the only
`_mm_prefetch` calls in the whole ggml tree), both by routing the address through one new
Expand Down
Loading
Loading