Skip to content

[megatron] 5/n towards Kimi K2.6: Kimi K2.5-family (KimiK25ForConditionalGeneration) support with INT4 QAT + LoRA - #2027

Draft
casper-hansen wants to merge 7 commits into
NovaSky-AI:mainfrom
casper-hansen:casper/kimi-5-k25-bridge
Draft

[megatron] 5/n towards Kimi K2.6: Kimi K2.5-family (KimiK25ForConditionalGeneration) support with INT4 QAT + LoRA#2027
casper-hansen wants to merge 7 commits into
NovaSky-AI:mainfrom
casper-hansen:casper/kimi-5-k25-bridge

Conversation

@casper-hansen

Copy link
Copy Markdown
Contributor

What

  • KimiK25TextBridge: DeepSeek-V3 bridge adapted to the unified VL checkpoint (text_config + language_model. weight prefix); text-only training with the vision tower left frozen in the inference engine (language_model_only), with a clear error for full-VLM requests.
  • vLLM: declare KimiK25ForConditionalGeneration LoRA-capable (patch applied in the SkyRL worker extension import; the DeepSeek decoder already supports LoRA). The worker-side LoRA gate is an instance-level runtime Protocol check that requires every SupportsLoRA member (is_3d_moe_weight, is_non_gated_moe, lora_skip_prefixes too); we mirror the DeepSeek decoder's declarations and pin it with a protocol-membership test.
  • Forward HF cache/auth env vars (HF_HOME & co) to Ray workers so model paths resolve identically on worker actors.
  • Truncated-real-model GPU integration test: bridge dispatch, provider construction, weight round-trip, LoRA export (incl. capacity-normalized expert LoRA).

Why

Kimi K2.5-family checkpoints (K2.6, K2.7-Code) are unified VL checkpoints whose language model is DeepSeek-V3 architecture; there is no upstream bridge for the arch name, so AutoBridge dispatch fails without this.

Stacked on 4/n (#2026) -- the GPU test exercises normalize_moe_lora; the diff includes 4/n until it merges. Marked draft until then. Part of the Kimi K2.x series (follow-up to #1862).

Made with Cursor

casper-hansen and others added 2 commits August 13, 2026 08:08
…viable for very large MoE

On a 384-expert / 61-layer model (Kimi-K2.7-Code) the first LoRA disk
sync OOM-killed workers node by node; _save_lora_adapters_and_sync had
three compounding costs:

- Only rank 0 wrote the PEFT files, which requires a shared filesystem:
  with merge_lora=false every vLLM worker hot-loads the adapter from its
  *local* filesystem. The first rank of each node now writes (identical
  content, atomic renames so engines never observe partial files), and
  rank 0 sends the LoraLoadRequest after a barrier.
- Every rank kept a full copy of the gathered adapter state; with
  per-expert PEFT keys that is tens of GB per rank (~650 GB per node in
  aggregate). Only the per-node writer ranks materialize it now -- the
  others just participate in the collectives.
- The float32 upcast doubled the state and the on-disk adapter for no
  fidelity gain (adapters train in bf16 and vLLM casts on load). The
  export keeps the training dtype.

Also expose megatron-bridge's capacity-normalized MoE LoRA
(megatron_config.lora_config.normalize_moe_lora: expert rank =
rank/topk); at full rank the per-expert export is inherently huge (41 GB
at rank 32) and is rewritten + re-read by every engine each sync (~5 GB
with normalization).

Co-authored-by: Cursor <cursoragent@cursor.com>
…onalGeneration) support with INT4 QAT + LoRA

- KimiK25TextBridge: DeepSeek-V3 bridge adapted to the unified VL
  checkpoint (text_config + language_model. weight prefix); text-only
  training with the vision tower left frozen in the inference engine
  (language_model_only), with a clear error for full-VLM requests.
- vLLM: declare KimiK25ForConditionalGeneration LoRA-capable (patch
  applied in the SkyRL worker extension import; the DeepSeek decoder
  already supports LoRA). The worker-side LoRA gate is an instance-level
  runtime Protocol check that requires every SupportsLoRA member
  (is_3d_moe_weight, is_non_gated_moe, lora_skip_prefixes too); mirror
  the DeepSeek decoder's declarations and pin it with a
  protocol-membership test.
- Forward HF cache/auth env vars (HF_HOME & co) to Ray workers so model
  paths resolve identically on worker actors.
- Truncated-real-model GPU integration test: bridge dispatch, provider
  construction, weight round-trip, LoRA export (incl. capacity-normalized
  expert LoRA via normalize_moe_lora).

Stacked on 4/n (the GPU test exercises normalize_moe_lora).

Co-authored-by: Cursor <cursoragent@cursor.com>
casper-hansen and others added 5 commits August 13, 2026 09:23
The flag lives on the shared MegatronLoraConfig and megatron-bridge
supports it on both PEFT types; lora_type=canonical_lora silently kept
full expert rank.

Co-authored-by: Cursor <cursoragent@cursor.com>
vLLM 0.26 (now on main via NovaSky-AI#1854) added an annotation-only lora_manager
member to the SupportsLoRA protocol. The patched
KimiK25ForConditionalGeneration is checked *structurally* (unlike native
nominal subclasses), so the member must exist on the class; without it the
supports_lora() gate rejects the model. Set it to None, the documented
'not yet set' state.

Also future-proof KimiK25TextBridge.build_conversion_tasks for
megatron-bridge 0.7.0 (NovaSky-AI#2042), which grows the base signature with a
weight_dtype keyword: forward *args/**kwargs instead of pinning the
two-argument shape.

Co-authored-by: Cursor <cursoragent@cursor.com>
@casper-hansen

Copy link
Copy Markdown
Contributor Author

Updated for the vLLM 0.26 bump (#1854, now on main) and merged main in (via the 4/n base branch):

  • vLLM 0.26 added an annotation-only lora_manager member to the SupportsLoRA protocol. Native models are nominal subclasses and skip the structural check, but the patched KimiK25ForConditionalGeneration is checked structurally via a runtime-checkable Protocol isinstance(), which requires every member to exist on the class. The patch now sets cls.lora_manager = None (the documented "not yet set" state); the worker's LoRA mixin assigns the real manager at runtime.
  • KimiK25TextBridge.build_conversion_tasks now forwards *args, **kwargs so it stays compatible with megatron-bridge 0.7.0 ([deps][megatron] bump megatron-bridge to 0.7.0 and megatron-core to 0.20.0 #2042), which grows the base signature with a weight_dtype keyword. If [deps][megatron] bump megatron-bridge to 0.7.0 and megatron-core to 0.20.0 #2042 lands first, its module-level None-task filter makes our per-bridge filter redundant but harmless; if this lands first, the filter is still needed on the current bridge pin. Heads-up that [deps][megatron] bump megatron-bridge to 0.7.0 and megatron-core to 0.20.0 #2042 and this PR both touch the same import block in model_bridges.py, so whichever merges second will need a trivial conflict resolution.

The rest of the K2.5 bridge (mapping re-rooting via get_common_mapping_list, _TextConfigShim, worker-wrap patch application) is unchanged: the 0.7.0 get_common_mapping_list keeps its signature, and hf_param is still only read at resolve/export time, so the post-construction prefixing stays valid.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant