1use super::with_tracing::QMatMul;
10use crate::{quantized_nn::RmsNorm, utils::repeat_kv};
11use candle::quantized::{gguf_file, QTensor};
12use candle::{DType, Device, IndexOp, Result, Tensor, D};
13use candle_nn::{kv_cache::KvCache, Activation, Embedding, Module};
14use std::io::{Read, Seek};
15use std::sync::Arc;
16
17struct Gguf<R: Read + Seek> {
18 ct: gguf_file::Content,
19 reader: R,
20 device: Device,
21}
22
23impl<R: Read + Seek> Gguf<R> {
24 fn new(ct: gguf_file::Content, reader: R, device: Device) -> Self {
25 Self { ct, reader, device }
26 }
27
28 fn qmatmul(&mut self, name: &str) -> Result<QMatMul> {
29 let ws = self.ct.tensor(&mut self.reader, name, &self.device)?;
30 QMatMul::from_weights(ws.into())
31 }
32
33 fn rms_norm(&mut self, name: &str, eps: f64) -> Result<RmsNorm> {
34 let ws = self.ct.tensor(&mut self.reader, name, &self.device)?;
35 RmsNorm::from_qtensor(ws, eps)
36 }
37
38 fn metadata(&self) -> &std::collections::HashMap<String, gguf_file::Value> {
39 &self.ct.metadata
40 }
41
42 fn tensor(&mut self, name: &str) -> Result<QTensor> {
43 self.ct.tensor(&mut self.reader, name, &self.device)
44 }
45
46 fn unquantized_tensor(&mut self, name: &str, dtype: DType) -> Option<Tensor> {
47 let t = self.ct.tensor(&mut self.reader, name, &self.device);
48 if let Ok(t) = &t {
49 t.dequantize(&self.device).unwrap().to_dtype(dtype).ok()
50 } else {
51 None
52 }
53 }
54}
55
56#[derive(Debug, Clone)]
57struct Mlp {
58 gate_up_proj: QMatMul,
59 down_proj: QMatMul,
60 act_fn: Activation,
61}
62
63impl Mlp {
64 fn new<R: Read + Seek>(gg: &mut Gguf<R>, prefix: &str) -> Result<Self> {
65 let gate_up_proj = gg.qmatmul(&format!("{prefix}.ffn_up.weight"))?;
67 let down_proj = gg.qmatmul(&format!("{prefix}.ffn_down.weight"))?;
68 let act_fn = Activation::Silu;
69 Ok(Self {
70 gate_up_proj,
71 down_proj,
72 act_fn,
73 })
74 }
75}
76
77impl Module for Mlp {
78 fn forward(&self, xs: &Tensor) -> Result<Tensor> {
79 let w = self.gate_up_proj.forward(xs)?;
80 let dim = w.dims().len() - 1;
81 let gate = w
82 .narrow(dim, 0, w.dim(dim)? / 2)?
83 .contiguous()?
84 .apply(&self.act_fn)?;
85 let up_states = w
86 .narrow(dim, w.dim(dim)? / 2, w.dim(dim)? / 2)?
87 .contiguous()?;
88 self.down_proj.forward(&(gate * up_states)?)
89 }
90}
91
92#[derive(Debug, Clone)]
93pub(crate) struct RotaryEmbedding {
94 sin: Tensor,
95 cos: Tensor,
96 rotary_dim: usize,
97}
98
99impl RotaryEmbedding {
100 pub(crate) fn new(
101 dtype: DType,
102 head_dim: usize,
103 max_position_embeddings: usize,
104 rope_theta: f64,
105 partial_rotary_factor: Option<f32>,
106 dev: &Device,
107 ) -> Result<Self> {
108 let rotary_dim = if let Some(factor) = partial_rotary_factor {
109 (factor * head_dim as f32) as usize
110 } else {
111 head_dim
112 };
113 let max_seq_len = max_position_embeddings;
114 let inv_freq: Vec<_> = (0..rotary_dim)
115 .step_by(2)
116 .map(|i| 1f32 / rope_theta.powf(i as f64 / rotary_dim as f64) as f32)
117 .collect();
118 let inv_freq_len = inv_freq.len();
119 let inv_freq = Tensor::from_vec(inv_freq, (1, inv_freq_len), dev)?.to_dtype(dtype)?;
120 let t = Tensor::arange(0u32, max_seq_len as u32, dev)?
121 .to_dtype(dtype)?
122 .reshape((max_seq_len, 1))?;
123 let freqs = t.matmul(&inv_freq)?;
124 Ok(Self {
125 sin: freqs.sin()?,
126 cos: freqs.cos()?,
127 rotary_dim,
128 })
129 }
130
131 pub(crate) fn apply(&self, xs: &Tensor, offset: usize) -> Result<Tensor> {
132 let (_, _, seq_len, _) = xs.dims4()?;
133 let (s, e) = (offset, offset + seq_len);
134 let cos = self.cos.i((s..e, ..))?.contiguous()?;
135 let sin = self.sin.i((s..e, ..))?.contiguous()?;
136 let xs_rot = xs
137 .i((0, .., .., ..self.rotary_dim))?
138 .unsqueeze(0)?
139 .contiguous()?;
140 let xs_pass = xs.i((0, .., .., self.rotary_dim..))?.unsqueeze(0)?;
141 let xs_rot = candle_nn::rotary_emb::rope_i(&xs_rot, &cos, &sin).unwrap();
142 Tensor::cat(&[&xs_rot, &xs_pass], D::Minus1)?.contiguous()
143 }
144}
145
146#[derive(Debug, Clone)]
147struct AttentionWeights {
148 q_proj: QMatMul,
149 k_proj: QMatMul,
150 v_proj: QMatMul,
151 o_proj: QMatMul,
152 attention_bq: Option<Tensor>,
153 attention_bk: Option<Tensor>,
154 attention_bv: Option<Tensor>,
155 num_heads: usize,
156 num_kv_heads: usize,
157 num_kv_groups: usize,
158 head_dim: usize,
159 rotary_emb: Arc<RotaryEmbedding>,
160 kv_cache: KvCache,
161 dtype: DType,
162 span_attn: tracing::Span,
163}
164
165impl AttentionWeights {
166 fn new<R: Read + Seek>(
167 gg: &mut Gguf<R>,
168 num_heads: usize,
169 num_kv_heads: usize,
170 head_dim: usize,
171 rotary_emb: Arc<RotaryEmbedding>,
172 prefix: &str,
173 dtype: DType,
174 ) -> Result<Self> {
175 let num_kv_groups = num_heads / num_kv_heads;
176
177 let q_proj = gg.qmatmul(&format!("{prefix}.attn_q.weight"))?;
178 let k_proj = gg.qmatmul(&format!("{prefix}.attn_k.weight"))?;
179 let v_proj = gg.qmatmul(&format!("{prefix}.attn_v.weight"))?;
180 let o_proj = gg.qmatmul(&format!("{prefix}.attn_output.weight"))?;
181
182 let attention_bq = gg.unquantized_tensor(&format!("{prefix}.attn_q.bias"), DType::F32);
183 let attention_bk = gg.unquantized_tensor(&format!("{prefix}.attn_k.bias"), DType::F32);
184 let attention_bv = gg.unquantized_tensor(&format!("{prefix}.attn_v.bias"), DType::F32);
185
186 let kv_cache = KvCache::new(2, 512);
189
190 let span_attn = tracing::span!(tracing::Level::TRACE, "attn");
191
192 Ok(Self {
193 q_proj,
194 k_proj,
195 v_proj,
196 o_proj,
197 attention_bq,
198 attention_bk,
199 attention_bv,
200 num_heads,
201 num_kv_heads,
202 num_kv_groups,
203 head_dim,
204 rotary_emb,
205 kv_cache,
206 dtype,
207 span_attn,
208 })
209 }
210
211 fn forward(&mut self, x: &Tensor, attn_mask: Option<&Tensor>, offset: usize) -> Result<Tensor> {
212 let _enter = self.span_attn.enter();
213 let (b, l, _) = x.dims3()?;
214
215 let q = self.q_proj.forward(x)?;
216 let k = self.k_proj.forward(x)?;
217 let v = self.v_proj.forward(x)?;
218 let q = if let Some(bq) = &self.attention_bq {
219 q.broadcast_add(bq)?
220 } else {
221 q
222 };
223
224 let k = if let Some(bk) = &self.attention_bk {
225 k.broadcast_add(bk)?
226 } else {
227 k
228 };
229
230 let v = if let Some(bv) = &self.attention_bv {
231 v.broadcast_add(bv)?
232 } else {
233 v
234 };
235
236 let q = q
237 .reshape((b, l, self.num_heads, self.head_dim))?
238 .transpose(1, 2)?;
239 let k = k
240 .reshape((b, l, self.num_kv_heads, self.head_dim))?
241 .transpose(1, 2)?;
242 let v = v
243 .reshape((b, l, self.num_kv_heads, self.head_dim))?
244 .transpose(1, 2)?;
245
246 let q = self.rotary_emb.apply(&q, offset)?;
247 let k = self.rotary_emb.apply(&k, offset)?;
248
249 let (q, k, v) = (
250 q.to_dtype(self.dtype)?,
251 k.to_dtype(self.dtype)?,
252 v.to_dtype(self.dtype)?,
253 );
254 if offset == 0 {
256 self.kv_cache.reset();
257 }
258
259 let k = k.contiguous()?;
260 let v = v.contiguous()?;
261 let (k, v) = self.kv_cache.append(&k.contiguous()?, &v.contiguous()?)?;
262
263 let k = repeat_kv(k, self.num_kv_groups)?.contiguous()?;
264 let v = repeat_kv(v, self.num_kv_groups)?.contiguous()?;
265
266 let scale = 1.0 / (self.head_dim as f64).sqrt();
267 let mut scores = (q.matmul(&k.transpose(2, 3)?)? * scale)?;
268 if let Some(mask) = attn_mask {
269 scores = scores.broadcast_add(mask)?;
270 }
271 let probs = candle_nn::ops::softmax_last_dim(&scores)?;
272 let ctx = probs.matmul(&v)?; let reshaped_ctx = ctx
274 .transpose(1, 2)?
275 .reshape((b, l, self.num_heads * self.head_dim))?;
276 self.o_proj.forward(&reshaped_ctx.to_dtype(x.dtype())?)
277 }
278}
279
280#[derive(Debug, Clone)]
281struct LayerWeights {
282 self_attn: AttentionWeights,
283 mlp: Mlp,
284 ffn_norm: RmsNorm,
285 attn_norm: RmsNorm,
286 post_ffw_norm: RmsNorm,
287 post_attention_norm: RmsNorm,
288}
289
290impl LayerWeights {
291 #[allow(clippy::too_many_arguments)]
292 fn new<R: Read + Seek>(
293 gg: &mut Gguf<R>,
294 num_attention_heads: usize,
295 num_key_value_heads: usize,
296 head_dim: usize,
297 rms_norm_eps: f64,
298 rotary: Arc<RotaryEmbedding>,
299 layer_idx: usize,
300 dtype: DType,
301 ) -> Result<Self> {
302 let prefix = format!("blk.{layer_idx}");
303
304 let attn_norm = gg.rms_norm(&format!("{prefix}.attn_norm.weight"), rms_norm_eps)?;
305 let ffn_norm = gg.rms_norm(&format!("{prefix}.ffn_norm.weight"), rms_norm_eps)?;
306
307 let post_ffw_norm = gg.rms_norm(&format!("{prefix}.post_ffw_norm.weight"), rms_norm_eps)?;
308 let post_attention_norm = gg.rms_norm(
309 &format!("{prefix}.post_attention_norm.weight"),
310 rms_norm_eps,
311 )?;
312
313 let self_attn = AttentionWeights::new(
314 gg,
315 num_attention_heads,
316 num_key_value_heads,
317 head_dim,
318 rotary,
319 &prefix,
320 dtype,
321 )?;
322 let mlp = Mlp::new(gg, &prefix)?;
323 Ok(Self {
324 self_attn,
325 mlp,
326 attn_norm,
327 ffn_norm,
328 post_ffw_norm,
329 post_attention_norm,
330 })
331 }
332
333 fn forward(&mut self, x: &Tensor, mask: Option<&Tensor>, offset: usize) -> Result<Tensor> {
334 let residual = x;
335 let x = self.attn_norm.forward(x)?;
336 let attn = self.self_attn.forward(&x, mask, offset)?;
337 let attn = self.post_attention_norm.forward(&attn)?;
338 let x = (attn + residual)?;
339
340 let residual = &x;
342 let x = self.ffn_norm.forward(&x)?;
343 let x = self.mlp.forward(&x)?;
344 let x = self.post_ffw_norm.forward(&x)?;
345 x + residual
346 }
347}
348
349#[derive(Debug, Clone)]
350pub struct ModelWeights {
351 embed_tokens: Embedding,
352 layers: Vec<LayerWeights>,
353 norm: RmsNorm,
354 lm_head: QMatMul,
355 device: Device,
356 dtype: DType,
357 span: tracing::Span,
358 span_output: tracing::Span,
359}
360
361impl ModelWeights {
362 pub fn from_gguf<R: Read + Seek>(
363 ct: gguf_file::Content,
364 reader: &mut R,
365 device: &Device,
366 dtype: DType,
367 ) -> Result<Self> {
368 let mut gg = Gguf::new(ct, reader, device.clone());
369 let md_get = |s: &str| match gg.metadata().get(s) {
370 None => candle::bail!("cannot find {s} in metadata"),
371 Some(v) => Ok(v),
372 };
373
374 let num_attention_heads = md_get("glm4.attention.head_count")?.to_u32()? as usize;
375 let num_kv_heads = md_get("glm4.attention.head_count_kv")?.to_u32()? as usize;
376 let head_dim = md_get("glm4.attention.key_length")?.to_u32()? as usize;
377 let num_layers = md_get("glm4.block_count")?.to_u32()? as usize;
378 let hidden_size = md_get("glm4.embedding_length")?.to_u32()? as usize;
379 let max_position_embeddings = md_get("glm4.context_length")?.to_u32()? as usize;
380 let rms_norm_eps = md_get("glm4.attention.layer_norm_rms_epsilon")?.to_f32()? as f64;
381 let rope_freq_base = md_get("glm4.rope.freq_base")?.to_f32()? as f64;
382
383 let embed_tensor = gg.tensor("token_embd.weight")?;
384 let embed_tokens = Embedding::new(embed_tensor.dequantize(device)?, hidden_size);
385
386 let rotary = Arc::new(RotaryEmbedding::new(
387 DType::F32,
388 head_dim,
389 max_position_embeddings,
390 rope_freq_base,
391 Some(0.5), device,
393 )?);
394
395 let mut layers = Vec::with_capacity(num_layers);
396 for i in 0..num_layers {
397 layers.push(LayerWeights::new(
398 &mut gg,
399 num_attention_heads,
400 num_kv_heads,
401 head_dim,
402 rms_norm_eps,
403 rotary.clone(),
404 i,
405 dtype,
406 )?);
407 }
408
409 let norm = gg.rms_norm("output_norm.weight", rms_norm_eps)?;
410 let lm_head_tensor = match gg.tensor("output.weight") {
412 Ok(tensor) => tensor,
413 Err(_) => gg.tensor("token_embd.weight")?,
414 };
415 let lm_head = QMatMul::from_weights(lm_head_tensor.into())?;
416 let span = tracing::span!(tracing::Level::TRACE, "model");
417 let span_output = tracing::span!(tracing::Level::TRACE, "output");
418 Ok(Self {
419 embed_tokens,
420 layers,
421 norm,
422 lm_head,
423 device: device.clone(),
424 dtype,
425 span,
426 span_output,
427 })
428 }
429
430 fn causal_mask(
431 &self,
432 b: usize,
433 tgt: usize,
434 offset: usize,
435 sw: Option<usize>,
436 ) -> Result<Tensor> {
437 let minf = f32::NEG_INFINITY;
438 let mask: Vec<_> = (0..tgt)
439 .flat_map(|i| {
440 (0..(tgt + offset)).map(move |j| {
441 let past_ok = j <= i + offset;
442 let sw_ok = match sw {
443 Some(w) => (i + offset) as i64 - j as i64 <= w as i64,
444 None => true,
445 };
446 if past_ok && sw_ok {
447 0.
448 } else {
449 minf
450 }
451 })
452 })
453 .collect();
454 Tensor::from_slice(&mask, (b, 1, tgt, tgt + offset), &self.device)?.to_dtype(self.dtype)
455 }
456
457 pub fn forward(&mut self, input: &Tensor, offset: usize) -> Result<Tensor> {
458 let _enter = self.span.enter();
459 let (b, l) = input.dims2()?;
460 let mut h = self.embed_tokens.forward(input)?;
461
462 let causal_mask = if l == 1 {
463 None
464 } else {
465 Some(self.causal_mask(b, l, offset, None)?)
466 };
467
468 for layer in &mut self.layers {
469 h = layer.forward(&h, causal_mask.as_ref(), offset)?;
470 }
471
472 let h = self.norm.forward(&h)?;
473 let _enter = self.span_output.enter();
474 let last_hidden = h.narrow(1, l - 1, 1)?;
475 self.lm_head.forward(&last_hidden)?.squeeze(1)
476 }
477}