Fix over-wide KV states from recorded sliding window cache updates - #48280
Open
VaggelisGian wants to merge 1 commit into
Open
Fix over-wide KV states from recorded sliding window cache updates#48280VaggelisGian wants to merge 1 commit into
VaggelisGian wants to merge 1 commit into
Conversation
With assisted or speculative decoding, DynamicSlidingWindowLayer runs with past recording active so that crop can roll back rejected drafts. While recording, update stored and returned the unbounded concatenation, but get_mask_sizes kept advertising sliding_window - 1 + query_length columns for the attention mask. Drafting bursts update the layer once per token with no crop in between, so the second and later tokens of a burst handed attention more key/value columns than the mask has: eager attention fails with a size mismatch and SDPA silently attends beyond-window states, making assisted decoding crash or diverge on any model whose sequence reaches the sliding window. Return only the trailing advertised window while recording; the full buffer stays stored internally for crop to roll back, and paths without recording are unchanged. Tests: a layer-level test pins returned width to get_mask_sizes across consecutive uncropped updates, an end-to-end test checks assisted decoding stays equal to greedy search with a prompt past the window, and a multi-token draft burst under eager attention crashes before the fix. Test Plan: python -m pytest tests/utils/test_cache_utils.py -k "recording_returns_advertised" -q --no-header -p no:cacheprovider fail-before: 1 failed (returned 5 != 4), pass-after: 1 passed python -m pytest tests/generation/test_utils.py -k "multi_token_draft" ... fail-before: 1 failed, RuntimeError "The size of tensor a (13) must match the size of tensor b (6)" pass-after: 1 passed (3 consecutive runs) python -m pytest tests/utils/test_cache_utils.py::CacheCroppingTests -q --no-header -p no:cacheprovider 5 passed python -m pytest tests/generation/test_utils.py -k "assisted" -q --no-header -p no:cacheprovider 22 passed, 3 skipped
Contributor
CI recapDashboard: View test results in Grafana |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Assisted and speculative decoding crash or silently corrupt output for any model with a sliding-window attention layer whose sequence reaches the sliding window. The regression came from #47447, which let assisted decoding run on real
DynamicSlidingWindowLayercaches instead of forcing a full cache: while past-recording is active,update()stored AND returned the unbounded key/value concatenation, butget_mask_sizes()kept advertising onlysliding_window - 1 + query_lengthcolumns for the attention mask. Draft bursts callupdate()once per drafted token with no crop in between, so from the second token of a burst onward attention received more key/value columns than the mask has:RuntimeError: The size of tensor a (13) must match the size of tensor b (6) at non-singleton dimension 3, deterministically reproducible;Minimal reproducer (CPU, tiny model):
The default confidence gate (0.4) collapses most draft bursts to a single token, which is why the mismatch hides in casual use; any confident drafting model or a lower threshold exposes it.
The fix keeps the unbounded buffer stored internally for
croprollback but returns only the trailing advertised window while recording. Paths without recording are bit-identical;get_mask_sizes, masking, and crop semantics are untouched. After the fix every probed configuration shows zero width mismatches, no crashes on eager, and greedy-equal results where the model is fully windowed.One honest caveat: assisted-vs-greedy divergence can still occur when the MAIN model has a bare sliding window, because multi-token verify forwards give later query positions extra context through plain causal masks. That is a separate pre-existing mask-content trait this PR deliberately does not touch.
No upstream issue exists for this to my knowledge; I searched open issues mentioning dflash/sliding-window assisted crashes before preparing this.
Code Agent Policy
This PR was prepared with the help of a code agent and is disclosed here per the Agentic contributions section of CONTRIBUTING.md; I reviewed every changed line and re-ran all verification locally. The first-time-contributor confirmation above stays unticked because it would be false. No existing issue or PR covers this fix, so there is no coordination link or competing work to differentiate from.
Before submitting
Pull Request checks?
to it if that's the case.
Who can review?
@zucchini-nlp (generation) @Cyrilvallez (generate/caches)
Test plan