nemo_automodel.components.speculative.serve_vllm
nemo_automodel.components.speculative.serve_vllm
Serve an Automodel-trained EAGLE-3 / P-EAGLE / DFlash drafter with vLLM.
This is the vLLM companion to serve_sglang. It exists because the
parallel-drafting (P-EAGLE) head produced by the EAGLE-3 recipe with
parallel_drafting: true is not servable by SGLang today (serve_sglang
rejects it); its inference runtime is vLLM’s parallel-drafting path
(vLLM >= 0.16, https://github.com/vllm-project/speculators/pull/480). The same
entry point also serves a plain (non-parallel) EAGLE-3 draft under vLLM, and a
DFlash-family draft (DFlash / JetSpec; vLLM’s dflash method landed in 0.20).
The EAGLE drafter checkpoints produced by the recipes are written by the
consolidated checkpointer as an HF-style model/ directory
(model.safetensors + config.json), optionally nested one level deeper in
a consolidated/ subdir. The draft already stores the d2t / t2d
vocab-remap buffers inside its weights in the exact offset form vLLM expects
(target_id = draft_id + d2t[draft_id]), so — unlike the SGLang path — no
separate speculative-token-map file is emitted.
Three fixups bridge the Automodel on-disk format to what vLLM loads:
architectures: the recipe writes["LlamaEagle3DraftModel"](the Automodel class name), which is not in vLLM’s model registry. vLLM routes every EAGLE-3 draft onLlamaForCausalLMEagle3(PEagleDraftModel/Eagle3LlamaForCausalLMare registered aliases), so the field is rewritten.pard_token(P-EAGLE only): vLLM’s parallel-drafting proposer reads the masked-slot token id frompard_token/ptd_token_id/dflash_config.mask_token_id, but the recipe only writes the top-levelmask_token_id. The value is copied topard_token(mirroring vLLM’s own speculatorsupdate_peaglemapping).- weight keys: Automodel wraps the draft as
self.model, so its weights are saved with a leadingmodel.(model.embed_tokens.weight). vLLM’s loader re-addsmodel.to every non-top-level weight, so the prefix is stripped during a one-time export into avllm_export/subdir (the source checkpoint is left untouched). A draft whose weights are already vLLM-standard skips the export and only gets the config fixups in place.
The script then shells out to python -m vllm.entrypoints.openai.api_server
with the right --speculative-config.
NOTE — vLLM is NOT bundled with the NeMo-AutoModel container image and is
intentionally NOT declared in pyproject.toml. To use this entry point,
install it yourself into the same environment:
uv pip install “vllm>=0.16”
Refer to https://github.com/vllm-project/vllm for the version matching your CUDA / PyTorch stack. If vLLM is missing this script exits with a clear install hint rather than crashing on import.
Typical usage (after training produces a P-EAGLE checkpoint at
./outputs/peagle/checkpoints/epoch_2_step_44326):
python -m nemo_automodel.components.speculative.serve_vllm
—target Qwen/Qwen3-8B
—draft ./outputs/peagle/checkpoints/epoch_2_step_44326
—num-speculative-tokens 8
--num-speculative-tokens defaults to the draft config’s num_depths (K),
so for a P-EAGLE head it can be omitted. Pass --print-only to inspect the
command without launching it; in that mode the on-disk architectures rewrite
is skipped and the printed paths reflect what a real launch would produce.
DFlash family: a draft trained by the DFlash / JetSpec recipes (the config’s
architectures is ["Qwen3DFlashDraftModel"]) is auto-detected and served
with vLLM’s dflash method — --method can be omitted. Its weights carry
no model. wrapper prefix and no embed/lm_head (vLLM shares the target’s), so
only the architectures rewrite (to vLLM’s registered DFlashDraftModel)
applies, and K defaults to block_size - 1 (a block is one anchor token plus
block_size - 1 mask slots). A JetSpec draft is the same on-disk format
trained with causal in-block attention: the recipe stamps
dflash_config.causal=true so vLLM matches it at inference; for a JetSpec
checkpoint trained before that stamp existed, pass --dflash-causal. A Domino
draft is rejected: its GRU correction head (prefix_gru / embed_proj) has
no vLLM runtime.
Module Contents
Functions
Data
_AUTOMODEL_DFLASH_ARCHITECTURE
API
Build the vLLM --speculative-config dict for a resolved draft.
parallel_drafting is a SpeculativeConfig field that vLLM defaults to
False; it must be set here for a P-EAGLE head (the draft’s own
config.json flag is not auto-promoted to the speculative config). A
DFlash draft needs no such flag: vLLM forces parallel drafting for the
dflash method itself.
Verify the vllm package can actually be imported, else exit (code 2).
Return the config.json key changes needed for vLLM to load a DFlash-family draft.
Only the architectures rewrite (to vLLM’s registered DFlashDraftModel)
is required: the draft weights carry no model. prefix and vLLM reads
dflash_config.mask_token_id / dflash_config.target_layer_ids where the
recipe already writes them. --dflash-causal additionally stamps
dflash_config.causal=true (a JetSpec draft trained before the recipe
stamped it).
Return the weight tensor names in draft_dir.
Reads the names from model.safetensors.index.json when sharded, otherwise
from the single model.safetensors header (no tensor data is loaded).
Returns an empty list when neither is present / readable.
Materialize a vLLM-loadable copy of draft_dir in a vllm_export/ subdir.
The safetensors keys are remapped to strip the Automodel self.model
wrapper prefix, the config receives the architectures / pard_token fixups, and
the tokenizer assets are copied alongside. The source checkpoint is left
untouched. The export is cached and rebuilt only when stale.
True when export_dir holds a complete config + weights newer than the source weights or config.
Return the HF-style drafter directory under draft_path, if present.
Accepts the outer epoch_<E>_step_<S> directory, its model/ subdir, or
the consolidated checkpointer’s model/consolidated/ (and a bare
consolidated/) layout, as well as a directory that is already the HF
model dir. Returns the first candidate holding config.json + a weight
file, else None.
Return True if path contains an HF-style weight artifact.
True when the draft config identifies a DFlash-family draft (DFlash / Domino / JetSpec).
Load a drafter config.json into a dict.
Return (load_file, save_file) from safetensors.torch or exit with a hint.
True when any weight carries the Automodel self.model wrapper prefix.
Parse command-line arguments for the vLLM serve helper.
Strip the self.model wrapper prefix so vLLM’s loader (which re-adds it) resolves it.
Top-level tensors (lm_head.weight, d2t, t2d, mask_hidden) do
not carry the prefix and are returned unchanged.
Resolve the vLLM speculative method: explicit --method, else detect from the draft config.
Resolve K: CLI value, else block_size - 1 (DFlash) / num_depths (EAGLE).
Patch config_path in place with the keys vLLM needs (see _vllm_config_updates).
Takes the already-parsed config so the caller’s read is not repeated.
No-op when the config already satisfies vLLM. The write is staged through a
sibling .tmp file and finalized with os.replace so an interrupted
write cannot leave the destination half-truncated.
Return the config.json key changes needed for vLLM to load this draft.
A DFlash-family draft gets the DFlash fixups (see _dflash_config_updates).
For an EAGLE draft, two fixups bridge the Automodel on-disk format to what
vLLM reads:
architectures-> the vLLM-canonicalLlamaForCausalLMEagle3(the recipe writes the Automodel class nameLlamaEagle3DraftModel, which vLLM’s registry does not know).- for a P-EAGLE head,
pard_token(=mask_token_id). This mirrors vLLM’s own speculatorsupdate_peaglemapping; the parallel-drafting proposer readspard_token/ptd_token_id/dflash_config.mask_token_id, none of which the recipe writes — it only writes the top-levelmask_token_id.
Returns an empty dict when the config already satisfies vLLM.
Build the python -m vllm.entrypoints.openai.api_server argv for a config.
Validate the environment, resolve the drafter ckpt, then exec vLLM.
Returns the vLLM server’s exit code, or 2 if vLLM is missing.
Resolve a user-supplied drafter path to the directory and config vLLM expects.
Parameters:
A local path to a recipe checkpoint (the epoch_* dir, its
model/ / model/consolidated/ subdir, or the HF model dir
itself) or a Hugging Face Hub repo id.
When True, no on-disk architectures rewrite is performed and
the returned paths reflect what a real launch would produce.
When True, stamp dflash_config.causal=true on a
DFlash-family draft (a JetSpec checkpoint trained before the recipe
stamped it).
Returns: str
(draft_path, config) where draft_path is what vLLM’s