ferrum_quantization/gguf/inventory/
metadata.rs1use std::collections::BTreeMap;
4use std::io::{Read, Seek};
5
6use candle_core::{Error, Result};
7
8use super::super::GgufHadamard;
9use super::header::{base_model_repository_index, Header};
10
11#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct GgufModelMetadata {
15 pub architecture: String,
16 pub source_repository_url: Option<String>,
17 pub base_model_count: Option<u64>,
18 pub base_model_repository_urls: BTreeMap<u64, String>,
20 pub hadamard: Option<GgufHadamard>,
22}
23
24impl GgufModelMetadata {
25 pub fn read<R: Read + Seek>(reader: &mut R) -> Result<Self> {
26 let header = Header::read(reader)?;
27 let string = |key: &str| {
28 header
29 .metadata
30 .get(key)
31 .map(|v| v.to_string().cloned())
32 .transpose()
33 };
34 let architecture = string("general.architecture")?
35 .filter(|value| !value.is_empty())
36 .ok_or_else(|| Error::Msg("GGUF requires nonempty general.architecture".into()))?;
37 let base_model_count = header
38 .metadata
39 .get("general.base_model.count")
40 .map(super::metadata_integer)
41 .transpose()?;
42 let mut base_model_repository_urls = BTreeMap::new();
43 for (key, value) in &header.metadata {
44 if let Some(index) = base_model_repository_index(key) {
45 if base_model_count.is_none_or(|count| index >= count) {
46 return Err(Error::Msg(format!(
47 "GGUF {key} is outside the declared general.base_model.count"
48 )));
49 }
50 base_model_repository_urls.insert(index, value.to_string()?.clone());
51 }
52 }
53 let hadamard = GgufHadamard::parse(
54 &header.metadata,
55 &architecture,
56 header
57 .tensors
58 .iter()
59 .map(|tensor| (tensor.name.as_str(), tensor.dimensions.as_slice())),
60 )?;
61 Ok(Self {
62 architecture,
63 source_repository_url: string("general.source.repo_url")?,
64 base_model_count,
65 base_model_repository_urls,
66 hadamard,
67 })
68 }
69}