Skip to content

Bump llama.cpp to 61881b1f7 (b10280), release v0.8.42 - #82

Merged
nyo16 merged 1 commit into
masterfrom
bump-llama-cpp-b10280
Aug 6, 2026
Merged

Bump llama.cpp to 61881b1f7 (b10280), release v0.8.42#82
nyo16 merged 1 commit into
masterfrom
bump-llama-cpp-b10280

Conversation

@nyo16

@nyo16 nyo16 commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Submodule ddd4ec14261881b1f7 (63 commits, b10217 → b10280). Unlike the last two ranges, this one breaks the upstream C API. Validating it against real models also surfaced
a separate bug that had left MTP speculative decoding inoperable since v0.8.41.

Required API fix

llama_sampler_init_penalties gained a leading int32_t n_vocab (#26520, which moved n_vocab out of llama_sampler_data and into
the penalty sampler so the CUDA backend sampler from #25262 can size its buffers). The NIF's one call site now passes llama_vocab_n_tokens(model->vocab()).

The argument count changed, so a missed call site is a compile error — but the two leading int32_ts are interchangeable to the compiler, so the penalties path is now covered
behaviourally: a repetition-inducing prompt under greedy decoding must be diverted by penalty_repeat, and penalty_repeat: 1.0 must be a byte-identical no-op. Previously only
sampler construction was tested.

Also in range, needing no change here: llama_sampler_init_dry dropped n_ctx_train (the binding doesn't expose DRY), and -1 no longer means "context size" for penalty_last_n
(#26524) — the NIF hardcodes 64, so behaviour is unchanged.

Fixed: MTP was completely broken since v0.8.41

b10217 added bool load_mtp to llama_model_params, defaulting to false, so non-speculative callers don't pay for the MTP head's tensors. v0.8.41's changelog read the field as
inert "because the NIF builds its params from llama_model_default_params() … no change needed" — true at compile time, wrong at runtime: the MTP layers simply stopped being read off
disk.

Nothing on the way in objects. Both contexts build, common_speculative_init returns ok, and the first draft then fails with verify decode failed: code=-1, far from the cause.

  • Model.load/2 gains :load_mtp (default false, matching upstream)
  • the flag is recorded on %Model{}
  • MTP.init/2 refuses a model loaded without it, naming the remedy — the layers cannot be attached after the fact, so init/2 is the only place the error is actionable

This went unnoticed because the :mtp suite had never been run against a real model — it needs an MTP-enabled GGUF and none was available when the tests were written. Now verified
end-to-end against Qwen3.5-9B (qwen35 with nextn_predict_layers) at ~67% draft acceptance with n_draft: 3. The new guard immediately caught a second call site in
llama_cpp_ex_test.exs that was missing the flag.

Tests: three smoke tests asserted something hybrid models cannot do

The cache-scope isolation test, the PromptCache.restore/4 partial-trim test, and — ironically — the hybrid cache_prompt regression test all required prefix_cache_tokens > 0.

Recurrent state isn't preserved per token, so llama_memory_seq_rm refuses a partial range and the Server correctly declines reuse (server.ex:812). The test named for hybrid GDN
models was therefore contradicting the very fallback its own comment documents. All three passed only because CI's generation model is dense.

Each now branches on TestModels.seq_rm_kind/1 (a memoised common_context_can_seq_rm probe) and asserts the documented contract for the model in hand: reuse on :part; the full-reset
fallback and {:error, :seq_rm_refused} on :full. No branch is a skip, and neither case has a catch-all, so an unexpected verdict raises rather than passing quietly.

Not a regression from this bumpllama-memory-recurrent.cpp and both hybrid memory implementations are byte-identical between b10217 and b10280; only the dense llama-kv-cache.cpp
was refactored.

⚠️ Known issue (pre-existing, not from this bump)

Cancelling an MTP stream and immediately reusing the session aborts the VM. request_cancel sets a flag and the loop then stops without emitting a terminal event
(llama_nif.cpp:2009), so Generator.stop/1 has no completion signal to await and returns while the dirty scheduler may still be inside llama_decode. Harmless for stream/3 (each
call owns a context that dies with it); not for an %MTP{} session, whose two long-lived contexts every call shares.

Reachable only now that MTP works at all. The fix means acknowledging cancellation in the NIF and in the cancellation protocol non-MTP streaming shares — deliberately not bundled into
a submodule bump. MTP's docs and the README carry the warning, and the reproduction lives in LlamaCppEx.MTPCancelTest, in its own module tagged only :mtp_cancel (not :mtp, since
--include beats --exclude and a second gate tag would abort --include mtp runs).

Validation

Built from source against the bumped submodule (Metal, LLAMA_BACKEND=auto):

Run Result
default suite (no model) 374 passed
--include smoke --include embeddings --include slow, Llama-3.2-3B (:part) 502 passed
--include smoke --include embeddings --include slow, Qwen3.5-0.8B (:full) 502 passed
--include mtp, Qwen3.5-9B 384 passed

Generation, chat templating, streaming, JSON-schema-constrained sampling, grammar-with-penalties and the penalties path were additionally exercised across nine architectures — llama,
qwen3, qwen35, qwen35moe, gemma4, gpt-oss, bert (Ministral) — plus Qwen3-Embedding-0.6B for the embedding paths (L2 norm exactly 1.0; paraphrase similarity 0.73 vs 0.20 for
unrelated text).

mix format --check-formatted and mix dialyzer (0 errors) both clean. Makefile's LLAMA_COMMIT follows the submodule — verified identical, so Hex source builds don't drift from git
checkouts. checksum.exs deliberately untouched; CI regenerates it after the release tag.

Submodule ddd4ec142 -> 61881b1f7 (63 commits, tag b10217 -> b10280). Unlike the
last two ranges this one breaks the upstream C API.

Required API fix:

- `llama_sampler_init_penalties` gained a leading `int32_t n_vocab` (#26520, which
  moved n_vocab out of llama_sampler_data so the CUDA backend sampler from #25262
  can size its buffers). The one call site now passes
  `llama_vocab_n_tokens(model->vocab())`. `llama_sampler_init_dry` also dropped
  `n_ctx_train`, which the binding does not expose. `-1` no longer means "context
  size" for penalty_last_n (#26524); the NIF hardcodes 64, so behaviour is
  unchanged.

Fixed: MTP speculative decoding was entirely broken since v0.8.41.

b10217 added `bool load_mtp` to llama_model_params, defaulting to false. v0.8.41
read that field as inert because the NIF builds its params from
`llama_model_default_params()` — true at compile time, wrong at runtime: the MTP
layers stopped being loaded. `common_speculative_init` still succeeds, so the
failure surfaced as `verify decode failed: code=-1` far from the cause.
`Model.load/2` gains `:load_mtp` (default false, matching upstream), it is
recorded on `%Model{}`, and `MTP.init/2` refuses a model loaded without it since
the layers cannot be attached afterwards. Unnoticed because the :mtp suite had
never run against a real model; now verified end-to-end at ~67% draft acceptance.

Tests: three smoke tests asserted prefix reuse that hybrid GDN models cannot do.

Recurrent state cannot be rolled back to an arbitrary position, so a partial
seq_rm is refused and the Server correctly declines reuse — meaning the
hybrid-model regression test contradicted the fallback it documents. They passed
only because CI's generation model is dense. Each now branches on
`TestModels.seq_rm_kind/1` and asserts the documented behaviour for the model in
hand, with no catch-all clause so an unexpected verdict raises. Not a regression
from this bump: the recurrent and hybrid memory implementations are byte-identical
across the range.

The MTP suite also moved to its own module so the model loads once — a per-test
load accumulated Metal buffers until ggml-alloc aborted the VM.

Known issue: cancelling an MTP stream and immediately reusing the session aborts
the VM (cancellation is unacknowledged, and a session's contexts are shared).
Pre-existing, reachable only now that MTP works; documented, and the reproduction
is gated behind its own :mtp_cancel tag. Fixing it changes the cancellation
protocol non-MTP streaming shares, so it is not bundled here.

Validation: 374 passed (default), 502 passed with smoke+embeddings+slow against
both a :part model (Llama-3.2-3B) and a :full one (Qwen3.5-0.8B), 384 passed with
--include mtp against Qwen3.5-9B. Generation, chat templating, streaming,
JSON-schema sampling, grammar-with-penalties and the penalties path itself checked
across nine architectures. mix format and dialyzer clean.
@nyo16
nyo16 merged commit 5ef3da6 into master Aug 6, 2026
6 checks passed
@nyo16
nyo16 deleted the bump-llama-cpp-b10280 branch August 6, 2026 02:38
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.

1 participant