@@ -248,6 +248,7 @@ llama_kv_cache::llama_kv_cache(
248248 : nullptr ;
249249 if (k_idx) {
250250 ggml_format_name (k_idx, " cache_k_idx_l%d" , il);
251+ msa_strict_slots = (n_stream == n_seq_max);
251252 }
252253
253254 std::vector<ggml_tensor *> k_idx_stream;
@@ -1025,6 +1026,11 @@ llama_kv_cache::slot_info llama_kv_cache::find_slot(const llama_ubatch & ubatch,
10251026
10261027 uint32_t head_cur = v_heads[seq_to_stream[seq_id]];
10271028
1029+ // MSA block selection assumes slot == logical position (append-only streams), which Head-based placement can technically violate after tail trims
1030+ if (msa_strict_slots) {
1031+ head_cur = 0 ;
1032+ }
1033+
10281034 // if we have enough unused cells before the current head ->
10291035 // better to start searching from the beginning of the cache, hoping to fill it
10301036 if (head_cur > cells.get_used () + 2 *n_tokens) {
@@ -1142,6 +1148,15 @@ void llama_kv_cache::apply_ubatch(const slot_info & sinfo, const llama_ubatch &
11421148
11431149 const auto idx = sinfo.idxs [s][ii];
11441150
1151+ if (msa_strict_slots && (llama_pos) idx != ubatch.pos [i]) {
1152+ LLAMA_LOG_ERROR (" %s: MSA slot/position invariant violated: "
1153+ " writing pos %d into cell %u (stream %u). The indexer cache "
1154+ " would desync and block selection would silently corrupt. "
1155+ " This is a bug, please report it with reproduction steps.\n " ,
1156+ __func__, ubatch.pos [i], idx, sinfo.strm [s]);
1157+ GGML_ABORT (" MSA: slot != pos" );
1158+ }
1159+
11451160 if (!cells.is_empty (idx)) {
11461161 assert (cells.seq_count (idx) == 1 );
11471162
0 commit comments