use crate::backends::gguf::types::MetaValue;
use crate::convert::arch::bake::BakeOp;
use crate::convert::model_card::{
emit_general_postlude, emit_general_prelude, get_model_id_components, ModelCard,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NomicBertCtx {
pub num_experts: Option<usize>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum MappedTensor {
Direct(String),
DirectWithBake { gguf_name: String, bake: BakeOp },
Drop,
}
pub fn map_tensor_name(
hf_name: &str,
hf_shape: &[usize],
ctx: &NomicBertCtx,
) -> Option<MappedTensor> {
let name = hf_name.strip_prefix("bert.").unwrap_or(hf_name);
match name {
"embeddings.word_embeddings.weight" => {
return Some(MappedTensor::Direct("token_embd.weight".to_string()))
}
"embeddings.token_type_embeddings.weight" => {
return Some(MappedTensor::DirectWithBake {
gguf_name: "token_types.weight".to_string(),
bake: BakeOp::Squeeze,
})
}
"embeddings.LayerNorm.weight" => {
return Some(MappedTensor::Direct("token_embd_norm.weight".to_string()))
}
"embeddings.LayerNorm.bias" => {
return Some(MappedTensor::Direct("token_embd_norm.bias".to_string()))
}
"emb_ln.weight" => return Some(MappedTensor::Direct("token_embd_norm.weight".to_string())),
"emb_ln.bias" => return Some(MappedTensor::Direct("token_embd_norm.bias".to_string())),
"pooler.dense.weight" | "pooler.dense.bias" => return Some(MappedTensor::Drop),
"embeddings.position_embeddings.weight" => return Some(MappedTensor::Drop),
_ => {}
}
let stripped = name.strip_prefix("encoder.layers.")?;
let dot = stripped.find('.')?;
let (layer_str, rest_with_dot) = stripped.split_at(dot);
let layer: usize = layer_str.parse().ok()?;
if layer.to_string() != layer_str {
return None;
}
let rest = &rest_with_dot[1..];
if rest == "mlp.experts.bias" {
return Some(MappedTensor::Drop);
}
if rest == "mlp.experts.mlp.w1" || rest == "mlp.experts.mlp.w2" {
let Some(n_experts) = ctx.num_experts else {
return None;
};
if hf_shape.len() != 2 {
return None;
}
let outer = hf_shape[0];
let n_embd = hf_shape[1];
if n_experts == 0 || outer % n_experts != 0 {
return None;
}
let n_inner = outer / n_experts;
return Some(if rest == "mlp.experts.mlp.w1" {
MappedTensor::DirectWithBake {
gguf_name: format!("blk.{layer}.ffn_up_exps.weight"),
bake: BakeOp::MoeExpertReshape {
n_experts,
n_inner,
n_embd,
},
}
} else {
MappedTensor::DirectWithBake {
gguf_name: format!("blk.{layer}.ffn_down_exps.weight"),
bake: BakeOp::MoeExpertTranspose {
n_experts,
n_inner,
n_embd,
},
}
});
}
let suffix = match rest {
"attn.Wqkv.weight" => "attn_qkv.weight",
"attn.Wqkv.bias" => "attn_qkv.bias",
"attn.out_proj.weight" => "attn_output.weight",
"attn.out_proj.bias" => "attn_output.bias",
"norm1.weight" => "attn_output_norm.weight",
"norm1.bias" => "attn_output_norm.bias",
"mlp.fc11.weight" => "ffn_gate.weight",
"mlp.fc12.weight" => "ffn_up.weight",
"mlp.fc1.weight" => "ffn_up.weight",
"mlp.fc1.bias" => "ffn_up.bias",
"mlp.fc2.weight" => "ffn_down.weight",
"mlp.fc2.bias" => "ffn_down.bias",
"mlp.router.layer.weight" => "ffn_gate_inp.weight",
"norm2.weight" => "layer_output_norm.weight",
"norm2.bias" => "layer_output_norm.bias",
_ => return None,
};
Some(MappedTensor::Direct(format!("blk.{layer}.{suffix}")))
}
pub fn build_metadata(
config: &serde_json::Value,
file_type: u32,
model_card: Option<&ModelCard>,
size_label: Option<&str>,
) -> Vec<(String, MetaValue)> {
let raw_name = config
.get("_name_or_path")
.and_then(|v| v.as_str())
.unwrap_or("model");
let id_components = get_model_id_components(raw_name);
let raw_name_string = raw_name.to_string();
let pick_u64 = |k_gpt: &str, k_hf: &str| -> u32 {
let v = config
.get(k_gpt)
.and_then(|v| v.as_u64())
.or_else(|| config.get(k_hf).and_then(|v| v.as_u64()))
.unwrap_or_else(|| panic!("config.json missing required key (`{k_gpt}` or `{k_hf}`)"));
v as u32
};
let hidden_size = pick_u64("n_embd", "hidden_size");
let n_layers = pick_u64("n_layer", "num_hidden_layers");
let n_head = pick_u64("n_head", "num_attention_heads");
let n_head_kv = n_head;
let moe_every_n = config
.get("moe_every_n_layers")
.and_then(|v| v.as_u64())
.filter(|&n| n > 0)
.map(|n| n as u32);
let is_moe = moe_every_n.is_some();
let arch_name = if is_moe {
"nomic-bert-moe"
} else {
"nomic-bert"
};
let head_dim = hidden_size / n_head;
let pad_token_id = config
.get("pad_token_id")
.and_then(|v| v.as_u64())
.map(|n| n as u32);
let n_positions_raw = config
.get("max_position_embeddings")
.or_else(|| config.get("n_positions"))
.and_then(|v| v.as_u64())
.expect("config.json missing required key `max_position_embeddings` (or `n_positions`)")
as u32;
let ctx_len = if is_moe {
let offset = 1 + pad_token_id
.expect("v2-moe config.json: pad_token_id required for context_length offset");
n_positions_raw.saturating_sub(offset)
} else {
n_positions_raw
};
let ffn_len = config
.get("n_inner")
.and_then(|v| v.as_u64())
.map(|x| x as u32)
.unwrap_or(4 * hidden_size);
let ln_eps = if is_moe {
1.0e-12_f32
} else {
config
.get("layer_norm_epsilon")
.and_then(|v| v.as_f64())
.unwrap_or(1.0e-12) as f32
};
let rope_theta = if is_moe {
1000.0_f32
} else {
config
.get("rotary_emb_base")
.and_then(|v| v.as_f64())
.unwrap_or(10000.0) as f32
};
if is_moe {
let n_experts = config
.get("num_experts")
.or_else(|| config.get("num_local_experts"))
.and_then(|v| v.as_u64())
.expect(
"v2-moe config.json: `moe_every_n_layers` present but \
`num_experts` / `num_local_experts` missing",
) as u32;
let moe_top_k = config
.get("moe_top_k")
.or_else(|| config.get("num_experts_per_tok"))
.and_then(|v| v.as_u64())
.expect(
"v2-moe config.json: `moe_every_n_layers` present but \
`moe_top_k` / `num_experts_per_tok` missing",
) as u32;
let n_layers_per_moe = moe_every_n.unwrap();
let _ = n_head_kv; let v2moe_name = id_components
.name
.clone()
.unwrap_or_else(|| raw_name_string.clone());
let mut kv_v2moe = emit_general_prelude(
arch_name,
v2moe_name,
&id_components,
size_label,
model_card,
None, );
kv_v2moe.reserve(20);
kv_v2moe.extend([
(format!("{arch_name}.block_count"), MetaValue::U32(n_layers)),
(
format!("{arch_name}.context_length"),
MetaValue::U32(ctx_len),
),
(
format!("{arch_name}.embedding_length"),
MetaValue::U32(hidden_size),
),
(
format!("{arch_name}.feed_forward_length"),
MetaValue::U32(ffn_len),
),
(
format!("{arch_name}.attention.head_count"),
MetaValue::U32(n_head),
),
(
format!("{arch_name}.rope.freq_base"),
MetaValue::F32(rope_theta),
),
(
format!("{arch_name}.attention.layer_norm_epsilon"),
MetaValue::F32(ln_eps),
),
(
format!("{arch_name}.expert_count"),
MetaValue::U32(n_experts),
),
(
format!("{arch_name}.attention.key_length"),
MetaValue::U32(head_dim),
),
(
format!("{arch_name}.attention.value_length"),
MetaValue::U32(head_dim),
),
(
format!("{arch_name}.attention.causal"),
MetaValue::Bool(false),
),
(format!("{arch_name}.pooling_type"), MetaValue::U32(1)),
(
format!("{arch_name}.moe_every_n_layers"),
MetaValue::U32(n_layers_per_moe),
),
(
format!("{arch_name}.expert_used_count"),
MetaValue::U32(moe_top_k),
),
]);
kv_v2moe.extend(emit_general_postlude(file_type));
kv_v2moe
} else {
vec![
(
"general.architecture".into(),
MetaValue::String(arch_name.into()),
),
(
"general.name".into(),
MetaValue::String(raw_name_string.clone()),
),
(
format!("{arch_name}.context_length"),
MetaValue::U32(ctx_len),
),
(
format!("{arch_name}.embedding_length"),
MetaValue::U32(hidden_size),
),
(format!("{arch_name}.block_count"), MetaValue::U32(n_layers)),
(
format!("{arch_name}.feed_forward_length"),
MetaValue::U32(ffn_len),
),
(
format!("{arch_name}.attention.head_count"),
MetaValue::U32(n_head),
),
(
format!("{arch_name}.attention.head_count_kv"),
MetaValue::U32(n_head_kv),
),
(
format!("{arch_name}.attention.layer_norm_epsilon"),
MetaValue::F32(ln_eps),
),
(
format!("{arch_name}.rope.freq_base"),
MetaValue::F32(rope_theta),
),
(format!("{arch_name}.pooling_type"), MetaValue::U32(1)),
("general.file_type".into(), MetaValue::U32(file_type)),
]
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
fn ctx_v15() -> NomicBertCtx {
NomicBertCtx { num_experts: None }
}
fn ctx_v2_moe() -> NomicBertCtx {
NomicBertCtx {
num_experts: Some(8),
}
}
const NO_SHAPE: &[usize] = &[];
#[track_caller]
fn assert_direct(hf: &str, expected_gguf: &str) {
let got = map_tensor_name(hf, NO_SHAPE, &ctx_v15());
match got.as_ref() {
Some(MappedTensor::Direct(s)) => assert_eq!(
s.as_str(),
expected_gguf,
"map_tensor_name({hf:?}) = Direct({s:?}), want Direct({expected_gguf:?})"
),
other => {
panic!("map_tensor_name({hf:?}) = {other:?}, want Some(Direct({expected_gguf:?}))")
}
}
}
#[test]
fn nomic_bert_tensor_name_round_trip() {
let cases: &[(&str, &str)] = &[
("embeddings.word_embeddings.weight", "token_embd.weight"),
("emb_ln.weight", "token_embd_norm.weight"),
("emb_ln.bias", "token_embd_norm.bias"),
("embeddings.LayerNorm.weight", "token_embd_norm.weight"),
("embeddings.LayerNorm.bias", "token_embd_norm.bias"),
("encoder.layers.0.attn.Wqkv.weight", "blk.0.attn_qkv.weight"),
("encoder.layers.5.attn.Wqkv.weight", "blk.5.attn_qkv.weight"),
(
"encoder.layers.11.attn.Wqkv.weight",
"blk.11.attn_qkv.weight",
),
(
"encoder.layers.0.attn.out_proj.weight",
"blk.0.attn_output.weight",
),
(
"encoder.layers.3.norm1.weight",
"blk.3.attn_output_norm.weight",
),
("encoder.layers.3.norm1.bias", "blk.3.attn_output_norm.bias"),
("encoder.layers.7.mlp.fc11.weight", "blk.7.ffn_gate.weight"),
("encoder.layers.7.mlp.fc12.weight", "blk.7.ffn_up.weight"),
("encoder.layers.7.mlp.fc2.weight", "blk.7.ffn_down.weight"),
(
"encoder.layers.9.norm2.weight",
"blk.9.layer_output_norm.weight",
),
(
"encoder.layers.9.norm2.bias",
"blk.9.layer_output_norm.bias",
),
];
for &(hf, expected_gguf) in cases {
assert_direct(hf, expected_gguf);
}
}
#[test]
fn nomic_bert_token_types_weight_emits_squeeze_bake() {
let ctx = ctx_v15();
match map_tensor_name("embeddings.token_type_embeddings.weight", NO_SHAPE, &ctx) {
Some(MappedTensor::DirectWithBake { gguf_name, bake }) => {
assert_eq!(gguf_name, "token_types.weight");
assert_eq!(bake, BakeOp::Squeeze);
}
other => panic!("expected DirectWithBake(Squeeze), got {other:?}"),
}
}
#[test]
fn nomic_bert_strips_optional_bert_prefix() {
assert_direct(
"bert.embeddings.word_embeddings.weight",
"token_embd.weight",
);
assert_direct(
"bert.encoder.layers.0.attn.Wqkv.weight",
"blk.0.attn_qkv.weight",
);
assert_direct(
"bert.encoder.layers.4.mlp.fc11.weight",
"blk.4.ffn_gate.weight",
);
assert_direct("bert.emb_ln.bias", "token_embd_norm.bias");
}
#[test]
fn nomic_bert_v2_moe_block_patterns_map() {
assert_direct("encoder.layers.0.attn.Wqkv.bias", "blk.0.attn_qkv.bias");
assert_direct("encoder.layers.5.attn.Wqkv.bias", "blk.5.attn_qkv.bias");
assert_direct(
"encoder.layers.0.attn.out_proj.bias",
"blk.0.attn_output.bias",
);
assert_direct(
"encoder.layers.11.attn.out_proj.bias",
"blk.11.attn_output.bias",
);
assert_direct("encoder.layers.0.mlp.fc1.weight", "blk.0.ffn_up.weight");
assert_direct("encoder.layers.0.mlp.fc1.bias", "blk.0.ffn_up.bias");
assert_direct("encoder.layers.0.mlp.fc2.bias", "blk.0.ffn_down.bias");
assert_direct("encoder.layers.10.mlp.fc1.weight", "blk.10.ffn_up.weight");
assert_direct(
"encoder.layers.1.mlp.router.layer.weight",
"blk.1.ffn_gate_inp.weight",
);
assert_direct(
"encoder.layers.11.mlp.router.layer.weight",
"blk.11.ffn_gate_inp.weight",
);
}
#[test]
fn nomic_bert_filter_tensors_drops_are_drop_outcome() {
let ctx = ctx_v2_moe();
assert_eq!(
map_tensor_name("pooler.dense.weight", NO_SHAPE, &ctx),
Some(MappedTensor::Drop)
);
assert_eq!(
map_tensor_name("pooler.dense.bias", NO_SHAPE, &ctx),
Some(MappedTensor::Drop)
);
assert_eq!(
map_tensor_name("embeddings.position_embeddings.weight", NO_SHAPE, &ctx),
Some(MappedTensor::Drop)
);
assert_eq!(
map_tensor_name("encoder.layers.0.mlp.experts.bias", NO_SHAPE, &ctx),
Some(MappedTensor::Drop)
);
assert_eq!(
map_tensor_name("encoder.layers.11.mlp.experts.bias", NO_SHAPE, &ctx),
Some(MappedTensor::Drop)
);
}
#[test]
fn nomic_bert_v2_moe_expert_weights_map_to_direct_with_bake() {
let ctx = ctx_v2_moe();
let hf_shape: &[usize] = &[24576, 768];
match map_tensor_name("encoder.layers.1.mlp.experts.mlp.w1", hf_shape, &ctx) {
Some(MappedTensor::DirectWithBake { gguf_name, bake }) => {
assert_eq!(gguf_name, "blk.1.ffn_up_exps.weight");
assert_eq!(
bake,
BakeOp::MoeExpertReshape {
n_experts: 8,
n_inner: 3072,
n_embd: 768,
}
);
}
other => panic!("expected DirectWithBake(MoeExpertReshape) for w1, got {other:?}"),
}
match map_tensor_name("encoder.layers.7.mlp.experts.mlp.w2", hf_shape, &ctx) {
Some(MappedTensor::DirectWithBake { gguf_name, bake }) => {
assert_eq!(gguf_name, "blk.7.ffn_down_exps.weight");
assert_eq!(
bake,
BakeOp::MoeExpertTranspose {
n_experts: 8,
n_inner: 3072,
n_embd: 768,
}
);
}
other => panic!("expected DirectWithBake(MoeExpertTranspose) for w2, got {other:?}"),
}
}
#[test]
fn nomic_bert_expert_weights_unmapped_when_ctx_lacks_num_experts() {
let ctx = ctx_v15();
let hf_shape: &[usize] = &[24576, 768];
assert_eq!(
map_tensor_name("encoder.layers.1.mlp.experts.mlp.w1", hf_shape, &ctx),
None,
"without num_experts the expert tensors must surface as \
Unmapped (typed error) to match no-fallback rule"
);
assert_eq!(
map_tensor_name("encoder.layers.1.mlp.experts.mlp.w2", hf_shape, &ctx),
None
);
}
#[test]
fn nomic_bert_expert_weights_unmapped_when_shape_does_not_divide() {
let ctx = ctx_v2_moe();
let hf_shape: &[usize] = &[24573, 768];
assert_eq!(
map_tensor_name("encoder.layers.1.mlp.experts.mlp.w1", hf_shape, &ctx),
None
);
}
#[test]
fn nomic_bert_tensor_name_rejects_unknown_kinds() {
let ctx = ctx_v2_moe();
assert_eq!(
map_tensor_name("encoder.layer.0.attention.self.Wqkv.weight", NO_SHAPE, &ctx),
None
);
assert_eq!(
map_tensor_name(
"encoder.layers.0.attention.self.query.weight",
NO_SHAPE,
&ctx
),
None
);
assert_eq!(
map_tensor_name("encoder.layers.0.mlp.fc11.bias", NO_SHAPE, &ctx),
None
);
assert_eq!(
map_tensor_name("encoder.layers.0.mlp.fc12.bias", NO_SHAPE, &ctx),
None
);
assert_eq!(
map_tensor_name("encoder.layers.01.attn.Wqkv.weight", NO_SHAPE, &ctx),
None
);
assert_eq!(
map_tensor_name("encoder.layers..attn.Wqkv.weight", NO_SHAPE, &ctx),
None
);
assert_eq!(
map_tensor_name("encoder.layers.0.unknown.weight", NO_SHAPE, &ctx),
None
);
}
#[test]
fn nomic_bert_metadata_built_from_gpt_style_config() {
let cfg = json!({
"_name_or_path": "nomic-ai/nomic-embed-text-v1.5",
"n_embd": 768,
"n_layer": 12,
"n_head": 12,
"n_inner": 3072,
"max_position_embeddings": 2048,
"layer_norm_epsilon": 1.0e-12,
"rotary_emb_base": 1000.0,
});
let kv = build_metadata(&cfg, 17 , None, None);
assert_eq!(kv.len(), 12, "NomicBert emits 12 KV pairs at v1");
let by_key: std::collections::HashMap<_, _> =
kv.iter().map(|(k, v)| (k.as_str(), v.clone())).collect();
assert_eq!(
by_key["general.architecture"],
MetaValue::String("nomic-bert".into())
);
assert_eq!(
by_key["general.name"],
MetaValue::String("nomic-ai/nomic-embed-text-v1.5".into())
);
assert_eq!(by_key["nomic-bert.context_length"], MetaValue::U32(2048));
assert_eq!(by_key["nomic-bert.embedding_length"], MetaValue::U32(768));
assert_eq!(by_key["nomic-bert.block_count"], MetaValue::U32(12));
assert_eq!(
by_key["nomic-bert.feed_forward_length"],
MetaValue::U32(3072)
);
assert_eq!(
by_key["nomic-bert.attention.head_count"],
MetaValue::U32(12)
);
assert_eq!(
by_key["nomic-bert.attention.head_count_kv"],
MetaValue::U32(12),
"NomicBert has no GQA — head_count_kv == head_count"
);
assert_eq!(
by_key["nomic-bert.attention.layer_norm_epsilon"],
MetaValue::F32(1.0e-12)
);
assert_eq!(by_key["nomic-bert.rope.freq_base"], MetaValue::F32(1000.0));
assert_eq!(
by_key["nomic-bert.pooling_type"],
MetaValue::U32(1),
"Nomic embeddings are mean-pooled (LLAMA_POOLING_TYPE_MEAN = 1)"
);
assert_eq!(by_key["general.file_type"], MetaValue::U32(17));
}
#[test]
fn nomic_bert_v2_moe_metadata_uses_nomic_bert_moe_arch() {
let cfg = json!({
"_name_or_path": "nomic-ai/nomic-embed-text-v2-moe",
"n_embd": 768,
"n_layer": 12,
"n_head": 12,
"n_inner": 3072,
"n_positions": 2048,
"pad_token_id": 1,
"layer_norm_epsilon": 1.0e-5, "rotary_emb_base": 10000.0, "moe_every_n_layers": 2,
"num_experts": 8,
"moe_top_k": 2,
});
let kv = build_metadata(&cfg, 17, None, None);
let by_key: std::collections::HashMap<_, _> =
kv.iter().map(|(k, v)| (k.as_str(), v.clone())).collect();
assert_eq!(
by_key["general.architecture"],
MetaValue::String("nomic-bert-moe".into()),
);
assert_eq!(
by_key["nomic-bert-moe.context_length"],
MetaValue::U32(2046),
"v2-moe: ctx_len = n_positions - (1 + pad_token_id) = 2046",
);
assert_eq!(
by_key["nomic-bert-moe.embedding_length"],
MetaValue::U32(768)
);
assert_eq!(by_key["nomic-bert-moe.block_count"], MetaValue::U32(12));
assert_eq!(
by_key["nomic-bert-moe.feed_forward_length"],
MetaValue::U32(3072)
);
assert_eq!(
by_key["nomic-bert-moe.attention.head_count"],
MetaValue::U32(12)
);
assert_eq!(
by_key["nomic-bert-moe.attention.layer_norm_epsilon"],
MetaValue::F32(1.0e-12),
"v2-moe: AutoConfig overrides config 1e-5 with BERT-default 1e-12"
);
assert_eq!(
by_key["nomic-bert-moe.rope.freq_base"],
MetaValue::F32(1000.0),
"v2-moe: AutoConfig injects rope_parameters.rope_theta=1000.0"
);
assert_eq!(
by_key["nomic-bert-moe.attention.key_length"],
MetaValue::U32(64)
);
assert_eq!(
by_key["nomic-bert-moe.attention.value_length"],
MetaValue::U32(64)
);
assert_eq!(
by_key["nomic-bert-moe.attention.causal"],
MetaValue::Bool(false)
);
assert_eq!(by_key["nomic-bert-moe.expert_count"], MetaValue::U32(8));
assert_eq!(
by_key["nomic-bert-moe.expert_used_count"],
MetaValue::U32(2)
);
assert_eq!(
by_key["nomic-bert-moe.moe_every_n_layers"],
MetaValue::U32(2)
);
assert!(
!by_key.contains_key("nomic-bert-moe.attention.head_count_kv"),
"v2-moe must NOT emit head_count_kv (canonical doesn't)"
);
assert!(
!by_key.contains_key("nomic-bert.context_length"),
"v2-moe must NOT emit nomic-bert.* prefix"
);
assert_eq!(by_key["general.quantization_version"], MetaValue::U32(2));
assert_eq!(by_key["general.file_type"], MetaValue::U32(17));
}
#[test]
fn nomic_bert_metadata_bert_style_keys_and_defaults() {
let cfg = json!({
"hidden_size": 64,
"num_hidden_layers": 2,
"num_attention_heads": 4,
"max_position_embeddings": 512,
});
let kv = build_metadata(&cfg, 0, None, None);
let by_key: std::collections::HashMap<_, _> =
kv.iter().map(|(k, v)| (k.as_str(), v.clone())).collect();
assert_eq!(
by_key["general.name"],
MetaValue::String("model".into()),
"name defaults to 'model' when _name_or_path absent"
);
assert_eq!(by_key["nomic-bert.embedding_length"], MetaValue::U32(64));
assert_eq!(by_key["nomic-bert.block_count"], MetaValue::U32(2));
assert_eq!(by_key["nomic-bert.attention.head_count"], MetaValue::U32(4));
assert_eq!(
by_key["nomic-bert.feed_forward_length"],
MetaValue::U32(256),
"n_inner defaults to 4 * embedding_length"
);
assert_eq!(
by_key["nomic-bert.attention.layer_norm_epsilon"],
MetaValue::F32(1.0e-12),
"layer_norm_epsilon defaults to 1.0e-12 per BertConfig"
);
assert_eq!(
by_key["nomic-bert.rope.freq_base"],
MetaValue::F32(10000.0),
"rotary_emb_base defaults to 10000.0"
);
assert_eq!(by_key["nomic-bert.pooling_type"], MetaValue::U32(1));
}
}