use std::collections::BTreeMap;
use std::io::{Read, Seek};
use candle_core::{Error, Result};
use super::super::GgufHadamard;
use super::header::{base_model_repository_index, Header};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GgufModelMetadata {
pub architecture: String,
pub source_repository_url: Option<String>,
pub base_model_count: Option<u64>,
pub base_model_repository_urls: BTreeMap<u64, String>,
pub hadamard: Option<GgufHadamard>,
}
impl GgufModelMetadata {
pub fn read<R: Read + Seek>(reader: &mut R) -> Result<Self> {
let header = Header::read(reader)?;
let string = |key: &str| {
header
.metadata
.get(key)
.map(|v| v.to_string().cloned())
.transpose()
};
let architecture = string("general.architecture")?
.filter(|value| !value.is_empty())
.ok_or_else(|| Error::Msg("GGUF requires nonempty general.architecture".into()))?;
let base_model_count = header
.metadata
.get("general.base_model.count")
.map(super::metadata_integer)
.transpose()?;
let mut base_model_repository_urls = BTreeMap::new();
for (key, value) in &header.metadata {
if let Some(index) = base_model_repository_index(key) {
if base_model_count.is_none_or(|count| index >= count) {
return Err(Error::Msg(format!(
"GGUF {key} is outside the declared general.base_model.count"
)));
}
base_model_repository_urls.insert(index, value.to_string()?.clone());
}
}
let hadamard = GgufHadamard::parse(
&header.metadata,
&architecture,
header
.tensors
.iter()
.map(|tensor| (tensor.name.as_str(), tensor.dimensions.as_slice())),
)?;
Ok(Self {
architecture,
source_repository_url: string("general.source.repo_url")?,
base_model_count,
base_model_repository_urls,
hadamard,
})
}
}