Download engine/crates/core/src/loader.rs from respanai/span-02: direct link, hf CLI and curl.
- Browser
- Download file 7.88 kB
-
https://huggingface.co/respanai/span-02/resolve/main/engine/crates/core/src/loader.rs
- Command line
-
hf download hf://respanai/span-02/engine/crates/core/src/loader.rs
-
curl -L -o loader.rs https://huggingface.co/respanai/span-02/resolve/main/engine/crates/core/src/loader.rs
7.88 kB
| //! safetensors loader: mmap, multi-shard (`model.safetensors.index.json`), bf16/f16/f32 -> f32, raw access for packed (NVFP4 / FP8) tensors. | |
| //! Own ~100-line header parser instead of the `safetensors` crate so that raw byte slices of u8/fp8 tensors can be handed straight to CUDA upload. | |
| use crate::tensor::Tensor; | |
| use anyhow::{anyhow, bail, Context, Result}; | |
| use half::{bf16, f16}; | |
| use memmap2::Mmap; | |
| use serde_json::Value; | |
| use std::collections::BTreeMap; | |
| use std::fs::File; | |
| use std::path::{Path, PathBuf}; | |
| pub enum DType { | |
| F32, | |
| F16, | |
| BF16, | |
| F8E4M3, | |
| F8E5M2, | |
| U8, | |
| I8, | |
| I32, | |
| I64, | |
| Bool, | |
| } | |
| impl DType { | |
| fn parse(s: &str) -> Result<DType> { | |
| Ok(match s { | |
| "F32" => DType::F32, | |
| "F16" => DType::F16, | |
| "BF16" => DType::BF16, | |
| "F8_E4M3" => DType::F8E4M3, | |
| "F8_E5M2" => DType::F8E5M2, | |
| "U8" => DType::U8, | |
| "I8" => DType::I8, | |
| "I32" => DType::I32, | |
| "I64" => DType::I64, | |
| "BOOL" => DType::Bool, | |
| o => bail!("unsupported safetensors dtype {o}"), | |
| }) | |
| } | |
| pub fn as_str(self) -> &'static str { | |
| match self { | |
| DType::F32 => "F32", | |
| DType::F16 => "F16", | |
| DType::BF16 => "BF16", | |
| DType::F8E4M3 => "F8_E4M3", | |
| DType::F8E5M2 => "F8_E5M2", | |
| DType::U8 => "U8", | |
| DType::I8 => "I8", | |
| DType::I32 => "I32", | |
| DType::I64 => "I64", | |
| DType::Bool => "BOOL", | |
| } | |
| } | |
| pub fn size(self) -> usize { | |
| match self { | |
| DType::F32 | DType::I32 => 4, | |
| DType::F16 | DType::BF16 => 2, | |
| DType::I64 => 8, | |
| _ => 1, | |
| } | |
| } | |
| } | |
| pub struct TensorInfo { | |
| pub dtype: DType, | |
| pub shape: Vec<usize>, | |
| file: usize, | |
| start: usize, | |
| end: usize, | |
| } | |
| pub struct WeightStore { | |
| maps: Vec<Mmap>, | |
| index: BTreeMap<String, TensorInfo>, | |
| } | |
| impl WeightStore { | |
| /// `path` = a single .safetensors file, or a directory holding `model.safetensors` / `model-0000x-of-0000y.safetensors` shards. | |
| pub fn open(path: impl AsRef<Path>) -> Result<Self> { | |
| let p = path.as_ref(); | |
| let files: Vec<PathBuf> = if p.is_file() { | |
| vec![p.to_path_buf()] | |
| } else { | |
| let mut v: Vec<PathBuf> = std::fs::read_dir(p) | |
| .with_context(|| format!("read_dir {p:?}"))? | |
| .filter_map(|e| e.ok().map(|e| e.path())) | |
| .filter(|f| f.extension().map_or(false, |x| x == "safetensors")) | |
| .collect(); | |
| v.sort(); | |
| v | |
| }; | |
| if files.is_empty() { | |
| bail!("no .safetensors in {p:?}"); | |
| } | |
| let mut s = WeightStore { maps: vec![], index: BTreeMap::new() }; | |
| for f in files { | |
| s.add_file(&f)?; | |
| } | |
| Ok(s) | |
| } | |
| fn add_file(&mut self, f: &Path) -> Result<()> { | |
| let file = File::open(f).with_context(|| format!("open {f:?}"))?; | |
| let mm = unsafe { Mmap::map(&file)? }; | |
| if mm.len() < 8 { | |
| bail!("{f:?}: too short"); | |
| } | |
| let n = u64::from_le_bytes(mm[..8].try_into().unwrap()) as usize; | |
| let hdr: Value = serde_json::from_slice(mm.get(8..8 + n).ok_or_else(|| anyhow!("{f:?}: bad header length"))?)?; | |
| let base = 8 + n; | |
| let fi = self.maps.len(); | |
| for (k, v) in hdr.as_object().ok_or_else(|| anyhow!("header not object"))? { | |
| if k == "__metadata__" { | |
| continue; | |
| } | |
| let dtype = DType::parse(v["dtype"].as_str().unwrap_or(""))?; | |
| let shape: Vec<usize> = v["shape"].as_array().ok_or_else(|| anyhow!("shape"))?.iter().map(|x| x.as_u64().unwrap() as usize).collect(); | |
| let off = v["data_offsets"].as_array().ok_or_else(|| anyhow!("offsets"))?; | |
| let (s, e) = (off[0].as_u64().unwrap() as usize, off[1].as_u64().unwrap() as usize); | |
| if base + e > mm.len() || e - s != shape.iter().product::<usize>() * dtype.size() { | |
| bail!("{f:?}: tensor {k} has inconsistent size"); | |
| } | |
| self.index.insert(k.clone(), TensorInfo { dtype, shape, file: fi, start: base + s, end: base + e }); | |
| } | |
| self.maps.push(mm); | |
| Ok(()) | |
| } | |
| pub fn names(&self) -> impl Iterator<Item = &String> { | |
| self.index.keys() | |
| } | |
| pub fn contains(&self, name: &str) -> bool { | |
| self.index.contains_key(name) | |
| } | |
| pub fn info(&self, name: &str) -> Result<&TensorInfo> { | |
| self.index.get(name).ok_or_else(|| anyhow!("tensor not found: {name}")) | |
| } | |
| pub fn raw(&self, name: &str) -> Result<&[u8]> { | |
| let i = self.info(name)?; | |
| Ok(&self.maps[i.file][i.start..i.end]) | |
| } | |
| /// any float dtype -> f32 tensor | |
| pub fn load_f32(&self, name: &str) -> Result<Tensor> { | |
| let i = self.info(name)?; | |
| let b = self.raw(name)?; | |
| let data: Vec<f32> = match i.dtype { | |
| DType::F32 => b.chunks_exact(4).map(|c| f32::from_le_bytes(c.try_into().unwrap())).collect(), | |
| DType::BF16 => b.chunks_exact(2).map(|c| bf16::from_le_bytes(c.try_into().unwrap()).to_f32()).collect(), | |
| DType::F16 => b.chunks_exact(2).map(|c| f16::from_le_bytes(c.try_into().unwrap()).to_f32()).collect(), | |
| DType::F8E4M3 => b.iter().map(|&x| crate::quant::fp8_e4m3_to_f32(x)).collect(), | |
| d => bail!("{name}: cannot convert {d:?} to f32 directly"), | |
| }; | |
| Ok(Tensor::new(i.shape.clone(), data)) | |
| } | |
| } | |
| mod tests { | |
| use super::*; | |
| use std::io::Write; | |
| fn write_st(path: &Path) { | |
| // 2 tensors: a: f32 [2,2], b: bf16 [3] | |
| let a: Vec<u8> = [1.0f32, 2.0, 3.0, 4.5].iter().flat_map(|x| x.to_le_bytes()).collect(); | |
| let b: Vec<u8> = [0.5f32, -1.0, 2.0].iter().flat_map(|x| bf16::from_f32(*x).to_le_bytes()).collect(); | |
| let hdr = format!( | |
| r#"{{"__metadata__":{{"format":"pt"}},"a":{{"dtype":"F32","shape":[2,2],"data_offsets":[0,16]}},"b":{{"dtype":"BF16","shape":[3],"data_offsets":[16,22]}}}}"# | |
| ); | |
| let mut f = File::create(path).unwrap(); | |
| f.write_all(&(hdr.len() as u64).to_le_bytes()).unwrap(); | |
| f.write_all(hdr.as_bytes()).unwrap(); | |
| f.write_all(&a).unwrap(); | |
| f.write_all(&b).unwrap(); | |
| } | |
| fn roundtrip_and_errors() { | |
| let dir = std::env::temp_dir().join(format!("dllm_loader_{}", std::process::id())); | |
| std::fs::create_dir_all(&dir).unwrap(); | |
| let p = dir.join("x.safetensors"); | |
| write_st(&p); | |
| let s = WeightStore::open(&dir).unwrap(); | |
| assert_eq!(s.names().collect::<Vec<_>>(), vec!["a", "b"]); | |
| let a = s.load_f32("a").unwrap(); | |
| assert_eq!((a.shape.clone(), a.data.clone()), (vec![2, 2], vec![1.0, 2.0, 3.0, 4.5])); | |
| assert_eq!(s.load_f32("b").unwrap().data, vec![0.5, -1.0, 2.0]); | |
| assert!(s.load_f32("nope").is_err()); | |
| // truncated file is rejected | |
| let bad = dir.join("bad.safetensors"); | |
| std::fs::write(&bad, &std::fs::read(&p).unwrap()[..20]).unwrap(); | |
| assert!(WeightStore::open(&bad).is_err()); | |
| std::fs::remove_dir_all(&dir).ok(); | |
| } | |
| fn real_tiny_fixture_names() { | |
| let p = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../../ref/tiny/model.safetensors"); | |
| if !p.exists() { | |
| return; | |
| } | |
| let s = WeightStore::open(&p).unwrap(); | |
| assert_eq!(s.info("model.language_model.layers.2.self_attn.k_proj.weight").unwrap().shape, vec![32, 64]); | |
| assert!(!s.contains("model.language_model.layers.2.self_attn.v_proj.weight")); // k_eq_v full layer | |
| assert!(s.contains("model.language_model.layers.0.self_attn.v_proj.weight")); | |
| } | |
| } | |