nemo_automodel.components.speculative.dflash.draft_kimi_k3
nemo_automodel.components.speculative.dflash.draft_kimi_k3
DFlash draft model with a dense Kimi K3 MLA backbone, plus its build helpers.
The Qwen3 draft (draft_qwen3.py) documents the DFlash contract: the draft
predicts a whole block_size block in one non-causal forward whose keys and
values are [target-hidden context | noise block], with the block structure
supplied entirely by the attention mask built in
nemo_automodel.components.speculative.dflash.core.
This module keeps that contract and swaps the backbone for Kimi K3’s:
- MLA with Q-LoRA and a compressed KV latent. The attention subclasses the
target’s own :class:
KimiMLAAttentionso the draft’s projection layout stays in lockstep with the layers whose hidden states it consumes; only the forward is replaced, because the draft’s keys and values span the context as well as the queried noise block. - NoPE. K3 requires
mla_use_nope=True: no rotary is applied anywhere in its full-attention layers (theqk_rope_head_dimslice is a plain head-shared extension of the key). The draft therefore has no rotary embedding and ignoresposition_ids. - SiTU feed-forward and fp32-variance RMSNorm, reusing the target’s
KimiK3MLP/KimiRMSNormmodules.
The draft is always dense: K3’s KDA linear-attention layers, routed and shared
experts, MTP heads, and the learned attention-residual mixer live in the target
only. Because the draft has no FlexAttention path, it consumes the dense
additive DFlash mask (attention_backend='sdpa').
Module Contents
Classes
Functions
Data
API
Bases: KimiMLAAttention
Non-causal K3 MLA whose keys/values are [context | noise-block].
Inherits the target’s MLA projections unchanged and replaces only the
forward. Queries come from the draft (noise) tokens only. K3’s MLA emits one
K/V per query head (kv_b_proj produces num_attention_heads slices of
qk_nope_head_dim + v_head_dim), so there is no GQA group repeat and the
target’s _expand_key_value_groups is not needed here.
Attend the draft block over [context | noise].
Unlike the target’s eager forward, this runs
F.scaled_dot_product_attention: a DFlash block batch makes both the
query and the key axis sequence-scale (num_anchors * block_size by
sequence + num_anchors * block_size), so materializing the score
matrix is not affordable. The dense additive mask is passed through
unchanged, which keeps the visibility semantics identical.
Parameters:
Tensor of shape [batch, blocks * block_size, hidden];
the draft (noise) tokens, which are the queries.
Tensor of shape [batch, sequence, hidden]; the
projected target-hidden context prepended to the keys and values.
Additive mask of shape
[batch, 1, blocks * block_size, sequence + blocks * block_size],
or None.
Ignored; accepted so the decoder layer can forward extras.
Returns: torch.Tensor
Tensor of shape [batch, blocks * block_size, hidden].
Bases: Module
Pre-norm K3 MLA block over [context | noise] followed by a dense SiTU MLP.
Run one draft layer.
Parameters:
Tensor of shape [batch, blocks * block_size, hidden].
Tensor of shape [batch, sequence, hidden].
Additive mask of shape
[batch, 1, blocks * block_size, sequence + blocks * block_size],
or None.
Returns: torch.Tensor
Tensor of shape [batch, blocks * block_size, hidden].
Bases: Module
DFlash draft model: a small dense non-causal K3 MLA stack over [context | noise].
The draft owns no embedding table and no LM head: the DFlash trainer embeds
the [anchor, MASK, ...] blocks with the frozen target’s embed_tokens
and decodes this model’s output with the frozen target’s lm_head.
Predict the draft blocks’ hidden states.
Parameters:
Unused. K3’s MLA is NoPE, so the draft has no rotary embedding; the argument is accepted to keep the trainer’s call signature identical across DFlash drafts.
Additive DFlash mask of shape
[batch, 1, blocks * block_size, sequence + blocks * block_size].
Tensor of shape [batch, blocks * block_size, hidden].
Tensor of shape
[batch, sequence, len(target_layer_ids) * hidden].
Ignored.
Returns: torch.Tensor
Tensor of shape [batch, blocks * block_size, hidden].
Build a dense MLA DFlash draft config from a Kimi K3 target’s text config.
The draft consumes the target’s frozen embed_tokens / lm_head and
fuses its hidden states, so it keeps the target’s MLA dims, hidden size, and
vocabulary and only shrinks the depth. Everything the draft does not build is
switched off explicitly rather than left at the target’s value, so the
serialized draft config describes the draft and not the target: KDA linear
attention, routed and shared experts, MTP layers, and the learned
attention-residual mixer all stay in the target.
Parameters:
The Kimi K3 target’s text config (kimi_linear).
Number of draft decoder layers.
Depth of the target’s text backbone; recorded so a reloaded draft config still describes which target it was trained on.
DFlash block size.
The recipe’s DFlash block, carrying mask_token_id and
target_layer_ids (the target layers whose hidden states the draft
consumes, which set the fc input width).
The draft’s attention implementation. Recorded on the config for the serving runtime; the draft itself always attends over the dense additive mask.
Returns: KimiK3TextConfig
A KimiK3TextConfig describing the draft.
Raises:
ValueError: Iftarget_configis not a Kimi K3 text config.
Extra from_pretrained kwargs for a frozen Kimi K3 DFlash target.
Two things a Qwen3-shaped target does not need:
configpins the architecture to the text-onlyKimiK3ForCausalLM. A K3 checkpoint declares the multimodalKimiK3ForConditionalGeneration, which would additionally build the vision tower that DFlash never reads.backendselects the expert-parallel token dispatcher and the HF state-dict adapter (which also dequantizes an FP8 base checkpoint on load), mirroring the frozen large-MoE target backends the DSpark recipe builds.expertsdefaults totorch_mmrather thangmmbecause the latter needs the optionalgrouped_gemmpackage.attnis left ateagerand is inert —KimiK3ForCausalLMnever readsbackend.attn, since its MLA and KDA layers each have a fixed attention path — andgate_precisionis left unset because K3 already defaults it to fp32.
Parameters:
The recipe’s recipe_args mapping.
Returns: dict
Keyword arguments to merge into the target’s from_pretrained call.