Skip to content

Add GLM-5.2 IndexShare DSA, MTP, and GGUF export support - #404

Draft
justinchuby wants to merge 28 commits into
mainfrom
glm5.2-moe-export
Draft

Add GLM-5.2 IndexShare DSA, MTP, and GGUF export support#404
justinchuby wants to merge 28 commits into
mainfrom
glm5.2-moe-export

Conversation

@justinchuby

@justinchuby justinchuby commented Jul 16, 2026

Copy link
Copy Markdown
Member

Summary

  • register Hugging Face glm_moe_dsa and llama.cpp/GGUF glm-dsa
  • export the 78-layer MLA+MoE backbone with GLM-5.2 IndexShare DSA
  • implement the fp32 indexer, official full/shared schedule (0, 1, 2, then every four layers from 6), cross-layer top-k reuse, and packed index-key cache
  • export the improved layer-78 MTP head as mtp/model.onnx, including embedding/hidden fusion, DSA+MoE block, and shared-head norm
  • map DSA indexer and supported NextN tensors from HF and GGUF
  • emit mtp_config.json for --runtime onnx-genai / ort-genai
  • retain dense MLA fallback through --glm-full-attention
  • preserve Tyrell's Q4/Q8 MatMulNBits and split-MLA/expert-streaming support

Runtime status

Portable ONNX preserves DSA selection numerics with standard ops (MatMul, LayerNormalization, Relu, TopK, ScatterElements, and Attention). No custom op is required for correctness. A selected-token sparse-attention kernel is still needed to realize GLM-5.2's long-context FLOP reduction instead of evaluating masked positions.

The MTP graph and step-0 topk_indices are exported. ORT GenAI does not currently expose separate MLA/indexer cache lengths or native MTP sidecar orchestration, so index_share_for_mtp_iteration remains a runtime follow-up; later speculative iterations recompute selection rather than silently reusing incorrect cache state.

IQ1/IQ2/IQ3 preservation remains a separate workstream and is unchanged here.

Validation

  • focused GLM/GGUF/ORT export set: 160 passed
  • related builder/config/task/MLA regressions: 279 passed
  • generic GLM graph/coverage/parity checks: 9 passed, 2 skipped
  • full non-integration suite excluding the pre-existing flaky ModernBERT parity case: 4509 passed, 329 skipped, 60 xfailed
  • generated target and MTP ONNX files pass onnx.checker, load in ONNX Runtime CPU, and execute one-token prefill with empty caches
  • Ruff import/unused checks and git diff --check pass

Follow-ups

  • add an efficient selected-token sparse-attention runtime kernel
  • add ORT GenAI native MTP orchestration with independent indexer-cache/control state
  • add an efficient selected-expert MoE runtime kernel
  • handle IQ1_M/IQ2_XXS/IQ3_XXS/IQ4_XS in the separate sub-4-bit workstream

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

Performance Comparison

Comparing 3dc671c80a8510

