Commit Graph

433 Commits

Author SHA1 Message Date
Joel Farthing 8ba790e8ad
state: fix V cache compatibility check for models with no V cache (#2212)
read_kv_cache_data gated the restore on kv_self.v_trans != (v_state == 1).
v_state == 2 records that the writer had no V cache, while v_trans tracks
flash attention rather than V allocation, so with -fa 0 on a K-only or MLA
cache the two disagree and the restore is refused.

Compare V cache presence in both directions, and transposition only when a
V cache exists on both sides. The write path and serialized layout are
unchanged.

Co-authored-by: Joel Farthing <262452229+joelfarthing@users.noreply.github.com>
2026-08-01 09:17:04 +03:00
Kawrakow b341d0b58b
Fix #2215 (#2217)
* Fix #2215

* Don't make a copy when you don't need to
2026-07-31 07:20:52 +03:00
Nexesenex 52836917a1
DeepSeek V4 - Checkpoints support (#2195)
* DSV4 checkpoints WIP

* DSV4 checkpoints: fix per-sequence state save/restore writing all streams

Critical bug: llama_state_seq_get_data() and llama_state_seq_set_data()
were serializing/deserializing ALL DSV4 compressed cache streams instead
of only the stream for the target sequence. This corrupted other active
sequences' compressed indexer state during checkpoint restore.

Fix:
- Add dsv4_stream_offset_size() helper to compute per-stream byte
  offset and size for any DSV4 cache tensor (CSA K, LID K, HCA K,
  and all state tensors)
- write_kv_cache_data: emit dsv4_single_stream flag + stream_idx
  so per-sequence saves only write that stream's portion
- write_dsv4_cache: accept stream_idx parameter (-1 = full tensor,
  >= 0 = single stream at computed offset)
- read_kv_cache_data: read the new format, validate consistency
  (per-stream data needs dest seq_id, full data needs seq_id=-1),
  and restore only the destination stream's bytes into the correct
  tensor offset

Format change (WIP, backward compat not required):
  [has_dsv4_cache] [n_layer] [single_stream] [stream_idx] [n_stream]
  [per-layer: layer_type + stream tensor data]

* DSV4 checkpoints: fix checkpoint search using pos_max instead of pos_min

Bug: checkpoint search condition cur.pos_min < n_past || cur.pos_min == 0
always matched all DSV4 checkpoints because they all have pos_min=0 (the KV
cache starts at 0 and never evicts). The reverse-iterator always picked the
NEWEST checkpoint regardless of how far past the intended rewind/divergence
point it extended, causing n_past to be set to the checkpoint's pos_max
(e.g. 9500) instead of the rewind point (e.g. 5000). This made the system
skip reprocessing tokens between the rewind point and the checkpoint's
pos_max.

Fix in both batch_pending_prompt and apply_checkpoint:
- Condition changed to cur.pos_max < n_past — only checkpoints that end
  BEFORE the divergence/rewind point are eligible
- Post-restore n_past uses it->pos_max directly instead of the incorrect
  max(pos_min + 1, pos_max) which always returned pos_max for DSV4 anyway

Now when rewinding to position 5000 with checkpoints at pos_max=8000+:
no checkpoint matches, falls through to full reprocessing (correct).
When rewinding to position 8000 with a checkpoint at pos_max=7500: only
500 tokens need reprocessing (optimal).

* DSV4 checkpoints: throttle creation via interval gating on all paths

When ctx_checkpoints_interval > 0, DSV4 checkpoints (~145 MiB) were still
created at every transition point (PP done, TG start, release) because
direct create_checkpoint() calls bypassed the interval gate.

Fix:
- Modified create_checkpoint_at_interval() to handle interval <= 0 as
  'always create' (preserving recurrent model behavior for small state)
- Replaced all 4 external direct create_checkpoint() calls with
  create_checkpoint_at_interval() so the interval gate is respected
- Unified the prompt-loading branch that had split
  create_checkpoint / at_interval calls

Now with --ctx-checkpoints-interval N, all checkpoint creation is
throttled to at most 1 per N positions regardless of the transition
phase.

* DSV4 checkpoints: clarify divergence log message for models with state checkpoints

The message 'does not support partial KV reuse' was misleading for DSV4,
which now supports checkpoint-based state restoration. Split the fallback
message: models with state checkpoints (DSV4, recurrent, hybrid) now print
'no checkpoint before divergence point' instead of 'does not support
partial KV reuse', explaining that the restore failed due to missing
checkpoints at the right position, not due to lack of support.

* DSV4 checkpoints: don't erase the just-restored checkpoint

The erasure condition pos_max > pos_min_thold was equivalent to
pos_max >= pos_next, erasing any checkpoint whose data touched or
went past the current write position. The checkpoint just restored
from (pos_max == pos_next) was immediately erased, wasting a ~145 MiB
checkpoint that was perfectly valid.

Fix:
- Changed erasure condition to pos_max > pos_next (strictly greater
  than the next write position). Checkpoints at exactly the current
  position (pos_max == pos_next, e.g. the one we just restored from)
  are kept.
- Preserved cache-aligned pos_next through the restore block so the
  erasure compares against cache positions, not prompt positions
  (pre-existing bug where the prompt-tokens call at line 3677
  overwrote pos_next with a prompt position).

* DSV4 checkpoints: update interval gate position after restore

After a checkpoint restore, slot.checkpoint_pos was still 0 (from
slot.release()), so the interval gate in create_checkpoint_at_interval
always passed (0 + 2048 <= pos + 1), creating a new ~145 MiB checkpoint
immediately after every restore — even just 5 tokens past the restored
checkpoint's position.

Fix: set slot.checkpoint_pos = it->pos_max in both restore paths
(apply_checkpoint generic restore and batch_pending_prompt DSV4
restore). This tells the gate that a checkpoint already exists at the
restored position, and no new one is needed until another interval
(2048 tokens) has elapsed.

* Missing info

* DSV4 checkpoints: document float-reduction-order reproducibility after restore

After a checkpoint restore, the PP batch loop processes remaining tokens
sequentially from n_past_prompt in chunks of n_batch. Because the loop
is stateless with no carry-over from earlier batches, the chunk boundaries
at and after the restore point are identical to a full-reprocess control
arm. This ensures float-reduction-order reproducibility between arms
when performing correctness validation.

Addresses joelfarthing's finding on openPangu, where mismatched chunk
boundaries between restore+reprocess and full-reprocess controls caused
bit-level differences that masked actual restore bugs.

* DSV4 checkpoints: verify position after restore

After both restore paths (apply_checkpoint and DSV4 batch_pending_prompt),
verify that llama_kv_cache_seq_pos_max() matches the checkpoint's pos_max.
A size-matched but misplaced restore can silently corrupt the KV cache;
on mismatch, force a full reset.

The DSV4 path pre-sets restored = true on byte-level success, then the
position check can revert it to false.  Only if (restored) proceeds with
the restored state, matching the apply_checkpoint pattern.

* DSV4 checkpoints: correct misleading comment about chunk-boundary reproducibility

* DSV4 checkpoints: add FNV-1a checksum integrity check for checkpoint data

Sanity check (pos_max): catches misplaced restores (wrong stream offset,
partial overwrite) where the byte count matches but the cache position
doesn't.

Correctness check (FNV-1a hash of serialized data): catches in-memory
corruption of the checkpoint data vector between creation and restore.

Both checks are applied in the standard (apply_checkpoint) and DSV4
(batch_pending_prompt) restore paths. A mismatch in either causes the
restore to be treated as failed, falling back to full reprocess.

File format bumped to CKPT v2 (magic 0x434b5054, version 2) with a
data_hash field per checkpoint. Old-format files (LLAMA_STATE_SEQ_MAGIC)
are still loaded: the hash is computed on load so validation works
uniformly.

* Defer checkpoint hash computation to offload creation path

The FNV-1a hash (~5-15ms per 150 MiB checkpoint) is no longer computed
during checkpoint creation. Instead, data_hash is set to 0 and
hash_computed to false. The hash is computed lazily on first access via
ensure_checkpoint_hash(), called from:
- apply_checkpoint (before the integrity check during restore)
- save_checkpoints_to_file (before writing to disk)

This removes the hash computation from the time-critical checkpoint
creation path, reducing the pause between batches.

* Reuse pre-allocated scratch buffer for checkpoint serialization

Adds a reusable std::vector<uint8_t> scratch buffer to server_context,
eliminating the per-checkpoint zero-init allocation (~150 MiB memset)
from ckpt.data.resize(). The scratch is grown on demand and handed
off to the checkpoint via swap() — a zero-copy move.

Also removes default arguments from server_prompt_checkpoint_update()
since all callers already pass every parameter explicitly.

* Compute checkpoint data hash incrementally during serialization

Instead of a second pass over the serialized buffer (costly for 145 MiB DSV4
checkpoints) or deferring to save/restore time (breaks in-memory verification),
compute the FNV-1a hash as a streaming operation during llama_state_seq_get_data.

llama_data_write_buffer gains an optional fnv_hash pointer and updates it
during write() and write_tensor_data() — the hash is computed from bytes as
they land in the output buffer, with zero extra memory reads.

The server then obtains the hash at creation time by passing &ckpt.data_hash
(pre-initialized to the FNV-1a offset basis) to llama_state_seq_get_data.
This replaces the deferred hash approach (ensure_checkpoint_hash / hash_computed)
and restores in-memory round-trip verification.

* DSV4 checkpoints: add round-trip serialization verification

After restore, re-serialize the KV cache and compare byte-for-byte against
the original checkpoint data.  This directly catches serialization bugs
that produce internally-consistent but wrong values (Joel's 59/64 case:
correct position, corrupted tensor data).

The check is added to both restore paths (standard apply_checkpoint and
DSV4 batch_pending_prompt) and runs after the pos_max sanity check and
FNV-1a hash integrity check.  Cost: one extra llama_state_seq_get_size +
llama_state_seq_get_data + memcmp of the checkpoint data.

* Remove dead _ckpt_max_size member

_ckpt_max_size was set by server_prompt_checkpoint_update but never read.
Removed the member, the function parameter, and the call site.

* Remove no-op resize after swap in server_prompt_checkpoint_update

After swap, ckpt.data holds the scratch buffer which was already
resized to checkpoint_size. Since n == checkpoint_size (asserted),
the resize is a no-op.

* remove dead (void)has_hash cast

The variable is actually used later (for old-format file detection),
so the unused-variable suppression cast is misleading.

* add missing const qualifiers on to_json() methods

Both server_prompt_checkpoint::to_json() and server_prompt::to_json()
were missing const, preventing use on const references.

* remove duplicate n_kept_prompt assignment in server_prompt::from_json()

n_kept_prompt was assigned twice with the same value, clobbering the
slot where n_discarded_prompt should have been read.

* remove redundant params_base parameter from create_checkpoint_at_interval()

The parameter is already accessible as a member of server_context.
All callers were passing this->params_base, so the indirection was
unnecessary.

* factor duplicated restore verification into verify_restored_checkpoint() helper

The 3-step verification (pos_max sanity, FNV-1a hash, and round-trip
memcmp) was duplicated verbatim across apply_checkpoint() and
batch_pending_prompt().  Extract it into a shared static helper with
a label parameter for context-specific log messages.

Also eliminates the pos_next save/restore dance in apply_checkpoint
by using a local variable for the prompt-limit computation, and
removes a stray commented-out debug printf.

* fix two comments: fnv1a_hash comment was misleading, erasure comment imprecise

- fnv1a_hash() is used for all checkpoint verification (not just
  backward-compat file loading) — broadened the description.
- 'may contain stale per-position state' → 'its per-position state
  is stale' — the erasure is unconditional when pos_max > pos_next,
  so the staleness is definite, not possible.

* remove FNV-1a hash and file-format bump (perf, Joelfarthing's review feedback)

The streaming FNV-1a hash added 65 ms to checkpoint creation and 107 ms
to restore (75 MiB checkpoints; roughly double at DSV4's 145 MiB).  The
pos_max sanity check alone is sufficient for catching the real failure
modes (wrong stream offset, partial overwrite), and the initial byte-
count check from llama_state_seq_set_data catches outright corruption.

Removed:
- Streaming hash from llama_data_write_buffer (fnv_hash, fnv_update)
- hash_out parameter from llama_state_seq_get_data / llama.h API
- data_hash field from server_prompt_checkpoint struct
- FNV-1a computation during checkpoint creation and verification
- CKPT v2 file format (revert to LLAMA_STATE_SEQ_MAGIC/version)
- fnv1a_hash() helper function

Kept:
- pos_max sanity check in verify_restored_checkpoint (cheap, catches
  misplaced restores)
- Scratch buffer reuse via swap() in server_prompt_checkpoint_update
  (pure perf win, independent of hash)

* fix: restore off-by-one in n_past calculation after checkpoint restore

size_up_to_pos(pos_max) returns the number of cached tokens at positions
STRICTLY LESS THAN pos_max (non-mtmd: min(pos_max, size)).  Since the
checkpoint encodes state for positions [pos_min, pos_max], the next
position to process is pos_max + 1, not pos_max.

This matters for DSV4 whose accumulator state is not position-indexed:
reprocessing the token at pos_max would double-count it in the compressed
indexer.  For recurrent models the old pos_min+1 workaround happened to
give the right answer (since pos_min == pos_max there), but using
pos_max + 1 is correct for both.

Fixes both restore paths (apply_checkpoint and DSV4 in batch_pending_prompt).

* fix: only write/read DSV4 cache section for DSV4 models

The has_dsv4_cache uint32 was emitted unconditionally, changing the
serialized state layout for every model architecture without bumping
LLAMA_STATE_SEQ_VERSION.  Old state/session files (which end before
this field) would fail with 'unexpectedly reached end of file' when
read by the new code.

Fix: guard the entire DSV4 section on both write and read sides with
ctx->model.arch == LLM_ARCH_DEEPSEEK4.  Non-DSV4 models see the
identical layout they always had.

* fix: validate stream_idx < n_stream in dsv4_stream_offset_size

stream_idx was only checked >= 0 via GGML_ASSERT, but never checked
against n_stream.  An invalid seq_id could compute an out-of-range
tensor offset or size, leading to memory corruption.

Now asserts 0 <= stream_idx < n_stream.

* fix: scratch buffer reuse — copy instead of swap

swap(scratch) moved the written data into ckpt.data but left scratch
empty.  The next call's resize would then re-allocate from scratch,
defeating the purpose.

Now copies the data (ckpt.data = scratch) so scratch retains its size
and capacity across calls.  resize becomes an in-place extension when
needed rather than a fresh allocation.

* fix: restore interval<=0 = disable semantics, split unconditional paths

The interval gate was inverted: interval <= 0 opened the gate, so every
call to create_checkpoint_at_interval created a checkpoint (PP, TG,
release, speculative).  This changed the documented behavior ('<=0
disable' per --help) and created extra checkpoints on every decoded
token for recurrent models.

Fix:
- create_checkpoint_at_interval returns immediately when interval <= 0
  (restoring the no-op semantics from the original code)
- Unconditional paths (release, PP end, PP start with slot.do_checkpoint)
  call create_checkpoint(slot) directly, matching the original layout
- Interval-gated paths (TG tokens, PP start without slot.do_checkpoint,
  speculative decoding) stay behind create_checkpoint_at_interval

* fix: gate ALL checkpoint creation by interval, not just TG paths

Three call sites bypassed the interval gate by calling create_checkpoint(slot)
directly instead of create_checkpoint_at_interval(slot):

  - PP batch-boundary (was creating mid-PP checkpoints at unpredictable positions)
  - PP end (created a checkpoint at every end-of-prompt, even if within the interval)
  - release (created a checkpoint at every release, even if just 5 tokens later)

This caused checkpoints 5 and 6 in the log to be created only 5 tokens apart
(pos_max=8488 and pos_max=8493), and checkpoint 7 at release 455 tokens later,
all with interval=2048.

The original code had all checkpoint creation gated by interval (single
create_checkpoint_at_interval function called everywhere). The 'unconditional'
paths were introduced by our earlier fix that split create_checkpoint_at_interval
into a no-op for interval<=0 — but the split was too aggressive, making release,
PP-end, and PP-batch-boundary always fire.

Fix: route all checkpoint creation through create_checkpoint_at_interval, which
already handles do_checkpoint (early return) and interval <= 0 (no-op) correctly.
create_checkpoint is now an internal helper called only from
create_checkpoint_at_interval.

Result: with interval=2048 and an 8494-token prompt, checkpoints are created at
2048, 4096, 6144, 8192 only — the original semantics.

* fix: off-by-one in checkpoint gate condition, use -1 sentinel

The gate condition 'checkpoint_pos + interval <= 1 + pos' opened one
position early for non-first intervals.  With checkpoint_pos=6143,
interval=2048: 6143+2048=8191, and pos=8190 gives 8191 <= 1+8190=8191
→ TRUE, creating a checkpoint at pos_max=8190 instead of 8191.

Root cause: checkpoint_pos=0 served dual duty ('no checkpoint yet' and
'checkpoint at position 0').  The '1 +' in the condition compensated
for this at startup but overcompensated later.

Fix:
- Change checkpoint_pos from size_t to llama_pos, initialized to -1
- Drop the '1 +' — condition is now checkpoint_pos + interval <= pos

With checkpoint_pos=-1: -1+2048=2047 <= 2047 → first checkpoint after
2048 tokens (correct).
With checkpoint_pos=6143: 6143+2048=8191 <= 8190 → FALSE (no early
open), 8191 <= 8191 → TRUE (opens at correct position).

Also fixes the mixed signed/unsigned comparison that existed with
size_t checkpoint_pos vs llama_pos pos.

* perf: serialize directly into ckpt.data, drop scratch buffer

The ckpt.data = scratch copy added ~10ms to checkpoint creation
(memcpy of 145 MiB).  The scratch buffer was originally introduced to
avoid per-checkpoint resize allocation, but the lazy-zero paging of
modern OSes makes the resize essentially free.

Drop the _ckpt_scratch member entirely.  Serialize directly into
ckpt.data after resize — same allocation cost, no extra copy.

* fix: replace GGML_ASSERT with runtime check in dsv4_stream_offset_size

GGML_ASSERT is compiled out in release builds (NDEBUG). An invalid
non-negative seq_id from the public state API would then compute
out-of-range tensor offsets and sizes, leading to memory corruption.

Replace with a runtime conditional that logs the error and sets
safe fallback values (offset=0, size=0).  The caller that reads/writes
0 bytes will fail downstream in a defined way.

* fix: restore tolerance mechanism, slot.do_checkpoint bypasses interval gate

Samuel reviewed that we removed the slot.do_checkpoint branch from PP
batch-boundary, but batch_pending_prompt still sets slot.do_checkpoint
when the tolerance threshold is reached.  Nowhere checks it, so the
tolerance checkpoint for short prompts (shorter than interval) is dead.

Fix: create_checkpoint_at_interval now checks slot.do_checkpoint — if
true, the interval gate is bypassed.  After a successful creation the
flag is cleared so normal interval gating resumes for subsequent
checkpoints.  Also handles interval <= 0 + slot.do_checkpoint correctly:
the early-return for disabled interval is itself gated by
!slot.do_checkpoint.

* revert: erasure condition back to cur.pos_max > pos_min_thold

The change from pos_min_thold to pos_next affected all models, not just
DSV4. Revert to the original condition (cur.pos_max >= pos_next after
integer simplification) which correctly erases checkpoints at or past
the write position.

* restore unconditional release checkpoint per firecoperana review

Release is a lifecycle boundary.  The interval gate is for throttling
mid-processing checkpoints; the release should always capture the final
state (when do_checkpoint is enabled).

* restore original PP batch-boundary branching per firecoperana review

The explicit slot.do_checkpoint branch in the PP batch-boundary is
restored.  The tolerance bypass is removed from create_checkpoint_at_interval
since it was only ever intended for the PP batch-boundary path (the
original code checked slot.do_checkpoint exclusively there).  This keeps
the tolerance mechanism from leaking into TG, PP-end, and other paths.

* restore original PP-end checkpoint condition per firecoperana review

The original created an unconditional checkpoint at PP end when tolerance
is disabled (<=0).  When tolerance > 0, the tolerance mechanism in the PP
loop handles the end-of-prompt capture at the tolerance point, so no
additional PP-end checkpoint is needed.

* consolidate DSV4 restore path into apply_checkpoint per firecoperana review

The DSV4-specific restore path in batch_pending_prompt duplicated the core
logic of apply_checkpoint (search, restore, verify) with different search
conditions and missing erasure.  Consolidate by:

- Adding is_state_ckpt_model flag to apply_checkpoint
- Bypassing the pos_min >= pos_min_thold guard for state-checkpoint models
  (DSV4 always has pos_min=0 from no eviction, so the guard blocked entry)
- Using pos_next instead of pos_min_thold for the search condition when
  is_state_ckpt_model (allows finding checkpoints at pos_max == n_past - 1)
- Differentiating the reset log message per model type
- Recomputing n_past_offset and n_discarded_prompt after apply_checkpoint
  (previously handled in the DSV4-specific path)

* conditional pos_next formula

State-checkpoint models (DSV4, recurrent) use pos_max + 1 — correct
for DSV4's multi-position checkpoints where pos_min=0, po neviction
max(pos_min+1, pos_max) = pos_max, which undercounts by 1.
For recurrent models pos_min==pos_max so both formulas agree.

Non-state-checkpoint models keep the original
max(pos_min + 1, pos_max) formula unchanged.

* remove redundant n_past_offset / n_discarded_prompt after apply_checkpoint

Both n_past and n_past_prompt are shifted by the same delta from the
restored checkpoint, so the difference (n_past_offset) is unchanged.
n_discarded_prompt is not used in the critical path.

* remove redundant speculative-decoding checkpoint per firecoperana review

speculative_decoding_accept is called from within the TG generation loop
which already creates interval-gated checkpoints at n_decoded > 1 (line
4779).  The inner call would double-create.

* narrow DSV4-specific search and pos_next formula to DSV4 only per firecoperana review

is_state_ckpt_model includes recurrent models (e.g. Qwen 3.6) where
pos_max < pos_next search semantics may not be appropriate.  Only
DSV4 needs pos_max+1 formula and pos_next-based search threshold.

* revert divergence-reset guard to original per firecoperana review

Unnecessary wrapping of the OpenPangu-only divergence path inside
!llama_model_supports_state_checkpoints.  The condition is already
specific enough (!llama_model_supports_partial_kv_reuse is
OpenPangu-only), and OpenPangu does not support state checkpoints,
so the original code was functionally identical.

* narrow guard bypass to DSV4 only per firecoperana review

Recurrent state-checkpoint models don't need the pos_min >=
pos_min_thold guard bypass — only DSV4 (which always has pos_min=0
due to no KV cache eviction) requires it.

* narrow reset log message to DSV4 only per firecoperana review

Replace remaining is_state_ckpt_model with is_dsv4 in the
do_reset log branch; remove the now-unused variable.

* cleanup: revert unnecessary newlines, spacing, and comment changes

* fix: restore partial KV reuse for DSV4 in llama_model_supports_partial_kv_reuse

DSV4 has private per-position state but uses state checkpoints to
restore after a mid-sequence divergence.  The function was returning
false, causing batch_pending_prompt to reset n_past=0 before
apply_checkpoint could restore from a checkpoint, which broke the
entire checkpoint mechanism.

* Remove bloat

* Reinstate deleted comment

* replace strcmp(arch_string) with llama_model_is_deepseek4()

SamuelOliveirads added the helper upstream — cleaner and avoids
the fragile string comparison.

* inline llama_model_supports_state_checkpoints into call site

Replaced with the inline expression
  llama_model_has_recurrent(model) || llama_model_is_deepseek4(model)
and removed the now-unused function from llama.h and llama-model.cpp.

* fix: GCC 13.3 variadic macro trailing comma in SLT_WRN

SLT_WRN expands to LOG_WRN with __VA_ARGS__ at the end. When no extra
args follow the format string, the dangling comma causes GCC 13.3 to
error with 'expected primary-expression before')' token. Use '%s'
pattern consistent with all other zero-arg SLT_WRN callers.

* dsv4_stream_offset_size: bool return, GGML_ASSERT on write, graceful abort on read

dsv4_stream_offset_size silently returned offset=0, size=0 for invalid
stream indices. Now returns bool — writer hard-aborts via GGML_ASSERT
(prevents writing corrupt checkpoints), reader aborts the restore via
return false (handles corrupt checkpoints gracefully).
2026-07-30 18:50:59 +03:00
Kawrakow b2d0b7c8c7
Set n_gpu_layers automatically (#2209) 2026-07-30 18:39:24 +03:00
Samuel Oliveira Alves dd837ff21a
DeepSeek V4 spec checkpoints (#2205)
* add DSV4 speculative checkpoints

* Fix DSV4 checkpoint cleanup indentation
2026-07-30 13:34:33 +03:00
Kawrakow 8a27bef8d4
DS4 refactoring (cont'd) (#2194) 2026-07-28 07:51:50 +03:00
Joel Farthing 1a7691fae7
DFlash: add Laguna XS 2.1 support (#2124)
* Add Laguna XS 2.1 DFlash support

* Fix DFlash prompt capture with microbatches

---------

Co-authored-by: Joel Farthing <262452229+joelfarthing@users.noreply.github.com>
2026-07-27 07:40:10 +03:00
Samuel Oliveira Alves de55d9e2f6
fix: adjust token count for MTP draft generation in kv cache update (#2181) 2026-07-25 18:07:48 +03:00
Thireus ☠ e5357286c0
hotswap: keep load-time-derived and transformed tensors coherent after reloads (follow-up to #2131) (#2163)
* hotswap: keep load-time-derived and transformed tensors coherent after reloads

- Re-derive the MLA combined attn_kv_b (computed_wkv_b) in place when a layer's
  attn_k_b/attn_v_b are hot-swapped: the mla>1 prompt-processing path consumes
  the derived tensor, so swaps of the source tensors previously had no effect
  (KLD stayed exactly 0 in per-tensor benchmarks, e.g. GLM-5.2 attn_k_b/v_b).
- Refuse (loudly) hot-swaps that cannot be correct: views into -mqkv/-muge
  merged tensors, khad-folded MLA weights, in-place-scaled ffn_gate_inp_s,
  BitNet fused scales, OpenPangu parameter-sink sources, and same-dtype swaps
  of mmap-backed tensors. A refused reload produces no 'reloaded tensor' line,
  so benchmark drivers quarantine the round instead of recording wrong data.
- Propagate reloaded data to same-name duplicate instances (tied lm head copy
  of token_embd, per-layer rope_freqs/rope_factors copies, expert-bias dups),
  warning when a duplicate cannot be refreshed.
- Warn that derived state stays stale where a refresh is not possible:
  pre-transposed wk_b_pp under -sm graph/attn, requantized MTP head
  (output_extra.weight), and k_b/v_b derived from a reloaded attn_kv_b.
- Warn at registration time when -rtr is enabled (restores cannot reproduce
  the run-time-repacked state; F16 -> BF16_R16 is lossy).
- server: only attempt the /health hot-swap reload when no slot is processing,
  and clear the KV cache + cached prompts after a successful reload (they were
  computed with the previous weights).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* Deduplicate llm_compute_wkv_b

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-07-22 17:34:43 +03:00
Kawrakow 7945404458
DS4: slowly approaching a meaningful performance (#2165)
* initial map to load deepseek 4 arch

* wip

* wip: match graph build and attn logic for dpv4

* wip: Enhance DeepSeek-V4 architecture with new tensor types and sqrtsoftplus gating function

* Update DeepSeek-V4 to support raw key indexing with read/write indices

* fix mismatch in attn_raw

* Enable FA with CSA/HCA

* Fix logit mismatch with FA path

* Clean traces and logs for debug

* Refactor DSV4 tensor handling for MTP execution and improve raw context management

* Refactor DeepSeek4 tensor operations: replace manual weighted sum and post-processing with new helper functions

* Share mHC pre-projection and fix packed DSV4 writes

* DSV4: add shared top-k selection and improve mask handling

* Fix DSV4 c2048 view stride and duplicate loader instantiation

* Reuse shared RMS normalization in DSV4 graph

* Replace DSV4 indexer rotation with shared Hadamard

* Share CSA visibility mask with DSV4 LID

* dsv4: document dependency ordering and reset state

* Remove DSV4 zero-dependency graph shim

* Fix DSV4 packed stream execution

* Remove DSV4 l_out backend override

* Enable DSV4 quantized K-only cache

* Revert "Enable DSV4 quantized K-only cache"

This reverts commit 04f9b425321f62ba60e16d1bea2f8de714cfe855.

* Fix DSV4 quantized cache accounting

* Fail closed on unsupported DSV4 cache lifecycle operations

* Various optimizations

* llama: fix GGML_METAL=ON build - missing ggml-metal.h include in llama-dflash.cpp (#2134)

llama-dflash.cpp calls ggml_backend_is_metal() and
ggml_backend_metal_set_n_cb() inside an #ifdef GGML_USE_METAL block but
never includes ggml-metal.h, so any Metal-enabled build fails to
compile. Add the same guarded include llama.cpp already uses.

* New op: ggml_sum_rows_ext (#2132)

* Add ggml_sum_rows_ext

* openPangu: use ggml_sum_rows_ext also in mhc_post

* openPangu: use ggml_sum_rows_ext also in mhc_tail

* Minor

* Reuse shared inverse RoPE operation for DSV4

* Reuse maintainer CUDA concat implementation

* WIP

* hc_pre

* hc_post

* Remove unnecessary mask manipulations

* WIP

* Take into account swiglu limits

* Turn on fused indexer by default

* Give names to mat mul results

* More named ops

* dsv4: do not uselessly copy the KV cache

+20% TG at 32k tokens

* mask_to_index and make CPU FA work with that

* Much better CPU-only, CUDA still not functional

* Better CPU TG

I'm now at 9.7 t/s for zero context and 6.5 t/s for context of 32k.
PP is 120 t/s for short context and 101 t/s at 32k.

* Even better CPU TG

I'm now at 8.1 t/s for context of 32k tokens.

* Turn off DSA on CUDA for now

* Fix CUDA DSA

* Remove again the unnecessary softmax result buffer

* Experiments

* Various

* More named ops

* Forgot to uncomment

---------

Co-authored-by: samuel <samueloliveira32df@gmail.com>
Co-authored-by: hchengit <95317477+hchengit@users.noreply.github.com>
2026-07-22 17:18:57 +03:00
mb8565 27d6291222
Fix uninitialized MROPE/IMROPE position sections in legacy-batch decode path (#2149)
For IMROPE models (Qwen3.5/QWEN35MOE) the RoPE op reads 4 position sections per
token, but the legacy null-pos decode fallback (llama_batch_get_one path, used by
llama-perplexity/llama-cli/llama-eval-callback) sized the position vector to n_tokens.
llama_set_inputs then copies n_tokens*4 out of that n_tokens-sized vector, an
out-of-bounds read that feeds garbage into 3 of the 4 rope sections, giving wrong and
run-to-run non-deterministic RoPE. Build the sections like the explicit-pos path (text
t,t,t,0). Standard-rope (NEOX) unchanged; VL image input uses the explicit-pos builder.

Qwen3.5-4B self-vs-self same-top: 91.3% -> 100.0% (CUDA), single-thread CPU likewise.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-18 09:07:50 +03:00
usrlocalben 65891dcdb3
include DSA indexer state in kv/slot serializer (#2146) 2026-07-18 09:01:26 +03:00
cora4 8e2d83cfee
Fix per_layer_token_embedding (#2117)
Co-authored-by: cora4 <you@example.com>
2026-07-12 11:58:09 +03:00
Joel Farthing bdb23e8763
openpangu: use fused indexer top_k with -fidx (CPU-only op) (#2111)
Route DSA indexer selection through ggml_indexer_topk when -fidx is set: one op computes the weighted-relu head sum plus causal mask and returns top-k rows without materializing the [n_kv, n_ihead, T] score tensor. Off by default; the unfused chunked/full paths are unchanged. Composes with gathered DSA, deferred attention-chunk masks, and the set_rows mask path.

Co-authored-by: Joel Farthing <262452229+joelfarthing@users.noreply.github.com>
2026-07-12 07:09:12 +03:00
Joel Farthing b90939934a
model: add openPangu-2.0-Flash (92B-A6B) with MLA-latent cache, DSA/SWA, mHC, and multi-head MTP (#2065)
* openpangu: Stage-1 converter probe for openPangu-2.0-Flash

Add OpenPanguV2ForCausalLM conversion support (converter-only; runtime graph
is Stage-2). Registers a new LLM_ARCH_OPENPANGU on the Python/gguf-py side:

- gguf-py/constants.py: MODEL_ARCH.OPENPANGU + name, indexer KV keys, 22 new
  tensor enums (DSA indexer x4, MoME convs x3, param-sink x2, mHC/Hyper-
  Connections x12, block-post-norm), and the full MODEL_TENSORS list reusing
  the deepseek MLA + MoE + NextN bricks.
- tensor_mapping.py: arch-specific block mappings that disambiguate the
  sandwich norms (post_attention/pre_mlp/post_mlp) and pin every Pangu-only
  tensor; non-block global mHC merge module.
- convert_hf_to_gguf.py: OpenPanguV2Model (subclasses DeepseekV2Model) with
  set_gguf_parameters (MLA/MoE/indexer/mHC/param-sink/DSA+SWA metadata),
  modify_tensors (expert merge, kv_b split, no MTP skip), and the
  OpenPanguV2Tokenizer pre-tokenizer hash.

Validated offline against the real 50-shard safetensors index: all 37,587
tensors map to a GGUF target (0 unmapped), and set_gguf_parameters reads only
hparams present in config.json. No weights downloaded; no GPU. Pinned on the
ik/dsa_loop_hadamard_blend DSA substrate.

* openpangu: Stage-2 arch scaffold (LLM_ARCH_OPENPANGU) — loadable, compiles

New arch on main (DSA-decoupled). Declares openPangu-2.0-Flash to the runtime so
the model loads into memory; the compute graph is the next step.

- llama-arch.{h,cpp}: LLM_ARCH_OPENPANGU + name; 3 KV keys (mhc_num_stream,
  mhc_recur_norm, param_sink_number); 18 tensor enums (mHC x12, MoME conv x3,
  param-sink x2, block-post-norm).
- llama-model.cpp: OPENPANGU tensor-name block, strings matched to the converter.
- llama-model.h: layer + model struct fields (mHC / conv / sink / block-post / merge).
- llama-hparams.{h,cpp}: reader (MLA + MoE + sigmoid gate + indexer + mHC +
  param-sink + NextN); n_layer_kv_from_start = n_layer - nextn (MTP skipped).
- llama-load-tensors.cpp: create_openpangu_tensors (GLM-DSA MLA/MoE base + Pangu
  tensors; indexer loaded-but-unused for dense fallback); dispatch + is_mla_attn.

Builds clean (CPU-only libllama). Dense-fallback design: no DSA indexer / SWA
windowing / MTP for first generation (exact <=512 tokens). Graph is Stage-2b.

* openpangu: fix compresskv_conv dim (kv_lora_rank, not +rope); pin attention order in spec

* openpangu: end-to-end runtime — build_openpangu graph runs, generates (garbled)

First full forward pass of openPangu-2.0-Flash on ik_llama. Pipeline works end to
end: new LLM_ARCH_OPENPANGU loads the Q4 GGUF, the graph executes, and llama-cli
generates 40 tokens (EXIT=0). Output is currently garbled (tensor-layout bug to
debug), but the structure is proven.

graphs/build_openpangu.cpp: dense decompressed-MHA attention + 4-stream mHC
(Hyper-Connections) with 20-iter Sinkhorn + MoE(sigmoid+shared) + sandwich norms
+ entry stream-repeat/tail-merge + inp_out_ids selection.

Bring-up fixes to load+run:
- llama-vocab.cpp: register 'openpangu' pre-tokenizer (QWEN2 family)
- llama.cpp: OPENPANGU -> LLAMA_ROPE_TYPE_NORM (was defaulting to NONE=-1)
- llama-load-tensors.cpp: full wkv_b load; k_b/v_b as flattened 2D; block_post_norm
  dim = S*H (10240); conv weights 2D {3,C}; mHC alpha/beta/gamma + param_sink +
  merge params use bare (no-.weight) tensor names
- llama-model.cpp: OPENPANGU is NOT is_mla_attn (decompressed MHA, standard KV cache)
- graph loop bounded to base layers (skip NextN/MTP)

v0 deferrals (need conv-state cache / manual attention path, all documented):
MoME convs (passthrough), o_conv, param_sink. Next: fix the layout bug to coherence.

* openpangu: COHERENT generation — NEOX rope, Sinkhorn orientation, MoME convs, param_sink

Four correctness fixes on top of the end-to-end scaffold, verified checkpoint-by-
checkpoint against a Python golden reference running on the GGUF's own dequantized
weights (block-0 activations now match to rounding at full fidelity):

- rope: NORM -> NEOX. Pangu config rope_interleave=false; the Infer source maps it
  as is_neox_style = not rope_interleave (rotary_mode='half').
- mHC Sinkhorn: the flat h_res block is torch-[r,c] row-major, so a bare ggml
  reshape lands column-fastest; the doubly-stochastic iteration ran transposed
  (Sinkhorn is not transpose-symmetric). One transpose at input fixes the whole
  chain including the mhc_post application.
- MoME convs (qa/compresskv/o): were passthrough stubs. Implemented as
  out = x + causal_conv1d(x) (every Infer call site uses residual_connection=1;
  tap stats confirm the perturbation form). Taps cast f16->f32 for ggml_mul.
  Batch-local v0: exact for fresh-sequence prefill; decode steps miss the
  t-1/t-2 taps until a conv-state cache exists.
- param_sink: 128 learned latent-KV entries prepended per layer via a manual
  attention path (kv_store + explicit soft_max over [sinks ++ cache]); huge
  effect at short context. o_conv now applied pre-o_proj on the same path.
  flash_attn forced off for OPENPANGU (FA kernel cannot see the sinks).
- converter: add_bos_token=true (HF prepends <|pangu_text_start|> via the
  post-processor; the key was absent so ik dropped BOS).

Greedy Q4_K_M smoke, chat template + <think>: coherent CoT reasoning and a
correct answer. Layer-0 instrumentation (opg0_* names) kept for now.

* openpangu: MoME conv-state cache — decode steps get real t-1/t-2 taps

Allocate a per-layer cache_s_l tensor for OPENPANGU base layers holding the last
two pre-conv latents of the three MoME sites, packed
[qa 2*1024 | compresskv 2*512 | o 2*6144] f32 (~60KB/layer). The conv helper
reads the [C,2] history window (zeros at sequence start, kv_head==0), builds
xx = [hist ++ x], and writes the last two columns back each ubatch — the concat
naturally handles both prefill chaining and the T==1 shift. Read precedes write
in graph order; the fixed-offset copy is graph-reuse safe.

Verified: prefill anchors unchanged (bit-path identical, zero-history branch);
-ub 1 token-by-token run matches the golden reference at t4 (qlora_conv 0.084,
R_block 0.008 rel; attn_out 0.15 on one channel = f16 KV-cache rounding, washes
out by post-norm); final logits differ from full-batch only by a common-mode
shift that softmax cancels. Chat-template greedy smoke: think-block repetition
is gone — clean structured CoT and correct answer.

v0 limits documented in the helper: one state slot (single sequence); cache
rewinds leave the state stale.

* openpangu: NextN/MTP speculative decoding — 1.7-1.8x TG on CPU

Wire the three NextN layers (46-48) into ik's MTP speculative framework
(--spec-type mtp). v0 drafts with head 1 (layer 46), self-chained by the
framework.

- llama.cpp: add OPENPANGU to the cparams.mtp arch allowlist (it was silently
  zeroed, which left the target context without a logits buffer once the server
  enabled embeddings -> GGML_ASSERT(lctx.logits) in speculative_is_compat).
- load-tensors: MTP layers carry no mHC tensors (tail_use_mhc=false in the
  reference) — create them only for base layers. nextn.* tensors were already
  wired by the Stage-1 probe.
- build_openpangu: extract the attention sublayer into
  build_openpangu_attention (shared base/MTP); add build_openpangu_mtp:
  eh_proj(cat(enorm(embed), hnorm(prev_hidden))) -> one plain-residual Pangu
  block (sandwich norms, convs+param_sink, MoE+shexp, no mHC/block_post_norm)
  -> shared_head norm+head. MTP branch returns the draft graph when
  mtp_op_type != NONE; main graph keeps all-token outputs under cparams.mtp.
  MTP convs run batch-local (no conv-state slot) — affects acceptance only.

A/B (Q4_K_M, CPU, greedy, 192-token chat CoT completion, warm back-to-back,
medians of 3, bracketed B/A/B):
  no-spec:              2.44 t/s  (2.34-2.86)
  --spec-type mtp:n_max=3: 4.23 / 4.49 t/s  (brackets)  => ~1.7-1.8x
Draft acceptance 34% on CoT prose (46% on repetitive text); spec and no-spec
greedy outputs are byte-identical. Headroom: conv-state for MTP drafts, n_max
tuning, true 3-head chaining (spec_step_idx).

* server: include draft_n/draft_n_accepted in /completion timings

get_formated_timings() (the /completion path) omitted the speculative
counters that get_timings() (the OAI path) already reports; add them,
guarded by n_draft_total > 0 like the OAI path.

* openpangu: position-indexed MoME conv-state ring — rollback-safe spec decoding + MTP draft chaining

The v0 single-slot conv state held the last-2 pre-conv latents of the most
recent batch, so any speculative draft rejection left latents of REJECTED
positions in the state and every later decode ran with wrong t-1/t-2 taps
(3 conv sites x 46 layers). At 192-token greedy runs every spec config
diverged from no-spec, each differently (rejection-pattern dependent).

Replace it with a per-layer ring cache_s_l [n_lora_q+n_lora_kv+n_head*v_dim, 16]:
column pos%16 holds position pos's pre-conv latents ([qa|ckv|o] packed).
Invariant: reads target only positions before the first batch token, which
are committed, and committed latents depend only on the committed prefix -
rollback-safe by construction, no checkpointing. Writes cover the last
min(T,16) batch positions in <=2 contiguous cpy segments; the copy sources
are views of the [hist ++ x] concat so the history read is an ancestor of
every write (read-before-write by graph dependency).

The ring is also allocated for the NextN/MTP layers, so the draft head
chains real conv taps across WARMUP -> sequential DRAFT_GEN steps (was
batch-local zero-history per draft token).

graph_reuse is forced off for the arch: ring view offsets are position-
baked and the reuse patcher only updates the standard K/V-store copies.
Measured cost on the CPU server path: none visible. ggml_set_rows driven
by an input index tensor is the future reuse-safe shape.

Verified (Q4_K_M, CPU, greedy 192-tok chat-CoT, warm single process):
- no-spec output byte-identical to pre-ring build
- spec output byte-identical to no-spec below the n_predict cap, for all
  of n_max in {1,2,3,4,6} x p_min in {0,0.3,0.6} (old build: all diverged)
- acceptance n3-p0: 33.9% -> 60.9%; n3-p0.3: 58.1% -> 68.9%
- TG medians: no-spec 3.19-3.32 t/s; mtp:n_max=3,p_min=0.3 6.97 t/s (~2.1x)

* openpangu: DSA lightning indexer + SWA schedule — long-context correctness past the dense fallback

The dense fallback was exact only <=512 tokens (SWA window). This wires the real
DSA/SWA hybrid schedule, self-contained from GGUF keys the converter already
writes (openpangu.swa_layers + sliding_window_list; absent keys keep the old
dense fallback):

- SWA layers (30 base @512): the generic inp_KQ_mask_swa path, per-layer mask
  choice in the builder. The NextN/MTP layers are SWA @2048 in the checkpoint
  schedule; MTP graphs run in their own context, so the mask fill picks
  hparams.n_swa_mtp when built with an MTP op type.
- DSA layers (16, every 3rd): lightning indexer implemented in-graph from the
  Infer reference semantics (jointfix _pangu_torch_calib): q_idx = wq_b on the
  post-conv post-norm q-lora latent (24x128), k_idx = rms-normed wk(x) shared
  across heads, both NEOX-roped on the FIRST n_rot channels; score =
  sum_g w_g * relu(q_g . k) in f32, causal-masked, exact top-k via
  argsort + ggml_set_rows scatter into a -1e30 base -> additive selection mask
  on the existing manual soft_max seam. Selection engages only when the causal
  window exceeds index_top_k (2048); below that the layer is exactly dense.
- Indexer keys cached per position (cache_idx_l, f32 [128, kv_size], DSA layers
  only) with the same committed-position invariant as the conv-state ring, so
  speculative rollbacks stay safe.
- param sinks remain outside both the window and the selection budget, matching
  the reference.

Verified (Q4_K_M, CPU):
- <=512 tokens: byte-identical to the dense build (96/160-token greedy)
- indexer scores vs a GGUF-dequant golden reference at 2101 tokens: 1e-3 rel
  (f16 weight rounding); top-3 selection indices exact on all compared queries
- >512 coherence clean; 3.4K-token needle retrieval through active selection
  (needle outside every SWA window, ~1300 positions pruned) answers exactly

* openpangu: MLA-latent KV cache — attention absorbed into the 512-latent, 14x smaller cache, ~2.2x TG

Store per position only [ckv_norm 512 | roped k_pe 64] (f32, k_l) plus the
transposed 512-latent (f32, v_l, v_trans layout); per-head K/V are never
materialized. q_nope is absorbed through attn_k_b (loaded 2D from the
converter split for base layers; derived at load via llm_prepare_mla for the
NextN layers - now guarded for layers without attention weights, e.g. the
idle NextN heads 2/3). The value side is the latent itself, up-projected
through attn_v_b after the weighted sum, matching the Infer _forward_dsa
reference. param sinks are native latent-space entries, which removes the
per-step full-cache concat+cast that dominated long-context decode.

llama_state row sizes now come from llama_kv_k_row_embd/llama_kv_v_row_embd
(arch-aware), fixing an out-of-bounds crash in the server prompt-cache save
path (hparams-derived 9216-wide rows vs actual 576-wide latent rows).

Verified (Q4_K_M, CPU): layer-0 attention output matches an f32 golden
reference computed from the same GGUF weight encodings (~1e-2 on O(1)
values); MTP spec output byte-identical to no-spec; 3.4K needle retrieval
through active DSA selection exact under greedy. Output differs from the
materialized build at the token level because attn_k_b/attn_v_b are
independently quantized tensors - both are legitimate Q4-fidelity encodings.

Perf (CPU, warm): no-spec TG 3.2-3.3 -> 6.9-7.1 t/s; mtp:n_max=3,p_min=0.3
-> 11.1 t/s (byte-exact, 67% acceptance); prefill 30.5 t/s at 3.4K; KV self
size at 4K ctx: 5.5 GiB -> 391 MiB. Not yet supported on the latent cache:
K-shift/defrag (context shifting) - unreached in current usage.

* openpangu: fence unsupported serving modes, truth-pass comments, drop dead weight/keys

Post-audit hardening. The cache's position-indexed side state (MoME conv ring,
DSA indexer keys) made several generic serving paths silently unsound; they are
now fenced loudly instead of documented as unsupported:

- s_l_position_ring flag on llama_kv_cache: the qnext-state predicate no longer
  claims the conv ring, so per-seq state save, seq_cp and the s_copy graph skip it
- state save/restore refused for the arch at every llama_state_* entry (the ring
  and idx_l are not in the state format; restoring without them diverges silently)
- K-shift/self-extend assert, defrag skips with a warning, server ctx_shift off
  via new llama_model_supports_ctx_shift()
- single sequence enforced at context creation (n_seq_max > 1 refused)
- server prompt-cache reuse limited to pure extension via new
  llama_model_supports_partial_kv_reuse(): mid-cache divergence reprocesses from
  scratch (the 16-column ring cannot rewind); multi-turn continuation stays fast
- MTP draft length clamped to 13 via new llama_model_max_draft_tokens() so a
  rejected draft can never overwrite the ring columns the next decode reads
- K/V cache types forced to f32 for the arch so the KV size log reports the truth
- cache_size(): real latent-cache branch (was falling through to the ~14x larger
  materialized estimate used for offload planning)
- unused fused wkv_b no longer loaded (TENSOR_SKIP; the graph runs entirely on the
  pre-split k_b/v_b), llm_prepare_mla openPangu special-case removed (it was a no-op)
- stale v0 comments rewritten to describe the shipped graph; converter stops
  writing dead keys (dsa_layers, block_post_layernorm_idx) and the tokenizer
  pre-hash is registered in convert_hf_to_gguf_update.py

Gates on this build: greedy spec output byte-identical to no-spec (EOS-terminated,
sha-equal); 3.4K needle retrieved exactly; -np 2 / state save / n_max=20 / stale
prefix reuse all refused or clamped with clear messages.

* openpangu: assert kv_head == first batch position at graph build

The ring, indexer and latent stores are addressed by absolute position through
kv_head; the fences make append-only decode the only reachable mode, but the
invariant was unchecked. Assert it at both graph entries (base and MTP) so any
future cache plumbing that breaks it fails at build instead of corrupting
output. Worst-case measurement builds pass pos = null and are exempt.

* openpangu: cont h_pre before the mHC broadcast mul (CUDA binbcast misreads strided views)

h_pre is a row-slice view of the fused mixes tensor. The CPU mul handles the
strides; the CUDA broadcast path reads the view as if contiguous, so token 0
mixes correctly and every later token gets h_post/h_res rows instead. First
divergent node in the whole graph (oracle rel 0.36 at opg0_attn_mhcpre_x,
fixed to 7.5e-5). Sibling views h_post/h_res were already cont-wrapped, which
is why only h_pre was exposed.

* openpangu: keep DSA zero-trick sources finite (CUDA clamp propagates the 0*(-inf) NaN)

The selection-mask base and zeros were built by scaling the MASKED scores by
zero, but post-mask sc contains -inf and 0 * -inf = NaN. The CPU clamp launders
NaN back to -1e30 (fminf/fmaxf ignore NaN); the CUDA clamp propagates it, so
every DSA layer emitted NaN masks at n_kv > top_k and logits collapsed
(observed: eval-callback CLAMP sum -1.3e36 on CPU vs nan on CUDA, 11748 NaNs
downstream). Scale the pre-mask finite scores instead, which is correct on any
backend regardless of clamp NaN semantics. Also defensively cont the strided
KQ_mask slice feeding the score add (same strided-view kernel class as the mHC
h_pre fix; unproven here but cheap). Gates after fix: 2600-token probe coherent,
3.4K needle exact ('7391') with and without MTP speculation, PP ~120 t/s.

* openpangu: f16 latent KV cache option (explicit -ctk/-ctv f16 halves cache memory, f32 stays default)

Track explicit cache-type requests through CLI/env; openPangu resolves no-request
to f32 (unchanged), accepts explicit f32/f16, warns and falls back to f32 for
BF16/quantized. Sink and cached-token KQ paths stay separate until after KQ so
the latent cache is read directly without the f32-only concat; value is the sum
of the sink and cache matmuls. Ring and DSA indexer caches stay f32; cache_size()
follows the resolved types.

* openpangu: enable graph reuse

* openpangu: wire multi-head MTP drafting

* openpangu: add MTP heads override

* openpangu: keep MTP update logits last

* openpangu: scope MTP warmup heads

* speculative: apply per-request MTP heads before warmup

* openpangu: fix multi-head MTP warmup computing on unwritten inputs

Each chained head called the build_inp_* helpers itself, so the warmup and
update graphs held one inp_tokens/inp_pos/inp_out_ids/KQ_mask tensor per
head while llama_set_inputs only fills the tensors the lctx pointers
reference, i.e. the last head's copies. Every head but the last read
unwritten compute-buffer memory: with heads=3 active even head 1's ring,
latent cache, and cached one-token draft were computed from garbage, which
is why depth-1 acceptance measured 4% against 98% for the heads=1 control.

Create the batch inputs once in build_openpangu and pass them to every
build_openpangu_mtp call, and fix the two chaining errors that were hiding
behind the garbage inputs:

- Shift the chained hidden: head k+1's row at position p consumes head k's
  output row at p-1, the same convention head 1 uses for the target's
  conditioned hidden rows. The predecessor of a batch's first row lives in
  the previous warmup/update, carried across decodes through a new
  inp_mtp_carry input backed by lctx.mtp_carry (written back per ubatch,
  zeroed when a prompt warmup restarts from position 0).
- Fill head 3's cache row at draft step 2: each draft step runs one head,
  so head 3's own decode at step 3 attended over a never-written row at
  the step-2 position. Pre-write it from the committed carry.

Also include the active head count in the graph-reuse key next to the
existing step index (reuse stays forced off for this arch).

* speculative: default MTP drafting to a single head

A stage without an explicit heads= override previously resolved to 0,
meaning all model heads, so multi-head drafting was silently on by
default for models that carry more than one NextN layer. Keep it opt-in
(heads=N or heads=0 for all) until multi-head measures a win over the
single-head config; single-head models are unaffected either way.

* speculative: fence MTP head upshift over a warmed prefix

Deeper NextN heads only hold valid cache rows for spans that were warmed
with them. A request drafting with more MTP heads than the cached prefix
was warmed with (e.g. a heads=1 conversation continued with heads=3, a
pure extension the divergence fence deliberately allows) would read
never-written deeper-head rows: verification keeps the output correct,
but acceptance quietly collapses and any measurement taken there is
misleading.

Track the minimum head count the committed context has been warmed with
since position 0 and have the server reprocess from scratch when a
request asks for more. Also announce the model's NextN head count and
the single-head default once at MTP context setup.

* openpangu: skip dead MTP chain compute and stall-free carry readback

The update chain's last head and the draft-time row fill only matter for
their latent-cache and conv-ring writes; their FFN, norms, and shared
head fed nothing. Add a cache-writes-only mode to the MTP block builder
that returns after the attention block, and use it at both sites.

The multi-head carry readback previously synchronized the scheduler
after every warmup/update decode, a hard stall on CUDA. Issue the
device-to-host copy async on the backend stream instead (stream order
protects the source buffer from later graphs) and synchronize lazily
when the host buffer is next consumed or resized.

* openpangu: stop emitting fused kv_b tensor

* openpangu: default latent cache to f16

* openpangu: refuse unsupported latent cache types

* Window OpenPangu SWA cache reads

* Gather OpenPangu DSA decode reads

Gather DSA decode attention over the selected latent rows for OpenPangu base-model decode and verify graphs. The gathered branch now uses ggml_top_k order directly, runs maskless softmax over sinks plus selected rows for T <= 14, and derives values from the gathered k_l rows instead of the transposed latent cache.

* Chunk OpenPangu indexer prefill scoring

* Chunk OpenPangu prefill attention

* Gather OpenPangu sparse prefill attention

* Drop OpenPangu value cache

* Add OpenPangu indexer cache type flag

* Add OpenPangu q8_0 latent cache type

Store the OpenPangu MLA latent K cache as q8_0 via -ctk q8_0 (about 0.53x of
f16); the default stays f16 so behavior is unchanged without the flag. Latent V
stays f16/f32.

The q8 latent cache is a storage format only: it is dequanted to F32 before all
compute. K reads go through openpangu_build_k_latent_for_read, V derivation
through openpangu_build_v_latent_from_k (full 576-wide row to F32, then slice),
and the DSA gather paths already dequant via get_rows. Feeding a q8 latent view
directly into the KQ mul_mat corrupts large-context prefill, so that path is
removed for quantized caches. The cache write stages ckv and kpe through F32 and
writes one full 576-wide q8 row per token.

Verified on a small discriminator model: the default f16 path is byte-identical
to the prior code; the first-DSA-layer attention envelope is within 0.6% of the
f16 cache (linf_rel 0.0057); top-k selection is bit-identical between cache
types; the q8 latent cache is 0.531x the f16 size at 8K and 32K context; and
generation stays coherent on both the dense and DSA-gather paths at all tested
context lengths.

* Remove OpenPangu debug trace env knobs and redundant DSA_TOPK override

Drop the five LLAMA_OPENPANGU_*_TRACE debug-logging knobs (DSA_GATHER_TRACE,
IDX_CHUNK_TRACE, ATT_CHUNK_TRACE, PREFILL_GATHER_TRACE, SWA_WINDOW_TRACE) and the
LLAMA_OPENPANGU_DSA_TOPK override, which duplicated the -dsatk / --dsa-top-k CLI
flag; top-k now comes solely from cparams.dsa_top_k. The five perf-tuning knobs
(DSA_GATHER, IDX_CHUNK, ATT_CHUNK, ATT_KQ_MAX_MIB, PREFILL_GATHER) are retained
pending the perf battery. No change to default behavior.

* Subchunk OpenPangu DSA prefill gather to fit CUDA grid limit

The prefill gathered-attention ggml_get_rows produced dst rows = topk *
token_chunk (2048 * 256 = 524288) mapped to the CUDA grid.y dimension, which
caps at 65535, crashing with GET_ROWS invalid argument at long context (N_KV
around 10.5K with the natural topk of 2048). Split the prefill gather into token
subchunks so topk * subchunk_tokens stays within the grid limit, and guard the
decode gather with the same fit check (falling back to the dense masked path if
a pathological topk would not fit). The subchunking is over the token dimension
only, so per-token attention is unchanged and the result is numerically
identical. Verified: the GPU sweep runs past the old crash boundary to 22K+ with
zero CUDA errors; CPU and -ctk q8_0 paths unaffected.

* openpangu: fix scheduler node budget for chunked DSA prefill; drop unused attn_kv_b; remove env tunables

- Size the scheduler graph node budget for the chunked DSA prefill so 32K/ub2048 no
  longer trips the hash-set reservation assert; derive the extra budget from the
  builder's chunk/top-k/window structure with a fixed safety margin.
- Remove LLAMA_OPENPANGU_* environment tunables from both the node-budget estimator
  and build_openpangu.cpp; use fixed constants in both so they stay in sync.
- Converter: emit only the split attn_k_b/attn_v_b projections and drop the unused
  fused attn_kv_b tensor.

* openpangu: restore DeepSeek converter kv_b; drop trace env + dead code; fix dense-fallback node budget

- convert_hf_to_gguf.py: restore fused attn_kv_b in DeepseekV2Model (shared
  parent); openPangu subclass keeps split-only k_b/v_b. Stops newly-converted
  DeepSeek GGUFs from failing to load.
- src/llama.cpp: remove LLAMA_GRAPH_REUSE_TRACE getenv, hit/miss counters, and
  the unconditional destructor log (no getenv or behavior change for any arch);
  node-budget estimator now covers the dense-fallback (n_swa==0) attention-chunk
  loop while skipping absent idx/top-k terms, preserving a strict overcount;
  remove unreachable openPangu split-cache block.
- src/llama-context.h: drop now-dead graph_reuse_hits/misses members.
- include/llama.h: move type_k/type_v/idx_type_k *_explicit bools to struct end
  to avoid a mid-struct ABI shift for out-of-tree consumers.
- src/graphs/build_openpangu.cpp: replace vestigial env-struct singletons with
  the OPENPANGU_* constants; drop a redundant Sinkhorn permute round-trip
  (one transpose; greedy output verified byte-identical).

Decode output unchanged (byte-identical greedy generation verified); shared-file
changes are openPangu-gated or restore the pre-PR baseline.

* openpangu: chat-parser support (reasoning split + thinking toggle)

Two openPangu-only fixes, both gated on the arch-unique token
<|pangu_text_start|> so no other model's parsing changes.

- chat-diff-analyzer: add a workarounds entry that force-sets TAG_BASED
  reasoning with an empty start and a </think> end. openPangu prefills
  <think> in the generation prompt, so the output is delimited only by
  </think>; the differential detector otherwise learns start="<think>"
  from the assistant-history form and fails to split, leaking reasoning
  into content. Same shape as the existing Laguna prefill patch.

- chat.cpp: bridge enable_thinking to the template's `thinking` variable.
  openPangu's template gates reasoning on `thinking` rather than the
  ecosystem-standard `enable_thinking`, so the standard toggle was inert.
  An explicit `thinking` chat_template_kwarg still overrides via the
  extra_context merge.

Blast radius: test-chat-auto-parser 437/437 unchanged; the sole
test-chat-template diff is a pre-existing GLM trailing-newline.

* openpangu: use ggml_cast for latent dequant reads

Replace ggml_cpy(view, ggml_new_tensor_2d(F32, ...)) with ggml_cast in the MLA
latent V-from-K and K-read helpers. ggml_cast emits the identical GGML_OP_CPY
node into a fresh f32 tensor, so behavior is unchanged; it is the idiomatic
form. Per review.

* openpangu: narrow SWA reuse-key fields to 32-bit

The openpangu_swa_window_view reuse key stored n_kv/n_tokens/window/pad as
int64_t, but these are bounded well under 2^31 (window/pad are uint32_t at
source; n_kv/n_tokens <= context length). Narrow to int32_t/uint32_t and drop
the widening casts. w_view/win_off stay int64_t: they feed ggml view
dims/offsets. Per review.

* openpangu: precompute param_sink derived tensors at load

The per-layer attention-sink block (sink_blk [576,NS]) and its transposed
latent (s_lat_t [NS,512]) are pure functions of the layer weights, yet were
rebuilt every eval across all 49 layers (RMS-norm + cast + concat + transpose).
Compute them once at load, mirroring the wk_b derived-weight precompute, and
read the stored tensors in build_openpangu_attention. Numerically identical;
removes per-token work at decode.

* openpangu: replace conv position-ring with ggml_ssm_conv + spec-rollback checkpoint

Migrate the MoME depthwise causal conv (three sites per attention sublayer:
qa-lora, compressed-kv, attn-out) from the bespoke 16-column position-indexed
ring onto the core ggml_ssm_conv op with a recurrent conv-state slot.

Cache: s_l becomes [2*conv_col_ne, qnext_state_slots], holding the (d_conv-1)=2
history taps per channel for the three sites (float offsets 0 / 2*n_lora_q /
2*(n_lora_q+n_lora_kv)). Drops the conv_hist_idx / conv_write_idx graph inputs
and their fill in llama_set_inputs; adds one single-sequence sq input for
ggml_ssm_conv shared across the three sites and the MTP head.

Speculative rollback: the position ring self-healed rejected draft columns by
absolute position; a recurrent slot does not, since seq_rm is a no-op for
recurrent state. openPangu is admitted at the three spec-checkpoint save/init
gates so the whole-slot shadow checkpoint (gpu-fallback) snapshots the conv
slot before drafting and restores it before the accepted-token replay. The
restore path is already keyed on ckpt.valid, so no gate change is needed there.
Per-step checkpoint mode is declined for openPangu, which has no SSM recurrent
term, so auto mode resolves to the whole-slot shadow.

Gated: non-spec needle unchanged; MTP-spec needle correct with healthy draft
acceptance (rollback verified via the acceptance canary).

* openpangu: single ggml_concat copy for the latent cache store

The non-quantized latent store split the [ckv | roped k_pe] row into two views
and two cache copies, with a base_offset field on the CacheCopy struct to place
the second one. Match the quantized path: concat the two parts and do one copy
into the cache row. This drops the second cache-copy slot (OPENPANGU_COPY_K_KPE)
and removes base_offset from CacheCopy entirely.

Cache contents are unchanged: the concat writes the same [ckv 512 | k_pe 64]
bytes to the same row. Gated on the needle for both the f16 latent path (the one
that changed) and the q8 latent path, plus coherence.

* openpangu: reuse the shared kr_l indexer cache instead of a separate idx_l

The DSA lightning indexer stored its per-position keys in an openPangu-only idx_l
cache, parallel to the kr_l indexer cache GLM-DSA already uses. Both have the same
storage contract: [indexer_head_size, kv_size], idx_type_k dtype, one row per KV
cell, written at kv_head and read [dim, n_kv] from zero. openPangu now allocates
its indexer keys into kr_l and shares the dsa_cache_copies graph-reuse fixup.

The fixup patch is factored into a helper that both the generic path and the
openPangu update_cache_copies branch call, so the openPangu indexer copy is
repointed to the current kv_head on graph reuse like every other cache write.
This drops the idx_l vector, its allocation and memory accounting, and the
openPangu third cache-copy slot (now one latent copy per layer).

Per-arch allocation predicates stay separate (GLM uses indexer_is_full, openPangu
uses the window==0 DSA schedule); only the kr_l storage and the copy fixup are
shared. openPangu keeps its no-shift/no-defrag/no-state-I/O behavior, and the GLM
Hadamard/k-shift logic stays GLM-gated.

Gated: needle correct on f16 and q8 latent caches and under MTP speculation
(acceptance unchanged at 0.67), plus coherence.

* openpangu: discard pos-0 graphs from reuse; retire stale conv-state comments

The ggml_ssm_conv refactor bakes the pos-0 conv-state reset into graph
topology (a scale-by-zero node on the state view). A graph built at pos 0
could be reused at pos > 0 when the batch shape and padded n_kv match (a
1-token prompt followed by TG is the concrete case), zeroing the conv
history on every reused decode. Admit openPangu at the existing
reset_previous gate so pos-0 graphs are discarded from reuse, the same
guard the qnext recurrent state relies on.

Also retire the internal phase-plan comments the conv refactor left
behind: they claimed the spec-checkpoint wiring had not landed in the
commit that landed it, and misdescribed the s_l slot as awaiting rollback
support.

Gated: needle 8457 on f16 and q8 latent, MTP-spec needle (drafts fully
accepted), coherence.

* openpangu: drop the _explicit cache-type plumbing; validate unconditionally

Review follow-up (item 1 of the second review). The explicit/default
distinction carried less than claimed: the latent K/V fallback was f16,
which is already the -ctk/-ctv and API default, so distinguishing unset
from set-to-the-default bought nothing, and the two bools were behaviorally
redundant. The only load-bearing use was the indexer cache, where openPangu
defaulted to f32 while -ictk defaults to f16. Gating the f16 indexer
directly (needle on f16 and q8 latent paths, MTP speculation, coherence)
shows no quality difference, so openPangu now takes the standard f16
indexer default and the f32 special case is gone. Default indexer cache
memory halves (64 -> 32 MiB at c 8192).

Removes type_k_explicit/type_v_explicit/idx_type_k_explicit from llama.h,
the cparams/mparams plumbing, and common; the resolve helpers become plain
unconditional validators, so -ctk q8_0 is honored and an unsupported type
errors out at load instead of silently coercing.

Gated: needle 8457 on the new f16-indexer default, on q8 latent with MTP
speculation, and with -ictk f32 explicitly honored (64 MiB f32 buffer in
the load log); -ctk q4_0 and -ictk q4_1 refused with a clear error.

* openpangu: keep MTP draft decodes position-contiguous under speculation

The MTP framework's one-token draft shortcut caches a prediction one row
past the accepted prefix during the accepted-token update, then skips
re-decoding the last sampled token at the next draft round. A
mask-addressed cache tolerates the resulting position gap; openPangu's
position-addressed append-only cache (cell == position) does not: after a
rollback the next draft decode lands one cell behind its position, and
after a full acceptance the cache head sits one row ahead of the next
draft base, either way tripping the kv_head == pos[0] invariant and
aborting the server. The checkpoint admission in the conv refactor made
this the standard openPangu speculative flow; the needle-first gates
never generated enough draft rounds against a short prompt to reach it.

Decline the shortcut re-seed for openPangu in mtp_accept_batch (restoring
the drafting behavior all measured acceptance numbers were taken on) and
trim rows at or beyond the draft base in mtp_speculative_gen_draft, so
every draft decode stays position-contiguous with the cache head.

Gated: the crashing flow (short prompt, 512-token spec generation, then a
second request) completes with acceptance 0.60 prose / 0.87 code,
matching the pre-checkpoint baseline profile; needle 8457 plus coherence
on f16+spec and q8+spec.

* openpangu: remove stale ring limits and fix MTP graph reuse

* cli: preserve speculative carry on fallback

Decode an already-emitted pending token when a draft cannot be used instead of sampling unchanged logits and duplicating output. Document single-head MTP as the default and multi-head modes as experimental.

---------

Co-authored-by: Joel Farthing <262452229+joelfarthing@users.noreply.github.com>
2026-07-11 12:29:20 +03:00
dmaivel 6a909f4ff6
Add --prefetch-experts to stream mmap'd MoE experts into page cache (#2101)
* Add --prefetch-experts to stream mmap'd MoE experts into page cache

* Drop fds, fault experts in with MADV_POPULATE_READ instead of pread

* Remove stale note about pread workers

* Move MoE prefetch behind ggml_backend_prefetch_* wrappers

* Cleanup stale comments

* Add --prefetch-experts-threads, drop GGML_MOE_PREFETCH_THREADS env var
2026-07-11 10:43:42 +03:00
Kawrakow 6d30fa2fe6
Fused indexer top_k (CPU only) (#2098)
* Indexer topk op - CPU only

We do save memory, but it is somehow much slower than what we have on
main.

* Make it a command line option
2026-07-09 18:04:41 +03:00
Nexes the Elder 6198a356a8
Remove deprecated Kompute (Vulkan compute) backend (#2097)
* Remove broken kompute submodule (ghost - nulled config, corrupted tracking)

The kompute submodule at ggml/src/kompute had its .git/modules/kompute/config
completely zeroed out (null bytes). The submodule was non-functional and is
not used in this fork. Removed:
  - .gitmodules entry
  - .git/config [submodule kompute] section
  - .git/modules/kompute directory
  - ggml/src/kompute working tree

* Extensive removal of all Kompute code and references

Removed the entire Kompute Vulkan compute backend which was
unmaintained and superseded by the Vulkan backend:

Files deleted:
  - ggml/src/ggml-kompute.cpp (Vulkan compute backend implementation)
  - ggml/include/ggml-kompute.h (header)
  - ggml/src/kompute-shaders/ (34 SPIR-V shader source files)

Build system:
  - ggml/CMakeLists.txt: removed GGML_KOMPUTE option
  - ggml/src/CMakeLists.txt: removed compile_shader function, submodule
    add, shader compilation, stamp targets, and all KOMPUTE source refs
  - CMakeLists.txt: removed LLAMA_KOMPUTE deprecation alias

Source code:
  - ggml/src/ggml-backend.cpp: removed kompute reg decl and call
  - ggml/include/ggml.h: removed ggml_cpu_has_kompute() declaration
  - ggml/src/ggml.c: removed ggml_cpu_has_kompute() implementation
    and its reference in ggml_cpu_has_gpublas()
  - src/llama.cpp: removed #include, backend init, buffer type, model
    loading guard, and GPU offload check for Kompute
  - src/llama-model-loader.cpp: removed kompute include
  - common/common.cpp: removed cpu_has_kompute print
  - tests/test-c.c: removed kompute include guard
  - examples/llama-bench/llama-bench.cpp: removed kompute member,
    construction, field serialization, and display string
  - scripts/compare-llama-bench.py: removed kompute from key props,
    bool props, and pretty names
  - scripts/sync-ggml.sh: removed kompute file copy lines
  - scripts/sync-ggml-am.sh: removed kompute path mappings

Git submodule:
  - .gitmodules: removed kompute entry
  - .git/config: removed [submodule kompute] section
  - .git/modules/kompute: removed
  - ggml/src/kompute: removed (working tree)
2026-07-08 10:01:01 +02:00
Kawrakow 05cba319e0
GLM DSA: reduce indexer cache size (#2093) 2026-07-07 09:35:42 +02:00
Kawrakow a0859ce5ed
GLM-DSA: add ability to use quantized indexer cache (#2075)
* GLM-DSA: improve TG performance even more

* Fix crash when using MTP

* GLM-DSA: much better PP performance (CPU-only)

* GLM-DSA: allow for quantized indexer cache
2026-07-06 12:04:15 +02:00
mb8565 9da563d09d
deepseek2 : GLM-DSA sparse attention (lightning indexer), --dsa off by default (#2045)
* Add GLM-5.2/DeepSeek-V3.2 DSA lightning indexer (batch-local, single-seq prefill)

Implements the sparse top-k "lightning indexer" attention for LLM_ARCH_GLM_DSA
in build_deepseek2_layer_attention (ik's deepseek2 graph).

What it does (per layer, gated on model.arch==GLM_DSA && indexer_attn_q_b):
- indexer_q = indexer_attn_q_b(q_lora latent), split rope(64)/nope(64), NEOX-rope
  the pe part, concat. indexer_k = indexer_attn_k(attn_norm out), LayerNorm w/ bias,
  same rope/concat (single key head, MQA).
- scores = relu(indexer_k . indexer_q), scaled per-head weights (indexer_proj),
  summed over heads, + base causal mask, then ggml_top_k(min(top_k, n_tokens)).
- sparse mask: ggml_fill(-inf) -> ggml_set_rows(0) at top_k positions -> + causal,
  used in the soft_max_ext attention path (-mla 1 -fa 0) instead of KQ_mask.

Simplifications (intentional, proven sound):
- Batch-local: no indexer KV-cache. Indexer keys are the current batch tokens.
- Walsh-Hadamard transform omitted: orthonormal rotation, (Hq).(Hk)==q.k, no score change.

Validation (GLM-5.2-UD-IQ2_M, 3x P100, -mla 1 -fa 0):
- Compiles clean (CUDA sm_60); loads and runs.
- c512 -b512 (n_seq=1) PPL = 2.7760, byte-identical to dense baseline (indexer
  disabled) = 2.7760, all 8 chunks match -> indexer is an exact no-op when
  top_k>=n_tokens. Proves correctness-preservation.
- 3105-token prompt completion (top_k=2048 < 3105 -> indexer ACTIVELY masks):
  prompt-eval produces coherent, accurate continuation, identical to dense for the
  prompt+early-gen tokens. No NaN/crash. Confirms the masking path works in prefill.

Known limitations (documented follow-ups, NOT handled):
- Single-sequence prefill only. Multi-sequence batches (n_seq>1, e.g. perplexity
  default n_batch>n_ctx) and kv_head>0 (decode) break the batch-local key->slot
  mapping. n_seq>1 -> NaN (use n_batch==n_ctx). Decode (kv_head>0): each generated
  token sees only itself as an indexer key, so generation degenerates into repetition
  after the prompt (dense A/B stays coherent) -- this is the decode-cache stub, the
  documented next step.
- Flash-attn path (-fa 1, F16 mask) still uses dense KQ_mask (soft_max path only).
- Decode indexer KV-cache + Hadamard cached-K storage not implemented.

Runtime gate: DSA_INDEXER_DISABLE=1 falls back to dense attention (for A/B).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* GLM-5.2 DSA indexer: decode-correct via persistent indexer-K cache

Make the lightning-indexer correct for DECODE (not just prefill). Previously the
indexer was batch-local, so a generated token only scored against itself and
generation degenerated. Now the indexer keys are cached across the full context.

Changes
- llama_kv_cache: add per-layer indexer-key cache `kr_l` [indexer_head_size, kv_size]
  (F16, MQA single head), allocated alongside the MLA latent cache for GLM_DSA.
- build_deepseek2_dsa_indexer: write the batch's (Hadamard-rotated) indexer keys to
  kr_l at kv_head, read back the full [128, n_kv] cached keys, and score the indexer
  queries against ALL past keys. Returns the full descending argsort of the scores.
- Walsh-Hadamard rotation of indexer q/k (cparams.dsa_indexer_hadamard, default on;
  filled in llama_set_inputs). Score-preserving; improves cached-K F16 precision.
- build_deepseek2_dsa_sparse_mask: rank-based full-coverage scatter (write a 0/-BIG
  penalty into EVERY key slot keyed by rank) instead of partial set_rows into a -inf
  fill — the CUDA in-place set_rows does not preserve an un-written base, which had
  corrupted decode when n_kv > top_k.
- Attention-sink force-inclusion (DSA_SINK, default 1): boost the first key(s) so the
  sink always survives top-k. The IQ2_M-quantized indexer under-ranks the sink, and
  masking it collapsed decode; with the boost, top_k=2048 over n_kv>2048 stays coherent.

ggml backend fixes (needed by the indexer)
- CUDA argsort: report unsupported when padded ncols > 1024 (one-thread-per-column
  bitonic launch limit) so the scheduler falls back to the CPU argsort. Fixes
  "invalid configuration argument" for top_k over a large n_kv.
- CUDA cpy/dup: support I32 -> I32 (top_k index copies / cross-backend moves).

Validation (GLM-5.2-UD-IQ2_M, 3xP100 + --cpu-moe, -mla 1 -fa 0)
- c512 PPL = 2.0743, byte-identical to dense (all 8 chunks): no-op path exact.
- Short-context decode (300 tok): coherent, identical to dense.
- Long-context decode (2521-tok prompt, n_kv>top_k, real masking of ~474 keys,
  120+ tok generated): coherent with the sink boost; dense A/B also coherent.

Gated behind arch==GLM_DSA + indexer tensors + kr_l cache; DSA_INDEXER_DISABLE=1
forces dense. Remaining: FA path still uses the dense KQ_mask; multi-sequence
(n_seq>1) batches; deepseek32 arch wiring.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* GLM-5.2 DSA indexer: wire sparse mask into the flash-attention path (-fa 1)

The DSA sparse top-k mask is now applied on the -fa 1 path (our serving config),
not just -fa 0 soft_max. c512 PPL on -fa 1 = 2.0743, byte-identical to dense
(no regression, indexer no-op exact at n_kv <= top_k). Gated arch==GLM_DSA with
DSA_INDEXER_DISABLE escape; -fa 0 path unchanged.

Long-context -fa 1 decode coherence (n_kv > top_k, mask actually biting) validation
is still running at commit time; the FA mask reuses the same full-coverage scatter
proven coherent on the -fa 0 decode path, so it should hold, but confirm before
relying on long-context -fa 1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* GLM-5.2 DSA indexer: UPDATE 4 — MLA-FA fix merged, FA path validated, multi-seq characterized

Document the re-validation after cherry-picking the MLA-FA vec-decode fix (5f18dcc0):

- FA path is ALIVE. Long-ctx -fa 1 decode (2521-tok prompt > top_k, mask actively
  biting) is now COHERENT at -mla 1 and -mla 3, vs the pre-fix degeneration into
  "0.0.0.0..." repetition. Matches dense (DSA_INDEXER_DISABLE) and -fa 0 controls.
- c512 -fa 1 PPL: indexer-ON == dense == 2.0854, byte-identical all 8 chunks (exact
  no-op when n_kv <= top_k; no regression). The 2.0743->2.0854 shift is the MLA-FA
  fix changing V accumulation, not an indexer artifact (ON==dense proves it).
- Indexer is feature-complete + validated for single-seq prefill+decode on both
  -fa 0 and -fa 1, at -mla 1 and -mla 3 (the R740 serving target).

Remaining PR gaps, characterized honestly:
- Multi-seq (n_seq>1) with active mask is BROKEN (n_seq=2 c4096 PPL 62.6 vs dense
  multi-seq 2.54 and single-seq indexer 3.05). No NaN/crash anymore. Root cause:
  the indexer uses a single scalar kv_head/n_kv for the whole ubatch; multi-seq
  needs per-sequence cache writes + per-sequence top-k. Fix deferred (structural).
- deepseek32 arch: N/A in this fork. DSA lives entirely under LLM_ARCH_GLM_DSA;
  there is no LLM_ARCH_DEEPSEEK32 enum. Documented the steps to add one if a real
  deepseek32 GGUF is ever served.

Also commit DSA_REFERENCE.md (verbatim mainline deepseek32/glm-dsa source, the port
reference), trimmed of a stray agent-handoff footer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* GLM-5.2 DSA indexer: per-sequence attention sink — fix multi-seq (n_seq>1)

UPDATE 5. The DSA lightning indexer was numerically broken for multi-sequence
batches once the top-k mask bites (n_kv > top_k): c4096 n_seq=2 PPL 62.6 vs
dense 2.54, while single-seq was fine. Root cause: the attention-sink
force-include boosted the GLOBAL key range [0, n_sink) by +1e20, which only
protects sequence 0's sink. With several sequences packed contiguously into one
ubatch (seq 0 at cells [0,n0), seq 1 at [n0,n1), ...), every non-first
sequence's sink lives at cell n0.. (not cell 0), got no boost, and was dropped
from top-k once the mask bites — collapsing that sequence (chunk[2]=61.2 while
chunk[1]=2.33).

The cache write and score/argsort were already per-sequence correct: tokens are
placed contiguously like the main K cache, and the base KQ_mask (filled from
kv_self.cells[i].has_seq_id) already drives cross-seq keys to -inf before
argsort. Only the sink was anchored at the wrong (global) cell.

Fix: replace the global arange sink boost with a per-graph input tensor
inp_dsa_sink {n_kv, n_tokens} (F32), filled on the CPU in llama_set_inputs from
kv_self.cells exactly like the KQ_mask:
  inp_dsa_sink[j,i] = 1e20 iff cell[i].pos in [0,n_sink) AND
                              cell[i].has_seq_id(seq_of_query_j), else 0
so each query force-includes only its OWN sequence's sink. For a single
contiguous sequence from pos 0 this is exactly the old "cell index < n_sink"
set with the same magnitude, so n_seq==1 is byte-identical.

Validation (3x P100, -ngl 99 --cpu-moe -mla 3 -fa 1, wikitext-2):
- c4096 n_seq=2 indexer chunk[2]: 61.2 -> 3.07 (== single-seq 3.05).
- c2048 topk=1024 (mask bites): n_seq=4 == n_seq=1 chunk-for-chunk
  (2.5005/2.6080/2.7759/3.1137 vs .../3.1138) -> multi-seq is numerically
  identical to processing each sequence alone.
- c512 n_seq=1 indexer ON == dense, all 4 chunks byte-identical (no regression).

n_seq=4 at full c4096 (n_kv=16384) OOMs the P100 compute buffer (capacity, not
correctness; n_seq=4 proven correct at c2048/n_kv=8192).

GLM-5.2 DSA indexer is now sequence-correct for n_seq>=1, prefill+decode,
soft_max+FA, -mla 1/-mla 3. Fully general and PR-ready.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* GLM-5.2 DSA indexer: UPDATE 6 — serving-correctness (kr_l maintained across shift/defrag/seq-ops; per-seq sink on first-present pos)

An adversarial review found the indexer was proven on the perplexity path but
not the serving path: the persistent indexer-K cache kr_l was written/read but
never *maintained* by the KV-cache mutators, and the attention sink anchored on
absolute pos<n_sink (wrong after multi-turn seq_rm). This closes those gaps and
pins down what is actually reachable on the MLA model.

kr_l maintenance:
- build_k_shift (llama-build-context.cpp): rotate the indexer keys by the same
  per-cell delta as the main K. The cached key is H*concat(RoPE(k_pe,pos),k_nope),
  so un-Hadamard (H sym/orthonormal => H*H=I) -> RoPE-delta the pe sub-block ->
  re-Hadamard. Exact because GLM-DSA has no rope-scaling metadata (ext_factor=0,
  attn_factor=1, freq_scale=1), so NEOX RoPE is pure/composable. Params mirror the
  forward indexer RoPE exactly (rope_factors=nullptr); no DEEPSEEK2 yarn-shift leak.
  Non-in-place (cont->rope->concat->re-Had->cpy), no aliasing. K-shift Hadamard
  input filled in llama_set_k_shift with the identical Sylvester construction.
- build_defrag: kr_l row-move mirrors the k_l move (defrag never changes pos, so
  no re-RoPE). max_moves divisor 6->9 *n_layer when the indexer cache is present.
- seq_rm/seq_cp/seq_keep are metadata-only (verified) so kr_l rows stay matched to
  cells; seq_add/seq_div set has_shift and route through K-shift. No seq-op change.

Per-seq sink (llama.cpp llama_set_inputs): anchor on each sequence's FIRST PRESENT
pos (min present pos over the scored n_kv span), not absolute pos<n_sink. After
multi-turn seq_rm drops a sequence's early tokens its earliest survivor has
pos>=n_sink; the absolute test would protect nothing. Fresh seq at pos 0 => min=0
=> byte-identical to the old behaviour.

Serving-shift finding (the whole point): a RoPE context-shift on this model is
REFUSED BY THE ENGINE. get_can_shift() returns false for all MLA models
(is_mla_model() includes GLM_DSA); llama_kv_cache_update returns 1 ->
"main : failed to eval". Reproduced AND isolated with a dense control
(DSA_INDEXER_DISABLE=1): dense fails identically at the same token. The failure is
pre-existing MLA engine behaviour, independent of the indexer. On the MLA path the
shift never happens, so the indexer's kr_l can never desync via K-shift; the
build_k_shift kr_l block is correct-and-dormant (documented loudly in code).

Validation (3x P100, -ngl 99 --cpu-moe -mla 3 -fa 1, GGML_CUDA_NO_PINNED=1,
numactl --interleave=all, wikitext-2):
- No regression: c512 n_seq=1 indexer ON == dense == 2.1957 +/- 0.12031,
  byte-identical all 4 chunks (2.2770/2.8741/2.3956/2.1957).
- Multi-seq: c4096 n_seq=2 chunk[1]=2.33 chunk[2]=3.07 healthy (== UPDATE 5;
  per-seq sink change did not regress).
- Serving shift: engine-refused for MLA, dense control fails identically.
- Independent adversarial review: GO, no correctness defect in the diff.
- Build clean (llama-cli, llama-perplexity, sm_60).

Comments updated (build_deepseek2.cpp): multi-seq+FA no longer limitations; sink
description matches per-seq min-pos anchoring; BIG=1e30 masks on both soft_max and
FA paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* GLM-5.2 DSA indexer: UPDATE 7 — FIX latent graph-reuse cache-fixup omission for the kr_l indexer cache

update_cache_copies() re-points the K/V cache writes to the current kv_head whenever a
compute graph is REUSED (can_reuse_graph reuses iff kv_self.n == prev->n_kv). The persistent
indexer-key cache write (kr_l) is a separate ggml_cpy whose destination view bakes kv_head at
graph-build time, and it was NEVER registered for that fixup. Under FA the cache pads to 256,
so consecutive single-token decode ubatches share the same padded n_kv and the graph IS reused;
without the fixup the kr_l write keeps landing in the first ubatch's slot and later ubatches
never populate their own recent index-key cells (those cells stay at the alloc-zeroed 0.0).
Structurally identical to the MiniMax MSA bug (fork commit 133d14c9).

Fix (mirrors the K/V cache_copies fixup, same shape as MSA 133d14c9):
- llama-context.h: new std::vector<CacheCopy> dsa_cache_copies.
- llama.cpp ctor: resize dsa_cache_copies to n_layer (null entries -> no-op when DSA off).
- build_deepseek2.cpp: register the kr_l ggml_cpy as dsa_cache_copies[il] = {kr_cpy, kr->nb[1]}.
- llama.cpp update_cache_copies(): re-point each registered cpy view_offs = kv_head*step and
  patch src[1]->data/data, exactly like K/V, with the c.cpy->view_src == kv_self.kr_l[il]
  (+ null/op) guard the MSA fix omitted. soft_max / non-DSA paths byte-identical.

Validation (GLM-5.2-UD-IQ2_M, 3x P100 -ngl 99 --cpu-moe -t 32, NO_PINNED, P2P-disable patch
re-applied to get a working multi-GPU baseline — see UPDATE 7.3; that patch was lost in the
upstream rebase and is required separately):
- c512 -fa1 -mla3 indexer ON: 2.1983 (== prior baseline; build healthy).
- Long-ctx FA decode, 2735-tok recall prompt, -mla3 -fa1 temp0, reuse ON (default): coherent,
  correct deep-context recall ("Dr. Mariana Velasquez ... Daniel Okonkwo") on BOTH the fixed and
  the unfixed binary.
- ub128 PPL -fa1 -mla3 reuse ON, unfixed: 1.7239/1.8211/2.1888/2.4517, healthy (no inflation).

Honest scope: the bug is real in code but LATENT for GLM-DSA at its configured top_k=2048
(permissive selection keeps the genuinely-attended recent blocks even when reuse leaves some
recent index-key cells stale), unlike MSA's tighter top-k where it inflated PPL ~2x. The fix is
correct and prevents the latent corruption from biting at any tighter top_k / longer ctx /
future serving config. The pre-P2P-patch "nan" seen at ub128 was P2P corruption, not this bug.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* GLM-DSA: convert sparse-attention control from env vars to CLI args (off by default)

Implements ikawrakow's direction from discussion #2040: the DSA sparse
indexer must be controllable via command-line argument (not environment
variables), and must be OFF by default for now.

Control surface, before -> after:
  DSA_INDEXER_DISABLE (env, inverted: on-by-default)  -> --dsa / -dsa
      (cparams.dsa, default false; opt-in, dense-by-default)
  DSA_TOPK_OVERRIDE   (env)                            -> --dsa-top-k N / -dsatk N
      (cparams.dsa_top_k, default -1 == model's configured indexer_top_k)
  DSA_HADAMARD_DISABLE, DSA_SINK (env)                 -> kept as DEBUG-ONLY env
      knobs (clearly commented; no CLI surface, not system on/off controls)

Plumbing mirrors existing boolean/int feature flags (-mla, -khad):
  include/llama.h        llama_context_params {bool dsa; int dsa_top_k;}
  src/llama.cpp          default_params (false / -1); cparams assignment
  src/llama-cparams.h    llama_cparams {bool dsa=false; int dsa_top_k=-1;}
  common/common.h        gpt_params {bool dsa=false; int dsa_top_k=-1;}
  common/common.cpp      arg parse + help text + cparams copy
  src/graphs/build_deepseek2.cpp  gate now checks cparams.dsa instead of
      getenv; top-k override reads cparams.dsa_top_k. Stays arch-gated to
      LLM_ARCH_GLM_DSA. When --dsa is off (default) the indexer function is
      never called -> existing dense MLA path, byte-identical to no-feature.

Validation (GLM-5.2-UD-IQ2_M, 3x P100, -ngl 99 --cpu-moe -mla 3 -fa 1,
wikitext-2, 4 chunks @ c2560):
  --dsa OFF (default, dense):              PPL 2.4151  (graph nodes 4166)
  --dsa ON, default top_k=2048:            PPL 2.4697  (graph nodes 8846)
  --dsa ON, --dsa-top-k 1024:              PPL 3.5107
Off-by-default runs the dense path; ON activates the indexer (node count
jumps, PPL shifts as the top-k mask bites once n_kv > top_k). No env var
is consulted for the primary on/off or the top-k knob.

Graph-parallel (-sm graph) interaction (the item ikawrakow flagged):
Under -sm graph the MLA layers are TP-split (wo->extra) and route to
build_deepseek2_tp_attention(), which contains NO indexer code. So --dsa
is silently a NO-OP under -sm graph: it does not error or crash, it runs
dense. Empirically, --dsa --dsa-top-k 1024 under -sm graph gives
PPL 2.4308 (chunks 1.6967/1.7906/2.1664/2.4308) -- the dense baseline
(2.4151), NOT the DSA top_k=1024 numbers (3.5107). The 0.016 delta is
f16 TP-reduce numerics, not DSA. Conclusion: DSA "works under deepseek2"
only on the non-TP (layer) path; serving DSA with -sm graph would require
wiring the indexer into the TP attention path (or a dedicated DSA arch).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* GLM-DSA: warn that --dsa is inactive under -sm graph/attn (TP path runs dense MLA)

The DSA lightning indexer is built only in the layer-mode (non-TP) attention
path. Under -sm graph / -sm attn the tensor-parallel attention path has no
indexer, so --dsa would silently run dense MLA. Emit a clear one-time
LLAMA_LOG_WARN at context creation instead of degrading silently.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* GLM-DSA: drop in-tree dev reference docs from the PR branch

DSA_REFERENCE.md and the R740 progress note are development scratch, not
part of the submission. Remove them so the PR diff is code-only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* GLM-DSA: fix CPU-only crashes in the sparse-attention path

PR #2045 adds GLM-DSA sparse attention but was validated on CUDA (--cpu-moe).
A CPU-only build (-ngl 0 --dsa) crashes in four spots where the CUDA backend
tolerates something the CPU backend does not. These make GLM-5.2 --dsa run
coherently on CPU; with --dsa off they are no-ops (DSA CPU path only).

1. set_rows into an F32 dest segfaults (ggml.c set_rows_f32):
   type_traits[F32].from_float is NULL, so the DSA sparse-mask scatter calls a
   NULL fn (segfault at ip=0). memcpy when the dest is F32. CUDA has a real F32
   set_rows path, so this only bit the CPU build.

2. ggml_add(F32 score, F16 mask) aborts on CPU (build_deepseek2_dsa_indexer and
   build_deepseek2_dsa_sparse_mask): under -fa 1 the dense KQ_mask is F16 and CPU
   add only accepts F32+F16 when src0 is F16. Cast the causal mask view to F32.
   CUDA's add accepts the mixed types.

3. dsa_fa_mask dim-1 concat must be F32 on CPU (build_deepseek2_dsa_fa_mask):
   CPU ggml_concat only supports F16 along dim 0; do the row (dim-1) concat in
   F32 then cast the result to F16. CUDA supports the F16 dim-1 concat.

4. indexer k_norm epsilon is 0 -> ggml_norm aborts (llama-hparams.cpp): the
   lightning-indexer k_norm is a non-RMS LayerNorm using f_norm_eps, but the
   GLM-DSA GGUF only carries the RMS eps so f_norm_eps stays 0
   (GGML_ASSERT(eps > 0)). Mirror the RMS eps. CUDA's norm doesn't assert on eps=0.

Validated: GLM-5.2 UD-Q4_K_M, single-socket Xeon w7-2475X, CPU-only (-ngl 0 --dsa)
- coherent at 49K+ ctx, correct 30K needle retrieval, prefill flat with length
(~32 tok/s, the O(L) DSA signature) vs the dense build's O(L^2) decline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* DSA: loop over attention heads + use builtin Hadamard

* DSA: ggml_blend

* DSA: remove a bunch of unnecessary ggml_cont

* DSA: fix CUDA blend - but something is still wrong

* DSA: use ggml_top_k instead of ggml_argsort when FA is ON

* CUDA: add CUB based argsort

* DSA: avoid graph leaves

* Various

* GLM-5.2 DSA: IndexShare (shared layers reuse full-layer top-k)

GLM-5.2's indexer_types marks 21 'full' layers that compute their own
lightning-indexer top-k and 57 'shared' layers that reuse the previous
full layer's top-k. This port computed an independent top-k on every
layer, which mis-selects keys on the 57 shared layers (the transformers
reference sets indexer=None on shared layers and reuses prev_topk).

Shared layers now reuse the most-recent full layer's selection. Full/
shared map derived from the config rule (full iff il<=1 or il%4==2),
which reproduces indexer_types exactly; loader can later override from
GGUF metadata. Built on #2063's tree; head-loop/ggml_hadamard/ggml_blend/
argsort/FA-mask unchanged.

4K PPL (unsloth IQ2_M, top_k 2048, CPU): DSA-on 3.1922 -> 2.7111, dense
2.6972 (~97% of the gap). top_k>=n_kv reproduces dense exactly. Single-
seq and 4x8 parallel decode coherent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Apply suggestion from @ikawrakow

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: mgkwill <168222+mgkwill@users.noreply.github.com>
Co-authored-by: Kawrakow <iwankawrakow@gmail.com>
2026-07-02 09:36:49 +02:00
Joel Farthing 615c3e11b8
DFlash: support gpt-oss drafts with attention bias (#2061)
Adds support for the z-lab gpt-oss DFlash drafts (e.g. z-lab/gpt-oss-20b-DFlash),
which use a Qwen3 backbone trained with attention_bias=true.

- dflash-draft loader: create optional attention bias tensors bq/bk/bv/bo
  (TENSOR_NOT_REQUIRED); the Qwen3.5/MiMo drafts have none and are unaffected.
- build_dflash: add those biases at the q/k/v/o projections (cross-context K/V
  and the noise block), each guarded so bias-free drafts are unchanged.
- Narrow the DFlash graph contract validator to reject only fused qkv biases
  (bqkv/bqk/bkv), which the graph still does not implement, and remove a dead
  duplicate of the validator in llama.cpp (the live copy is in llama-dflash.cpp).

The gpt-oss tokenizer recognition needed to convert the draft is a separate,
general gpt-oss conversion fix submitted independently.

Verified on gpt-oss-20b: coherent output and 42% draft acceptance
(~2.95 accepted tokens/cycle) at cross_ctx=128.

Co-authored-by: Joel Farthing <262452229+joelfarthing@users.noreply.github.com>
2026-06-30 09:02:18 +02:00
mb8565 c713bd599b
llama : fix CPU-only load crash on a CUDA build (device_mem out-of-bounds) (#2037)
Loading a model with no GPU layers on a binary built with CUDA crashes in
`llm_load_tensors`. The GPU-fit block is guarded by `if (device_count > 0)`, but
`device_count` comes from `model.splits`, which always has at least one entry
(`{1.0f}`). The memory array it indexes, `device_mem`, is sized by `model.devices`,
which is empty when no GPU is present or when the model is loaded with `-ngl 0`. So the
block runs with `device_count >= 1` and reads `device_mem[0]` out of bounds.

Repro: build with `-DGGML_CUDA=ON` on a host that has no usable GPU, or hide the GPUs
with `CUDA_VISIBLE_DEVICES=""`, then load any model. The load segfaults inside the fit
loop (confirmed with DeepSeek-V2-Lite-Q4_K_M). With a real GPU present `model.devices`
is non-empty even at `-ngl 0`, so the crash needs the empty-device case.

The fix is to also require `!model.devices.empty()` before entering the GPU-fit block.
CPU-only placement is already handled earlier, all layers go to the CPU when there are
no GPU layers, so skipping this block on a CPU-only load is correct.

GPU loads still take the block since `model.devices` is non-empty. CPU-only loads on a
CUDA build now finish and decode normally instead of crashing.

Co-authored-by: local-llm <local-llm@local-llm-R740.cruvis.org>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 08:47:19 +02:00
Kawrakow b84902d2ad
Split mode graph for dense Qwen35 MTP (#2027)
* WIP: Split mode graph for Gemma4 assistant

Something is not right - acceptance drops to nearly zero.

* Per model CUDA contexts

Still not working!?

* This works

The issue was that I was not correctly calculating the number
of KV heads for the split KV cache.

* Compiler warnings

* It is better to use llama_context pointers as keys

* Split mode graph for dense Qwen35 MTP
2026-06-25 11:12:22 +02:00
Kawrakow d5507e33ae
Split mode graph for dense Gemma4 assistant (#2022)
* WIP: Split mode graph for Gemma4 assistant

Something is not right - acceptance drops to nearly zero.

* Per model CUDA contexts

Still not working!?

* This works

The issue was that I was not correctly calculating the number
of KV heads for the split KV cache.

* Compiler warnings

* It is better to use llama_context pointers as keys
2026-06-24 18:29:32 +02:00
Kawrakow bf23a7599c
Avoid Gemma4 assistant strange tensor name warnings (#2023) 2026-06-24 11:23:22 +02:00
Joel Farthing 64fceb70bc
DFlash: use persistent FA-ready K/V cache (#1997)
* Prototype physical-order DFlash KV cache

(cherry picked from commit f9093d9ee57cf66f6ce44c42524158bb1449d1c9)

* Use persistent FA-ready DFlash KV cache

(cherry picked from commit cfed6ae456b5448ac0053fbd5994037af845a69a)

* Address DFlash review cleanup

---------

Co-authored-by: Joel Farthing <262452229+joelfarthing@users.noreply.github.com>
2026-06-22 16:49:35 +02:00
magikRUKKOLA 72440a19fc
on-demand tensor reload (#1989)
* host-swap tensor loop

the host-swap functionality is only triggered when the certain env. variables are declared

* target_include_directories tweak

* hot-swap tensor support

two intrusions:
1.) at the model loading to collect the snapshot
2.) the modification of the `/health` HTTP endpoint to be able to trigger the hot-swap via sending the `llama-server` the HTTP-request.
*both a braced by the specific env. variables

* hot-swap tensor support; graph invalidation

ggml_backend_cuda_invalidate_graphs export

* hot-swap tensor support

graph invalidation implementation;  extended debug output (commented out)

* llama_reload_changed_tensors export

* tensor hot-swap on-demand reload

cpu-only/hybrid/gpu-only with split mode layer/graph full support implementation

* docs

* reuse the gguf parsing from llama.cpp

gguf_init_from_file, gguf_find_tensor, ggml_get_tensor

* remove the manual scheduling for hybrid inference

* update docs

* tensor shape validation

* update docs

* update docs

accidentally wiped the previous changes;  so recovered them

* revert the GGML_CUDA_MAX_DEVICES to 16

* update llama_reload_changed_tensor

update llama_reload_changed_tensor, revert CMakeLists.txt

* update llama_reload_changed_tensor

* GGML_MAX_SRC

GGML_MAX_SRC compile-time definition support

* GGML_MAX_SRC

GGML_MAX_SRC compile-time definition support

* GGML_MAX_SRC

GGML_MAX_SRC compile-time definition support

* llama_reload_changed_tensor

update llama_reload_changed_tensor definition

* refactory

move the tensor-reloading implementation to llama-reload.cpp, llama-reload-info.h;  some bugfixes and code reduction

* revert

added back the missing newline

* update docs

* reload_info constructor

* bugfix: cpu-only

TODO: improve the working environment by compiling for multiple hardware configurations;  possibly make a test pipeline

* cpu-only bugfix

set the fix again after unsuccessful sync with main

* windows os compilation fix

#include <string>

* fix windows os build

error C2039: 'string': is not a member of 'std'

* remove dead file

* implement perplexity in server

* Revert "implement perplexity in server"
2026-06-22 16:36:34 +02:00
Kawrakow d47f484d29
Force Gemma4 assistant to be loaded on last GPU (#1999)
* Allow graph reuse for Gemma4 MTP

* Force Gemma4 assistant to be loaded on last GPU
2026-06-19 18:17:13 +02:00
Kawrakow 8369cf7412
Allow graph reuse for Gemma4 MTP (#1996) 2026-06-19 18:16:53 +02:00
Kawrakow b21653a56f
Fully remove any BLAS remnants (#2001)
* Fully remove any BLAS remnants

* Also these
2026-06-19 17:26:09 +02:00
gapeleon 4f220159b8
Fix (Gemma-4 Vision): Correct KQ mask fill for causal models in non-causal flash-attn mode (#1985)
When llama_set_causal_attn(false) is called on a causal model (e.g.
Gemma-4 during vision image decode), llama_set_inputs took the non-causal
else-branch (designed for pure embedding models).

That path wrote the F16 mask with stride n_tokens instead of n_kv, and iterated batch
indices rather than KV cache cells.

The result was that every image query row beyond the first was
written at the wrong offset, leaving stale -inf values from
previous decodes visible to the GPU kernel. Any conversation
that had built up prior KV mask data would produce all-inf attention scores
for most image tokens, collapsing softmax to NaN and aborting at sampling.

Resolves #1984
2026-06-17 16:52:45 +02:00
Kawrakow f9078e169b
Merge pull request #1970 from SamuelOliveirads/feat/dflash-implementation
Add DFlash support
2026-06-16 15:07:55 +02:00
SamuelOliveirads 6cae8c7ba2 clean logs 2026-06-14 21:07:57 -03:00
SamuelOliveirads 0d75eee35a remove duplicated code and unnecesary refactor 2026-06-14 16:02:02 -03:00
Jun Yamog 0df00b3b94 Add preliminary MiniMax-M3 support 2026-06-14 12:23:20 +00:00
BECCA-Labs 053202f97a fix: initialize rpc_device endpoint and device index before parsing 2026-06-13 16:13:44 -05:00
SamuelOliveirads 3a1d46c4d1 Merge remote-tracking branch 'origin/main' into feat/dflash-implementation
# Conflicts:
#	common/common.cpp
#	common/speculative.cpp
#	convert_hf_to_gguf.py
#	examples/server/server-context.cpp
#	examples/server/server-context.h
#	src/llama-arch.cpp
#	src/llama-arch.h
#	src/llama-model.cpp
#	src/llama.cpp
2026-06-13 17:27:52 -03:00
Farmadupe d1339249d7
Cleanup: Unify location of m-rope repacking for token and embd (#1924)
* unify location of rope-position-array rewriting prior to ubatching

* Reorder terms.
2026-06-12 08:27:50 +02:00
Joel Farthing 4a1e2eaa69
model: add Cohere2-MoE North Mini Code support (#1945)
* Add Cohere2 MoE North Mini Code support

* Fix Cohere2 MoE expert tensor emission

* Enhance Cohere2-MoE support by modifying tensor handling and configuration logic

* Fix Cohere2-MoE graph split reduce handling

---------

Co-authored-by: Joel Farthing <262452229+joelfarthing@users.noreply.github.com>
2026-06-10 15:28:27 +02:00
Kawrakow 366e478cb6
Bug fixes (#1940)
* Bug fixes

* More
2026-06-10 07:45:49 +02:00
Kawrakow 2768b62515
Split mode graph for Laguna (#1939) 2026-06-09 10:13:30 +02:00
Kawrakow 11c3546235
Support for alternative Gemma4 assistant (#1937) 2026-06-09 09:30:12 +02:00
Joel Farthing bbe1a511ee
model: add Poolside Laguna XS.2 support (#1911)
* llama: register Laguna architecture

* llama: add Laguna graph support

* llama: place Laguna MoE tensors for cpu-moe

* gguf: add Laguna metadata and tokenizer ids

* convert: support Poolside Laguna XS.2

* model: align Laguna RoPE and graph semantics

* model: align Laguna partial offload with review feedback

* model: localize Laguna SWA YaRN defaults

* model: localize Laguna SWA RoPE constants

---------

Co-authored-by: Joel Farthing <262452229+joelfarthing@users.noreply.github.com>
2026-06-08 18:33:12 +02:00
Farmadupe 6b9de3dbaa
Fix mrope application across chunk boundaries (Fixes #993 and #1902 -- part 2) (#1918)
* (qwen3vl) Correct calculation for injection point of deepstack image embeddings

INjection point for deepstack embeddings used Hyperparameter n_embd_inp(), which caused the hidden state to be double accounted for, causing an OOB array access. The correct accessor is n_embd()

* Fix m-rope when pipeline parallelism is enabled
2026-06-05 17:10:02 +02:00
SamuelOliveirads 08e4590dcb implement gpu argmax 2026-06-04 20:45:12 -03:00
Kawrakow 4406e637b5
Split mode graph for Mellum (#1920) 2026-06-04 15:20:41 +02:00
Joel Farthing dc51c6f9b2
Add Mellum2 architecture support (#1919)
Co-authored-by: Joel Farthing <262452229+joelfarthing@users.noreply.github.com>
2026-06-04 14:28:02 +02:00
SamuelOliveirads dc43cdf06b move dflash for it own file 2026-06-02 10:22:13 -03:00
SamuelOliveirads 3d73312d9d apply workspace support for KV cache 2026-06-01 09:55:34 -03:00