use candle_core::Tensor;
use candle_nn::VarBuilder;
use super::attention::ModernBertAttention;
use super::config::ModernBertConfig;
use super::mlp::ModernBertMlp;
use crate::error::Result;
use crate::layers::layer_norm::LayerNorm;
use crate::layers::mask::AttentionMask;
use crate::layers::rope::RotaryEmbedding;
pub struct ModernBertLayer {
attn_norm: Option<LayerNorm>,
attention: ModernBertAttention,
mlp_norm: LayerNorm,
mlp: ModernBertMlp,
}
impl ModernBertLayer {
pub fn load(config: &ModernBertConfig, vb: VarBuilder, layer: usize) -> Result<Self> {
let hidden = config.hidden_size;
let attn_norm = if layer == 0 {
None
} else {
Some(LayerNorm::load_without_bias(
vb.pp("attn_norm"),
hidden,
config.norm_eps,
)?)
};
Ok(Self {
attn_norm,
attention: ModernBertAttention::load(config, vb.clone())?,
mlp_norm: LayerNorm::load_without_bias(vb.pp("mlp_norm"), hidden, config.norm_eps)?,
mlp: ModernBertMlp::load(config, vb)?,
})
}
pub fn forward(
&self,
hidden: &Tensor,
mask: &AttentionMask,
rope: &RotaryEmbedding,
) -> Result<Tensor> {
let normed = match &self.attn_norm {
Some(norm) => norm.forward(hidden)?,
None => hidden.clone(),
};
let hidden = (hidden + self.attention.forward(&normed, mask, rope)?)?;
let projected = self.mlp.forward(&self.mlp_norm.forward(&hidden)?)?;
Ok((hidden + projected)?)
}
}