fix(MODEL-TEXT-GLM4-MOE-LITE-ROUTER-F32): GLM's router is fp32 because Glm4MoE says so, not because a config key does - #2946
Merged
Conversation
…ss property upstream, and our port reads it from a config key GLM does not ship `Glm4MoeLiteForCausalLM`'s MoE block is `Glm4MoeLite`, a bare subclass of `Glm4MoE`, whose gate is an fp32 `nn.Linear` fed fp32 activations with no config key in the path (glm4_moe.py:141-146,205,218 at 5559679229). Our port composes the DeepSeek-V2 block, whose router dtype comes from `moe_router_dtype`, and the published GLM-4.7-Flash config.json does not declare it — so our router logits round to bf16 in front of a top-4-of-64 `noaux_tc` selection. The spec lands before the implementation so the commit order proves it, and it records plainly that this host can neither run the repaired forward against the oracle nor move the frozen `our_ids.npy` the SACRED gate compares. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…e Glm4MoE says so, not because a config key does `Glm4MoeLiteForCausalLM` composes the DeepSeek-V2 forward and loader in this port, so its MoE router logit dtype came from `ParseDeepseekV2Params`, which mirrors `_get_moe_router_dtype` and resolves fp32 only for `model_type == "glm_moe_dsa"` or an explicit `moe_router_dtype: "float32"`. The published `zai-org/GLM-4.7-Flash` config.json declares neither, so our router logits were rounded to bf16 before the top-k. Upstream `Glm4MoeLiteForCausalLM` does not use `DeepseekV2MoE` at all. Its MoE block is `Glm4MoeLite`, a bare subclass of `Glm4MoE` (glm4_moe_lite.py:86-87, :161-165 at 5559679229), whose gate is `nn.Linear(hidden_size, n_routed_experts, bias=False, dtype=torch.float32)` (glm4_moe.py:141-146) fed `hidden_states.to(dtype=torch.float32)` (:218) and declared `router_logits_dtype=torch.float32` (:205). No config key participates. `ParseGlm4MoeLiteParams` is the one place that says so, and all three GLM registry hooks resolve through it, including the loader, so the params the forward reads cannot drift from the params the config hook validated. The measurement, not the argument: at GLM's real router configuration — 64 experts, top-4, `noaux_tc` sigmoid with the correction bias, `norm_topk_prob`, `routed_scaling_factor` 1.8 — rounding the logits to bf16 changes the selected top-4 SET for 34 of 4096 tokens. That is a different expert's weights running, which no tolerance absorbs, and it is what `deepseek_v2.h:164-168`'s "a token gate cannot see this either way" does not cover: that sentence is about a store that is too WIDE. This does not claim the 59-position SACRED gap is closed. No GLM-4.7-Flash snapshot is on this host or the NAS, the committed `our_ids.npy` is frozen, and the spec records both under `## Owed`. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…ured, including the 34/4096 selection flips The spec claimed the bf16 router store is a discrete selection error; the tests now say by how much. At GLM's real router configuration the store changes the top-4 SET for 34 of 4096 tokens, the dtype is observable through the forward at 6 of 16 fixed seeds, and the production load-and-forward path carries the f32 arm byte for byte. The mutation table records that every mutation rebuilt clean, so none of them was caught by the compiler rather than by a test, and that deleting the loader call site reds only the reachability case. It also names the revision the upstream anchors were read at, `5559679229`, which is the goldens' pin and not the active `e126687a9a`, and owes the re-read as O5 rather than letting the reader infer it. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…om the committed artifacts, and say what it rules out The per-prompt mismatch counts [2,6,6,12,12,11,10,0] looked like a distribution with structure; they are almost entirely `16 - first divergent index`, because after the first divergence the sequences barely re-converge. So the only real variable is WHERE each prompt first diverges, and that localises without a checkpoint. The absolute positions are 19, 10, 20, 16, 10, 15, 12 — no shared page, block or layer boundary, and no dependence on prompt length — which is evidence against a positional or structural defect. No prompt diverges before generated index 4, which a small numerical perturbation predicts and a structural bug does not, and the one prompt that never diverges is the memorised quotation. This supports the router-dtype repair without claiming it is the whole gap; O1 still owes the re-capture. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…light gates instead of calling the run green `scripts/agent-preflight.sh --staged` exits 1 here. One failure is the documented load flake in the x86 floor harness, which passes standalone on this same tree with 10 of 10 OK. The other is `test_oracle_pin` asserting that the distribution and runtime version strings differ, which they no longer do; both constants come from files this change does not touch, so it is a property of `origin/main` and is already owned as #2931. Recording the reasons beside the failures is what stops a later reader taking "preflight exits 1" as a verdict on this change, and stops this session taking the exit code as a pass. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
Fifteen commits, none of them touching `glm4_moe_lite_registry.cpp`, `deepseek_v2_weights.cpp` or the GLM goldens. `tests/CMakeLists.txt` auto-merged because the new registration and main's additions are in different blocks. One of them bears directly on this row's prose and is the reason this merge is read rather than taken: `b55f63ed6` records that a token gate HAS now run at the active pin `e126687a9a`. The spec still says the upstream anchors here were read at `5559679229`, which stays true and is what O5 owes; what changes is that the pin is no longer entirely unmeasured, so the spec must not imply otherwise. The row's gate is re-run on the merged tree, because a merge has falsified a pull request's own prose in this repository before. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
…pec's own claim about the active pin, so repair it The spec said no gate has run at `e126687a9a`. `b55f63ed6`, merged in the commit before this one, records that one has: job 7386f034 on GB10, 2026-09-04, `IDS mismatched_positions 0 of 96`, `TOKENGATE_VERDICT PASS`. That gate is a different model's battery, so what it changes is the sentence and not the obligation: no `Glm4MoeLiteForCausalLM` golden has been re-captured at the active pin, and O5 still owes the re-read of `glm4_moe.py` there. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5 [claude-code]
mudler
added a commit
that referenced
this pull request
Sep 5, 2026
…and #2946 `main` advanced by one commit while this branch's gate ran -- `1043101d3 docs(ENG-ATTENTION-WINDOW): reopen W2 and correct a Now section that claimed both sides`. It is a documentation correction on an unrelated row and touches none of the three pull requests' files: neither `glm4_moe_lite*`, nor the gfx1151 arm spec and its evidence directory, nor the portq reconcile record. The merge is clean and nothing it brings in reads on what this branch asserts. THE GATE IS NOT GREEN, AND THIS IS WHAT IS RED. `scripts/agent-preflight.sh` reported `1 gate(s) failed: test_cpu_x86_llamacpp_floor`, in `test_a_contended_leg_is_discarded_and_never_summarised`, which asserts the harness exits 2 and observed 4. Those are two different stop conditions, not a near-miss. `scripts/cpu-x86-llamacpp-floor.sh:336` exits 2 on `GIVING_UP too many discards` after 24 attempts, which is what the case forces with `FOREIGN_MAX="-1"`. Line `:127` exits 4 on `NO_QUIET_WINDOW`, meaning the box never got quiet enough to START a leg. The case can therefore stop for a second legitimate reason it does not admit. THIS CHANGE DID NOT CAUSE IT, and that was established rather than assumed: - `tests/scripts/test_cpu_x86_llamacpp_floor.py` is BYTE-IDENTICAL to `origin/main`; `git diff --stat origin/main HEAD -- <path>` is empty. - `scripts/cpu-x86-llamacpp-floor.sh`, the harness the case actually executes, is BYTE-IDENTICAL to `origin/main` too. - Two of the three pull requests touch no compiled code at all; the third touches `glm4_moe_lite_registry.cpp`, one new header and one new test. - The whole file passes on rerun on this exact tree: `10 passed in 263.56s`. THE CASE IS INTERMITTENT AND THE CAUSE IS NOT ESTABLISHED. My first reading was that host load causes it: the failure carries the host's own load average, `32.35 35.66 34.85`, taken while four sessions ran `agent-preflight.sh` concurrently. The rerun refutes load as the cause -- it passed 10 of 10 at load `60.46` falling to `45.73`, HIGHER than the load it failed at. So the mechanism I first proposed does not survive its own control, and #2978 carries that correction rather than the tidier story. The `builders` gate is ruled out: `pgrep -x "no-such-process-name"` matches nothing and every captured line reads `builders=0`. What survives is enough to land on. The case is intermittent on a tree byte-identical to `main` in both the test and the harness, so whatever the trigger is, this change cannot be it. Nothing was weakened to get here: no assertion deleted, no scope widened, and no retry-until-green -- the one failing run and the eleven passing ones are all reported, and the gate is filed as #2978 rather than silenced. `check-pr-size.py` is one of five gates preflight SKIPS because it cannot supply `--base`/`--head` (#2826). Run by hand against `origin/main` it returns `OK: every explicit path class is within its review budget`. The other four need `--compile-commands` or `--vendored-root`; `tree-compiles` covered the two translation units this change reaches. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:claude-opus-5-1m [claude-code]
Collaborator
Author
|
Landed on The gate was NOT green and the landing commit says so rather than implying otherwise: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Glm4MoeLiteForCausalLM(GLM-4.7-Flash) computed its MoE router logits inbf16. Upstream computes them in fp32, unconditionally, and it does so
because GLM's MoE block is a different class from DeepSeek-V2's — not because
of anything in the config.
The divergence, read at the pin the goldens were captured on (
5559679229)Upstream
Glm4MoeLiteForCausalLMdoes not useDeepseekV2MoE. Its MoE block isGlm4MoeLite, a bare subclass ofGlm4MoE(glm4_moe_lite.py:86-87,instantiated at
:161-165), whose gate isnn.Linear(hidden_size, n_routed_experts, bias=False, dtype=torch.float32)(
glm4_moe.py:141-146), fedhidden_states.to(dtype=torch.float32)(:218),declared
router_logits_dtype=torch.float32(:205). No config keyparticipates.
DeepseekV2MoEresolves the same dtype from the config instead —GateLinear(..., out_dtype=_get_moe_router_dtype(config))(
deepseek_v2.py:308-314,:123-133) — which returns fp32 only formodel_type == "glm_moe_dsa"or an explicitmoe_router_dtype: "float32".This port composes the DeepSeek-V2 forward and loader for GLM, so GLM's dtype
came from
deepseek_v2_weights.cpp:332anddeepseek_v2.cpp:363sized the logitbuffer from it. The published
zai-org/GLM-4.7-Flashconfig.jsondeclares nomoe_router_dtypeand itsmodel_typeisglm4_moe_lite, so the flag resolvedfalse.
The change
ParseGlm4MoeLiteParamsin the GLM registry TU composesParseDeepseekV2Params(config, allow_mtp_tail=true)and then setsrouter_dtype_is_f32 = true, which is whatGlm4MoeLitebeingGlm4MoEmeansin a port that composes them. All three GLM registry hooks —
parse_config,load_weights,make_kv_cache— resolve through it, so theparams the forward reads cannot drift from the params the config hook validated.
ParseDeepseekV2Paramsis untouched and still does not readmodel_type, whichdeepseek_v2_weights.cpp:321-330argues for and is right about: that parserserves
DeepseekV2ForCausalLM. GLM-5.3 already carries its own answer the sameway (
glm_moe_dsa.cpp:353).Measured, not argued
deepseek_v2.h:164-168says "a token gate cannot see this either way". Thatholds for a store that is merely too WIDE. It does not hold for one that is too
NARROW in front of a top-k, and GLM routes top-4 of 64 with
noaux_tcsigmoid scoring plus an
e_score_correction_bias,norm_topk_prob: trueandrouted_scaling_factor: 1.8. New case T3 drivesvt::MoeRouterTopKat exactlythat configuration:
A changed SET is a different expert's weights running. It is bimodal, not a
tolerance. Two more measurements from the same file:
RED and GREEN, both counts
Built
-O0, CPU only (this host has no CUDA toolkit), atc796fea41.Before the one-line repair, whole file in place:
After:
Mutations
Every one rebuilt (
BUILD_RC=0each time, so nothing here was caught by thecompiler instead of by a test) and restored, each restore verified by
sha256sum -cagainst a baseline taken before the first mutation.p.router_dtype_is_f32 = true;5 | 2 passed | 3 failed,assertions: 23 | 20 passed | 3 failedweights.params = ParseGlm4MoeLiteParams(config);5 | 4 passed | 1 failed, 2 assertions — only T4, reporting the registry's output as the bf16 arm0/40960/16seeds differ,worst 0M2 is the reachability result. T4 goes in through
ModelRegistry::Loadover asynthetic GLM-4.7-Flash checkpoint written to disk and
ModelRegistry::Forwardon its default configuration (
gather_logitstrue, so the device-resident arm),and compares the registry's own logits to the two arms built from the same bytes
by the same loader. Without the loader call site it reds while T1, T2 and T3 stay
green, which is exactly what that call site owes.
Regression
test_glm4_moe_lite_router_dtypetest_glm4_moe_lite_loadtest_deepseek_v2_forwardtest_deepseek_v2_loadtest_ops_moe_router_groupedDeepseekV2ForCausalLMis byte-identical:ParseDeepseekV2Paramsis unchanged.What this does NOT claim
It does not claim the SACRED gate is fixed.
Glm4MoeLiteForCausalLMmatchesthe pinned oracle at 69 of 128 positions (#2839, PR #2906), and no assertion in
test_glm4_moe_lite_paged_engine.cppis touched here. That gate compares afrozen
our_ids.npy, so no forward change can move it from this host at all;only a re-capture on a GPU with the checkpoint can. No
zai-org/GLM-4.7-Flashsnapshot exists on this box or on the NAS.
T3's 0.83% is per MoE layer per token on
N(0,1)synthetic logits. Taken at facevalue across GLM-4.7-Flash's 46 MoE layers it would put a changed selection on
roughly a third of token positions, which is the order of the observed 59
divergent positions — but the real logit distribution is not that one, and
nothing here measures the checkpoint. The spec records it under
## OwedO1.Upstream anchors were read at
5559679229, the goldens' own pin and the PRIORparity pin, not at the active
e126687a9a. One token gate has now run at theactive pin and passed (
b55f63ed6, merged in here), but it is a differentmodel's battery and no
Glm4MoeLiteForCausalLMgolden has been re-capturedthere. Owed as O5.
Preflight, and the two gates that are red for reasons this change did not cause
scripts/agent-preflight.sh --stagedexits 1. Neither failure is caused by thischange, and the exit code is not being read as the verdict either way:
test_cpu_x86_llamacpp_floor—test_a_contended_leg_is_discarded_and_never_summarisedexpected rc 2 and got 4 while the box was at
load=75.28 76.77 62.18fromother sessions' builds; the harness discarded the contended leg and ran out of
retries. Re-run standalone on this same tree:
Ran 10 tests ... OK.tools suites—test_oracle_pin.test_metadata_and_runtime_strings_differ_on_the_pinasserts
VLLM_DISTRIBUTION_VERSION != VLLM_ORACLE_VERSION, and both now read0.28.1rc1.dev132+ge126687a9. Those constants come from.agents/upstream-sync.mdthrough
tools/bench/serve_low_common.py:107-108, neither of which this changetouches, so it is a property of
origin/main. Already open and owned as#2931.
Five gates SKIP for want of a cross-compiler, a CUDA toolkit or a CI-only input.
check-pr-size.pyis one of them and was run by hand:OK: every explicit path class is within its review budget.origin/mainwas merged in at 15 commits (e23eeaa94) and the row's gate re-runon the merged tree: identical counts, identical numbers. That merge falsified one
sentence of this pull request's own prose — the active-pin claim above — which is
repaired in
a5b4830earather than left standing.Filed on the way, owned rather than deferred
neartie_gap_mnats.npyis identically zero in 12 of 18 golden directories while
our_idsdivergesfrom
greedy_idsat 13 to 83 positions in each. At a first divergence ourprefix equals the oracle's, so the oracle's teacher-forced argmax there IS the
golden token and the gap must be positive. Established from the committed
artifacts alone; owned under this spec's
## OwedO3.routed_scaling_factoris applied to the routing weights, where upstream applies it to the routed
output. A recorded deviation whose only gate vehicle had a factor of 1.0, which
made it bit-identical there; GLM's 1.8 makes it live.
## OwedO4.Closes #2928.
FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:claude-opus-5 [claude-code]