Skip to main content

ferrum_quantization/gguf/inventory/
metadata.rs

1//! Product source discovery independent of the tensor decoder's type support.
2
3use 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/// Declared provenance used to locate independent semantic/tokenizer sources.
12/// Reading this metadata does not validate or load the tensor payload.
13#[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    /// Sparse because repository URLs are optional for each declared parent.
19    pub base_model_repository_urls: BTreeMap<u64, String>,
20    /// Validated transform declarations, without reading tensor payloads.
21    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}