diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7e0af9cd4..03df5cf0a 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -549,7 +549,12 @@ jobs: repository: ggml-org/llama.cpp ref: ${{ steps.tag.outputs.tag }} path: llamacpp-ui - sparse-checkout: tools/ui + # tools/ui holds the npm project AND the ui.cpp.in / ui.h.in templates; + # scripts/ holds ui-assets.cmake, which consumes them. Both are needed + # since upstream #28445 replaced the embed.cpp host tool. + sparse-checkout: | + tools/ui + scripts sparse-checkout-cone-mode: true - uses: actions/setup-node@v7 with: @@ -565,30 +570,46 @@ jobs: npm ci --ignore-scripts npm run build test -f dist/index.html - - name: Embed assets into ui.cpp / ui.h (gzip parity with upstream) - working-directory: llamacpp-ui/tools/ui + - name: Embed assets into ui.cpp / ui.h (upstream scripts/ui-assets.cmake) shell: bash run: | set -euo pipefail - # gzip every asset into dist/_gzip/ so llama-ui-embed embeds the - # compressed bytes (LLAMA_UI_GZIP parity); embed auto-detects _gzip. - ( 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 ) - # llama-ui-embed is a self-contained C++17 host tool (no npm) — build + run it. - g++ -O2 -std=c++17 -o llama-ui-embed embed.cpp - mkdir -p "$GITHUB_WORKSPACE/llama/webui-generated" - ./llama-ui-embed \ - "$GITHUB_WORKSPACE/llama/webui-generated/ui.cpp" \ - "$GITHUB_WORKSPACE/llama/webui-generated/ui.h" \ - dist + # Upstream #28445 ("ui : embed assets directly with CMake") deleted the + # tools/ui/embed.cpp host tool this step used to compile, and replaced it + # with scripts/ui-assets.cmake -- a plain `cmake -P` script, no npm and no + # host executable. Priority 1 of its provisioning order is "pre-built + # assets in /dist", which is exactly what the npm step + # above produced, so BUILD_UI and HF_ENABLED stay OFF: no second npm run + # and no Hugging Face download happen here. LLAMA_UI_GZIP is upstream's + # own knob and replaces the hand-rolled gzip loop this step used to do. + GEN="${RUNNER_TEMP}/ui-assets" + OUT="${GITHUB_WORKSPACE}/llama/webui-generated" + mkdir -p "$GEN" "$OUT" + cmake \ + "-DUI_SOURCE_DIR=${GITHUB_WORKSPACE}/llamacpp-ui/tools/ui" \ + "-DUI_BINARY_DIR=${GEN}" \ + "-DLLAMA_SOURCE_DIR=${GITHUB_WORKSPACE}/llamacpp-ui" \ + -DBUILD_UI=OFF \ + -DHF_ENABLED=OFF \ + -DLLAMA_UI_GZIP=ON \ + -P "${GITHUB_WORKSPACE}/llamacpp-ui/scripts/ui-assets.cmake" + # The script also drops a ui-gzip/ working tree next to the generated + # sources; copy only the two files the artifact is defined to carry. + cp "$GEN/ui.cpp" "$GEN/ui.h" "$OUT/" echo "=== generated WebUI assets ===" - ls -la "$GITHUB_WORKSPACE/llama/webui-generated" - if grep -q LLAMA_UI_HAS_ASSETS "$GITHUB_WORKSPACE/llama/webui-generated/ui.h"; then - echo "LLAMA_UI_HAS_ASSETS: present (real WebUI embedded)" + ls -la "$OUT" + # Guard against a silently empty WebUI. A bare `grep LLAMA_UI_HAS_ASSETS` + # does NOT work here and would pass the failure case: upstream's ui.h.in + # emits "/* #undef LLAMA_UI_HAS_ASSETS */" when the table is empty, so the + # token is present either way. (The old embed.cpp emitted no such line at + # all, which is why the naive grep used to be sufficient.) Assert the ACTIVE + # #define and a non-zero asset count instead -- verified against both paths. + N=$(sed -n 's/.*std::array.*/\1/p' "$OUT/ui.h" | head -1) + if grep -qE '^[[:space:]]*#define[[:space:]]+LLAMA_UI_HAS_ASSETS' "$OUT/ui.h" \ + && [ -n "$N" ] && [ "$N" -gt 0 ]; then + echo "LLAMA_UI_HAS_ASSETS: present, $N assets embedded" else - echo "ERROR: embed produced an empty asset table" >&2 + echo "ERROR: ui-assets.cmake produced an empty asset table (assets=${N:-unknown})" >&2 exit 1 fi - name: Upload WebUI artifact diff --git a/CLAUDE.md b/CLAUDE.md index fab124e72..c1a7ae351 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: **b10850** +Current llama.cpp pinned version: **b10870** ## Upgrading CUDA Version @@ -470,9 +470,21 @@ Pipeline (`.github/workflows/publish.yml`): pinned `b` tag from `llama/CMakeLists.txt`'s `GIT_TAG`, sparse-checks-out `ggml-org/llama.cpp@` `tools/ui`, runs the upstream Svelte build (`npm ci && npm run build`), gzips `dist/` into `dist/_gzip/` (LLAMA_UI_GZIP - parity), builds the self-contained `llama-ui-embed` host tool (plain C++17, **no - npm**) and runs it to produce the platform-independent **`webui-generated/ui.cpp` - + `ui.h`**, uploaded as the `webui-generated` artifact. + parity), then runs upstream's own **`scripts/ui-assets.cmake`** (a plain `cmake -P` + script, **no npm and no host executable**) to produce the platform-independent + **`webui-generated/ui.cpp` + `ui.h`**, uploaded as the `webui-generated` artifact. + Upstream **#28445** deleted the `tools/ui/embed.cpp` host tool this job used to + compile and replaced it with that script plus `ui.cpp.in`/`ui.h.in` templates; the + job passes `BUILD_UI=OFF HF_ENABLED=OFF` so the script takes its priority-1 path + ("pre-built assets in `/dist`") over the tree npm just built — no + second npm run, no Hugging Face download. `LLAMA_UI_GZIP` is upstream's own knob + and replaced the job's hand-rolled gzip loop. The sparse checkout therefore needs + **`scripts` as well as `tools/ui`**. **The completeness guard cannot be a bare + `grep LLAMA_UI_HAS_ASSETS`**: `ui.h.in` emits `/* #undef LLAMA_UI_HAS_ASSETS */` + for an empty table, so that token is present either way and the check passes the + failure case — the old `embed.cpp` emitted no such line, which is why the naive + grep used to work. The job asserts the **active** `#define` plus a non-zero count + parsed out of `std::array`. 2. **Every native build job** (`needs: [startgate, build-webui]`) downloads that artifact into `webui-generated/` before building. npm never runs in the dockcross cross-compilers (which have no node) or per-platform. @@ -489,14 +501,14 @@ needs no extra step here, `build-webui` re-reads the tag and rebuilds the matchi **Building the WebUI locally** (optional — a plain `cmake` build uses the stub and ships no UI): ```bash -# needs node/npm + network; embed.cpp is plain C++17 (no npm) -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 ) \ - && g++ -O2 -std=c++17 -o /tmp/llama-ui-embed embed.cpp ) -mkdir -p webui-generated -/tmp/llama-ui-embed webui-generated/ui.cpp webui-generated/ui.h /tmp/lc/tools/ui/dist +# needs node/npm + network for the asset build; the embed step is plain cmake -P +git clone --depth 1 --branch b10870 https://github.com/ggml-org/llama.cpp /tmp/lc +( cd /tmp/lc/tools/ui && npm ci && npm run build ) +mkdir -p webui-generated /tmp/ui-gen +cmake -DUI_SOURCE_DIR=/tmp/lc/tools/ui -DUI_BINARY_DIR=/tmp/ui-gen \ + -DLLAMA_SOURCE_DIR=/tmp/lc -DBUILD_UI=OFF -DHF_ENABLED=OFF -DLLAMA_UI_GZIP=ON \ + -P /tmp/lc/scripts/ui-assets.cmake +cp /tmp/ui-gen/ui.cpp /tmp/ui-gen/ui.h webui-generated/ cmake -B build && cmake --build build --target jllama # now embeds the real UI ``` `webui-generated/` is git-ignored. @@ -530,7 +542,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 b10850`), the +Because `sccache` is **content-addressed** and llama.cpp is pinned (`GIT_TAG b10870`), 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 @@ -1453,7 +1465,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 b10850`. +llama.cpp is fetched via CMake FetchContent, pinned to `GIT_TAG b10870`. **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 f344da820..4a2965958 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 b10850](https://img.shields.io/badge/llama.cpp-%23b10850-informational)](https://github.com/ggml-org/llama.cpp/releases/tag/b10850) +[![llama.cpp b10870](https://img.shields.io/badge/llama.cpp-%23b10870-informational)](https://github.com/ggml-org/llama.cpp/releases/tag/b10870) [![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 a76f656dc..83656e00a 100644 --- a/docs/history/llama-cpp-breaking-changes.md +++ b/docs/history/llama-cpp-breaking-changes.md @@ -698,3 +698,5 @@ Used during `llama.cpp` version bumps: when upgrading, scan this file from the r | **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. **Both open questions are answered by the root-cause row at the end of this table**: the commit is `8c0b9cd04` (#27701), and no — upstream has not fixed it (the bare `splits[i] /= split_sum` is still there at b10850), so the fix is carried locally as `patches/0012`. | | 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. | | **macOS-15 failure — root cause + fix, 2026-09-08** | `src/llama-model.cpp` (upstream) ← `ggml/src/ggml-metal/ggml-metal-device.m` `8c0b9cd04` ([#27701](https://github.com/ggml-org/llama.cpp/pull/27701)); carried as `llama/patches/0012` | **Found by reading, not by bisecting — the bisect was prepared and then not needed.** The chain, each link checked against the source rather than inferred: **(1)** `8c0b9cd04` "metal : fix memory query under low-memory conditions" lies inside the b10618→b10797 window and rewrote `ggml_metal_device_get_memory` to `*free = *total > cur ? *total - cur : 0`. **(2)** Both the green and the red run log `current allocated size is greater than the recommended max working set size`, i.e. `cur > total` — so that condition is the **precondition, not the discriminator**; it held on both sides of the regression. **(3)** Before the clamp, `*total - cur` **underflowed** to a huge `size_t`, which normalised harmlessly; after it, the device reports exactly `free == 0`. **(4)** `load_tensors`' `if (free == 0 && total == 0)` host-memory fallback does not fire, because `total` is `recommendedMaxWorkingSetSize` and is non-zero. **(5)** `splits[0] = 0` → `split_sum = 0` → `splits[i] /= split_sum` = **NaN**. **(6)** `std::upper_bound(…, NaN)` — every comparison false — returns the end iterator, so `layer_gpu == 1`. **(7)** `devices.at(1)` on a one-element vector throws `std::out_of_range`, whose libc++ `what()` is the bare string `"vector"`; `llama.cpp`'s `catch (const std::exception & err)` prints it verbatim. **(8)** The **discriminator** is `const int act_gpu_layers = devices.empty() ? 0 : …`: without a GPU backend `devices` is empty, every layer returns early on `cpu_dev`, and the `.at()` line is unreachable — which is exactly why only the Metal job failed. This accounts for every observation the two investigation rows above collected: Metal-only; only on *repeat* loads in a long-lived JVM (that is merely how `currentAllocatedSize` grew past the recommended set size, not a precondition of its own); `what() == "vector"`; ~30 ms in, right after the vocab warnings; and the absence of a `hyperparameters:`/`vocabulary:` prefix, since upstream's own rethrows would have added one. **The fix** is `patches/0012`: `llama_model_splits_normalize()` falls back to an even split when the weights sum to zero, and `llama_model_splits_select_device()` bounds-checks the lookup and throws a message naming the function, the layer, the device index and the split points. Both are lifted out of `load_tensors` into free functions **for testability** — the failing state needs a real over-committed GPU and cannot be arranged through any API — with an upstream `tests/test-model-split.cpp` and, because a FetchContent subproject sets `LLAMA_BUILD_TESTS=OFF`, a project-side runnable guard `src/test/cpp/test_model_split.cpp` that links the same two functions into `jllama_test` on every platform. **A second trigger, found while writing the fix up and verified against the unfixed library:** `--tensor-split` is parsed with `std::stof` and never range-checked, so `-ts 1,-1` makes the weights cancel, `split_sum` is 0 again, the split points become `[inf, -nan]`, and every layer maps one past the last device — on CUDA, Vulkan or ROCm as much as on Metal, in a fresh process with no memory pressure. The macOS failure is therefore one *instance* of a general defect, not a Metal edge case, which is what settles the question of upstream-submittability. **Two lessons worth carrying.** The `ggml/src/**` "safe to skip" rule in the review list is sound for compile/link breaks and blind to runtime ones; this is the first entry where it cost something, and the cost was ~180 builds of bisect window. And a `catch (…) { log(err.what()); }` over a library that throws `std::out_of_range` is a **diagnostic dead end** on libc++, which reports it as `"vector"` and nothing else — the second half of `0012` exists for that reason alone, and is why the message a future occurrence produces will name its own cause. | +| b10850–b10870 | `common/chat.cpp` (**−2524 lines**: every model-specific chat parser split out into a new `common/parsers/` directory — 19 new files, wired in via `common/parsers/sources.cmake` + `common/CMakeLists.txt`), `common/arg.cpp` (**behaviour change, not a signature change** — see below), `common/speculative.cpp` (**behaviour change** — see below), `ggml/include/ggml.h` (**additive + one deprecation**: `ggml_prec` gains `GGML_PREC_UNDEFINED`/`BF16`/`F16`/`Q8`/`Q4`, `GGML_PREC_DEFAULT` kept as a same-value alias marked deprecated; two new `GGML_API` functions `ggml_prec_set_acc` / `ggml_prec_set_src`, and **`ggml_mul_mat_set_prec` + `ggml_flash_attn_ext_set_prec` are now `GGML_DEPRECATED`**), `tools/server/server-context.cpp` (checkpoint eviction), `tools/mtmd/clip.cpp` + two model files, `src/llama-model.cpp` (**#28160**, lazy-mode AUTO), `tests/CMakeLists.txt`. **`common/chat.h` is byte-identical in the range** | **No project-source change.** The headline number is misleading: `common/chat.cpp` losing 2524 lines is a pure **internal reorganisation** — `common/chat.h`, which `jllama.cpp` includes directly and which is #2 on the priority review list, does not change at all, so nothing the project compiles against moved. The `ggml.h` change is additive plus deprecations that break nothing: `GGML_PREC_DEFAULT` keeps its value, and the two newly-deprecated functions still exist. The project source was grepped for all of it — `GGML_PREC`, `ggml_prec_set_*`, `ggml_mul_mat_set_prec`, `ggml_flash_attn_ext_set_prec` across `src/main/cpp/**` and `src/test/cpp/**` — with **zero** references, so none of it can reach us. (A deprecation is worth naming anyway: it is the shape that becomes a removal two bumps later, and a removal is the one change that breaks a build with no diff hunk to notice.) Server contract re-checked mechanically and **byte-identical in all three dimensions** (request-field set, `set_hard_limits` bounds, response keys in both emit forms). **Two behaviour changes that a header diff cannot see, and both reach every entry point that parses argv** (`NativeServer` in both modes, `LlamaModel`'s own parameter parse): **(1)** `--mmproj-device` now **defaults to `--device`** instead of auto-selecting (`common_params_parse` assigns `params.mmproj_device = params.devices.front()` when `mmproj_use_gpu` is set and `-mmdev` was not) — a caller that sets `--device` but not `-mmdev` now pins the multimodal projector to the same device rather than letting it choose, which is the surface `MultimodalIntegrationTest` and every vision user exercises. **(2)** the **draft model inherits the global device list** the same way, and `common_speculative_init` now only overwrites `result.devices` **when the spec device list is non-empty** (it previously assigned unconditionally), plus forces `LLAMA_SPLIT_MODE_LAYER` when the draft is pinned to exactly one device. That is the speculative-decoding path `LlamaModelTest#testSpeculativeDecoding` drives — the same test that was red on macOS before `patches/0012`, so a failure there after this bump needs to be attributed carefully between the two. **Chunking, with the figures recorded rather than a verdict asserted:** the full diff is **393 KB over 20 commits**, over the runbook's 100 KiB threshold; the **review surface proper** (`common/`, `include/`, `tools/server/`, `tools/mtmd/`, `ggml/include/`, top-level `CMakeLists.txt`) is **28 files, +2686 / −2480**, but ~2500 of those lines on each side are the one mechanical parser move, so the material change is a few dozen lines. Bumped straight rather than chunked on that basis, with the raw numbers here so the call is auditable. | +| b10850–b10870 | patches + upstream verification | **The intersection was NOT empty, and `0012` was the patch at risk.** The range touches `src/llama-model.cpp` and `tests/CMakeLists.txt` — both files `patches/0012` modifies, one week after that patch landed — plus `common/arg.cpp` (`0001`) and `tools/server/server-context.cpp` (`0002`/`0003`/`0010`). So this bump could not be waved through on a disjoint file list. **The `0012`-specific check `CLAUDE.md` mandates was run by hand first**, because the fail-loud applier detects "does not apply" but never "upstream already fixed this": `git show b10870:src/llama-model.cpp | grep -A3 split_sum` still shows the bare `splits[i] /= split_sum` with **no zero-sum guard**, so upstream has not adopted the fix and the patch stays rather than being dropped. Upstream's own change to that file (#28160, resolving `LLAMA_LAZY_MODE_AUTO` to `OFF` on devices without mmap support) sits ~60 lines above the patched region and is unrelated. Then the applier was run for real: fresh `rm -rf llama/build && cmake -B build -DBUILD_TESTING=ON`, configure clean, stamp written at head `1945e092030f8668ff93382799502d01490e564d` (= `b10870`), **all nine hashes recorded**. | diff --git a/llama/CMakeLists.txt b/llama/CMakeLists.txt index 0b107226e..3609f7255 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 b10850 + GIT_TAG b10870 PATCH_COMMAND ${CMAKE_COMMAND} -DPATCH_DIR=${CMAKE_CURRENT_SOURCE_DIR}/patches -DLLAMA_SRC= diff --git a/llama/src/main/cpp/webui_stub/ui.h b/llama/src/main/cpp/webui_stub/ui.h index feb15889e..a2589a282 100644 --- a/llama/src/main/cpp/webui_stub/ui.h +++ b/llama/src/main/cpp/webui_stub/ui.h @@ -6,7 +6,7 @@ // ui.h — minimal stand-in for the WebUI asset interface that llama.cpp's // tools/ui (CMake target "llama-ui") normally GENERATES into ui.h / ui.cpp at -// build time via the llama-ui-embed host tool. +// build time via scripts/ui-assets.cmake (before upstream #28445: the embed.cpp host tool). // // The upstream HTTP transport (tools/server/server-http.cpp) does // #include "ui.h" @@ -14,7 +14,7 @@ // llama_ui_use_gzip(). We compile server-http.cpp directly into libjllama but do // NOT ship the Svelte WebUI assets (building them needs npm, or a prebuilt-asset // download from Hugging Face) — so we provide the exact "empty asset table" -// interface that embed.cpp emits for its n_assets == 0 branch: the struct plus +// interface upstream's ui.h.in emits for its n_assets == 0 branch: the struct plus // the three functions, returning nothing. // // LLAMA_UI_HAS_ASSETS is intentionally left UNDEFINED. Every static-asset-serving 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 4bebe2f4b..17b071801 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 "b10850"}) that mirrors the + *

