fix(mlx): let sharded models re-derive layer indices after pipeline split - #2287
Draft
zhast wants to merge 1 commit into
Draft
fix(mlx): let sharded models re-derive layer indices after pipeline split#2287zhast wants to merge 1 commit into
zhast wants to merge 1 commit into
Conversation
…plit pipeline_auto_parallel() replaces the model's layer stack with a slice after the model has already been constructed. Any index a model derived from the full stack in __init__ -- which cache entry holds the first full-attention layer, which holds the first recurrent layer -- then addresses the wrong entry on every shard but the first, and the model builds its attention and SSM masks from the wrong cache. Nothing raises. Generation runs at full speed and returns fluent-looking tokens with no semantics, which makes this expensive to track down. auto_parallel already repairs this by name for GptOssMoeModel, Step35InnerModel, Qwen3_5/Qwen3Next and NemotronH. That list has grown once per hybrid model added, and a stack that is not on it fails silently rather than loudly. Add a general opt-in hook so a model can re-derive its own state from the sharded layer list; models that do not define it are completely unaffected. Found while running GLM-5.3-Flash (glm5_next: 34 gated-delta linear-attention layers interleaved with 11 sparse-attention layers) across a 4-node pipeline. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
The bug
pipeline_auto_parallel()replaces the model's layer stack with a slice after the model has been constructed:Any index a model derived from the full stack in
__init__— which cache entry holds the first full-attention layer, which holds the first recurrent layer — now addresses the wrong entry on every shard but the first. The model then builds its attention and SSM masks from the wrong cache.Nothing raises. Generation runs at full speed and returns fluent-looking tokens with no semantics. That failure mode is expensive to track down: activations stay finite and well-scaled all the way through, so every obvious diagnostic looks healthy.
Why a general hook
auto_parallelalready repairs exactly this, by name, for four model types:That list has grown once per hybrid model added. The problem is not that the list is incomplete — it is that a stack which is not on it fails silently instead of loudly, including any model loaded from outside the tree.
This adds a small opt-in hook after
_set_layers()so a model can re-derive its own state from the sharded layer list. Models that do not defineresync_sharded_layersare completely unaffected — no behaviour change for anything in-tree today.How it was found
Running GLM-5.3-Flash (
glm5_next: 34 gated-delta linear-attention layers interleaved with 11 DeepSeek-sparse-attention layers, hyper-connections, 288-expert MoE) across a 4-node Mac Studio pipeline over Thunderbolt/RDMA.The inner model caches:
On the full 45-layer stack that gives
ssm_idx=0, fa_idx=3. The shard holding global layers 27–44 needsfa_idx=0, ssm_idx=1. With the stale values,create_ssm_mask()receives a full-attentionCacheListand the attention path receives the recurrent cache.Note the
, 0)default is a second trap: a shard containing no layer of one kind silently gets index0— a valid index into the wrong entry — rather thanNone.Testing
src/exo/worker/engines/mlx/tests/test_pipeline_layer_resync.pycovers both that the hook fires and corrects indices on a non-first shard, and that a model without the hook shards exactly as before.Draft: happy to fold the four existing
isinstancespecial-cases onto this hook if you'd prefer that shape, but left them untouched here to keep the change additive.🤖 Generated with Claude Code