feat(models): port LLaDA-2 MoE (masked-diffusion LLM, DeepSeek-V3 MoE FFN)#659
Merged
Conversation
02f8b2d to
251cce2
Compare
Member
Author
Real-checkpoint validation (orchestrator-run on GB10)Validated end-to-end against the real
Notes:
Real-checkpoint closure bar met; moving to status:done. |
Add support for LLaDA-2 MoE (`model_type: llada2_moe`, e.g. `inclusionAI/LLaDA2.0-mini`), a decoder-only masked-diffusion language model with a DeepSeek-V3-style MoE FFN. It generates by iterative block-wise unmasking of `<|mask|>` tokens rather than autoregressive decode, so like DiffusionGemma it owns its generation loop and reports `supports_batching() == false`. New module `src/models/llada2_moe/`: - `mod.rs`: config with the exact serde defaults (derived head_dim/rotary_dim, and the omitted-key fallbacks for eos_token_id -> pad_token_id, mask_token_id -> 156895, use_qk_norm -> true); bidirectional attention with a fused-QKV runtime split, per-head QK-norm, and partial RoPE; dense SwiGLU MLP; and the MoE block. The routed experts reuse `switch_layers::SwitchGLU` + `moe_weighted_sum`; the gate is modeled on `deepseek_v3::MoEGate` with the three LLaDA-2 deltas (bias key `expert_bias`, `+ 1e-20` top-k normalization, and a group mask that fills non-kept groups with a large negative constant instead of zero because the bias may be negative). The router runs in float32. - `generate.rs`: the semi-autoregressive block-unmasking engine. The prompt is committed into per-layer KV caches block by block; each generation block is denoised by repeatedly forwarding it against the frozen prefix (read-only `visible_state`) and revealing positions whose chosen-token probability clears a linearly decaying threshold (at least one reveal per step to guarantee progress). Schedule and transfer-mask selection are pure host-side functions. - `tests.rs`: loop-schedule, config-default, and gate-math unit tests, plus the synthetic parity test (MoE block equals a naive per-expert host loop; a fixed-logits run reproduces the hand-computed reveal order) and an on-disk tiny checkpoint (2 layers, 1 dense + 1 MoE, 8 experts) loaded through the real detection + loading path and generated end to end. Runtime integration: detection arm, `ModelType`/registry/metadata/`LoadedModel` wiring, the nonstandard construction arm, the tensor-parallel exclusion, CLI `generate` routing plus a `generate_llada2` driver, the `chat` interactive rejection, and both diffusion-worker dispatch points in `model_worker`. The diffusion worker's tokenize/stream/cancel/usage plumbing is factored into one `serve_streaming_request` helper shared by DiffusionGemma and LLaDA-2 (DiffusionGemma behavior unchanged); a LLaDA-2 options mapper and worker loop sit next to the gemma ones, with `--max-denoising-steps` mapping to the per-block step count. Docs updated in `docs/supported-models.md`. Also wrap a pre-existing constant assertion in `switch_layers` tests in a `const` block to keep `clippy --tests -D warnings` clean under the pinned toolchain. The real-checkpoint smoke test (~32.5 GB LLaDA2.0-mini download + real-runtime generation) was NOT executed on this Linux/GB10 host and remains for orchestrator Phase 3.5 validation.
251cce2 to
53cfbf1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ports LLaDA-2 MoE (
model_type: llada2_moe, e.g.inclusionAI/LLaDA2.0-mini): a decoder-only masked-diffusion language model with a DeepSeek-V3-style MoE FFN. It generates by iterative block-wise unmasking of<|mask|>tokens rather than autoregressive decode, so like DiffusionGemma it is a model-owned batch-1 generator (supports_batching() == false) served on the diffusion worker loop.What changed
New module
src/models/llada2_moe/:mod.rs— config with the exact serde defaults from the spec, including derivedhead_dim/rotary_dimand the load-bearing omitted-key fallbacks (eos_token_id->pad_token_id,mask_token_id-> 156895,use_qk_norm-> true). Bidirectional attention (no causal mask anywhere) with a fused-QKV runtime split (boundaries[num_heads, num_heads + num_kv_heads]), per-head QK-norm, and partial RoPE (rotary_dimofhead_dim, non-traditional). Dense SwiGLU MLP fori < first_k_dense_replace, MoE block otherwise. Routed experts reuseswitch_layers::SwitchGLU+moe_weighted_sum; the gate followsdeepseek_v3::MoEGatewith the three LLaDA-2 deltas: bias keyexpert_bias,+ 1e-20top-k normalization, and a group mask that fills non-kept groups with a large negative constant (not zero, sinceexpert_biasmay be negative). Router runs in float32.generate.rs— the semi-autoregressive block-unmasking engine: prefill commits full prompt blocks into per-layer KV caches; each generation block is denoised by repeatedly forwarding it against the frozen prefix (read-onlyKVCache::visible_state) and revealing positions whose chosen-token probability clears a linearly decaying threshold, with a single forced reveal when none clears it (progress guarantee). Schedule and transfer-mask selection are pure host-side functions.tests.rs— loop-schedule, config-default, and gate-math unit tests, plus the synthetic parity test and an on-disk tiny checkpoint loaded through the real detection + loading path.Runtime integration (completion requires the working runtime, not standalone modules):
detection.rs,models/mod.rs(variant + registry + display + exhaustiveness),model_metadata.rs,loaded_model.rs(variant + delegation),loading/nonstandard.rs,distributed/tensor_parallel/inference.rs(exclusion), CLIcommands/generate.rs+ newcommands/generate_llada2.rs,commands/chat.rsrejection, and bothLoadedModel::DiffusionGemmadispatch points inserver/model_worker.rs. The diffusion worker's tokenize/stream/cancel/usage plumbing is factored into oneserve_streaming_requesthelper shared by DiffusionGemma and LLaDA-2 (DiffusionGemma behavior byte-identical); a LLaDA-2 options mapper + worker loop sit beside the gemma ones, with--max-denoising-stepsmapping to the per-block step count. Docs row added todocs/supported-models.md.Also wraps one pre-existing constant assertion in
switch_layerstests in aconstblock soclippy --tests -D warningsstays clean under the pinned 1.93.1 toolchain (byte-identical otherwise; unrelated to LLaDA-2).The issue's stated closure bar includes downloading
inclusionAI/LLaDA2.0-mini(~32.5 GB) and generating through the real runtime on bothmlxcel generateandmlxcel-server. This was NOT executed on this Linux/GB10 host (it cannot complete within the build/watchdog budget) and remains for orchestrator Phase 3.5 validation. This PR does not imply that bar is met; it delivers everything else (implementation + full integration + synthetic-parity + unit tests, all green locally).Test plan
cargo test --lib models::llada2_moe— 20 tests green (loop-schedule, config defaults, gate math, MoE-vs-naive-loop parity, fixed-logits reveal order, on-disk synthetic checkpoint detect + load + generate)cargo test --lib server::diffusion_worker— 11 green (6 DiffusionGemma unchanged + 5 new LLaDA-2 option-mapper / media-reject / finish-reason)cargo test --lib -- every_variant all_model_types_covers— registry exhaustiveness guards greencargo clippy --lib --tests -- -D warningsandcargo clippy --bin mlxcel -- -D warningscleancargo fmtcleaninclusionAI/LLaDA2.0-mini— deferred to Phase 3.5 (see caveat above)Closes #546