Skip to content

Update pinned llama.cpp - #927

Merged
i386 merged 2 commits into
mainfrom
jd/update-llama-pin
Jun 30, 2026
Merged

Update pinned llama.cpp#927
i386 merged 2 commits into
mainfrom
jd/update-llama-pin

Conversation

@i386

@i386 i386 commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • update the pinned llama.cpp upstream SHA to 86b94708f22478f900b76ca02e316f4f3418faff
  • rebase and regenerate the 10-patch Skippy llama.cpp patch queue on top of the new upstream

Validation

  • LLAMA_WORKDIR=$(mktemp -d /tmp/mesh-llm-llama.XXXXXX) + scripts/prepare-llama.sh pinned passed in a clean temporary checkout
  • just build compiled patched llama.cpp, the UI, and the Rust debug binary; the wrapper then hung in local macOS keychain trust repair for Mesh-LLM Local Codesign, so I interrupted only that signing/trust step

Summary by CodeRabbit

  • New Features

    • Added experimental staged inference support across more model families.
    • Introduced broader chat-template handling, including thinking-mode and grammar improvements.
    • Added new sampling options, session checkpoint/restore, and support for loading models from multiple parts.
    • Expanded state portability with native KV page, recurrent state, and prefix-cache support.
  • Bug Fixes

    • Improved handling of staged execution boundaries, token verification, and detokenization.
    • Fixed several model-loading and cache-management edge cases for better reliability.
  • Chores

    • Updated ABI/versioning and internal compatibility markers.

@github-actions
github-actions Bot requested a review from ndizazzo June 30, 2026 02:46
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@i386, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 25 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a1486044-6cf3-4442-93ea-9ebc31df0c2f

📥 Commits

Reviewing files that changed from the base of the PR and between 5c20125 and b4a1e9d.

📒 Files selected for processing (1)
  • .github/workflows/pr_builds.yml
📝 Walkthrough

Walkthrough

Ten sequential patch files add the "Skippy" experimental C ABI to a vendored llama.cpp fork. The patches introduce a public include/skippy.h header with model/session/KV-page/GGUF-slice APIs, thread-local graph and model-loader stage filters, architecture-specific activation sideband graph inputs, native KV-page export/import, KV-cache compaction, ordered multi-part GGUF loading, per-architecture staged-execution early returns, and MTP (nextn) decode support.

Changes

Skippy Staged-Inference ABI

Layer / File(s) Summary
Public Skippy C ABI header and build wiring
third_party/llama.cpp/patches/0001-...patch, third_party/llama.cpp/patches/0009-...patch, third_party/llama.cpp/patches/0010-...patch, third_party/llama.cpp/upstream.txt
Introduces include/skippy.h (and later include/skippy/common.h, include/skippy/devices.h) defining versioning enums, status/error types, runtime config, activation descriptors, and all exported C entrypoints. ABI patch version increments across patches (0→27). Wires skippy.cpp and stage-chat.cpp into build targets. Pins a new upstream hash.
Stage filter infrastructure
third_party/llama.cpp/patches/0001-...patch, third_party/llama.cpp/patches/0006-...patch
Adds thread-local skippy_graph_filter (set/clear/get) in llama-graph.cpp/.h; modifies embedding graph construction to handle null tok_embd for non-first-stage activations. Adds thread-local llama_model_loader_stage_filter in llama-model-loader.cpp/.h with tensor-skip logic that returns nullptr and adjusts size_data for filtered tensors.
Activation sideband graph inputs
third_party/llama.cpp/patches/0002-...patch, third_party/llama.cpp/patches/0007-...patch, third_party/llama.cpp/patches/0008-...patch
Introduces llm_graph_input_stage_tokens (I32 token IDs), llm_graph_input_rwkv7_v_first (RWKV7 hidden state), and llm_graph_input_gemma3n_altup (Gemma3n AltUp projection) with TLS backing stores and sideband output tensors in llm_graph_result. Guards llm_graph_input_mem_hybrid* set_input/can_reuse paths against absent backend buffers. Adds llama_context::get_gf_res_prev().
KV cache native page staging (across patches 2, 4, 5, 8)
third_party/llama.cpp/patches/0002-...patch, third_party/llama.cpp/patches/0004-...patch, third_party/llama.cpp/patches/0005-...patch, third_party/llama.cpp/patches/0008-...patch
Patch 2 introduces stage_export_kv_page/stage_import_kv_page with descriptor-driven KV tensor copy. Patch 4 removes this implementation. Patch 5 re-adds it with refined validation and KV cell allocation. Patch 8 updates it to skip absent V streams and guard zero-sized V loops rather than failing.
KV cache cell compaction
third_party/llama.cpp/patches/0009-...patch
Extends llama_kv_cache::init_update() with optional cell compaction when optimize is enabled, adding compact_cells() and copy_compacted_cells() internals and a did_compact flag that prevents false LLAMA_MEMORY_STATUS_NO_UPDATE from being emitted.
Model loader: multi-part loading and tensor filtering refinements
third_party/llama.cpp/patches/0002-...patch, third_party/llama.cpp/patches/0003-...patch, third_party/llama.cpp/patches/0007-...patch, third_party/llama.cpp/patches/0008-...patch
Adds ordered_parts mode to llama_model_loader for GGUF multi-part loading without split metadata. Introduces llama_model_loader_last_tensor_filtered() to adjust QKV bias creation around filtered weights. Adds a requested-shape-match helper for Gemma3n AltUp filtering. Fixes GEMMA4 expert retention, MoE scale tensor metadata checks, and n_embd_head_v_all initialization.
Model graph stage-boundary early returns
third_party/llama.cpp/patches/0006-...patch
Updates model graph construction to limit layer iteration to [il_start, il_end), suppresses inp_pos/inp_out_ids when output is excluded, and emits a stage_boundary callback with early return when the filtered stage excludes output. Demonstrated in lfm2 with the same pattern applied across other architectures.
MTP (nextn) execution for DeepSeek2 and GLM-DSA
third_party/llama.cpp/patches/0010-...patch
Adds llama_model_deepseek2::graph_mtp and a GLM-DSA alias. Validates nextn embedding byte sizes in llama_context::decode before async tensor reads. Adds a top_k non-null assertion in the attention graph. Adjusts the DeepSeek2/GLM-DSA stage-filter lambda by MTP context type and anchors the h_nextn graph node.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • Mesh-LLM/mesh-llm#839: Carries the early Skippy ABI header and skippy_graph_filter/llama_model_loader_stage_filter wiring that this PR replaces with updated patch versions.
  • Mesh-LLM/mesh-llm#858: Consumes the skippy_verify_tokens_frame_sampled/skippy_decode_step_frame_sampled_mtp_n1 entry points added in this PR for GLM MTP parity and batched verify sampling.
  • Mesh-LLM/mesh-llm#888: Propagates native_mtp_enabled in layer-package generation metadata to select the native MTP execution paths introduced here.

