Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
3bc36a5
[None][feat] add unified KV cache layout foundation
allisonlim-nv Oct 12, 2026
dad9f1f
[None][refactor] simplify unified KV cache layer specs
allisonlim-nv Oct 12, 2026
6ea5ad8
[None][test] remove unified KV cache foundation tests
allisonlim-nv Oct 12, 2026
090cbc0
[None][docs] remove unified KV cache design document
allisonlim-nv Oct 12, 2026
511975f
[None][feat] support per-layer storage layouts in KVCacheManagerV2
allisonlim-nv Oct 13, 2026
bfc8bc7
fix: normalize explicit KV cache spec windows
allisonlim-nv Oct 13, 2026
18c71c1
fix: use explicit KV cache dtype for cold-page codec
allisonlim-nv Oct 13, 2026
ae64dca
test: cover explicit KV cache dtype for cold-page codec
allisonlim-nv Oct 13, 2026
701abea
fix: honor physical slot strides in KV cache views
allisonlim-nv Oct 13, 2026
623816c
test: cover mixed KV cache odd-slot addressing
allisonlim-nv Oct 14, 2026
7cde28e
fix: restore KV cache subclass and test compatibility
allisonlim-nv Oct 14, 2026
4a3bb25
[None][test] fix mixed KV cache slot address arithmetic
allisonlim-nv Oct 14, 2026
9d0107b
[None][fix] align NVFP4 data and scale page layouts
allisonlim-nv Oct 15, 2026
e273a98
[None][fix] accept mixed KV cache pointer table layouts
allisonlim-nv Oct 16, 2026
f6b1d08
[None][fix] preserve sink and reuse policy in KV cache costing
allisonlim-nv Oct 16, 2026
eea6dcb
Merge main into unified KV cache manager foundation
allisonlim-nv Oct 16, 2026
b6db612
[TRTLLM-20434][feat] Dflash Unified KVCache
allisonlim-nv Oct 16, 2026
b757065
Merge branch 'main' into feat/dflash-unified-kv
allisonlim-nv Oct 17, 2026
39d814d
[None][fix] share the DFlash draft KV cache in disagg only with specu…
allisonlim-nv Oct 18, 2026
22cf25c
[None][fix] gate the disagg shared draft KV cache on matching tensor_…
allisonlim-nv Oct 18, 2026
feed06a
[None][test] cover the disagg shared draft KV gate for generation-onl…
allisonlim-nv Oct 18, 2026
d6aa95f
Merge main into feat/dflash-unified-kv
allisonlim-nv Oct 18, 2026
0319dc3
test: use default timeout for unified DFlash KV test
allisonlim-nv Oct 19, 2026
1f39128
fix: restore DFlash private arena fallback for separate KV caches
allisonlim-nv Oct 19, 2026
97fb214
fix: seed unified DFlash context lengths from bounded token counts
allisonlim-nv Oct 19, 2026
caf4c45
test: cover DFlash mixed-precision block tables
allisonlim-nv Oct 19, 2026
875eff2
fix: require matching attention DP for shared draft KV
allisonlim-nv Oct 19, 2026
212fdab
test: enable shared draft KV in DFlash integration test
allisonlim-nv Oct 19, 2026
5e418d1
Merge branch 'main' into feat/dflash-unified-kv
allisonlim-nv Oct 19, 2026
1302d43
fix: keep mixed-window DFlash caches separate
allisonlim-nv Oct 19, 2026
7a85a73
[None][feat] unify the standalone DSpark target and draft KV cache
allisonlim-nv Oct 20, 2026
c42ce5a
Merge main and resolve unified KV cache conflicts
allisonlim-nv Oct 20, 2026
c0bd339
Merge main into DSpark unified KV branch
allisonlim-nv Oct 22, 2026
238ff33
docs: clarify disaggregated draft KV opt-in
allisonlim-nv Oct 26, 2026
2bcd2b2
test: retain engine stats for DFlash acceptance length
allisonlim-nv Oct 26, 2026
633b010
Merge main into DSpark unified KV branch
allisonlim-nv Oct 26, 2026
76c4cef
fix: size shared DSpark KV from checkpoint block width
allisonlim-nv Oct 26, 2026
6a4fc09
fix: preserve shared draft KV selection intent
allisonlim-nv Oct 27, 2026
a11e8a2
Merge branch 'main' into feat/dspark-unified-kv-standalone
allisonlim-nv Oct 27, 2026
d2b0bb4
style: format DSpark block width helper
allisonlim-nv Oct 27, 2026
7402051
fix: align CuTe MLA per-layer page indices with buffer origin
allisonlim-nv Oct 27, 2026
e0171d6
test: cover CuTe MLA reads with nonzero layer offsets
allisonlim-nv Oct 27, 2026
4cd7c93
Merge main and resolve shared draft KV conflicts
allisonlim-nv Oct 27, 2026
e3c43bd
Merge main and resolve unified draft KV cache conflicts
allisonlim-nv Oct 30, 2026
4d3904a
test: split disaggregated draft KV environment fallback case
allisonlim-nv Oct 30, 2026
bd3e437
Merge main and relocate unified draft KV tests to pyexecutor
allisonlim-nv Oct 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 32 additions & 1 deletion docs/source/features/speculative-decoding.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ Reference: [DFlash: Distilled Flash Speculative Decoding](https://arxiv.org/pdf/
* `speculative_model`: Path or HuggingFace model ID for the DFlash draft model.
* `mask_token_id`: Token ID used as the mask token for parallel prediction. If not set, it is read from the draft model config.
* `target_layer_ids`: List of target model layer indices whose hidden states are captured for cross-attention in the draft model. If not set, read from the draft model config.
* `attention_backend`: Cross-attention backend for the draft model, independent of the drafter's standard attention modules. `"VANILLA"` (the default) uses FlashAttention with a contiguous context K/V cache and runs anywhere. `"TRTLLM"` uses TRTLLM-Gen FMHA (via FlashInfer) over a private paged context K/V cache and supports SM100/SM103 only. `"FA4"` uses the flash-attn CuTe DSL kernels on the same paged cache and supports SM90 only.
* `attention_backend`: Cross-attention backend for the draft model, independent of the drafter's standard attention modules. `"VANILLA"` (the default) uses FlashAttention with a contiguous context K/V cache and runs anywhere. `"TRTLLM"` uses TRTLLM-Gen FMHA (via FlashInfer) over a paged context K/V cache held by the KV cache manager (see [Draft KV cache](#dflash-draft-kv-cache)) and supports SM100/SM103 only. `"FA4"` uses the flash-attn CuTe DSL kernels on the same paged cache and supports SM90 only.

```python
from tensorrt_llm.llmapi import DFlashDecodingConfig
Expand All @@ -170,6 +170,37 @@ speculative_config = DFlashDecodingConfig(
llm = LLM("/path/to/target_model", speculative_config=speculative_config)
```

#### DFlash draft KV cache

With `attention_backend="TRTLLM"`, TRTLLM target attention, and the default
`KVCacheManagerV2`, the drafter's layers are allocated in the target's KV cache
manager. Target and draft layers share one page table, so one KV budget covers
both and each request's draft pages are allocated, padded and freed with its
target pages. The draft layers keep the drafter's own dtype, so an FP8 target KV
cache (`kv_cache_config.dtype: fp8`) is paired with a BF16 draft cache.

In disaggregated serving, direct worker launches require
`TRTLLM_DISAGG_SHARED_DRAFT_KV=1` on both the context and generation workers
to enable shared draft-KV transfer. The `disaggregated_mpi_worker` launcher
enables this setting automatically when both servers enable compatible
speculation and use the same `tensor_parallel_size` and
`enable_attention_dp`.

When sharing is enabled, the context server's draft pages are transferred to
the generation server with the target pages, so the drafter starts generation
with the prompt context instead of an empty one. This requires the draft layers
to have the target's rank-local KV-head count; the draft dtype and head
dimension may differ, except that an NVFP4 target KV cache requires an
identical draft layout. Both servers must use the same layout.

The drafter keeps a separate draft KV cache manager instead when any of the
following applies: context parallelism larger than 1, a per-layer KV-head count
on the target or drafter, a specialized KV cache manager (for example, hybrid Mamba
models), Gemma 4 hybrid attention, or, in disaggregated serving, a draft layout
that cannot be transferred with the target's. When the layers are shared, the
worker log reports `target and draft KV layers share one KV cache manager` at
startup.

[DFlash 2](https://inco.ai/blog/dflash2/) is also supported. The same `DFlashDecodingConfig` can be used for DFlash 2; no extra arguments are required.

[LiLiCorr](https://arxiv.org/abs/2608.20530) checkpoints use the same
Expand Down
21 changes: 21 additions & 0 deletions tensorrt_llm/_torch/attention/backends/fmha/cute_dsl_mla.py
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,12 @@ def _run_mla_decode(
params: FmhaParams,
kernel_dtype: torch.dtype,
) -> None:
from tensorrt_llm._torch.pyexecutor.kv_cache.kv_cache_manager_v2 import (
KVCacheManagerV2,
Role,
)
from tensorrt_llm.runtime.kv_cache_manager_v2 import BAD_PAGE_INDEX

attn = params.attn
meta = params.meta

Expand Down Expand Up @@ -680,6 +686,21 @@ def _run_mla_decode(
page_table = self._generation_page_table(
page_table_layer, meta.num_contexts, batch_size, layer_in_pool
)
if (
layers_in_pool == 1
and isinstance(meta.kv_cache_manager, KVCacheManagerV2)
and meta.kv_cache_manager._use_per_layer_page_tables
):
# V2 per-layer indices include the layer offset, but get_buffers()
# already starts at this layer's KEY buffer. Apply the offset once.
converter = meta.kv_cache_manager.impl.get_page_index_converter(
local_layer_idx, Role.KEY
)
layer_offset = int(converter.layer_offset)
if layer_offset:
page_table = torch.where(
page_table == BAD_PAGE_INDEX, page_table, page_table - layer_offset
)

# KVCacheManager exposes NHD pages as [num_pages, 1, page_size, 1, head_dim].
# The CuTe DSL kernel consumes a paged [page_size, dim, num_pages] view
Expand Down
23 changes: 23 additions & 0 deletions tensorrt_llm/_torch/disaggregation/native/peer.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,29 @@ def _check_peer_compatible(self, peer_ri: RankInfo) -> bool:
peer_ri.page_table,
)
AttentionPolicy.validate_peer_compatible(self_page_table, peer_ri.page_table)
if self._ri.num_draft_layers != peer_ri.num_draft_layers:
# The layer-count tolerance below would transfer only the target
# layers and leave the drafter's prompt KV unset on the receiver.
raise ValueError(
"PeerRegistrar: unified draft KV layer count differs "
f"(local={self._ri.num_draft_layers}, peer={peer_ri.num_draft_layers}); "
"both peers must use the same speculative decoding KV cache layout; "
"set TRTLLM_DISAGG_SHARED_DRAFT_KV identically on both workers."
)
if self._ri.num_draft_layers:
if self._ri.tp_size != peer_ri.tp_size:
raise ValueError(
"PeerRegistrar: a shared draft KV cache needs equal tp_size "
f"(local={self._ri.tp_size}, peer={peer_ri.tp_size}); "
"unset TRTLLM_DISAGG_SHARED_DRAFT_KV or use the same TP on both workers."
)
if self._ri.attention.enable_attention_dp != peer_ri.attention.enable_attention_dp:
raise ValueError(
"PeerRegistrar: a shared draft KV cache needs the same attention DP "
f"setting (local={self._ri.attention.enable_attention_dp}, "
f"peer={peer_ri.attention.enable_attention_dp}); unset "
"TRTLLM_DISAGG_SHARED_DRAFT_KV or match it on both workers."
)

self_layers = sum(self._ri.layer_num_per_pp)
peer_layers = sum(peer_ri.layer_num_per_pp)
Expand Down
6 changes: 6 additions & 0 deletions tensorrt_llm/_torch/disaggregation/native/rank_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ class RankInfo:
aux_meta: Optional[AuxBufferMeta] = None
page_table: Optional[KVCachePageTable] = None
bounded_replay_on_generation: bool = False
# Layers a unified KV cache manager owns for the drafter (layer specs with
# a DRAFT owner). They ride the target's pages, so both peers must agree.
num_draft_layers: int = 0

@property
def tp_size_per_dp_group(self) -> int:
Expand Down Expand Up @@ -114,6 +117,9 @@ def from_kv_cache_manager(
aux_meta=aux_buffer_meta,
page_table=build_page_table_from_manager(kvm),
bounded_replay_on_generation=getattr(kvm, "bounded_replay_on_generation", False),
num_draft_layers=(
len(kvm.draft_layer_ids) if getattr(kvm, "layer_specs", None) is not None else 0
),
)

@classmethod
Expand Down
Loading
Loading