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
//! One post-norm block: `h = norm1(attn(h) + h)`, then `h = norm2(mlp(h) + h)`; no final norm.

use candle_core::Tensor;
use candle_nn::VarBuilder;

use super::attention::NomicBertAttention;
use super::config::NomicBertConfig;
use super::mlp::NomicBertMlp;
use crate::error::Result;
use crate::layers::layer_norm::LayerNorm;
use crate::layers::mask::AttentionMask;
use crate::layers::rope::RotaryEmbedding;

pub struct NomicBertLayer {
  attention: NomicBertAttention,
  mlp: NomicBertMlp,
  norm1: LayerNorm,
  norm2: LayerNorm,
}

impl NomicBertLayer {
  /// `vb` is `encoder.layers.{i}`.
  pub fn load(config: &NomicBertConfig, vb: VarBuilder) -> Result<Self> {
    Ok(Self {
      attention: NomicBertAttention::load(config, vb.clone())?,
      mlp: NomicBertMlp::load(config, vb.clone())?,
      norm1: LayerNorm::load(vb.pp("norm1"), config.n_embd, config.layer_norm_epsilon)?,
      norm2: LayerNorm::load(vb.pp("norm2"), config.n_embd, config.layer_norm_epsilon)?,
    })
  }

  pub fn forward(
    &self,
    hidden: &Tensor,
    mask: &AttentionMask,
    rope: &RotaryEmbedding,
  ) -> Result<Tensor> {
    let attended = self.attention.forward(hidden, mask, rope)?;
    let hidden = self.norm1.forward(&(attended + hidden)?)?;
    let projected = self.mlp.forward(&hidden)?;
    Ok(self.norm2.forward(&(projected + &hidden)?)?)
  }
}