use candle_core::Tensor;
use candle_nn::VarBuilder;
use super::attention::Gemma3Attention;
use super::config::Gemma3Config;
use super::config::LayerType;
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 LayerContext<'a> {
pub rope_local: &'a RotaryEmbedding,
pub rope_global: &'a RotaryEmbedding,
pub mask_local: &'a AttentionMask,
pub mask_global: &'a AttentionMask,
}
pub struct Gemma3Layer {
layer_type: LayerType,
input_layernorm: RmsNorm,
self_attn: Gemma3Attention,
post_attention_layernorm: RmsNorm,
pre_feedforward_layernorm: RmsNorm,
mlp: GatedMlp,
post_feedforward_layernorm: RmsNorm,
}
impl Gemma3Layer {
pub fn load(config: &Gemma3Config, layer_type: LayerType, vb: VarBuilder) -> Result<Self> {
let norm = |name: &str| {
RmsNorm::load(
vb.pp(name),
config.hidden_size,
config.rms_norm_eps,
RmsNormStyle::OnePlusScale,
)
};
Ok(Self {
layer_type,
input_layernorm: norm("input_layernorm")?,
self_attn: Gemma3Attention::load(config, vb.pp("self_attn"))?,
post_attention_layernorm: norm("post_attention_layernorm")?,
pre_feedforward_layernorm: norm("pre_feedforward_layernorm")?,
mlp: GatedMlp::load(
vb.pp("mlp"),
config.hidden_size,
config.intermediate_size,
config.hidden_activation,
)?,
post_feedforward_layernorm: norm("post_feedforward_layernorm")?,
})
}
pub fn forward(&self, hidden: &Tensor, ctx: &LayerContext) -> Result<Tensor> {
let (rope, mask) = match self.layer_type {
LayerType::SlidingAttention => (ctx.rope_local, ctx.mask_local),
LayerType::FullAttention => (ctx.rope_global, ctx.mask_global),
};
let attn = self
.self_attn
.forward(&self.input_layernorm.forward(hidden)?, rope, mask)?;
let attended = (hidden + self.post_attention_layernorm.forward(&attn)?)?;
let inner = self
.mlp
.forward(&self.pre_feedforward_layernorm.forward(&attended)?)?;
Ok((attended + self.post_feedforward_layernorm.forward(&inner)?)?)
}
}