Repository navigation
[OMNIML-6079] [2/2] Add the Kimi-K3 expert-parallel adapter - #2749
shengliangxu wants to merge 24 commits into
Conversation
|
Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
f73bc4f to
0098f3a
Compare
660d339 to
784e815
Compare
|
|
/ok to test 784e815 |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## shengliangx/hfep-11b-kimi-k25-vl #2749 +/- ##
===================================================================
Coverage ? 62.63%
===================================================================
Files ? 668
Lines ? 74485
Branches ? 0
===================================================================
Hits ? 46654
Misses ? 27831
Partials ? 0
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
0098f3a to
3d6fb9d
Compare
784e815 to
57494b6
Compare
|
/ok to test 57494b6 |
3d6fb9d to
1c16b42
Compare
57494b6 to
25176cb
Compare
1c16b42 to
ecf9d71
Compare
25176cb to
53b85aa
Compare
|
/ok to test 53b85aa |
ecf9d71 to
d525e64
Compare
53b85aa to
ba6939d
Compare
|
/ok to test ba6939d |
ba6939d to
dec8f8c
Compare
d525e64 to
0c39a27
Compare
dec8f8c to
954d8bf
Compare
|
/ok to test 4389363 |
|
/claude review |
| ) | ||
| _patch_kimi_moe_block(kmod) | ||
| # Recorded at the first patch that lasts, so a step below that raises still leaves the record. | ||
| mark_patched_every_load("kimi_k3") |
There was a problem hiding this comment.
[SUGGESTION] The refusal's key is a literal here that has to equal ModelSpec.model_type in specs.py, and nothing checks the two agree.
check_not_patched_by_prepare is called with the model_type load_pretrained resolved from the checkpoint's config, so if the spec's model_type were ever renamed (or a second Kimi-K3 spelling registered) this early mark would land under a key no load looks up. The loss is silent and exactly the case this call exists for: apply_distributed_support still marks with the real model_type for a prepare that returns, so only the raised-mid-prepare path — the one test_a_prepare_that_raises_after_patching_still_records_it guards — would quietly stop refusing. The test asserts the same literal, so it would not catch the divergence either.
Cheapest fix is to derive it rather than spell it:
from modelopt.torch.models import get_spec # or thread the model_type through prepare
...
mark_patched_every_load(get_spec("kimi_k3").model_type)or, if the literal is preferred, have the spec test assert it: assert get_spec("kimi_k3").model_type == "kimi_k3" alongside the existing ep_spec.prepare is kimi_k3.prepare check in test_the_kimi_k3_spec_routes_expert_parallelism_through_this_adapter. Non-blocking — the placement itself is right, and I verified the three assignments in _patch_kimi_moe_block cannot raise between patching and this call.
There was a problem hiding this comment.
Taken in f186098. kimi_k3.ep.MODEL_TYPE is now the one definition: the spec registers it and the early record keys on it. The test checks the record against get_spec("kimi_k3").model_type.
| A spec whose ``prepare`` patches every later load of its model | ||
| (:attr:`~modelopt.torch.models.EPSpec.prepare_patches_every_load`) leaves code only an | ||
| expert-parallel load builds from: once it ran in this process, a whole-model or FSDP2-sharded | ||
| load of the model is refused before anything is built. |
There was a problem hiding this comment.
[SUGGESTION] "refused before anything is built" is true of the load but not of the plan decision that precedes it, and the ordering is the whole point of this guard — so the claim is worth narrowing.
In load_pretrained, this check sits inside the _refused_together("load checks", ...) block at model_load_utils.py:1055, which runs after resolve_distribution_plan (:990). That decision calls build_meta_causal_lm, so under torchrun a meta model has already been constructed from the patched KimiSparseMoeBlock by the time the refusal fires. Harmless in practice — it is a throwaway on meta, and _spec_ep_plan names the fused params so the decision comes out the same — but a reader taking the docstring literally would conclude nothing touches the patched classes first.
Related and worth a sentence while you are here: with MODELOPT_EP_DEGREE=1 under torchrun the decision raises its own generic ValueError ("can be expert-parallel, but the plan resolved …") before reaching this check, so the clear "load it in a new process" message is only what the FSDP2 and single-process routes get. The two unit cases cover exactly those, so coverage is fine; it is the docstring that over-promises.
| load of the model is refused before anything is built. | |
| expert-parallel load builds from: once it ran in this process, a whole-model or FSDP2-sharded | |
| load of the model is refused before it builds or loads one. The plan decision's throwaway meta | |
| build runs earlier, so an EP degree of 1 under torchrun meets its own refusal first. |
There was a problem hiding this comment.
Taken in f186098, with your wording. check_not_patched_by_prepare, the spec field and prepare's docstring now say the refusal comes before the load builds or loads the model. check_not_patched_by_prepare adds that the plan decision's throwaway meta build runs earlier, so under torchrun an EP degree of 1 meets the decision's own refusal first.
|
/ok to test f186098 |
Kimi-K3's text backbone, kimi_linear, has no native transformers modeling to swap in, so the adapter patches the checkpoint's own code in place. It replaces the MoE block's per-expert ModuleList with fused experts that dispatch through the experts interface, keeping K3's latent expert space and its custom SwiGLU. It registers converters that fuse the checkpoint's per-expert w1/w3 into gate_up_proj and stack w2 into down_proj, and installs the on-the-fly decompressor for its MXFP4 experts. It registers torch_a2a before the plan decision builds the model on the meta device. Its spec states the plan prepare attaches and declares transformers 5.13, the release it was validated against. The default PTQ units keep K3's latent-expert projections and residual gates, and Kimi's mm_projector, unquantized, as vLLM and the published NVFP4 checkpoints expect. The fixes the checkpoint's own modeling needs on current transformers and fla-core now come from their own module, kimi_k3/_remote_fixes.py (#3132). The refusal of a Kimi-K3 left whole on every rank has its own test instead of a row in the Kimi-K2.5 adapter's. The adapter's earlier history is archived; this commit replaces it. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The remote-code fixes' review trimmed the shared stand-in to what those fixes patch. The adapter's tests also build the checkpoint's per-expert MoE block, its gate and situ activation, so they come back here. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…does Kimi-K3's text config names it num_experts; n_routed_experts is DeepSeek-V3's spelling, left from the Kimi-K2.5 copy of this test. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
_patch_kimi_moe_block replaces KimiSparseMoeBlock's forward on the class, but swaps the per-expert ModuleList for the fused experts only in the patched __init__. A block built before the patch, as a model loaded earlier in the process holds, kept its ModuleList and then called it as the fused experts, which a ModuleList cannot run (NotImplementedError, no forward). The patched forward now runs the block's own forward for a block that still holds its per-expert experts. The patched-block test calls its reference block, built before the patch, again after patching. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
Two functions imported it locally, with no import cycle or optional dependency to defer: the adapter already imports _remote_fixes at module scope, which imports print_rank_0 there. The transformers imports and register_torch_a2a stay local, for the reasons their comments give. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
KimiK3Experts' fused parameter layout and its eager forward loop are adapted from transformers' MiniMaxM3VLExperts (v5.18.0, commit a906d3c), which is Apache-2.0. Following CONTRIBUTING.md's guidance on copied code: the file now carries the source link and the original copyright and license notice above the NVIDIA header, the insert-license hook leaves the file alone, and LICENSE lists the copyright holder among its third-party notices. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The decompressor rewrites the registered w1/w3/w2 converters to read the packed experts, and the model keeps the rewritten converters as the conversions its load ran; the export names the experts back through them. The test rewrites the registered converters as the decompressor does for Kimi-K3's stored names and maps ModelOpt's per-expert names through the export's reverse name mapper: gate_proj, up_proj and down_proj go back to w1, w3 and w2. With the rewritten converters reversed as rewritten, the mapper refuses on an arity mismatch, and the export would keep the in-memory names. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
A bounded 2-rank GPU case for the Kimi-K3 adapter on a real distributed load. A tiny Kimi-K3-shaped remote checkpoint, written by the test from kimi_k3_tiny_modeling (the module tree, names and per-expert MoE the adapter patches, as the checkpoint's own code builds them), stores its 8 routed experts as packed MXFP4, as Kimi-K3 does. Loaded through the model spec and load_and_prepare_ep at an EP degree of the world size: - each rank decompresses only its own experts, exactly; - the sharded model's logits match the whole model loaded on one GPU; - FP8 calibration, each rank on its own batch, matches the whole model calibrated on every batch, expert weights and activations alike; - the export's reverse name mapper names the experts back to the checkpoint's w1/w3/w2. Each rank's findings are gathered before any rank fails, and the object collectives give up in two minutes, so a regression fails instead of hanging. One GPU skips it. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The decompressor keeps integer sources uncast at the loader's spawn of each source tensor, which covers an expert-parallel load's shard path, where it hooked only the copy before. The unit tests' hook-restoring fixture and the 2-GPU job's save and restore now cover spawn_materialize as well as _materialize_copy, so neither patch leaks into later tests or the pooled GPU workers, whichever of the two the decompressor patches. The GPU file imports the tiny Kimi-K3 modeling below its transformers guard, and the Kimi-K3 case's skip reason names the package it needs. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…e Kimi-K3 The plan decision now raises the generic left-whole refusal as a ValueError, and keeps the RuntimeError for a transformers below what the model's spec needs, which names the release. The remote Kimi-K3 case expects whichever arm its transformers takes, as the Kimi-VL case does. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…prepare The plan decision applies a model's spec only for the classes the sharded loads build; a model class they refuse is planned whole on every rank, never expert-parallel. For a packed Kimi-K3, the spec's prepare patches the checkpoint's own code for the process and marks only its copy of the config for the experts' decompression, so running it for such a load would leave the whole load with fused blocks its unmarked config cannot fill. The test plans a packed Kimi-K3 on four ranks through AutoModel: prepare never runs, the plan is the whole model on every rank, and the config returned is the one read, unmarked and still holding its compressed-tensors scheme. It fails, on prepare, when the spec is applied regardless of the class. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…does Kimi-K3's own config class copies its language model's quantization_config to the top-level config, where the sharded loads' refusal of a quantized checkpoint reads it. The tiny Kimi-K3 the GPU case writes now does the same. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…3 tests It has no import cycle or optional dependency to defer; it now sits with the other ModelOpt imports, below the transformers guard. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
… tests The decompressor patches the loader's spawn_materialize and the conversion mapping, on every transformers release it runs on. The Kimi-K3 unit fixture and the 2-GPU job also saved and restored the converter's add_tensor and materialize_tensors and _materialize_copy, which nothing patches any more. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…made for A process that already ran a Kimi-K3 expert-parallel load keeps the patched remote classes, so a later load of the same checkpoint through a class the sharded loads refuse, such as AutoModel, runs on them. The plan decision itself never makes such a load. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
From Claude's review: prepare patched the remote MoE block and registered the fusion converters before the decompressor's install, which can still refuse the checkpoint (compressed-tensors missing, several config groups, an unknown format, a dtype other than bf16). A process that went on after the refusal kept the patches. The install now runs first: it validates before it installs anything, and its hooks rewrite only a config it marked. The converters need only be registered before the load, which the comment now says. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
From Claude's review: prepare decided whether to install the decompressor from the config's quantization_config, which prepare itself drops, while the decompressor clears its mark at the load. A config prepared and loaded before, passed to a later load, so skipped the decompressor, and the fused experts loaded newly initialized from a checkpoint storing packed weights. The checkpoint's own config.json now counts too, at its top or text level: such a config reaches the install, which refuses it, since nothing would decompress its experts. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…g is refused From cjluo-nv's review: the refusal test now has the real decompressor refuse, for a scheme compressed-tensors cannot parse and for a dtype other than bf16, and checks that the remote block, the config, the converter registry, the registered experts implementations and the loader hooks are all as before. The lost-scheme test builds the config as a load returns it, and the 2-GPU Kimi-K3 job passes the returned model.config to another load of the checkpoint, which must refuse it. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…for an EP plan alone #2748 has the plan decision build the model the checkpoint's own code builds, and run a spec's prepare only once the plan is expert-parallel. Kimi-K3's spec now names its import step (the OutputRecorder its code imports from where transformers no longer keeps it, and its imports staged), which patches no class, config, converter or loader hook, and puts its stated plan on text_config, where the expert count is. Its prepare, and so its torch_a2a registration, runs only on the expert-parallel route. The note on patches outliving their load narrows to a later load in the same process after an expert-parallel one. The end-to-end test runs the real Kimi-K3 prepare through the decision: FSDP2-sharded, or left whole and refused, the remote MoE block, the converter registry, the experts implementations, the loader and the config as read are untouched; an expert-parallel plan fuses the experts and registers torch_a2a. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
The plan decision now runs before prepare installs the decompressor, and reads a packed Kimi-K3 checkpoint's compressed-tensors format on the config as read. Kimi-K3's spec names that format among those its prepare decompresses, as #2748 lets a spec state, so a decided run of a packed Kimi-K3 is still planned expert-parallel. The end-to-end test gains a packed row, which fails without it. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
From cjluo-nv's review: as CONTRIBUTING asks, the Kimi-K3 adapter's tests move to tests/unit/torch/models/kimi_k3/test_ep.py, mirroring kimi_k3/ep.py, as the other Kimi adapters' do. The GPU integration case keeps its place in the expert-parallel load's file. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…are spoiled - The plan decision builds the checkpoint's own code before prepare, and Kimi-K3's VLM class takes none of the keyword arguments post_init passes tie_weights. Its spec's make_buildable now also loads the code and adapts tie_weights, as #2748's Kimi steps do. - Kimi-K3's prepare swaps its remote MoE block's class for good, so its spec says prepare_patches_every_load: once an expert-parallel load of it ran, a whole-model or FSDP2 load in the process is refused before anything is built (cjluo-nv's review). The note on patches outliving their load says so. The unit test fails without the refusal, and the 2-GPU job now runs the review's exact continuation, a whole-model load through AutoModel with the returned model.config, which every rank must refuse. - From Claude's review, _checkpoint_quantized reads every nested config spelling the load reads, llm_config too, and the lost-scheme test gains that level. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
…n it reads From Claude's review of the refusal: - The record that a prepare patched every later load landed only once prepare returned, so a step after the MoE block's patch that raised left the process patched but unrecorded. Kimi-K3's prepare now calls moe.mark_patched_every_load right after that patch; apply_distributed_support still records a prepare that completes. test_a_prepare_that_raises_after_patching_still_records_it fails without the early mark. - _checkpoint_quantized is the one place prepare reads the checkpoint itself, and prepare is handed no hub options: its docstring now says a hub id is read at its default revision, a local checkpoint as it is. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
… refusal precedes From Claude's review: - The early record of prepare's lasting patch spelled kimi_k3 as a literal, which had to equal the spec's model_type. kimi_k3.ep.MODEL_TYPE is now the one definition the spec registers and the record keys on, and the test checks the record against the spec. - The refusal precedes the load's build, not the plan decision's throwaway meta build, which runs earlier: the docs now say a refused load is refused before it builds or loads the model, and that under torchrun an EP degree of 1 meets the decision's own refusal first. Signed-off-by: Shengliang Xu <shengliangx@nvidia.com>
|
/ok to test a60c01c |
Note
Split on 2026-10-30 at the reviewers' request: this PR is now [2/2], the Kimi-K3 expert-parallel adapter itself. [1/2] is #3132: the fixes the checkpoint's own modeling code needs on current transformers and fla-core, moved unchanged out of this PR into
modelopt/torch/models/kimi_k3/_remote_fixes.py, with their tests. #3132 merged on 2026-10-30.Base: #2748's branch,
shengliangx/hfep-11b-kimi-k25-vl, since 2026-10-30: #2748 runs a model spec'sprepareonly once the plan is decided expert-parallel, which this PR's Kimi-K3 spec now relies on. Below #2748 sit #3138 andshengliangx/hfep-base-11b-kimi, a branch (not a PR) that merges #2746 and #2747 with main, which holds #3130 and #3132 since they merged. The diff below is this PR's alone, and it merges after #3138 and #2748.Earlier re-cuts: on 2026-10-29 this PR moved off #2748 onto a branch merging #2746 and #2747, carrying the one test row and the import-list line it had used from #2748. The series first became a tree on 2026-10-27, from main 1521053 (this PR was [20/21]); it was rebased onto main 7c3c868 later that day, after #2739 merged, and onto main f03651e that night.
Series layout
main([OMNIML-6073] [2/3] Quantize fused experts under grouped_mm; leave transposed layouts unquantized #2745, [OMNIML-6064] Load each rank's checkpoint shard straight into a meta-built model #2739, [OMNIML-6052] Add the distribution plan, a run-scoped process group, and collectives that fail together #2736, [OMNIML-6054] Decide how a model is distributed, from the model built on the meta device #2737, [OMNIML-6082] [1/3] Let a reverse conversion record a merge rule no key matched #3100, [OMNIML-6082] [2/3] Move each tensor of a sharded model whole to one owner rank #3101, [OMNIML-6081] [1/2] Pack sharded weights in FSDP2 weight-update windows, and NVFP4 DTensor shards #3099, [OMNIML-6081] [2/2] Fold fused experts' quantizers into their weights shard-locally #2750, [OMNIML-6063] [1/3] Stage and keep patched a checkpoint's own modeling code #3130 and [OMNIML-6079] [1/2] Fix Kimi-K3's own modeling code for current transformers and fla-core #3132 have merged)require_transformers, and a VLM built without its LM headprepareon the expert-parallel route onlyWhat does this PR do?
Type of change: New feature
Kimi-K3's text backbone,
kimi_linear, has no native transformers modeling to swap in, so the adapter patches the checkpoint's remote code in place:ModuleListwith fused experts that dispatch through the experts interface, keeping K3's latent expert space and its custom SwiGLU.w1/w3intogate_up_projand stackw2intodown_proj. The MXFP4 experts are decompressed as they load ([OMNIML-6063] [3/3] Load a checkpoint its model spec decompresses through the expert-parallel loader #2747).torch_a2a, the experts implementation it names on the text config, with transformers. transformers refuses to build a model whose experts implementation it has not registered;prepareruns only on the expert-parallel route, so that is the only route that registers it.A_logshape andsigmoid(beta), the causal-mask and cache signatures, the kwargs newer transformers pass totie_weights, and theOutputRecorderimport. They are [1/2] of this split, [OMNIML-6079] [1/2] Fix Kimi-K3's own modeling code for current transformers and fla-core #3132, which describes each.prepareruns only once the plan is decided expert-parallel ([OMNIML-6078] [2/2] Add the Kimi-K2.5 expert-parallel adapter #2748). Before deciding, the plan decision runs only the spec'smake_buildable: theOutputRecordershim, the staging of the code's imports, and its VLM class'stie_weightsadapted to the argumentspost_initpasses it, which change nothing the model computes or loads. It reads the plan the spec states, ontext_config, and finds the experts in the model the checkpoint's own code builds. So a whole-model or FSDP2 load never meets Kimi-K3's patches unless an expert-parallel load of it ran first in the same process; since the patches are process-wide and stay after that load, the spec says so (prepare_patches_every_load), andload_pretrainedthen refuses a whole-model or FSDP2-sharded load of Kimi-K3 in that process before anything is built. A block built beforeprepare, as a model loaded earlier in the process holds, keeps its per-expert experts and its own forward. The other fixes are what the remote code needs on current transformers and fla-core anyway.torch_a2agoes only on the prepared copy, and the decompressor rewrites only configs it marked.routed_expert_down_proj/routed_expert_up_proj) and its scalar residual gates (self_attention_res_proj,mlp_res_proj,output_attn_res_proj) unquantized in every built-in format.mm_projectorto the default unit's vision block.--reciperuns, which keep the whole VLM, quantized Kimi's.It needs transformers 5.13, the release it was developed and validated against; its spec declares it, so an older release is refused before
prepareruns. Its spec states the planprepareattaches, as #2748's do, so a run at an EP degree of 1 still finds its experts (#2746).Usage
Picked automatically by
apply_distributed_supportforkimi_k3configs undertorchrun; see #2754.Testing
tests/unit/torch/models/test_kimi_k3_ep.py(+592 lines), against the stand-in remote module that follows the checkpoint's own code (tests/_test_utils/torch/kimi_k3_remote.py, from [OMNIML-6079] [1/2] Fix Kimi-K3's own modeling code for current transformers and fla-core #3132), covering:situactivation. The block derives latent MoE fromrouted_expert_hidden_size, as the checkpoint's modeling does; the config has nouse_latent_moefield;preparepatching the defining module once, failing loudly when none defines the MoE block, and refusing a call without the checkpoint's path before it changes anything;prepareattaches equal to the one the spec states;prepareleaving the loader alone for bf16 experts, which now runs withoutcompressed-tensorsinstalled;torch_a2a, passing transformers' experts-implementation check. Without the registration it fails with theexperts_implementation="torch_a2a" is not supportederror that a tiny Kimi-K3 on 2 GPUs (transformers 5.18) hit while deciding its plan.tests/unit/torch/utils/test_model_load_utils.py(intest_resolve_distribution_plan.pybefore [OMNIML-6054] Decide how a model is distributed, from the model built on the meta device #2737 split it up): a remote-code Kimi-K3 left whole on every rank at an EP degree of 1 is refused, as [OMNIML-6078] [2/2] Add the Kimi-K2.5 expert-parallel adapter #2748's Kimi models are. The test is Kimi-K3's own, with its own stand-in model (block_sparse_moe), and sits apart from [OMNIML-6078] [2/2] Add the Kimi-K2.5 expert-parallel adapter #2748's, so the two PRs merge in either order.tests/unit/recipe/test_presets.py: the fp8 and nvfp4 presets leave those five projections andmm_projectorunquantized.auto_maploads.betatests into one, themm_projectorpreset into the Kimi projections preset. Each drop is checked by mutation.tests/uniton transformers 5.18 and 5.5 and on torch 2.8, and the hf_ptq example tests, each with the same failures as main in that environment. CI runs the full unit suite.--qformat nvfp4 --kv_cache_qformat fp8, max calibration on 64 samples of the default text blend (sequence length 128, batch 2). It decided its plan, previewed before and after PTQ on both ranks, calibrated in 51 s and exported, in 4 min 28 s at 13.9 GB per GPU. Before this revision it failed while deciding its plan (torch_a2anot registered), and with that fixed, ingenerate(the cache's query offset). GPU CI does not run on this PR while its base is a stacked branch, so the rest of this revision's GPU tests have not run at this head.all 92 router correction biases match the checkpoint, to BF16 rounding of its F32 values;
routed experts export as
experts.N.w1/w2/w3and shared experts asgate_proj/up_proj/down_proj, the naming vLLM 0.28's Kimi-K3 loader reads;the latent-expert projections, the residual gates and
mm_projector(373 modules) come out BF16, matching nvidia/Kimi-K3-NVFP4;vLLM 0.28 loads the export and starts serving at TP=32. It cannot serve one with an FP8 KV cache, however:
TBD: generation from the checkpoint-mirror recipe
models/moonshotai/Kimi-K3/ptq/nvfp4_experts-fp8_pb_attention, with [OMNIML-6073] [2/2] Calibrate fused experts on grouped_mm and torch_a2a instead of forcing eager #3074's expert weight-scale fix.functools.wraps, so signatures and tracebacks show the kernel's own; the wrapper test checks the name and signature. Both now ship in [OMNIML-6079] [1/2] Fix Kimi-K3's own modeling code for current transformers and fla-core #3132.test_model_load_utils.py, with the stand-in model'stie_weights. Pre-commit, mypy included, passes at this head over the whole change against main, andtests/unit/torch/utils,tests/unit/torch/modelsandtests/unit/recipe/test_presets.pypass on CPU at this head (torch 2.14, transformers 5.18 and 5.5.4).tests/_test_utils/torch/kimi_k3_remote.py, which both PRs' tests import: [OMNIML-6079] [1/2] Fix Kimi-K3's own modeling code for current transformers and fla-core #3132 keeps what its fixes patch, and this PR adds back the per-expert MoE block, its gate and thesituactivation its tests build. Every test of the former PR is in exactly one of the two, unchanged apart from names and imports. The Kimi-K3 refusal test is now its own (_remote_kimi_k3(), withblock_sparse_moe), apart from [OMNIML-6078] [2/2] Add the Kimi-K2.5 expert-parallel adapter #2748's. Pre-commit, mypy included, passes at this head over the whole change against main. On CPU (torch 2.14),tests/unit/torch/models,tests/unit/torch/utils/test_model_load_utils.pyandtests/unit/recipe/test_presets.pypass on transformers 5.18 and 5.5.4; the adapter's own tests skip below transformers 5.8, as before.tests/unit/torch/models,test_model_load_utils.py,test_ep_loader.pyandtests/unit/recipe/test_presets.pypass on CPU, torch 2.14, on transformers 5.18 and 5.5.4.num_experts, as Kimi-K3's does. Pre-commit against main passes, mypy included;tests/unit/torch/models,test_model_load_utils.pyandtest_ep_loader.pyandtests/unit/recipe/test_presets.pypass on CPU, torch 2.14, on transformers 5.18 and 5.5.4.tests/unit/torch/models,test_model_load_utils.py,test_ep_loader.py,test_unplaced_source_keys.pyandtests/unit/recipe/test_presets.py.preparekeeps its own forward: the patched-block test calls its pre-patch block again after patching, which fails without the fix;w1/w3/w2: a unit test maps ModelOpt's per-expert names through the exporter's reverse name mapper, over Kimi-K3's registered converters rewritten as the decompressor rewrites them, and fails with the arity mismatch without [OMNIML-6063] [3/3] Load a checkpoint its model spec decompresses through the expert-parallel loader #2747's decompressor fix;test_kimi_k3_packed_experts_load_calibrate_and_name_back_expert_parallelintests/gpu/torch/utils/test_expert_parallel_load.py: a tiny Kimi-K3-shaped remote checkpoint (tests/_test_utils/torch/kimi_k3_tiny_modeling.py) with 8 packed MXFP4 experts, loaded expert-parallel through the spec; each rank checks its decompressed experts, its logits against the whole model on one GPU, FP8 calibration against the whole model's, and the exported expert names. Not yet run on GPUs: a cluster run at this head is pending. Its non-distributed parts run on CPU (transformers 5.18): the experts decompress exactly, the forward runs, FP8 calibration picks up the fused experts, and the names map back. One GPU skips it;print_rank_0is imported at module scope;KimiK3Expertsis attributed to transformers'MiniMaxM3VLExperts, which it is adapted from (file header,LICENSE, license-hook exclusion);tests/unit/torch/models,test_model_load_utils.py,test_ep_loader.py,test_unplaced_source_keys.pyandtests/unit/recipe/test_presets.pypass: 605 on transformers 5.18, and 470 passed, 56 skipped on 5.5.4.AutoModelgets noprepare: the plan is the whole model on every rank, and the config returned is the one read, unmarked and still holding its compressed-tensors scheme. Without [OMNIML-6076] [3/3] Apply the model spec when deciding and loading expert-parallel #2746's model-class check it fails, onprepare;build_reverse_name_mapperat module scope, and both restore only the loader hooks the decompressor patches;tests/unit/torch/models,tests/unit/torch/utilsandtests/unit/recipe/test_presets.pypass on transformers 5.18 and 5.5.4;tests/gpu/torch/utils/test_expert_parallel_load.pypassed: on 2 GPUs, 4 passed and[ep-half]skipped, as it needs 4 GPUs; on 4 GPUs, all 5 passed,[ep-half]included.test_kimi_k3_packed_experts_load_calibrate_and_name_back_expert_parallelpassed on both. It failed at 7a50ed7, with the packed weight cast to bf16 on the expert-parallel shard path, before [OMNIML-6063] [2/3] Decompress compressed-tensors MoE experts as they load #3137 kept it uncast there;prepareinstalls the decompressor before it patches anything, so a refusal leaves the process unchanged (a78d656). The refusal test has the real decompressor refuse, for an unparsable scheme and for a dtype other than bf16, and checks that the remote block, the config, the converter registry, the experts implementations and the loader hooks are unchanged. Both cases fail with the old order;preparealso reads whether the checkpoint is compressed from its ownconfig.json(ea287d8). A config that has lost its scheme and the mark, as a load's returnedmodel.confighas, is refused instead of loading the experts newly initialized. The unit test builds the config as the load returns it and fails without the change; the 2-GPU Kimi-K3 job passes the returnedmodel.configto another load and checks the refusal (342c4fb);tests/unit/torch/models,tests/unit/torch/utilsandtests/unit/recipe/test_presets.pypass on transformers 5.18 and 5.5.4;tests/gpu/torch/utils/test_expert_parallel_load.pypassed: on 2 GPUs, 4 passed and[ep-half]skipped, as it needs 4 GPUs; on 4 GPUs, all 5 passed. The Kimi-K3 case passed on both, including the reload with the returnedmodel.config, which every rank refused.prepareonly on the expert-parallel route; this PR's base moved to [OMNIML-6078] [2/2] Add the Kimi-K2.5 expert-parallel adapter #2748's branch. At 63c8267:make_importable, nowmake_buildable: theOutputRecordershim and the staging of its imports), puts its stated plan ontext_config, and names compressed-tensors among the formats itspreparedecompresses (c459add, 63c8267).torch_a2ais registered only on the expert-parallel route, and the patches-persist note narrows to a later load after an expert-parallel one;test_only_an_expert_parallel_plan_patches_kimi_k3s_code[fsdp2|whole|expert-parallel|expert-parallel-packed]runs the realpreparethrough the decision. FSDP2-sharded, or left whole and refused, the remote MoE block, the converter registry,torch_a2a, the loader hooks and the config as read are untouched. An expert-parallel plan fuses the experts, and a packed checkpoint is planned expert-parallel and gets the decompressor. The fsdp2 and whole cases fail on the old decision, the packed one without the spec's formats;tests/unit/torch/models,tests/unit/torch/utilsandtests/unit/recipe/test_presets.pypass on transformers 5.18 and 5.5.4;tests/gpu/torch/utils/test_expert_parallel_load.pypassed: on 2 GPUs, 4 passed and[ep-half]skipped, as it needs 4; on 4 GPUs, all 5 passed. The Kimi-K3 case passed on both, its reload refusal included.EPSpec.prepare_patches_every_load, recorded byapply_distributed_support, checked byload_pretrained).test_a_load_after_a_prepare_that_patched_every_load_is_refused[whole|fsdp2]fails without it, and the 2-GPU Kimi-K3 job runs the review's exact continuation, which every rank must refuse;tie_weights, which the decision's build needs on transformers 5.18, and the checkpoint-config read coversllm_configtoo;tests/unit/torch/models/kimi_k3/test_ep.py;tests/unit/torch/models,tests/unit/torch/utilsandtests/unit/recipe/test_presets.pypass on transformers 5.18 and 5.5.4. At this head, with transformers 5.18.0 and compressed-tensors 0.19.0, the whole GPU file passed: on 2 GPUs, 5 passed and[ep-half]skipped, as it needs 4; on 4 GPUs, all 6 passed. The Kimi-K3 case passed on both, the same-process whole-model reload refused on every rank. It loads through the spec and the loader directly, not through the plan decision.preparerecords its lasting patch where it makes it, so a step after it that raises still leaves the refusal armed (test_a_prepare_that_raises_after_patching_still_records_itfails without it), and_checkpoint_quantized's docstring says which revision it reads. Pre-commit against main passes, mypy included;tests/unit/torch/models,tests/unit/torch/utilsandtests/unit/recipe/test_presets.pypass on CPU, torch 2.14, on transformers 5.18 and 5.5.4. At this head, the whole GPU file passed, with transformers 5.18.0 and compressed-tensors 0.19.0: on 2 GPUs, 5 passed and[ep-half]skipped, as it needs 4; on 4 GPUs, all 6 passed; both Kimi cases passed on both.prepare's lasting patch keys onkimi_k3.ep.MODEL_TYPE, the one definition the spec registers, and the docs say the refusal comes before the load builds or loads the model. The plan decision's throwaway meta build runs earlier. The Kimi-K3 case's behaviour is unchanged since the GPU run at 4389363: the key has the same value. Pre-commit against main passes, mypy included;tests/unit/torch/models,tests/unit/torch/utilsandtests/unit/recipe/test_presets.pypass on CPU, torch 2.14, on transformers 5.18 and 5.5.4.test_model_load_utils.pynow holds the moved expert-parallel loader tests; its one import conflict kept both sides' imports, and the own diff is otherwise unchanged. Pre-commit against main passes, mypy included;tests/unit/torch/models,tests/unit/torch/utilsandtests/unit/recipe/test_presets.pypass on CPU, torch 2.14, on transformers 5.18 and 5.5.4. The base changed under the Kimi GPU cases ([OMNIML-6076] [3/3] Apply the model spec when deciding and loading expert-parallel #2746's load now checks the spec first), so the whole GPU file was rerun at this head, and passed, with transformers 5.18.0 and compressed-tensors 0.19.0: on 2 GPUs, 5 passed and[ep-half]skipped, as it needs 4; on 4 GPUs, all 6 passed. Both Kimi cases passed on both; they load through the spec and the loader directly, not through the plan decision.KimiK3Experts' fused parameters and eager loop are adapted from transformers v5.18.0'sMiniMaxM3VLExperts(Apache-2.0). The file carries the source link and notice, andLICENSElists the copyright holder. The human licensing sign-off (NVBug 6046893) is pending.Before your PR is "Ready for review"
mm_projectorunquantized in every preset that imports it; andprepare, which only an expert-parallel plan runs, patches the checkpoint's own classes for the process (see above).CONTRIBUTING.md: ✅ transformers'MiniMaxM3VLExperts(Apache-2.0), attributed in the file header andLICENSE; human licensing sign-off pending (NVBug 6046893). flash-linear-attention is a runtime requirement of the checkpoint's own remote code, not of ModelOpt.Additional Information
Part of expert-parallel PTQ for Hugging Face MoE models (was part 20 of 21). This PR is [2/2]; it merges after #3138 and #2748, which merge after #2746 and #2747 (#3132 has merged).
Summary by CodeRabbit