1use std::collections::BTreeMap;
21use std::fmt;
22
23use crate::safetensors::{Dtype, SafetensorsIndex};
24
25#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct ExpectedTensor {
28 pub name: String,
30 pub shape: Vec<usize>,
32 pub dtype: Dtype,
34}
35
36impl ExpectedTensor {
37 #[must_use]
39 pub fn new(name: impl Into<String>, shape: impl Into<Vec<usize>>, dtype: Dtype) -> Self {
40 Self {
41 name: name.into(),
42 shape: shape.into(),
43 dtype,
44 }
45 }
46}
47
48#[derive(Clone, Debug, PartialEq, Eq)]
50pub enum Finding {
51 Missing {
53 name: String,
55 expected_shape: Vec<usize>,
57 expected_dtype: Dtype,
59 },
60 ShapeMismatch {
62 name: String,
64 expected: Vec<usize>,
66 actual: Vec<usize>,
68 },
69 DtypeMismatch {
71 name: String,
73 expected: Dtype,
75 actual: Dtype,
77 },
78 Extra {
80 name: String,
82 shape: Vec<usize>,
84 dtype: Dtype,
86 },
87}
88
89impl Finding {
90 #[must_use]
92 pub fn name(&self) -> &str {
93 match self {
94 Self::Missing { name, .. }
95 | Self::ShapeMismatch { name, .. }
96 | Self::DtypeMismatch { name, .. }
97 | Self::Extra { name, .. } => name,
98 }
99 }
100
101 #[must_use]
103 pub const fn class(&self) -> &'static str {
104 match self {
105 Self::Missing { .. } => "MISSING",
106 Self::ShapeMismatch { .. } => "SHAPE-MISMATCH",
107 Self::DtypeMismatch { .. } => "DTYPE-MISMATCH",
108 Self::Extra { .. } => "EXTRA",
109 }
110 }
111}
112
113impl fmt::Display for Finding {
114 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115 match self {
116 Self::Missing {
117 name,
118 expected_shape,
119 expected_dtype,
120 } => write!(
121 f,
122 "MISSING {name}: manifest requires {expected_shape:?} {expected_dtype}, \
123 checkpoint has no such tensor"
124 ),
125 Self::ShapeMismatch {
126 name,
127 expected,
128 actual,
129 } => write!(
130 f,
131 "SHAPE-MISMATCH {name}: manifest requires {expected:?}, checkpoint has {actual:?}"
132 ),
133 Self::DtypeMismatch {
134 name,
135 expected,
136 actual,
137 } => write!(
138 f,
139 "DTYPE-MISMATCH {name}: manifest requires {expected}, checkpoint has {actual}"
140 ),
141 Self::Extra { name, shape, dtype } => write!(
142 f,
143 "EXTRA {name}: checkpoint has {shape:?} {dtype}, manifest does not \
144 mention it"
145 ),
146 }
147 }
148}
149
150#[derive(Clone, Debug, Default)]
152pub struct WeightsManifest {
153 label: String,
154 expected: BTreeMap<String, ExpectedTensor>,
155}
156
157impl WeightsManifest {
158 #[must_use]
160 pub fn new(label: impl Into<String>) -> Self {
161 Self {
162 label: label.into(),
163 expected: BTreeMap::new(),
164 }
165 }
166
167 #[must_use]
169 pub fn from_expectations(
170 label: impl Into<String>,
171 expectations: impl IntoIterator<Item = ExpectedTensor>,
172 ) -> Self {
173 let mut manifest = Self::new(label);
174 for expectation in expectations {
175 manifest.expect(expectation);
176 }
177 manifest
178 }
179
180 pub fn expect(&mut self, tensor: ExpectedTensor) -> &mut Self {
182 self.expected.insert(tensor.name.clone(), tensor);
183 self
184 }
185
186 #[must_use]
188 pub fn label(&self) -> &str {
189 &self.label
190 }
191
192 #[must_use]
194 pub fn len(&self) -> usize {
195 self.expected.len()
196 }
197
198 #[must_use]
200 pub fn is_empty(&self) -> bool {
201 self.expected.is_empty()
202 }
203
204 #[must_use]
209 pub fn audit(&self, index: &SafetensorsIndex) -> CensusReport {
210 let mut findings = Vec::new();
211
212 for (name, expectation) in &self.expected {
213 match index.entry(name) {
214 None => findings.push(Finding::Missing {
215 name: name.clone(),
216 expected_shape: expectation.shape.clone(),
217 expected_dtype: expectation.dtype,
218 }),
219 Some(actual) => {
220 if actual.shape != expectation.shape {
221 findings.push(Finding::ShapeMismatch {
222 name: name.clone(),
223 expected: expectation.shape.clone(),
224 actual: actual.shape.clone(),
225 });
226 }
227 if actual.dtype != expectation.dtype {
230 findings.push(Finding::DtypeMismatch {
231 name: name.clone(),
232 expected: expectation.dtype,
233 actual: actual.dtype,
234 });
235 }
236 }
237 }
238 }
239
240 for entry in index.entries() {
241 if !self.expected.contains_key(&entry.name) {
242 findings.push(Finding::Extra {
243 name: entry.name.clone(),
244 shape: entry.shape.clone(),
245 dtype: entry.dtype,
246 });
247 }
248 }
249
250 CensusReport {
251 label: self.label.clone(),
252 expected_count: self.expected.len(),
253 actual_count: index.len(),
254 findings,
255 }
256 }
257
258 pub fn verify(&self, index: &SafetensorsIndex) -> Result<(), Box<CensusReport>> {
265 let report = self.audit(index);
266 if report.is_green() {
267 Ok(())
268 } else {
269 Err(Box::new(report))
270 }
271 }
272}
273
274#[derive(Clone, Debug)]
276pub struct CensusReport {
277 label: String,
278 expected_count: usize,
279 actual_count: usize,
280 findings: Vec<Finding>,
281}
282
283impl CensusReport {
284 #[must_use]
286 pub fn is_green(&self) -> bool {
287 self.findings.is_empty()
288 }
289
290 #[must_use]
292 pub fn findings(&self) -> &[Finding] {
293 &self.findings
294 }
295
296 #[must_use]
298 pub fn count_of(&self, class: &str) -> usize {
299 self.findings
300 .iter()
301 .filter(|finding| finding.class() == class)
302 .count()
303 }
304
305 #[must_use]
307 pub fn label(&self) -> &str {
308 &self.label
309 }
310
311 #[must_use]
313 pub fn render(&self) -> String {
314 use fmt::Write as _;
315
316 if self.is_green() {
317 return format!(
318 "weights census `{}`: GREEN ({} tensors matched)",
319 self.label, self.expected_count
320 );
321 }
322
323 let mut out = format!(
324 "weights census `{}`: REFUSED — {} divergence(s)\n manifest expects {} tensor(s); \
325 checkpoint declares {}\n MISSING {} · SHAPE-MISMATCH {} · DTYPE-MISMATCH {} · EXTRA \
326 {}\n",
327 self.label,
328 self.findings.len(),
329 self.expected_count,
330 self.actual_count,
331 self.count_of("MISSING"),
332 self.count_of("SHAPE-MISMATCH"),
333 self.count_of("DTYPE-MISMATCH"),
334 self.count_of("EXTRA"),
335 );
336 for finding in &self.findings {
337 let _ = writeln!(out, " {finding}");
339 }
340 out.push_str(
341 " this is a wrong or stale checkpoint — refusing to load rather than synthesize \
342 garbage",
343 );
344 out
345 }
346}
347
348impl fmt::Display for CensusReport {
349 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
350 f.write_str(&self.render())
351 }
352}
353
354impl std::error::Error for CensusReport {}
355
356#[cfg(test)]
357mod tests {
358 use super::*;
359 use crate::safetensors::SafetensorsIndex;
360 use serde_json::{Value, json};
361
362 fn checkpoint(parts: &[(&str, Dtype, &[usize])]) -> Vec<u8> {
363 let mut directory = serde_json::Map::new();
364 let mut offset = 0usize;
365 for (name, dtype, shape) in parts {
366 let elements: usize = shape.iter().product();
367 let bytes = elements * dtype.size();
368 directory.insert(
369 (*name).to_owned(),
370 json!({
371 "dtype": dtype.as_str(),
372 "shape": shape,
373 "data_offsets": [offset, offset + bytes],
374 }),
375 );
376 offset += bytes;
377 }
378 let header = serde_json::to_vec(&Value::Object(directory)).expect("serializes");
379 let mut out = (header.len() as u64).to_le_bytes().to_vec();
380 out.extend_from_slice(&header);
381 out.extend_from_slice(&vec![0u8; offset]);
382 out
383 }
384
385 fn manifest(parts: &[(&str, &[usize], Dtype)]) -> WeightsManifest {
386 WeightsManifest::from_expectations(
387 "test",
388 parts
389 .iter()
390 .map(|(name, shape, dtype)| ExpectedTensor::new(*name, shape.to_vec(), *dtype)),
391 )
392 }
393
394 #[test]
395 fn matching_checkpoint_is_green() {
396 let buffer = checkpoint(&[("a", Dtype::Bf16, &[2, 2]), ("b", Dtype::F32, &[4])]);
397 let index = SafetensorsIndex::parse(&buffer).expect("parses");
398 let manifest = manifest(&[("a", &[2, 2], Dtype::Bf16), ("b", &[4], Dtype::F32)]);
399
400 let report = manifest.audit(&index);
401 assert!(report.is_green(), "{}", report.render());
402 assert!(manifest.verify(&index).is_ok());
403 assert!(report.render().contains("GREEN"));
404 }
405
406 #[test]
407 fn missing_tensor_is_named_and_refused() {
408 let buffer = checkpoint(&[("a", Dtype::Bf16, &[2, 2])]);
409 let index = SafetensorsIndex::parse(&buffer).expect("parses");
410 let manifest = manifest(&[("a", &[2, 2], Dtype::Bf16), ("b", &[4], Dtype::F32)]);
411
412 let report = manifest.verify(&index).expect_err("must refuse");
413 assert_eq!(report.count_of("MISSING"), 1);
414 let rendered = report.render();
415 assert!(rendered.contains("MISSING"));
416 assert!(rendered.contains('b'));
417 assert!(rendered.contains("REFUSED"));
418 }
419
420 #[test]
421 fn shape_mismatch_is_named_and_refused() {
422 let buffer = checkpoint(&[("w", Dtype::Bf16, &[2, 4])]);
423 let index = SafetensorsIndex::parse(&buffer).expect("parses");
424 let manifest = manifest(&[("w", &[2, 2], Dtype::Bf16)]);
425
426 let report = manifest.verify(&index).expect_err("must refuse");
427 assert_eq!(report.count_of("SHAPE-MISMATCH"), 1);
428 let rendered = report.render();
429 assert!(rendered.contains("[2, 2]"), "{rendered}");
430 assert!(rendered.contains("[2, 4]"), "{rendered}");
431 }
432
433 #[test]
434 fn dtype_mismatch_is_reported_independently_of_shape() {
435 let buffer = checkpoint(&[("w", Dtype::F32, &[2, 2])]);
437 let index = SafetensorsIndex::parse(&buffer).expect("parses");
438 let manifest = manifest(&[("w", &[2, 2], Dtype::Bf16)]);
439
440 let report = manifest.verify(&index).expect_err("must refuse");
441 assert_eq!(report.count_of("DTYPE-MISMATCH"), 1);
442 assert_eq!(report.count_of("SHAPE-MISMATCH"), 0);
443 }
444
445 #[test]
446 fn wrong_shape_and_dtype_both_report() {
447 let buffer = checkpoint(&[("w", Dtype::F32, &[8])]);
448 let index = SafetensorsIndex::parse(&buffer).expect("parses");
449 let manifest = manifest(&[("w", &[2, 2], Dtype::Bf16)]);
450
451 let report = manifest.verify(&index).expect_err("must refuse");
452 assert_eq!(report.count_of("SHAPE-MISMATCH"), 1);
453 assert_eq!(report.count_of("DTYPE-MISMATCH"), 1);
454 }
455
456 #[test]
457 fn extra_tensor_is_reported() {
458 let buffer = checkpoint(&[("a", Dtype::Bf16, &[2, 2]), ("surprise", Dtype::F32, &[1])]);
460 let index = SafetensorsIndex::parse(&buffer).expect("parses");
461 let manifest = manifest(&[("a", &[2, 2], Dtype::Bf16)]);
462
463 let report = manifest.verify(&index).expect_err("must refuse");
464 assert_eq!(report.count_of("EXTRA"), 1);
465 assert!(report.render().contains("surprise"));
466 }
467
468 #[test]
469 fn every_divergence_is_listed_not_just_the_first() {
470 let buffer = checkpoint(&[
471 ("keep", Dtype::Bf16, &[2, 2]),
472 ("wrong_shape", Dtype::Bf16, &[9]),
473 ("wrong_dtype", Dtype::F32, &[2]),
474 ("unexpected", Dtype::F32, &[1]),
475 ]);
476 let index = SafetensorsIndex::parse(&buffer).expect("parses");
477 let manifest = manifest(&[
478 ("keep", &[2, 2], Dtype::Bf16),
479 ("wrong_shape", &[4], Dtype::Bf16),
480 ("wrong_dtype", &[2], Dtype::Bf16),
481 ("absent", &[7], Dtype::Bf16),
482 ]);
483
484 let report = manifest.verify(&index).expect_err("must refuse");
485 assert_eq!(report.count_of("MISSING"), 1);
486 assert_eq!(report.count_of("SHAPE-MISMATCH"), 1);
487 assert_eq!(report.count_of("DTYPE-MISMATCH"), 1);
488 assert_eq!(report.count_of("EXTRA"), 1);
489 assert_eq!(report.findings().len(), 4);
490 }
491
492 #[test]
493 fn report_is_greppable_by_class() {
494 let buffer = checkpoint(&[("a", Dtype::Bf16, &[1])]);
495 let index = SafetensorsIndex::parse(&buffer).expect("parses");
496 let manifest = manifest(&[("b", &[1], Dtype::Bf16)]);
497 let report = manifest.audit(&index);
498
499 let classes: Vec<_> = report.findings().iter().map(Finding::class).collect();
500 assert!(classes.contains(&"MISSING"));
501 assert!(classes.contains(&"EXTRA"));
502 assert_eq!(report.findings()[0].name(), "b");
503 }
504}