Skip to content

fix: checkpoint restore for hybrid/recurrent models#1976

Open
razlani wants to merge 1 commit into
ikawrakow:mainfrom
razlani:fix/checkpoint-restore-2
Open

fix: checkpoint restore for hybrid/recurrent models#1976
razlani wants to merge 1 commit into
ikawrakow:mainfrom
razlani:fix/checkpoint-restore-2

Conversation

@razlani

@razlani razlani commented Jun 16, 2026

Copy link
Copy Markdown

Fixes #1762. Overlaps with #1888 (same root issue) - my fix addresses an additional root cause and includes a prompt cache load guard.

Three bugs compound to make checkpoint restore non-functional on hybrid/recurrent models (Qwen3.6, Mamba). Every turn after idle forces a full re-prefill from token 0.

  1. Partial KV delete fails for recurrent models, fallback nukes all state including checkpoints before apply_checkpoint() runs
  2. apply_checkpoint gate condition excludes the recovery case (n_past zeroed by Offload Bitnet token embeddings to the GPU #1)
  3. Checkpoint search uses pos_min which has wrong semantics for hybrid models
  • Preserve cache_tokens/n_past/checkpoints when partial KV delete fails for recurrent models, instead of nuking everything
  • Add full_match_kv_lost path into apply_checkpoint() for when KV is empty but checkpoints exist
  • Use pos_max-based checkpoint matching for recurrent models
  • Lower checkpoint creation threshold 64 > 4 for recurrent models
  • Skip prompt cache load when slot already has a good prefix match (f_keep >= cache_ram_similarity)

Tested via (Qwen3.6-35B-A3B, RTX 5070 Ti, -np 1 -c 131072):

  • Before: full re-prefill every turn after idle (10-34s at 30-100k context)
  • After: checkpoint restored in 5-15ms, ~100 successful restores across multiple sessions
  • With client-side preserve_thinking: true: 0 full drops, >130 perfect cache hits in a session

Single-slot only. Multi-slot recurrent has separate issues (#1932/#1933).

- skip prompt cache load when slot has good match
Comment on lines +3581 to +3585
// Recurrent/hybrid models need checkpoints much sooner to avoid gaps that force
// full reprocessing - 4 tokens vs 64 for transformer-only.
const bool has_recurrent = llama_model_has_recurrent(llama_get_model(slot.ctx));
const int checkpoint_min_tokens = has_recurrent ? 4 : 64;
do_checkpoint = do_checkpoint && pos_min >= 0 && slot.cache_tokens.n_tokens() >= checkpoint_min_tokens;

@firecoperana firecoperana Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need to save a checkpoint when you only need to reprocess 4 tokens compared to 64?

Comment on lines +3517 to +3519
if (has_recurrent) {
return cur.pos_max <= pos_next;
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was a recent PR that we have to revert because this breaks the model when you regenerate the same conversation, so it does not work.

Comment on lines +3836 to +3840
if (has_recurrent_ckpt) {
// KV is cleared but keep token tracking for checkpoint restore.
// apply_checkpoint() will find a checkpoint at or before n_past
// and restore the recurrent state from it.
} else {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't think this is correct. We always need to set n_past to 0. No need to change this part.

Comment on lines +3491 to +3500
int32_t pos_min = llama_kv_cache_seq_pos_min(slot.ctx, slot.id);
const bool kv_empty = pos_min < 0;

// For recurrent/hybrid models: also enter checkpoint search when KV is empty
// (slot went idle and KV was cleared) but cache_tokens still reflects a prior
// decode and the new prompt fully covers that cached prefix.
const bool full_match_kv_lost = has_recurrent && kv_empty &&
slot.n_past > 0 &&
slot.n_past == slot.cache_tokens.n_tokens() &&
!slot.server_cached_prompt.checkpoints.empty();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you clear the kv cache, checkpoints should be cleared too, so there is nothing to search.

@SamuelOliveirads SamuelOliveirads left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far from fire's point I didn't find any other problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] qwen3next (Qwen3.6): context checkpoints never restored — full prompt re-processing on every turn

3 participants