Model Metric Baseline Current Delta
bert (feature-extraction) model_size_bytes 359 KB 359 KB +0.0%
bert (feature-extraction) num_nodes 60 60 +0.0%
falcon model_size_bytes 364 KB 364 KB +0.0%
falcon num_nodes 68 68 +0.0%
gemma2 model_size_bytes 428 KB 428 KB +0.0%
gemma2 num_nodes 107 107 +0.0%
gpt2 model_size_bytes 388 KB 388 KB +0.0%
gpt2 num_nodes 54 54 +0.0%
llama model_size_bytes 425 KB 425 KB +0.0%
llama num_nodes 62 62 +0.0%
llama (static-cache) model_size_bytes 425 KB 425 KB +0.0%
llama (static-cache) num_nodes 58 58 +0.0%
mamba (ssm-text-generation) model_size_bytes 296 KB 296 KB +0.0%
mamba (ssm-text-generation) num_nodes 98 98 +0.0%
phi3 model_size_bytes 421 KB 421 KB +0.0%
phi3 num_nodes 60 60 +0.0%
phi3 (static-cache) model_size_bytes 421 KB 421 KB +0.0%
phi3 (static-cache) num_nodes 56 56 +0.0%
qwen2 model_size_bytes 425 KB 425 KB +0.0%
qwen2 num_nodes 62 62 +0.0%
qwen2 (static-cache) model_size_bytes 425 KB 425 KB +0.0%
qwen2 (static-cache) num_nodes 58 58 +0.0%
qwen3_5_moe (hybrid-text-generation) model_size_bytes 506 KB 506 KB +0.0%
qwen3_5_moe (hybrid-text-generation) num_nodes 275 275 +0.0%
qwen3_5_text (hybrid-text-generation) model_size_bytes 458 KB 458 KB +0.0%
qwen3_5_text (hybrid-text-generation) num_nodes 129 129 +0.0%
qwen3_5_vl (hybrid-qwen-vl) model_size_bytes 977 KB 977 KB +0.0%
qwen3_5_vl (hybrid-qwen-vl) num_nodes 413 413 +0.0%
t5 (seq2seq) model_size_bytes 836 KB 836 KB +0.0%
t5 (seq2seq) num_nodes 166 166 +0.0%
whisper (speech-to-text) model_size_bytes 1008 KB 1008 KB +0.0%
whisper (speech-to-text) num_nodes 128 128 +0.0%

No performance regressions.

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown

🏗️ Architecture Diff

Comparing 3dc671c80a8510

Model Sub-model Changes Status
bert (feature-extraction) model 0
falcon model 0
gemma2 model 0
gemma4 (gemma4) decoder 0
gemma4 (gemma4) embedding 0
gemma4 (gemma4) vision_encoder 0
gemma4_text model 0
gpt2 model 0
llama model 0
llama (static-cache) model 0
mamba (ssm-text-generation) model 0
phi3 model 0
phi3 (static-cache) model 0
qwen model 0
qwen (static-cache) model 0
qwen2 model 0
qwen2 (static-cache) model 0
qwen2_moe model 0
qwen2_moe (static-cache) model 0
qwen3 model 0
qwen3 (static-cache) model 0
qwen3_5_moe (hybrid-text-generation) model 0
qwen3_5_text (hybrid-text-generation) model 0
qwen3_5_vl (hybrid-qwen-vl) decoder 0
qwen3_5_vl (hybrid-qwen-vl) embedding 0
qwen3_5_vl (hybrid-qwen-vl) vision_encoder 0
qwen3_moe model 0
qwen3_moe (static-cache) model 0
qwen3_next (hybrid-text-generation) model 0
t5 (seq2seq) decoder 0
t5 (seq2seq) encoder 0
whisper (speech-to-text) decoder 0
whisper (speech-to-text) encoder 0

No architecture changes detected.


Legend: ⚪ No change · 🔵 Minor (attrs/inits) · 🟡 Moderate (nodes added/removed) · 🔴 Major (interface changed)

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-pass export support for GLM-5.2 / GLM-MoE DSA by wiring the architecture into the registry, enabling GGUF (glm-dsa) config/weight ingestion (including split MLA KV-B fusion and expert-by-expert streaming), and extending the quantized import path to cover additional supported layouts. Also accepts --runtime onnx-genai as an alias for ort-genai.

Changes:

  • Register glm_moe_dsa (HF) / glm-dsa (GGUF) and add a GLM-specific model wrapper reusing the DeepSeek-V3 MLA+MoE backbone with dense-attention fallback.
  • Extend GGUF import: map GLM tensor names, extract GLM metadata into ArchitectureConfig, fuse split MLA KV-B weights, and stream stacked expert tensors.
  • Extend quantized repacking/CLI: support float requantization to 8-bit block-32 targets and accept --runtime onnx-genai.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/cli_test.py Adds CLI coverage for --runtime onnx-genai alias.
