From 70013d030c12c4bb9aefe891b1bfa0269fdfaf05 Mon Sep 17 00:00:00 2001 From: vladimir-voinea Date: Thu, 27 Aug 2026 20:19:10 +0300 Subject: [PATCH] qwen4exp: keep the QSA indexer cache in lockstep with the attention cache The indexer cache (llama_memory_hybrid_idx::mem_idx) was a separate llama_kv_cache with its own cell metadata (positions, sequence ownership, used-set). Although init_batch hands it the attention cache's slot infos, the two caches' cell state can drift apart under server slot management (seq_rm on slot release): the indexer drops cells the attention cache keeps, so used_max_p1 -- and therefore get_n_kv() -- diverge, and the graph builder asserts in llama_model_qwen4exp::graph::graph: GGML_ASSERT(mctx_idx->get_n_kv() == inp->mctx->get_attn()->get_n_kv() && "the indexer cache must track the attention cache cell for cell") This aborts deep-context (QSA budget exceeded) and multi-slot unified-KV generation. Fix: construct the indexer cache with the attention cache as `mem_other`, so it shares the attention cache's cell metadata (v_cells_impl) via the existing `other` mechanism. The two then agree cell-for-cell by construction and can never drift. The indexer keeps its own K/V tensors (the share callback is null), and the attn_rot/n_embd_head values inherited from the attention cache are harmless because the indexer path never uses them (cpy_k stores raw keys). Fixes #27780 --- src/llama-memory-hybrid-idx.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/llama-memory-hybrid-idx.cpp b/src/llama-memory-hybrid-idx.cpp index d4e59d77e570..20ed9c1f193a 100644 --- a/src/llama-memory-hybrid-idx.cpp +++ b/src/llama-memory-hybrid-idx.cpp @@ -52,10 +52,17 @@ llama_memory_hybrid_idx::llama_memory_hybrid_idx( LLAMA_LOG_INFO("%s: creating indexer KV cache, size = %u cells\n", __func__, kv_size); + // the indexer cache is a side buffer addressed cell-for-cell by the attention + // cache: it must track the attention cache's cells exactly or QSA top-k reads + // the wrong cells. sharing the attention cache's cell metadata (v_cells_impl) + // via `other` makes the two agree by construction -- the indexer keeps its own + // K/V tensors but sees the same positions/sequences/used-set as attention, so + // used_max_p1 and hence n_kv can never drift apart. seq ops on the indexer + // become no-ops (the shared cells are managed by the attention cache). return new llama_kv_cache( model, hparams_idx, type_k, type_v, v_trans, offload, unified, kv_size, n_seq_max, n_pad, n_swa, swa_type, - nullptr, filter_idx, nullptr, nullptr, "idx_"); + get_mem_attn(), filter_idx, nullptr, nullptr, "idx_"); }()) {} llama_memory_context_ptr llama_memory_hybrid_idx::init_batch(llama_batch_allocr & balloc, uint32_t n_ubatch, bool embd_all) {