Suggested reviewers

  • michaelneale
  • ndizazzo
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: updating the pinned llama.cpp upstream.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jd/update-llama-pin

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (16)
third_party/llama.cpp/patches/0004-Add-lanes-external-media-and-chat-grammar-support.patch (3)

1506-1527: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Advertise the implemented chat-tool and grammar features.

skippy_abi_features() defines support bits for SKIPPY_FEATURE_CHAT_TEMPLATE_TOOLS and SKIPPY_FEATURE_CHAT_SAMPLING_GRAMMAR, but does not return them, so feature-probing clients will treat these implemented APIs as unavailable.

Suggested fix
            SKIPPY_FEATURE_SESSION_CHECKPOINT |
            SKIPPY_FEATURE_PACKAGE_PART_LOAD |
            SKIPPY_FEATURE_GENERATION_SIGNALS |
-           SKIPPY_FEATURE_EXTERNAL_MEDIA_PREFILL;
+           SKIPPY_FEATURE_EXTERNAL_MEDIA_PREFILL |
+           SKIPPY_FEATURE_CHAT_TEMPLATE_TOOLS |
+           SKIPPY_FEATURE_CHAT_SAMPLING_GRAMMAR;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0004-Add-lanes-external-media-and-chat-grammar-support.patch`
around lines 1506 - 1527, Update skippy_abi_features() so it advertises every
implemented capability currently supported by the ABI, including
SKIPPY_FEATURE_CHAT_TEMPLATE_TOOLS and SKIPPY_FEATURE_CHAT_SAMPLING_GRAMMAR, by
adding their bits to the returned feature mask alongside the existing
SKIPPY_FEATURE_GENERATION_SIGNALS and SKIPPY_FEATURE_EXTERNAL_MEDIA_PREFILL
entries. Keep the feature list in sync with the actual APIs exposed in this
patch so clients probing skippy_abi_features() see the new chat-tool and grammar
support as available.

1751-1757: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Return an error for invalid chat sampling metadata.

skippy_session_configure_chat_sampling() clears sampling and returns success when JSON parsing or grammar setup throws. That hides malformed metadata and silently disables grammar-constrained sampling.

Suggested fix
     } catch (const std::exception & e) {
         skippy_clear_chat_sampling(session);
-        return skippy_success(out_error);
+        skippy_set_error(out_error, SKIPPY_STATUS_INVALID_ARGUMENT, e.what());
+        return SKIPPY_STATUS_INVALID_ARGUMENT;
     } catch (...) {
         skippy_clear_chat_sampling(session);
-        return skippy_success(out_error);
+        skippy_set_error(out_error, SKIPPY_STATUS_INVALID_ARGUMENT, "invalid chat sampling metadata");
+        return SKIPPY_STATUS_INVALID_ARGUMENT;
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0004-Add-lanes-external-media-and-chat-grammar-support.patch`
around lines 1751 - 1757, In skippy_session_configure_chat_sampling(), the
current catch blocks for std::exception and ... clear sampling and return
success, which hides malformed metadata; change this flow so JSON parsing or
grammar setup failures propagate as an error instead of silently succeeding. Use
the existing out_error and skippy_success/skippy_clear_chat_sampling handling in
this function to return a failure status with a clear invalid chat sampling
metadata message, while keeping the success path unchanged for valid metadata.

1538-1569: 🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Synchronize shared context and lane ownership.

Sessions now share stage_model->ctx and mutate lane_in_use, but there is no mutex around lane allocation/freeing or decode/reset/trim calls on the shared llama_context. Concurrent sessions can race and corrupt per-lane KV/state.

Also applies to: 1621-1663

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0004-Add-lanes-external-media-and-chat-grammar-support.patch`
around lines 1538 - 1569, `stage_model->ctx` and `lane_in_use` are shared across
sessions without synchronization, so lane allocation/freeing and shared-context
operations can race. Add a mutex/lock on the stage model and guard all lane
ownership changes plus every decode/reset/trim path that touches the shared
`llama_context`. Update the affected session methods around
`skippy_finish_model_open` and the later lane-management code referenced by the
review so they acquire the same lock before reading or mutating `lane_in_use` or
calling into `stage_model->ctx`.
third_party/llama.cpp/patches/0008-Add-external-decode-media-prefill-and-newer-family-s.patch (3)

946-960: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject malformed position sidebands instead of silently falling back.

If positions != nullptr but position_count is wrong, this path ignores caller-supplied positions and uses n_past positions. For external media/M-RoPE prefill, that can decode at the wrong positions without surfacing an error.

Suggested fix
-    if (positions != nullptr && position_count == expected_position_count) {
+    if ((positions == nullptr) != (position_count == 0)) {
+        skippy_set_error(out_error, SKIPPY_STATUS_INVALID_ARGUMENT, "positions and position_count must be provided together");
+        return SKIPPY_STATUS_INVALID_ARGUMENT;
+    }
+    if (positions != nullptr && position_count != expected_position_count) {
+        skippy_set_error(out_error, SKIPPY_STATUS_INVALID_ARGUMENT, "position_count does not match model position layout");
+        return SKIPPY_STATUS_INVALID_ARGUMENT;
+    }
+    if (positions != nullptr) {
         std::memcpy(pos_storage.data(), positions, expected_position_count*sizeof(llama_pos));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0008-Add-external-decode-media-prefill-and-newer-family-s.patch`
around lines 946 - 960, The position handling in the external prefill path
silently falls back to n_past when positions is non-null but position_count is
invalid. Update the logic around the pos_storage fill branch to reject malformed
caller-supplied sidebands instead of entering the fallback path, and surface an
error from this decode/prefill function so external media/M-RoPE prefill cannot
proceed with incorrect positions.

393-405: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Guard the transposed-V loops when a selected layer has no V stream.

The new selection path permits has_v_stream == false, setting v_element_bytes/v_row_bytes to zero, but only the non-transposed V loops are skipped. In v_trans mode, the existing branch will still dereference layer->v_stream[strm].