{@link #LLAMA_CPP_VERSION} is a pure-Java string ({@code "b10870"}) 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 "b10850-"} — call + * plus the resolved upstream commit, e.g. {@code "b10870-"} — 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 "b10850"}. + * The pinned llama.cpp release tag this library was built against, e.g. {@code "b10870"}. * *

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 = "b10850"; + public static final String LLAMA_CPP_VERSION = "b10870"; // Constants holder — not instantiable. private LlamaCppVersion() {} diff --git a/llama/src/test/cpp/test_model_split.cpp b/llama/src/test/cpp/test_model_split.cpp index 5395af0d9..18042b3fa 100644 --- a/llama/src/test/cpp/test_model_split.cpp +++ b/llama/src/test/cpp/test_model_split.cpp @@ -1,3 +1,7 @@ +// SPDX-FileCopyrightText: 2026 Bernard Ladenthin +// +// SPDX-License-Identifier: MIT +// // Runnable guard for patches/0012 (the layer-split fix in llama.cpp's src/llama-model.cpp). // // The patch also ships an upstream test (tests/test-model-split.cpp), but a FetchContent @@ -26,7 +30,7 @@ void expect_every_layer_maps_into_range(const std::vector &splits, int n_ int idx = -1; ASSERT_NO_THROW(idx = llama_model_splits_select_device(splits, il, n_layers)) << "layer " << il; EXPECT_GE(idx, 0) << "layer " << il; - EXPECT_LT((size_t) idx, splits.size()) << "layer " << il; + EXPECT_LT((size_t)idx, splits.size()) << "layer " << il; } } @@ -52,7 +56,7 @@ TEST(LlamaModelSplits, NormalizeSingleDeviceTakesEverything) { // currentAllocatedSize has grown past its recommendedMaxWorkingSetSize -- makes the sum of the // weights zero. Dividing by it put a NaN in every split point. TEST(LlamaModelSplits, ZeroSumDoesNotProduceNaNSplitPoints) { - for (size_t n_devices : {(size_t) 1, (size_t) 2, (size_t) 4}) { + for (size_t n_devices : {(size_t)1, (size_t)2, (size_t)4}) { std::vector splits(n_devices, 0.0f); llama_model_splits_normalize(splits); @@ -68,7 +72,7 @@ TEST(LlamaModelSplits, ZeroSumDoesNotProduceNaNSplitPoints) { // load_tensors() indexed one past the last device -- an std::out_of_range whose libc++ what() is // the bare string "vector". This is the assertion that would have failed before the fix. TEST(LlamaModelSplits, ZeroSumStillMapsEveryLayerToARealDevice) { - for (size_t n_devices : {(size_t) 1, (size_t) 2, (size_t) 4}) { + for (size_t n_devices : {(size_t)1, (size_t)2, (size_t)4}) { std::vector splits(n_devices, 0.0f); llama_model_splits_normalize(splits); expect_every_layer_maps_into_range(splits, 32);