nvidia/trtllm-moe-develop
>- Review, design, and refactor TensorRT-LLM PyTorch MoE code for architecture fit, clean code, maintainability, and testability. Always use for any modification, review, refactor, or design planning that touches MoE modules, including tensorrt_llm/_torch/modules/fused_moe, ConfigurableMoE, MoE backends, MoEScheduler/moe_scheduler.py, forward execution/chunking, communication strategies, EPLB, quantization/weight handling, routing, factories, MoE docs, or MoE tests. Also use when the user asks whether a MoE design follows the current architecture or whether a MoE refactor is reasonable.
npx skills add https://github.com/NVIDIA/TensorRT-LLM --skill trtllm-moe-develop
Use this skill to keep MoE changes aligned with the current TensorRT-LLM MoE
architecture. Favor module roles, API boundaries, and testability over local
style cleanup.
Before proposing or editing MoE code, read:
CODING_GUIDELINES.mdtensorrt_llm/_torch/modules/fused_moe/MOE_DEVELOPER_GUIDE.mdtests/unittest/_torch/modules/moe/Also inspect these files when the area is relevant:
moe_scheduler.py, configurable_moe.py,interface.py, backend run_moe/quantize_input paths, and communication code.
moe_scheduler.py, mega_moe/,configurable_moe.py, quantization.py, and communication code.
tensorrt_llm/_torch/modules/fused_moe/communication/base.pyand communication_factory.py.
tensorrt_llm/_torch/modules/fused_moe/quantization.py.interface.py, moe_load_balancer.py, quantization.py,moe_scheduler.py, current forward-execution/chunking code, and
test_moe_module.py.
tests/unittest/_torch/modules/moe/moe_test_utils.py andquantize_utils.py when adding backend, quantization, skip, or parameter
coverage.
For module-specific work, read references/moe-canonical-code-examples.md
after the guide and load only the relevant section. Each design gate or review
should cite at least one concrete code example with file:line evidence.
Treat MOE_DEVELOPER_GUIDE.md as the in-repo source of truth for MoE
architecture. Treat this skill as the agent workflow
layer that tells Codex how to apply that source of truth while designing,
editing, or reviewing code.
Use the guide this way:
File Map, Backend Capability Matrix, execution-flow/EPLB constraints,
Canonical Examples, and Anti-Patterns.
pattern, and test plan.
skill; prefer the guide as the current reference.
fused-communication behavior, EPLB behavior, or test convention, check whether
the guide also needs an update.
either update the guide as part of the change or report it as follow-up.
Guide-update checklist:
File Map.Backend Capability Matrix.Canonical Examples.Anti-Patterns.Tests.Preserve these owner boundaries:
ConfigurableMoE is the assembler/orchestrator. It wires backend,communication, EPLB, weight lifecycle delegation, and shared wrapper
bookkeeping.
weight lifecycle boundary. They expose and implement create_weights,
load_weights, post_load_weights, process_weights_after_loading, and
pre_reload_weights as needed, select any FusedMoEMethod, and make those
hooks compatible with ConfigurableMoE deferred weight creation and reload
flows. Backends may delegate quantization-specific tensor layout, loading,
post-load transforms, and scale setup to a quantization method, but backend
lifecycle hooks remain the public owner of weight handling. New
ConfigurableMoE-compatible backends should expose quantize_input and
run_moe, not forward or forward_impl, unless the user explicitly asks
for legacy standalone behavior. For an active ConfigurableMoE-compatible
backend, run_moe must be a concrete implementation, not an empty stub, and
ConfigurableMoE or its scheduler should call backend.run_moe(...) as the
compute entrypoint. Backend-specific alternatives such as run_with_prequant
are acceptable only as private helpers called from run_moe, not as public
wrapper/scheduler targets that bypass the common contract. The current MoE
interface still covers both legacy standalone MoE modules and newer backends;
treat legacy forward methods as transitional until a dedicated MoEBackend
interface exists. Backends should not become orchestration or
external-communication state machines.
quantization-specific weight tensor layout, loading details, post-load
transforms, scale setup, and EPLB fix-up registration. They do not replace
backend ownership of the weight lifecycle API.
MoEScheduler owns forward-time policy: padding/truncation, chunking,dispatch/quantize ordering, EPLB hook ordering, zero-token chunk behavior,
external-vs-fused communication workflow, and backend run_moe invocation.
ConfigurableMoE constructs the scheduler from backend.scheduler_kind and
delegates to it; schedulers may read wrapper state and call wrapper helpers but
must not own lifecycle, weight loading, DWDP record, repeat_idx advancement,
or communication lifetime. The only sanctioned scheduler mutation of
moe.comm is through determine_communication_method fallback.
one test file while leaving moe_test_utils.py or quantize_utils.py stale is
usually incomplete.
communication, routing, EPLB, or multi-GPU behavior.
A refactor is good only if it keeps these roles clearer than before.
Role:
forward_impl focused on wrapper-level work: resolve output dtype,delegate execution, record DWDP, advance repeat_idx once.
policy.
Main APIs / references:
configurable_moe.py: ConfigurableMoE.__init__, backend construction,communication strategy creation/bypass, scheduler construction, forward_impl,
validate_backend.
MOE_DEVELOPER_GUIDE.md: ConfigurableMoE orchestrator and file map.Checklist:
ConfigurableMoE as an assembler.forward_impl unless it is a temporarycompatibility bridge with a clear follow-up.
forward_impl or an extracted scheduler should invoke backend computationthrough backend.run_moe(...); direct calls to backend-specific compute
entrypoints such as run_with_prequant are red flags unless the change is
explicitly a short-lived adapter and run_moe remains the real implementation.
repeat_idx, DWDP record, backend attr sync, andcommunication lifetime stays in one place.
validation, and optional DWDP setup are initialized, because schedulers read
that wrapper state.
forward_impl should not accumulate chunking, routing, communication, EPLB,or fused-kernel branches; that policy belongs in MoEScheduler.
Role:
chunking, dispatch ordering, adaptive pre/post quant dispatch, EPLB wait/stat
update/route/CPU-stage hook ordering, zero-token chunk behavior, and backend
run_moe invocation.
MoESchedulerKind, notthrough wrapper isinstance checks.
backend construction, weight lifecycle, communication object lifetime, DWDP
record, or repeat_idx advancement.
Main APIs / references:
moe_scheduler.py: MoEScheduler, ExternalCommMoEScheduler,FusedCommMoEScheduler, create_moe_scheduler.
interface.py: MoESchedulerKind and backend scheduler_kind.configurable_moe.py: scheduler construction and thin forward_impldelegation.
communication/base.py: supports_post_quant_dispatch, prepare_dispatch,dispatch, and combine contracts used by ExternalCommMoEScheduler.
Checklist:
moe_scheduler.py, not in ConfigurableMoE or abackend, unless it is truly backend-local compute inside run_moe.
ExternalCommMoEScheduler owns host-side dispatch/combine, communicationfallback, optional multi-stream chunk overlap, padding/truncation, and external
communication EPLB statistic paths.
FusedCommMoEScheduler owns fused-kernel lockstep: ADP stripping,per-rank-consistent chunk count, zero-token launches, no external
dispatch/combine, and ignore_allreduce=False EPLB statistic update.
backend.quantize_input(...) and backend.run_moe(...); theymust not call backend-specific alternate compute helpers that bypass run_moe.
repeat_idx, run DWDP record/prefetch, create ordestroy communication strategies, or call weight lifecycle hooks.
scheduler helper code, with comments explaining why the common run_moe
contract is insufficient for that backend.
chunking, zero-token chunks, DP padding/truncation, EPLB hook order, and
fused-communication lockstep.
Role:
create_weights, load_weights, post_load_weights,
process_weights_after_loading, and pre_reload_weights.
quantize_input and run_moe shape/kernel contracts. run_moe mustlaunch the backend compute path for every active ConfigurableMoE-compatible
backend. Do not leave it as NotImplementedError while the wrapper calls an
alternate method such as run_with_prequant.
forward or forward_impl for new ConfigurableMoE-compatiblebackends unless the user explicitly requests legacy standalone behavior; if
required, document why the normal backend contract is insufficient.
fused inside the kernel.
Main APIs / references:
interface.py: MoE, scheduler_kind, can_implement,_supports_load_balancer, validate_configurable_moe when present, and
weight lifecycle hooks
(create_weights, load_weights, post_load_weights,
process_weights_after_loading, pre_reload_weights).
fused_moe_cutlass.py: reference backend using external communication.mega_moe/: reference area for a fused-communication backend.create_moe.py: backend selection and fallback path.Checklist:
can_implement() returns clear (False, reason) for unsupported quant,dtype, shape, or hardware.
narrow error; create_weights() is safe under ConfigurableMoE deferred weight
creation, load_weights() honors or rejects allow_partial_loading, and
post_load_weights() / process_weights_after_loading() /
pre_reload_weights() keep transformed weights and reload metadata coherent.
layout-specific weight registration/loading/transforms; callers should not
need to reach into quantization.py directly.
run_moe is implemented and is the method reached by ConfigurableMoE or thescheduler. If a helper like run_with_prequant exists for performance or
naming compatibility, it is called from run_moe, not directly from wrapper
policy code.
scheduler_kind and not hiddenbehind wrapper isinstance checks. Backends with kernel-fused exchange declare
MoESchedulerKind.FUSED_COMM; normal backends use EXTERNAL_COMM.
narrow contract, not in scattered forward branches.
implemented by a FusedMoEMethod.
fused-communication backend whose kernel owns the exchange.
test_moe_backend.py.forward methods can be read for compatibility context, butthey are not the default pattern for new backend work.
When importing or wrapping an upstream kernel, derive the TRT-LLM adapter
contract from the lowest-level kernel consumer. Comments, docs, design notes,
and parameter names are useful hints, but they are not proof of the runtime ABI.
make_layout, TMA,MMA/GEMM transforms, and stride usage. Record required tensor shape, stride,
physical storage layout, and boundary view layout.
alpha, norm constants, block scales, activation scales, and weight scales are
loaded and multiplied before deciding how upper layers compute or pack them.
Treat weight bytes, block scales/SF, and global alpha/norm constants as
separate contracts.
input/output to an adapter responsibility: storage tensor, view/transposition,
dtype reinterpretation, padding, scale packing, workspace ownership,
synchronization, and output reduction. Validate parity with upstream
invocation dumps, not just final output.
Role:
exposes the lifecycle hooks, owns when they are called, and is accountable for
reload/EPLB consistency.
quant scales, and EPLB weight fix-ups should live in quantization.py as a
backend-selected FusedMoEMethod implementation when they are specific to a
quantization layout.
method or base class before creating a new one, then make the backend select
and invoke it through the lifecycle hooks.
Main APIs / references:
quantization.py: FusedMoEMethodBase, create_weights, load_weights,post_load_weights, setup_quant_scales, eplb_support_status,
supports_online_eplb, need_load_shared_weights.
quantization.py are the reference patterns.Checklist:
quantization-specific tensor layouts are represented by a backend-selected
quantization method, not ad hoc caller or wrapper code.
semantics match.
create_weights() registers module parameters with the correct slot, expert,hidden, intermediate, and scale layout.
load_weights() handles supported loading modes and rejects unsupported onesclearly. Preserve the EPLB split: common MoE FC weights/biases
(w3_w1_weight, w2_weight, and bias tensors when present) use the shared
FusedMoEMethodBase.load_weights() / post_load_weights() path, where
need_load_shared_weights(module) gates CPU shared staging and registration.
for scales, alphas, transformed weights, or layout-specific views that are not
covered by the base FC weight path. Those extra tensors must also be gated by
need_load_shared_weights(module) before loading, transforming, or registering
shared copies. If a specialized method cannot reuse the base FC path because
its raw parameter layout is incompatible, the design must call out that
exception and preserve equivalent base semantics explicitly.
post_load_weights() performs transforms, shared-weight setup, and scalesetup in the quantization method only for tensors outside the base FC path;
base FC weight registration should still flow through the base class whenever
possible.
setup_quant_scales() is updated when a quant mode exposes scales consumed bybackend, communication, or forward-execution paths.
SUPPORTED, NOT_SUPPORTED, orNOT_VERIFIED.
Role:
quantization, forward execution, communication, and tests.
Main APIs / references:
interface.py: _supports_load_balancer, _add_raw_shared_weights_for_unmap,_using_load_balancer, _using_dynamic_load_balancer, validation hooks.
quantization.py: eplb_support_status, need_load_shared_weights,register_all_parameter_slot_and_to_fix_weight_fns, setup_quant_scales,
post_load_weights.
ignore_allreduce,per-chunk first/last hook ordering.
test_moe_module.py: EPLB params and generate_*_eplb_test_params.Checklist:
FusedMoEMethodBase usingneed_load_shared_weights(module) in its shared-load/register flow.
are handled by the concrete quantization method and must add their own
need_load_shared_weights(module) gated shared-load/register logic.
quantization method, including any fix-up functions for transformed weights.
ignore_allreducecorrectly for the communication path.
run_moe, and CPU weightmigration.
num_slots, num_experts, ep_size, and slot-vs-expert IDs are not mixed.test_moe_module.py, including thebackend/comm/quant combination that changed.
Dynamic EPLB needs host-resident copies of per-expert tensors so that
MoeLoadBalancer can migrate experts between ranks via host shared memory.
Each per-expert nn.Parameter on the module has a parallel CPU staging buffer;
all of them are passed to register_all_parameter_slot_and_to_fix_weight_fns
once loading finishes. Any new per-expert Parameter MUST add its own staging
buffer and migration hook, or the shared-load path will either write out of
bounds or silently corrupt routed slots (NVBug 6130334 / PR #13856).
Full family in the NVFP4 path (quantization.py):
| GPU nn.Parameter on module | CPU shared staging buffer | Sized by |
|---|---|---|
| w3_w1_weight (packed FP4) | module.local_shared_w3_w1_tensors | len(local_shared_load_expert_ids) |
| w2_weight (packed FP4) | module.local_shared_w2_tensors | same |
| w3_w1_bias / w2_bias (if bias=True) | module.local_shared_w3_w1_bias_tensors / module.local_shared_w2_bias_tensors | same |
| w3_w1_weight_scale / w2_weight_scale (block scales) | module.local_shared_w3_w1_scale_tensors / module.local_shared_w2_scale_tensors | same |
| fc31_alpha / fc2_alpha (per-expert fp32 scalar) | shared_fc31_alpha / shared_fc2_alpha (local variables in process_weights_after_loading) | num_shared = len(tmp_shared_weight_scale_2) |
| fc31_weight_scale_2 / fc2_weight_scale_2 (per-expert fp32 scalar, gated by force_dynamic_quantization) | shared_fc31_weight_scale_2 / shared_fc2_weight_scale_2 (local variables) | same |
Key index-space distinction:
expert_size_per_partition = num_slots / ep_size is the routed-slot count onthis rank; sizes the on-GPU module Parameters.
num_shared = len(local_shared_load_expert_ids) = num_experts / shared_size,where shared_size = shared_mpi_comm.Get_size() is the same-node MPI rank
count (from MPI_COMM_TYPE_SHARED split); sizes the CPU staging buffers.
shared_size < ep_size is legal and makesnum_shared > expert_size_per_partition. Any code that writes into a
routed-sized Parameter using a staging-space index will go out of bounds.
shared_size == ep_size is enforced by theassert shared_size == local_size in MoeLoadBalancer._setup_mpi_comm, so
single-node unit tests cannot exercise the
num_shared > expert_size_per_partition failure mode through parameter
tuning alone. A regression test for staging-index correctness must either
(a) invoke the reconcile/migration function directly with a crafted staging
dict, or (b) run on a real multi-node Slurm environment.
Naming convention quirk: bulk weights and block-scales use
module.local_shared_*_tensors (attribute on module, deleted after register);
per-expert scalars (alphas, weight_scale_2) use shared_* (function-local).
Both are equally valid migration sources -- the distinction is historical.
Checklist for adding a new per-expert Parameter to an EPLB-supporting
quantization method:
nn.Parameter sized expert_size_per_partition increate_weights().
tmp_shared_*_weight_scale_X dictkeyed by enumerate(local_shared_load_expert_ids) during the
need_load_shared_weights(module) branch.
process_weights_after_loading() (or the equivalent finalize step),allocate a CPU shared_* buffer sized num_shared and fill it from the
temp dict. Pass it as an explicit destination to reconcile/compute
helpers -- do NOT write into the on-module .data[expert_idx] from the
shared path, since expert_idx is in staging space and the on-module
Parameter is in routed space.
weight_fns dict handed toregister_all_parameter_slot_and_to_fix_weight_fns({...}) so migration can
find it.
its signature must take the destination tensor as a parameter (not read
module.<param>.data directly), so the same body serves both index spaces.
Red flags:
create_weights() but never addedto any weight_fns migration dict -- it will be stale after the first EPLB
migration.
tmp_shared_* and writesmodule.<per_expert_param>.data[expert_idx] -- the staging-space index can
exceed the routed-space bound (multi-node) or silently overwrite routed
slots (single-node).
fc31_* / fc2_* pair registered but its twinnot (or one added to weight_fns but not the other) -- migration will leave
half the state stale.
Role:
ordering they support relative to quantization.
communication strategies rather than being forced through the factory.
Main APIs / references:
communication/base.py: Communication, is_platform_supported,is_workload_feasible, supports_post_quant_dispatch, prepare_dispatch,
dispatch, combine.
communication/communication_factory.py: strategy selection.nvlink_one_sided.py, nvlink_two_sided.py, deep_ep.py,allgather_reducescatter.py.
Checklist:
supports_post_quant_dispatch() is correct for the payload layout.prepare_dispatch() is used only for metadata/statistics that must happenbefore dispatch.
dispatch() and combine() maintain enough internal state for the pair to becorrect.
load balancer through the forward-execution path.
test_moe_comm.py or module-level tests when changing strategybehavior.
Role:
moe_scheduler.py as the current owner of forward-time policy. Use thissection as the detailed checklist for scheduler changes and for reviews that
suspect policy has leaked back into the wrapper or backend.
communication strategy lifetime, DWDP record, and repeat_idx advancement
remain wrapper-level concerns.
Main APIs / references:
moe_scheduler.py: scheduler ABC, external/fused scheduler implementations,chunk helpers, EPLB hook order, and backend kwargs construction.
configurable_moe.py: scheduler construction and wrapper lifecycle afterscheduler return.
run_moe/quantize_inputcontracts.
communication behavior.
Checklist:
repeat_idx once per forward_impl; schedulers must notmutate it independently.
fallback, quantize/dispatch order, EPLB hooks, and output truncation.
Communication.dispatch orcombine.
and zero-token behavior.
Role:
hardware capability, and model config.
Main APIs / references:
routing.py: routing method implementations.create_moe.py: get_moe_cls, create_moe_backend, create_moe.moe_test_utils.py: backend enum, backend class map, skip logic.Checklist:
reasons.
can_implement() instead of hiding bugs withbroad skips.
Role:
test matrices centralized and consistent across backend-level and module-level
tests.
can_implement() instead of hiding failures with broad local skips.
Main APIs / references:
tests/unittest/_torch/modules/moe/moe_test_utils.py: MoeBackendType,get_backend_class, get_quick_skip_reason, backend-specific
should_skip_*, iter_base_test_configs, CI acceleration logic.
tests/unittest/_torch/modules/moe/quantize_utils.py: quantized test weightgeneration and quant-parameter setup.
test_moe_backend.py: backend interface tests for quantize_input andrun_moe.
test_moe_module.py: ConfigurableMoE integration matrix, multi-GPU, and EPLBcoverage.
test_moe_comm.py: communication dispatch/combine coverage.Checklist:
MoeBackendType, get_backend_class, backend/modulematrices, and skip logic.
checks when applicable.
capability checks.
documented in the test helpers.
test_fused_moe.py are used only for compatibility; newConfigurableMoE behavior belongs in test_moe_backend.py, test_moe_module.py,
or focused comm/routing/load-balancer tests.
Before editing, write a short gate:
## MoE Design Gate
- Change area: <ConfigurableMoE / MoEScheduler-forward-execution / backend / quantization-weights / EPLB / communication / routing-factory / test-matrix / tests>
- Owner boundary: <where the behavior belongs and why>
- Main API touched: <method/class names>
- Reference pattern: <existing file/class/function from references/moe-canonical-code-examples.md, with file:line evidence>
- Guide sections used: <MOE_DEVELOPER_GUIDE.md sections>
- Guide update needed: <yes/no; which section if yes>
- Refactor needed: <yes/no; one reason tied to architecture, not style>
- Test plan: <backend/module/comm/routing/EPLB/multi-GPU tests>
If the owner boundary is unclear, inspect more code before editing.
Recommend a refactor when it:
ConfigurableMoE while preserving its assembler role.delegation for weights/scales.
MoEScheduler rather than wrapper/backend branches.
and module tests.
semantics change.
forward-time policy in moe_scheduler.py without changing
performance-critical semantics.
Reject or question a refactor when it:
ConfigurableMoE instead ofselecting behavior through MoESchedulerKind / MoEScheduler.
be used.
For reviews, lead with findings and concrete references:
## Findings
- [High] <file:line> <architecture, correctness, or testability issue>
- [Medium] <file:line> <maintainability or boundary issue>
- [Low] <file:line> <local cleanup>
## Architecture Fit
- ConfigurableMoE remains assembler: <yes/no>
- Owner boundaries respected: <yes/no>
- Scheduler boundary respected: <yes/no; forward policy in `moe_scheduler.py`, lifecycle in wrapper, compute in backend>
- Refactor recommended: <yes/no + reason>
## Guide Alignment
- Sections checked: <MOE_DEVELOPER_GUIDE.md sections>
- Guide update needed: <yes/no + section>
## Checklist Coverage
- Weights/quantization: <covered/gap>
- EPLB: <covered/gap>
- Communication: <covered/gap>
- MoEScheduler/forward execution: <covered/gap>
- Backend: <covered/gap>
- Forward execution/chunking details: <covered/gap>
- Test matrix/helpers: <covered/gap>
- Tests: <covered/gap>
If there are no findings, say so and list remaining test or performance risk.
Prefer the unified MoE tests:
tests/unittest/_torch/modules/moe/moe_test_utils.py and quantize_utils.py, then run the affected backend/module tests below.pytest tests/unittest/_torch/modules/moe/test_moe_backend.py -k '<backend or quant>'.pytest tests/unittest/_torch/modules/moe/test_moe_module.py -k '<backend or feature>'.pytest tests/unittest/_torch/modules/moe/test_moe_comm.py -k '<strategy>'.pytest tests/unittest/_torch/modules/test_moe_routing.py -k '<routing>'.pytest tests/unittest/_torch/modules/test_moe_load_balancer.py -k '<case>'.pytest tests/unittest/_torch/multi_gpu/test_moe_a2a.py -k '<case>'.When GPU resources are required, use the TRT-LLM GPU allocation/test-runner
skills first and record skipped tests with reasons.
Take nvidia/trtllm-moe-develop from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.