diff --git a/CHANGELOG.md b/CHANGELOG.md index 175a4176..887a5c13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -58,7 +58,7 @@ from version 5.0.0 onward. Pre-fork releases (`1.x`–`4.2.0`) were authored by where the backend cannot provide it, `OFF` disables it. ### Changed -- **llama.cpp `b10731` → `b10819`.** No project-source change: every header move in the range is +- **llama.cpp `b10731` → `b10850`.** No project-source change: every header move in the range is additive or a **widening** const-qualification, and the server wire contract is byte-identical (request-field set, `set_hard_limits` bounds and response keys all verified mechanically, which is the check that catches the contract-behind-a-stable-signature breaks a header diff cannot see). @@ -110,6 +110,17 @@ from version 5.0.0 onward. Pre-fork releases (`1.x`–`4.2.0`) were authored by (`ggml-metal-context.m`, llama.cpp #28399), a SYCL Kronecker-product/FWHT restore (#28254) and the matching upstream test. Nothing in the review surface, nothing in the server contract. + The `b10819` → `b10850` step is 31 commits and 466 KB in total, but **10 files / +181 / −98** + across the paths this project links against — `common/arg.cpp`, `common/jinja/{caps,runtime}.cpp`, + `common/log.{cpp,h}` and `tools/server/server-models.{cpp,h}` plus that tool's CMake, README and + a router test. Everything else is GPU backends, the WebUI, docs and upstream CI. Two of those + files are patch targets (`common/arg.cpp` for `0001`, `tools/server/server-models.cpp` for + `0008`, the latter with 159 lines rewritten), so the intersection was **not** empty this time and + the applier had to prove it rather than the file list implying it: a fresh configure applied all + eight patches clean and stamped them at head `f114f91f`. The server wire contract was re-checked + mechanically and is byte-identical — request-field set, `set_hard_limits` bounds, and response + keys in **both** emit forms. + - **llama.cpp `b10682` → `b10731`.** One project-source change came out of it, and it is the kind a header diff does not surface: upstream renamed `--tensor-read-lazy` to `-lzm` / `--lazy-mode` (env `LLAMA_ARG_TENSOR_READ_LAZY` → `LLAMA_ARG_LAZY_MODE`) **with no alias**. The binding emitted diff --git a/CLAUDE.md b/CLAUDE.md index f05d0285..9b0c2123 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co Java bindings for [llama.cpp](https://github.com/ggerganov/llama.cpp) via JNI, providing a high-level API for LLM inference in Java. The Java layer communicates with a native C++ library through JNI. -Current llama.cpp pinned version: **b10819** +Current llama.cpp pinned version: **b10850** ## Upgrading CUDA Version @@ -490,7 +490,7 @@ needs no extra step here, `build-webui` re-reads the tag and rebuilds the matchi ships no UI): ```bash # needs node/npm + network; embed.cpp is plain C++17 (no npm) -git clone --depth 1 --branch b10819 https://github.com/ggml-org/llama.cpp /tmp/lc +git clone --depth 1 --branch b10850 https://github.com/ggml-org/llama.cpp /tmp/lc ( cd /tmp/lc/tools/ui && npm ci && npm run build \ && ( cd dist && find . -type f -not -path './_gzip/*' \ | while read -r f; do mkdir -p "_gzip/$(dirname "$f")"; gzip -9 -c "$f" > "_gzip/$f"; done ) \ @@ -530,7 +530,7 @@ cache lives in **Depot Cache** over sccache's **WebDAV** backend: - `SCCACHE_WEBDAV_TOKEN: ${{ secrets.DEPOT_TOKEN }}` — a Depot **organization** token, stored as the repo secret **`DEPOT_TOKEN`**. -Because `sccache` is **content-addressed** and llama.cpp is pinned (`GIT_TAG b10819`), the +Because `sccache` is **content-addressed** and llama.cpp is pinned (`GIT_TAG b10850`), the ~280 upstream object files are byte-identical every run, so a warm cache recompiles only the *changed* files. Depot's cache is **shared across all branches** (unlike GitHub's per-branch `actions/cache`), so every branch builds incrementally; a `b` version bump @@ -1451,7 +1451,7 @@ ctest --test-dir build --output-on-failure -R "ResultsToJson" #### Upstream source location (in CMake build tree) -llama.cpp is fetched via CMake FetchContent, pinned to `GIT_TAG b10819`. +llama.cpp is fetched via CMake FetchContent, pinned to `GIT_TAG b10850`. **GoogleTest** is a separate `BUILD_TESTING`-only FetchContent (`GIT_TAG v1.17.0`), used solely by the `jllama_test` C++ unit-test binary — not by the shipped library, and not coupled to the diff --git a/README.md b/README.md index 66919da3..f344da82 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ **Build:** ![Java 8+](https://img.shields.io/badge/Java-8%2B-informational) ![Platform](https://img.shields.io/badge/Platform-Linux%20%7C%20macOS%20%7C%20Windows%20%7C%20Android-lightgrey) -[![llama.cpp b10819](https://img.shields.io/badge/llama.cpp-%23b10819-informational)](https://github.com/ggml-org/llama.cpp/releases/tag/b10819) +[![llama.cpp b10850](https://img.shields.io/badge/llama.cpp-%23b10850-informational)](https://github.com/ggml-org/llama.cpp/releases/tag/b10850) [![JPMS](https://img.shields.io/badge/JPMS-modular%20JAR-25A162)](https://openjdk.org/projects/jigsaw/) ![JUnit](https://img.shields.io/badge/tested%20with-JUnit6-25A162) [![JSpecify](https://img.shields.io/badge/JSpecify-1.0.0%20%40NullMarked-25A162)](https://jspecify.dev) diff --git a/docs/history/llama-cpp-breaking-changes.md b/docs/history/llama-cpp-breaking-changes.md index 975c47fc..6eefa0ff 100644 --- a/docs/history/llama-cpp-breaking-changes.md +++ b/docs/history/llama-cpp-breaking-changes.md @@ -694,4 +694,6 @@ Used during `llama.cpp` version bumps: when upgrading, scan this file from the r | b10797–b10817 | patches + upstream verification | **Zero intersection with the patch set.** The 42 files the eight patches touch were intersected against the range's changed-file list: empty, so no patch context can have moved. The applier was run for real anyway — fresh `rm -rf build && cmake -B build -DBUILD_TESTING=ON`, configure clean, stamp at `head cd8cdf397dc21d986e69069ce2180651d693fdff`, all eight hashes recorded. **Chunking:** the full diff is 307 KB, over the runbook's 100 KiB threshold, but excluding `ggml/src/**`, `tools/ui/**`, `docs/**`, `.github/**` and `conversion/**` — every one of them outside the review surface the rule exists to bound — it is **68 KB, under the threshold**. Same call as the previous bump, and this time the excluded-diff figure alone justifies it. This range does not address the macOS-15 failure; see the investigation row at the end of this table, which **refutes** the #28323 suspicion recorded here and shows the b10792–b10797 window itself was never established. | | b10817–b10819 | `ggml/src/ggml-metal/ggml-metal-context.m` (**#28399: one-line memory-leak fix on an early-return path**), `ggml/src/ggml-sycl/fwht.cpp` (#28254: restore Kronecker-product FWHT support and unbreak `test-backend-ops` on SYCL), `tests/test-backend-ops.cpp`. | **No project-source change, and the review surface is empty**: all three changed files are ggml backends or upstream tests, none of which this project links against or compiles into `jllama`. The server contract was re-checked and is byte-identical (request-field set, response keys in both emit forms); `tools/server/`, `common/`, `include/`, `tools/mtmd/` and `ggml/include/` have no change at all in the range. The Metal leak fix sits next to the **macOS-15 failure** and is not a fix for it; the investigation row below supersedes the reasoning recorded here, including the claim that "the no-Metal macOS job fails identically" (there was no no-Metal job). | | b10817–b10819 | patches + upstream verification | **Zero intersection with the patch set**: the 42 files the eight patches touch versus the range's 3 changed files — empty, so no patch context can have moved. The applier was run for real regardless: fresh `rm -rf build && cmake -B build -DBUILD_TESTING=ON`, configure clean, stamp at `head 6a1a922d269908a29cbd4b49c27e6a8e7fd10fae`, all eight hashes recorded. **No chunking question arises**: the full diff is 17 KB across 2 commits, far inside the runbook's 100 KiB threshold, so this is a straight bump *by* the rule rather than an exception to it. | -| **macOS-15 failure — investigation, 2026-09-05** | `Java Tests macOS 15 arm64 (Metal)` + `(no Metal)`, run **33861339600** (#897, dispatch, pin b10797) | **The #28323 lead is refuted, and the b10792–b10797 bisect window it rested on was never established.** Read this row before spending time on the earlier suspicion recorded two rows up. **What actually fails.** Two tests, `MemoryManagementTest#testPromptCacheCompleteMissAfterWarmup` and `LlamaModelTest#testSpeculativeDecoding`, both with `LlamaException: could not load model from given file path` over a native `llama_model_load: error loading model: vector`. The recorded symptom ("the draft model") is **half wrong**: the first failure is **codellama-7b.Q2_K.gguf**, the second the AMD-Llama-135m draft — so it is not size-specific. Both are *repeat* loads: the same 7B model had already loaded and generated successfully dozens of times in the same JVM over the preceding ten minutes. `what() == "vector"` is libc++'s message for `std::out_of_range` from `vector::at()` and for `vector`'s `length_error`; the 33 ms elapsed (10.06.078 → 10.06.111) and the position right after the vocab warnings put it in `load_hparams`/`load_tensors`, not in the big allocation phase. **Why #28323 cannot be it — three independent reasons.** `n_expert_used_arr` is a `std::array` read through `operator[]` behind an `il < n_layer_all` guard, so it cannot raise `out_of_range("vector")` (and its failure mode would be `GGML_ABORT`, which aborts rather than throws). Both failing models are dense (`n_expert == 0`), so the two changed call sites in `weight_buft_supported` (`GGML_OP_MUL_MAT_ID`, `GGML_OP_ADD_ID`) are unreachable and the `load_tensors` guard short-circuits on `n_expert > 0` before ever calling it. And the change is a **widening** — `max(il)` ≥ `[0]` — so it cannot narrow a value into an out-of-range one. **The "no Metal" job has never been a no-Metal job**, which invalidates the "both macOS jobs fail identically, so Metal is not involved" reasoning. It passed `-DLLAMA_METAL=OFF`, and upstream's `llama_option_depr(WARNING LLAMA_METAL GGML_METAL)` forwards only `if (${OLD})` — i.e. it acts on `ON` and **silently ignores `OFF`**, leaving `GGML_METAL` at its `APPLE` default of `ON`. CMake emits no "unused variable" warning either, because the variable *is* read; it just has no effect. Both macOS-15 jobs were therefore the same Metal build, which is why their logs are identical (44 `MTL0`/`ggml_metal` lines in the supposedly Metal-free one) and why "identical failure" carried no information. Fixed in this change set by passing `-DGGML_METAL=OFF`; the two `-DLLAMA_METAL_EMBED_LIBRARY=ON` sites are unaffected, since the shim does forward a truthy value. **The bisect window is wrong, and much wider than 5 commits.** The last CI observation of the macOS *Java test* jobs passing is run **33275073456** (#875, the v5.1.0 release dispatch, 2026-08-29) at pin **b10618**. b10731 was never observed: run #887 (dispatch, b10731) failed at `Code style (spotless) + package graph`, which skipped everything downstream — 30 jobs, no `Java Tests macOS` among them — and every other run between #875 and #897 was cancelled. So the regression window is **b10618 → b10797**, ~180 upstream builds, not b10792 → b10797. **Memory pressure is not the discriminator**, checked rather than assumed: the *green* run #875 logged **8** `ggml_metal_log_allocated_size: current allocated size is greater than the recommended max working set size` warnings and more free pages than the red run, which logged **6**. The 7 GB runner is tight in both. **Not resolved.** No bisect was run: it needs Apple-silicon hardware or one `workflow_dispatch` per step, neither available in the sandbox this was investigated from. The decisive next steps, in order: (1) dispatch one run on the current `main` (b10819) — nothing on it has been validated by CI at all, so even the premise "still red" is unverified; (2) with the flag fixed, compare the now-genuinely-Metal-free job against the Metal one, which is the experiment the old flag silently prevented; (3) only then bisect, over **b10618…b10797**. | +| **macOS-15 failure — investigation, 2026-09-05** | `Java Tests macOS 15 arm64 (Metal)` + `(no Metal)`, run **33861339600** (#897, dispatch, pin b10797) | **The #28323 lead is refuted, and the b10792–b10797 bisect window it rested on was never established.** Read this row before spending time on the earlier suspicion recorded two rows up. **What actually fails.** Two tests, `MemoryManagementTest#testPromptCacheCompleteMissAfterWarmup` and `LlamaModelTest#testSpeculativeDecoding`, both with `LlamaException: could not load model from given file path` over a native `llama_model_load: error loading model: vector`. The recorded symptom ("the draft model") is **half wrong**: the first failure is **codellama-7b.Q2_K.gguf**, the second the AMD-Llama-135m draft — so it is not size-specific. Both are *repeat* loads: the same 7B model had already loaded and generated successfully dozens of times in the same JVM over the preceding ten minutes. `what() == "vector"` is libc++'s message for `std::out_of_range` from `vector::at()` and for `vector`'s `length_error`; the 33 ms elapsed (10.06.078 → 10.06.111) and the position right after the vocab warnings put it in `load_hparams`/`load_tensors`, not in the big allocation phase. **Why #28323 cannot be it — three independent reasons.** `n_expert_used_arr` is a `std::array` read through `operator[]` behind an `il < n_layer_all` guard, so it cannot raise `out_of_range("vector")` (and its failure mode would be `GGML_ABORT`, which aborts rather than throws). Both failing models are dense (`n_expert == 0`), so the two changed call sites in `weight_buft_supported` (`GGML_OP_MUL_MAT_ID`, `GGML_OP_ADD_ID`) are unreachable and the `load_tensors` guard short-circuits on `n_expert > 0` before ever calling it. And the change is a **widening** — `max(il)` ≥ `[0]` — so it cannot narrow a value into an out-of-range one. **The "no Metal" job has never been a no-Metal job**, which invalidates the "both macOS jobs fail identically, so Metal is not involved" reasoning. It passed `-DLLAMA_METAL=OFF`, and upstream's `llama_option_depr(WARNING LLAMA_METAL GGML_METAL)` forwards only `if (${OLD})` — i.e. it acts on `ON` and **silently ignores `OFF`**, leaving `GGML_METAL` at its `APPLE` default of `ON`. CMake emits no "unused variable" warning either, because the variable *is* read; it just has no effect. Both macOS-15 jobs were therefore the same Metal build, which is why their logs are identical (44 `MTL0`/`ggml_metal` lines in the supposedly Metal-free one) and why "identical failure" carried no information. Fixed in this change set by passing `-DGGML_METAL=OFF`; the two `-DLLAMA_METAL_EMBED_LIBRARY=ON` sites are unaffected, since the shim does forward a truthy value. **The bisect window is wrong, and much wider than 5 commits.** The last CI observation of the macOS *Java test* jobs passing is run **33275073456** (#875, the v5.1.0 release dispatch, 2026-08-29) at pin **b10618**. b10731 was never observed: run #887 (dispatch, b10731) failed at `Code style (spotless) + package graph`, which skipped everything downstream — 30 jobs, no `Java Tests macOS` among them — and every other run between #875 and #897 was cancelled. So the regression window is **b10618 → b10797**, ~180 upstream builds, not b10792 → b10797. **Memory pressure is not the discriminator**, checked rather than assumed: the *green* run #875 logged **8** `ggml_metal_log_allocated_size: current allocated size is greater than the recommended max working set size` warnings and more free pages than the red run, which logged **6**. The 7 GB runner is tight in both. **Resolved to Metal by run 33994480652** — see the row below, which carries the experiment this one could only propose. | +| **macOS-15 failure — cause isolated, 2026-09-06** | `Java Tests macOS 15 arm64 (Metal)` vs `(no Metal)`, run **33994480652** (#910, dispatch, pin b10819) | **It is a Metal bug.** The first dispatch after the `-DGGML_METAL=OFF` fix turns the two macOS-15 jobs into a clean single-variable comparison, and they disagree: same runner, same OS, same `-DGGML_NATIVE=OFF`, identical **1742 tests / 11 skipped** on both sides, differing only in `GGML_METAL`. With Metal: **2 errors** — `MemoryManagementTest` 1 of 10 and `LlamaModelTest` 1 of 60, both `llama_model_load: error loading model: vector`, unchanged from #897. Without Metal: **0 errors**, those same classes 10/10 and 60/60, and **zero** `MTL0`/`ggml_metal` lines in the log where the old build had 44 — so the flag fix demonstrably took effect. The standing conclusion "the no-Metal job fails identically, so this is not a Metal problem" was an artifact of the no-op flag and is now retired. **What this does NOT establish:** `Java Tests macOS 14 arm64 (Metal)` is green, but that job differs from the red one in **two** variables at once — OS version *and* `GGML_NATIVE` (macos-14 builds host-native, macos-15 passes `-DGGML_NATIVE=OFF`) — so it cannot narrow "Metal" to "Metal on macOS 15" or to "Metal plus a portable build". Only the macos-15 pair isolates anything. **What it buys.** The bisect window stays b10618 → b10797 in tags, but the *candidate set* collapses from every upstream change to the Metal backend alone (`ggml/src/ggml-metal/**`) — precisely the directory every bump review in this table waved through as "GPU backend, safe to skip". That rule is sound for compile/link breaks and blind to runtime ones; this is the first entry where it cost something. **Still open:** which Metal commit, and whether upstream has since fixed it. The rest of run #910 was green — 51 success, 1 failure, 12 skipped (all downstream of the red job), including `Run PIT mutation tests: success`, which is the first CI confirmation of the PIT gate since the SLF4J classpath fix. | +| b10819–b10850 | `common/arg.cpp` (**additive**: `--log-jsonl` / `--no-log-jsonl`), `common/log.{cpp,h}` (**additive**: `common_log_set_jsonl`, plus a `level_str` helper and a `json.h` include), `common/jinja/{caps,runtime}.cpp`, `tools/server/server-models.{cpp,h}` (**159 lines rewritten**; `request_stop(name)` added to the header), `tools/server/CMakeLists.txt` + `README.md` + `tests/unit/test_router.py`. **`include/`, `ggml/include/` and `tools/mtmd/` are untouched in the range.** | **No project-source change.** The header moves are additive: `common_log_set_jsonl` is new API nothing here calls, and the two new `common_arg` entries are CLI-only. The 159 changed lines in `server-models.cpp` are the router's instance lifecycle, not its argv rendering. **The patch intersection was NOT empty this time** — `common/arg.cpp` (patch `0001`) and `tools/server/server-models.cpp` (patch `0008`) are both in the range, so the applier had to prove the contexts survived rather than a disjoint file list implying it: a fresh `rm -rf build && cmake -B build -DBUILD_TESTING=ON` applied all eight clean and stamped them at head `f114f91f9ed6792cf402437e3874adad98902744`. Server contract re-checked mechanically and byte-identical (request-field set, `set_hard_limits` bounds, response keys in both emit forms). **Chunking, with the figures recorded rather than a verdict asserted:** the full diff is **466 KB over 31 commits**; excluding the paths this table's earlier rows excluded (`ggml/src`, `tools/ui`, `docs`, `.github`, `conversion`) it is still **171 KB**, over the runbook's 100 KiB threshold — but that figure is misleading, because the remainder is dominated by `scripts/ui-assets.cmake`, `tests/test-backend-ops.cpp`, a new model architecture (`src/models/spark2-5.cpp` + its Jinja template) and `gguf-py/`, none of which the project links against and none of which the old exclusion list names. The **review surface proper** — `common/`, `include/`, `tools/server/`, `tools/mtmd/`, `ggml/include/`, top-level `CMakeLists.txt` — is **27 KB / 10 files / +181 / −98**, comfortably inside the threshold. Recorded both ways so the call is auditable. **Metal moved in this range too** (`ggml-metal-device.m` +7/−2 and a new 147-line `ggml-metal-tuning.cpp`), which matters only because of the macOS-15 finding in the row below: this bump neither targets nor is known to fix it. | diff --git a/llama/CMakeLists.txt b/llama/CMakeLists.txt index 523e64f2..22029106 100644 --- a/llama/CMakeLists.txt +++ b/llama/CMakeLists.txt @@ -173,7 +173,7 @@ set(LLAMA_BUILD_APP OFF CACHE BOOL "" FORCE) FetchContent_Declare( llama.cpp GIT_REPOSITORY https://github.com/ggerganov/llama.cpp.git - GIT_TAG b10819 + GIT_TAG b10850 PATCH_COMMAND ${CMAKE_COMMAND} -DPATCH_DIR=${CMAKE_CURRENT_SOURCE_DIR}/patches -DLLAMA_SRC= diff --git a/llama/src/main/java/net/ladenthin/llama/value/LlamaCppVersion.java b/llama/src/main/java/net/ladenthin/llama/value/LlamaCppVersion.java index 43346887..4bebe2f4 100644 --- a/llama/src/main/java/net/ladenthin/llama/value/LlamaCppVersion.java +++ b/llama/src/main/java/net/ladenthin/llama/value/LlamaCppVersion.java @@ -10,13 +10,13 @@ * library was compiled against, exposed as a compile-time constant so callers can render a badge or * emit a startup log line without loading the native library. * - *