src/mobius/main.py Implements onnx-genai as an alias for ort-genai.
src/mobius/models/glm_moe_dsa.py Adds GLM-5.2 DSA model wrapper over DeepSeek MLA+MoE with weight filtering.
src/mobius/models/glm_moe_dsa_test.py Adds GLM-specific graph/config/quantization/preprocess tests.
src/mobius/models/deepseek.py Adds quantized linear/embedding injection via linear_class + QuantizedEmbedding option.
src/mobius/components/_deepseek_mla.py Threads linear_class through MLA projections.
src/mobius/components/_moe.py Threads linear_class into expert MLPs.
src/mobius/models/init.py Exports GlmMoeDsaCausalLMModel.
src/mobius/_registry.py Registers glm_moe_dsa and adds a default model id.
src/mobius/_configs/_base.py Adds DSA/MTP-related config fields and enables rope_interleave default for glm_moe_dsa.
src/mobius/integrations/gguf/_tensor_mapping.py Adds GLM-DDA tensor mapping + skips indexer/nextn tensors.
src/mobius/integrations/gguf/_tensor_mapping_test.py Tests GLM tensor mapping behavior.
src/mobius/integrations/gguf/_config_mapping.py Adds GLM metadata key mapping + postprocess to derive MLA/DSA fields.
src/mobius/integrations/gguf/_reader_test.py Tests GGUF arch→model_type mapping and GLM config extraction.
src/mobius/integrations/gguf/_builder.py Adds split KV-B fusion, expert-by-expert repack, and allows 8-bit requant targets.
src/mobius/integrations/gguf/_builder_test.py Tests GLM normalization behaviors and Q8 requantized head behavior.
src/mobius/integrations/gguf/_repacker.py Extends float requantization to bits=8, block=32.
src/mobius/integrations/gguf/_repacker_test.py Adds Q8 float requantization round-trip bound test.
docs/research/glm52-export.md Documents GLM-5.2 verified architecture, current scope, and follow-ups.

Comment thread src/mobius/integrations/gguf/_builder.py
Comment thread src/mobius/models/glm_moe_dsa_test.py
@justinchuby justinchuby changed the title Add GLM-5.2 / GLM-MoE export support Add GLM-5.2 IndexShare DSA, MTP, and GGUF export support Jul 16, 2026
@justinchuby

Copy link
Copy Markdown
Member Author

♻️ Rebased onto main to pick up #398 (onnx-genai InferenceMetadata export). Resolved a __main__.py conflict: --runtime onnx-genai now routes to #398's write_inference_metadata (emits inference_metadata.yaml) instead of the earlier ad-hoc ort-genai alias, and the stale alias CLI test was updated to assert the metadata path. GLM-5.2 MoE + IndexShare DSA + MTP commits are intact on top.

