nemo_rl.models.generation.vllm.draft_utils#
vLLM-side draft co-training helpers; vllm imports stay function-local.
Module Contents#
Functions#
Whether this worker’s engine needs the draft module-sharing disable. |
|
Keep the drafter’s embed_tokens/lm_head separate from the target’s. |
|
Whether a |
|
Whether this rank should own the co-trained speculator. |
|
Expected incoming |
|
Hard-error when the trainer’s |
|
Refuse to load draft embed/lm_head through target-shared modules. |
Data#
API#
- nemo_rl.models.generation.vllm.draft_utils.COTRAINED_SPECULATIVE_METHODS#
(‘dspark’, ‘dflash’, ‘eagle3’)
- nemo_rl.models.generation.vllm.draft_utils.DRAFT_DISABLE_MODULE_SHARING_ENV#
‘NRL_DRAFT_DISABLE_MODULE_SHARING’
- nemo_rl.models.generation.vllm.draft_utils.draft_module_sharing_disable_required(
- config: dict[str, Any],
Whether this worker’s engine needs the draft module-sharing disable.
Full-stream draft co-training refits the drafter’s trained embed_tokens/lm_head. Under load_format=”dummy” the pinned vLLM would alias those drafter modules to the target model’s (no checkpoint load ever marks them as owned), and the draft refit would then overwrite the policy’s serving weights through the alias. The method name alone doesn’t imply a full stream – Megatron block-drafter paths can use the same dspark/dflash method names with a headless exporter that relies on module sharing, just like the megatron eagle3 path does – so gate on _draft_full_refit (true only for Automodel co-training, which always streams the drafter’s entire state_dict) for all three methods.
- nemo_rl.models.generation.vllm.draft_utils.disable_draft_module_sharing() None#
Keep the drafter’s embed_tokens/lm_head separate from the target’s.
The pinned vLLM’s
load_dspark_model/load_dflash_model/load_eagle_modelalias the drafter’s embed_tokens and lm_head modules to the target model’s whenever the drafter’sload_weightshas not marked them as owned – which is always the case underload_format="dummy", where no checkpoint weights are read at startup. With draft co-training the refit stream carries traineddraft.embed_tokens/draft.lm_headweights, and loading them through the aliased modules silently replaces the POLICY model’s serving embed and lm_head with the draft’s. Forcing_should_shareto False makes the drafter keep the modules it built in__init__; refit fills them before the first generation, and CUDA graphs capture drafter-owned storage._should_shareis defined in the eagle utils module and imported by value into the dspark/dflash utils modules, so each module’s global must be rebound individually.A second, independent embed_tokens-sharing decision lives in
SpecDecodeBaseProposer._maybe_share_embeddings(used by the eagle3 proposer): it shares wheneverself.model.has_own_embed_tokensis falsy, a flag only ever set (to True) byprocess_eagle_weightwhen a weight literally named “embed_tokens” is loaded – which never happens underload_format="dummy". This is not reachable through_should_shareat all, so it needs its own no-op patch; skipping it entirely leaves each drafter holding the embed_tokens it built in__init__, matching the_should_shareoverride above.Must run before engine creation (drafter load and CUDA-graph capture).
- nemo_rl.models.generation.vllm.draft_utils._DRAFT_SKIPPED_KEY_SUBSTRINGS#
None
- nemo_rl.models.generation.vllm.draft_utils._is_full_draft_stream(draft_keys: set[str] | list[str]) bool#
Whether a
draft.*stream is the Automodel FULL drafter.The Automodel co-training path streams the drafter’s entire state_dict (always including per-layer keys like
layers.0.*, and normallyembed_tokenstoo), regardless of method (dspark/dflash/eagle3). The megatron eagle3 exporter instead streams a PARTIAL drafter – it intentionally omitsembed_tokensand aliases its single collapsed layer asmidlayer.*instead oflayers.0.*, relying on drafter module sharing at serve time.Neither signal alone is reliable, so the stream is treated as FULL unless it carries neither: a one-key batch slice carrying only
lm_head.weight– the genuine megatron partial path’s minimal payload – is read as partial, while a FULL stream missing justembed_tokensis still caught by its remaininglayers.*keys.
- nemo_rl.models.generation.vllm.draft_utils._draft_owns_speculator(
- model_runner: Any,
- method: Optional[str],
Whether this rank should own the co-trained speculator.
vLLM keeps the drafter on the last pipeline stage, so earlier stages legitimately have no speculator and must skip draft payloads; every rank owns it in single-stage layouts.
- nemo_rl.models.generation.vllm.draft_utils._expected_draft_keys(draft_model: Any, method: str) set[str]#
Expected incoming
draft.*keys derived from the drafter’s layout.Inverts the drafter
load_weightsname handling shared byQwen3DSparkForCausalLM/DFlashQwen3ForCausalLM/Eagle3Qwen3ForCausalLM: trainer names are prefixed withmodel.(exceptlm_head.*, andd2twhich maps todraft_id_to_target_id), and fused parameters load from their stacked components (qkv_proj <- q/k/v_proj, gate_up_proj <- gate/up_proj). Parameters the loader never feeds from the stream (see _DRAFT_SKIPPED_KEY_SUBSTRINGS) are excluded.
- nemo_rl.models.generation.vllm.draft_utils._validate_draft_refit_info(
- model_runner: Any,
- draft_model: Any,
- method: Optional[str],
- state_dict_info: dict[str, Any],
Hard-error when the trainer’s
draft.*manifest mismatches the drafter.Owning ranks require exactly the drafter’s loadable key set (plus keys the loader intentionally skips); non-owning ranks ignore draft payloads entirely and are never required to have a drafter.
- nemo_rl.models.generation.vllm.draft_utils._assert_drafter_owns_modules(
- model_runner: Any,
- draft_model: Any,
- draft_weights: list[tuple[str, Any]],
Refuse to load draft embed/lm_head through target-shared modules.
vLLM’s drafter loaders may alias the drafter’s embed_tokens and lm_head to the target model’s modules as a weight-sharing optimization; loading refit draft weights through such an alias would overwrite the policy’s serving weights with the draft’s (see
disable_draft_module_sharing). Guard every refit so any path that reintroduces the sharing fails loudly instead of silently corrupting generation.