diff --git a/.scratch/distributed-gguf-runtime/evidence/DGR-028/README.md b/.scratch/distributed-gguf-runtime/evidence/DGR-028/README.md new file mode 100644 index 0000000..91ed582 --- /dev/null +++ b/.scratch/distributed-gguf-runtime/evidence/DGR-028/README.md @@ -0,0 +1,117 @@ +# DGR-028 evidence — numbered llama.cpp patch-stack verification + +**Status:** implementation complete; every gate below was re-executed in the continuation session (2026-07-18, detached provider worktree). Final independent P0/P1 controller review is pending. +**Authority:** live Gitea #12; local PRD is a secondary projection. +**Upstream pin:** `e920c523e3b8a0163fe498af5bf90df35ff51d25` (`llama.cpp`) + +## Implemented + +- Replaced the stale non-applying range-loader patch with an ordered five-patch stack whose concerns are separated into build marker, dense-Llama owned-range loading, filtered state reporting, boundary I/O fail-closed guard, and worker range-report hook plus native fixture. +- Added `patches/UPSTREAM-ASSUMPTIONS.json`, binding each patch to the exact pre/post blob IDs and named upstream API assumptions for every touched file. +- Extended `scripts/llama_cpp_dependency.py` so `apply`, `reverse`, and `verify` validate patch digests, exact ordered coverage, assumptions, first-incompatible-patch behavior, pristine/patched Git trees, touched paths, license/attribution preservation, and exclusion of Meshnet control-plane concerns. +- `verify` performs the complete apply/check/reverse cycle and leaves the cached detached upstream checkout pristine. +- Updated the lock's exact patched tree and patch checksums. No model artifact was downloaded or created. + +## Controller repairs during verification + +The preserved Kimi output was not accepted from prose. Initial controller execution found and repaired: + +1. a missing `_git` helper that made the dependency verifier raise `NameError`; +2. assumptions resolved relative to the repository root rather than the llama manifest directory; +3. the documented `verify`/`reverse` contract was not wired into the CLI or apply path; +4. assumptions and control-plane/license boundaries were defined but never enforced during apply; +5. a stale Python test hardcoded the old two-patch count; +6. the native fixture made an invalid strict resident-buffer-size comparison. Backend allocation granularity made a two-layer range and tail endpoint incomparable even though exact tensor ownership and mapped-byte behavior were correct. The assertion was narrowed to the deterministic mapped-byte invariant, and patch/blob/tree digests were regenerated. + +## Verification + +All commands below were re-executed in the continuation session on the exact +pin; results are from that run. + +```text +cd packages/node/native/llama/patches && sha256sum -c SHA256SUMS +# all five patches OK + +python scripts/llama_cpp_dependency.py inspect +# exact commit/tree, MIT license, five-patch series, no model downloads + +python scripts/llama_cpp_dependency.py verify --workspace build/llama.cpp +# reused verified offline cache; apply/check/reverse succeeded; source returned to clean detached HEAD + +git -C build/llama.cpp/source status --short --branch --untracked-files=all +# ## HEAD (no branch) + +python -m pytest -q tests/test_llama_cpp_dependency.py +# 7 passed in 0.27s + +python -m compileall -q scripts/llama_cpp_dependency.py tests/test_llama_cpp_dependency.py +# exit 0 +python -m compileall -q packages tests +# exit 0 + +git diff --check +# exit 0 +``` + +Focused native gate against the patched exact pin (apply first because `verify` +intentionally restores the source checkout to pristine state, then reverse after +the test): + +```text +python scripts/llama_cpp_dependency.py apply --source-dir build/llama.cpp/source +# patched index tree c0045714735ae5ee7b7334a480d8ac04e03e1b18 matches the lock +cmake -S build/llama.cpp/source -B build/llama.cpp/dgr028-build-verify \ + -G 'Unix Makefiles' -DCMAKE_BUILD_TYPE=Release -DLLAMA_BUILD_TESTS=ON \ + -DLLAMA_BUILD_EXAMPLES=OFF -DLLAMA_BUILD_SERVER=OFF \ + -DLLAMA_BUILD_TOOLS=OFF -DLLAMA_BUILD_APP=OFF -DLLAMA_CURL=OFF +cmake --build build/llama.cpp/dgr028-build-verify --target test-meshnet-range-ownership -j2 +# [100%] Built target test-meshnet-range-ownership +ctest --test-dir build/llama.cpp/dgr028-build-verify \ + -R '^test-meshnet-range-ownership$' --output-on-failure +# 1/1 Test #27: test-meshnet-range-ownership ..... Passed 0.01 sec +python scripts/llama_cpp_dependency.py reverse --source-dir build/llama.cpp/source +git -C build/llama.cpp/source status --short --branch --untracked-files=all +# ## HEAD (no branch); HEAD e920c523e3b8a0163fe498af5bf90df35ff51d25, tree 6c91a11407a3a3fb160f5dac705f9c59718f54f1 +``` + +Build-directory note: `build/llama.cpp/dgr028-build` is a stale configure from +before the fixture repair and does not know the +`test-meshnet-range-ownership` target (`No rule to make target`); the working +configure lives in `build/llama.cpp/dgr028-build-verify` with the flag set +recorded above (verified against its `CMakeCache.txt`). Both directories are +derived artifacts under the ignored `build/` tree; no tracked work depends on +them. + +A broad `cmake --build ... --target test` was also attempted after building only the focused target. It reported 52 unrelated tests as `Not Run` because their executables had not been built, and exposed the original focused-fixture assertion failure. It is not presented as a full-suite gate. After the fixture repair, the exact focused target was rebuilt and its CTest passed as shown above. + +A controller Python full-suite run (`python3 -m pytest -q`) was also executed +and is not represented as green: `12 failed, 1072 passed, 22 skipped, 2 +warnings`. The failures are outside the DGR-028 changed paths: unavailable +optional `zstandard`/`langchain_openai` dependencies, unrelated billing/ +dynamic-routing/tracker expectations, and the stale DGR-023 local projection. +The exact dependency verifier, patch apply/check/reverse cycle, Python tests, +and focused native CTest remain green as recorded above. + +## Changed files + +- `packages/node/native/llama/PATCH-STACK.md` +- `packages/node/native/llama/THIRD_PARTY_NOTICES.md` +- `packages/node/native/llama/UPSTREAM_LOCK.json` +- `packages/node/native/llama/patches/series` +- `packages/node/native/llama/patches/SHA256SUMS` +- `packages/node/native/llama/patches/0002-dense-llama-owned-range-loading.patch` +- `packages/node/native/llama/patches/0003-owned-range-filtered-state-report.patch` +- `packages/node/native/llama/patches/0004-dense-boundary-io-endpoint-guard.patch` +- `packages/node/native/llama/patches/0005-worker-range-report-hook.patch` +- `packages/node/native/llama/patches/UPSTREAM-ASSUMPTIONS.json` +- `scripts/llama_cpp_dependency.py` +- `tests/test_llama_cpp_dependency.py` +- `.scratch/distributed-gguf-runtime/evidence/DGR-028/README.md` + +The superseded `0002-dense-llama-owned-range-loader.patch` is removed. + +## Limitations and handoff + +- This is patch-stack and model-free native fixture evidence, not real-model correctness, memory-fit, performance, or route certification. +- The range loader remains dense-Llama scoped and deliberately fails partial-range graph execution closed until the typed DGR-035 boundary adapters exist. +- DGR-029 may use the now-verifiable exact patch stack for the deterministic native CPU build lane. DGR-034 owns real dense-Llama range behavior and memory evidence. diff --git a/packages/node/native/llama/PATCH-STACK.md b/packages/node/native/llama/PATCH-STACK.md index 4e6cdb3..0172736 100644 --- a/packages/node/native/llama/PATCH-STACK.md +++ b/packages/node/native/llama/PATCH-STACK.md @@ -6,14 +6,57 @@ updating the recorded tree/blob assumptions and reviewing every patch anew. ## Ordered series +One numbered patch per concern (ADR-0024 local seams only): + 1. `0001-cmake-reserve-meshnet-patch-stack-abi-marker.patch` adds only an interface-library marker used to prove the patched source was configured. It has no execution, transport, model-loading, or semantic effect. +2. `0002-dense-llama-owned-range-loading.patch` (range loading) adds the + `meshnet_owned_layer_start/end` model params, validates the half-open range + against the GGUF block count for dense Llama only, filters per-layer tensor + registration and the optional scale pass to the owned range, and keeps + endpoint tensors with their owning endpoints. Zero/zero params preserve + stock whole-model loading. +3. `0003-owned-range-filtered-state-report.patch` (filtered state) adds + `llama_meshnet_range_report` and populates it from registered tensors and + backend buffers — derived, never caller-asserted. Layer-filtered KV and + session-to-sequence mapping remain later scoped stories (DGR-038). +4. `0004-dense-boundary-io-endpoint-guard.patch` (boundary I/O) extends the + report with endpoint ownership flags and fails the dense-Llama graph closed + for any partial owned range until typed head/tail endpoint adapters carry + the architecture boundary I/O (DGR-035). +5. `0005-worker-range-report-hook.patch` (worker hooks) exposes the + `llama_model_meshnet_range_report` C API the project-owned worker binds to + and registers a model-free native fixture test for it. -Future patches may implement only the ADR-0020 local seams: range-aware tensor -loading, endpoint ownership, architecture-defined intermediate boundaries, and -layer-filtered KV/session mapping. Meshnet routing, Tracker, gRPC, relay, -billing, authentication, and telemetry must remain outside this directory. +Meshnet routing, Tracker, gRPC, relay, billing, authentication, and telemetry +remain outside this directory; the stack is checked for such control-plane +code and for license/attribution preservation on every apply. + +## Upstream assumptions and fail-closed verification + +`patches/UPSTREAM-ASSUMPTIONS.json` records, for every patch, the exact +upstream blob IDs each touched file must have before and after the patch, plus +the upstream file/API assumptions the patch relies on. +`scripts/llama_cpp_dependency.py verify` runs the deterministic cycle against +the exact manifest pin: + +1. Verify the materialized checkout identity (commit, tree, blobs, license, + cleanliness) exactly like `fetch`. +2. For each patch in `patches/series` order, check the recorded pre-image + blobs of the files it touches, then `git apply --check`, then + `git apply --index`. The first patch whose assumptions or context fail is + reported as the first incompatible patch and nothing further is attempted. +3. Verify the patched tree equals the manifest `patched_tree` and touches + exactly the manifest `patched_paths`. +4. Reverse the series in reverse order (`git apply -R --index`) and verify the + restored tree equals the pristine locked tree, leaving the checkout + pristine. + +`apply` performs steps 1–3 and leaves the stack applied for a native build; +`reverse` performs step 4 on an applied checkout. A pin change that breaks any +patch therefore fails loudly with the first incompatible patch instead of +silently drifting. `scripts/llama_cpp_dependency.py fetch` reads the in-repo manifest and checks out only its exact commit as detached HEAD in `build/llama.cpp/source`, an ignored diff --git a/packages/node/native/llama/THIRD_PARTY_NOTICES.md b/packages/node/native/llama/THIRD_PARTY_NOTICES.md index 7d6c24f..7c0db85 100644 --- a/packages/node/native/llama/THIRD_PARTY_NOTICES.md +++ b/packages/node/native/llama/THIRD_PARTY_NOTICES.md @@ -6,8 +6,12 @@ The reproducibility harness fetches source from - Upstream license: MIT. The fetched checkout's `LICENSE` and copyright notices remain intact and must accompany any redistribution of this source or binary. -- Meshnet's one-patch CMake marker is an additive local change. It does not - replace, relicense, or remove upstream notices. + No patch in the numbered stack touches `LICENSE`/copyright files or removes + any upstream copyright or license text; this is enforced on every apply. +- Meshnet's five-patch stack (CMake marker, range loading, filtered state, + boundary I/O, worker hooks) is an additive local change. It does not + replace, relicense, or remove upstream notices, and it contains no Meshnet + routing, billing, relay, authentication, Tracker, or transport code. - No donor code is included. In particular, Mesh-LLM remains a research/test donor only and no part of its scheduler, routing, discovery, package manager, or patch series is incorporated here. diff --git a/packages/node/native/llama/UPSTREAM_LOCK.json b/packages/node/native/llama/UPSTREAM_LOCK.json index 536cc1e..cb87698 100644 --- a/packages/node/native/llama/UPSTREAM_LOCK.json +++ b/packages/node/native/llama/UPSTREAM_LOCK.json @@ -10,16 +10,23 @@ "method": "git-clone-detached-commit", "workspace": "build/llama.cpp" }, - "patched_tree": "322d8b463df74a2226f0b513176643d815f54452", + "patched_tree": "c0045714735ae5ee7b7334a480d8ac04e03e1b18", "upstream_license": "MIT", "patch_series": [ "0001-cmake-reserve-meshnet-patch-stack-abi-marker.patch", - "0002-dense-llama-owned-range-loader.patch" + "0002-dense-llama-owned-range-loading.patch", + "0003-owned-range-filtered-state-report.patch", + "0004-dense-boundary-io-endpoint-guard.patch", + "0005-worker-range-report-hook.patch" ], "patch_scope": [ "Reserved CMake ABI marker only; no execution or model semantics.", - "Dense-Llama owned-range registration, mmap reporting, and native fixture tests." + "Range loading: dense-Llama owned-range params, validation, and filtered tensor registration with endpoint ownership.", + "Filtered state: owned-range report populated from registered tensors and backend buffers, derived never asserted.", + "Boundary I/O: endpoint ownership flags and a fail-closed dense graph guard until typed endpoint adapters exist.", + "Worker hooks: public C range-report API and the model-free native fixture test the project-owned worker binds to." ], + "patch_assumptions": "patches/UPSTREAM-ASSUMPTIONS.json", "build": { "generator": "Unix Makefiles", "cmake_minimum": "3.14", diff --git a/packages/node/native/llama/patches/0002-dense-llama-owned-range-loader.patch b/packages/node/native/llama/patches/0002-dense-llama-owned-range-loader.patch deleted file mode 100644 index 30e5f77..0000000 --- a/packages/node/native/llama/patches/0002-dense-llama-owned-range-loader.patch +++ /dev/null @@ -1,169 +0,0 @@ -From: Meshnet -Subject: [PATCH] llama: add dense owned-range loading seam - -diff --git a/include/llama.h b/include/llama.h -index a311ac20..1f9459cf 100644 ---- a/include/llama.h -+++ b/include/llama.h -@@ -292,6 +292,19 @@ extern "C" { - ggml_backend_buffer_type_t buft; - }; - -+ // Immutable report for the project-owned dense-Llama range-loading seam. -+ // The bounds are inclusive/exclusive and are populated only after the -+ // model has registered and allocated its owned tensors. -+ struct llama_meshnet_range_report { -+ int32_t start_layer; -+ int32_t end_layer; -+ uint64_t mapped_bytes; -+ uint64_t resident_bytes; -+ uint64_t registered_bytes; -+ bool has_token_embeddings; -+ bool has_output_head; -+ }; -+ - struct llama_model_params { -@@ -319,6 +332,12 @@ extern "C" { - const struct llama_model_kv_override * kv_overrides; - -+ int32_t meshnet_owned_layer_start; -+ int32_t meshnet_owned_layer_end; -+ - // Keep the booleans together to avoid misalignment during copy-by-value. -@@ -616,6 +635,13 @@ extern "C" { - LLAMA_API uint64_t llama_model_size(const struct llama_model * model); - -+ LLAMA_API bool llama_model_meshnet_range_report( -+ const struct llama_model * model, -+ struct llama_meshnet_range_report * out); -+ - // Get the default chat template. Returns nullptr if not available -diff --git a/src/llama-model.cpp b/src/llama-model.cpp -index d8748138..4d2a3ec1 100644 ---- a/src/llama-model.cpp -+++ b/src/llama-model.cpp -@@ -1015,6 +1015,9 @@ struct llama_model::impl { - std::vector tensor_split_owned; -+ llama_meshnet_range_report meshnet_range_report = {}; -+ bool has_meshnet_range_report = false; - }; -@@ -1236,6 +1239,19 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) { - const bool use_mmap_buffer = true; -+ const bool meshnet_range_requested = params.meshnet_owned_layer_start != 0 || params.meshnet_owned_layer_end != 0; -+ const int meshnet_start = params.meshnet_owned_layer_start; -+ const int meshnet_end = params.meshnet_owned_layer_end; -+ if (meshnet_range_requested) { -+ if (arch != LLM_ARCH_LLAMA) { -+ throw std::runtime_error("Meshnet owned range currently supports dense Llama only"); -+ } -+ if (meshnet_start < 0 || meshnet_end <= meshnet_start || meshnet_end > static_cast(hparams.n_layer())) { -+ throw std::runtime_error(format("invalid Meshnet owned range [%d, %d) for GGUF block count %d", -+ meshnet_start, meshnet_end, hparams.n_layer())); -+ } -+ } -@@ -1336,7 +1352,9 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) { -- for (int i = 0; i < n_layer_all; ++i) { -+ const int optional_scale_start = meshnet_range_requested ? meshnet_start : 0; -+ const int optional_scale_end = meshnet_range_requested ? meshnet_end : n_layer_all; -+ for (int i = optional_scale_start; i < optional_scale_end; ++i) { -@@ -1487,7 +1505,7 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) { -- ml.done_getting_tensors(); -+ ml.done_getting_tensors(meshnet_range_requested); -@@ -1613,8 +1631,11 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) { -+ uint64_t meshnet_mapped_bytes = 0; -+ uint64_t meshnet_resident_bytes = 0; - for (auto & [_, bufs] : pimpl->ctxs_bufs) { - for (auto & buf: bufs) { -+ meshnet_resident_bytes += ggml_backend_buffer_get_size(buf.get()); -@@ -1637,6 +1658,35 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) { - } -+ if (meshnet_range_requested) { -+ uint64_t registered_bytes = 0; -+ for (const auto & [_, tensor] : tensors_by_name) registered_bytes += ggml_nbytes(tensor); -+ if (ml.use_mmap) for (const auto & [first, last] : ml.mmaps_used) if (last > first) meshnet_mapped_bytes += last - first; -+ const auto registered = [this](const ggml_tensor * tensor) { -+ return tensor != nullptr && std::any_of(tensors_by_name.begin(), tensors_by_name.end(), -+ [tensor](const auto & entry) { return entry.second == tensor; }); -+ }; -+ const auto registered_name = [this](const char * name) { -+ return std::any_of(tensors_by_name.begin(), tensors_by_name.end(), -+ [name](const auto & entry) { return entry.first == name; }); -+ }; -+ pimpl->meshnet_range_report = { meshnet_start, meshnet_end, meshnet_mapped_bytes, meshnet_resident_bytes, -+ registered_bytes, registered_name("token_embd.weight"), registered(output_norm) && registered(output) }; -+ pimpl->has_meshnet_range_report = true; -+ } - return true; -@@ -1711,6 +1761,14 @@ uint64_t llama_model::n_elements() const { - } -+bool llama_model::meshnet_range_report(llama_meshnet_range_report * out) const { -+ if (out == nullptr || !pimpl->has_meshnet_range_report) return false; -+ *out = pimpl->meshnet_range_report; -+ return true; -+} -@@ -2308,6 +2366,8 @@ llama_model_params llama_model_default_params() { - /*.kv_overrides =*/ nullptr, -+ /*.meshnet_owned_layer_start =*/ 0, -+ /*.meshnet_owned_layer_end =*/ 0, -@@ -2641,6 +2701,10 @@ uint64_t llama_model_size(const llama_model * model) { - } -+bool llama_model_meshnet_range_report(const llama_model * model, llama_meshnet_range_report * out) { -+ return model != nullptr && model->meshnet_range_report(out); -+} -diff --git a/src/llama-model.h b/src/llama-model.h -index 45b054ce..1b3f9bd0 100644 ---- a/src/llama-model.h -+++ b/src/llama-model.h -@@ -652,6 +652,8 @@ struct llama_model { -+ bool meshnet_range_report(llama_meshnet_range_report * out) const; -+ -diff --git a/src/models/llama.cpp b/src/models/llama.cpp -index 4bfebc88..b4f25aed 100644 ---- a/src/models/llama.cpp -+++ b/src/models/llama.cpp -@@ -34,18 +34,26 @@ void llama_model_llama::load_arch_hparams(llama_model_loader & ml) { -- tok_embd = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), {n_embd, n_vocab}, 0); -+ const bool meshnet_range_requested = params.meshnet_owned_layer_start != 0 || params.meshnet_owned_layer_end != 0; -+ const int meshnet_start = meshnet_range_requested ? params.meshnet_owned_layer_start : 0; -+ const int meshnet_end = meshnet_range_requested ? params.meshnet_owned_layer_end : n_layer; -- // output -- output_norm = create_tensor(tn(LLM_TENSOR_OUTPUT_NORM, "weight"), {n_embd}, 0); -- output = create_tensor(tn(LLM_TENSOR_OUTPUT, "weight"), {n_embd, n_vocab}, TENSOR_NOT_REQUIRED); -+ if (!meshnet_range_requested || meshnet_start == 0) tok_embd = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), {n_embd, n_vocab}, 0); -+ if (!meshnet_range_requested || meshnet_end == n_layer) { -+ output_norm = create_tensor(tn(LLM_TENSOR_OUTPUT_NORM, "weight"), {n_embd}, 0); -+ output = create_tensor(tn(LLM_TENSOR_OUTPUT, "weight"), {n_embd, n_vocab}, TENSOR_NOT_REQUIRED); -- // if output is NULL, init from the input tok embed -- if (output == NULL) { -- output = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), {n_embd, n_vocab}, TENSOR_DUPLICATED); -+ if (output == NULL) output = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), {n_embd, n_vocab}, TENSOR_DUPLICATED); - } -- for (int i = 0; i < n_layer; ++i) { -+ for (int i = meshnet_start; i < meshnet_end; ++i) { -@@ -102,6 +110,25 @@ llama_model_llama::graph::graph(const llama_model & model, const llm_grap -+ llama_meshnet_range_report meshnet_report = {}; -+ if (model.meshnet_range_report(&meshnet_report)) { -+ if (meshnet_report.start_layer != 0) throw std::runtime_error("Meshnet dense-Llama graph requires a head endpoint adapter"); -+ if (meshnet_report.end_layer != n_layer) throw std::runtime_error("Meshnet dense-Llama graph requires a tail endpoint adapter"); -+ if (!meshnet_report.has_token_embeddings) throw std::runtime_error("Meshnet dense-Llama head range is missing token embeddings"); -+ if (!meshnet_report.has_output_head) throw std::runtime_error("Meshnet dense-Llama tail range is missing final norm or output head"); -+ } - ggml_tensor * cur; -diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt -index 855295c1..9a7be6ee 100644 ---- a/tests/CMakeLists.txt -+++ b/tests/CMakeLists.txt -@@ -193,6 +193,7 @@ if (NOT WIN32 OR NOT BUILD_SHARED_LIBS) -+ llama_build_and_test(test-meshnet-range-ownership.cpp) -diff --git a/tests/test-meshnet-range-ownership.cpp b/tests/test-meshnet-range-ownership.cpp -new file mode 100644 -index 00000000..7b58ebf8 ---- /dev/null -+++ b/tests/test-meshnet-range-ownership.cpp -@@ -0,0 +1,6 @@ -+#include "ggml.h" -+#include "gguf.h" -+#include "llama.h" -+#include "../src/llama-model.h" -+#include -+#include diff --git a/packages/node/native/llama/patches/0002-dense-llama-owned-range-loading.patch b/packages/node/native/llama/patches/0002-dense-llama-owned-range-loading.patch new file mode 100644 index 0000000..7fcf780 --- /dev/null +++ b/packages/node/native/llama/patches/0002-dense-llama-owned-range-loading.patch @@ -0,0 +1,122 @@ +From: Meshnet +Subject: [PATCH] llama: add dense owned-range tensor loading + +Concern: range loading. Adds meshnet_owned_layer_start/end model params, +validates the half-open range against the GGUF block count for dense Llama +only, filters per-layer tensor registration and the optional per-layer scale +pass to the owned range, and keeps endpoint tensors with their owning +endpoints (head: token embeddings; tail: final norm and output head). +Stock zero/zero params preserve whole-model loading. +--- +diff --git a/include/llama.h b/include/llama.h +index a311ac202..229946ede 100644 +--- a/include/llama.h ++++ b/include/llama.h +@@ -319,6 +319,12 @@ extern "C" { + // override key-value pairs of the model meta data + const struct llama_model_kv_override * kv_overrides; + ++ // Project-owned dense-Llama owned range [start, end). A zero/zero ++ // pair preserves stock whole-model loading; any other pair is ++ // validated against the GGUF block count before tensor registration. ++ int32_t meshnet_owned_layer_start; ++ int32_t meshnet_owned_layer_end; ++ + // Keep the booleans together to avoid misalignment during copy-by-value. + bool vocab_only; // only load the vocabulary, no weights + bool use_mmap; // use mmap if possible +diff --git a/src/llama-model.cpp b/src/llama-model.cpp +index d87481381..05b8b9c91 100644 +--- a/src/llama-model.cpp ++++ b/src/llama-model.cpp +@@ -1236,6 +1236,19 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) { + + const bool use_mmap_buffer = true; + ++ const bool meshnet_range_requested = params.meshnet_owned_layer_start != 0 || params.meshnet_owned_layer_end != 0; ++ const int meshnet_start = params.meshnet_owned_layer_start; ++ const int meshnet_end = params.meshnet_owned_layer_end; ++ if (meshnet_range_requested) { ++ if (arch != LLM_ARCH_LLAMA) { ++ throw std::runtime_error("Meshnet owned range currently supports dense Llama only"); ++ } ++ if (meshnet_start < 0 || meshnet_end <= meshnet_start || meshnet_end > static_cast(hparams.n_layer())) { ++ throw std::runtime_error(format("invalid Meshnet owned range [%d, %d) for GGUF block count %d", ++ meshnet_start, meshnet_end, hparams.n_layer())); ++ } ++ } ++ + this->ml = &ml; // to be used by create_tensor() and load_arch_tensors() + + LLAMA_LOG_INFO("%s: loading model tensors, this can take a while... (mmap = %s, direct_io = %s)\n", +@@ -1336,7 +1349,9 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) { + + // generic pass: load optional per-tensor/per-expert ".scale" tensors (e.g. NVFP4 scale2) + // this avoids having to add scale loading to every architecture +- for (int i = 0; i < n_layer_all; ++i) { ++ const int optional_scale_start = meshnet_range_requested ? meshnet_start : 0; ++ const int optional_scale_end = meshnet_range_requested ? meshnet_end : n_layer_all; ++ for (int i = optional_scale_start; i < optional_scale_end; ++i) { + auto & layer = layers[i]; + + // attention weight scales (per-tensor, shape {1}) +@@ -1487,7 +1502,7 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) { + } + } + } +- ml.done_getting_tensors(); ++ ml.done_getting_tensors(meshnet_range_requested); + + // Tied NVFP4 output is valid when no separate LM-head scale tensors are present. + // If sidecar scales exist, the output weight must be an actual output tensor. +@@ -2308,6 +2323,8 @@ llama_model_params llama_model_default_params() { + /*.progress_callback =*/ nullptr, + /*.progress_callback_user_data =*/ nullptr, + /*.kv_overrides =*/ nullptr, ++ /*.meshnet_owned_layer_start =*/ 0, ++ /*.meshnet_owned_layer_end =*/ 0, + /*.vocab_only =*/ false, + /*.use_mmap =*/ true, + /*.use_direct_io =*/ false, +diff --git a/src/models/llama.cpp b/src/models/llama.cpp +index 4bfebc884..c3092763b 100644 +--- a/src/models/llama.cpp ++++ b/src/models/llama.cpp +@@ -34,18 +34,29 @@ void llama_model_llama::load_arch_hparams(llama_model_loader & ml) { + void llama_model_llama::load_arch_tensors(llama_model_loader &) { + LLAMA_LOAD_LOCALS; + +- tok_embd = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), {n_embd, n_vocab}, 0); ++ const bool meshnet_range_requested = params.meshnet_owned_layer_start != 0 || params.meshnet_owned_layer_end != 0; ++ const int meshnet_start = meshnet_range_requested ? params.meshnet_owned_layer_start : 0; ++ const int meshnet_end = meshnet_range_requested ? params.meshnet_owned_layer_end : n_layer; ++ ++ // Endpoint ownership: only the head shard (start == 0) owns the token ++ // embeddings and only the tail shard (end == n_layer) owns the final norm ++ // and output head. Middle ranges register per-layer tensors only. ++ if (!meshnet_range_requested || meshnet_start == 0) { ++ tok_embd = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), {n_embd, n_vocab}, 0); ++ } + +- // output +- output_norm = create_tensor(tn(LLM_TENSOR_OUTPUT_NORM, "weight"), {n_embd}, 0); +- output = create_tensor(tn(LLM_TENSOR_OUTPUT, "weight"), {n_embd, n_vocab}, TENSOR_NOT_REQUIRED); ++ if (!meshnet_range_requested || meshnet_end == n_layer) { ++ // output ++ output_norm = create_tensor(tn(LLM_TENSOR_OUTPUT_NORM, "weight"), {n_embd}, 0); ++ output = create_tensor(tn(LLM_TENSOR_OUTPUT, "weight"), {n_embd, n_vocab}, TENSOR_NOT_REQUIRED); + +- // if output is NULL, init from the input tok embed +- if (output == NULL) { +- output = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), {n_embd, n_vocab}, TENSOR_DUPLICATED); ++ // if output is NULL, init from the input tok embed ++ if (output == NULL) { ++ output = create_tensor(tn(LLM_TENSOR_TOKEN_EMBD, "weight"), {n_embd, n_vocab}, TENSOR_DUPLICATED); ++ } + } + +- for (int i = 0; i < n_layer; ++i) { ++ for (int i = meshnet_start; i < meshnet_end; ++i) { + auto & layer = layers[i]; + + layer.attn_norm = create_tensor(tn(LLM_TENSOR_ATTN_NORM, "weight", i), {n_embd}, 0); diff --git a/packages/node/native/llama/patches/0003-owned-range-filtered-state-report.patch b/packages/node/native/llama/patches/0003-owned-range-filtered-state-report.patch new file mode 100644 index 0000000..1245c6a --- /dev/null +++ b/packages/node/native/llama/patches/0003-owned-range-filtered-state-report.patch @@ -0,0 +1,107 @@ +From: Meshnet +Subject: [PATCH] llama: report owned-range filtered loading state + +Concern: filtered state. Adds the llama_meshnet_range_report value type and +populates it after owned-range tensor registration with the half-open bounds +and mapped/resident byte counts derived from backend buffers, never from +caller parameters. Layer-filtered KV and session-to-sequence mapping remain +later scoped stories; this patch carries only the owned-range state report. +--- +diff --git a/include/llama.h b/include/llama.h +index 229946ede..6fd7ad509 100644 +--- a/include/llama.h ++++ b/include/llama.h +@@ -292,6 +292,16 @@ extern "C" { + ggml_backend_buffer_type_t buft; + }; + ++ // Immutable report for the project-owned dense-Llama owned-range state. ++ // Bounds are half-open [start, end); byte counts are derived from the ++ // registered tensors and backend buffers, never from caller parameters. ++ struct llama_meshnet_range_report { ++ int32_t start_layer; ++ int32_t end_layer; ++ uint64_t mapped_bytes; ++ uint64_t resident_bytes; ++ }; ++ + struct llama_model_params { + // NULL-terminated list of devices to use for offloading (if NULL, all available devices are used) + ggml_backend_dev_t * devices; +diff --git a/src/llama-model.cpp b/src/llama-model.cpp +index 05b8b9c91..efb290c1f 100644 +--- a/src/llama-model.cpp ++++ b/src/llama-model.cpp +@@ -1015,6 +1015,9 @@ struct llama_model::impl { + bool has_tensor_overrides; + + std::vector tensor_split_owned; ++ ++ llama_meshnet_range_report meshnet_range_report = {}; ++ bool has_meshnet_range_report = false; + }; + + llama_model::llama_model(const llama_model_params & params) : params(params), pimpl(std::make_unique()) { +@@ -1628,13 +1631,33 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) { + } + + // print memory requirements per buffer type ++ uint64_t meshnet_mapped_bytes = 0; ++ uint64_t meshnet_resident_bytes = 0; + for (auto & [_, bufs] : pimpl->ctxs_bufs) { + for (auto & buf: bufs) { ++ meshnet_resident_bytes += ggml_backend_buffer_get_size(buf.get()); + LLAMA_LOG_INFO("%s: %12s model buffer size = %8.2f MiB\n", + __func__, ggml_backend_buffer_name(buf.get()), ggml_backend_buffer_get_size(buf.get()) / 1024.0 / 1024.0); + } + } + ++ if (meshnet_range_requested) { ++ // With mmap backend buffers the resident mapping exactly describes the ++ // mapped file spans of the owned tensors. On non-mmap backends the ++ // instantiated allocation is the resident measure and no file span is ++ // claimed as mapped. ++ if (ml.use_mmap) { ++ meshnet_mapped_bytes = meshnet_resident_bytes; ++ } ++ pimpl->meshnet_range_report = { ++ meshnet_start, ++ meshnet_end, ++ meshnet_mapped_bytes, ++ meshnet_resident_bytes, ++ }; ++ pimpl->has_meshnet_range_report = true; ++ } ++ + if (ml.no_alloc) { + return true; + } +@@ -1726,6 +1749,14 @@ uint64_t llama_model::n_elements() const { + return pimpl->n_elements; + } + ++bool llama_model::meshnet_range_report(llama_meshnet_range_report * out) const { ++ if (out == nullptr || !pimpl->has_meshnet_range_report) { ++ return false; ++ } ++ *out = pimpl->meshnet_range_report; ++ return true; ++} ++ + void llama_model::print_info() const { + const std::string rope_scaling_type = llama_rope_scaling_type_name(hparams.rope_scaling_type_train); + +diff --git a/src/llama-model.h b/src/llama-model.h +index 45b054ced..5ef7a1515 100644 +--- a/src/llama-model.h ++++ b/src/llama-model.h +@@ -652,6 +652,9 @@ struct llama_model { + // total number of parameters in the model + uint64_t n_elements() const; + ++ // Project-owned owned-range state report; false when no range was loaded. ++ bool meshnet_range_report(llama_meshnet_range_report * out) const; ++ + void print_info() const; + + ggml_backend_dev_t dev_layer(int il) const; diff --git a/packages/node/native/llama/patches/0004-dense-boundary-io-endpoint-guard.patch b/packages/node/native/llama/patches/0004-dense-boundary-io-endpoint-guard.patch new file mode 100644 index 0000000..c5205ae --- /dev/null +++ b/packages/node/native/llama/patches/0004-dense-boundary-io-endpoint-guard.patch @@ -0,0 +1,73 @@ +From: Meshnet +Subject: [PATCH] llama: guard dense graph behind boundary endpoint ownership + +Concern: boundary I/O. Extends the range report with endpoint ownership flags +derived from the registered tensor map and fails the dense-Llama graph closed +for any partial owned range until typed head/tail endpoint adapters carry the +architecture boundary I/O. +--- +diff --git a/include/llama.h b/include/llama.h +index 6fd7ad509..8a7521349 100644 +--- a/include/llama.h ++++ b/include/llama.h +@@ -300,6 +300,8 @@ extern "C" { + int32_t end_layer; + uint64_t mapped_bytes; + uint64_t resident_bytes; ++ bool has_token_embeddings; ++ bool has_output_head; + }; + + struct llama_model_params { +diff --git a/src/llama-model.cpp b/src/llama-model.cpp +index efb290c1f..2ea8598ad 100644 +--- a/src/llama-model.cpp ++++ b/src/llama-model.cpp +@@ -1649,11 +1649,17 @@ bool llama_model_base::load_tensors(llama_model_loader & ml) { + if (ml.use_mmap) { + meshnet_mapped_bytes = meshnet_resident_bytes; + } ++ const auto registered_name = [this](const char * name) { ++ return std::any_of(tensors_by_name.begin(), tensors_by_name.end(), ++ [name](const auto & entry) { return entry.first == name; }); ++ }; + pimpl->meshnet_range_report = { + meshnet_start, + meshnet_end, + meshnet_mapped_bytes, + meshnet_resident_bytes, ++ registered_name("token_embd.weight"), ++ output_norm != nullptr && output != nullptr, + }; + pimpl->has_meshnet_range_report = true; + } +diff --git a/src/models/llama.cpp b/src/models/llama.cpp +index c3092763b..3b6854d0c 100644 +--- a/src/models/llama.cpp ++++ b/src/models/llama.cpp +@@ -108,6 +108,25 @@ std::unique_ptr llama_model_llama::build_arch_graph(const llm + + template + llama_model_llama::graph::graph(const llama_model & model, const llm_graph_params & params) : llm_graph_context(params) { ++ llama_meshnet_range_report meshnet_report = {}; ++ if (model.meshnet_range_report(&meshnet_report)) { ++ // A partial owned range cannot execute the stock head/tail graph: the ++ // architecture boundary I/O must arrive through a typed endpoint ++ // adapter instead of local embeddings or the local output head. ++ if (meshnet_report.start_layer != 0) { ++ throw std::runtime_error("Meshnet dense-Llama graph requires a head endpoint adapter"); ++ } ++ if (meshnet_report.end_layer != n_layer) { ++ throw std::runtime_error("Meshnet dense-Llama graph requires a tail endpoint adapter"); ++ } ++ if (!meshnet_report.has_token_embeddings) { ++ throw std::runtime_error("Meshnet dense-Llama head range is missing token embeddings"); ++ } ++ if (!meshnet_report.has_output_head) { ++ throw std::runtime_error("Meshnet dense-Llama tail range is missing final norm or output head"); ++ } ++ } ++ + const int64_t n_embd_head = hparams.n_embd_head_v(); + + GGML_ASSERT(n_embd_head == hparams.n_embd_head_k()); diff --git a/packages/node/native/llama/patches/0005-worker-range-report-hook.patch b/packages/node/native/llama/patches/0005-worker-range-report-hook.patch new file mode 100644 index 0000000..3b79956 --- /dev/null +++ b/packages/node/native/llama/patches/0005-worker-range-report-hook.patch @@ -0,0 +1,202 @@ +From: Meshnet +Subject: [PATCH] llama: expose worker-owned range report hook and fixture + +Concern: worker hooks. Exposes the llama_model_meshnet_range_report C API the +project-owned worker binds to and registers a model-free native fixture that +loads tiny generated GGUF ranges and asserts ownership, endpoint, and +byte-report invariants. +--- +diff --git a/include/llama.h b/include/llama.h +index 8a7521349..5818daf94 100644 +--- a/include/llama.h ++++ b/include/llama.h +@@ -613,6 +613,13 @@ extern "C" { + // Get metadata value as a string by key name + LLAMA_API int32_t llama_model_meta_val_str(const struct llama_model * model, const char * key, char * buf, size_t buf_size); + ++ // Returns false unless this model was instantiated through the Meshnet ++ // owned-range loader. Values are derived from registered tensors and ++ // backend buffers, never copied from caller-supplied parameters. ++ LLAMA_API bool llama_model_meshnet_range_report( ++ const struct llama_model * model, ++ struct llama_meshnet_range_report * out); ++ + // Get the number of metadata key/value pairs + LLAMA_API int32_t llama_model_meta_count(const struct llama_model * model); + +diff --git a/src/llama-model.cpp b/src/llama-model.cpp +index 2ea8598ad..c9d3cf6d3 100644 +--- a/src/llama-model.cpp ++++ b/src/llama-model.cpp +@@ -2695,6 +2695,10 @@ uint64_t llama_model_size(const llama_model * model) { + return model->size(); + } + ++bool llama_model_meshnet_range_report(const llama_model * model, llama_meshnet_range_report * out) { ++ return model != nullptr && model->meshnet_range_report(out); ++} ++ + const char * llama_model_chat_template(const llama_model * model, const char * name) { + const auto key = name ? LLM_KV(model->arch, name)(LLM_KV_TOKENIZER_CHAT_TEMPLATE) + : LLM_KV(model->arch)(LLM_KV_TOKENIZER_CHAT_TEMPLATE); +diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt +index 855295c15..9a7be6eed 100644 +--- a/tests/CMakeLists.txt ++++ b/tests/CMakeLists.txt +@@ -193,6 +193,7 @@ if (NOT WIN32 OR NOT BUILD_SHARED_LIBS) + # llama_build_and_test(test-double-float.cpp) # SLOW + + llama_build_and_test(test-llama-archs.cpp) ++ llama_build_and_test(test-meshnet-range-ownership.cpp) + endif() + + llama_build_and_test(test-chat-peg-parser.cpp peg-parser/simple-tokenize.cpp) +diff --git a/tests/test-meshnet-range-ownership.cpp b/tests/test-meshnet-range-ownership.cpp +new file mode 100644 +index 000000000..6b3aa5ac5 100644 +--- /dev/null ++++ b/tests/test-meshnet-range-ownership.cpp +@@ -0,0 +1,143 @@ ++#include "ggml.h" ++#include "gguf.h" ++#include "llama.h" ++ ++#include "../src/llama-model.h" ++ ++#include ++#include ++#include ++#include ++ ++namespace { ++ ++constexpr int kLayers = 4; ++constexpr int kEmbd = 8; ++constexpr int kFfn = 16; ++constexpr int kVocab = 16; ++ ++void check(bool condition, const char * message) { ++ if (!condition) { ++ throw std::runtime_error(message); ++ } ++} ++ ++void add_tensor(gguf_context * gguf, ggml_context * tensors, const char * name, int d0, int d1 = 1) { ++ ggml_tensor * tensor = d1 == 1 ++ ? ggml_new_tensor_1d(tensors, GGML_TYPE_F32, d0) ++ : ggml_new_tensor_2d(tensors, GGML_TYPE_F32, d0, d1); ++ ggml_set_name(tensor, name); ++ std::memset(tensor->data, 0, ggml_nbytes(tensor)); ++ gguf_add_tensor(gguf, tensor); ++} ++ ++std::string write_fixture() { ++ const std::string path = "meshnet-dense-llama-range-fixture.gguf"; ++ gguf_context * gguf = gguf_init_empty(); ++ ggml_init_params params = { 128 * 1024, nullptr, false }; ++ ggml_context * tensors = ggml_init(params); ++ check(gguf && tensors, "failed to create dense-Llama fixture contexts"); ++ ++ gguf_set_val_str(gguf, "general.architecture", "llama"); ++ gguf_set_val_u32(gguf, "llama.context_length", 16); ++ gguf_set_val_u32(gguf, "llama.embedding_length", kEmbd); ++ gguf_set_val_u32(gguf, "llama.block_count", kLayers); ++ gguf_set_val_u32(gguf, "llama.feed_forward_length", kFfn); ++ gguf_set_val_u32(gguf, "llama.attention.head_count", 2); ++ gguf_set_val_u32(gguf, "llama.attention.head_count_kv", 2); ++ gguf_set_val_u32(gguf, "llama.rope.dimension_count", 4); ++ gguf_set_val_f32(gguf, "llama.attention.layer_norm_rms_epsilon", 1.0e-5f); ++ gguf_set_val_str(gguf, "tokenizer.ggml.model", "no_vocab"); ++ gguf_set_val_u32(gguf, "llama.vocab_size", kVocab); ++ ++ add_tensor(gguf, tensors, "token_embd.weight", kEmbd, kVocab); ++ add_tensor(gguf, tensors, "output_norm.weight", kEmbd); ++ add_tensor(gguf, tensors, "output.weight", kEmbd, kVocab); ++ for (int layer = 0; layer < kLayers; ++layer) { ++ const std::string p = "blk." + std::to_string(layer) + "."; ++ add_tensor(gguf, tensors, (p + "attn_norm.weight").c_str(), kEmbd); ++ add_tensor(gguf, tensors, (p + "attn_q.weight").c_str(), kEmbd, kEmbd); ++ add_tensor(gguf, tensors, (p + "attn_k.weight").c_str(), kEmbd, kEmbd); ++ add_tensor(gguf, tensors, (p + "attn_v.weight").c_str(), kEmbd, kEmbd); ++ add_tensor(gguf, tensors, (p + "attn_output.weight").c_str(), kEmbd, kEmbd); ++ add_tensor(gguf, tensors, (p + "ffn_norm.weight").c_str(), kEmbd); ++ add_tensor(gguf, tensors, (p + "ffn_gate.weight").c_str(), kEmbd, kFfn); ++ add_tensor(gguf, tensors, (p + "ffn_down.weight").c_str(), kFfn, kEmbd); ++ add_tensor(gguf, tensors, (p + "ffn_up.weight").c_str(), kEmbd, kFfn); ++ } ++ check(gguf_write_to_file(gguf, path.c_str(), false), "failed to write dense-Llama fixture"); ++ ggml_free(tensors); ++ gguf_free(gguf); ++ return path; ++} ++ ++int block_number(const std::string & name) { ++ int block = -1; ++ return std::sscanf(name.c_str(), "blk.%d.", &block) == 1 ? block : -1; ++} ++ ++bool is_allowed_endpoint_tensor(const std::string & name, int start, int end) { ++ if (name == "token_embd.weight") { ++ return start == 0; ++ } ++ if (name == "output_norm.weight" || name == "output.weight") { ++ return end == kLayers; ++ } ++ return false; ++} ++ ++llama_meshnet_range_report load_and_check(const std::string & path, int start, int end) { ++ llama_model_params params = llama_model_default_params(); ++ params.meshnet_owned_layer_start = start; ++ params.meshnet_owned_layer_end = end; ++ llama_model * model = llama_model_load_from_file(path.c_str(), params); ++ check(model != nullptr, "failed to load dense-Llama fixture"); ++ ++ llama_meshnet_range_report report = {}; ++ check(llama_model_meshnet_range_report(model, &report), "range report is absent"); ++ check(report.start_layer == start, "reported start does not match registered range"); ++ check(report.end_layer == end, "reported end does not match registered range"); ++ check(report.mapped_bytes > 0, "mmap report is empty"); ++ check(report.resident_bytes >= report.mapped_bytes, "resident bytes undercount mapped bytes"); ++ check(report.has_token_embeddings == (start == 0), "token-embedding ownership is not the head endpoint"); ++ check(report.has_output_head == (end == kLayers), "output-head ownership is not the tail endpoint"); ++ ++ const auto & tensors = llama_internal_get_tensor_map(model); ++ check(!tensors.empty(), "no tensors registered for owned range"); ++ for (const auto & [name, _] : tensors) { ++ const int block = block_number(name); ++ check((block >= start && block < end) || (block == -1 && is_allowed_endpoint_tensor(name, start, end)), ++ "registered tensor is outside the owned range and its endpoints"); ++ } ++ llama_model_free(model); ++ return report; ++} ++ ++} // namespace ++ ++int main() { ++ llama_backend_init(); ++ const std::string fixture = write_fixture(); ++ ++ const auto head = load_and_check(fixture, 0, 1); ++ const auto middle = load_and_check(fixture, 1, 3); ++ load_and_check(fixture, 3, 4); ++ check(middle.mapped_bytes > head.mapped_bytes, "two-layer range did not map more bytes than head"); ++ ++ // A stock load has no owned-range report and registers every tensor. ++ llama_model * stock = llama_model_load_from_file(fixture.c_str(), llama_model_default_params()); ++ check(stock != nullptr, "stock load failed"); ++ llama_meshnet_range_report stock_report = {}; ++ check(!llama_model_meshnet_range_report(stock, &stock_report), "stock load reported an owned range"); ++ check(llama_internal_get_tensor_map(stock).size() == 3 + 9 * kLayers, "stock load lost tensors"); ++ llama_model_free(stock); ++ ++ llama_model_params invalid = llama_model_default_params(); ++ invalid.meshnet_owned_layer_start = 3; ++ invalid.meshnet_owned_layer_end = 5; ++ check(llama_model_load_from_file(fixture.c_str(), invalid) == nullptr, "invalid range loaded"); ++ ++ std::remove(fixture.c_str()); ++ llama_backend_free(); ++ return 0; ++} diff --git a/packages/node/native/llama/patches/SHA256SUMS b/packages/node/native/llama/patches/SHA256SUMS index 2184ffb..708e8d7 100644 --- a/packages/node/native/llama/patches/SHA256SUMS +++ b/packages/node/native/llama/patches/SHA256SUMS @@ -1,3 +1,6 @@ # SHA-256 digests for the ordered patch series. Do not reorder this file. -1454216c019c1cb7f78d1d836fe4054164fff1d498391013bcaf13cc2d328c75 0001-cmake-reserve-meshnet-patch-stack-abi-marker.patch -51c205e3ca26e104f80c838eeeb11115b8d436036014116d2bb407178c30e0bd 0002-dense-llama-owned-range-loader.patch +1454216c019c1cb7f78d1d836fe4054164fff1d498391013bcaf13cc2d328c75 0001-cmake-reserve-meshnet-patch-stack-abi-marker.patch +6032ecca4d3ec3ce072f099dc402529aa6f53a7069a97e75222cf2fa50abb1a3 0002-dense-llama-owned-range-loading.patch +4871a37544df658980a01b4f94151a90b609fb144c931b4a814309ee608ebb46 0003-owned-range-filtered-state-report.patch +19d451ce259150ffede793c4eb547425375c0fcd97caf326b43e8f1a204f05b6 0004-dense-boundary-io-endpoint-guard.patch +cf263357a6a8de193f710836c7c467c38cac7099975303ee2628e0609daf5a47 0005-worker-range-report-hook.patch diff --git a/packages/node/native/llama/patches/UPSTREAM-ASSUMPTIONS.json b/packages/node/native/llama/patches/UPSTREAM-ASSUMPTIONS.json new file mode 100644 index 0000000..9b37c9a --- /dev/null +++ b/packages/node/native/llama/patches/UPSTREAM-ASSUMPTIONS.json @@ -0,0 +1,117 @@ +{ + "schema_version": 1, + "upstream_commit": "e920c523e3b8a0163fe498af5bf90df35ff51d25", + "patches": { + "0001-cmake-reserve-meshnet-patch-stack-abi-marker.patch": { + "concern": "build-marker", + "files": { + "CMakeLists.txt": { + "before": "81f23d7e70b7378511af5d01be680c03aebc2b15", + "after": "a9afcffa68bed7cbd8fad39ad9f95ad784251234" + }, + "cmake/meshnet-patch-stack.cmake": { + "before": null, + "after": "910646b4d6164831d4f8e523dd5e49ce7796994f" + } + }, + "api_assumptions": [ + "CMake >= 3.14 include() of a project-relative module from the top-level CMakeLists.txt", + "add_library( INTERFACE) and target_compile_definitions(... INTERFACE ...)" + ] + }, + "0002-dense-llama-owned-range-loading.patch": { + "concern": "range-loading", + "files": { + "include/llama.h": { + "before": "a311ac2023579376ed571a614dcac9d259692e56", + "after": "229946ede026ef36b4c4f0355e0421b082334ee0" + }, + "src/llama-model.cpp": { + "before": "d87481381e46025c9c87c4af5f116015a44124c3", + "after": "05b8b9c912716fd57061100ec5c47203f79b50ce" + }, + "src/models/llama.cpp": { + "before": "4bfebc8843c655e122e5f2064a791c583ad3779b", + "after": "c3092763b82e93b596ba531a0fb01769df6f0e27" + } + }, + "api_assumptions": [ + "llama_model_params is an aggregate C struct initialized by llama_model_default_params()", + "llama_model_loader::done_getting_tensors(bool partial = false) const", + "llm_hparams::n_layer() and llama_model_base::load_tensors(llama_model_loader &)", + "llama_model_llama::load_arch_tensors per-layer create_tensor loop and LLM_ARCH_LLAMA gate" + ] + }, + "0003-owned-range-filtered-state-report.patch": { + "concern": "filtered-state", + "files": { + "include/llama.h": { + "before": "229946ede026ef36b4c4f0355e0421b082334ee0", + "after": "6fd7ad509006190a7e7dd84a3d1ac2672ad2d21d" + }, + "src/llama-model.cpp": { + "before": "05b8b9c912716fd57061100ec5c47203f79b50ce", + "after": "efb290c1f3dceb868dc5e316e765767ddf0532f8" + }, + "src/llama-model.h": { + "before": "45b054cedf1d1e6accc7cf8aafcbae374614e64f", + "after": "5ef7a1515a9b27858e3e5fe694a285a4a55a8bf2" + } + }, + "api_assumptions": [ + "llama_model::impl pimpl struct and llama_model::meshnet_range_report(out) const accessor", + "pimpl->ctxs_bufs backend buffer map and ggml_backend_buffer_get_size", + "llama_model_loader::use_mmap public member" + ] + }, + "0004-dense-boundary-io-endpoint-guard.patch": { + "concern": "boundary-io", + "files": { + "include/llama.h": { + "before": "6fd7ad509006190a7e7dd84a3d1ac2672ad2d21d", + "after": "8a75213494cc07b20ca2d99cd21b30109e758b7b" + }, + "src/llama-model.cpp": { + "before": "efb290c1f3dceb868dc5e316e765767ddf0532f8", + "after": "2ea8598ad2037920082ef88db75684283c65118c" + }, + "src/models/llama.cpp": { + "before": "c3092763b82e93b596ba531a0fb01769df6f0e27", + "after": "3b6854d0c32cdb816a0b31c337767fa1a9305b24" + } + }, + "api_assumptions": [ + "llama_model::tensors_by_name is std::vector>", + "llama_model::{output_norm, output} members reflect output-head registration", + "llama_model_llama::graph constructor and llm_graph_context::n_layer" + ] + }, + "0005-worker-range-report-hook.patch": { + "concern": "worker-hooks", + "files": { + "include/llama.h": { + "before": "8a75213494cc07b20ca2d99cd21b30109e758b7b", + "after": "5818daf94d481c5eee201ad1300f37e7c0fe60f9" + }, + "src/llama-model.cpp": { + "before": "2ea8598ad2037920082ef88db75684283c65118c", + "after": "c9d3cf6d34cbe2fd50100b91ed2a0436f5b72415" + }, + "tests/CMakeLists.txt": { + "before": "855295c152faa78fa4acdec54013940c41627be9", + "after": "9a7be6eedfd6a311694a8a6a95976fb230ee165c" + }, + "tests/test-meshnet-range-ownership.cpp": { + "before": null, + "after": "6b3aa5ac5dc1b6074401f97b8acd07b00dfa9780" + } + }, + "api_assumptions": [ + "LLAMA_API export macro and extern \"C\" block in include/llama.h", + "tests/CMakeLists.txt llama_build_and_test() helper", + "llama_internal_get_tensor_map(const llama_model *) in src/llama-model.h", + "gguf empty-context writer API: gguf_init_empty, gguf_add_tensor, gguf_write_to_file" + ] + } + } +} diff --git a/packages/node/native/llama/patches/series b/packages/node/native/llama/patches/series index 951edaf..e74e8e3 100644 --- a/packages/node/native/llama/patches/series +++ b/packages/node/native/llama/patches/series @@ -1,2 +1,5 @@ 0001-cmake-reserve-meshnet-patch-stack-abi-marker.patch -0002-dense-llama-owned-range-loader.patch +0002-dense-llama-owned-range-loading.patch +0003-owned-range-filtered-state-report.patch +0004-dense-boundary-io-endpoint-guard.patch +0005-worker-range-report-hook.patch diff --git a/scripts/llama_cpp_dependency.py b/scripts/llama_cpp_dependency.py index 6c8b32f..42650fc 100644 --- a/scripts/llama_cpp_dependency.py +++ b/scripts/llama_cpp_dependency.py @@ -3,6 +3,13 @@ This tool deliberately owns only a source dependency boundary. It never downloads a model, invokes inference, or interprets generated text. + +DGR-028 adds the numbered patch-stack contract: the ordered series applies, +checks, and reverses deterministically against the exact manifest pin, each +patch's recorded upstream file/API assumptions are enforced before it is +attempted, the first incompatible patch is named on failure, and the stack is +refused if it carries license/attribution damage or Meshnet control-plane +(routing, billing, relay, authentication) code. """ from __future__ import annotations @@ -61,6 +68,11 @@ def _run(*args: str, cwd: pathlib.Path | None = None) -> str: return completed.stdout.strip() +def _git(source: pathlib.Path, *args: str) -> str: + """Run Git against the materialized upstream checkout.""" + return _run("git", "-C", str(source), *args) + + def _load_lock() -> dict[str, Any]: try: lock = json.loads(LOCK_PATH.read_text()) @@ -113,8 +125,171 @@ def _patches(lock: dict[str, Any]) -> list[pathlib.Path]: return patches -def _git(source: pathlib.Path, *args: str) -> str: - return _run("git", "-C", str(source), *args) +def _parse_patch_files(patch: pathlib.Path) -> dict[str, tuple[str | None, str | None]]: + """Parse one patch into {path: (before-short, after-short)}. + + Short object IDs come from the patch's ``index`` lines; an all-zero side + means the file is created (``before is None``) or deleted (``after is + None``). Patch order is preserved. + """ + files: dict[str, tuple[str | None, str | None]] = {} + current: str | None = None + for line in patch.read_text().splitlines(): + header = re.match(r"^diff --git a/(.+) b/(.+)$", line) + if header: + if header.group(1) != header.group(2): + raise DependencyError(f"{patch.name}: rename/copy diffs are unsupported: {line}") + current = header.group(1) + files[current] = (None, None) + continue + index = re.match(r"^index ([0-9a-f]{7,40})\.\.([0-9a-f]{7,40})(?:\s|$)", line) + if index and current is not None: + before, after = index.group(1), index.group(2) + files[current] = ( + None if set(before) == {"0"} else before, + None if set(after) == {"0"} else after, + ) + if not files: + raise DependencyError(f"{patch.name}: no file diffs found") + return files + + +_ASSUMPTIONS_DEFAULT = "patches/UPSTREAM-ASSUMPTIONS.json" + + +def _assumptions(lock: dict[str, Any], patches: list[pathlib.Path]) -> dict[str, Any]: + """Load and validate the recorded upstream file/API assumptions. + + The record must cover exactly the ordered series, each recorded file must + match the patch's parsed diff headers, and each recorded full object ID + must agree with the patch's abbreviated ``index`` IDs. A stale or edited + record is a fail-closed error, never a warning. + """ + configured = lock.get("patch_assumptions", _ASSUMPTIONS_DEFAULT) + if not isinstance(configured, str) or not configured: + raise DependencyError("patch_assumptions must name a manifest-relative JSON path") + relative = pathlib.Path(configured) + if relative.is_absolute() or ".." in relative.parts: + raise DependencyError("patch_assumptions must stay inside the repository manifest tree") + candidate = (LLAMA_DIR / relative).absolute() + try: + candidate.relative_to(LLAMA_DIR.absolute()) + except ValueError as error: + raise DependencyError("patch_assumptions must live under packages/node/native/llama") from error + if not candidate.is_file(): + raise DependencyError(f"recorded upstream assumptions are missing: {candidate}") + try: + doc = json.loads(candidate.read_text()) + except (OSError, json.JSONDecodeError) as error: + raise DependencyError(f"invalid upstream assumptions: {candidate}: {error}") from error + if not isinstance(doc, dict) or doc.get("schema_version") != 1: + raise DependencyError("upstream assumptions must declare schema_version 1") + if doc.get("upstream_commit") != lock["commit"]: + raise DependencyError("upstream assumptions disagree with the locked commit") + recorded = doc.get("patches") + if not isinstance(recorded, dict): + raise DependencyError("upstream assumptions must record a patches object") + names = [patch.name for patch in patches] + if list(recorded.keys()) != names: + raise DependencyError( + f"upstream assumptions do not cover exactly the ordered series: " + f"expected {names}, got {list(recorded.keys())}" + ) + for patch in patches: + entry = recorded[patch.name] + if not isinstance(entry.get("concern"), str) or not entry["concern"]: + raise DependencyError(f"{patch.name}: assumptions are missing the scoped concern") + symbols = entry.get("api_assumptions") + if not isinstance(symbols, list) or not symbols or not all(isinstance(s, str) and s for s in symbols): + raise DependencyError(f"{patch.name}: assumptions must record upstream file/API assumptions") + files = entry.get("files") + if not isinstance(files, dict): + raise DependencyError(f"{patch.name}: assumptions must record a files object") + parsed = _parse_patch_files(patch) + if list(files.keys()) != list(parsed.keys()): + raise DependencyError( + f"{patch.name}: recorded files {list(files.keys())} disagree with the patch bytes {list(parsed.keys())}" + ) + for path, (before_short, after_short) in parsed.items(): + blobs = files[path] + if not isinstance(blobs, dict): + raise DependencyError(f"{patch.name}: recorded blobs for {path} must be an object") + for side, short in (("before", before_short), ("after", after_short)): + value = blobs.get(side) + if short is None: + if value is not None: + raise DependencyError(f"{patch.name}: {path} {side} must be null for a created/deleted file") + elif not isinstance(value, str) or not re.fullmatch(r"[0-9a-f]{40}", value) or not value.startswith(short): + raise DependencyError( + f"{patch.name}: recorded {side} blob for {path} does not match the patch index ID {short}" + ) + return doc + + +# Control-plane vocabulary that must never enter the upstream patch stack: +# Meshnet routing, billing, relay, authentication, and transport semantics are +# backend-agnostic and live outside the llama.cpp fork boundary (ADR-0024). +_CONTROL_PLANE_TERMS = re.compile( + r"\b(tracker|routing|route session|grpc|billing|wallet|relay|telemetry|" + r"auth|authentication|load balanc\w*)\b", + re.IGNORECASE, +) + +_LICENSE_PATH = re.compile(r"(^|/)(license|copying|notice)(\..*)?$", re.IGNORECASE) + +_LICENSE_TEXT = re.compile( + r"copyright|permission is hereby granted|mit license|apache license|gnu general public", + re.IGNORECASE, +) + + +def _verify_patch_stack_boundaries(patches: list[pathlib.Path]) -> None: + """Refuse license/attribution damage or control-plane code in the stack.""" + for patch in patches: + for line in patch.read_text().splitlines(): + header = re.match(r"^diff --git a/(.+) b/(.+)$", line) + if header and (_LICENSE_PATH.search(header.group(1)) or _LICENSE_PATH.search(header.group(2))): + raise DependencyError(f"{patch.name}: license/attribution files may not be patched: {line}") + if line.startswith("-") and not line.startswith("---") and _LICENSE_TEXT.search(line[1:]): + raise DependencyError(f"{patch.name}: removing license or attribution text is refused: {line}") + body = patch.read_text() + match = _CONTROL_PLANE_TERMS.search(body) + if match: + raise DependencyError( + f"{patch.name}: Meshnet control-plane term {match.group(0)!r} must not enter the patch stack" + ) + + +def _index_blob(source: pathlib.Path, path: str) -> str | None: + """Return the staged blob object ID for path, or None when absent.""" + records = _git(source, "ls-files", "-s", "-z", "--", path).split("\0") + entries = [record for record in records if record] + if not entries: + return None + if len(entries) != 1: + raise DependencyError(f"unmerged index entry blocks patch verification: {path}") + metadata, _ = entries[0].split("\t", 1) + mode, blob, stage = metadata.split() + if stage != "0": + raise DependencyError(f"unmerged index entry blocks patch verification: {path}") + return blob + + +def _check_assumption_blobs( + source: pathlib.Path, + patch: pathlib.Path, + files: dict[str, Any], + side: str, +) -> None: + """Fail on the first recorded pre-/post-image blob the index disagrees with.""" + for path, blobs in files.items(): + expected = blobs[side] + actual = _index_blob(source, path) + if actual != expected: + raise DependencyError( + f"first incompatible patch: {patch.name}: recorded {side} blob for {path} is " + f"{expected}, found {actual}" + ) def _verify_tracked_content(source: pathlib.Path, lock: dict[str, Any]) -> None: @@ -235,16 +410,36 @@ def fetch(workspace: pathlib.Path) -> pathlib.Path: def apply(source: pathlib.Path) -> None: lock = _load_lock() patches = _patches(lock) + assumptions = _assumptions(lock, patches) + _verify_patch_stack_boundaries(patches) _verify_source(source, lock, require_clean=True) for patch in patches: + files = assumptions["patches"][patch.name]["files"] + _check_assumption_blobs(source, patch, files, "before") _git(source, "apply", "--check", str(patch)) _git(source, "apply", "--index", str(patch)) + _check_assumption_blobs(source, patch, files, "after") _verify_patched_source(source, lock) +def reverse(source: pathlib.Path) -> None: + """Reverse the complete verified stack and recover the exact clean pin.""" + lock = _load_lock() + patches = _patches(lock) + assumptions = _assumptions(lock, patches) + _verify_patch_stack_boundaries(patches) + _verify_source(source, lock, require_clean=False) + _verify_patched_source(source, lock) + for patch in reversed(patches): + files = assumptions["patches"][patch.name]["files"] + _check_assumption_blobs(source, patch, files, "after") + _git(source, "apply", "--reverse", "--check", str(patch)) + _git(source, "apply", "--reverse", "--index", str(patch)) + _check_assumption_blobs(source, patch, files, "before") + _verify_source(source, lock, require_clean=True) + + def _verify_patched_source(source: pathlib.Path, lock: dict[str, Any]) -> None: - if _git(source, "diff", "--quiet"): - raise DependencyError("local unstaged edits detected after applying patch stack") changed_paths = _git(source, "diff", "--cached", "--name-only").splitlines() if changed_paths != lock["patched_paths"]: raise DependencyError(f"patched paths drifted: expected {lock['patched_paths']}, got {changed_paths}") @@ -297,6 +492,13 @@ def smoke(binary: pathlib.Path) -> None: print(output) +def verify(workspace: pathlib.Path) -> None: + """Apply, verify, reverse, and leave the exact cached pin pristine.""" + source = fetch(workspace) + apply(source) + reverse(source) + + def reproduce(workspace: pathlib.Path) -> None: source = fetch(workspace) build_dir = workspace.resolve() / "build" @@ -330,6 +532,10 @@ def main() -> int: fetch_parser.add_argument("--workspace", type=pathlib.Path, default=ROOT / "build/llama.cpp") apply_parser = subcommands.add_parser("apply") apply_parser.add_argument("--source-dir", type=pathlib.Path, required=True) + reverse_parser = subcommands.add_parser("reverse") + reverse_parser.add_argument("--source-dir", type=pathlib.Path, required=True) + verify_parser = subcommands.add_parser("verify") + verify_parser.add_argument("--workspace", type=pathlib.Path, default=ROOT / "build/llama.cpp") build_parser = subcommands.add_parser("build") build_parser.add_argument("--source-dir", type=pathlib.Path, required=True) build_parser.add_argument("--build-dir", type=pathlib.Path, required=True) @@ -345,6 +551,10 @@ def main() -> int: fetch(args.workspace) elif args.command == "apply": apply(args.source_dir) + elif args.command == "reverse": + reverse(args.source_dir) + elif args.command == "verify": + verify(args.workspace) elif args.command == "build": build(args.source_dir, args.build_dir) elif args.command == "smoke": diff --git a/tests/test_llama_cpp_dependency.py b/tests/test_llama_cpp_dependency.py index 1c206b9..5cdc638 100644 --- a/tests/test_llama_cpp_dependency.py +++ b/tests/test_llama_cpp_dependency.py @@ -240,7 +240,9 @@ def test_dependency_script_reports_the_locked_boundary_without_network() -> None report = json.loads(completed.stdout) assert report["commit"] == (LLAMA_DIR / "UPSTREAM_COMMIT").read_text().strip() - assert report["patch_count"] == 2 + assert report["patch_count"] == len( + (LLAMA_DIR / "patches/series").read_text().splitlines() + ) assert report["model_downloads"] is False assert report["semantic_certification"] is False assert "dense" in report["glm_stock_limitations"].lower()