justinchuby and others added 7 commits July 21, 2026 22:47
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The IndexShare DSA indexer shares the model's rotary_emb cos/sin cache,
which is sized qk_rope_head_dim/2. GlmMoeDsaIndexer.project_key passed
rotary_embedding_dim=index_head_dim//2 to the opset-24 RotaryEmbedding op,
which then expects a cos cache of (index_head_dim//2)/2 and fails shape
validation at runtime:

  Input 'cos_cache' dimension 2 should be same as head_size / 2 or
  rotary_embedding_dim / 2, got 4

The indexer key spans the full index_head_dim and must be fully rotated,
matching the main MLA q_rope/k_rope calls (rotary_embedding_dim=0). Fixes
end-to-end inference of glm_moe_dsa models through the onnx-genai engine.

Adds a regression test asserting indexer RotaryEmbedding nodes use full
rotation (rotary_embedding_dim=0).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sibling of export_glm_tiny.py. Attaches an int4 block-32 asymmetric
QuantizationConfig so the tiny glm_moe_dsa linear projections and
per-expert MoE MLPs are emitted as com.microsoft::MatMulNBits, producing
an onnx-genai-loadable artifact for the quantized E2E smoke test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…experts

Add FusedQuantizedMoE component that emits a single int4/block-32 expert-major
com.microsoft::QMoE node (swiglu_fusion=1) for the shared GLM/DeepSeek routed
expert stack, gated behind ArchitectureConfig.fused_quantized_moe. GLM sigmoid +
noaux_tc routing is preserved exactly: scores_for_choice feeds QMoE router_probs
for top-k selection, and the normalized+scaled combine weights are scattered
into the optional router_weights input (normalize_routing_weights=0). Adds
DeepSeekMoEGate.route_for_qmoe, export_glm_tiny_qmoe.py, and a fused-path unit
test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 21, 2026 22:47
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 6 comments.

Comment thread src/mobius/components/_moe.py
Comment thread export_glm_tiny_quant.py Outdated
Comment thread export_glm_tiny_quant.py Outdated
Comment thread export_glm_tiny_qmoe.py Outdated
Comment thread export_deepseek_v2_tiny.py Outdated
Comment thread docs/research/glm52-export.md
Copilot AI review requested due to automatic review settings July 21, 2026 22:54
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (5)

src/mobius/components/_moe.py:296

  • FusedQuantizedMoE is documented as symmetric-only (no zero-points), but init does not enforce QuantizationConfig.sym=True. If a caller enables fused_quantized_moe with asymmetric quantization, the graph will still be built but will pass None for *zero_points inputs to QMoE, which is inconsistent with the quantization setup.
        qc = config.quantization
        bits = qc.bits
        block_size = qc.group_size
        if bits not in (1, 2, 4, 8):
            raise ValueError(f"QMoE expert_weight_bits must be 1/2/4/8, got {bits}")
        if block_size < 16 or (block_size & (block_size - 1)):
            raise ValueError(f"QMoE block_size must be a power of 2 >= 16, got {block_size}")

export_glm_tiny_quant.py:11

  • This docstring states mobius has no fused com.microsoft::QMoE emitter and that QMoE is not emitted, but this PR introduces FusedQuantizedMoE and export_glm_tiny_qmoe.py. The docstring is now inaccurate and will mislead readers.
mobius has no fused ``com.microsoft::QMoE`` emitter for any model (only an
unquantized ``com.microsoft::MoE`` path in gemma4), so QMoE is NOT emitted
here — see the decision note for the scoping gap.

export_glm_tiny_quant.py:36

  • Hard-coded absolute output paths make this script non-portable (and leak a developer-specific directory layout into the repo). Prefer environment variables and a safe default like /tmp so contributors can run it without editing source.
OUT = "/home/justinchu/glm-e2e-artifacts/glm-5.2-tiny-q4"
FP32_OUT = "/home/justinchu/glm-e2e-artifacts/glm-5.2-tiny"

export_glm_tiny_qmoe.py:36

  • Hard-coded absolute output paths make this script non-portable. Prefer environment variables and a safe default like /tmp so contributors can run it without editing source.
OUT = "/home/justinchu/glm-e2e-artifacts/glm-5.2-tiny-qmoe"
FP32_OUT = "/home/justinchu/glm-e2e-artifacts/glm-5.2-tiny"

export_deepseek_v2_tiny.py:19

  • Hard-coded absolute output path makes this script non-portable. Prefer an environment variable and a safe default like /tmp so contributors can run it without editing source.
OUT = "/home/justinchu/ds-e2e-artifacts/deepseek-v2-tiny"

Comment thread src/mobius/models/deepseek.py
Copilot AI review requested due to automatic review settings July 21, 2026 23:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (4)

src/mobius/models/deepseek.py:328

  • fused_quantized_moe switches the MoE implementation to FusedQuantizedMoE, which expects packed expert-major tensors (fc1_experts_weights, fc1_scales, fc2_experts_weights, fc2_scales) instead of the existing per-expert projections. The current GGUF loader streams routed expert tensors into per-expert keys (e.g. ...mlp.experts.{i}.gate_proj.weight), and existing preprocessors map those under ...mlp.moe.experts.*, so enabling this flag is likely to make weight application fail due to missing packed initializers. Either add a packing step (similar to Gemma4’s fused-expert preprocessing) or raise a clear error when packed tensors aren’t provided.
        qc = config.quantization
        use_fused_qmoe = (
            config.fused_quantized_moe and qc is not None and qc.quant_method != "none"
        )
        if use_fused_qmoe:

export_glm_tiny_quant.py:11

  • The module docstring claims mobius has no fused com.microsoft::QMoE emitter, but this PR adds FusedQuantizedMoE and a fused_quantized_moe config flag. This comment is now misleading for readers using these export helpers.
mobius has no fused ``com.microsoft::QMoE`` emitter for any model (only an
unquantized ``com.microsoft::MoE`` path in gemma4), so QMoE is NOT emitted
here — see the decision note for the scoping gap.

export_glm_tiny_quant.py:36

  • Hard-coded absolute output paths (/home/justinchu/...) make this script non-portable and will fail in most environments/CI. Consider deriving these from CLI args or environment variables with a safe default (e.g., relative to the current working directory).
OUT = "/home/justinchu/glm-e2e-artifacts/glm-5.2-tiny-q4"
FP32_OUT = "/home/justinchu/glm-e2e-artifacts/glm-5.2-tiny"

export_glm_tiny_qmoe.py:36

  • Hard-coded absolute output paths (/home/justinchu/...) make this script non-portable and will fail in most environments/CI. Consider deriving these from CLI args or environment variables with a safe default (e.g., relative to the current working directory).
OUT = "/home/justinchu/glm-e2e-artifacts/glm-5.2-tiny-qmoe"
FP32_OUT = "/home/justinchu/glm-e2e-artifacts/glm-5.2-tiny"

Comment thread tests/cli_test.py Outdated
Comment thread src/mobius/models/glm_moe_dsa.py Outdated
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 21, 2026 23:15
@CLAassistant

CLAassistant commented Jul 29, 2026

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
1 out of 2 committers have signed the CLA.

✅ justinchuby
❌ Copilot
You have signed the CLA already but the status is still pending? Let us recheck it.

Comment thread src/mobius/models/glm_moe_dsa.py Fixed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 43 out of 43 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

src/mobius/models/glm_moe_dsa.py:294

  • The PR description states the DSA path is exported using only standard ONNX ops (no custom op required for correctness), but this implementation emits a custom pkg.nxrt::IndexShare node. That makes the exported model dependent on a non-standard opset and contradicts the stated portability/correctness claim.

Please either (a) add a portable path that lowers DSA to standard ops (e.g., dense additive mask + Attention) behind a config/CLI flag, or (b) update the PR description/docs to explicitly call out the custom-op dependency for DSA builds.
docs/research/glm52-export.md:88

  • This section claims the exported DSA graph uses only standard ONNX ops and requires no custom op for correctness, but the implementation and tests in this PR emit a custom pkg.nxrt::IndexShare op (and assert Attention count is 0). The doc should be updated to match the current export contract.
Portable ONNX preserves DSA selection numerics by converting the selected
indices to an additive mask for the standard `Attention` operator. No custom op
is required for correctness. Practical million-token performance still needs
a selected-token sparse-attention kernel so the runtime avoids evaluating
masked K/V positions.

src/mobius/rewrite_rules/_group_query_attention_test.py:206

  • _build_tiny_mla_graph() is introduced here but is not referenced anywhere else in this test module. Keeping unused helpers in test code makes future refactors harder and can confuse readers about intended coverage.

Either wire this helper into a test case, or remove it if it’s leftover from earlier iterations.
src/mobius/rewrite_rules/_decompose_attention_test.py:172

  • The acceptable error tolerance for the softcap cases was loosened from 1e-5 to 5e-3, which is a large relaxation and risks masking real parity regressions in the attention decomposition.

Please add a short comment explaining why this larger atol is required (e.g., known numerical instability/EP differences), and consider tightening it as much as possible.

Keep fused QMoE activation inputs in the model dtype and fail fast for CUDA exports that require router_weights, since ORT CUDA QMoE currently ignores input 14. Add regression tests for activation dtype and the CUDA guard.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Signed-off-by: justinchuby <justinchuby@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 29, 2026 17:38
Comment thread src/mobius/components/_moe_test.py Fixed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 43 out of 43 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (2)

src/mobius/main.py:291

  • In the --model code path, --glm-full-attention always passes config_overrides into build(), so it will also disable DSA/MTP for any model type, not just glm_moe_dsa. This is surprising for a GLM-specific flag and can silently alter other architectures that use these fields. Consider loading the HF config when the flag is present, verifying model_type == 'glm_moe_dsa', and only then applying overrides.
            config_overrides=(
                {"use_dsa": False, "num_nextn_predict_layers": 0}
                if args.glm_full_attention
                else None
            ),

src/mobius/main.py:251

  • --glm-full-attention is applied unconditionally when building from --config, even if the resolved model_type is not glm_moe_dsa. Since use_dsa / num_nextn_predict_layers also affect other architectures (e.g. DeepSeek-V4 CSA/MTP), this flag can silently change unrelated exports. Please validate the model type and fail fast when the flag is used on non-GLM-5.2 configs.

This issue also appears on line 287 of the same file.

        if args.glm_full_attention:
            config = dataclasses.replace(
                config,
                use_dsa=False,
                num_nextn_predict_layers=0,

@justinchuby
justinchuby marked this pull request as ready for review July 30, 2026 04:57
@justinchuby
justinchuby requested a review from a team July 30, 2026 04:57
@justinchuby

Copy link
Copy Markdown
Member Author

@copilot merge from main

Copilot AI added 2 commits July 30, 2026 05:02
Signed-off-by: GitHub <noreply@github.com>

# Conflicts:
#	src/mobius/integrations/gguf/_tensor_mapping.py
#	src/mobius/integrations/onnx_genai/auto_export.py
Copilot AI review requested due to automatic review settings July 30, 2026 05:03

Copilot AI commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

@copilot merge from main

Merged origin/main (6 commits: WebGPU GQA tests, Phi-3.5 LongRoPE/CLIP, Qwen3.5-dense, explicit ONNX GenAI metadata, Gemma3 multimodal, ORT ≥1.28 softcap test) in 3da96d0. Resolved two file conflicts by keeping both sides (_GLM_DSA_MAPPING + updated _QWEN35_HYBRID_EXTRAS comment in _tensor_mapping.py; _write_mtp_config + _add_explicit_io_to_file in auto_export.py).

Also fixed the two CI linter failures brought in with the last commits (6f0cfe2):

  • RUF043 (_moe_test.py:275): changed match="CUDA.*ignores router_weights"match=r"CUDA.*ignores router_weights" (raw string).
  • RUFF-FORMAT (glm_moe_dsa.py): ran ruff format to apply the pending whitespace/line-wrap changes.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 43 out of 43 changed files in this pull request and generated no new comments.

Comments suppressed due to low confidence (4)

src/mobius/models/glm_moe_dsa.py:287

  • The PR description claims Portable ONNX correctness for DSA uses only standard ops and that no custom op is required, but this implementation hard-depends on the custom pkg.nxrt::IndexShare op for the DSA path. That means a generic ONNX Runtime / non-nxrt environment cannot run the exported graph as-is.

Either (a) add a portable standard-op fallback for DSA (e.g., dense Attention with a ScatterElements-built sparse mask) behind a config flag, or (b) update the PR description/docs to state that DSA execution requires the pkg.nxrt custom-op runtime.
src/mobius/models/glm_moe_dsa.py:576

  • GlmMoeDsaCausalLMModel.__init__ only computes/validates the IndexShare schedule when config.indexer_types is None. If a caller provides an explicit indexer_types list with the wrong length, the model will accept it and later code (task cache packing, per-layer indexing) can fail with an IndexError instead of the intended clear ValueError.

Consider validating whenever DSA is enabled (e.g., if config.use_dsa: config.indexer_types = _indexer_types(config)), since _indexer_types() already performs the length check for caller-supplied schedules.
src/mobius/models/glm_moe_dsa.py:26

  • This file imports apply_rotary_pos_emb from a private module path (mobius.components._rotary_embedding). Project convention is to import components/helpers from the public mobius.components API to avoid brittle private-module dependencies.

Consider re-exporting apply_rotary_pos_emb from mobius.components and importing it from there (or provide a small local wrapper) so the new model doesn’t depend on a private module path.
src/mobius/rewrite_rules/_decompose_attention_test.py:172

  • The parity tolerance for the softcap cases was loosened from 1e-5 to 5e-3, which is a large change and can mask regressions in the softcap path. If this is required due to a known ORT/kernel numeric change, please add a short comment explaining why this tolerance is expected/acceptable (and ideally link to an issue or ORT version note).

justinchuby and others added 2 commits August 3, 2026 18:28
# Conflicts:
#	src/mobius/_builder.py
#	src/mobius/integrations/ort_genai/auto_export.py
…metric-MoE zero-points, docstrings, --glm-full-attention help

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings August 3, 2026 19:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 43 out of 43 changed files in this pull request and generated no new comments.

Suppressed comments (3)

src/mobius/integrations/onnx_genai/auto_export.py:577

  • In the special-case ['model', 'mtp'] branch, write_decoder_metadata() is called but _add_explicit_io_to_file() is not. Every other pipeline branch (and the single-decoder fallback) augments the emitted YAML with explicit I/O derived from the actual ONNX ports; skipping it here can leave inference_metadata.yaml missing/incorrect inputs/outputs role data for the package.
    if component_names == ["model", "mtp"]:
        path = write_decoder_metadata(
            output_dir, config=resolved_config, kv_native_dtype=kv_native_dtype
        )
        artifacts = {
            "inference_metadata": path,
            "mtp_config": _write_mtp_config(output_dir, resolved_config),
        }

src/mobius/models/glm_moe_dsa.py:569

  • The class docstring says "portable IndexShare", but the DSA path relies on a custom-domain op (pkg.nxrt::IndexShare). "Portable" is misleading here unless there is also a standard-op fallback.
    docs/research/glm52-export.md:88
  • This section claims the DSA path is "Portable ONNX" with only standard ops and "No custom op". However, the implementation currently emits the custom pkg.nxrt::IndexShare operator (and explicitly avoids Attention/ScatterElements) for the DSA attention path, so a runtime implementation of that custom op is required.
Portable ONNX preserves DSA selection numerics by converting the selected
indices to an additive mask for the standard `Attention` operator. No custom op
is required for correctness. Practical million-token performance still needs
a selected-token sparse-attention kernel so the runtime avoids evaluating
masked K/V positions.

Copilot AI review requested due to automatic review settings August 4, 2026 03:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 43 out of 43 changed files in this pull request and generated no new comments.

Suppressed comments (2)

docs/research/glm52-export.md:88

  • This section says the DSA path is implemented by converting selected indices into an additive mask for the standard Attention op and that no custom op is required. The current implementation lowers sparse attention to the custom pkg.nxrt::IndexShare op (and only the dense fallback uses standard Attention), so this paragraph is misleading and contradicts the exported graphs.
Portable ONNX preserves DSA selection numerics by converting the selected
indices to an additive mask for the standard `Attention` operator. No custom op
is required for correctness. Practical million-token performance still needs
a selected-token sparse-attention kernel so the runtime avoids evaluating
masked K/V positions.

src/mobius/integrations/onnx_genai/auto_export.py:577

  • In the {model, mtp} package case you return write_decoder_metadata(...) without calling _add_explicit_io_to_file(...), unlike the other code paths. That means the emitted inference_metadata.yaml won’t be augmented with the actual ONNX input/output port roles even when real models are present, which is inconsistent with the rest of the emitter and can lead to stale metadata if the decoder I/O contract changes.
    if component_names == ["model", "mtp"]:
        path = write_decoder_metadata(
            output_dir, config=resolved_config, kv_native_dtype=kv_native_dtype
        )
        artifacts = {
            "inference_metadata": path,
            "mtp_config": _write_mtp_config(output_dir, resolved_config),
        }
        tokenizer_path = _write_hf_tokenizer(output_dir, source)
        if tokenizer_path is not None:
            artifacts["tokenizer"] = tokenizer_path
        return artifacts

@justinchuby
justinchuby marked this pull request as draft August 4, 2026 17:00
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.

5 participants