spark_runtime/weights/gguf/
config.rs1use std::path::Path;
12
13use anyhow::{Context, Result};
14use atlas_core::config::{GgufConfigInputs, GgufMeta, ModelConfig, config_from_gguf};
15
16use super::container::GgufFile;
17use super::find_gguf;
18
19impl GgufMeta for GgufFile {
20 fn get_u64(&self, key: &str) -> Option<u64> {
21 GgufFile::get_u64(self, key)
22 }
23 fn get_f64(&self, key: &str) -> Option<f64> {
24 GgufFile::get_f64(self, key)
25 }
26 fn get_str(&self, key: &str) -> Option<&str> {
27 GgufFile::get_str(self, key)
28 }
29 fn get_arr_len(&self, key: &str) -> Option<usize> {
30 GgufFile::arr_len(self, key)
31 }
32}
33
34pub fn config_from_gguf_dir(model_dir: &Path) -> Result<ModelConfig> {
40 let path = find_gguf(model_dir)
41 .with_context(|| format!("no .gguf file in {}", model_dir.display()))?;
42 let file =
43 std::fs::File::open(&path).with_context(|| format!("failed to open {}", path.display()))?;
44 let mmap = unsafe { memmap2::MmapOptions::new().map(&file)? };
47 let gguf = GgufFile::parse(&mmap)
48 .with_context(|| format!("failed to parse GGUF metadata: {}", path.display()))?;
49
50 let token_embd_vocab = gguf
51 .tensor("token_embd.weight")
52 .and_then(|t| t.dims.last().copied());
53 let has_output_weight = gguf.tensor("output.weight").is_some();
54
55 let inputs = GgufConfigInputs {
56 meta: &gguf,
57 token_embd_vocab,
58 has_output_weight,
59 };
60 config_from_gguf(&inputs).context("failed to build ModelConfig from GGUF metadata")
61}
62
63#[cfg(test)]
64mod tests {
65 use super::*;
66
67 #[test]
70 fn gguf_meta_bridge_forwards() {
71 let mut b: Vec<u8> = Vec::new();
73 let push_u32 = |b: &mut Vec<u8>, v: u32| b.extend_from_slice(&v.to_le_bytes());
74 let push_u64 = |b: &mut Vec<u8>, v: u64| b.extend_from_slice(&v.to_le_bytes());
75 let push_str = |b: &mut Vec<u8>, s: &str| {
76 b.extend_from_slice(&(s.len() as u64).to_le_bytes());
77 b.extend_from_slice(s.as_bytes());
78 };
79 push_u32(&mut b, 0x4655_4747); push_u32(&mut b, 3); push_u64(&mut b, 0); push_u64(&mut b, 2); push_str(&mut b, "qwen3.block_count");
85 push_u32(&mut b, 4); push_u32(&mut b, 28);
87 push_str(&mut b, "tokenizer.ggml.tokens");
89 push_u32(&mut b, 9); push_u32(&mut b, 8); push_u64(&mut b, 3); for s in ["a", "bb", "ccc"] {
93 push_str(&mut b, s);
94 }
95 while !b.len().is_multiple_of(32) {
98 b.push(0);
99 }
100 let gguf = GgufFile::parse(&b).unwrap();
101 let m: &dyn GgufMeta = &gguf;
102 assert_eq!(m.get_u64("qwen3.block_count"), Some(28));
103 assert_eq!(m.get_arr_len("tokenizer.ggml.tokens"), Some(3));
104 assert_eq!(m.get_str("nonexistent"), None);
105 }
106}