Skip to main content

eredu_core/
artifact.rs

1//! Backend-neutral model artifact inspection and preparation planning.
2//!
3//! Inspection parses configuration and checkpoint headers only. It never
4//! materializes tensor payloads or creates a device/runtime object.
5
6use crate::checkpoint::{TensorCatalog, TensorDescriptor, TensorDtype, TensorStorage};
7pub use eredu_checkpoint::artifact::ArtifactFile;
8use eredu_checkpoint::{
9    artifact::{fingerprint_artifact_files, ArtifactFingerprintError, ArtifactMemberFingerprint},
10    safetensors::SafetensorsShards,
11    store::{SharedCheckpointSource, TensorMetadata},
12    StoredDtype,
13};
14use eredu_gguf::{Checkpoint as GgufCheckpoint, GgmlType, MetadataValue};
15use serde::{Deserialize, Serialize};
16use serde_json::Value;
17use std::{
18    collections::{BTreeMap, BTreeSet},
19    fs::File,
20    io::Read,
21    path::{Path, PathBuf},
22    sync::Arc,
23};
24
25/// Content-exact identity of one ordered logical artifact.
26///
27/// The representation is intentionally independent of filesystem location and
28/// metadata. Its digest covers the identity domain, sorted logical file roles,
29/// exact byte lengths, and exact file content.
30#[derive(Clone, Copy, Eq, Hash, PartialEq)]
31pub struct ArtifactIdentity([u8; 32]);
32
33impl ArtifactIdentity {
34    /// Returns the raw SHA-256 digest.
35    pub const fn digest(self) -> [u8; 32] {
36        self.0
37    }
38}
39
40/// Opaque identity of one header-inspection admission instance.
41///
42/// Cloning or architecture-projecting an inspection preserves this token. Independently
43/// inspecting even identical bytes creates a different token, allowing later cold-selection
44/// handoffs to reject accidental cross-plan substitution without reading payload content.
45#[derive(Clone)]
46pub struct ArtifactAdmissionToken(Arc<()>);
47
48impl ArtifactAdmissionToken {
49    fn new() -> Self {
50        Self(Arc::new(()))
51    }
52
53    /// Returns whether two values originated from the same admitted inspection instance.
54    pub fn same_admission(&self, other: &Self) -> bool {
55        Arc::ptr_eq(&self.0, &other.0)
56    }
57}
58
59impl std::fmt::Debug for ArtifactAdmissionToken {
60    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        formatter.write_str("ArtifactAdmissionToken(..)")
62    }
63}
64
65impl std::fmt::Display for ArtifactIdentity {
66    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        write!(formatter, "sha256:{}", hex_digest(&self.0))
68    }
69}
70
71impl std::fmt::Debug for ArtifactIdentity {
72    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        std::fmt::Display::fmt(self, formatter)
74    }
75}
76
77/// Exact in-memory identity facts for one logical artifact member.
78#[derive(Debug, Clone, Eq, PartialEq)]
79pub struct ArtifactMemberIdentity {
80    logical_role: String,
81    length: u64,
82    digest: [u8; 32],
83}
84
85impl ArtifactMemberIdentity {
86    /// Creates one exact logical member identity.
87    pub fn new(logical_role: impl Into<String>, length: u64, digest: [u8; 32]) -> Self {
88        Self {
89            logical_role: logical_role.into(),
90            length,
91            digest,
92        }
93    }
94
95    /// Stable role used in the artifact layout.
96    pub fn logical_role(&self) -> &str {
97        &self.logical_role
98    }
99
100    /// Exact member byte length.
101    pub const fn length(&self) -> u64 {
102        self.length
103    }
104
105    /// Exact member content digest.
106    pub const fn digest(&self) -> [u8; 32] {
107        self.digest
108    }
109}
110
111impl From<ArtifactMemberFingerprint> for ArtifactMemberIdentity {
112    fn from(member: ArtifactMemberFingerprint) -> Self {
113        Self::new(member.logical_role(), member.length(), member.digest())
114    }
115}
116
117/// Fingerprints exact content under one explicit semantic domain.
118///
119/// Input order is immaterial: logical roles are sorted canonically. Empty
120/// domains, empty roles, empty artifacts, and duplicate roles are rejected.
121pub fn fingerprint_artifact(
122    domain: &str,
123    members: impl IntoIterator<Item = ArtifactMemberIdentity>,
124) -> Result<ArtifactIdentity, ArtifactError> {
125    if domain.is_empty() {
126        return Err(ArtifactError::InvalidArtifactIdentity(
127            "artifact identity domain must not be empty".into(),
128        ));
129    }
130    let mut members = members.into_iter().collect::<Vec<_>>();
131    if members.is_empty() {
132        return Err(ArtifactError::InvalidArtifactIdentity(
133            "artifact identity requires at least one file".into(),
134        ));
135    }
136    if members.iter().any(|member| member.logical_role.is_empty()) {
137        return Err(ArtifactError::InvalidArtifactIdentity(
138            "artifact member has an empty logical role".into(),
139        ));
140    }
141    members.sort_unstable_by(|left, right| left.logical_role.cmp(&right.logical_role));
142    if let Some(pair) = members
143        .windows(2)
144        .find(|pair| pair[0].logical_role == pair[1].logical_role)
145    {
146        return Err(ArtifactError::InvalidArtifactIdentity(format!(
147            "duplicate artifact logical role {:?}",
148            pair[0].logical_role
149        )));
150    }
151
152    let mut hasher = sha2::Sha256::new();
153    hash_identity_component(&mut hasher, b"eredu-checkpoint-artifact-v2");
154    hash_identity_component(&mut hasher, domain.as_bytes());
155    use sha2::Digest as _;
156    hasher.update((members.len() as u64).to_le_bytes());
157    for member in members {
158        hash_identity_component(&mut hasher, member.logical_role.as_bytes());
159        hasher.update(member.length.to_le_bytes());
160        hasher.update(member.digest);
161    }
162    Ok(ArtifactIdentity(hasher.finalize().into()))
163}
164
165/// Fingerprints filesystem members through checkpoint-owned stable I/O and
166/// applies the canonical in-memory identity algorithm.
167pub fn fingerprint_filesystem_artifact(
168    domain: &str,
169    files: impl IntoIterator<Item = ArtifactFile>,
170) -> Result<ArtifactIdentity, ArtifactError> {
171    fingerprint_artifact(
172        domain,
173        fingerprint_artifact_files(files)?
174            .into_iter()
175            .map(ArtifactMemberIdentity::from),
176    )
177}
178
179/// Fingerprints the exact SafeTensors members admitted during inspection.
180///
181/// Member names come from the admitted index (or the submitted direct-file
182/// name), so relocation of an otherwise identical artifact does not affect
183/// the identity.
184pub fn fingerprint_safetensors_artifact(
185    domain: &str,
186    shards: &SafetensorsShards,
187) -> Result<ArtifactIdentity, ArtifactError> {
188    fingerprint_filesystem_artifact(
189        domain,
190        shards
191            .logical_payload_paths()
192            .iter()
193            .map(|(role, path)| ArtifactFile::new(role, path)),
194    )
195}
196
197/// Fingerprints the exact GGUF split members admitted by the portable parser.
198///
199/// Logical roles derive solely from validated split numbers rather than
200/// filenames or filesystem locations.
201pub fn fingerprint_gguf_artifact(
202    domain: &str,
203    checkpoint: &GgufCheckpoint,
204) -> Result<ArtifactIdentity, ArtifactError> {
205    fingerprint_filesystem_artifact(
206        domain,
207        checkpoint
208            .shards()
209            .iter()
210            .map(|shard| ArtifactFile::new(format!("split/{:05}", shard.split_no()), shard.path())),
211    )
212}
213
214fn hash_identity_component(hasher: &mut sha2::Sha256, value: &[u8]) {
215    use sha2::Digest as _;
216    hasher.update((value.len() as u64).to_le_bytes());
217    hasher.update(value);
218}
219
220fn hex_digest(bytes: &[u8]) -> String {
221    const DIGITS: &[u8; 16] = b"0123456789abcdef";
222    let mut output = String::with_capacity(bytes.len() * 2);
223    for &byte in bytes {
224        output.push(DIGITS[usize::from(byte >> 4)] as char);
225        output.push(DIGITS[usize::from(byte & 0x0f)] as char);
226    }
227    output
228}
229
230/// Backend-neutral loader contract required by an inspected artifact.
231///
232/// Architecture registries select this protocol while resolving family identity.
233/// Core uses it to route preparation without knowing any concrete model family.
234#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Serialize, Deserialize)]
235#[serde(rename_all = "snake_case")]
236#[non_exhaustive]
237pub enum LoadingProtocol {
238    /// Ordinary whole-model preparation followed by a model session.
239    Model,
240    /// Realtime multi-stream preparation followed by a realtime session.
241    Realtime,
242}
243
244/// Artifact container selected during inspection.
245#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
246#[serde(rename_all = "snake_case")]
247#[non_exhaustive]
248pub enum ArtifactFormat {
249    /// Hugging Face SafeTensors directory.
250    SafeTensors,
251    /// Single-file or canonically sharded GGUF checkpoint.
252    Gguf,
253}
254
255/// Resolved portable model configuration.
256#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
257pub struct ModelConfiguration {
258    /// Submitted outer `model_type` or GGUF architecture.
259    declared_model_type: String,
260    /// Nested text architecture selected for dispatch where applicable.
261    effective_model_type: String,
262    /// Open canonical family name supplied by the architecture registry.
263    family: String,
264    /// Neutral loader contract selected by the architecture registry.
265    loading_protocol: LoadingProtocol,
266    /// Raw JSON configuration for SafeTensors artifacts.
267    #[serde(skip_serializing_if = "Option::is_none")]
268    json: Option<Value>,
269}
270
271impl ModelConfiguration {
272    /// Creates validated portable model-configuration facts.
273    pub fn new(
274        declared_model_type: impl Into<String>,
275        effective_model_type: impl Into<String>,
276        family: impl Into<String>,
277        loading_protocol: LoadingProtocol,
278        json: Option<Value>,
279    ) -> Result<Self, ArtifactError> {
280        let configuration = Self {
281            declared_model_type: declared_model_type.into(),
282            effective_model_type: effective_model_type.into(),
283            family: family.into(),
284            loading_protocol,
285            json,
286        };
287        if [
288            &configuration.declared_model_type,
289            &configuration.effective_model_type,
290            &configuration.family,
291        ]
292        .into_iter()
293        .any(|value| value.trim().is_empty())
294        {
295            return Err(ArtifactError::InvalidArtifact(
296                "model configuration identities must be non-empty".into(),
297            ));
298        }
299        Ok(configuration)
300    }
301
302    /// Submitted outer model type or GGUF architecture.
303    pub fn declared_model_type(&self) -> &str {
304        &self.declared_model_type
305    }
306
307    /// Nested model type selected for architecture dispatch.
308    pub fn effective_model_type(&self) -> &str {
309        &self.effective_model_type
310    }
311
312    /// Open canonical architecture-family name.
313    pub fn family(&self) -> &str {
314        &self.family
315    }
316
317    /// Neutral loading protocol selected by the architecture registry.
318    pub const fn loading_protocol(&self) -> LoadingProtocol {
319        self.loading_protocol
320    }
321
322    /// Raw SafeTensors JSON configuration, when present.
323    pub const fn json(&self) -> Option<&Value> {
324        self.json.as_ref()
325    }
326}
327
328/// Portable configuration plus the architecture-owned plan produced while resolving it.
329#[derive(Debug, Clone)]
330pub struct ResolvedModelConfiguration<P> {
331    /// Backend-neutral configuration facts used by core orchestration.
332    configuration: ModelConfiguration,
333    /// Typed architecture state proven valid by the resolver.
334    architecture_plan: P,
335}
336
337impl<P> ResolvedModelConfiguration<P> {
338    /// Couples one portable configuration with the exact plan derived from it.
339    pub fn new(configuration: ModelConfiguration, architecture_plan: P) -> Self {
340        Self {
341            configuration,
342            architecture_plan,
343        }
344    }
345
346    /// Portable configuration facts used by neutral orchestration.
347    pub const fn configuration(&self) -> &ModelConfiguration {
348        &self.configuration
349    }
350
351    /// Architecture-owned plan derived from the exact configuration.
352    pub const fn architecture_plan(&self) -> &P {
353        &self.architecture_plan
354    }
355
356    /// Separates the portable facts from architecture-owned state.
357    pub fn into_parts(self) -> (ModelConfiguration, P) {
358        (self.configuration, self.architecture_plan)
359    }
360}
361
362/// Architecture-owned resolver used by neutral artifact inspection.
363///
364/// Core owns the transport contract but deliberately does not recognize model
365/// family aliases, GGUF architecture spellings, or nested configuration wrappers.
366pub trait ModelConfigurationResolver {
367    /// Architecture-owned state retained from artifact inspection through materialization.
368    type ArtifactPlan: Clone + std::fmt::Debug;
369
370    /// Resolves one Hugging Face `config.json` value to its canonical family.
371    fn resolve_safetensors(
372        &self,
373        json: &Value,
374    ) -> Result<ResolvedModelConfiguration<Self::ArtifactPlan>, ArtifactError>;
375
376    /// Resolves and structurally admits one GGUF architecture and checkpoint.
377    fn resolve_gguf(
378        &self,
379        architecture: &str,
380        checkpoint: &GgufCheckpoint,
381    ) -> Result<ResolvedModelConfiguration<Self::ArtifactPlan>, ArtifactError>;
382
383    /// Declares the sibling artifacts required by one admitted GGUF architecture.
384    fn gguf_companion_requirements(
385        &self,
386        architecture: &str,
387        checkpoint: &GgufCheckpoint,
388    ) -> Result<Vec<GgufCompanionRequirement>, ArtifactError>;
389
390    /// Finalizes resolved architecture state against the inspected tensor catalog
391    /// and exact sidecars selected by inspection.
392    fn artifact_plan(
393        &self,
394        _path: &Path,
395        _format: ArtifactFormat,
396        _configuration: &ModelConfiguration,
397        _tensors: &TensorCatalog,
398        _validated_gguf: Option<&ValidatedGguf>,
399        resolved_plan: Self::ArtifactPlan,
400    ) -> Result<Self::ArtifactPlan, ArtifactError> {
401        Ok(resolved_plan)
402    }
403}
404
405/// Semantic identity of a separately stored GGUF companion.
406#[derive(Debug, Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
407#[non_exhaustive]
408pub enum GgufCompanionRole {
409    /// Media encoder and projection weights consumed with the decoder.
410    MediaProjector,
411    /// Architecture-declared role not covered by a common semantic variant.
412    Named(String),
413}
414
415/// Encoding policy used to select among matching GGUF companions.
416#[derive(Debug, Clone, Copy, Eq, PartialEq)]
417#[non_exhaustive]
418pub enum GgufCompanionEncoding {
419    /// Require a checkpoint whose tensor catalog contains no quantized weights.
420    DenseRequired,
421    /// Prefer a dense checkpoint, but admit one unambiguous quantized checkpoint.
422    DensePreferred,
423}
424
425/// Architecture-declared filename and encoding policy for one GGUF companion.
426#[derive(Debug, Clone, Eq, PartialEq)]
427pub struct GgufCompanionRequirement {
428    role: GgufCompanionRole,
429    required: bool,
430    filename_prefix: String,
431    parent_search_depth: usize,
432    encoding: GgufCompanionEncoding,
433}
434
435impl GgufCompanionRequirement {
436    /// Creates one validated companion requirement.
437    pub fn new(
438        role: GgufCompanionRole,
439        required: bool,
440        filename_prefix: impl Into<String>,
441        parent_search_depth: usize,
442        encoding: GgufCompanionEncoding,
443    ) -> Result<Self, ArtifactError> {
444        let filename_prefix = filename_prefix.into();
445        if filename_prefix.trim().is_empty()
446            || matches!(&role, GgufCompanionRole::Named(name) if name.trim().is_empty())
447        {
448            return Err(ArtifactError::InvalidArtifact(
449                "GGUF companion roles and filename prefixes must be non-empty".into(),
450            ));
451        }
452        Ok(Self {
453            role,
454            required,
455            filename_prefix,
456            parent_search_depth,
457            encoding,
458        })
459    }
460
461    /// Semantic role of the resolved artifact.
462    pub fn role(&self) -> &GgufCompanionRole {
463        &self.role
464    }
465}
466
467/// Parses an optional GGUF integer metadata value as lossless `u32` values.
468pub fn gguf_u32_metadata_values(
469    key: &str,
470    value: Option<&MetadataValue>,
471) -> Result<Vec<u32>, ArtifactError> {
472    let Some(value) = value else {
473        return Ok(Vec::new());
474    };
475    value.to_u32_vec().ok_or_else(|| {
476        ArtifactError::InvalidArtifact(format!(
477            "GGUF metadata key {key:?} must contain an integer or integer array whose values fit in u32"
478        ))
479    })
480}
481
482/// Header-only artifact inspection result.
483#[derive(Debug, Clone)]
484pub struct ArtifactInspection<P = ()> {
485    admission_token: ArtifactAdmissionToken,
486    path: PathBuf,
487    format: ArtifactFormat,
488    configuration: ModelConfiguration,
489    tensors: TensorCatalog,
490    safetensors_shards: Option<SafetensorsShards>,
491    validated_gguf: Option<ValidatedGguf>,
492    architecture_plan: P,
493}
494
495/// Portable GGUF facts admitted by core inspection.
496///
497/// Backends may enrich this result with runtime-specific compatibility checks,
498/// but do not need to repeat the portable metadata and catalog validation.
499#[derive(Debug, Clone)]
500pub struct ValidatedGguf {
501    checkpoint: GgufCheckpoint,
502    companions: BTreeMap<GgufCompanionRole, ValidatedGgufCompanion>,
503}
504
505/// One exact sibling GGUF admitted during portable inspection.
506#[derive(Debug, Clone)]
507pub struct ValidatedGgufCompanion {
508    path: PathBuf,
509    checkpoint: GgufCheckpoint,
510}
511
512impl ValidatedGgufCompanion {
513    /// Resolved companion path.
514    pub fn path(&self) -> &Path {
515        &self.path
516    }
517
518    /// Header-only checkpoint admitted by portable inspection.
519    pub fn checkpoint(&self) -> &GgufCheckpoint {
520        &self.checkpoint
521    }
522}
523
524impl ValidatedGguf {
525    /// Header-only checkpoint admitted by portable inspection.
526    pub fn checkpoint(&self) -> &GgufCheckpoint {
527        &self.checkpoint
528    }
529
530    /// Returns the resolved companion for one semantic role.
531    pub fn companion(&self, role: &GgufCompanionRole) -> Option<&ValidatedGgufCompanion> {
532        self.companions.get(role)
533    }
534
535    /// Returns every resolved companion in stable role order.
536    pub fn companions(
537        &self,
538    ) -> impl Iterator<Item = (&GgufCompanionRole, &ValidatedGgufCompanion)> {
539        self.companions.iter()
540    }
541
542    /// Consumes the admission proof into its exact primary and companion handles.
543    pub fn into_parts(
544        self,
545    ) -> (
546        GgufCheckpoint,
547        BTreeMap<GgufCompanionRole, ValidatedGgufCompanion>,
548    ) {
549        (self.checkpoint, self.companions)
550    }
551}
552
553impl<P> ArtifactInspection<P> {
554    /// Opaque origin retained across clones and architecture-plan projections.
555    pub fn admission_token(&self) -> ArtifactAdmissionToken {
556        self.admission_token.clone()
557    }
558    /// Submitted artifact path.
559    pub fn path(&self) -> &Path {
560        &self.path
561    }
562    /// Detected artifact format.
563    pub const fn format(&self) -> ArtifactFormat {
564        self.format
565    }
566    /// Resolved model configuration.
567    pub fn configuration(&self) -> &ModelConfiguration {
568        &self.configuration
569    }
570    /// Validated portable tensor catalog.
571    pub fn tensors(&self) -> &TensorCatalog {
572        &self.tensors
573    }
574    /// Exact canonical SafeTensors shard set admitted during inspection.
575    pub fn safetensors_shards(&self) -> Option<&SafetensorsShards> {
576        self.safetensors_shards.as_ref()
577    }
578    /// Validated portable GGUF result, when applicable.
579    pub fn validated_gguf(&self) -> Option<&ValidatedGguf> {
580        self.validated_gguf.as_ref()
581    }
582    /// Portable GGUF checkpoint handle, when applicable.
583    pub fn gguf_checkpoint(&self) -> Option<&GgufCheckpoint> {
584        self.validated_gguf().map(ValidatedGguf::checkpoint)
585    }
586    /// Architecture-owned state derived from the exact inspected artifact.
587    pub fn architecture_plan(&self) -> &P {
588        &self.architecture_plan
589    }
590    /// Mutably borrows architecture-owned state for facade-level enrichment.
591    pub fn architecture_plan_mut(&mut self) -> &mut P {
592        &mut self.architecture_plan
593    }
594
595    /// Replaces architecture-owned state without changing admitted artifact facts.
596    ///
597    /// Architecture registries use this only for typed additive projections
598    /// whose original checkpoint admission remains authoritative.
599    pub fn map_architecture_plan<Q>(self, map: impl FnOnce(P) -> Q) -> ArtifactInspection<Q> {
600        ArtifactInspection {
601            admission_token: self.admission_token,
602            path: self.path,
603            format: self.format,
604            configuration: self.configuration,
605            tensors: self.tensors,
606            safetensors_shards: self.safetensors_shards,
607            validated_gguf: self.validated_gguf,
608            architecture_plan: map(self.architecture_plan),
609        }
610    }
611}
612
613/// Requested load-time weight transformation.
614#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
615#[serde(tag = "kind", rename_all = "snake_case")]
616#[non_exhaustive]
617pub enum QuantizationRequest {
618    /// Per-group affine integer quantization.
619    Affine {
620        /// Scalars per quantization group.
621        group_size: u32,
622        /// Packed bits per scalar.
623        bits: u8,
624    },
625    /// Microscaling FP4 with E2M1 values and E8M0 scales.
626    MxFp4,
627}
628
629/// Coarse backend-neutral weight residency request.
630#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize)]
631#[serde(rename_all = "snake_case")]
632#[non_exhaustive]
633pub enum ResidencyRequest {
634    /// Keep all owned weights resident.
635    #[default]
636    FullyResident,
637    /// Keep a bounded layer window resident and stage remaining layers from host.
638    LayerwiseHost,
639    /// Stream bounded layer units from disk.
640    DenseDiskStream,
641    /// Manage independently addressable parameter banks beside ordinary units.
642    AddressableParameterBanks,
643}
644
645/// Backend-neutral inputs to materialization-route selection.
646#[derive(Debug, Clone, Copy, Default, Eq, PartialEq, Serialize, Deserialize)]
647pub struct PreparationPolicy {
648    /// Optional requested load-time transformation.
649    quantization: Option<QuantizationRequest>,
650    /// Requested residency family.
651    residency: ResidencyRequest,
652    /// Exact parallel topology selected for materialization, when explicitly configured.
653    topology: Option<crate::topology::ParallelTopology>,
654    /// Capabilities required from the exact prepared session.
655    required_session_capabilities: crate::backend::SessionCapabilities,
656}
657
658impl PreparationPolicy {
659    /// Creates a portable preparation request with no optional session facilities.
660    pub const fn new(
661        quantization: Option<QuantizationRequest>,
662        residency: ResidencyRequest,
663    ) -> Self {
664        Self {
665            quantization,
666            residency,
667            topology: None,
668            required_session_capabilities: crate::backend::SessionCapabilities::new(
669                false, false, false,
670            ),
671        }
672    }
673
674    /// Returns the requested load-time transformation.
675    pub const fn quantization(self) -> Option<QuantizationRequest> {
676        self.quantization
677    }
678    /// Returns the requested residency family.
679    pub const fn residency(self) -> ResidencyRequest {
680        self.residency
681    }
682    /// Returns the requested semantic topology.
683    pub const fn topology(self) -> Option<crate::topology::ParallelTopology> {
684        self.topology
685    }
686    /// Returns the exact required session facilities.
687    pub const fn required_session_capabilities(self) -> crate::backend::SessionCapabilities {
688        self.required_session_capabilities
689    }
690    /// Returns a request with a selected semantic topology.
691    pub const fn with_topology(mut self, topology: crate::topology::ParallelTopology) -> Self {
692        self.topology = Some(topology);
693        self
694    }
695    /// Returns a request with exact required session facilities.
696    pub const fn with_required_session_capabilities(
697        mut self,
698        capabilities: crate::backend::SessionCapabilities,
699    ) -> Self {
700        self.required_session_capabilities = capabilities;
701        self
702    }
703
704    /// Validates exact session requirements independently from device requirements.
705    pub fn validate_session_capabilities(
706        &self,
707        available: &crate::backend::SessionCapabilities,
708    ) -> Result<(), crate::backend::SessionCapabilityError> {
709        self.required_session_capabilities.validate(available)
710    }
711}
712
713/// Canonical materialization recipe selected by the core planner.
714#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize, Deserialize)]
715#[serde(rename_all = "snake_case")]
716#[non_exhaustive]
717pub enum MaterializationRoute {
718    /// Resident materialization, optionally with a load-time transform.
719    Resident,
720    /// Bounded ordinary-unit materialization.
721    Layerwise,
722    /// Independent addressable parameter-bank materialization.
723    AddressableParameterBanks,
724}
725
726/// Fully inspected input supplied to one selected backend for materialization.
727#[derive(Debug, Clone)]
728pub struct ModelPreparationPlan<P = ()> {
729    inspection: ArtifactInspection<P>,
730    policy: PreparationPolicy,
731    route: MaterializationRoute,
732    admitted_session_capabilities: crate::backend::SessionCapabilities,
733}
734
735impl<P> ModelPreparationPlan<P> {
736    /// Builds a plan directly from the exact admission retained by a neutral
737    /// inspection/selection outcome.
738    pub fn from_retained_admission(
739        inspection: ArtifactInspection<P>,
740        admission: crate::PreparationAdmission,
741    ) -> Result<Self, ArtifactError> {
742        if admission.request().format() != inspection.format() {
743            return Err(ArtifactError::InvalidArtifact(
744                "retained preparation admission has a different artifact format".into(),
745            ));
746        }
747        Ok(Self {
748            inspection,
749            policy: admission.request().policy(),
750            route: admission.route(),
751            admitted_session_capabilities: admission.session_capabilities(),
752        })
753    }
754
755    /// Header-only inspection owned by the plan.
756    pub fn inspection(&self) -> &ArtifactInspection<P> {
757        &self.inspection
758    }
759    /// Validated caller policy.
760    pub const fn policy(&self) -> PreparationPolicy {
761        self.policy
762    }
763    /// Canonical materialization route.
764    pub const fn route(&self) -> MaterializationRoute {
765        self.route
766    }
767    /// Exact session capabilities admitted before materialization.
768    pub const fn admitted_session_capabilities(&self) -> crate::backend::SessionCapabilities {
769        self.admitted_session_capabilities
770    }
771
772    /// Consumes the plan into the exact portable artifact admitted by inspection.
773    ///
774    /// Architecture state remains available through [`Self::inspection`] before
775    /// this consuming operation. Policy and route are intentionally not repeated
776    /// in a positional handoff: materializers must inspect those named properties
777    /// before consuming the plan.
778    pub fn into_artifact(self) -> ModelArtifact {
779        match self.inspection.validated_gguf {
780            Some(validated) => ModelArtifact::Gguf {
781                path: self.inspection.path,
782                configuration: self.inspection.configuration,
783                tensors: self.inspection.tensors,
784                validated,
785            },
786            None => ModelArtifact::SafeTensors {
787                path: self.inspection.path,
788                configuration: self.inspection.configuration,
789                tensors: self.inspection.tensors,
790                shards: self
791                    .inspection
792                    .safetensors_shards
793                    .expect("SafeTensors inspection retains its admitted shard set"),
794            },
795        }
796    }
797}
798
799/// Portable artifact payload consumed by a backend materializer.
800#[derive(Debug, Clone)]
801#[non_exhaustive]
802pub enum ModelArtifact {
803    /// SafeTensors directory and validated header catalog.
804    SafeTensors {
805        /// Model directory.
806        path: PathBuf,
807        /// Resolved configuration.
808        configuration: ModelConfiguration,
809        /// Header-only tensor catalog.
810        tensors: TensorCatalog,
811        /// Exact canonical shard set admitted during header inspection.
812        shards: SafetensorsShards,
813    },
814    /// Validated GGUF checkpoint handle and metadata-derived configuration.
815    Gguf {
816        /// Submitted first-shard path.
817        path: PathBuf,
818        /// Resolved configuration.
819        configuration: ModelConfiguration,
820        /// Header-only tensor catalog.
821        tensors: TensorCatalog,
822        /// Portable admission proof containing the exact primary and companions.
823        validated: ValidatedGguf,
824    },
825}
826
827/// Opens an admitted SafeTensors artifact through the canonical prepared source.
828///
829/// The exact shard set comes from header-only inspection. This function does
830/// not rediscover a directory or index, and it validates the current headers
831/// against the retained portable catalog before applying the already admitted
832/// checkpoint resolution. Tensor payloads remain unopened until a later
833/// selected materialization requests a lease.
834pub fn open_prepared_safetensors_artifact(
835    tensors: &TensorCatalog,
836    shards: SafetensorsShards,
837    resolution: eredu_checkpoint::validation::ResolvedCheckpointPlan,
838    max_cached_shards: usize,
839) -> Result<SharedCheckpointSource, ArtifactError> {
840    let catalog = tensors
841        .descriptors()
842        .map(|tensor| {
843            let storage = tensor.storage.as_ref().ok_or_else(|| {
844                ArtifactError::InvalidArtifact(format!(
845                    "prepared SafeTensors tensor {:?} has no storage provenance",
846                    tensor.name
847                ))
848            })?;
849            let stored_dtype = tensor_dtype_to_stored(&tensor.dtype);
850            Ok((
851                tensor.name.clone(),
852                TensorMetadata {
853                    name: tensor.name.clone(),
854                    logical_shape: tensor.shape.clone(),
855                    physical_shape: tensor.shape.clone(),
856                    stored_dtype,
857                    encoded_byte_len: storage.length,
858                    backing_shard: Some(PathBuf::from(&storage.member)),
859                },
860            ))
861        })
862        .collect::<Result<BTreeMap<_, _>, ArtifactError>>()?;
863    eredu_checkpoint::store::open_prepared_safetensors_source(
864        shards,
865        catalog,
866        resolution,
867        max_cached_shards,
868    )
869    .map_err(Into::into)
870}
871
872fn tensor_dtype_to_stored(dtype: &TensorDtype) -> StoredDtype {
873    match dtype {
874        TensorDtype::Bool => StoredDtype::Bool,
875        TensorDtype::U8 => StoredDtype::U8,
876        TensorDtype::I8 => StoredDtype::I8,
877        TensorDtype::I16 => StoredDtype::I16,
878        TensorDtype::U16 => StoredDtype::U16,
879        TensorDtype::F16 => StoredDtype::F16,
880        TensorDtype::Bf16 => StoredDtype::BF16,
881        TensorDtype::I32 => StoredDtype::I32,
882        TensorDtype::U32 => StoredDtype::U32,
883        TensorDtype::F32 => StoredDtype::F32,
884        TensorDtype::F64 => StoredDtype::F64,
885        TensorDtype::I64 => StoredDtype::I64,
886        TensorDtype::U64 => StoredDtype::U64,
887        TensorDtype::Complex64 => StoredDtype::C64,
888        TensorDtype::Encoded(name) => match name.as_str() {
889            "F8_E4M3" => StoredDtype::F8E4M3,
890            "F4" => StoredDtype::F4,
891            "F8_E8M0" => StoredDtype::F8E8M0,
892            "F8_E5M2" => StoredDtype::F8E5M2,
893            _ => StoredDtype::Other(name.clone()),
894        },
895    }
896}
897
898/// Inspect a local artifact without loading tensor payloads.
899pub fn inspect_artifact<R: ModelConfigurationResolver>(
900    path: impl AsRef<Path>,
901    resolver: &R,
902) -> Result<ArtifactInspection<R::ArtifactPlan>, ArtifactError> {
903    let path = path.as_ref();
904    if is_gguf(path) {
905        inspect_gguf(path, resolver)
906    } else if path.is_dir() {
907        inspect_safetensors(path, resolver)
908    } else if !path.exists() {
909        Err(ArtifactError::MissingArtifact(path.to_path_buf()))
910    } else {
911        Err(ArtifactError::UnsupportedContainer(path.to_path_buf()))
912    }
913}
914
915/// Validate policy and select one backend-independent materialization route.
916pub fn plan_model_preparation<P>(
917    inspection: ArtifactInspection<P>,
918    policy: PreparationPolicy,
919    admitted_session_capabilities: crate::backend::SessionCapabilities,
920) -> Result<ModelPreparationPlan<P>, ArtifactError> {
921    let route = validate_preparation_policy(inspection.configuration.loading_protocol, policy)?;
922    Ok(ModelPreparationPlan {
923        inspection,
924        policy,
925        route,
926        admitted_session_capabilities,
927    })
928}
929
930/// Validate a preparation policy against resolved portable artifact facts.
931pub fn validate_preparation_policy(
932    protocol: LoadingProtocol,
933    policy: PreparationPolicy,
934) -> Result<MaterializationRoute, ArtifactError> {
935    if protocol != LoadingProtocol::Model {
936        return Err(ArtifactError::UnsupportedLoadingProtocol(protocol));
937    }
938    let route = match policy.residency {
939        ResidencyRequest::FullyResident => MaterializationRoute::Resident,
940        ResidencyRequest::LayerwiseHost | ResidencyRequest::DenseDiskStream => {
941            MaterializationRoute::Layerwise
942        }
943        ResidencyRequest::AddressableParameterBanks => {
944            MaterializationRoute::AddressableParameterBanks
945        }
946    };
947    Ok(route)
948}
949
950fn inspect_gguf<R: ModelConfigurationResolver>(
951    path: &Path,
952    resolver: &R,
953) -> Result<ArtifactInspection<R::ArtifactPlan>, ArtifactError> {
954    let checkpoint = GgufCheckpoint::open(path)?;
955    let architecture_name = checkpoint
956        .metadata()
957        .get("general.architecture")
958        .and_then(MetadataValue::as_str)
959        .ok_or(ArtifactError::MissingGgufArchitecture)?;
960    let (configuration, resolved_plan) = resolver
961        .resolve_gguf(architecture_name, &checkpoint)?
962        .into_parts();
963    let requirements = resolver.gguf_companion_requirements(architecture_name, &checkpoint)?;
964    let companions = resolve_gguf_companions(path, &requirements)?;
965    validate_gguf_container(&checkpoint)?;
966    let tensors = checkpoint
967        .tensors()
968        .map(|tensor| {
969            let descriptor = tensor.descriptor();
970            let shape = descriptor
971                .dimensions
972                .iter()
973                .map(|&dimension| {
974                    usize::try_from(dimension).map_err(|_| {
975                        ArtifactError::InvalidArtifact(format!(
976                            "GGUF tensor {:?} dimension {dimension} exceeds the host address space",
977                            descriptor.name
978                        ))
979                    })
980                })
981                .collect::<Result<Vec<_>, _>>()?;
982            Ok(TensorDescriptor {
983                name: descriptor.name.clone(),
984                shape,
985                dtype: gguf_dtype(descriptor.ggml_type),
986                storage: None,
987            })
988        })
989        .collect::<Result<Vec<_>, ArtifactError>>()?;
990    let tensors = TensorCatalog::new(tensors)?;
991    let validated_gguf = ValidatedGguf {
992        checkpoint,
993        companions,
994    };
995    let architecture_plan = resolver.artifact_plan(
996        path,
997        ArtifactFormat::Gguf,
998        &configuration,
999        &tensors,
1000        Some(&validated_gguf),
1001        resolved_plan,
1002    )?;
1003    Ok(ArtifactInspection {
1004        admission_token: ArtifactAdmissionToken::new(),
1005        path: path.to_path_buf(),
1006        format: ArtifactFormat::Gguf,
1007        configuration,
1008        tensors,
1009        safetensors_shards: None,
1010        validated_gguf: Some(validated_gguf),
1011        architecture_plan,
1012    })
1013}
1014
1015/// Resolves architecture-declared GGUF companions without materializing payloads.
1016pub fn resolve_gguf_companions(
1017    primary: &Path,
1018    requirements: &[GgufCompanionRequirement],
1019) -> Result<BTreeMap<GgufCompanionRole, ValidatedGgufCompanion>, ArtifactError> {
1020    let mut resolved = BTreeMap::new();
1021    let mut declared_roles = BTreeSet::new();
1022    for requirement in requirements {
1023        if !declared_roles.insert(requirement.role.clone()) {
1024            return Err(ArtifactError::InvalidArtifact(format!(
1025                "GGUF companion role {:?} was declared more than once",
1026                requirement.role
1027            )));
1028        }
1029        let mut directories = Vec::new();
1030        let mut directory = primary.parent().unwrap_or_else(|| Path::new("."));
1031        directories.push(directory.to_path_buf());
1032        for _ in 0..requirement.parent_search_depth {
1033            let Some(parent) = directory.parent() else {
1034                break;
1035            };
1036            if parent == directory {
1037                break;
1038            }
1039            directories.push(parent.to_path_buf());
1040            directory = parent;
1041        }
1042        let mut candidates = Vec::new();
1043        for directory in &directories {
1044            let candidate_start = candidates.len();
1045            for entry in std::fs::read_dir(directory)? {
1046                let path = entry?.path();
1047                let name = path
1048                    .file_name()
1049                    .and_then(|name| name.to_str())
1050                    .unwrap_or_default();
1051                if path != primary
1052                    && path.is_file()
1053                    && name
1054                        .get(..requirement.filename_prefix.len())
1055                        .is_some_and(|prefix| {
1056                            prefix.eq_ignore_ascii_case(&requirement.filename_prefix)
1057                        })
1058                    && is_gguf(&path)
1059                {
1060                    let checkpoint = GgufCheckpoint::open(&path)?;
1061                    if checkpoint.physical_tensor_count() == 0 {
1062                        return Err(ArtifactError::InvalidArtifact(format!(
1063                            "GGUF companion {} contains no tensors",
1064                            path.display()
1065                        )));
1066                    }
1067                    let dense = checkpoint.tensors().all(|tensor| {
1068                        matches!(
1069                            tensor.descriptor().ggml_type,
1070                            eredu_gguf::GgmlType::F32
1071                                | eredu_gguf::GgmlType::F16
1072                                | eredu_gguf::GgmlType::Bf16
1073                        )
1074                    });
1075                    candidates.push((path, checkpoint, dense));
1076                }
1077            }
1078            if candidates.len() != candidate_start {
1079                break;
1080            }
1081        }
1082        candidates.sort_by(|left, right| left.0.cmp(&right.0));
1083        candidates.dedup_by(|left, right| left.0 == right.0);
1084        let dense = candidates
1085            .iter()
1086            .filter(|candidate| candidate.2)
1087            .collect::<Vec<_>>();
1088        let selected = match requirement.encoding {
1089            GgufCompanionEncoding::DenseRequired => match dense.as_slice() {
1090                [candidate] => Some(*candidate),
1091                [] if candidates.is_empty() => None,
1092                [] => {
1093                    return Err(ArtifactError::InvalidArtifact(format!(
1094                        "GGUF companion {:?} requires dense F32, F16, or BF16 tensors, but all {} matching candidates are quantized",
1095                        requirement.role,
1096                        candidates.len()
1097                    )))
1098                }
1099                _ => return Err(ambiguous_companion(requirement, &directories, dense.len())),
1100            },
1101            GgufCompanionEncoding::DensePreferred => match dense.as_slice() {
1102                [candidate] => Some(*candidate),
1103                [] => match candidates.as_slice() {
1104                    [candidate] => Some(candidate),
1105                    [] => None,
1106                    _ => {
1107                        return Err(ambiguous_companion(
1108                            requirement,
1109                            &directories,
1110                            candidates.len(),
1111                        ))
1112                    }
1113                },
1114                _ => return Err(ambiguous_companion(requirement, &directories, dense.len())),
1115            },
1116        };
1117        match selected {
1118            Some((path, checkpoint, _)) => {
1119                resolved.insert(
1120                    requirement.role.clone(),
1121                    ValidatedGgufCompanion {
1122                        path: path.clone(),
1123                        checkpoint: checkpoint.clone(),
1124                    },
1125                );
1126            }
1127            None if requirement.required => {
1128                return Err(ArtifactError::MissingRequiredGgufCompanion {
1129                    role: requirement.role.clone(),
1130                    filename_prefix: requirement.filename_prefix.clone(),
1131                    searched_directories: directories,
1132                })
1133            }
1134            None => {}
1135        }
1136    }
1137    Ok(resolved)
1138}
1139
1140fn ambiguous_companion(
1141    requirement: &GgufCompanionRequirement,
1142    directories: &[PathBuf],
1143    candidates: usize,
1144) -> ArtifactError {
1145    ArtifactError::InvalidArtifact(format!(
1146        "GGUF companion {:?} is ambiguous: found {candidates} preferred candidates in {}",
1147        requirement.role,
1148        display_directories(directories)
1149    ))
1150}
1151
1152fn display_directories(directories: &[PathBuf]) -> String {
1153    directories
1154        .iter()
1155        .map(|directory| directory.display().to_string())
1156        .collect::<Vec<_>>()
1157        .join(", ")
1158}
1159
1160fn validate_gguf_container(checkpoint: &GgufCheckpoint) -> Result<(), ArtifactError> {
1161    if checkpoint.physical_tensor_count() == 0 {
1162        return Err(ArtifactError::InvalidArtifact(
1163            "GGUF model checkpoint contains no tensors".into(),
1164        ));
1165    }
1166    Ok(())
1167}
1168
1169fn inspect_safetensors<R: ModelConfigurationResolver>(
1170    path: &Path,
1171    resolver: &R,
1172) -> Result<ArtifactInspection<R::ArtifactPlan>, ArtifactError> {
1173    let config_path = path.join("config.json");
1174    let json: Value = serde_json::from_reader(File::open(&config_path)?)?;
1175    let (configuration, resolved_plan) = resolver.resolve_safetensors(&json)?.into_parts();
1176    let shards = SafetensorsShards::discover(path)?;
1177    let mut descriptors = Vec::new();
1178    let mut names = BTreeSet::new();
1179    for shard in shards.payload_paths() {
1180        for descriptor in inspect_safetensors_header(shard)? {
1181            if !names.insert(descriptor.name.clone()) {
1182                return Err(ArtifactError::DuplicateTensor(descriptor.name));
1183            }
1184            descriptors.push(descriptor);
1185        }
1186    }
1187    let tensors = TensorCatalog::new(descriptors)?;
1188    if tensors.is_empty() {
1189        return Err(ArtifactError::InvalidArtifact(
1190            "SafeTensors checkpoint contains no tensors".into(),
1191        ));
1192    }
1193    let architecture_plan = resolver.artifact_plan(
1194        path,
1195        ArtifactFormat::SafeTensors,
1196        &configuration,
1197        &tensors,
1198        None,
1199        resolved_plan,
1200    )?;
1201    Ok(ArtifactInspection {
1202        admission_token: ArtifactAdmissionToken::new(),
1203        path: path.to_path_buf(),
1204        format: ArtifactFormat::SafeTensors,
1205        configuration,
1206        tensors,
1207        safetensors_shards: Some(shards),
1208        validated_gguf: None,
1209        architecture_plan,
1210    })
1211}
1212
1213#[derive(Deserialize)]
1214struct RawSafetensorInfo {
1215    dtype: String,
1216    shape: Vec<usize>,
1217    data_offsets: [u64; 2],
1218}
1219
1220fn inspect_safetensors_header(path: &Path) -> Result<Vec<TensorDescriptor>, ArtifactError> {
1221    const MAX_HEADER_BYTES: u64 = 100_000_000;
1222    let mut file = File::open(path)?;
1223    let file_len = file.metadata()?.len();
1224    let mut length = [0_u8; 8];
1225    file.read_exact(&mut length)?;
1226    let header_len = u64::from_le_bytes(length);
1227    if header_len > MAX_HEADER_BYTES {
1228        return Err(ArtifactError::InvalidArtifact(format!(
1229            "SafeTensors header in {} exceeds {MAX_HEADER_BYTES} bytes",
1230            path.display()
1231        )));
1232    }
1233    let mut header = vec![
1234        0_u8;
1235        usize::try_from(header_len).map_err(|_| {
1236            ArtifactError::InvalidArtifact("SafeTensors header length overflows usize".into())
1237        })?
1238    ];
1239    file.read_exact(&mut header)?;
1240    let raw: BTreeMap<String, Value> = serde_json::from_slice(&header)?;
1241    let payload_start = 8_u64
1242        .checked_add(header_len)
1243        .ok_or_else(|| ArtifactError::InvalidArtifact("SafeTensors offset overflow".into()))?;
1244    let mut entries = raw
1245        .into_iter()
1246        .filter(|(name, _)| name != "__metadata__")
1247        .map(|(name, value)| {
1248            serde_json::from_value::<RawSafetensorInfo>(value).map(|info| (name, info))
1249        })
1250        .collect::<Result<Vec<_>, _>>()?;
1251    entries.sort_by_key(|(_, info)| info.data_offsets[0]);
1252    let mut output = Vec::with_capacity(entries.len());
1253    let mut expected_offset = 0_u64;
1254    for (name, info) in entries {
1255        // SafeTensors rank-zero tensors are scalar parameters with one stored
1256        // element. Gemma media clipping bounds use this representation.
1257        if info.shape.contains(&0) {
1258            return Err(ArtifactError::InvalidArtifact(format!(
1259                "SafeTensors tensor {name:?} has an invalid shape"
1260            )));
1261        }
1262        let [start, end] = info.data_offsets;
1263        if start != expected_offset || end < start {
1264            return Err(ArtifactError::InvalidArtifact(format!(
1265                "SafeTensors tensor {name:?} has non-contiguous data offsets"
1266            )));
1267        }
1268        expected_offset = end;
1269        let absolute = payload_start
1270            .checked_add(start)
1271            .ok_or_else(|| ArtifactError::InvalidArtifact("SafeTensors offset overflow".into()))?;
1272        output.push(TensorDescriptor {
1273            name,
1274            shape: info.shape,
1275            dtype: safetensors_dtype(&info.dtype),
1276            storage: Some(TensorStorage {
1277                member: path.display().to_string(),
1278                offset: absolute,
1279                length: end - start,
1280            }),
1281        });
1282    }
1283    if payload_start
1284        .checked_add(expected_offset)
1285        .ok_or_else(|| ArtifactError::InvalidArtifact("SafeTensors length overflow".into()))?
1286        != file_len
1287    {
1288        return Err(ArtifactError::InvalidArtifact(format!(
1289            "SafeTensors payload length does not match header in {}",
1290            path.display()
1291        )));
1292    }
1293    Ok(output)
1294}
1295
1296fn safetensors_dtype(dtype: &str) -> TensorDtype {
1297    match dtype {
1298        "BOOL" => TensorDtype::Bool,
1299        "U8" => TensorDtype::U8,
1300        "I8" => TensorDtype::I8,
1301        "I16" => TensorDtype::I16,
1302        "U16" => TensorDtype::U16,
1303        "F16" => TensorDtype::F16,
1304        "BF16" => TensorDtype::Bf16,
1305        "I32" => TensorDtype::I32,
1306        "U32" => TensorDtype::U32,
1307        "F32" => TensorDtype::F32,
1308        "F64" => TensorDtype::F64,
1309        "I64" => TensorDtype::I64,
1310        "U64" => TensorDtype::U64,
1311        "C64" => TensorDtype::Complex64,
1312        other => TensorDtype::Encoded(other.into()),
1313    }
1314}
1315
1316fn gguf_dtype(dtype: GgmlType) -> TensorDtype {
1317    match dtype {
1318        GgmlType::F32 => TensorDtype::F32,
1319        GgmlType::F16 => TensorDtype::F16,
1320        GgmlType::Bf16 => TensorDtype::Bf16,
1321        GgmlType::I8 => TensorDtype::I8,
1322        GgmlType::I16 => TensorDtype::I16,
1323        GgmlType::I32 => TensorDtype::I32,
1324        GgmlType::I64 => TensorDtype::I64,
1325        GgmlType::F64 => TensorDtype::F64,
1326        encoded => TensorDtype::Encoded(format!("{encoded:?}")),
1327    }
1328}
1329
1330fn is_gguf(path: &Path) -> bool {
1331    path.extension()
1332        .and_then(|extension| extension.to_str())
1333        .is_some_and(|extension| extension.eq_ignore_ascii_case("gguf"))
1334}
1335
1336/// Portable artifact inspection/planning failure.
1337#[derive(Debug, thiserror::Error)]
1338#[non_exhaustive]
1339pub enum ArtifactError {
1340    /// Artifact identity domain, membership, or logical roles are invalid.
1341    #[error("invalid artifact identity: {0}")]
1342    InvalidArtifactIdentity(String),
1343    /// Artifact path does not exist.
1344    #[error("model artifact does not exist: {0}")]
1345    MissingArtifact(PathBuf),
1346    /// Path is not a supported artifact container.
1347    #[error("model artifact must be a SafeTensors directory or .gguf file: {0}")]
1348    UnsupportedContainer(PathBuf),
1349    /// Model type is not recognized.
1350    #[error("unsupported model type: {0}")]
1351    UnsupportedModelType(String),
1352    /// GGUF architecture is not recognized.
1353    #[error("unsupported GGUF architecture: {0}")]
1354    UnsupportedGgufArchitecture(String),
1355    /// GGUF architecture metadata is absent or has the wrong type.
1356    #[error("GGUF metadata is missing string key \"general.architecture\"")]
1357    MissingGgufArchitecture,
1358    /// An architecture-required sibling GGUF artifact could not be found.
1359    #[error(
1360        "required GGUF companion {role:?} matching {filename_prefix:?} was not found in {searched}",
1361        searched = display_directories(.searched_directories)
1362    )]
1363    MissingRequiredGgufCompanion {
1364        /// Semantic role of the missing companion.
1365        role: GgufCompanionRole,
1366        /// Filename prefix used to discover the companion.
1367        filename_prefix: String,
1368        /// Directories searched in priority order.
1369        searched_directories: Vec<PathBuf>,
1370    },
1371    /// Header/catalog content is contradictory.
1372    #[error("invalid model artifact: {0}")]
1373    InvalidArtifact(String),
1374    /// Architecture-owned artifact planning rejected the inspected inputs.
1375    #[error("invalid architecture artifact plan: {0}")]
1376    InvalidArchitecturePlan(String),
1377    /// A tensor name occurred more than once.
1378    #[error("duplicate checkpoint tensor {0:?}")]
1379    DuplicateTensor(String),
1380    /// Canonical SafeTensors shard discovery or path admission failed.
1381    #[error(transparent)]
1382    SafetensorsShards(#[from] eredu_checkpoint::safetensors::SafetensorsShardError),
1383    /// Filesystem-backed stable content fingerprinting failed.
1384    #[error(transparent)]
1385    ArtifactFingerprint(#[from] ArtifactFingerprintError),
1386    /// Prepared checkpoint source validation or acquisition failed.
1387    #[error(transparent)]
1388    CheckpointStore(#[from] eredu_checkpoint::store::StoreError),
1389    /// Requested quantization transformation is unavailable for the artifact.
1390    #[error("unsupported model quantization policy: {0}")]
1391    UnsupportedQuantizationPolicy(String),
1392    /// Requested residency mode is unavailable for the artifact.
1393    #[error("unsupported model residency policy: {0}")]
1394    UnsupportedResidencyPolicy(String),
1395    /// The general model planner cannot satisfy the resolved loader contract.
1396    #[error("model artifact requires the {0:?} loading protocol")]
1397    UnsupportedLoadingProtocol(LoadingProtocol),
1398    /// Ordinary filesystem error.
1399    #[error(transparent)]
1400    Io(#[from] std::io::Error),
1401    /// JSON configuration/header error.
1402    #[error(transparent)]
1403    Json(#[from] serde_json::Error),
1404    /// GGUF parsing/catalog error.
1405    #[error(transparent)]
1406    Gguf(#[from] eredu_gguf::Error),
1407    /// Neutral tensor catalog error.
1408    #[error(transparent)]
1409    Catalog(#[from] crate::checkpoint::CatalogError),
1410}
1411
1412#[cfg(test)]
1413mod tests {
1414    use super::*;
1415    use eredu_gguf::{GgmlType, MetadataArray, TensorInput, Writer};
1416    use std::io::Write;
1417
1418    #[test]
1419    fn artifact_identity_rejects_empty_and_duplicate_layouts() {
1420        assert!(matches!(
1421            fingerprint_artifact("", [ArtifactMemberIdentity::new("weights", 7, [1; 32])]),
1422            Err(ArtifactError::InvalidArtifactIdentity(_))
1423        ));
1424        assert!(matches!(
1425            fingerprint_artifact("model", std::iter::empty::<ArtifactMemberIdentity>()),
1426            Err(ArtifactError::InvalidArtifactIdentity(_))
1427        ));
1428        assert!(matches!(
1429            fingerprint_artifact("model", [ArtifactMemberIdentity::new("", 7, [1; 32])]),
1430            Err(ArtifactError::InvalidArtifactIdentity(_))
1431        ));
1432        assert!(matches!(
1433            fingerprint_artifact(
1434                "model",
1435                [
1436                    ArtifactMemberIdentity::new("weights", 7, [1; 32]),
1437                    ArtifactMemberIdentity::new("weights", 7, [1; 32]),
1438                ],
1439            ),
1440            Err(ArtifactError::InvalidArtifactIdentity(_))
1441        ));
1442    }
1443
1444    #[test]
1445    fn artifact_identity_is_order_and_location_independent_but_domain_and_layout_exact() {
1446        let first = tempfile::tempdir().unwrap();
1447        let relocated = tempfile::tempdir().unwrap();
1448        for directory in [first.path(), relocated.path()] {
1449            std::fs::write(directory.join("a"), b"first").unwrap();
1450            std::fs::write(directory.join("b"), b"second").unwrap();
1451        }
1452        let fingerprint = |root: &Path, domain: &str, reversed: bool| {
1453            let mut files = vec![
1454                ArtifactFile::new("decoder", root.join("a")),
1455                ArtifactFile::new("projector", root.join("b")),
1456            ];
1457            if reversed {
1458                files.reverse();
1459            }
1460            fingerprint_filesystem_artifact(domain, files).unwrap()
1461        };
1462        let identity = fingerprint(first.path(), "model", false);
1463        assert_eq!(identity, fingerprint(first.path(), "model", true));
1464        assert_eq!(identity, fingerprint(relocated.path(), "model", false));
1465        assert_ne!(identity, fingerprint(first.path(), "assistant", false));
1466        assert_ne!(
1467            identity,
1468            fingerprint_filesystem_artifact(
1469                "model",
1470                [
1471                    ArtifactFile::new("decoder-renamed", first.path().join("a")),
1472                    ArtifactFile::new("projector", first.path().join("b")),
1473                ],
1474            )
1475            .unwrap()
1476        );
1477        assert_eq!(format!("{identity:?}"), identity.to_string());
1478        assert_eq!(identity.to_string().len(), 71);
1479    }
1480
1481    #[test]
1482    fn artifact_identity_includes_exact_content_and_length() {
1483        let directory = tempfile::tempdir().unwrap();
1484        let path = directory.path().join("weights");
1485        let identify = || {
1486            fingerprint_filesystem_artifact("model", [ArtifactFile::new("weights", &path)]).unwrap()
1487        };
1488        std::fs::write(&path, b"a").unwrap();
1489        let short = identify();
1490        std::fs::write(&path, b"b").unwrap();
1491        assert_ne!(short, identify());
1492        std::fs::write(&path, b"a\0").unwrap();
1493        assert_ne!(short, identify());
1494    }
1495
1496    #[test]
1497    fn admitted_safetensors_identity_is_relocation_independent_and_content_exact() {
1498        let first = tempfile::tempdir().unwrap();
1499        let relocated = tempfile::tempdir().unwrap();
1500        write_safetensors_fixture(first.path(), "llama");
1501        write_safetensors_fixture(relocated.path(), "llama");
1502        let first_path = first.path().join("original-name.safetensors");
1503        let relocated_path = relocated.path().join("renamed.safetensors");
1504        std::fs::rename(first.path().join("model.safetensors"), &first_path).unwrap();
1505        std::fs::rename(relocated.path().join("model.safetensors"), &relocated_path).unwrap();
1506        let first_shards = SafetensorsShards::discover(&first_path).unwrap();
1507        let relocated_shards = SafetensorsShards::discover(&relocated_path).unwrap();
1508        let identify = |shards: &SafetensorsShards| {
1509            fingerprint_safetensors_artifact("speculative-test", shards).unwrap()
1510        };
1511        let original = identify(&first_shards);
1512        assert_eq!(original, identify(&relocated_shards));
1513
1514        let payload = relocated_path;
1515        let mut bytes = std::fs::read(&payload).unwrap();
1516        let last = bytes.last_mut().unwrap();
1517        *last ^= 0x01;
1518        std::fs::write(&payload, bytes).unwrap();
1519        assert_ne!(original, identify(&relocated_shards));
1520    }
1521
1522    #[test]
1523    fn admitted_gguf_identity_uses_split_roles_and_exact_content() {
1524        let first = tempfile::tempdir().unwrap();
1525        let relocated = tempfile::tempdir().unwrap();
1526        let write = |path: &Path| {
1527            let data = 1.0_f32.to_le_bytes();
1528            Writer::default()
1529                .write(
1530                    File::create(path).unwrap(),
1531                    &BTreeMap::from([(
1532                        "general.architecture".into(),
1533                        MetadataValue::String("llama".into()),
1534                    )]),
1535                    &[TensorInput {
1536                        name: "weight",
1537                        dimensions: &[1],
1538                        ggml_type: GgmlType::F32,
1539                        data: &data,
1540                    }],
1541                )
1542                .unwrap();
1543        };
1544        let first_path = first.path().join("first-name.gguf");
1545        let relocated_path = relocated.path().join("different-name.gguf");
1546        write(&first_path);
1547        write(&relocated_path);
1548        let first_checkpoint = GgufCheckpoint::open(&first_path).unwrap();
1549        let relocated_checkpoint = GgufCheckpoint::open(&relocated_path).unwrap();
1550        let original = fingerprint_gguf_artifact("speculative-test", &first_checkpoint).unwrap();
1551        assert_eq!(
1552            original,
1553            fingerprint_gguf_artifact("speculative-test", &relocated_checkpoint).unwrap()
1554        );
1555
1556        let mut bytes = std::fs::read(&relocated_path).unwrap();
1557        *bytes.last_mut().unwrap() ^= 0x01;
1558        std::fs::write(&relocated_path, bytes).unwrap();
1559        assert_ne!(
1560            original,
1561            fingerprint_gguf_artifact("speculative-test", &relocated_checkpoint).unwrap()
1562        );
1563    }
1564
1565    #[test]
1566    fn canonical_prepared_safetensors_open_rejects_substitution_without_payload_reads() {
1567        use eredu_checkpoint::{
1568            schema::{
1569                CatalogPolicy, SafetensorsCheckpointPlan, SafetensorsTensorConstraint,
1570                StoredDtypeConstraint,
1571            },
1572            store::SafetensorsWeightStore,
1573        };
1574
1575        let directory = tempfile::tempdir().unwrap();
1576        write_safetensors_fixture(directory.path(), "llama");
1577        let inspection = inspect_artifact(directory.path(), &FixtureResolver).unwrap();
1578        let shards = inspection.safetensors_shards().unwrap().clone();
1579        let plan = SafetensorsCheckpointPlan::new(
1580            "fixture",
1581            vec![SafetensorsTensorConstraint::required(
1582                "token_embd.weight",
1583                vec![2, 2],
1584                StoredDtypeConstraint::Exact(StoredDtype::F32),
1585            )],
1586            Vec::new(),
1587            CatalogPolicy::strict(),
1588        )
1589        .unwrap();
1590        let store = SafetensorsWeightStore::open_admitted(shards.clone(), 1).unwrap();
1591        let resolution =
1592            eredu_checkpoint::validation::resolve_safetensors_plan(&store, &plan).unwrap();
1593        let prepared = open_prepared_safetensors_artifact(
1594            inspection.tensors(),
1595            shards.clone(),
1596            resolution.clone(),
1597            1,
1598        )
1599        .unwrap();
1600        assert_eq!(prepared.source_diagnostics().unwrap().physical_reads, 0);
1601
1602        let header = br#"{"token_embd.weight":{"dtype":"F32","shape":[4],"data_offsets":[0,16]}}"#;
1603        let mut file = File::create(directory.path().join("model.safetensors")).unwrap();
1604        file.write_all(&(header.len() as u64).to_le_bytes())
1605            .unwrap();
1606        file.write_all(header).unwrap();
1607        file.write_all(&[0_u8; 16]).unwrap();
1608        drop(file);
1609        assert!(matches!(
1610            open_prepared_safetensors_artifact(inspection.tensors(), shards, resolution, 1,),
1611            Err(ArtifactError::CheckpointStore(
1612                eredu_checkpoint::store::StoreError::PreparedCatalogMismatch { .. }
1613            ))
1614        ));
1615    }
1616
1617    struct FixtureResolver;
1618
1619    #[derive(Debug, Clone, Default, Eq, PartialEq)]
1620    struct FixtureArtifactPlan {
1621        format: Option<ArtifactFormat>,
1622    }
1623
1624    impl ModelConfigurationResolver for FixtureResolver {
1625        type ArtifactPlan = FixtureArtifactPlan;
1626
1627        fn resolve_safetensors(
1628            &self,
1629            json: &Value,
1630        ) -> Result<ResolvedModelConfiguration<Self::ArtifactPlan>, ArtifactError> {
1631            let model_type = json
1632                .get("model_type")
1633                .and_then(Value::as_str)
1634                .ok_or_else(|| ArtifactError::InvalidArtifact("missing model_type".into()))?;
1635            let family = match model_type {
1636                "llama" => "llama",
1637                "gemma4" => "gemma4",
1638                "future" => "future_family",
1639                other => return Err(ArtifactError::UnsupportedModelType(other.into())),
1640            };
1641            Ok(ResolvedModelConfiguration::new(
1642                ModelConfiguration {
1643                    declared_model_type: model_type.into(),
1644                    effective_model_type: model_type.into(),
1645                    family: family.into(),
1646                    loading_protocol: LoadingProtocol::Model,
1647                    json: Some(json.clone()),
1648                },
1649                FixtureArtifactPlan::default(),
1650            ))
1651        }
1652
1653        fn resolve_gguf(
1654            &self,
1655            architecture: &str,
1656            _checkpoint: &GgufCheckpoint,
1657        ) -> Result<ResolvedModelConfiguration<Self::ArtifactPlan>, ArtifactError> {
1658            let family = match architecture {
1659                "llama" => "llama",
1660                "future" => "future_family",
1661                other => return Err(ArtifactError::UnsupportedGgufArchitecture(other.into())),
1662            };
1663            Ok(ResolvedModelConfiguration::new(
1664                ModelConfiguration {
1665                    declared_model_type: architecture.into(),
1666                    effective_model_type: architecture.into(),
1667                    family: family.into(),
1668                    loading_protocol: LoadingProtocol::Model,
1669                    json: None,
1670                },
1671                FixtureArtifactPlan::default(),
1672            ))
1673        }
1674
1675        fn gguf_companion_requirements(
1676            &self,
1677            architecture: &str,
1678            _checkpoint: &GgufCheckpoint,
1679        ) -> Result<Vec<GgufCompanionRequirement>, ArtifactError> {
1680            if architecture == "future" {
1681                return Ok(vec![GgufCompanionRequirement::new(
1682                    GgufCompanionRole::MediaProjector,
1683                    false,
1684                    "mmproj",
1685                    0,
1686                    GgufCompanionEncoding::DensePreferred,
1687                )?]);
1688            }
1689            Ok(Vec::new())
1690        }
1691
1692        fn artifact_plan(
1693            &self,
1694            _path: &Path,
1695            format: ArtifactFormat,
1696            _configuration: &ModelConfiguration,
1697            _tensors: &TensorCatalog,
1698            _validated_gguf: Option<&ValidatedGguf>,
1699            _resolved_plan: Self::ArtifactPlan,
1700        ) -> Result<Self::ArtifactPlan, ArtifactError> {
1701            Ok(FixtureArtifactPlan {
1702                format: Some(format),
1703            })
1704        }
1705    }
1706
1707    fn write_safetensors_fixture(root: &Path, model_type: &str) {
1708        std::fs::write(
1709            root.join("config.json"),
1710            format!(r#"{{"model_type":"{model_type}"}}"#),
1711        )
1712        .unwrap();
1713        let header =
1714            br#"{"token_embd.weight":{"dtype":"F32","shape":[2,2],"data_offsets":[0,16]}}"#;
1715        let mut file = File::create(root.join("model.safetensors")).unwrap();
1716        file.write_all(&(header.len() as u64).to_le_bytes())
1717            .unwrap();
1718        file.write_all(header).unwrap();
1719        file.write_all(&[0_u8; 16]).unwrap();
1720    }
1721
1722    fn write_gguf_fixture(path: &Path, ggml_type: GgmlType) {
1723        let metadata = BTreeMap::from([(
1724            "general.architecture".into(),
1725            MetadataValue::String("clip".into()),
1726        )]);
1727        let (dimensions, data) = match ggml_type {
1728            GgmlType::F32 => (vec![1], 1.0_f32.to_le_bytes().to_vec()),
1729            GgmlType::Q8_0 => (vec![32], vec![0_u8; 34]),
1730            other => panic!("unsupported fixture encoding {other:?}"),
1731        };
1732        Writer::default()
1733            .write(
1734                File::create(path).unwrap(),
1735                &metadata,
1736                &[TensorInput {
1737                    name: "projector.weight",
1738                    dimensions: &dimensions,
1739                    ggml_type,
1740                    data: &data,
1741                }],
1742            )
1743            .unwrap();
1744    }
1745
1746    #[test]
1747    fn companion_planning_selects_by_catalog_encoding_not_filename() {
1748        let root = tempfile::tempdir().unwrap();
1749        let primary = root.path().join("model.gguf");
1750        write_gguf_fixture(&primary, GgmlType::F32);
1751        let quantized_name = root.path().join("mmproj-f16.gguf");
1752        let dense_name = root.path().join("mmproj-q4_k.gguf");
1753        write_gguf_fixture(&quantized_name, GgmlType::Q8_0);
1754        write_gguf_fixture(&dense_name, GgmlType::F32);
1755        let requirement = GgufCompanionRequirement::new(
1756            GgufCompanionRole::MediaProjector,
1757            true,
1758            "mmproj",
1759            1,
1760            GgufCompanionEncoding::DensePreferred,
1761        )
1762        .unwrap();
1763
1764        let companions = resolve_gguf_companions(&primary, &[requirement]).unwrap();
1765
1766        assert_eq!(
1767            companions
1768                .get(&GgufCompanionRole::MediaProjector)
1769                .unwrap()
1770                .path(),
1771            dense_name
1772        );
1773    }
1774
1775    #[test]
1776    fn dense_only_and_required_companion_policies_fail_closed() {
1777        let root = tempfile::tempdir().unwrap();
1778        let primary = root.path().join("model.gguf");
1779        write_gguf_fixture(&primary, GgmlType::F32);
1780        write_gguf_fixture(&root.path().join("mmproj.gguf"), GgmlType::Q8_0);
1781        let optional = GgufCompanionRequirement::new(
1782            GgufCompanionRole::MediaProjector,
1783            false,
1784            "mmproj",
1785            0,
1786            GgufCompanionEncoding::DenseRequired,
1787        )
1788        .unwrap();
1789        assert!(resolve_gguf_companions(&primary, &[optional]).is_err());
1790        let required = GgufCompanionRequirement::new(
1791            GgufCompanionRole::MediaProjector,
1792            true,
1793            "mmproj",
1794            0,
1795            GgufCompanionEncoding::DenseRequired,
1796        )
1797        .unwrap();
1798        assert!(resolve_gguf_companions(&primary, &[required]).is_err());
1799    }
1800
1801    #[test]
1802    fn missing_required_companion_preserves_its_semantic_role() {
1803        let root = tempfile::tempdir().unwrap();
1804        let primary = root.path().join("model.gguf");
1805        write_gguf_fixture(&primary, GgmlType::F32);
1806        let requirement = GgufCompanionRequirement::new(
1807            GgufCompanionRole::MediaProjector,
1808            true,
1809            "mmproj",
1810            0,
1811            GgufCompanionEncoding::DensePreferred,
1812        )
1813        .unwrap();
1814
1815        let error = resolve_gguf_companions(&primary, &[requirement]).unwrap_err();
1816
1817        assert!(matches!(
1818            error,
1819            ArtifactError::MissingRequiredGgufCompanion {
1820                role: GgufCompanionRole::MediaProjector,
1821                filename_prefix,
1822                searched_directories,
1823            } if filename_prefix == "mmproj" && searched_directories == [root.path()]
1824        ));
1825    }
1826
1827    #[test]
1828    fn loading_protocol_is_family_agnostic() {
1829        assert!(matches!(
1830            validate_preparation_policy(LoadingProtocol::Realtime, PreparationPolicy::default()),
1831            Err(ArtifactError::UnsupportedLoadingProtocol(
1832                LoadingProtocol::Realtime
1833            ))
1834        ));
1835    }
1836
1837    #[test]
1838    fn gguf_u32_metadata_is_lossless_and_fail_closed() {
1839        let values = MetadataValue::Array(MetadataArray::Uint64(vec![0, u32::MAX.into()]));
1840        assert_eq!(
1841            gguf_u32_metadata_values("tokenizer.ids", Some(&values)).unwrap(),
1842            vec![0, u32::MAX]
1843        );
1844        assert!(gguf_u32_metadata_values(
1845            "tokenizer.ids",
1846            Some(&MetadataValue::Uint64(u64::from(u32::MAX) + 1))
1847        )
1848        .is_err());
1849        assert!(
1850            gguf_u32_metadata_values("tokenizer.ids", Some(&MetadataValue::Int32(-1))).is_err()
1851        );
1852        assert!(gguf_u32_metadata_values(
1853            "tokenizer.ids",
1854            Some(&MetadataValue::String("1".into()))
1855        )
1856        .is_err());
1857        assert!(gguf_u32_metadata_values("tokenizer.ids", None)
1858            .unwrap()
1859            .is_empty());
1860    }
1861
1862    #[test]
1863    fn safetensors_inspection_and_planning_are_backend_neutral() {
1864        let root = tempfile::tempdir().unwrap();
1865        write_safetensors_fixture(root.path(), "llama");
1866        let inspection = inspect_artifact(root.path(), &FixtureResolver).unwrap();
1867        assert_eq!(inspection.configuration().family, "llama");
1868        assert_eq!(inspection.tensors().len(), 1);
1869        assert_eq!(
1870            inspection
1871                .safetensors_shards()
1872                .unwrap()
1873                .payload_paths()
1874                .len(),
1875            1
1876        );
1877        let plan = plan_model_preparation(
1878            inspection,
1879            PreparationPolicy::default(),
1880            crate::backend::SessionCapabilities::default(),
1881        )
1882        .unwrap();
1883        assert_eq!(plan.route(), MaterializationRoute::Resident);
1884        let architecture_plan = plan.inspection().architecture_plan().clone();
1885        let artifact = plan.into_artifact();
1886        let ModelArtifact::SafeTensors { shards, .. } = artifact else {
1887            panic!("expected SafeTensors artifact");
1888        };
1889        assert_eq!(shards.payload_paths().len(), 1);
1890        assert_eq!(architecture_plan.format, Some(ArtifactFormat::SafeTensors));
1891    }
1892
1893    #[test]
1894    fn safetensors_inspection_rejects_index_entries_missing_from_their_shard() {
1895        let root = tempfile::tempdir().unwrap();
1896        write_safetensors_fixture(root.path(), "llama");
1897        std::fs::rename(
1898            root.path().join("model.safetensors"),
1899            root.path().join("model-00001.safetensors"),
1900        )
1901        .unwrap();
1902        std::fs::write(
1903            root.path().join("model.safetensors.index.json"),
1904            r#"{"weight_map":{"missing.weight":"model-00001.safetensors"}}"#,
1905        )
1906        .unwrap();
1907
1908        assert!(matches!(
1909            inspect_artifact(root.path(), &FixtureResolver),
1910            Err(ArtifactError::SafetensorsShards(
1911                eredu_checkpoint::safetensors::SafetensorsShardError::MalformedIndex { .. }
1912            ))
1913        ));
1914    }
1915
1916    #[cfg(unix)]
1917    #[test]
1918    fn safetensors_inspection_rejects_indexed_symlinks_outside_the_access_root() {
1919        use std::os::unix::fs::symlink;
1920
1921        let parent = tempfile::tempdir().unwrap();
1922        let outside = parent.path().join("outside");
1923        std::fs::create_dir(&outside).unwrap();
1924        write_safetensors_fixture(&outside, "llama");
1925
1926        let checkpoint = parent.path().join("checkpoint");
1927        std::fs::create_dir(&checkpoint).unwrap();
1928        std::fs::write(checkpoint.join("config.json"), r#"{"model_type":"llama"}"#).unwrap();
1929        symlink(
1930            outside.join("model.safetensors"),
1931            checkpoint.join("model-00001.safetensors"),
1932        )
1933        .unwrap();
1934        std::fs::write(
1935            checkpoint.join("model.safetensors.index.json"),
1936            r#"{"weight_map":{"token_embd.weight":"model-00001.safetensors"}}"#,
1937        )
1938        .unwrap();
1939
1940        assert!(matches!(
1941            inspect_artifact(&checkpoint, &FixtureResolver),
1942            Err(ArtifactError::SafetensorsShards(
1943                eredu_checkpoint::safetensors::SafetensorsShardError::UnsafeShardPath { .. }
1944            ))
1945        ));
1946    }
1947
1948    #[test]
1949    fn safetensors_native_dtypes_remain_typed_in_the_portable_catalog() {
1950        assert_eq!(safetensors_dtype("BOOL"), TensorDtype::Bool);
1951        assert_eq!(safetensors_dtype("I64"), TensorDtype::I64);
1952        assert_eq!(safetensors_dtype("U32"), TensorDtype::U32);
1953        assert_eq!(safetensors_dtype("F64"), TensorDtype::F64);
1954        assert_eq!(safetensors_dtype("C64"), TensorDtype::Complex64);
1955        assert_eq!(
1956            safetensors_dtype("F8_E4M3"),
1957            TensorDtype::Encoded("F8_E4M3".into())
1958        );
1959    }
1960
1961    #[test]
1962    fn gguf_dense_dtypes_remain_typed_in_the_portable_catalog() {
1963        assert_eq!(gguf_dtype(GgmlType::F16), TensorDtype::F16);
1964        assert_eq!(gguf_dtype(GgmlType::Bf16), TensorDtype::Bf16);
1965        assert_eq!(gguf_dtype(GgmlType::F32), TensorDtype::F32);
1966        assert_eq!(
1967            gguf_dtype(GgmlType::Q4K),
1968            TensorDtype::Encoded("Q4K".into())
1969        );
1970    }
1971
1972    #[test]
1973    fn core_accepts_families_defined_only_by_the_resolver() {
1974        let root = tempfile::tempdir().unwrap();
1975        write_safetensors_fixture(root.path(), "future");
1976        let inspection = inspect_artifact(root.path(), &FixtureResolver).unwrap();
1977        assert_eq!(inspection.configuration().family, "future_family");
1978        assert_eq!(
1979            inspection.configuration().loading_protocol,
1980            LoadingProtocol::Model
1981        );
1982        assert!(plan_model_preparation(
1983            inspection,
1984            PreparationPolicy::default(),
1985            crate::backend::SessionCapabilities::default(),
1986        )
1987        .is_ok());
1988    }
1989
1990    #[test]
1991    fn safetensors_inspection_accepts_rank_zero_scalar_parameters() {
1992        let root = tempfile::tempdir().unwrap();
1993        std::fs::write(
1994            root.path().join("config.json"),
1995            r#"{"model_type":"gemma4"}"#,
1996        )
1997        .unwrap();
1998        let header = br#"{"clip.output_max":{"dtype":"F32","shape":[],"data_offsets":[0,4]}}"#;
1999        let mut file = File::create(root.path().join("model.safetensors")).unwrap();
2000        file.write_all(&(header.len() as u64).to_le_bytes())
2001            .unwrap();
2002        file.write_all(header).unwrap();
2003        file.write_all(&0.0_f32.to_le_bytes()).unwrap();
2004
2005        let inspection = inspect_artifact(root.path(), &FixtureResolver).unwrap();
2006        assert_eq!(
2007            inspection.tensors().get("clip.output_max").unwrap().shape,
2008            Vec::<usize>::new()
2009        );
2010    }
2011
2012    #[test]
2013    fn parallel_policy_binds_the_exact_neutral_topology() {
2014        let root = tempfile::tempdir().unwrap();
2015        write_safetensors_fixture(root.path(), "llama");
2016        let topology = crate::topology::ParallelTopology::new(2, 3, 4, 1).unwrap();
2017        let policy = PreparationPolicy {
2018            topology: Some(topology),
2019            ..PreparationPolicy::default()
2020        };
2021
2022        let plan = plan_model_preparation(
2023            inspect_artifact(root.path(), &FixtureResolver).unwrap(),
2024            policy,
2025            crate::backend::SessionCapabilities::default(),
2026        )
2027        .unwrap();
2028
2029        assert_eq!(plan.policy(), policy);
2030        assert_eq!(plan.policy().topology, Some(topology));
2031        assert_eq!(plan.route(), MaterializationRoute::Resident);
2032    }
2033
2034    #[test]
2035    fn policy_leaves_expert_cache_capability_to_architecture_and_backend() {
2036        let root = tempfile::tempdir().unwrap();
2037        write_safetensors_fixture(root.path(), "llama");
2038        let plan = plan_model_preparation(
2039            inspect_artifact(root.path(), &FixtureResolver).unwrap(),
2040            PreparationPolicy {
2041                residency: ResidencyRequest::AddressableParameterBanks,
2042                ..PreparationPolicy::default()
2043            },
2044            crate::backend::SessionCapabilities::default(),
2045        )
2046        .unwrap();
2047        assert_eq!(
2048            plan.route(),
2049            MaterializationRoute::AddressableParameterBanks
2050        );
2051    }
2052
2053    #[test]
2054    fn policy_leaves_nonresident_quantization_capability_to_architecture_and_backend() {
2055        let root = tempfile::tempdir().unwrap();
2056        write_safetensors_fixture(root.path(), "llama");
2057        let policy = PreparationPolicy {
2058            quantization: Some(QuantizationRequest::MxFp4),
2059            residency: ResidencyRequest::LayerwiseHost,
2060            ..PreparationPolicy::default()
2061        };
2062        let plan = plan_model_preparation(
2063            inspect_artifact(root.path(), &FixtureResolver).unwrap(),
2064            policy,
2065            crate::backend::SessionCapabilities::default(),
2066        )
2067        .unwrap();
2068        assert_eq!(plan.policy(), policy);
2069        assert_eq!(plan.route(), MaterializationRoute::Layerwise);
2070    }
2071
2072    #[test]
2073    fn gguf_plan_owns_the_portable_checkpoint_for_later_materialization() {
2074        let root = tempfile::tempdir().unwrap();
2075        let path = root.path().join("model.gguf");
2076        let data = [0_u8; 2];
2077        let metadata = BTreeMap::from([
2078            (
2079                "general.architecture".into(),
2080                MetadataValue::String("llama".into()),
2081            ),
2082            ("llama.block_count".into(), MetadataValue::Uint32(1)),
2083            ("llama.embedding_length".into(), MetadataValue::Uint32(1)),
2084        ]);
2085        Writer::default()
2086            .write(
2087                File::create(&path).unwrap(),
2088                &metadata,
2089                &[TensorInput {
2090                    name: "token_embd.weight",
2091                    dimensions: &[1],
2092                    ggml_type: GgmlType::F16,
2093                    data: &data,
2094                }],
2095            )
2096            .unwrap();
2097
2098        let inspection = inspect_artifact(&path, &FixtureResolver).unwrap();
2099        let validated = inspection.validated_gguf().unwrap();
2100        assert_eq!(validated.checkpoint().physical_tensor_count(), 1);
2101        assert_eq!(inspection.configuration().declared_model_type, "llama");
2102        assert_eq!(
2103            inspection.tensors().get("token_embd.weight").unwrap().dtype,
2104            TensorDtype::F16
2105        );
2106
2107        let plan = plan_model_preparation(
2108            inspection,
2109            PreparationPolicy::default(),
2110            crate::backend::SessionCapabilities::default(),
2111        )
2112        .unwrap();
2113        let architecture_plan = plan.inspection().architecture_plan().clone();
2114        let artifact = plan.into_artifact();
2115        let ModelArtifact::Gguf {
2116            configuration,
2117            validated,
2118            ..
2119        } = artifact
2120        else {
2121            panic!("expected GGUF artifact");
2122        };
2123        assert_eq!(architecture_plan.format, Some(ArtifactFormat::Gguf));
2124        assert_eq!(configuration.family, "llama");
2125        assert_eq!(validated.checkpoint().physical_tensor_count(), 1);
2126    }
2127
2128    #[test]
2129    fn core_accepts_architecture_owned_gguf_schema() {
2130        let root = tempfile::tempdir().unwrap();
2131        let path = root.path().join("model.gguf");
2132        let data = 1.0_f32.to_le_bytes();
2133        let metadata = BTreeMap::from([
2134            (
2135                "general.architecture".into(),
2136                MetadataValue::String("future".into()),
2137            ),
2138            ("future.state_width".into(), MetadataValue::Uint32(1)),
2139        ]);
2140        Writer::default()
2141            .write(
2142                File::create(&path).unwrap(),
2143                &metadata,
2144                &[TensorInput {
2145                    name: "state.in_proj",
2146                    dimensions: &[1],
2147                    ggml_type: GgmlType::F32,
2148                    data: &data,
2149                }],
2150            )
2151            .unwrap();
2152
2153        let inspection = inspect_artifact(&path, &FixtureResolver).unwrap();
2154
2155        assert_eq!(inspection.configuration().family, "future_family");
2156        assert!(inspection.tensors().get("state.in_proj").is_some());
2157    }
2158
2159    #[test]
2160    fn preparation_plan_carries_the_exact_inspected_companion() {
2161        let root = tempfile::tempdir().unwrap();
2162        let primary = root.path().join("model.gguf");
2163        let scalar = 1.0_f32.to_le_bytes();
2164        Writer::default()
2165            .write(
2166                File::create(&primary).unwrap(),
2167                &BTreeMap::from([(
2168                    "general.architecture".into(),
2169                    MetadataValue::String("future".into()),
2170                )]),
2171                &[TensorInput {
2172                    name: "state.in_proj",
2173                    dimensions: &[1],
2174                    ggml_type: GgmlType::F32,
2175                    data: &scalar,
2176                }],
2177            )
2178            .unwrap();
2179        let projector = root.path().join("mmproj.gguf");
2180        write_gguf_fixture(&projector, GgmlType::F32);
2181
2182        let inspection = inspect_artifact(&primary, &FixtureResolver).unwrap();
2183        assert_eq!(
2184            inspection
2185                .validated_gguf()
2186                .unwrap()
2187                .companion(&GgufCompanionRole::MediaProjector)
2188                .unwrap()
2189                .path(),
2190            projector
2191        );
2192        let ModelArtifact::Gguf { validated, .. } = plan_model_preparation(
2193            inspection,
2194            PreparationPolicy::default(),
2195            crate::backend::SessionCapabilities::default(),
2196        )
2197        .unwrap()
2198        .into_artifact() else {
2199            panic!("expected GGUF preparation artifact");
2200        };
2201        assert_eq!(
2202            validated
2203                .companion(&GgufCompanionRole::MediaProjector)
2204                .unwrap()
2205                .path(),
2206            projector
2207        );
2208    }
2209}