entrenar/ecosystem/realizar/
exporter.rs1use super::error::GgufExportError;
4use super::metadata::{GeneralMetadata, GgufMetadata};
5use super::provenance::ExperimentProvenance;
6use super::quantization::QuantizationType;
7use std::path::Path;
8
9#[derive(Debug, Clone)]
11pub struct GgufExporter {
12 quantization: QuantizationType,
14 metadata: GgufMetadata,
16 validate: bool,
18 threads: usize,
20}
21
22impl Default for GgufExporter {
23 fn default() -> Self {
24 Self::new(QuantizationType::Q4KM)
25 }
26}
27
28impl GgufExporter {
29 pub fn new(quantization: QuantizationType) -> Self {
31 Self {
32 quantization,
33 metadata: GgufMetadata::default(),
34 validate: true,
35 threads: num_cpus(),
36 }
37 }
38
39 pub fn with_metadata(mut self, metadata: GgufMetadata) -> Self {
41 self.metadata = metadata;
42 self
43 }
44
45 pub fn with_general(mut self, general: GeneralMetadata) -> Self {
47 self.metadata.general = general;
48 self
49 }
50
51 pub fn with_provenance(mut self, provenance: ExperimentProvenance) -> Self {
53 self.metadata.provenance = Some(provenance);
54 self
55 }
56
57 pub fn without_validation(mut self) -> Self {
59 self.validate = false;
60 self
61 }
62
63 pub fn with_threads(mut self, threads: usize) -> Self {
65 self.threads = threads.max(1);
66 self
67 }
68
69 pub fn quantization(&self) -> QuantizationType {
71 self.quantization
72 }
73
74 pub fn metadata(&self) -> &GgufMetadata {
76 &self.metadata
77 }
78
79 pub fn export(
84 &self,
85 _input_path: impl AsRef<Path>,
86 output_path: impl AsRef<Path>,
87 ) -> Result<GgufExportResult, GgufExportError> {
88 let output = output_path.as_ref();
89
90 if let Some(parent) = output.parent() {
92 if !parent.exists() {
93 return Err(GgufExportError::IoError(format!(
94 "Output directory does not exist: {}",
95 parent.display()
96 )));
97 }
98 }
99
100 Ok(GgufExportResult {
108 output_path: output.to_path_buf(),
109 quantization: self.quantization,
110 metadata_keys: self
111 .metadata
112 .provenance
113 .as_ref()
114 .map_or(0, |p| p.to_metadata_pairs().len())
115 + self.metadata.custom.len(),
116 estimated_size_bytes: 0, })
118 }
119
120 pub fn collect_metadata(&self) -> Vec<(String, String)> {
122 let mut pairs = Vec::new();
123
124 pairs
126 .push(("general.architecture".to_string(), self.metadata.general.architecture.clone()));
127 pairs.push(("general.name".to_string(), self.metadata.general.name.clone()));
128
129 if let Some(ref author) = self.metadata.general.author {
130 pairs.push(("general.author".to_string(), author.clone()));
131 }
132 if let Some(ref desc) = self.metadata.general.description {
133 pairs.push(("general.description".to_string(), desc.clone()));
134 }
135 if let Some(ref license) = self.metadata.general.license {
136 pairs.push(("general.license".to_string(), license.clone()));
137 }
138 if let Some(ref url) = self.metadata.general.url {
139 pairs.push(("general.url".to_string(), url.clone()));
140 }
141
142 pairs.push(("general.file_type".to_string(), self.quantization.as_str().to_string()));
143
144 if let Some(ref prov) = self.metadata.provenance {
146 pairs.extend(prov.to_metadata_pairs());
147 }
148
149 for (key, value) in &self.metadata.custom {
151 pairs.push((format!("custom.{key}"), value.clone()));
152 }
153
154 pairs
155 }
156}
157
158#[derive(Debug, Clone)]
160pub struct GgufExportResult {
161 pub output_path: std::path::PathBuf,
163 pub quantization: QuantizationType,
165 pub metadata_keys: usize,
167 pub estimated_size_bytes: u64,
169}
170
171fn num_cpus() -> usize {
173 std::thread::available_parallelism().map(std::num::NonZero::get).unwrap_or(4)
174}