Skip to content

Commit 94200f2

Browse files
fix: add @ModuleInfo to all Module-typed properties for weight update support
Module-typed properties declared as plain `let` without @ModuleInfo cannot be updated via Module.update(modules:verify:), which is called during quantization. This causes a fatal crash when loading quantized models: MLXNN.UpdateError.needModuleInfo("Unable to get @ModuleInfo for BertModel.pooler -- must be wrapped to receive updates") The quantize() function replaces Linear modules with QuantizedLinear via update(modules:), which requires @ModuleInfo setters. Without them, the non-throwing update(modules:) wrapper hits try! on the thrown error → SIGABRT. Fixed 36 properties across MLXEmbedders, MLXLLM, and MLXVLM: - MLXEmbedders: BertModel.pooler, NomicBertModel.pooler, Qwen3 TransformerBlock.mlp, Qwen3ModelInner.norm - MLXLLM: norm properties in 29 model classes, plus FalconH1Mixer.conv1d - MLXVLM: Idefics3 TransformerBlock.mlp, Idefics3 LanguageModel.norm, Ministral3ModelInner.norm, LanguageModelInner.norm Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bc3c20e commit 94200f2

36 files changed

+80
-80
lines changed

Libraries/MLXEmbedders/Models/Bert.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ public class BertModel: Module, EmbeddingModel {
259259
@ModuleInfo(key: "embeddings") fileprivate var embedder: BertEmbedding
260260

261261
/// A linear layer used to "pool" the [CLS] token into a single sentence vector.
262-
let pooler: Linear?
262+
@ModuleInfo var pooler: Linear?
263263

264264
/// The stack of Transformer layers.
265265
fileprivate let encoder: Encoder
@@ -280,10 +280,10 @@ public class BertModel: Module, EmbeddingModel {
280280

281281
if lmHead {
282282
_lmHead.wrappedValue = LMHead(config)
283-
self.pooler = nil
283+
_pooler.wrappedValue = nil
284284
} else {
285285
// Pooler projects the [CLS] token to a hidden state of the same size
286-
pooler = Linear(config.embedDim, config.embedDim)
286+
_pooler.wrappedValue = Linear(config.embedDim, config.embedDim)
287287
_lmHead.wrappedValue = nil
288288
}
289289
}

Libraries/MLXEmbedders/Models/NomicBert.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ public class NomicBertModel: Module, EmbeddingModel {
671671

672672
/// The optional pooler layer.
673673
/// Used to extract a single vector representation for the whole sequence (usually from the [CLS] token).
674-
let pooler: Linear?
674+
@ModuleInfo var pooler: Linear?
675675

676676
/// The stack of Transformer blocks.
677677
fileprivate let encoder: Encoder
@@ -696,9 +696,9 @@ public class NomicBertModel: Module, EmbeddingModel {
696696

697697
// Initialize Pooler (for sentence embeddings)
698698
if pooler {
699-
self.pooler = Linear(config.embedDim, config.embedDim, bias: false)
699+
_pooler.wrappedValue = Linear(config.embedDim, config.embedDim, bias: false)
700700
} else {
701-
self.pooler = nil
701+
_pooler.wrappedValue = nil
702702
}
703703

704704
// Initialize LM Head (for training/masked prediction)

Libraries/MLXEmbedders/Models/Qwen3.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ private class TransformerBlock: Module {
169169
@ModuleInfo(key: "self_attn") var attention: Attention
170170

171171
/// The feed-forward network (SwiGLU).
172-
let mlp: MLP
172+
@ModuleInfo var mlp: MLP
173173

174174
/// Normalization applied before the attention layer.
175175
@ModuleInfo(key: "input_layernorm") var inputLayerNorm: RMSNorm
@@ -182,7 +182,7 @@ private class TransformerBlock: Module {
182182
public init(_ args: Qwen3Configuration) {
183183
// Initialize the two main processing sub-layers
184184
_attention.wrappedValue = Attention(args)
185-
self.mlp = MLP(dimensions: args.hiddenSize, hiddenDimensions: args.intermediateSize)
185+
_mlp.wrappedValue = MLP(dimensions: args.hiddenSize, hiddenDimensions: args.intermediateSize)
186186

187187
// Initialize RMSNorm layers with the specified epsilon for numerical stability
188188
_inputLayerNorm.wrappedValue = RMSNorm(
@@ -236,7 +236,7 @@ private class Qwen3ModelInner: Module {
236236
fileprivate let layers: [TransformerBlock]
237237

238238
/// The final normalization layer applied after all transformer blocks.
239-
let norm: RMSNorm
239+
@ModuleInfo var norm: RMSNorm
240240

241241
/// Initializes the model backbone.
242242
/// - Parameter args: Configuration containing `vocabularySize`, `hiddenLayers`, and `hiddenSize`.
@@ -255,7 +255,7 @@ private class Qwen3ModelInner: Module {
255255
}
256256

257257
// 3. Initialize final RMSNorm
258-
self.norm = RMSNorm(dimensions: args.hiddenSize, eps: args.rmsNormEps)
258+
_norm.wrappedValue = RMSNorm(dimensions: args.hiddenSize, eps: args.rmsNormEps)
259259
}
260260

261261
/// Forward pass through the model backbone.

Libraries/MLXLLM/Models/Apertus.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ private class ApertusModelInner: Module {
303303
@ModuleInfo(key: "embed_tokens") var embedTokens: Embedding
304304

305305
let layers: [ApertusBlock]
306-
let norm: RMSNorm
306+
@ModuleInfo var norm: RMSNorm
307307

308308
public init(_ args: ApertusConfiguration) {
309309
precondition(args.vocabSize > 0)
@@ -313,7 +313,7 @@ private class ApertusModelInner: Module {
313313
dimensions: args.hiddenSize
314314
)
315315
self.layers = (0 ..< args.numHiddenLayers).map { _ in ApertusBlock(args) }
316-
self.norm = RMSNorm(dimensions: args.hiddenSize, eps: args.rmsNormEps)
316+
_norm.wrappedValue = RMSNorm(dimensions: args.hiddenSize, eps: args.rmsNormEps)
317317
}
318318

319319
public func callAsFunction(

Libraries/MLXLLM/Models/BaichuanM1.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ public class BaichuanM1ModelInner: Module {
203203
@ModuleInfo(key: "embed_tokens") var embedTokens: Embedding
204204

205205
fileprivate let layers: [BaichuanM1DecoderLayer]
206-
let norm: RMSNorm
206+
@ModuleInfo var norm: RMSNorm
207207

208208
init(_ config: BaichuanM1Configuration) {
209209
self.args = config
@@ -212,7 +212,7 @@ public class BaichuanM1ModelInner: Module {
212212
self.layers = (0 ..< config.hiddenLayers).map {
213213
BaichuanM1DecoderLayer(config, layerIdx: $0)
214214
}
215-
norm = RMSNorm(dimensions: config.hiddenSize, eps: config.rmsNormEps)
215+
_norm.wrappedValue = RMSNorm(dimensions: config.hiddenSize, eps: config.rmsNormEps)
216216
}
217217

218218
func callAsFunction(

Libraries/MLXLLM/Models/BailingMoe.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ class BailingMoeTransformerBlock: Module {
313313
public class BailingMoeModelInner: Module {
314314
@ModuleInfo(key: "word_embeddings") var embedTokens: Embedding
315315
let layers: [BailingMoeTransformerBlock]
316-
let norm: RMSNorm
316+
@ModuleInfo var norm: RMSNorm
317317

318318
init(_ args: BailingMoeConfiguration) {
319319
precondition(args.vocabularySize > 0)
@@ -322,7 +322,7 @@ public class BailingMoeModelInner: Module {
322322
self.layers = (0 ..< args.hiddenLayers).map {
323323
BailingMoeTransformerBlock(args, layerIdx: $0)
324324
}
325-
self.norm = RMSNorm(dimensions: args.hiddenSize, eps: args.rmsNormEps)
325+
_norm.wrappedValue = RMSNorm(dimensions: args.hiddenSize, eps: args.rmsNormEps)
326326
}
327327

328328
func callAsFunction(_ inputs: MLXArray, cache: [KVCache]? = nil) -> MLXArray {

Libraries/MLXLLM/Models/Bitnet.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -405,7 +405,7 @@ public class BitnetModelInner: Module {
405405
@ModuleInfo(key: "embed_tokens") var embedTokens: Embedding
406406

407407
fileprivate let layers: [BitnetTransformerBlock]
408-
var norm: RMSNorm
408+
@ModuleInfo var norm: RMSNorm
409409

410410
init(_ args: BitnetConfiguration) {
411411
precondition(args.vocabularySize > 0)
@@ -417,7 +417,7 @@ public class BitnetModelInner: Module {
417417
layers = (0 ..< args.hiddenLayers).map { _ in
418418
BitnetTransformerBlock(args)
419419
}
420-
norm = RMSNorm(dimensions: args.hiddenSize, eps: args.rmsNormEps)
420+
_norm.wrappedValue = RMSNorm(dimensions: args.hiddenSize, eps: args.rmsNormEps)
421421
}
422422

423423
func callAsFunction(_ inputs: MLXArray, cache: [KVCache]? = nil) -> MLXArray {

Libraries/MLXLLM/Models/Cohere.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ public class CohereModelInner: Module {
121121
@ModuleInfo(key: "embed_tokens") var embedTokens: Embedding
122122

123123
fileprivate let layers: [CohereTransformerBlock]
124-
let norm: LayerNorm
124+
@ModuleInfo var norm: LayerNorm
125125

126126
public init(_ args: CohereConfiguration) {
127127
precondition(args.vocabularySize > 0)
@@ -133,7 +133,7 @@ public class CohereModelInner: Module {
133133
.map { _ in
134134
CohereTransformerBlock(args)
135135
}
136-
self.norm = LayerNorm(dimensions: args.hiddenSize, eps: args.layerNormEps)
136+
_norm.wrappedValue = LayerNorm(dimensions: args.hiddenSize, eps: args.layerNormEps)
137137
}
138138

139139
public func callAsFunction(_ inputs: MLXArray, cache: [KVCache]? = nil) -> MLXArray {

Libraries/MLXLLM/Models/Ernie4_5.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ class Ernie45DecoderLayer: Module {
173173
public class Ernie45ModelInner: Module {
174174
@ModuleInfo(key: "embed_tokens") var embedTokens: Embedding
175175
let layers: [Ernie45DecoderLayer]
176-
let norm: RMSNorm
176+
@ModuleInfo var norm: RMSNorm
177177

178178
public init(_ args: Ernie45Configuration) {
179179
self._embedTokens.wrappedValue = Embedding(
@@ -182,7 +182,7 @@ public class Ernie45ModelInner: Module {
182182
self.layers = (0 ..< args.numHiddenLayers).map { _ in
183183
Ernie45DecoderLayer(args)
184184
}
185-
self.norm = RMSNorm(dimensions: args.hiddenSize, eps: args.rmsNormEps)
185+
_norm.wrappedValue = RMSNorm(dimensions: args.hiddenSize, eps: args.rmsNormEps)
186186
}
187187

188188
public func callAsFunction(_ inputs: MLXArray, cache: [KVCache]? = nil) -> MLXArray {

Libraries/MLXLLM/Models/Exaone4.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ public class Exaone4ModelInner: Module {
141141
@ModuleInfo(key: "embed_tokens") var embedTokens: Embedding
142142

143143
fileprivate let layers: [Exaone4TransformerBlock]
144-
let norm: RMSNorm
144+
@ModuleInfo var norm: RMSNorm
145145

146146
public init(_ args: Exaone4Configuration) {
147147
precondition(args.vocabularySize > 0)
@@ -162,7 +162,7 @@ public class Exaone4ModelInner: Module {
162162
}
163163
return Exaone4TransformerBlock(args, isLocal: isLocal)
164164
}
165-
self.norm = RMSNorm(dimensions: args.hiddenSize, eps: args.rmsNormEps)
165+
_norm.wrappedValue = RMSNorm(dimensions: args.hiddenSize, eps: args.rmsNormEps)
166166
}
167167

168168
public func callAsFunction(_ inputs: MLXArray, cache: [KVCache]? = nil) -> MLXArray {

0 commit comments

Comments
 (0)