Skip to content

Pass target context as ctx_other so Gemma4 E4B MTP can init - #92

Merged
nyo16 merged 1 commit into
nyo16:masterfrom
dai-yamashita:master
Sep 14, 2026
Merged

nyo16 merged 1 commit into
nyo16:masterfrom
dai-yamashita:master

Conversation

@dai-yamashita

Copy link
Copy Markdown
Contributor

Fixes #91.

Why

MTP.init/2 already accepts a sidecar :draft_model (#86). That is enough for Qwen 3.8. It is not enough for Gemma4 E4B.

gemma4-assistant requires llama_context_params.ctx_other at draft context construction — shared KV and the target's token embeddings. llama.cpp throws if the pointer is missing:

Gemma4Assistant requires ctx_other to be set (this warning is normal during memory fitting)

Context.create/2 never exposed the field. The NIF starts from llama_context_default_params(), so params.ctx_other stayed nullptr. Qwen's constructor leaves cparams.ctx_other as nullptr, which is why the existing sidecar path stayed green.

Creating a draft context on the assistant GGUF without a target still fails. That is llama.cpp's contract.

What changed

Context.create/2 takes optional :ctx_other. The NIF copies the raw llama_context* into params.ctx_other and Fine-keeps the peer so the target cannot be collected while the draft still holds the pointer.

MTP.init/2 always passes the target as the draft's :ctx_other. Callers do not set it. Qwen ignores it; Gemma4 needs it.

{:ok, target} = LlamaCppEx.load_model("gemma-4-E4B-it-Q4_K_M.gguf",
                                      n_gpu_layers: -1, load_mtp: true)
{:ok, head}   = LlamaCppEx.load_model("mtp-gemma-4-E4B-it-Q8_0.gguf",
                                      n_gpu_layers: -1, load_mtp: true)

{:ok, mtp} = LlamaCppEx.MTP.init(target, draft_model: head, n_draft: 3)

Same :draft_model API as Qwen 3.8. The only new requirement is the peer pointer on the draft.

:mtp_sidecar gained a second fixture pair behind LLAMA_SMOKE_MTP_E4B_MODEL / LLAMA_SMOKE_MTP_E4B_DRAFT_MODEL. Unset vars skip that module, so --include mtp_sidecar with only the Qwen pair stays green.

Model.n_embd_out/1 docs now note that gemma4-assistant has a narrow n_embd and a wide n_embd_out. The existing hidden-width guard still compares n_embd_out.

No @version bump. The fork mix.exs is still 0.8.43.

Measured

Unsloth gemma-4-E4B-it-Q4_K_M.gguf (4_971_171_584 B) + mtp-gemma-4-E4B-it-Q8_0.gguf (98_653_248 B), M3 Ultra, Metal, 2026-09-12:

  • Hex 0.8.48: MTP.init/2 fails with the ctx_other throw above.
  • This commit: MTP.init/2 returns a session; generate is non-empty; timing_us.ckpt == 0 (E4B is dense, not hybrid).
  • Isolated Context.create(head) without a target still fails. Expected.

I did not re-run the Qwen 3.8 sidecar matrix. The NIF default remains omitted → nullptr.

Deliberately not changed

Verification

Default suite (no model) plus the new guards:

  • Context.create/2 rejects a non-%Context{} :ctx_other
  • explicit ctx_other: nil is the omit path
  • %Context{} carries the peer resource it was built from

Optional E4B gate (skips when unset):

GGML_METAL_NO_RESIDENCY=1 LLAMA_BACKEND=metal LLAMA_SMOKE_MTP_E4B_MODEL=/path/to/gemma-4-E4B-it-Q4_K_M.gguf LLAMA_SMOKE_MTP_E4B_DRAFT_MODEL=/path/to/mtp-gemma-4-E4B-it-Q8_0.gguf   mix test --include mtp_sidecar

Qwen-only --include mtp_sidecar is unchanged: MTPSidecarTest still requires LLAMA_SMOKE_MTP_MODEL + LLAMA_SMOKE_MTP_DRAFT_MODEL.

Review notes

  • The Fine keep on LlamaContext.ctx_other is load-bearing for Gemma4. Qwen drops the C pointer, but the Elixir peer field is still set so the resource graph is the same for both architectures.
  • I did not bump @version. Happy to add a changelog/release-guide note if you want this in 0.8.49.

gemma4-assistant requires llama_context_params.ctx_other at draft
construction; Qwen still works when the option is omitted (nullptr).
@nyo16

nyo16 commented Sep 14, 2026

Copy link
Copy Markdown
Owner

Thanks @dai-yamashita — this is the right fix, and it matches upstream exactly: common_speculative_init_result sets cparams.ctx_other = ctx_tgt for every draft context, and llama-context.cpp only reads
it for GEMMA4_ASSISTANT / EAGLE3 / DFLASH, so the Qwen in-file and sidecar paths are untouched.

@nyo16
nyo16 merged commit 7351812 into nyo16:master Sep 14, 2026
9 checks passed
nyo16 added a commit that referenced this pull request Sep 14, 2026
… section

v0.8.49 was tagged before #92 merged, so its entries landed under a shipped
heading. Moved to a new Unreleased section, with the #91 root cause and the
Gemma 4 E4B verification at b10944.
nyo16 added a commit that referenced this pull request Sep 14, 2026
CHANGELOG Unreleased -> v0.8.50; mix.exs @Version 0.8.49 -> 0.8.50.
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.

Gemma4 E4B MTP sidecar fails: Context.create leaves ctx_other nullptr

2 participants