{@link #LLAMA_CPP_VERSION} is a pure-Java string ({@code "b10819"}) that mirrors the + *

{@link #LLAMA_CPP_VERSION} is a pure-Java string ({@code "b10850"}) that mirrors the * {@code GIT_TAG} in {@code llama/CMakeLists.txt}. It is available even when {@code libjllama} is * absent (pure-Java checkout, before {@code System.load}), which is what makes it suitable for a * lightweight version badge in Android or other UIs.

* *

For the authoritative value that is baked into the native binary — the build number - * plus the resolved upstream commit, e.g. {@code "b10819-"} — call + * plus the resolved upstream commit, e.g. {@code "b10850-"} — call * {@link net.ladenthin.llama.LlamaModel#getLlamaCppBuildInfo()} instead; that reads llama.cpp's own * {@code build-info} through JNI and therefore cannot drift from the compiled library (but requires * the native library to be loaded).

@@ -24,14 +24,14 @@ public final class LlamaCppVersion { /** - * The pinned llama.cpp release tag this library was built against, e.g. {@code "b10819"}. + * The pinned llama.cpp release tag this library was built against, e.g. {@code "b10850"}. * *

Kept in lockstep with {@code GIT_TAG} in {@code llama/CMakeLists.txt} — see the * "Upgrading/Downgrading llama.cpp Version" checklist in {@code CLAUDE.md}. This is the * compile-time pin; use {@link net.ladenthin.llama.LlamaModel#getLlamaCppBuildInfo()} for the * value actually linked into the native binary.

*/ - public static final String LLAMA_CPP_VERSION = "b10819"; + public static final String LLAMA_CPP_VERSION = "b10850"; // Constants holder — not instantiable. private LlamaCppVersion() {}