candding 0.1.0

Pure candle embeddings for Rust with no ONNX: dense, sparse, multi-vector and rerank models today, image models on the roadmap
Documentation
use candle_core::Tensor;
use candle_nn::VarBuilder;

use super::attention::Qwen3Attention;
use super::config::Qwen3Config;
use crate::error::Result;
use crate::layers::mask::AttentionMask;
use crate::layers::mlp::GatedMlp;
use crate::layers::rms_norm::RmsNorm;
use crate::layers::rms_norm::RmsNormStyle;
use crate::layers::rope::RotaryEmbedding;

pub struct Qwen3Layer {
  input_layernorm: RmsNorm,
  self_attn: Qwen3Attention,
  post_attention_layernorm: RmsNorm,
  mlp: GatedMlp,
}

impl Qwen3Layer {
  pub fn load(config: &Qwen3Config, vb: VarBuilder) -> Result<Self> {
    let eps = config.rms_norm_eps;
    Ok(Self {
      input_layernorm: RmsNorm::load(
        vb.pp("input_layernorm"),
        config.hidden_size,
        eps,
        RmsNormStyle::Scale,
      )?,
      self_attn: Qwen3Attention::load(config, vb.pp("self_attn"))?,
      post_attention_layernorm: RmsNorm::load(
        vb.pp("post_attention_layernorm"),
        config.hidden_size,
        eps,
        RmsNormStyle::Scale,
      )?,
      mlp: GatedMlp::load(
        vb.pp("mlp"),
        config.hidden_size,
        config.intermediate_size,
        config.hidden_act,
      )?,
    })
  }

  /// Pre-norm residual block: `h += attn(norm(h)); h += mlp(norm(h))`.
  pub fn forward(
    &self,
    hidden: &Tensor,
    rope: &RotaryEmbedding,
    mask: &AttentionMask,
  ) -> Result<Tensor> {
    let attended = (hidden
      + self
        .self_attn
        .forward(&self.input_layernorm.forward(hidden)?, rope, mask)?)?;
    let inner = self
      .mlp
      .forward(&self.post_attention_layernorm.forward(&attended)?)?;
    Ok((attended + inner)?)
  }
}