use super::error::GgufExportError;
use super::metadata::{GeneralMetadata, GgufMetadata};
use super::provenance::ExperimentProvenance;
use super::quantization::QuantizationType;
use std::path::Path;
#[derive(Debug, Clone)]
pub struct GgufExporter {
quantization: QuantizationType,
metadata: GgufMetadata,
validate: bool,
threads: usize,
}
impl Default for GgufExporter {
fn default() -> Self {
Self::new(QuantizationType::Q4KM)
}
}
impl GgufExporter {
pub fn new(quantization: QuantizationType) -> Self {
Self {
quantization,
metadata: GgufMetadata::default(),
validate: true,
threads: num_cpus(),
}
}
pub fn with_metadata(mut self, metadata: GgufMetadata) -> Self {
self.metadata = metadata;
self
}
pub fn with_general(mut self, general: GeneralMetadata) -> Self {
self.metadata.general = general;
self
}
pub fn with_provenance(mut self, provenance: ExperimentProvenance) -> Self {
self.metadata.provenance = Some(provenance);
self
}
pub fn without_validation(mut self) -> Self {
self.validate = false;
self
}
pub fn with_threads(mut self, threads: usize) -> Self {
self.threads = threads.max(1);
self
}
pub fn quantization(&self) -> QuantizationType {
self.quantization
}
pub fn metadata(&self) -> &GgufMetadata {
&self.metadata
}
pub fn export(
&self,
_input_path: impl AsRef<Path>,
output_path: impl AsRef<Path>,
) -> Result<GgufExportResult, GgufExportError> {
let output = output_path.as_ref();
if let Some(parent) = output.parent() {
if !parent.exists() {
return Err(GgufExportError::IoError(format!(
"Output directory does not exist: {}",
parent.display()
)));
}
}
Ok(GgufExportResult {
output_path: output.to_path_buf(),
quantization: self.quantization,
metadata_keys: self
.metadata
.provenance
.as_ref()
.map_or(0, |p| p.to_metadata_pairs().len())
+ self.metadata.custom.len(),
estimated_size_bytes: 0, })
}
pub fn collect_metadata(&self) -> Vec<(String, String)> {
let mut pairs = Vec::new();
pairs
.push(("general.architecture".to_string(), self.metadata.general.architecture.clone()));
pairs.push(("general.name".to_string(), self.metadata.general.name.clone()));
if let Some(ref author) = self.metadata.general.author {
pairs.push(("general.author".to_string(), author.clone()));
}
if let Some(ref desc) = self.metadata.general.description {
pairs.push(("general.description".to_string(), desc.clone()));
}
if let Some(ref license) = self.metadata.general.license {
pairs.push(("general.license".to_string(), license.clone()));
}
if let Some(ref url) = self.metadata.general.url {
pairs.push(("general.url".to_string(), url.clone()));
}
pairs.push(("general.file_type".to_string(), self.quantization.as_str().to_string()));
if let Some(ref prov) = self.metadata.provenance {
pairs.extend(prov.to_metadata_pairs());
}
for (key, value) in &self.metadata.custom {
pairs.push((format!("custom.{key}"), value.clone()));
}
pairs
}
}
#[derive(Debug, Clone)]
pub struct GgufExportResult {
pub output_path: std::path::PathBuf,
pub quantization: QuantizationType,
pub metadata_keys: usize,
pub estimated_size_bytes: u64,
}
fn num_cpus() -> usize {
std::thread::available_parallelism().map(std::num::NonZero::get).unwrap_or(4)
}