Suggested fix
     } else {
         for (const auto * layer : selected) {
+            if (v_element_bytes == 0) {
+                continue;
+            }
             auto * v = layer->v_stream[strm];
     } else {
         for (const auto * layer : selected) {
+            if (desc.v_element_bytes == 0) {
+                continue;
+            }
             auto * v = layer->v_stream[strm];

Also applies to: 409-442

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0008-Add-external-decode-media-prefill-and-newer-family-s.patch`
around lines 393 - 405, The selected-layer export path in
llama_kv_cache::stage_export_kv_page still enters the transposed-V handling even
when a layer has no V stream, which can dereference layer->v_stream[strm]. Add
an early skip or equivalent guard in the v_trans branch alongside the existing
v_row_bytes check so layers with no V stream are ignored in both transposed and
non-transposed paths, and make sure the guard applies to the loops that build V
payloads for selected layers.

28-95: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Bump the staged-runtime ABI for these new exports. This patch adds public flags and session/prefill entrypoints, so SKIPPY_ABI_VERSION_* should move here and the Rust FFI constants should stay aligned.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0008-Add-external-decode-media-prefill-and-newer-family-s.patch`
around lines 28 - 95, The staged-runtime ABI needs to be bumped to account for
the new public exports added here. Update the SKIPPY_ABI_VERSION constant(s)
alongside the new skippy_session_begin_external_decode,
skippy_session_end_external_decode, skippy_prefill_chunk_frame_sampled,
skippy_prefill_chunk_frame_with_positions, and
skippy_prefill_chunk_frame_sampled_with_positions declarations, and make sure
the Rust FFI-side ABI constants remain in sync with the C header.

Source: Coding guidelines

third_party/llama.cpp/patches/0003-Add-staged-sampling-checkpoints-and-part-loading.patch (1)

279-295: 🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift

Keep ordered GGUF part contexts alive.

ctx_gguf is local to each loop iteration, but weights_map stores pointers derived from it (ctx_gguf.get() and cur). If llama_tensor_weight retains those pointers, later tensor loading will dereference freed GGUF/tensor metadata.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0003-Add-staged-sampling-checkpoints-and-part-loading.patch`
around lines 279 - 295, The staged part-loading logic in the tensor-loading loop
is storing `llama_tensor_weight` entries that depend on `ctx_gguf.get()` and
`cur`, but `ctx_gguf` is currently a per-iteration local in the GGUF part
handling code. Update the part-context management so the GGUF contexts created
in this block stay alive for as long as `weights_map` may use them, using the
existing `contexts`/`weights_map` flow around `gguf_init_from_file`,
`weights_map.emplace`, and `llama_tensor_weight` to retain ordered per-part
metadata instead of letting it be destroyed at the end of the loop iteration.
third_party/llama.cpp/patches/0005-Add-resident-prefix-cache-and-session-refinements.patch (3)

1268-1274: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Synchronize before mutating native KV tensors.

Export synchronizes before reading KV state, but import writes cells/tensors without first draining pending backend work. Add a pre-import sync before stage_import_kv_page.

Proposed fix
     if (kv == nullptr) {
         return out_error != nullptr && *out_error != nullptr ? (*out_error)->status : SKIPPY_STATUS_RUNTIME_ERROR;
     }
 
+    session->ctx->synchronize();
+
     std::string error;
     if (!kv->stage_import_kv_page(session->seq_id, *desc, input, input_bytes, error)) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0005-Add-resident-prefix-cache-and-session-refinements.patch`
around lines 1268 - 1274, Import path needs a pre-write synchronization before
mutating native KV tensors in the session import flow. In the function that
calls skippy_get_kv_cache and then kv->stage_import_kv_page, insert a sync/drain
of pending backend work immediately after the cache lookup succeeds and before
any KV page import or tensor writes happen. Use the same synchronization
mechanism already used by the export/read path so the import path is consistent
and safe.

884-891: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Set n_past to the imported state length.

Using max(session->n_past, imported_cells) leaves the session ahead after restoring a shorter state, so the next decode can append at the wrong position.

Proposed fix
-    session->n_past = std::max<int32_t>(
-            session->n_past,
-            static_cast<int32_t>(std::min<uint32_t>(
-                    imported_cells,
-                    static_cast<uint32_t>(std::numeric_limits<int32_t>::max()))));
+    session->n_past = static_cast<int32_t>(std::min<uint32_t>(
+            imported_cells,
+            static_cast<uint32_t>(std::numeric_limits<int32_t>::max())));
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0005-Add-resident-prefix-cache-and-session-refinements.patch`
around lines 884 - 891, Update skippy_update_session_state_after_import so it
assigns session->n_past from imported_cells instead of taking the max with the
existing value; the restore path should reflect the imported state length
exactly, with the same clamping to int32_t bounds already present. Keep the fix
localized to this helper and ensure any callers relying on restored session
position now advance from the imported length rather than the prior session
state.

520-526: 🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Avoid deleting existing KV cells before import allocation succeeds.

seq_rm runs before find_slot; if allocation fails, the original page range has already been removed. Preflight capacity or make this path rollback-safe before mutating the cache.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0005-Add-resident-prefix-cache-and-session-refinements.patch`
around lines 520 - 526, The import path in seq_rm/find_slot is mutating the KV
cache too early: existing cells are removed before allocation is confirmed.
Update the logic around seq_rm, find_slot, and apply_ubatch so capacity is
checked first or the operation is rollback-safe, and only call seq_rm after a
slot has been successfully reserved to avoid losing the original page range on
failure.
third_party/llama.cpp/patches/0010-Add-MTP-execution-support-and-sampling-cleanup.patch (5)

1953-1955: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Set out_error before returning unsupported for AltUp batch input.

This branch returns SKIPPY_STATUS_UNSUPPORTED without an error message, unlike the surrounding unsupported cases.

Proposed fix
             float * dst = embd_storage.data() + static_cast<size_t>(i)*n_embd_inp;
             if ((input_desc->flags & SKIPPY_ACTIVATION_FLAG_GEMMA3N_ALTUP) != 0) {
+                skippy_set_error(out_error, SKIPPY_STATUS_UNSUPPORTED, "batched activation decode does not support Gemma3n AltUp sidebands yet");
                 return SKIPPY_STATUS_UNSUPPORTED;
             } else if (n_embd_inp == n_embd) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0010-Add-MTP-execution-support-and-sampling-cleanup.patch`
around lines 1953 - 1955, In the AltUp batch-input guard inside the Skippy
execution path, the early return from the branch that checks
SKIPPY_ACTIVATION_FLAG_GEMMA3N_ALTUP should also populate out_error before
returning SKIPPY_STATUS_UNSUPPORTED. Update the same conditional block near the
n_embd_inp/n_embd handling so it matches the surrounding unsupported-case
behavior by setting a clear error message first, then returning.

1446-1454: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Don’t mutate the MTP sidecar when no draft output is requested.

With out_mtp_draft == nullptr, skippy_mtp_propose_next() still decodes into mtp_ctx but never records mtp_has_pending_draft, leaving sidecar KV state inconsistent for the next sync.

Proposed fix
     if (!skippy_mtp_available(session) || predicted_token < 0 || !session->mtp_has_pending_h) {
         return skippy_success(out_error);
     }
+    if (out_mtp_draft == nullptr) {
+        return skippy_success(out_error);
+    }
 
     llama_context * mtp_ctx = session->stage_model->mtp_ctx;

Also applies to: 1857-1861

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0010-Add-MTP-execution-support-and-sampling-cleanup.patch`
around lines 1446 - 1454, The MTP draft bookkeeping in skippy_mtp_propose_next
is updating session state only inside the out_mtp_draft branch, which leaves
mtp_has_pending_draft and related sidecar state inconsistent when no draft
output is requested. Update skippy_mtp_propose_next so the pending-draft/session
state is recorded whenever a draft is actually produced, and avoid mutating the
MTP sidecar or leaving stale pending state when out_mtp_draft is null; make the
same adjustment in the duplicate location referenced by the patch.

330-330: 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Pass a DSA top_k into MTP attention before enabling GLM-DSA MTP.

Line 330 now asserts top_k is non-null, but the MTP graph’s MLA attention call still passes nullptr, and GLM-DSA aliases this graph. A native MTP draft on GLM-DSA can abort the process; compute/share the indexer top_k for MTP or gate GLM-DSA MTP off until it exists.

Also applies to: 602-604, 799-800

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0010-Add-MTP-execution-support-and-sampling-cleanup.patch`
at line 330, The MTP MLA attention path now asserts that top_k is available, but
the MTP graph still passes nullptr, so GLM-DSA MTP can abort when this alias is
used. Update the MTP attention setup in the relevant draft/execution path to
compute and pass the DSA indexer top_k instead of nullptr, or disable the
GLM-DSA MTP path until that value exists. Make the same fix wherever this
MTP/GLM-DSA alias is wired so the GGML_ASSERT(top_k != nullptr) condition is
always satisfied.

1735-1738: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reject token-only batch decode on non-output runtime slices.

This API has no activation output, but a filtered first slice with include_output=false passes the current checks and then samples from logits that the graph does not produce. Add an unsupported check for filtered non-output stages before decoding.

Proposed guard
     if (skippy_is_filtered(first) && first->stage_model->config.layer_start > 0) {
         skippy_set_error(out_error, SKIPPY_STATUS_UNSUPPORTED, "batched token decode requires the first runtime slice or a full model");
         return SKIPPY_STATUS_UNSUPPORTED;
     }
+    if (skippy_is_filtered(first) && !first->stage_model->config.include_output) {
+        skippy_set_error(out_error, SKIPPY_STATUS_UNSUPPORTED, "batched token decode requires an output stage");
+        return SKIPPY_STATUS_UNSUPPORTED;
+    }

Also applies to: 1771-1778

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0010-Add-MTP-execution-support-and-sampling-cleanup.patch`
around lines 1735 - 1738, Add an unsupported guard in the batched token decode
path so a filtered first stage with include_output=false is rejected before
sampling. In the decode/sampling logic around the existing
skippy_is_filtered(first) check, ensure first->stage_model->config.layer_start >
0 or another non-output runtime slice is treated as unsupported and returns
SKIPPY_STATUS_UNSUPPORTED via skippy_set_error. Apply the same fix in both
affected decode entry points referenced by the diff so token-only batch decode
never proceeds when the graph does not produce logits.

1771-1778: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Sync or clear MTP state after batched decodes.

The single-request paths call skippy_mtp_sync_target_tokens(), but these batch loops advance n_past and token history without updating mtp_pending_h or sidecar KV. Subsequent MTP drafts can be based on stale hidden state; add a batch-aware sync using the correct row per request, or clear MTP state after each advanced session.

Also applies to: 2014-2034

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0010-Add-MTP-execution-support-and-sampling-cleanup.patch`
around lines 1771 - 1778, The batch decode loop in the skippy session path
advances token history but never refreshes or clears MTP state, unlike the
single-request flow. Update the batched decode handling around skippy_session,
skippy_record_tokens, and skippy_sample_token_ith to either call
skippy_mtp_sync_target_tokens for each request using the matching row/index or
explicitly clear mtp_pending_h and the sidecar KV after advancing n_past. Keep
the fix applied consistently to both affected batch loops so subsequent MTP
drafts use current state.
third_party/llama.cpp/patches/0006-Expand-staged-execution-across-dense-and-recurrent-f.patch (1)

1264-1268: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Don’t silently skip Qwen3VL deepstack injections for mid-stage splits.

When stage_filtered && il_start > 0, this disables deepstack_out for every layer in the slice. If the slice still contains il < n_deepstack_layers, those projector residuals are omitted and staged activations diverge from full-model execution. Reject splits inside the deepstack range or carry the required sideband through the ABI.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0006-Expand-staged-execution-across-dense-and-recurrent-f.patch`
around lines 1264 - 1268, The deepstack injection guard in the staged execution
path is now skipping `deepstack_out` whenever `stage_filtered` is set and
`il_start > 0`, which omits required projector residuals in slices that still
cover early layers. Update the loop in the staged execution logic to preserve
deepstack additions for any `il < n_deepstack_layers`, or explicitly reject
stage splits that start inside the deepstack range; if the split must remain,
pass the needed sideband through the staging ABI so `deepstack_out` stays
consistent with full-model execution.
🧹 Nitpick comments (1)
third_party/llama.cpp/patches/0009-Add-chat-grammar-device-enumeration-and-runtime-even.patch (1)

512-570: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Reuse a scratch buffer during compaction copies.

This allocates a fresh std::vector for every K/V row copy, and transposed V copies do that per element. Reuse one buffer across calls to avoid allocator churn on large compactions.

Proposed refactor
 static void llama_kv_cache_copy_tensor_bytes(
         ggml_tensor * tensor,
         size_t src_offset,
         size_t dst_offset,
-        size_t size) {
+        size_t size,
+        std::vector<uint8_t> & scratch) {
     if (tensor == nullptr || size == 0 || src_offset == dst_offset) {
         return;
     }
 
-    std::vector<uint8_t> bytes(size);
-    ggml_backend_tensor_get(tensor, bytes.data(), src_offset, size);
-    ggml_backend_tensor_set(tensor, bytes.data(), dst_offset, size);
+    scratch.resize(size);
+    ggml_backend_tensor_get(tensor, scratch.data(), src_offset, size);
+    ggml_backend_tensor_set(tensor, scratch.data(), dst_offset, size);
 }
 
 void llama_kv_cache::copy_compacted_cells(const std::vector<compaction_move> & moves) const {
+    std::vector<uint8_t> scratch;
     for (const auto & layer : layers) {
         for (const compaction_move & move : moves) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@third_party/llama.cpp/patches/0009-Add-chat-grammar-device-enumeration-and-runtime-even.patch`
around lines 512 - 570, The compaction copy path in
llama_kv_cache::copy_compacted_cells allocates a new std::vector<uint8_t> for
every tensor slice copy, and the transposed V branch does this per element.
Refactor llama_kv_cache_copy_tensor_bytes to take a reusable scratch buffer (or
otherwise reuse one buffer across the loop in copy_compacted_cells) so K/V row
copies and the v_trans path avoid repeated allocator churn. Keep the fix
localized to llama_kv_cache_copy_tensor_bytes and
llama_kv_cache::copy_compacted_cells.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In
`@third_party/llama.cpp/patches/0003-Add-staged-sampling-checkpoints-and-part-loading.patch`:
- Around line 279-295: The staged part-loading logic in the tensor-loading loop
is storing `llama_tensor_weight` entries that depend on `ctx_gguf.get()` and
`cur`, but `ctx_gguf` is currently a per-iteration local in the GGUF part
handling code. Update the part-context management so the GGUF contexts created
in this block stay alive for as long as `weights_map` may use them, using the
existing `contexts`/`weights_map` flow around `gguf_init_from_file`,
`weights_map.emplace`, and `llama_tensor_weight` to retain ordered per-part
metadata instead of letting it be destroyed at the end of the loop iteration.

In
`@third_party/llama.cpp/patches/0004-Add-lanes-external-media-and-chat-grammar-support.patch`:
- Around line 1506-1527: Update skippy_abi_features() so it advertises every
implemented capability currently supported by the ABI, including
SKIPPY_FEATURE_CHAT_TEMPLATE_TOOLS and SKIPPY_FEATURE_CHAT_SAMPLING_GRAMMAR, by
adding their bits to the returned feature mask alongside the existing
SKIPPY_FEATURE_GENERATION_SIGNALS and SKIPPY_FEATURE_EXTERNAL_MEDIA_PREFILL
entries. Keep the feature list in sync with the actual APIs exposed in this
patch so clients probing skippy_abi_features() see the new chat-tool and grammar
support as available.
- Around line 1751-1757: In skippy_session_configure_chat_sampling(), the
current catch blocks for std::exception and ... clear sampling and return
success, which hides malformed metadata; change this flow so JSON parsing or
grammar setup failures propagate as an error instead of silently succeeding. Use
the existing out_error and skippy_success/skippy_clear_chat_sampling handling in
this function to return a failure status with a clear invalid chat sampling
metadata message, while keeping the success path unchanged for valid metadata.
- Around line 1538-1569: `stage_model->ctx` and `lane_in_use` are shared across
sessions without synchronization, so lane allocation/freeing and shared-context
operations can race. Add a mutex/lock on the stage model and guard all lane
ownership changes plus every decode/reset/trim path that touches the shared
`llama_context`. Update the affected session methods around
`skippy_finish_model_open` and the later lane-management code referenced by the
review so they acquire the same lock before reading or mutating `lane_in_use` or
calling into `stage_model->ctx`.

In
`@third_party/llama.cpp/patches/0005-Add-resident-prefix-cache-and-session-refinements.patch`:
- Around line 1268-1274: Import path needs a pre-write synchronization before
mutating native KV tensors in the session import flow. In the function that
calls skippy_get_kv_cache and then kv->stage_import_kv_page, insert a sync/drain
of pending backend work immediately after the cache lookup succeeds and before
any KV page import or tensor writes happen. Use the same synchronization
mechanism already used by the export/read path so the import path is consistent
and safe.
- Around line 884-891: Update skippy_update_session_state_after_import so it
assigns session->n_past from imported_cells instead of taking the max with the
existing value; the restore path should reflect the imported state length
exactly, with the same clamping to int32_t bounds already present. Keep the fix
localized to this helper and ensure any callers relying on restored session
position now advance from the imported length rather than the prior session
state.
- Around line 520-526: The import path in seq_rm/find_slot is mutating the KV
cache too early: existing cells are removed before allocation is confirmed.
Update the logic around seq_rm, find_slot, and apply_ubatch so capacity is
checked first or the operation is rollback-safe, and only call seq_rm after a
slot has been successfully reserved to avoid losing the original page range on
failure.

In
`@third_party/llama.cpp/patches/0006-Expand-staged-execution-across-dense-and-recurrent-f.patch`:
- Around line 1264-1268: The deepstack injection guard in the staged execution
path is now skipping `deepstack_out` whenever `stage_filtered` is set and
`il_start > 0`, which omits required projector residuals in slices that still
cover early layers. Update the loop in the staged execution logic to preserve
deepstack additions for any `il < n_deepstack_layers`, or explicitly reject
stage splits that start inside the deepstack range; if the split must remain,
pass the needed sideband through the staging ABI so `deepstack_out` stays
consistent with full-model execution.

In
`@third_party/llama.cpp/patches/0008-Add-external-decode-media-prefill-and-newer-family-s.patch`:
- Around line 946-960: The position handling in the external prefill path
silently falls back to n_past when positions is non-null but position_count is
invalid. Update the logic around the pos_storage fill branch to reject malformed
caller-supplied sidebands instead of entering the fallback path, and surface an
error from this decode/prefill function so external media/M-RoPE prefill cannot
proceed with incorrect positions.
- Around line 393-405: The selected-layer export path in
llama_kv_cache::stage_export_kv_page still enters the transposed-V handling even
when a layer has no V stream, which can dereference layer->v_stream[strm]. Add
an early skip or equivalent guard in the v_trans branch alongside the existing
v_row_bytes check so layers with no V stream are ignored in both transposed and
non-transposed paths, and make sure the guard applies to the loops that build V
payloads for selected layers.
- Around line 28-95: The staged-runtime ABI needs to be bumped to account for
the new public exports added here. Update the SKIPPY_ABI_VERSION constant(s)
alongside the new skippy_session_begin_external_decode,
skippy_session_end_external_decode, skippy_prefill_chunk_frame_sampled,
skippy_prefill_chunk_frame_with_positions, and
skippy_prefill_chunk_frame_sampled_with_positions declarations, and make sure
the Rust FFI-side ABI constants remain in sync with the C header.

In
`@third_party/llama.cpp/patches/0010-Add-MTP-execution-support-and-sampling-cleanup.patch`:
- Around line 1953-1955: In the AltUp batch-input guard inside the Skippy
execution path, the early return from the branch that checks
SKIPPY_ACTIVATION_FLAG_GEMMA3N_ALTUP should also populate out_error before
returning SKIPPY_STATUS_UNSUPPORTED. Update the same conditional block near the
n_embd_inp/n_embd handling so it matches the surrounding unsupported-case
behavior by setting a clear error message first, then returning.
- Around line 1446-1454: The MTP draft bookkeeping in skippy_mtp_propose_next is
updating session state only inside the out_mtp_draft branch, which leaves
mtp_has_pending_draft and related sidecar state inconsistent when no draft
output is requested. Update skippy_mtp_propose_next so the pending-draft/session
state is recorded whenever a draft is actually produced, and avoid mutating the
MTP sidecar or leaving stale pending state when out_mtp_draft is null; make the
same adjustment in the duplicate location referenced by the patch.
- Line 330: The MTP MLA attention path now asserts that top_k is available, but
the MTP graph still passes nullptr, so GLM-DSA MTP can abort when this alias is
used. Update the MTP attention setup in the relevant draft/execution path to
compute and pass the DSA indexer top_k instead of nullptr, or disable the
GLM-DSA MTP path until that value exists. Make the same fix wherever this
MTP/GLM-DSA alias is wired so the GGML_ASSERT(top_k != nullptr) condition is
always satisfied.
- Around line 1735-1738: Add an unsupported guard in the batched token decode
path so a filtered first stage with include_output=false is rejected before
sampling. In the decode/sampling logic around the existing
skippy_is_filtered(first) check, ensure first->stage_model->config.layer_start >
0 or another non-output runtime slice is treated as unsupported and returns
SKIPPY_STATUS_UNSUPPORTED via skippy_set_error. Apply the same fix in both
affected decode entry points referenced by the diff so token-only batch decode
never proceeds when the graph does not produce logits.
- Around line 1771-1778: The batch decode loop in the skippy session path
advances token history but never refreshes or clears MTP state, unlike the
single-request flow. Update the batched decode handling around skippy_session,
skippy_record_tokens, and skippy_sample_token_ith to either call
skippy_mtp_sync_target_tokens for each request using the matching row/index or
explicitly clear mtp_pending_h and the sidecar KV after advancing n_past. Keep
the fix applied consistently to both affected batch loops so subsequent MTP
drafts use current state.

---

Nitpick comments:
In
`@third_party/llama.cpp/patches/0009-Add-chat-grammar-device-enumeration-and-runtime-even.patch`:
- Around line 512-570: The compaction copy path in
llama_kv_cache::copy_compacted_cells allocates a new std::vector<uint8_t> for
every tensor slice copy, and the transposed V branch does this per element.
Refactor llama_kv_cache_copy_tensor_bytes to take a reusable scratch buffer (or
otherwise reuse one buffer across the loop in copy_compacted_cells) so K/V row
copies and the v_trans path avoid repeated allocator churn. Keep the fix
localized to llama_kv_cache_copy_tensor_bytes and
llama_kv_cache::copy_compacted_cells.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a33c6fd5-ea88-45ec-a0ee-a79af157ee36

📥 Commits

Reviewing files that changed from the base of the PR and between 100f548 and 5c20125.

📒 Files selected for processing (11)
  • third_party/llama.cpp/patches/0001-Add-Skippy-ABI-and-package-writer-foundation.patch
  • third_party/llama.cpp/patches/0002-Add-early-staged-model-family-and-chat-support.patch
  • third_party/llama.cpp/patches/0003-Add-staged-sampling-checkpoints-and-part-loading.patch
  • third_party/llama.cpp/patches/0004-Add-lanes-external-media-and-chat-grammar-support.patch
  • third_party/llama.cpp/patches/0005-Add-resident-prefix-cache-and-session-refinements.patch
  • third_party/llama.cpp/patches/0006-Expand-staged-execution-across-dense-and-recurrent-f.patch
  • third_party/llama.cpp/patches/0007-Expand-staged-execution-across-VL-and-broad-model-fa.patch
  • third_party/llama.cpp/patches/0008-Add-external-decode-media-prefill-and-newer-family-s.patch
  • third_party/llama.cpp/patches/0009-Add-chat-grammar-device-enumeration-and-runtime-even.patch
  • third_party/llama.cpp/patches/0010-Add-MTP-execution-support-and-sampling-cleanup.patch
  • third_party/llama.cpp/upstream.txt


- name: SDK and API crate tests
if: ${{ matrix.group == 'sdk-api' && (needs.changes.outputs.all_rust == 'true' || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-client') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-api-client') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-api-server') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-config') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-commands') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-events') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-hardware-profile') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-runtime-install') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-sdk') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-cli') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-tui') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-embedded-runtime') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-console-server') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-ffi') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-nodejs')) }}
if: ${{ matrix.group == 'sdk-api' && (needs.changes.outputs.all_rust == 'true' || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-client') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-api-client') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-api-server') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-config') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-commands') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-events') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-hardware-profile') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-runtime-install') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-native-runtime') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-routing') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-types') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-sdk') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-cli') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-tui') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-embedded-runtime') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-console-server') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-ffi') || contains(fromJson(needs.changes.outputs.affected_crates || '[]'), 'mesh-llm-nodejs')) }}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like this change is getting included multiple time across PRs

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure why tho?

@i386
i386 merged commit 922f127 into main Jun 30, 2026
32 checks passed
@i386
i386 deleted the jd/update-llama-pin branch June 30, 2026 03:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants