Skip to content

Add KV cache state extraction/restoration to quantized_qwen2 and quantized_qwen3_moe - #3784

Open
alytaphoenix wants to merge 4 commits into
huggingface:mainfrom
alytaphoenix:upstream-kv-cache-state
Open

alytaphoenix wants to merge 4 commits into
huggingface:mainfrom
alytaphoenix:upstream-kv-cache-state

Conversation

@alytaphoenix

Copy link
Copy Markdown

Adds explicit KV cache state extraction/restoration to quantized_qwen2::ModelWeights and quantized_qwen3_moe::GGUFQWenMoE, plus a clear_kv_cache on the latter (mirroring the method already present on quantized_qwen2/quantized_qwen3/quantized_llama).

Why

Both model types previously exposed no way to get attention state out of a live model or put a previously-saved state back in — only an implicit reset via index_pos == 0 on the next forward. That's enough for the common single-conversation server loop, but not for a caller that needs to persist a session's KV state across process restarts, or swap which conversation a resident model is serving without reprocessing every token from the start of the conversation.

What

Both new methods share the same shape and pairing convention so callers don't need per-architecture branching:

pub fn kv_cache_state(&self) -> Vec<Option<(Tensor, Tensor)>>;
pub fn set_kv_cache_state(&mut self, state: Vec<Option<(Tensor, Tensor)>>) -> Result<()>;

One entry per layer, in layer order. None means that layer hasn't been forwarded through yet. set_kv_cache_state errors (rather than silently partial-restoring) on a layer-count mismatch, since mismatched state from a different model/architecture should be rejected loudly.

GGUFQWenMoE::clear_kv_cache follows the existing quantized_qwen2/quantized_qwen3 pattern (iterate layers, clear the K/V pair) — it was simply missing for the MoE variant.

No behavior change for existing callers; all three methods are additive.

Testing

cargo build -p candle-transformers --features metal passes clean. This has also been exercised live in downstream code: extracting state after a multi-turn conversation, restoring into a freshly-loaded model, and confirming the restored KV tensors are bit-identical to the originals before continuing generation.

Alyta Phoenix added 2 commits July 26, 2026 22:15
…tized_qwen3_moe

Both model types kept their per-layer KV cache fields private with no
accessor beyond clear_kv_cache() (reset-only) -- neither exposed any
way to extract the current cache state or restore a previously-saved
one, so a caller couldn't persist and later resume attention state
without reprocessing every token from scratch.

Both new methods follow the same shape and pairing convention
(Vec<Option<(Tensor, Tensor)>>, one entry per layer, errors rather than
silently partial-restoring on a layer-count mismatch) so callers don't
need per-architecture branching to handle either backend's state.

@ivarflakstad ivarflakstad left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Code looks good.

Please remove the comments. Not only are they redundant, which already means they shouldn't be there - they are also incorrect.

For example

/// Resets every layer's KV cache -- for reusing one loaded model across
/// independent requests without reloading weights. Added for ratatoskr,
/// which serializes model access behind a mutex (see ratatoskr's
/// src/model/mod.rs); ratatoskr now also tracks warm sessions and only
/// calls this when actually starting a new/different conversation, not
/// unconditionally on every request.

We're not adding this for ratatoskr. We're adding it because we want to be able to clear the kv cache, regardless of model or system using candle.

This happens because the LLM you've used includes the implicit bias of the prompt / project in it's work.

Something to consider for future contributions as well ☺️

Review feedback (ivarflakstad): these comments named a downstream
project (ratatoskr) and its specific usage pattern, which doesn't
belong in a general-purpose candle contribution. Reworded to describe
what the code does for any caller, matching the style already used by
quantized_qwen2's equivalent functions and this same file's per-layer
versions.
@alytaphoenix

Copy link
Copy Markdown
Author

You're right, and sorry for the noise -- I should have caught that before pushing. Removed the ratatoskr-specific framing from both doc comments; they now just describe what the code does for any caller, matching the style of quantized_qwen2's equivalent functions. Will be more careful about this in future contributions. Thanks for catching it.

Addresses the review: the comments this PR introduced were redundant with
the method names and, as noted, not accurate. Removes all 30 of them across
quantized_qwen2.rs and quantized_qwen3_moe.rs.

An earlier commit on this branch only stripped the project-specific framing,
which was a narrower change than what was asked for -- the comments
themselves remained, including the `clear_kv_cache` one quoted in the
review.

Upstream's own `/// Clear the KV cache across all layers.` on
quantized_qwen2.rs is deliberately left alone; it predates this PR and is
not ours to remove.
@alytaphoenix

Copy link
Copy Markdown
Author

Thanks, and sorry for the delay — the earlier commit here only stripped the project-specific framing, which was narrower than what you asked for. The comments themselves were still in place, including the clear_kv_cache one you quoted.

All 30 doc comments this PR added are now removed, across both quantized_qwen2.rs and quantized_qwen3_moe.rs. Since they're gone, the accuracy problem you flagged goes with them — but if it pointed at something wrong in the code rather than just the prose, I'd like to know, since that would be worth fixing separately.

One deliberate exception: /// Clear the KV cache across all layers. in quantized_qwen2.rs is left alone. It's upstream's own comment rather than one this PR introduced, so it didn't seem mine to delete.

@alytaphoenix

Copy link
Copy Markdown
Author

Removed all the doc comments this PR introduced (a63ba35a) — including the clear_kv_cache one you quoted. The one remaining KV-cache comment on quantized_qwen2.rs predates this PR (already on main), so left that alone. Ready for re-review.

@alytaphoenix

Copy link
Copy Markdown
Author

Gentle ping — the doc comments you flagged were all removed in a63ba35a back in August, so the CHANGES_REQUESTED here should be clearable whenever you have a moment. Happy to rebase if it's drifted.

This branch has not been deployed

No deployments
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.

2 participants