use std::collections::HashMap;
use anyhow::Context;
use candle_core::{
quantized::gguf_file::{self, Value},
Result,
};
use tracing::info;
use super::GGUFArchitecture;
pub struct Content {
_contents: Vec<gguf_file::Content>,
arch: GGUFArchitecture,
all_metadata: HashMap<String, Value>,
}
impl Content {
pub fn from_readers<R: std::io::Seek + std::io::Read>(readers: &mut [&mut R]) -> Result<Self> {
let mut contents = Vec::new();
let n_readers = readers.len();
for reader in readers.iter_mut() {
contents.push(gguf_file::Content::read(reader)?);
}
let n_splits = contents
.iter()
.filter_map(|ct| {
ct.metadata
.get("split.count")
.map(|val| val.to_u64().unwrap())
})
.fold(Vec::new(), |mut accum, x| {
if !accum.contains(&x) {
accum.push(x);
}
accum
});
if n_splits.len() > 1 {
candle_core::bail!("GGUF files have differing `split.count` values: {n_splits:?}. Perhaps the GGUF files do not match?");
}
#[allow(clippy::cast_possible_truncation)]
if !n_splits.is_empty() && n_readers != n_splits[0] as usize {
candle_core::bail!(
"Number of GGUF files does not match the number of splits, expected {} files.",
n_splits[0]
);
} else if n_splits.len() == 1 {
info!("GGUF file has been split into {} shards", n_splits[0]);
}
let mut arch = None;
for ct in &contents {
if !ct.metadata.contains_key("general.architecture") {
continue;
}
arch = Some(
ct.metadata["general.architecture"]
.to_string()
.context("Model metadata should have declared an architecture")
.and_then(GGUFArchitecture::from_value)
.unwrap(),
);
}
let arch = arch.expect("GGUF files must specify `general.architecture`");
let mut all_metadata = HashMap::new();
for content in &contents {
all_metadata.extend(content.metadata.clone())
}
Ok(Self {
_contents: contents,
arch,
all_metadata,
})
}
pub fn arch(&self) -> GGUFArchitecture {
self.arch
}
pub fn get_metadata(&self) -> &HashMap<String, Value> {
&self.all_metadata
}
}