use serde::Deserialize;
use crate::error::CanddingError;
use crate::error::Result;
#[derive(Clone, Debug, Deserialize)]
pub struct ModernBertConfig {
pub hidden_size: usize,
pub num_attention_heads: usize,
pub num_hidden_layers: usize,
pub intermediate_size: usize,
#[serde(default = "default_hidden_activation")]
pub hidden_activation: String,
#[serde(default = "default_global_attn_every_n_layers")]
pub global_attn_every_n_layers: usize,
#[serde(default = "default_local_attention")]
pub local_attention: usize,
#[serde(default = "default_global_rope_theta")]
pub global_rope_theta: f64,
#[serde(default = "default_local_rope_theta")]
pub local_rope_theta: f64,
#[serde(default = "default_attention_bias")]
pub attention_bias: bool,
#[serde(default = "default_mlp_bias")]
pub mlp_bias: bool,
#[serde(default = "default_norm_bias")]
pub norm_bias: bool,
#[serde(default = "default_norm_eps")]
pub norm_eps: f64,
pub vocab_size: usize,
pub max_position_embeddings: usize,
}
fn default_hidden_activation() -> String {
"gelu".to_string()
}
fn default_global_attn_every_n_layers() -> usize {
3
}
fn default_local_attention() -> usize {
128
}
fn default_global_rope_theta() -> f64 {
160_000.0
}
fn default_local_rope_theta() -> f64 {
10_000.0
}
fn default_attention_bias() -> bool {
false
}
fn default_mlp_bias() -> bool {
false
}
fn default_norm_bias() -> bool {
false
}
fn default_norm_eps() -> f64 {
1e-5
}
impl ModernBertConfig {
pub fn head_dim(&self) -> usize {
self.hidden_size / self.num_attention_heads
}
pub fn is_global(&self, layer: usize) -> bool {
layer.is_multiple_of(self.global_attn_every_n_layers)
}
pub fn attention_window(&self) -> usize {
self.local_attention / 2 + 1
}
pub fn ensure_supported(&self) -> Result<()> {
if self.hidden_activation != "gelu" {
return Err(CanddingError::UnsupportedArchitecture(format!(
"modernbert: hidden_activation {} is not supported; the port applies the erf GELU",
self.hidden_activation
)));
}
if self.attention_bias {
return Err(CanddingError::UnsupportedArchitecture(format!(
"modernbert: attention_bias {} is not supported; no attention bias tensor is loaded",
self.attention_bias
)));
}
if self.mlp_bias {
return Err(CanddingError::UnsupportedArchitecture(format!(
"modernbert: mlp_bias {} is not supported; no MLP bias tensor is loaded",
self.mlp_bias
)));
}
if self.norm_bias {
return Err(CanddingError::UnsupportedArchitecture(format!(
"modernbert: norm_bias {} is not supported; every norm is loaded weight-only",
self.norm_bias
)));
}
if self.global_attn_every_n_layers == 0 {
return Err(CanddingError::UnsupportedArchitecture(format!(
"modernbert: global_attn_every_n_layers {} is not supported; a modulus of zero",
self.global_attn_every_n_layers
)));
}
if !self.local_attention.is_multiple_of(2) {
return Err(CanddingError::UnsupportedArchitecture(format!(
"modernbert: local_attention {} is not supported; the reference halves it with integer division; the \
port would round",
self.local_attention
)));
}
Ok(())
}
}
#[cfg(test)]
mod tests;