use std::collections::{BTreeMap, HashMap};
use std::fs::{self, File};
use std::path::{Path, PathBuf};
use memmap2::Mmap;
use safetensors::{tensor::Dtype, SafeTensors};
use crate::convert::source_dtype::{fp8, mxfp4};
use crate::core::mlx_safetensors_loader::{discover_shards, read_floats_to_f32};
use crate::quantize::ggml_quants::SourceDtype;
#[derive(Debug, Clone)]
pub struct HfTensor {
pub name: String,
pub shape: Vec<usize>,
pub source_dtype: SourceDtype,
pub data: Vec<f32>,
}
#[derive(Debug, Clone)]
pub struct TensorMeta {
pub name: String,
pub shape: Vec<usize>,
pub source_dtype: SourceDtype,
shard_idx: usize,
data_off_start: usize,
data_off_end: usize,
}
impl TensorMeta {
pub fn numel(&self) -> usize {
self.shape.iter().product()
}
}
pub struct HfModelSource {
pub config: serde_json::Value,
shards: Vec<ShardMmap>,
metas: Vec<TensorMeta>,
fp8_cfg: Option<Fp8Config>,
deepseek_v4: bool,
excluded_mtp_tensors: usize,
by_name: HashMap<String, usize>,
}
impl std::fmt::Debug for HfModelSource {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HfModelSource")
.field("config_keys", &self.config.as_object().map(|o| o.len()))
.field("shards", &self.shards.len())
.field("metas", &self.metas.len())
.field("fp8", &self.fp8_cfg.is_some())
.finish()
}
}
struct ShardMmap {
mmap: Mmap,
header_byte_len: usize,
}
fn is_deepseek_hash_route(name: &str) -> bool {
name.starts_with("layers.") && name.ends_with(".ffn.gate.tid2eid")
}
fn is_deepseek_expert_weight(name: &str) -> bool {
let Some(rest) = name.strip_prefix("layers.") else {
return false;
};
let Some((_layer, rest)) = rest.split_once(".ffn.experts.") else {
return false;
};
let Some((_expert, projection)) = rest.split_once('.') else {
return false;
};
matches!(projection, "w1.weight" | "w2.weight" | "w3.weight")
}
impl ShardMmap {
fn tensor_bytes(&self, off_start: usize, off_end: usize) -> &[u8] {
&self.mmap[self.header_byte_len + off_start..self.header_byte_len + off_end]
}
}
#[derive(Debug)]
pub enum SourceError {
Io(std::io::Error),
ConfigParse(serde_json::Error),
Discover(String),
Safetensors(String),
UnsupportedSourceDtype { tensor: String, dtype: String },
MissingFp8Scales { tensor: String, scale: String },
Fp8Dequant {
tensor: String,
error: fp8::Fp8Error,
},
Mxfp4Dequant {
tensor: String,
error: mxfp4::Mxfp4Error,
},
InvalidHashRoute { tensor: String, value: i64 },
InvalidFp8Config(String),
}
impl std::fmt::Display for SourceError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
SourceError::Io(e) => write!(f, "source/io: {e}"),
SourceError::ConfigParse(e) => write!(f, "source/config.json: {e}"),
SourceError::Discover(s) => write!(f, "source/discover: {s}"),
SourceError::Safetensors(s) => write!(f, "source/safetensors: {s}"),
SourceError::UnsupportedSourceDtype { tensor, dtype } => {
write!(
f,
"source/unsupported dtype `{dtype}` on tensor `{tensor}` \
(supported: F32, F16, BF16; architecture-specific FP8, E2M1, and integer routes require matching source metadata)"
)
}
SourceError::MissingFp8Scales { tensor, scale } => write!(
f,
"source/fp8: tensor `{tensor}` requires missing sibling `{scale}`"
),
SourceError::Fp8Dequant { tensor, error } => {
write!(f, "source/fp8: dequant `{tensor}`: {error}")
}
SourceError::Mxfp4Dequant { tensor, error } => {
write!(f, "source/mxfp4: dequant `{tensor}`: {error}")
}
SourceError::InvalidHashRoute { tensor, value } => write!(
f,
"source/hash-route: `{tensor}` value {value} is not exactly representable as I32"
),
SourceError::InvalidFp8Config(s) => write!(f, "source/fp8 config: {s}"),
}
}
}
impl std::error::Error for SourceError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
SourceError::Io(e) => Some(e),
SourceError::ConfigParse(e) => Some(e),
_ => None,
}
}
}
impl From<std::io::Error> for SourceError {
fn from(e: std::io::Error) -> Self {
SourceError::Io(e)
}
}
impl From<serde_json::Error> for SourceError {
fn from(e: serde_json::Error) -> Self {
SourceError::ConfigParse(e)
}
}
#[derive(Debug, Clone)]
pub struct Fp8Config {
pub block_size: fp8::BlockSize,
pub modules_to_not_convert: Vec<String>,
}
impl Fp8Config {
pub fn from_config(config: &serde_json::Value) -> Result<Option<Self>, SourceError> {
let qc = match config.get("quantization_config") {
Some(v) if v.is_object() => v,
_ => return Ok(None),
};
let qm = match qc.get("quant_method").and_then(|v| v.as_str()) {
Some(s) => s,
None => return Ok(None),
};
if qm != "fp8" {
return Ok(None);
}
let wbs = qc
.get("weight_block_size")
.or_else(|| qc.get("weight_block"))
.ok_or_else(|| {
SourceError::InvalidFp8Config(
"quant_method=fp8 but `weight_block_size`/`weight_block` is missing".into(),
)
})?;
let arr = wbs.as_array().ok_or_else(|| {
SourceError::InvalidFp8Config(format!(
"`weight_block_size` must be a 2-element array, got {wbs:?}"
))
})?;
if arr.len() != 2 {
return Err(SourceError::InvalidFp8Config(format!(
"`weight_block_size` must be a 2-element array, got len {}",
arr.len()
)));
}
let parse_dim = |v: &serde_json::Value| -> Result<usize, SourceError> {
v.as_u64().map(|x| x as usize).ok_or_else(|| {
SourceError::InvalidFp8Config(format!(
"`weight_block_size` entries must be non-negative ints, got {v:?}"
))
})
};
let block_size = (parse_dim(&arr[0])?, parse_dim(&arr[1])?);
let modules_to_not_convert = qc
.get("modules_to_not_convert")
.and_then(|v| v.as_array())
.map(|a| {
a.iter()
.filter_map(|x| x.as_str().map(|s| s.to_string()))
.collect::<Vec<_>>()
})
.unwrap_or_default();
Ok(Some(Fp8Config {
block_size,
modules_to_not_convert,
}))
}
pub fn is_not_converted(&self, tensor_name: &str) -> bool {
self.modules_to_not_convert
.iter()
.any(|m| tensor_name.contains(m.as_str()))
}
}
impl HfModelSource {
pub fn open(model_dir: &Path) -> Result<Self, SourceError> {
let config_path = model_dir.join("config.json");
let config_raw = fs::read_to_string(&config_path).map_err(|e| {
SourceError::Io(std::io::Error::new(
e.kind(),
format!("read {}: {e}", config_path.display()),
))
})?;
let config: serde_json::Value = serde_json::from_str(&config_raw)?;
let fp8_cfg = Fp8Config::from_config(&config)?;
let model_type = config
.get("model_type")
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string();
let deepseek_v4 = model_type == "deepseek_v4";
let shards_map: BTreeMap<String, PathBuf> =
discover_shards(model_dir).map_err(|e| SourceError::Discover(format!("{e:#}")))?;
let mut shard_paths: Vec<PathBuf> = shards_map.values().cloned().collect();
shard_paths.sort();
shard_paths.dedup();
let mut shards: Vec<ShardMmap> = Vec::with_capacity(shard_paths.len());
let mut metas: Vec<TensorMeta> = Vec::new();
let mut by_name: HashMap<String, usize> = HashMap::new();
let mut excluded_mtp_tensors = 0usize;
for (shard_idx, shard_path) in shard_paths.iter().enumerate() {
let f = File::open(shard_path).map_err(|e| {
SourceError::Io(std::io::Error::new(
e.kind(),
format!("open {}: {e}", shard_path.display()),
))
})?;
let mmap = unsafe { Mmap::map(&f) }.map_err(|e| {
SourceError::Io(std::io::Error::new(
e.kind(),
format!("mmap {}: {e}", shard_path.display()),
))
})?;
let (header_size, meta) = SafeTensors::read_metadata(&mmap[..]).map_err(|e| {
SourceError::Safetensors(format!("header parse {}: {e}", shard_path.display()))
})?;
let header_byte_len = 8 + header_size;
for name in meta.offset_keys() {
let info = meta
.info(&name)
.expect("offset_keys yields names that index_map contains");
if super::arch::should_drop_source_tensor(&model_type, &name) {
if deepseek_v4 && name.starts_with("mtp.") {
excluded_mtp_tensors += 1;
}
continue;
}
let mut logical_shape = info.shape.clone();
let source_dtype = match info.dtype {
Dtype::F32 => SourceDtype::F32,
Dtype::F16 => SourceDtype::F16,
Dtype::BF16 => SourceDtype::BF16,
Dtype::F8_E4M3 => {
SourceDtype::Fp8E4M3
}
Dtype::F8_E8M0 if deepseek_v4 && name.ends_with(".scale") => {
SourceDtype::E8M0Scale
}
Dtype::U8 if deepseek_v4 && name.ends_with(".scale") => {
SourceDtype::E8M0Scale
}
Dtype::I8 | Dtype::U8 if deepseek_v4 && is_deepseek_expert_weight(&name) => {
if logical_shape.len() != 2 {
return Err(SourceError::UnsupportedSourceDtype {
tensor: name.to_string(),
dtype: format!(
"{:?} packed E2M1 with non-2D shape {logical_shape:?}",
info.dtype
),
});
}
logical_shape[1] = logical_shape[1].checked_mul(2).ok_or_else(|| {
SourceError::Safetensors(format!(
"tensor `{name}` logical column overflow"
))
})?;
SourceDtype::Mxfp4E2M1
}
Dtype::I32 if deepseek_v4 && is_deepseek_hash_route(&name) => SourceDtype::I32,
Dtype::I64 if deepseek_v4 && is_deepseek_hash_route(&name) => SourceDtype::I64,
other => {
return Err(SourceError::UnsupportedSourceDtype {
tensor: name.to_string(),
dtype: format!("{other:?}"),
});
}
};
let idx = metas.len();
metas.push(TensorMeta {
name: name.to_string(),
shape: logical_shape,
source_dtype,
shard_idx,
data_off_start: info.data_offsets.0,
data_off_end: info.data_offsets.1,
});
by_name.insert(name.to_string(), idx);
}
shards.push(ShardMmap {
mmap,
header_byte_len,
});
}
Ok(HfModelSource {
config,
shards,
metas,
fp8_cfg,
deepseek_v4,
excluded_mtp_tensors,
by_name,
})
}
pub fn tensor_metas(&self) -> impl Iterator<Item = &TensorMeta> + '_ {
let fp8_active = self.fp8_cfg.is_some();
let deepseek_v4 = self.deepseek_v4;
self.metas.iter().filter(move |m| {
!(fp8_active && m.name.ends_with(".weight_scale_inv"))
&& !(deepseek_v4 && m.name.ends_with(".scale"))
})
}
pub fn tensor_count(&self) -> usize {
self.tensor_metas().count()
}
pub fn excluded_mtp_tensor_count(&self) -> usize {
self.excluded_mtp_tensors
}
pub fn materialize_tensor(&self, name: &str) -> Result<HfTensor, SourceError> {
let idx = self.by_name.get(name).copied().ok_or_else(|| {
SourceError::Safetensors(format!("tensor `{name}` not found in any shard"))
})?;
let m = &self.metas[idx];
if (self.fp8_cfg.is_some() && name.ends_with(".weight_scale_inv"))
|| (self.deepseek_v4 && name.ends_with(".scale"))
{
return Err(SourceError::UnsupportedSourceDtype {
tensor: name.into(),
dtype: "fp8 sibling scale (consumed inline by main weight)".into(),
});
}
materialize_tensor(self, m)
}
pub fn iter_tensors(&self) -> TensorStream<'_> {
TensorStream {
source: self,
cursor: 0,
}
}
}
pub struct TensorStream<'a> {
source: &'a HfModelSource,
cursor: usize,
}
impl<'a> Iterator for TensorStream<'a> {
type Item = Result<HfTensor, SourceError>;
fn next(&mut self) -> Option<Self::Item> {
let fp8_active = self.source.fp8_cfg.is_some();
let deepseek_v4 = self.source.deepseek_v4;
loop {
if self.cursor >= self.source.metas.len() {
return None;
}
let m = &self.source.metas[self.cursor];
if (fp8_active && m.name.ends_with(".weight_scale_inv"))
|| (deepseek_v4 && m.name.ends_with(".scale"))
{
self.cursor += 1;
continue;
}
self.cursor += 1;
return Some(materialize_tensor(self.source, m));
}
}
}
fn materialize_tensor(src: &HfModelSource, m: &TensorMeta) -> Result<HfTensor, SourceError> {
let shard = &src.shards[m.shard_idx];
let raw_bytes = shard.tensor_bytes(m.data_off_start, m.data_off_end);
let (source_dtype, data) = match m.source_dtype {
SourceDtype::F32 => (
SourceDtype::F32,
read_floats_to_f32(raw_bytes, Dtype::F32)
.map_err(|e| SourceError::Safetensors(format!("F32 dequant {}: {e:#}", m.name)))?,
),
SourceDtype::F16 => (
SourceDtype::F16,
read_floats_to_f32(raw_bytes, Dtype::F16)
.map_err(|e| SourceError::Safetensors(format!("F16 dequant {}: {e:#}", m.name)))?,
),
SourceDtype::BF16 => (
SourceDtype::BF16,
read_floats_to_f32(raw_bytes, Dtype::BF16)
.map_err(|e| SourceError::Safetensors(format!("BF16 dequant {}: {e:#}", m.name)))?,
),
SourceDtype::Fp8E4M3 => {
let cfg = src
.fp8_cfg
.as_ref()
.ok_or_else(|| SourceError::UnsupportedSourceDtype {
tensor: m.name.clone(),
dtype: "F8_E4M3 without quantization_config.quant_method=fp8".into(),
})?;
if cfg.is_not_converted(&m.name) {
return Err(SourceError::InvalidFp8Config(format!(
"tensor `{}` matches modules_to_not_convert \
but is on disk as FP8 — config and weights disagree",
m.name
)));
}
let scale_name = if src.deepseek_v4 {
m.name
.strip_suffix(".weight")
.map(|base| format!("{base}.scale"))
.ok_or_else(|| {
SourceError::InvalidFp8Config(format!(
"DeepSeek-V4 FP8 tensor `{}` does not end in .weight",
m.name
))
})?
} else {
format!("{}_scale_inv", m.name)
};
let scale_idx = src.by_name.get(&scale_name).copied().ok_or_else(|| {
SourceError::MissingFp8Scales {
tensor: m.name.clone(),
scale: scale_name.clone(),
}
})?;
let scale_meta = &src.metas[scale_idx];
let valid_scale = if src.deepseek_v4 {
matches!(scale_meta.source_dtype, SourceDtype::E8M0Scale)
} else {
matches!(scale_meta.source_dtype, SourceDtype::F32)
};
if !valid_scale {
return Err(SourceError::InvalidFp8Config(format!(
"scale `{scale_name}`: expected F32, got {:?}",
scale_meta.source_dtype
)));
}
let scale_shard = &src.shards[scale_meta.shard_idx];
let scale_bytes =
scale_shard.tensor_bytes(scale_meta.data_off_start, scale_meta.data_off_end);
let scale_inv = if src.deepseek_v4 {
scale_bytes
.iter()
.copied()
.map(mxfp4::decode_e8m0)
.collect()
} else {
read_floats_to_f32(scale_bytes, Dtype::F32).map_err(|e| {
SourceError::Safetensors(format!("scale dequant {scale_name}: {e:#}"))
})?
};
let f32_data =
fp8::dequantize_fp8_block(raw_bytes, &scale_inv, &m.shape, cfg.block_size)
.map_err(|e| SourceError::Fp8Dequant {
tensor: m.name.clone(),
error: e,
})?;
(SourceDtype::Fp8E4M3, f32_data)
}
SourceDtype::Mxfp4E2M1 => {
let scale_name = m
.name
.strip_suffix(".weight")
.map(|base| format!("{base}.scale"))
.ok_or_else(|| {
SourceError::Safetensors(format!(
"packed expert `{}` does not end in .weight",
m.name
))
})?;
let scale_idx = src.by_name.get(&scale_name).copied().ok_or_else(|| {
SourceError::MissingFp8Scales {
tensor: m.name.clone(),
scale: scale_name.clone(),
}
})?;
let scale_meta = &src.metas[scale_idx];
if !matches!(scale_meta.source_dtype, SourceDtype::E8M0Scale) {
return Err(SourceError::InvalidFp8Config(format!(
"expert scale `{scale_name}`: expected F8_E8M0, got {:?}",
scale_meta.source_dtype
)));
}
let scale_shard = &src.shards[scale_meta.shard_idx];
let scale_bytes =
scale_shard.tensor_bytes(scale_meta.data_off_start, scale_meta.data_off_end);
let packed_shape = [m.shape[0], m.shape[1] / 2];
let data =
mxfp4::dequantize_e2m1(raw_bytes, &packed_shape, scale_bytes, &scale_meta.shape)
.map_err(|error| SourceError::Mxfp4Dequant {
tensor: m.name.clone(),
error,
})?;
(SourceDtype::Mxfp4E2M1, data)
}
SourceDtype::I32 => {
if raw_bytes.len() != m.numel() * 4 {
return Err(SourceError::Safetensors(format!(
"I32 tensor `{}` byte length {} != {}",
m.name,
raw_bytes.len(),
m.numel() * 4
)));
}
let mut data = Vec::with_capacity(m.numel());
for b in raw_bytes.chunks_exact(4) {
let value = i32::from_le_bytes([b[0], b[1], b[2], b[3]]);
if (value as f32) as i32 != value {
return Err(SourceError::InvalidHashRoute {
tensor: m.name.clone(),
value: value as i64,
});
}
data.push(value as f32);
}
(SourceDtype::I32, data)
}
SourceDtype::I64 => {
if raw_bytes.len() != m.numel() * 8 {
return Err(SourceError::Safetensors(format!(
"I64 tensor `{}` byte length {} != {}",
m.name,
raw_bytes.len(),
m.numel() * 8
)));
}
let mut data = Vec::with_capacity(m.numel());
for b in raw_bytes.chunks_exact(8) {
let value = i64::from_le_bytes([b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]]);
if i32::try_from(value).is_err() || (value as f32) as i64 != value {
return Err(SourceError::InvalidHashRoute {
tensor: m.name.clone(),
value,
});
}
data.push(value as f32);
}
(SourceDtype::I64, data)
}
SourceDtype::E8M0Scale => {
return Err(SourceError::UnsupportedSourceDtype {
tensor: m.name.clone(),
dtype: "F8_E8M0 sidecar (consumed inline by weight)".into(),
});
}
};
let expect = m.numel();
if data.len() != expect {
return Err(SourceError::Safetensors(format!(
"tensor `{}`: dequant produced {} f32s, shape product {}",
m.name,
data.len(),
expect
)));
}
Ok(HfTensor {
name: m.name.clone(),
shape: m.shape.clone(),
source_dtype,
data,
})
}
#[cfg(test)]
mod tests {
use super::*;
use safetensors::tensor::TensorView;
fn write_minimal_single_file(
dir: &Path,
tensors: &[(&str, Dtype, Vec<usize>, Vec<u8>)],
config: &serde_json::Value,
) {
let views: Vec<(String, TensorView<'_>)> = tensors
.iter()
.map(|(name, dtype, shape, bytes)| {
let v = TensorView::new(*dtype, shape.clone(), bytes).expect("TensorView");
(name.to_string(), v)
})
.collect();
let view_refs: Vec<(String, &TensorView<'_>)> =
views.iter().map(|(n, v)| (n.clone(), v)).collect();
let bytes = safetensors::tensor::serialize(view_refs, None).expect("serialize");
fs::write(dir.join("model.safetensors"), bytes).expect("write safetensors");
fs::write(
dir.join("config.json"),
serde_json::to_string_pretty(config).unwrap(),
)
.expect("write config.json");
}
fn collect_tensors(src: &HfModelSource) -> Vec<HfTensor> {
src.iter_tensors().map(|r| r.expect("stream")).collect()
}
#[test]
fn open_single_file_f32_round_trip() {
let dir = tempfile::tempdir().unwrap();
let f32_bytes: Vec<u8> = (0..6).flat_map(|i| (i as f32).to_le_bytes()).collect();
write_minimal_single_file(
dir.path(),
&[("model.norm.weight", Dtype::F32, vec![6], f32_bytes)],
&serde_json::json!({ "model_type": "llama" }),
);
let src = HfModelSource::open(dir.path()).expect("open");
let tensors = collect_tensors(&src);
assert_eq!(tensors.len(), 1);
let t = &tensors[0];
assert_eq!(t.name, "model.norm.weight");
assert_eq!(t.shape, vec![6]);
assert_eq!(t.source_dtype, SourceDtype::F32);
assert_eq!(t.data, vec![0.0, 1.0, 2.0, 3.0, 4.0, 5.0]);
assert_eq!(src.config["model_type"], "llama");
}
#[test]
fn open_dequantizes_f16_and_bf16() {
let dir = tempfile::tempdir().unwrap();
let f16_vals: Vec<f32> = vec![1.5, -0.5, 2.0, 0.25];
let f16_bytes: Vec<u8> = f16_vals
.iter()
.flat_map(|v| half::f16::from_f32(*v).to_le_bytes())
.collect();
let bf16_vals: Vec<f32> = vec![1.0, -2.0, 0.5];
let bf16_bytes: Vec<u8> = bf16_vals
.iter()
.flat_map(|v| half::bf16::from_f32(*v).to_le_bytes())
.collect();
write_minimal_single_file(
dir.path(),
&[
("a.weight", Dtype::F16, vec![4], f16_bytes),
("b.weight", Dtype::BF16, vec![3], bf16_bytes),
],
&serde_json::json!({}),
);
let src = HfModelSource::open(dir.path()).expect("open");
let tensors = collect_tensors(&src);
let by_name: std::collections::HashMap<&str, &HfTensor> =
tensors.iter().map(|t| (t.name.as_str(), t)).collect();
let a = by_name["a.weight"];
assert_eq!(a.source_dtype, SourceDtype::F16);
assert_eq!(a.shape, vec![4]);
for (got, want) in a.data.iter().zip(f16_vals.iter()) {
assert!((got - want).abs() < 1e-3, "F16 round-trip drift");
}
let b = by_name["b.weight"];
assert_eq!(b.source_dtype, SourceDtype::BF16);
assert_eq!(b.shape, vec![3]);
for (got, want) in b.data.iter().zip(bf16_vals.iter()) {
assert!(
(got - want).abs() < 0.05 * want.abs().max(1.0),
"BF16 drift"
);
}
}
#[test]
fn open_unsupported_dtype_errors_typed() {
let dir = tempfile::tempdir().unwrap();
let u8_bytes: Vec<u8> = vec![1, 2, 3, 4];
write_minimal_single_file(
dir.path(),
&[("weird.weight", Dtype::U8, vec![4], u8_bytes)],
&serde_json::json!({}),
);
let err = HfModelSource::open(dir.path()).expect_err("must error");
match err {
SourceError::UnsupportedSourceDtype { tensor, .. } => {
assert_eq!(tensor, "weird.weight");
}
other => panic!("expected UnsupportedSourceDtype, got {other:?}"),
}
}
#[test]
fn fp8_config_detection() {
let dir = tempfile::tempdir().unwrap();
let fp8_bytes: Vec<u8> = vec![0x38; 16];
let scale_bytes: Vec<u8> = 2.0_f32.to_le_bytes().to_vec();
write_minimal_single_file(
dir.path(),
&[
(
"model.layers.0.mlp.gate_proj.weight",
Dtype::F8_E4M3,
vec![4, 4],
fp8_bytes,
),
(
"model.layers.0.mlp.gate_proj.weight_scale_inv",
Dtype::F32,
vec![1, 1],
scale_bytes,
),
],
&serde_json::json!({
"model_type": "minimax_m2",
"quantization_config": {
"quant_method": "fp8",
"weight_block_size": [4, 4],
}
}),
);
let src = HfModelSource::open(dir.path()).expect("open");
let tensors = collect_tensors(&src);
assert_eq!(tensors.len(), 1, "scale tensor must be hidden");
let t = &tensors[0];
assert_eq!(t.name, "model.layers.0.mlp.gate_proj.weight");
assert_eq!(t.source_dtype, SourceDtype::Fp8E4M3);
assert_eq!(t.shape, vec![4, 4]);
for v in &t.data {
assert_eq!(*v, 2.0);
}
assert_eq!(src.tensor_count(), 1);
}
#[test]
fn fp8_modules_to_not_convert() {
let dir = tempfile::tempdir().unwrap();
let bf16_vals = [1.0_f32, -2.0, 0.5, 0.25];
let bf16_bytes: Vec<u8> = bf16_vals
.iter()
.flat_map(|v| half::bf16::from_f32(*v).to_le_bytes())
.collect();
let fp8_bytes: Vec<u8> = vec![0x38; 4]; let scale_bytes: Vec<u8> = 1.0_f32.to_le_bytes().to_vec();
write_minimal_single_file(
dir.path(),
&[
("lm_head.weight", Dtype::BF16, vec![2, 2], bf16_bytes),
(
"model.layers.0.mlp.gate_proj.weight",
Dtype::F8_E4M3,
vec![2, 2],
fp8_bytes,
),
(
"model.layers.0.mlp.gate_proj.weight_scale_inv",
Dtype::F32,
vec![1, 1],
scale_bytes,
),
],
&serde_json::json!({
"model_type": "minimax_m2",
"quantization_config": {
"quant_method": "fp8",
"weight_block_size": [2, 2],
"modules_to_not_convert": ["lm_head", "embed_tokens"],
}
}),
);
let src = HfModelSource::open(dir.path()).expect("open");
let tensors = collect_tensors(&src);
let by_name: std::collections::HashMap<&str, &HfTensor> =
tensors.iter().map(|t| (t.name.as_str(), t)).collect();
assert_eq!(tensors.len(), 2);
let head = by_name["lm_head.weight"];
assert_eq!(head.source_dtype, SourceDtype::BF16);
for (got, want) in head.data.iter().zip(bf16_vals.iter()) {
assert!((got - want).abs() < 0.05 * want.abs().max(1.0));
}
let gate = by_name["model.layers.0.mlp.gate_proj.weight"];
assert_eq!(gate.source_dtype, SourceDtype::Fp8E4M3);
assert_eq!(gate.data, vec![1.0, 1.0, 1.0, 1.0]);
}
#[test]
fn fp8_missing_scale_errors() {
let dir = tempfile::tempdir().unwrap();
let fp8_bytes: Vec<u8> = vec![0x38; 4];
write_minimal_single_file(
dir.path(),
&[(
"model.layers.0.mlp.gate_proj.weight",
Dtype::F8_E4M3,
vec![2, 2],
fp8_bytes,
)],
&serde_json::json!({
"model_type": "minimax_m2",
"quantization_config": {
"quant_method": "fp8",
"weight_block_size": [2, 2],
}
}),
);
let src = HfModelSource::open(dir.path()).expect("open");
let err = src
.iter_tensors()
.next()
.expect("one tensor")
.expect_err("must error");
match err {
SourceError::MissingFp8Scales { tensor, .. } => {
assert_eq!(tensor, "model.layers.0.mlp.gate_proj.weight");
}
other => panic!("expected MissingFp8Scales, got {other:?}"),
}
}
#[test]
fn fp8_without_config_unsupported() {
let dir = tempfile::tempdir().unwrap();
let fp8_bytes: Vec<u8> = vec![0x38; 4];
write_minimal_single_file(
dir.path(),
&[("raw_fp8.weight", Dtype::F8_E4M3, vec![2, 2], fp8_bytes)],
&serde_json::json!({
"model_type": "llama" }),
);
let src = HfModelSource::open(dir.path()).expect("open");
let err = src
.iter_tensors()
.next()
.expect("one tensor")
.expect_err("must error");
match err {
SourceError::UnsupportedSourceDtype { tensor, .. } => {
assert_eq!(tensor, "raw_fp8.weight");
}
other => panic!("expected UnsupportedSourceDtype, got {other:?}"),
}
}
#[test]
fn open_missing_config_errors() {
let dir = tempfile::tempdir().unwrap();
let f32_bytes: Vec<u8> = (0..4).flat_map(|i| (i as f32).to_le_bytes()).collect();
let view = TensorView::new(Dtype::F32, vec![4], &f32_bytes).unwrap();
let bytes =
safetensors::tensor::serialize(vec![("a.weight".to_string(), &view)], None).unwrap();
fs::write(dir.path().join("model.safetensors"), bytes).unwrap();
let err = HfModelSource::open(dir.path()).expect_err("must error");
match err {
SourceError::Io(_) => {}
other => panic!("expected Io error for missing config.json, got {other:?}"),
}
}
#[test]
fn iter_tensors_does_not_buffer_prior_tensors() {
let dir = tempfile::tempdir().unwrap();
let mut entries: Vec<(String, Vec<usize>, Vec<u8>)> = Vec::new();
for i in 0..10 {
let n = 128usize;
let bytes: Vec<u8> = (0..n)
.flat_map(|j| ((i * 100 + j) as f32).to_le_bytes())
.collect();
entries.push((format!("t{i}.weight"), vec![n], bytes));
}
let views: Vec<(String, TensorView<'_>)> = entries
.iter()
.map(|(n, sh, b)| {
(
n.clone(),
TensorView::new(Dtype::F32, sh.clone(), b).unwrap(),
)
})
.collect();
let view_refs: Vec<(String, &TensorView<'_>)> =
views.iter().map(|(n, v)| (n.clone(), v)).collect();
let bytes = safetensors::tensor::serialize(view_refs, None).unwrap();
fs::write(dir.path().join("model.safetensors"), bytes).unwrap();
fs::write(dir.path().join("config.json"), "{}").unwrap();
let src = HfModelSource::open(dir.path()).expect("open");
let mut iter = src.iter_tensors();
let first = iter.next().unwrap().unwrap();
assert_eq!(first.name, "t0.weight");
let second = iter.next().unwrap().unwrap();
assert_eq!(second.name, "t1.weight");
assert!(std::mem::size_of::<TensorStream<'_>>() <= 64);
}
fn deepseek_config() -> serde_json::Value {
serde_json::json!({
"model_type": "deepseek_v4",
"quantization_config": {"quant_method": "fp8", "weight_block": [128, 128]}
})
}
#[test]
fn deepseek_official_dense_expert_and_hash_formats_decode_in_process() {
let dir = tempfile::tempdir().unwrap();
let hash: Vec<u8> = [0_i64, 3, 255]
.into_iter()
.flat_map(i64::to_le_bytes)
.collect();
write_minimal_single_file(
dir.path(),
&[
(
"layers.3.attn.wq_a.weight",
Dtype::F8_E4M3,
vec![1, 128],
vec![0x38; 128],
),
("layers.3.attn.wq_a.scale", Dtype::U8, vec![1, 1], vec![128]),
(
"layers.3.ffn.experts.0.w1.weight",
Dtype::I8,
vec![1, 16],
vec![0x21; 16],
),
(
"layers.3.ffn.experts.0.w1.scale",
Dtype::F8_E8M0,
vec![1, 1],
vec![127],
),
("layers.0.ffn.gate.tid2eid", Dtype::I64, vec![1, 3], hash),
("mtp.0.enorm.weight", Dtype::F16, vec![1], vec![0, 0]),
],
&deepseek_config(),
);
let src = HfModelSource::open(dir.path()).unwrap();
assert_eq!(src.tensor_count(), 3);
assert_eq!(src.excluded_mtp_tensor_count(), 1);
let expert_meta = src
.tensor_metas()
.find(|m| m.name.ends_with("w1.weight"))
.unwrap();
assert_eq!(expert_meta.shape, vec![1, 32]);
assert_eq!(
src.materialize_tensor("layers.3.attn.wq_a.weight")
.unwrap()
.data,
vec![2.0; 128]
);
let expert = src
.materialize_tensor("layers.3.ffn.experts.0.w1.weight")
.unwrap();
assert_eq!(&expert.data[..4], &[0.5, 1.0, 0.5, 1.0]);
assert_eq!(
src.materialize_tensor("layers.0.ffn.gate.tid2eid")
.unwrap()
.data,
vec![0.0, 3.0, 255.0]
);
}
#[test]
fn deepseek_rejects_malformed_scale_dtype_group_and_weight_dtype() {
let dir = tempfile::tempdir().unwrap();
write_minimal_single_file(
dir.path(),
&[
(
"layers.0.ffn.experts.0.w1.weight",
Dtype::U8,
vec![1, 16],
vec![0; 16],
),
(
"layers.0.ffn.experts.0.w1.scale",
Dtype::F32,
vec![1, 1],
1_f32.to_le_bytes().to_vec(),
),
],
&deepseek_config(),
);
let src = HfModelSource::open(dir.path()).unwrap();
assert!(matches!(
src.materialize_tensor("layers.0.ffn.experts.0.w1.weight"),
Err(SourceError::InvalidFp8Config(_))
));
let dir = tempfile::tempdir().unwrap();
write_minimal_single_file(
dir.path(),
&[
(
"layers.0.ffn.experts.0.w1.weight",
Dtype::I8,
vec![1, 16],
vec![0; 16],
),
(
"layers.0.ffn.experts.0.w1.scale",
Dtype::F8_E8M0,
vec![2, 1],
vec![127; 2],
),
],
&deepseek_config(),
);
let src = HfModelSource::open(dir.path()).unwrap();
assert!(matches!(
src.materialize_tensor("layers.0.ffn.experts.0.w1.weight"),
Err(SourceError::Mxfp4Dequant {
error: mxfp4::Mxfp4Error::ScaleShapeMismatch { .. },
..
})
));
let dir = tempfile::tempdir().unwrap();
write_minimal_single_file(
dir.path(),
&[(
"layers.0.attn.wq_a.weight",
Dtype::U8,
vec![1, 16],
vec![0; 16],
)],
&deepseek_config(),
);
assert!(matches!(
HfModelSource::open(dir.path()),
Err(SourceError::UnsupportedSourceDtype { .. })
));
}
}