ATOM supports the EAGLE-family speculative decoding methods through the
--method eagle3 CLI flag. This guide covers both the original EAGLE 3 path
(in production since the K2.5 release) and the EAGLE 3.1 extensions added
in May 2026 (fc_norm, post-norm hidden state feedback, MLA draft variant).
For the framework that hosts both methods, see
serving_benchmarking_guide.md § 6.
EAGLE 3 is a self-distilled drafter that runs one transformer decoder layer
per speculative step over the concatenation of an embedding tensor and a
target-side hidden state. The drafter shares the target's tokenizer and
emits its own auxiliary projection (fc) over a few selected target hidden
layers (eagle_aux_hidden_state_layer_ids).
EAGLE 3.1 keeps the same --method eagle3 code path and adds two
backward-compatible toggles in the drafter HF config. They are detected via
getattr(...) with the EAGLE 3 default, so loading an existing EAGLE 3
checkpoint (no new fields) takes the original code path byte-for-byte.
Field on draft config.json |
Type / default | Behavior when set |
|---|---|---|
target_hidden_size |
int, default hidden_size |
Sizes the fc input fan-in per aux slot. Lets the draft project from a target whose hidden dim differs from the draft's. |
num_aux_hidden_states |
int, default len(eagle_aux_hidden_state_layer_ids) (else 3) |
Number of target hidden-state slots concatenated into the fc input. Multiplies the fc fan-in. |
fc_norm |
bool, default False |
When True, each aux slot is RMSNormed (one RMSNorm per slot) before concatenation and fc. When False, the legacy raw-concat path runs. |
norm_output |
bool, default False |
When True, the drafter feeds its post-norm hidden state to its own next speculative step instead of the pre-norm residual. The logits path always applies the final norm once. |
Together these target attention drift: under long contexts or out-of-distribution prompts, the drafter's attention shifts away from the sink token as speculative depth grows, and acceptance length collapses. EAGLE 3.1 reports up to 2× acceptance length on long-context production workloads and up to 2.03× per-user output throughput at concurrency 1 on Kimi-K2.6-NVFP4 + vLLM TP=4 (GB200). See the vLLM blog post and the original integration in vLLM PR #42764.
ATOM ships two EAGLE 3 drafter classes, both routed through
atom.spec_decode.eagle.support_eagle_model_arch_dict based on the draft
HF config's architectures[0] after Config.hf_config_override rewrites it
to the internal name:
| Target stack | Draft HF arch (in config.json) |
Internal class (in support_eagle_model_arch_dict) |
Source file | KV cache |
|---|---|---|---|---|
| MHA / GQA (Llama-style) | LlamaForCausalLMEagle3 |
Eagle3LlamaModel |
atom/models/eagle3_llama.py |
Independent draft pool via Eagle3DraftBuilder |
| MLA (DeepSeek V3 / K2.6) | Eagle3DeepseekV2ForCausalLM |
Eagle3DeepseekMLAModel |
atom/models/eagle3_deepseek_mla.py |
Piggybacks the target's MLA KV pool at layer_id = num_hidden_layers |
Both classes implement the same set of EAGLE 3.1 toggles. The MLA variant
reuses the MLA attention block from atom/models/deepseek_v2.py rather than
re-implementing it. Because its KV shape is identical to the target's, no
parallel Eagle3MLADraftBuilder is needed — the draft slots into the target
pool as one additional layer (accounted for by num_nextn_predict_layers).
To support post-norm feedback, forward() on EAGLE 3.1 drafters returns a
tuple:
hidden_for_logits, hidden_for_next_step = model(input_ids, positions, hidden_states)hidden_for_logits: pre-norm hidden state. The finalnorm+lm_headincompute_logits()applies on top of this, exactly as in EAGLE 3.hidden_for_next_step: post-norm hidden state whennorm_output=True, otherwise identical tohidden_for_logits. This is what the propose loop feeds back ashidden_stateson the next speculative step.
EagleProposer.propose() (atom/spec_decode/eagle.py) unpacks the tuple via
isinstance(model_out, tuple), so legacy drafters returning a single tensor
continue to work without changes.
python -m atom.entrypoints.openai_server \
--model /path/to/Kimi-K2.6-MXFP4 \
--trust-remote-code \
-tp 8 \
--kv_cache_dtype fp8 \
--method eagle3 \
--num-speculative-tokens 3 \
--draft-model lightseekorg/kimi-k2.6-eagle3.1-mlaThe draft config.json should expose fc_norm: true, norm_output: true,
kv_lora_rank (signals MLA), and an
eagle_config.eagle_aux_hidden_state_layer_ids list. These drive the routing
and toggles automatically — no extra CLI flags.
python -m atom.entrypoints.openai_server \
--model /path/to/Kimi-K2.5-MXFP4 \
--trust-remote-code \
-tp 8 \
--kv_cache_dtype fp8 \
--method eagle3 \
--num-speculative-tokens 3 \
--draft-model /path/to/kimi-k2.5-eagle3The legacy draft's config.json has none of the new fields, so all getattr
toggles fall through to their EAGLE 3 defaults. Behavior is byte-equivalent
to pre-EAGLE-3.1 ATOM.
ATOM's scheduler already emits per-window acceptance via the SpecStats
class (atom/model_engine/scheduler.py). With
num_speculative_tokens=3, a [MTP Stats] line appears every 1000 decode
steps in the server log:
[MTP Stats] acceptance: 59.62% avg toks/fwd: 2.79 dist 0/1/2/3: 21.69/19.53/16.97/41.80%
No instrumentation needed — read these directly from the server's stdout log
(docker logs <container> or the tee target you launched it under).
Measured 2026-05-27 on the rocm/atom-dev:latest container, TP=8, MXFP4
targets, gsm8k 5-shot, 1319 samples.
| Setup | gsm8k 5-shot | Acceptance | Avg toks/fwd | Dist 0/1/2/3 |
|---|---|---|---|---|
K2.5 MXFP4 + kimi-k2.5-eagle3 (legacy path) |
0.9356 (±0.0068) | 67.96% | 3.04 | 14.95 / 16.30 / 18.68 / 50.07% |
K2.6 MXFP4 + kimi-k2.6-eagle3.1-mla |
0.9393 (±0.0066) | 59.62% | 2.79 | 21.69 / 19.53 / 16.97 / 41.80% |
The K2.5 legacy run is the backward-compat probe — it must match the pre-EAGLE-3.1 K2.5 baseline. The K2.6 run shows EAGLE 3.1 functional on the new MLA draft without regressing correctness vs. the legacy path.
The lower acceptance rate on K2.6 vs K2.5 is not a regression — the two spec pairs are not directly comparable (different targets, different draft training maturity), and gsm8k is a short-context structured-reasoning task rather than the long-context production workload EAGLE 3.1 was designed for. A direct K2.6-target ablation requires an additional K2.5-style EAGLE 3 draft sized to K2.6, which is not yet publicly available.
- Adding a new MLA target's EAGLE 3.1 draft: register an HF arch rewrite
in
Config.hf_config_override(atom/config.py), add the entry tosupport_eagle_model_arch_dict(atom/spec_decode/eagle.py), and reuseEagle3DeepseekMLAModelif the target's MLA head dims match. Sizing per draft is automatic via the fourgetattrtoggles in § 1. atom_configcopy semantics inEagleProposer.__init__: the draft config is constructed by shallow-copyingatom_configand then isolatinghf_config+compilation_config. Avoidcopy.deepcopy(atom_config)— models like K2.6 attachcuda.Streamobjects to the config tree from custom HF modeling code, andStreamcannot be pickled.mtp_start_layer_idxdispatch inatom/model_engine/model_runner.pybranches onspeculative_config.method == "eagle3", not on the presence ofeagle3_draft_builder. MLA drafts piggyback the target pool and so have no draft builder, but they still need the layer-index offset.- Do not modify
@support_torch_compile-decorated model files for EAGLE-related changes — instrument at call sites (EagleProposer.propose,ModelRunner.run_model) instead. See the projectCLAUDE.md.
- vLLM integration PR (merged main, ships v0.22.0): vllm-project/vllm#42764
- TorchSpec training PR: lightseekorg/TorchSpec#97
- vLLM blog: https://vllm.ai/blog/2026-05-26-eagle-3-1
- Kimi K2.6 EAGLE 3.1 draft model: https://huggingface.co/lightseekorg/kimi-k2.6-eagle3.1-mla
- Original EAGLE repository: https://github.com/SafeAILab/EAGLE