Repository navigation
feat(models): add Phixtral (phixtral) text model support #844
Description
Corrected against
references/mlx-lm(commit15b522f, 2026-07-11) on 2026-08-05.
The detection arm in the original body would never fire
The original acceptance criteria ask for "phixtral" => ... in src/models/detection.rs. No phixtral checkpoint declares that string. mlabonne/phixtral-4x2_8 declares "model_type": "phi-msft", and upstream reaches the implementation through a rename table, not the config value:
# mlx_lm/utils.py
MODEL_REMAPPING = { ..., "phi-msft": "phixtral", ... }mlxcel already has an arm for that string, and it points somewhere else:
// src/models/detection.rs:154
"phi" | "phi-msft" => Ok(ModelType::Phi),So a phixtral checkpoint currently routes to the dense Phi decoder. The port therefore has to discriminate an existing arm rather than add a new one. The reliable discriminator is num_local_experts in the config (4 on phixtral-4x2_8, absent on dense Phi-2), the same signal upstream's config carries.
Validated checkpoint
mlabonne/phixtral-4x2_8 ships fp16 only, so a local conversion is needed. Done and verified:
mlx_lm convert --hf-path mlabonne/phixtral-4x2_8 -q --q-bits 4 --q-group-size 64
produces a 4.1 GB 4-bit checkpoint (models/phixtral-4x2_8-4bit, 4.503 bits/weight) that keeps model_type: phi-msft, n_layer: 32, n_embd: 2560, rotary_dim: 32, num_local_experts: 4, num_experts_per_tok: 2.
Architecture, as upstream implements it
Confirmed accurate in the original body, with two additions worth naming:
- Routing is
mx.softmax(scores, axis=-1, precise=True)over the top-k gate logits, and the gate is a plainnn.Linear. - Upstream has a real
sanitizethat rewrites per-expert keys; theModel.sanitizeloop iteratesrange(self.args.num_local_experts), so the checkpoint's expert layout has to be mapped rather than consumed as-is. Check the converted checkpoint's actual key layout before assuming the pre-stackedSwitchGLUpath applies.
nn.LayerNorm (with bias) rather than RMSNorm, partial rotary over rotary_dim only, and the Phi-2 single-LayerNorm parallel-residual block are all as described.
Original issue body (retained for history)
Summary
Add native mlxcel support for Phixtral (model_type: phixtral), a Mixtral-style sparse MoE built on the Phi-2 backbone. Each block keeps the Phi-2 single-LayerNorm parallel-residual layout (attention and MLP share one input LayerNorm and run in parallel) and uses partial-rotary RoPEAttention, but replaces the dense MLP with top-k routing over num_local_experts experts. Porting brings the family to the same load-and-generate path as the other text models.
Upstream reference
Public checkpoint
mlabonne/phixtral-4x2_8(4 experts, top-2). Confirm an existing MLX SafeTensors conversion or produce one before validation. Real-checkpoint validation is a hard gate.
Architecture notes
- Mixtral-style sparse MoE on a Phi-2 backbone.
RoPEAttentionwith partial rotary (rotary_dim): only the firstrotary_dimslots are rotated.nn.LayerNorm(with bias), not RMSNorm.- Phi-2 block shape: a single input LayerNorm per block feeding attention and MLP in PARALLEL (parallel residual), summed into the residual.
- Routing: an
nn.Lineargate selects top-num_experts_per_tok(default 2) ofnum_local_expertsexperts per token.
Implementation plan
Cite and reuse existing mlxcel modules:
- Phi attention + LayerNorm + parallel-residual block: reuse
src/models/phi3.rs/src/models/phi3small.rsfor the Phi attention and LayerNorm handling. Notesrc/models/phimoe.rsalready combines a Phi-family backbone (LayerNorm with bias, attention proj bias) with MoE routing and is the closest existing reference; start from it. - Mixtral-style top-k MoE: reuse the routing pattern in
src/models/mixtral.rsand the expert compute insrc/models/switch_layers.rs. - The distinctive work is combining Phi-2 partial rotary (
rotary_dim) + the single-LayerNorm parallel-residual block with switch-based top-k MoE (gate asnn.Linear, softmax over top-k logits).
Touchpoints & acceptance criteria
Follow docs/adding-models.md. Done only when it loads and generates from a real checkpoint.
- Config struct + serde parse for
phixtral(rotary_dim,num_local_experts,num_experts_per_tok, LayerNorm eps, Phi-2 dims), with sensible#[serde(default)]s. -
from_weightsconstructs the model from a realWeightMap, including gate + per-expert weights. -
sanitizehandles the checkpoint's expert key layout / stacking. - Detection arm added in
src/models/detection.rs("phixtral" => ...). - Registration added via
for_each_model_registration!insrc/model_metadata.rs. - TP / distributed arch-string wired if the family is served in that path.
- Unit tests in a
_tests.rsfile beside the implementation (config parse, partial-rotary dims, parallel-residual block, top-k gate selection). -
docs/supported-models.mdupdated with the new family. - Real-checkpoint validation:
mlxcel listshows the arch and./target/release/mlxcel generate -m <path> -p "..." -n <N>produces coherent output.
Effort
MEDIUM. Phi attention/LayerNorm and Mixtral top-k routing both exist (phimoe.rs already fuses a Phi backbone with MoE); the new logic is Phi-2 partial rotary + parallel residual combined with the switch MoE gate.
Activity
docs(models): document the modelsize / modeltype / arch label taxonomy (
feat(models): add Phixtral (phi-msft sparse MoE) text model support (l…
docs(models): document the modelsize / modeltype / arch label taxonomy (
feat(models): add Phixtral (phi-msft sparse MoE) text model support (l…