1use crate::StoredDtype;
4
5use std::collections::{BTreeMap, BTreeSet};
6
7use crate::schema::{
8 AlternativeLayoutGroup, CatalogPolicy, GgufCheckpointPlan, GgufTensorConstraint,
9 GgufTypeConstraint, SafetensorsCheckpointPlan, SafetensorsTensorConstraint,
10 StoredDtypeConstraint, TensorRequirement, TensorRole,
11};
12use eredu_gguf::{Checkpoint as GgufCheckpoint, GgmlType as GgufType};
13
14#[derive(Debug, Clone, Eq, PartialEq)]
16pub struct CatalogTensorMetadata {
17 pub shape: Vec<usize>,
19 pub stored_dtype: StoredDtype,
21}
22
23pub trait SafetensorsCatalog {
25 fn keys(&self) -> Vec<String>;
27 fn metadata(&self, key: &str) -> Result<CatalogTensorMetadata, String>;
29}
30
31impl SafetensorsCatalog for dyn crate::store::CheckpointSource + '_ {
32 fn keys(&self) -> Vec<String> {
33 self.source_keys()
34 }
35
36 fn metadata(&self, key: &str) -> Result<CatalogTensorMetadata, String> {
37 self.source_metadata(key)
38 .map(|metadata| CatalogTensorMetadata {
39 shape: metadata.logical_shape,
40 stored_dtype: metadata.stored_dtype,
41 })
42 .map_err(|error| error.to_string())
43 }
44}
45
46#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd)]
48pub enum CheckpointIssueKind {
49 MissingTensor,
51 UnexpectedTensor,
53 ConflictingLayout,
55 ShapeMismatch,
57 UnsupportedEncoding,
59 CompanionMismatch,
61 InvalidGeometry,
63 ValidationUnavailable,
65}
66
67#[derive(Debug, Clone, Eq, PartialEq)]
69pub struct CheckpointIssue {
70 pub kind: CheckpointIssueKind,
72 pub detail: String,
74 pub tensor_name: Option<String>,
76 pub tensor_type_code: Option<u32>,
78 pub metadata_key: Option<String>,
80}
81
82#[derive(Debug, Clone, Eq, PartialEq)]
84pub enum CheckpointValidation {
85 Exact,
87 Invalid(Vec<CheckpointIssue>),
89 Unverified(CheckpointIssue),
91}
92
93#[derive(Debug, Clone, Eq, PartialEq, thiserror::Error)]
95#[error("strict checkpoint validation failed")]
96pub struct StrictLoadFailure {
97 pub missing: Vec<String>,
99 pub unused: Vec<String>,
101}
102
103impl CheckpointValidation {
104 pub fn into_loader_result(self) -> Result<(), StrictLoadFailure> {
106 match self {
107 Self::Exact => Ok(()),
108 Self::Unverified(issue) => Err(StrictLoadFailure {
109 missing: Vec::new(),
110 unused: vec![issue.detail],
111 }),
112 Self::Invalid(issues) => {
113 let mut missing = issues
114 .iter()
115 .filter(|issue| issue.kind == CheckpointIssueKind::MissingTensor)
116 .filter_map(|issue| issue.tensor_name.clone())
117 .collect::<Vec<_>>();
118 let mut unused = issues
119 .into_iter()
120 .filter(|issue| issue.kind != CheckpointIssueKind::MissingTensor)
121 .map(|issue| {
122 if issue.kind == CheckpointIssueKind::UnexpectedTensor {
123 issue.tensor_name.unwrap_or(issue.detail)
124 } else {
125 issue.detail
126 }
127 })
128 .collect::<Vec<_>>();
129 missing.sort();
130 missing.dedup();
131 unused.sort();
132 Err(StrictLoadFailure { missing, unused })
133 }
134 }
135 }
136
137 pub fn from_issues(issues: Vec<CheckpointIssue>) -> Self {
139 if issues.is_empty() {
140 Self::Exact
141 } else {
142 Self::Invalid(issues)
143 }
144 }
145}
146
147pub fn missing(name: &str) -> CheckpointIssue {
149 CheckpointIssue {
150 kind: CheckpointIssueKind::MissingTensor,
151 detail: format!("checkpoint is missing required tensor {name:?}"),
152 tensor_name: Some(name.into()),
153 tensor_type_code: None,
154 metadata_key: None,
155 }
156}
157
158pub fn shape_mismatch(name: &str, expected: &[usize], actual: &[usize]) -> CheckpointIssue {
160 CheckpointIssue {
161 kind: CheckpointIssueKind::ShapeMismatch,
162 detail: format!("tensor {name:?} expected shape {expected:?}, got {actual:?}"),
163 tensor_name: Some(name.into()),
164 tensor_type_code: None,
165 metadata_key: None,
166 }
167}
168
169#[derive(Debug, Clone, Eq, PartialEq)]
175pub struct ResolvedCheckpointPlan {
176 identity: String,
177 source_keys: BTreeSet<String>,
178 unclaimed_keys: BTreeSet<String>,
179}
180
181impl ResolvedCheckpointPlan {
182 pub fn identity(&self) -> &str {
184 &self.identity
185 }
186
187 pub fn source_keys(&self) -> &BTreeSet<String> {
189 &self.source_keys
190 }
191
192 pub fn unclaimed_keys(&self) -> &BTreeSet<String> {
194 &self.unclaimed_keys
195 }
196
197 pub fn project_claimed_sources(
205 &self,
206 identity: impl Into<String>,
207 source_keys: BTreeSet<String>,
208 ) -> Result<Self, String> {
209 if let Some(source) = source_keys
210 .iter()
211 .find(|source| !self.source_keys.contains(*source))
212 {
213 return Err(format!(
214 "checkpoint projection introduced unadmitted source {source:?}"
215 ));
216 }
217 Ok(Self {
218 identity: identity.into(),
219 source_keys,
220 unclaimed_keys: self.unclaimed_keys.clone(),
221 })
222 }
223}
224
225#[derive(Debug, Clone, Eq, PartialEq)]
226struct PhysicalMetadata<E> {
227 shape: Vec<usize>,
228 encoding: E,
229}
230
231trait Constraint<E> {
232 fn key(&self) -> &str;
233 fn aliases(&self) -> &[String];
234 fn shape(&self) -> &[usize];
235 fn alternate_shapes(&self) -> &[Vec<usize>];
236 fn element_count(&self) -> Option<usize>;
237 fn requirement(&self) -> TensorRequirement;
238 fn role(&self) -> TensorRole;
239 fn accepts(&self, encoding: &E) -> bool;
240 fn encoding_detail(&self) -> String;
241 fn unsupported_detail(&self, identity: &str, actual: &E) -> String;
242 fn type_code(&self, _actual: &E) -> Option<u32> {
243 None
244 }
245}
246
247impl Constraint<StoredDtype> for SafetensorsTensorConstraint {
248 fn key(&self) -> &str {
249 &self.key
250 }
251 fn aliases(&self) -> &[String] {
252 &self.aliases
253 }
254 fn shape(&self) -> &[usize] {
255 &self.shape
256 }
257 fn alternate_shapes(&self) -> &[Vec<usize>] {
258 &self.alternate_shapes
259 }
260 fn element_count(&self) -> Option<usize> {
261 self.element_count
262 }
263 fn requirement(&self) -> TensorRequirement {
264 self.requirement
265 }
266 fn role(&self) -> TensorRole {
267 self.role
268 }
269 fn accepts(&self, encoding: &StoredDtype) -> bool {
270 self.dtype.accepts(encoding)
271 }
272 fn encoding_detail(&self) -> String {
273 match &self.dtype {
274 StoredDtypeConstraint::Exact(dtype) => format!("{dtype:?}"),
275 StoredDtypeConstraint::OneOf(dtypes) => format!("one of {dtypes:?}"),
276 StoredDtypeConstraint::Floating => "F16, BF16, or F32".into(),
277 }
278 }
279 fn unsupported_detail(&self, _identity: &str, actual: &StoredDtype) -> String {
280 format!(
281 "tensor {:?} uses unsupported SafeTensors dtype {actual:?}; expected {}",
282 self.key,
283 self.encoding_detail()
284 )
285 }
286}
287
288impl Constraint<GgufType> for GgufTensorConstraint {
289 fn key(&self) -> &str {
290 &self.key
291 }
292 fn aliases(&self) -> &[String] {
293 &self.aliases
294 }
295 fn shape(&self) -> &[usize] {
296 &self.shape
297 }
298 fn alternate_shapes(&self) -> &[Vec<usize>] {
299 &self.alternate_shapes
300 }
301 fn element_count(&self) -> Option<usize> {
302 self.element_count
303 }
304 fn requirement(&self) -> TensorRequirement {
305 self.requirement
306 }
307 fn role(&self) -> TensorRole {
308 self.role
309 }
310 fn accepts(&self, encoding: &GgufType) -> bool {
311 self.encoding.accepts(*encoding)
312 }
313 fn encoding_detail(&self) -> String {
314 let GgufTypeConstraint::OperationClass(operation) = &self.encoding;
315 format!("a {operation:?} operation")
316 }
317 fn unsupported_detail(&self, identity: &str, actual: &GgufType) -> String {
318 format!(
319 "GGUF tensor {:?} uses {actual:?} (type {}) for {}, which the {identity} loader does not support",
320 self.key,
321 actual.code(),
322 self.encoding_detail()
323 )
324 }
325 fn type_code(&self, actual: &GgufType) -> Option<u32> {
326 Some(actual.code())
327 }
328}
329
330pub fn validate_safetensors_plan(
332 store: &(impl SafetensorsCatalog + ?Sized),
333 plan: &SafetensorsCheckpointPlan,
334) -> CheckpointValidation {
335 let mut catalog = BTreeMap::new();
336 let mut metadata_issues = Vec::new();
337 for key in store.keys() {
338 match store.metadata(&key) {
339 Ok(CatalogTensorMetadata {
340 shape,
341 stored_dtype,
342 ..
343 }) => {
344 catalog.insert(
345 key,
346 PhysicalMetadata {
347 shape,
348 encoding: stored_dtype,
349 },
350 );
351 }
352 Err(error) => metadata_issues.push(metadata_failure(&key, error)),
353 }
354 }
355 let mut issues = validate_catalog(
356 &catalog,
357 &plan.identity,
358 &plan.common_tensors,
359 &plan.layout_groups,
360 &plan.catalog_policy,
361 );
362 metadata_issues.append(&mut issues);
363 CheckpointValidation::from_issues(metadata_issues)
364}
365
366pub fn resolve_safetensors_plan(
368 store: &(impl SafetensorsCatalog + ?Sized),
369 plan: &SafetensorsCheckpointPlan,
370) -> Result<ResolvedCheckpointPlan, CheckpointValidation> {
371 let mut catalog = BTreeMap::new();
372 let mut metadata_issues = Vec::new();
373 for key in store.keys() {
374 match store.metadata(&key) {
375 Ok(CatalogTensorMetadata {
376 shape,
377 stored_dtype,
378 ..
379 }) => {
380 catalog.insert(
381 key,
382 PhysicalMetadata {
383 shape,
384 encoding: stored_dtype,
385 },
386 );
387 }
388 Err(error) => metadata_issues.push(metadata_failure(&key, error)),
389 }
390 }
391 resolve_catalog(
392 &catalog,
393 &plan.identity,
394 &plan.common_tensors,
395 &plan.layout_groups,
396 &plan.catalog_policy,
397 metadata_issues,
398 )
399}
400
401pub fn validate_gguf_plan(
403 checkpoint: &GgufCheckpoint,
404 plan: &GgufCheckpointPlan,
405) -> CheckpointValidation {
406 let catalog = gguf_catalog(checkpoint);
407 CheckpointValidation::from_issues(validate_catalog(
408 &catalog,
409 &plan.identity,
410 &plan.common_tensors,
411 &plan.layout_groups,
412 &plan.catalog_policy,
413 ))
414}
415
416pub fn resolve_gguf_plan(
418 checkpoint: &GgufCheckpoint,
419 plan: &GgufCheckpointPlan,
420) -> Result<ResolvedCheckpointPlan, CheckpointValidation> {
421 resolve_catalog(
422 &gguf_catalog(checkpoint),
423 &plan.identity,
424 &plan.common_tensors,
425 &plan.layout_groups,
426 &plan.catalog_policy,
427 Vec::new(),
428 )
429}
430
431fn gguf_catalog(checkpoint: &GgufCheckpoint) -> BTreeMap<String, PhysicalMetadata<GgufType>> {
432 checkpoint
433 .tensors()
434 .map(|tensor| {
435 let descriptor = tensor.descriptor();
436 (
437 descriptor.name.clone(),
438 PhysicalMetadata {
439 shape: descriptor
440 .row_major_shape()
441 .into_iter()
442 .map(|dimension| usize::try_from(dimension).unwrap_or(usize::MAX))
443 .collect(),
444 encoding: descriptor.ggml_type,
445 },
446 )
447 })
448 .collect()
449}
450
451fn resolve_catalog<E, T>(
452 catalog: &BTreeMap<String, PhysicalMetadata<E>>,
453 identity: &str,
454 common: &[T],
455 groups: &[AlternativeLayoutGroup<T>],
456 policy: &CatalogPolicy,
457 mut issues: Vec<CheckpointIssue>,
458) -> Result<ResolvedCheckpointPlan, CheckpointValidation>
459where
460 E: std::fmt::Debug,
461 T: Constraint<E>,
462{
463 issues.extend(validate_catalog(catalog, identity, common, groups, policy));
464 if !issues.is_empty() {
465 return Err(CheckpointValidation::from_issues(issues));
466 }
467
468 let mut source_keys = BTreeSet::new();
469 let mut select_constraint = |constraint: &T| {
470 if let Some(key) = std::iter::once(constraint.key())
471 .chain(constraint.aliases().iter().map(String::as_str))
472 .find(|key| catalog.contains_key(*key))
473 {
474 source_keys.insert(key.to_string());
475 }
476 };
477 for constraint in common {
478 select_constraint(constraint);
479 }
480
481 for group in groups {
482 if let Some(variant) = group.variants.iter().find(|variant| {
483 variant
484 .discriminator_keys
485 .iter()
486 .all(|key| discriminator_present(catalog, variant, key))
487 }) {
488 for constraint in &variant.tensors {
489 select_constraint(constraint);
490 }
491 }
492 }
493
494 let unclaimed_keys = if policy.strict {
495 BTreeSet::new()
496 } else {
497 catalog
498 .keys()
499 .filter(|key| !source_keys.contains(*key))
500 .filter(|key| !policy.explicitly_allowed_keys.contains(*key))
501 .filter(|key| {
502 !policy
503 .allowed_prefixes
504 .iter()
505 .any(|prefix| key.starts_with(prefix))
506 })
507 .filter(|key| {
508 !policy
509 .allowed_suffixes
510 .iter()
511 .any(|suffix| key.ends_with(suffix))
512 })
513 .cloned()
514 .collect()
515 };
516
517 Ok(ResolvedCheckpointPlan {
518 identity: identity.into(),
519 source_keys,
520 unclaimed_keys,
521 })
522}
523
524pub fn validate_matching_gguf_encodings(
526 checkpoint: &GgufCheckpoint,
527 pairs: impl IntoIterator<Item = (String, String)>,
528 label: &str,
529) -> Vec<CheckpointIssue> {
530 validate_gguf_encoding_pairs(checkpoint, pairs, label)
531}
532
533fn validate_gguf_encoding_pairs(
534 checkpoint: &GgufCheckpoint,
535 pairs: impl IntoIterator<Item = (String, String)>,
536 label: &str,
537) -> Vec<CheckpointIssue> {
538 let catalog = checkpoint
539 .tensors()
540 .map(|tensor| (tensor.descriptor().name.as_str(), tensor))
541 .collect::<BTreeMap<_, _>>();
542 let mut issues = Vec::new();
543 for (gate_name, up_name) in pairs {
544 let (Some(gate), Some(up)) = (
545 catalog.get(gate_name.as_str()),
546 catalog.get(up_name.as_str()),
547 ) else {
548 continue;
549 };
550 let gate_type = gate.descriptor().ggml_type;
551 let up_type = up.descriptor().ggml_type;
552 let compatible = gate_type == up_type
553 && gate.affine() == up.affine()
554 && gate.is_mxfp4() == up.is_mxfp4();
555 if !compatible {
556 issues.push(CheckpointIssue {
557 kind: CheckpointIssueKind::CompanionMismatch,
558 detail: format!(
559 "{label} paired expert tensors {gate_name:?} and {up_name:?} use incompatible encodings {:?} and {:?}",
560 gate_type, up_type
561 ),
562 tensor_name: Some(gate_name),
563 tensor_type_code: Some(gate_type.code()),
564 metadata_key: None,
565 });
566 }
567 }
568 issues
569}
570
571fn validate_catalog<E, T>(
572 catalog: &BTreeMap<String, PhysicalMetadata<E>>,
573 identity: &str,
574 common: &[T],
575 groups: &[AlternativeLayoutGroup<T>],
576 policy: &CatalogPolicy,
577) -> Vec<CheckpointIssue>
578where
579 E: std::fmt::Debug,
580 T: Constraint<E>,
581{
582 let mut issues = Vec::new();
583 let mut accounted = BTreeSet::new();
584 for constraint in common {
585 account_constraint(&mut accounted, constraint);
586 validate_constraint(catalog, identity, constraint, &mut issues);
587 }
588
589 for group in groups {
590 let mut present = Vec::new();
591 let mut partial = Vec::new();
592 for variant in &group.variants {
593 let count = variant
594 .discriminator_keys
595 .iter()
596 .filter(|key| discriminator_present(catalog, variant, key))
597 .count();
598 if count == variant.discriminator_keys.len() {
599 present.push(variant);
600 } else if count != 0 {
601 partial.push((variant, count));
602 }
603 }
604
605 for (variant, count) in &partial {
606 issues.push(CheckpointIssue {
607 kind: CheckpointIssueKind::ConflictingLayout,
608 detail: format!(
609 "checkpoint plan {:?} layout group {:?} has partially present variant {:?}: {count}/{} discriminator tensors are present",
610 identity,
611 group.id,
612 variant.id,
613 variant.discriminator_keys.len()
614 ),
615 tensor_name: variant
616 .discriminator_keys
617 .iter()
618 .find(|key| !discriminator_present(catalog, variant, key))
619 .cloned(),
620 tensor_type_code: None,
621 metadata_key: None,
622 });
623 }
624
625 if !present.is_empty() && !partial.is_empty() {
626 issues.push(CheckpointIssue {
627 kind: CheckpointIssueKind::ConflictingLayout,
628 detail: format!(
629 "checkpoint plan {:?} layout group {:?} mixes present variants {:?} with partially present variants {:?}",
630 identity,
631 group.id,
632 present
633 .iter()
634 .map(|variant| variant.id.as_str())
635 .collect::<Vec<_>>(),
636 partial
637 .iter()
638 .map(|(variant, _)| variant.id.as_str())
639 .collect::<Vec<_>>()
640 ),
641 tensor_name: present
642 .first()
643 .and_then(|variant| variant.discriminator_keys.first())
644 .cloned(),
645 tensor_type_code: None,
646 metadata_key: None,
647 });
648 }
649
650 if present.len() > 1 {
651 issues.push(CheckpointIssue {
652 kind: CheckpointIssueKind::ConflictingLayout,
653 detail: format!(
654 "checkpoint plan {:?} layout group {:?} has conflicting variants {:?}",
655 identity,
656 group.id,
657 present
658 .iter()
659 .map(|variant| variant.id.as_str())
660 .collect::<Vec<_>>()
661 ),
662 tensor_name: present
663 .get(1)
664 .and_then(|variant| variant.discriminator_keys.first())
665 .cloned(),
666 tensor_type_code: None,
667 metadata_key: None,
668 });
669 } else if present.is_empty() && partial.is_empty() && group.required {
670 let missing_key = group
671 .variants
672 .first()
673 .and_then(|variant| variant.discriminator_keys.first())
674 .cloned();
675 issues.push(CheckpointIssue {
676 kind: CheckpointIssueKind::MissingTensor,
677 detail: format!(
678 "checkpoint plan {:?} has no matching variant for required layout group {:?}",
679 identity, group.id
680 ),
681 tensor_name: missing_key,
682 tensor_type_code: None,
683 metadata_key: None,
684 });
685 }
686
687 for variant in present {
688 for constraint in &variant.tensors {
689 account_constraint(&mut accounted, constraint);
690 validate_constraint(catalog, identity, constraint, &mut issues);
691 }
692 }
693 for (variant, _) in partial {
694 for constraint in &variant.tensors {
695 if constraint_present(catalog, constraint) {
696 account_constraint(&mut accounted, constraint);
697 }
698 validate_constraint(catalog, identity, constraint, &mut issues);
699 }
700 }
701 }
702
703 if policy.strict {
704 for key in catalog.keys() {
705 if accounted.contains(key)
706 || policy.explicitly_allowed_keys.contains(key)
707 || policy
708 .allowed_prefixes
709 .iter()
710 .any(|prefix| key.starts_with(prefix))
711 || policy
712 .allowed_suffixes
713 .iter()
714 .any(|suffix| key.ends_with(suffix))
715 {
716 continue;
717 }
718 issues.push(CheckpointIssue {
719 kind: CheckpointIssueKind::UnexpectedTensor,
720 detail: format!("{identity} catalog contains unexpected tensor {key:?}"),
721 tensor_name: Some(key.clone()),
722 tensor_type_code: None,
723 metadata_key: None,
724 });
725 }
726 }
727 issues
728}
729
730fn constraint_present<E, T: Constraint<E>>(
731 catalog: &BTreeMap<String, PhysicalMetadata<E>>,
732 constraint: &T,
733) -> bool {
734 std::iter::once(constraint.key())
735 .chain(constraint.aliases().iter().map(String::as_str))
736 .any(|key| catalog.contains_key(key))
737}
738
739fn discriminator_present<E, T: Constraint<E>>(
740 catalog: &BTreeMap<String, PhysicalMetadata<E>>,
741 variant: &crate::schema::LayoutVariant<T>,
742 key: &str,
743) -> bool {
744 variant
745 .tensors
746 .iter()
747 .find(|constraint| constraint.key() == key)
748 .map_or_else(
749 || catalog.contains_key(key),
750 |constraint| constraint_present(catalog, constraint),
751 )
752}
753
754fn account_constraint<E, T: Constraint<E>>(accounted: &mut BTreeSet<String>, constraint: &T) {
755 accounted.insert(constraint.key().to_string());
756 accounted.extend(constraint.aliases().iter().cloned());
757}
758
759fn validate_constraint<E: std::fmt::Debug, T: Constraint<E>>(
760 catalog: &BTreeMap<String, PhysicalMetadata<E>>,
761 identity: &str,
762 constraint: &T,
763 issues: &mut Vec<CheckpointIssue>,
764) {
765 let present = std::iter::once(constraint.key())
766 .chain(constraint.aliases().iter().map(String::as_str))
767 .filter_map(|key| catalog.get(key).map(|metadata| (key, metadata)))
768 .collect::<Vec<_>>();
769 if present.len() > 1 {
770 issues.push(CheckpointIssue {
771 kind: CheckpointIssueKind::ConflictingLayout,
772 detail: format!(
773 "checkpoint plan {identity:?} contains multiple physical aliases for logical tensor {:?}: {:?}",
774 constraint.key(),
775 present.iter().map(|(key, _)| *key).collect::<Vec<_>>()
776 ),
777 tensor_name: present.get(1).map(|(key, _)| (*key).to_string()),
778 tensor_type_code: None,
779 metadata_key: None,
780 });
781 return;
782 }
783 let Some((actual_key, actual)) = present.first().copied() else {
784 if constraint.requirement() == TensorRequirement::Required {
785 if constraint.role() == TensorRole::Companion {
786 issues.push(companion_issue(
787 constraint.key(),
788 format!(
789 "checkpoint is missing required companion tensor {:?}",
790 constraint.key()
791 ),
792 ));
793 } else {
794 issues.push(missing(constraint.key()));
795 }
796 }
797 return;
798 };
799
800 if constraint.role() == TensorRole::Companion
801 && (!accepts_shape(constraint, &actual.shape) || !constraint.accepts(&actual.encoding))
802 {
803 issues.push(companion_issue(
804 actual_key,
805 format!(
806 "companion tensor {:?} expected shape {:?} and {}, got {:?} {:?}",
807 actual_key,
808 constraint.shape(),
809 constraint.encoding_detail(),
810 actual.shape,
811 actual.encoding
812 ),
813 ));
814 return;
815 }
816
817 if !accepts_shape(constraint, &actual.shape) {
818 if let Some(element_count) = constraint.element_count() {
819 issues.push(CheckpointIssue {
820 kind: CheckpointIssueKind::ShapeMismatch,
821 detail: format!(
822 "tensor {actual_key:?} must contain {element_count} elements for the loader transform, got {:?}",
823 actual.shape
824 ),
825 tensor_name: Some(actual_key.into()),
826 tensor_type_code: constraint.type_code(&actual.encoding),
827 metadata_key: None,
828 });
829 } else if constraint.alternate_shapes().is_empty() {
830 issues.push(shape_mismatch(
831 actual_key,
832 constraint.shape(),
833 &actual.shape,
834 ));
835 } else {
836 let expected = std::iter::once(constraint.shape())
837 .chain(
838 constraint
839 .alternate_shapes()
840 .iter()
841 .map(|shape| shape.as_slice()),
842 )
843 .collect::<Vec<_>>();
844 issues.push(CheckpointIssue {
845 kind: CheckpointIssueKind::ShapeMismatch,
846 detail: format!(
847 "tensor {actual_key:?} expected one of shapes {expected:?}, got {:?}",
848 actual.shape
849 ),
850 tensor_name: Some(actual_key.into()),
851 tensor_type_code: None,
852 metadata_key: None,
853 });
854 }
855 }
856 if !constraint.accepts(&actual.encoding) {
857 issues.push(CheckpointIssue {
858 kind: CheckpointIssueKind::UnsupportedEncoding,
859 detail: constraint.unsupported_detail(identity, &actual.encoding),
860 tensor_name: Some(actual_key.into()),
861 tensor_type_code: constraint.type_code(&actual.encoding),
862 metadata_key: None,
863 });
864 }
865}
866
867fn accepts_shape<E, T: Constraint<E>>(constraint: &T, actual: &[usize]) -> bool {
868 if let Some(element_count) = constraint.element_count() {
869 return actual
870 .iter()
871 .try_fold(1usize, |count, dimension| count.checked_mul(*dimension))
872 == Some(element_count);
873 }
874 actual == constraint.shape()
875 || constraint
876 .alternate_shapes()
877 .iter()
878 .any(|shape| shape == actual)
879}
880
881fn companion_issue(name: &str, detail: String) -> CheckpointIssue {
882 CheckpointIssue {
883 kind: CheckpointIssueKind::CompanionMismatch,
884 detail,
885 tensor_name: Some(name.into()),
886 tensor_type_code: None,
887 metadata_key: Some("quantization".into()),
888 }
889}
890
891fn metadata_failure(name: &str, error: String) -> CheckpointIssue {
892 CheckpointIssue {
893 kind: CheckpointIssueKind::ConflictingLayout,
894 detail: format!("could not validate tensor {name:?}: {error}"),
895 tensor_name: Some(name.into()),
896 tensor_type_code: None,
897 metadata_key: None,
898 }
899}
900
901#[cfg(test)]
902mod tests {
903 use super::*;
904 use crate::schema::{LayoutVariant, TensorOperation};
905
906 fn safe(
907 key: &str,
908 shape: &[usize],
909 dtype: StoredDtype,
910 ) -> (String, PhysicalMetadata<StoredDtype>) {
911 (
912 key.into(),
913 PhysicalMetadata {
914 shape: shape.to_vec(),
915 encoding: dtype,
916 },
917 )
918 }
919
920 fn safe_plan(
921 common: Vec<SafetensorsTensorConstraint>,
922 groups: Vec<AlternativeLayoutGroup<SafetensorsTensorConstraint>>,
923 policy: CatalogPolicy,
924 ) -> SafetensorsCheckpointPlan {
925 SafetensorsCheckpointPlan::new("test", common, groups, policy).unwrap()
926 }
927
928 #[test]
929 fn required_optional_unexpected_and_catalog_exclusions_are_generic() {
930 let plan = safe_plan(
931 vec![
932 SafetensorsTensorConstraint::required(
933 "required",
934 vec![2],
935 StoredDtypeConstraint::Floating,
936 ),
937 SafetensorsTensorConstraint::required(
938 "optional",
939 vec![1],
940 StoredDtypeConstraint::Floating,
941 )
942 .optional(),
943 ],
944 Vec::new(),
945 CatalogPolicy {
946 strict: true,
947 explicitly_allowed_keys: BTreeSet::from(["allowed".into()]),
948 allowed_prefixes: vec!["cache.".into()],
949 allowed_suffixes: vec![".rotary".into()],
950 },
951 );
952 let catalog = BTreeMap::from([
953 safe("required", &[2], StoredDtype::F16),
954 safe("allowed", &[1], StoredDtype::I32),
955 safe("cache.value", &[1], StoredDtype::I32),
956 safe("layer.rotary", &[1], StoredDtype::F32),
957 safe("unexpected", &[1], StoredDtype::F32),
958 ]);
959 let issues = validate_catalog(
960 &catalog,
961 &plan.identity,
962 &plan.common_tensors,
963 &plan.layout_groups,
964 &plan.catalog_policy,
965 );
966 assert_eq!(issues.len(), 1);
967 assert_eq!(issues[0].kind, CheckpointIssueKind::UnexpectedTensor);
968
969 let mut non_strict = plan.clone();
970 non_strict.catalog_policy.strict = false;
971 assert!(validate_catalog(
972 &catalog,
973 &non_strict.identity,
974 &non_strict.common_tensors,
975 &non_strict.layout_groups,
976 &non_strict.catalog_policy,
977 )
978 .is_empty());
979 let resolved = resolve_catalog(
980 &catalog,
981 &non_strict.identity,
982 &non_strict.common_tensors,
983 &non_strict.layout_groups,
984 &non_strict.catalog_policy,
985 Vec::new(),
986 )
987 .unwrap();
988 assert_eq!(
989 resolved.unclaimed_keys(),
990 &BTreeSet::from(["unexpected".into()])
991 );
992 }
993
994 #[test]
995 fn admitted_resolution_projection_cannot_introduce_sources() {
996 let admitted = ResolvedCheckpointPlan {
997 identity: "complete".into(),
998 source_keys: BTreeSet::from(["target".into(), "extension".into()]),
999 unclaimed_keys: BTreeSet::from(["metadata".into()]),
1000 };
1001 let target = admitted
1002 .project_claimed_sources("target", BTreeSet::from(["target".into()]))
1003 .unwrap();
1004
1005 assert_eq!(target.identity(), "target");
1006 assert_eq!(target.source_keys(), &BTreeSet::from(["target".into()]));
1007 assert_eq!(
1008 target.unclaimed_keys(),
1009 &BTreeSet::from(["metadata".into()])
1010 );
1011 assert!(admitted
1012 .project_claimed_sources("target", BTreeSet::from(["not-admitted".into()]))
1013 .is_err());
1014 }
1015
1016 #[test]
1017 fn exact_floating_and_encoded_fp8_constraints_keep_storage_distinct() {
1018 let constraints = vec![
1019 SafetensorsTensorConstraint::required(
1020 "exact",
1021 vec![1],
1022 StoredDtypeConstraint::Exact(StoredDtype::U8),
1023 ),
1024 SafetensorsTensorConstraint::required(
1025 "weight",
1026 vec![2, 2],
1027 StoredDtypeConstraint::OneOf(vec![StoredDtype::F8E4M3, StoredDtype::U8]),
1028 ),
1029 SafetensorsTensorConstraint::required(
1030 "scale",
1031 vec![1, 1],
1032 StoredDtypeConstraint::Floating,
1033 )
1034 .companion(),
1035 ];
1036 let plan = safe_plan(constraints, Vec::new(), CatalogPolicy::strict());
1037 for floating in [StoredDtype::F16, StoredDtype::BF16, StoredDtype::F32] {
1038 let catalog = BTreeMap::from([
1039 safe("exact", &[1], StoredDtype::U8),
1040 safe("weight", &[2, 2], StoredDtype::F8E4M3),
1041 safe("scale", &[1, 1], floating),
1042 ]);
1043 assert!(validate_catalog(
1044 &catalog,
1045 &plan.identity,
1046 &plan.common_tensors,
1047 &plan.layout_groups,
1048 &plan.catalog_policy,
1049 )
1050 .is_empty());
1051 }
1052 let catalog = BTreeMap::from([
1053 safe("exact", &[1], StoredDtype::U8),
1054 safe("weight", &[2, 2], StoredDtype::U8),
1055 safe("scale", &[1, 1], StoredDtype::I32),
1056 ]);
1057 let issues = validate_catalog(
1058 &catalog,
1059 &plan.identity,
1060 &plan.common_tensors,
1061 &plan.layout_groups,
1062 &plan.catalog_policy,
1063 );
1064 assert_eq!(issues[0].kind, CheckpointIssueKind::CompanionMismatch);
1065 }
1066
1067 #[test]
1068 fn physical_aliases_are_selected_once_and_accounted_by_strict_catalogs() {
1069 let plan = safe_plan(
1070 vec![SafetensorsTensorConstraint::required(
1071 "canonical",
1072 vec![2],
1073 StoredDtypeConstraint::Floating,
1074 )
1075 .with_aliases(["released"])],
1076 Vec::new(),
1077 CatalogPolicy::strict(),
1078 );
1079 let released = BTreeMap::from([safe("released", &[2], StoredDtype::BF16)]);
1080 assert!(validate_catalog(
1081 &released,
1082 &plan.identity,
1083 &plan.common_tensors,
1084 &plan.layout_groups,
1085 &plan.catalog_policy,
1086 )
1087 .is_empty());
1088
1089 let conflicting = BTreeMap::from([
1090 safe("canonical", &[2], StoredDtype::F16),
1091 safe("released", &[2], StoredDtype::BF16),
1092 ]);
1093 let issues = validate_catalog(
1094 &conflicting,
1095 &plan.identity,
1096 &plan.common_tensors,
1097 &plan.layout_groups,
1098 &plan.catalog_policy,
1099 );
1100 assert_eq!(issues.len(), 1);
1101 assert_eq!(issues[0].kind, CheckpointIssueKind::ConflictingLayout);
1102 assert_eq!(issues[0].tensor_name.as_deref(), Some("released"));
1103 }
1104
1105 #[test]
1106 fn safetensors_alternate_shapes_accept_only_declared_layouts() {
1107 let plan = safe_plan(
1108 vec![SafetensorsTensorConstraint::required(
1109 "convolution",
1110 vec![4, 1, 2],
1111 StoredDtypeConstraint::Floating,
1112 )
1113 .with_alternate_shapes([vec![4, 2, 1]])],
1114 Vec::new(),
1115 CatalogPolicy::strict(),
1116 );
1117 let alternate = BTreeMap::from([safe("convolution", &[4, 2, 1], StoredDtype::BF16)]);
1118 assert!(validate_catalog(
1119 &alternate,
1120 &plan.identity,
1121 &plan.common_tensors,
1122 &plan.layout_groups,
1123 &plan.catalog_policy,
1124 )
1125 .is_empty());
1126
1127 let undeclared = BTreeMap::from([safe("convolution", &[4, 2], StoredDtype::BF16)]);
1128 let issues = validate_catalog(
1129 &undeclared,
1130 &plan.identity,
1131 &plan.common_tensors,
1132 &plan.layout_groups,
1133 &plan.catalog_policy,
1134 );
1135 assert_eq!(issues.len(), 1);
1136 assert_eq!(issues[0].kind, CheckpointIssueKind::ShapeMismatch);
1137 }
1138
1139 #[test]
1140 fn element_count_constraints_accept_reshape_equivalent_storage() {
1141 let plan = safe_plan(
1142 vec![SafetensorsTensorConstraint::required(
1143 "convolution",
1144 vec![4, 1, 2],
1145 StoredDtypeConstraint::Floating,
1146 )
1147 .with_element_count(8)],
1148 Vec::new(),
1149 CatalogPolicy::strict(),
1150 );
1151 let reshaped = BTreeMap::from([safe("convolution", &[4, 2], StoredDtype::BF16)]);
1152 assert!(validate_catalog(
1153 &reshaped,
1154 &plan.identity,
1155 &plan.common_tensors,
1156 &plan.layout_groups,
1157 &plan.catalog_policy,
1158 )
1159 .is_empty());
1160
1161 let wrong = BTreeMap::from([safe("convolution", &[4, 3], StoredDtype::BF16)]);
1162 let issues = validate_catalog(
1163 &wrong,
1164 &plan.identity,
1165 &plan.common_tensors,
1166 &plan.layout_groups,
1167 &plan.catalog_policy,
1168 );
1169 assert_eq!(issues.len(), 1);
1170 assert_eq!(issues[0].kind, CheckpointIssueKind::ShapeMismatch);
1171 assert!(issues[0].detail.contains("must contain 8 elements"));
1172 }
1173
1174 fn alternatives() -> Vec<AlternativeLayoutGroup<SafetensorsTensorConstraint>> {
1175 vec![AlternativeLayoutGroup {
1176 id: "projection".into(),
1177 required: true,
1178 variants: vec![
1179 LayoutVariant {
1180 id: "packed".into(),
1181 tensors: vec![SafetensorsTensorConstraint::required(
1182 "packed",
1183 vec![4, 2],
1184 StoredDtypeConstraint::Floating,
1185 )],
1186 discriminator_keys: vec!["packed".into()],
1187 },
1188 LayoutVariant {
1189 id: "split".into(),
1190 tensors: vec![
1191 SafetensorsTensorConstraint::required(
1192 "gate",
1193 vec![2, 2],
1194 StoredDtypeConstraint::Floating,
1195 ),
1196 SafetensorsTensorConstraint::required(
1197 "up",
1198 vec![2, 2],
1199 StoredDtypeConstraint::Floating,
1200 ),
1201 ],
1202 discriminator_keys: vec!["gate".into(), "up".into()],
1203 },
1204 ],
1205 }]
1206 }
1207
1208 #[test]
1209 fn alternatives_report_missing_partial_and_conflicting_layouts() {
1210 let plan = safe_plan(Vec::new(), alternatives(), CatalogPolicy::strict());
1211 let evaluate = |catalog: BTreeMap<_, _>| {
1212 validate_catalog(
1213 &catalog,
1214 &plan.identity,
1215 &plan.common_tensors,
1216 &plan.layout_groups,
1217 &plan.catalog_policy,
1218 )
1219 };
1220 assert!(evaluate(BTreeMap::from([safe("packed", &[4, 2], StoredDtype::F32)])).is_empty());
1221 let missing = evaluate(BTreeMap::new());
1222 assert_eq!(missing[0].kind, CheckpointIssueKind::MissingTensor);
1223 let partial = evaluate(BTreeMap::from([safe("gate", &[2, 2], StoredDtype::F32)]));
1224 assert!(partial
1225 .iter()
1226 .any(|issue| issue.kind == CheckpointIssueKind::MissingTensor));
1227 let conflict = evaluate(BTreeMap::from([
1228 safe("packed", &[4, 2], StoredDtype::F32),
1229 safe("gate", &[2, 2], StoredDtype::F32),
1230 safe("up", &[2, 2], StoredDtype::F32),
1231 ]));
1232 assert!(conflict
1233 .iter()
1234 .any(|issue| issue.detail.contains("conflicting variants")));
1235 let mixed_partial = evaluate(BTreeMap::from([
1236 safe("packed", &[4, 2], StoredDtype::F32),
1237 safe("gate", &[2, 2], StoredDtype::F32),
1238 ]));
1239 assert!(mixed_partial
1240 .iter()
1241 .any(|issue| issue.detail.contains("partially present variants")));
1242 }
1243
1244 #[test]
1245 fn resolution_selects_one_layout_and_only_its_physical_sources() {
1246 let plan = safe_plan(Vec::new(), alternatives(), CatalogPolicy::strict());
1247 let catalog = BTreeMap::from([safe("packed", &[4, 2], StoredDtype::BF16)]);
1248 let resolved = resolve_catalog(
1249 &catalog,
1250 &plan.identity,
1251 &plan.common_tensors,
1252 &plan.layout_groups,
1253 &plan.catalog_policy,
1254 Vec::new(),
1255 )
1256 .unwrap();
1257
1258 assert_eq!(
1259 resolved.source_keys().iter().cloned().collect::<Vec<_>>(),
1260 ["packed"]
1261 );
1262 assert!(!resolved.source_keys().contains("gate"));
1263 assert!(!resolved.source_keys().contains("up"));
1264 }
1265
1266 #[test]
1267 fn gguf_operation_class_constraints_are_checked() {
1268 let constraints = vec![
1269 GgufTensorConstraint::required(
1270 "index",
1271 vec![2],
1272 GgufTypeConstraint::OperationClass(TensorOperation::I32),
1273 ),
1274 GgufTensorConstraint::required(
1275 "matrix",
1276 vec![2, 2],
1277 GgufTypeConstraint::OperationClass(TensorOperation::Matrix),
1278 ),
1279 GgufTensorConstraint::required(
1280 "flexible",
1281 vec![2, 2],
1282 GgufTypeConstraint::OperationClass(TensorOperation::Dense),
1283 )
1284 .with_alternate_shapes([vec![2, 1, 2]]),
1285 ];
1286 let plan =
1287 GgufCheckpointPlan::new("test", constraints, Vec::new(), CatalogPolicy::strict())
1288 .unwrap();
1289 let catalog = BTreeMap::from([
1290 (
1291 "index".into(),
1292 PhysicalMetadata {
1293 shape: vec![2],
1294 encoding: GgufType::I32,
1295 },
1296 ),
1297 (
1298 "matrix".into(),
1299 PhysicalMetadata {
1300 shape: vec![2, 2],
1301 encoding: GgufType::Q4K,
1302 },
1303 ),
1304 (
1305 "flexible".into(),
1306 PhysicalMetadata {
1307 shape: vec![2, 1, 2],
1308 encoding: GgufType::F16,
1309 },
1310 ),
1311 ]);
1312 assert!(validate_catalog(
1313 &catalog,
1314 &plan.identity,
1315 &plan.common_tensors,
1316 &plan.layout_groups,
1317 &plan.catalog_policy,
1318 )
1319 .is_empty());
1320 }
1321
1322 #[test]
1323 fn issue_order_does_not_depend_on_checkpoint_key_order() {
1324 let plan = safe_plan(
1325 vec![SafetensorsTensorConstraint::required(
1326 "required",
1327 vec![2],
1328 StoredDtypeConstraint::Exact(StoredDtype::F32),
1329 )],
1330 Vec::new(),
1331 CatalogPolicy::strict(),
1332 );
1333 let left = BTreeMap::from([
1334 safe("z", &[1], StoredDtype::F16),
1335 safe("a", &[1], StoredDtype::F16),
1336 ]);
1337 let right = left
1338 .iter()
1339 .rev()
1340 .map(|(key, value)| (key.clone(), value.clone()))
1341 .collect();
1342 let validate = |catalog: &BTreeMap<_, _>| {
1343 validate_catalog(
1344 catalog,
1345 &plan.identity,
1346 &plan.common_tensors,
1347 &plan.layout_groups,
1348 &plan.catalog_policy,
1349 )
1350 };
1351 assert_eq!(validate(&left), validate(&right));
1352 }
1353}