Skip to main content

ferrum_interfaces/vnext/
model.rs

1use serde::de::DeserializeOwned;
2use serde::{Deserialize, Deserializer, Serialize};
3use sha2::{Digest, Sha256};
4use std::collections::{BTreeMap, BTreeSet};
5use std::sync::Arc;
6
7mod checkpoint;
8pub use checkpoint::{
9    CheckpointInputDependency, ProgramCheckpointInputs, StateCheckpointCapability,
10    StateCheckpointContents, StateCheckpointContract, PROGRAM_CHECKPOINT_INPUTS_VERSION,
11    STATE_CHECKPOINT_CONTRACT_VERSION,
12};
13
14use super::{
15    checked_elements, physical_component_ids, validate_physical_layout_budget, AttributeId,
16    AxisWeightComponent, BlockQuantizationSpec, CanonicalRational, CompositeWeightPart,
17    ContractVersion, ElementType, ExternalModelMetadataId, FamilyNumericalProfiles,
18    HadamardApplication, HadamardSigns, ModelFamilyId, NodeId, NumericalExecutionProfile,
19    NumericalProfileId, OperationId, PhysicalStorageLayout, PhysicalWeightComponentBinding,
20    PhysicalWeightLayout, PhysicalWeightPadding, ProgramValueId, QuantizationGrouping,
21    QuantizationPacking, QuantizationSpec, ResolvedTensorLayout, ResolvedWeightBinding,
22    ResolvedWeightComponentLayout, ResolvedWeightLogicalValidation, SemanticValue, StateId,
23    StateInitialization, TokenizerId, VNextError, WeightComponentRole, WeightEncoding,
24    WeightFormatId, WeightId, WeightLayoutId, MAX_PHYSICAL_WEIGHT_LAYOUT_DEPTH,
25    MAX_PHYSICAL_WEIGHT_LAYOUT_NODES,
26};
27
28#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
29pub struct WeightComponentSpec {
30    pub id: WeightId,
31    pub role: WeightComponentRole,
32    /// Ordered checkpoint tensors forming this physical component. Dense
33    /// multi-source components use `[source_count, source_shape...]` and stack
34    /// tensors in this exact order. Format adapters may instead define a typed
35    /// source recipe (for example values plus quantization sidecars), but must
36    /// validate every source identity, order, shape, and resulting byte count.
37    /// Aliases are resolved before the model family constructs this schema.
38    pub external_names: Vec<String>,
39    pub dimensions: Vec<u64>,
40    pub encoding: WeightEncoding,
41    pub required: bool,
42}
43
44impl WeightComponentSpec {
45    /// Exact bytes occupied by this physical component. Separate-component
46    /// quantized dimensions are byte dimensions. Block-quantized dimensions
47    /// are block-grid dimensions and are multiplied by the block ABI size.
48    /// Logical element counts live on `WeightTensorSpec` and are checked
49    /// against the corresponding physical layout contract.
50    pub fn physical_bytes(&self) -> Result<u64, VNextError> {
51        self.encoding.physical_bytes(&self.dimensions, &self.id)
52    }
53
54    pub fn dense_element_type(&self) -> Option<ElementType> {
55        self.encoding.dense_element_type()
56    }
57
58    pub fn physical_element_type(&self) -> ElementType {
59        self.dense_element_type().unwrap_or(ElementType::U8)
60    }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
64pub struct PhysicalWeightComponentRef {
65    pub component_id: WeightId,
66    pub physical_dimensions: Vec<u64>,
67    pub resource_bytes: u64,
68    pub element_type: ElementType,
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
72pub struct WeightTensorSpec {
73    pub id: WeightId,
74    pub dimensions: Vec<u64>,
75    /// Dtype observed by the semantic program after decoding any physical
76    /// packing, indexing, or quantization components.
77    pub logical_element_type: ElementType,
78    pub physical_layout: PhysicalWeightLayout,
79    pub required: bool,
80}
81
82impl WeightTensorSpec {
83    pub fn logical_elements(&self) -> Result<u64, VNextError> {
84        checked_elements(&self.dimensions).ok_or_else(|| VNextError::InvalidExecutionPlan {
85            reason: format!("logical weight `{}` element count overflows u64", self.id),
86        })
87    }
88
89    pub fn logical_bytes(&self) -> Result<u64, VNextError> {
90        self.logical_elements()?
91            .checked_mul(self.logical_element_type.size_bytes())
92            .ok_or_else(|| VNextError::InvalidExecutionPlan {
93                reason: format!("logical weight `{}` byte size overflows u64", self.id),
94            })
95    }
96}
97
98#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
99pub struct WeightSchema {
100    pub format_id: WeightFormatId,
101    pub layout_id: WeightLayoutId,
102    pub version: ContractVersion,
103    pub components: Vec<WeightComponentSpec>,
104    pub tensors: Vec<WeightTensorSpec>,
105}
106
107impl WeightSchema {
108    pub(crate) fn normalize(&mut self) {
109        self.components
110            .sort_by(|left, right| left.id.cmp(&right.id));
111        for tensor in &mut self.tensors {
112            tensor.physical_layout.normalize();
113        }
114        self.tensors.sort_by(|left, right| left.id.cmp(&right.id));
115    }
116
117    pub fn validate(&self, family_id: &ModelFamilyId) -> Result<(), VNextError> {
118        if self.version.major == 0 || self.components.is_empty() || self.tensors.is_empty() {
119            return Err(VNextError::UnknownWeightLayout {
120                family_id: family_id.to_string(),
121                layout_id: self.layout_id.to_string(),
122            });
123        }
124        for tensor in &self.tensors {
125            validate_physical_layout_budget(&tensor.physical_layout).map_err(|reason| {
126                VNextError::InvalidModelConfig {
127                    family_id: family_id.to_string(),
128                    field: format!("weight_schema.tensors.{}.physical_layout", tensor.id),
129                    reason,
130                }
131            })?;
132        }
133        let mut component_ids = BTreeSet::new();
134        let mut names = BTreeSet::new();
135        let mut components = BTreeMap::new();
136        let mut quantization_abis = BTreeMap::new();
137        for component in &self.components {
138            if !component_ids.insert(component.id.clone())
139                || component.external_names.is_empty()
140                || component.dimensions.is_empty()
141                || component
142                    .external_names
143                    .iter()
144                    .any(|name| name.trim().is_empty() || !names.insert(name.clone()))
145                || component.dimensions.iter().any(|extent| *extent == 0)
146            {
147                return Err(VNextError::InvalidModelConfig {
148                    family_id: family_id.to_string(),
149                    field: "weight_schema.components".to_owned(),
150                    reason: "component identities, names, and dimensions must be valid and unique"
151                        .to_owned(),
152                });
153            }
154            if let WeightEncoding::Quantized(quantization) = &component.encoding {
155                quantization.validate()?;
156            }
157            if let WeightEncoding::BlockQuantized(quantization) = &component.encoding {
158                quantization.validate()?;
159            }
160            let quantization_format = match &component.encoding {
161                WeightEncoding::Quantized(spec) => Some(&spec.format_id),
162                WeightEncoding::BlockQuantized(spec) => Some(&spec.format_id),
163                WeightEncoding::Dense { .. } | WeightEncoding::DenseAffine { .. } => None,
164            };
165            if let Some(format_id) = quantization_format {
166                if let Some(existing) = quantization_abis.get(format_id) {
167                    if existing != &component.encoding {
168                        return Err(VNextError::InvalidModelConfig {
169                            family_id: family_id.to_string(),
170                            field: "weight_schema.components.encoding".to_owned(),
171                            reason: format!(
172                                "quantization format `{format_id}` maps to conflicting physical ABIs"
173                            ),
174                        });
175                    }
176                } else {
177                    quantization_abis.insert(format_id.clone(), component.encoding.clone());
178                }
179            }
180            if let WeightEncoding::DenseAffine { element_type, .. } = &component.encoding {
181                if component.role != WeightComponentRole::Values
182                    || !matches!(
183                        element_type,
184                        ElementType::F16 | ElementType::Bf16 | ElementType::F32
185                    )
186                {
187                    return Err(VNextError::InvalidModelConfig {
188                        family_id: family_id.to_string(),
189                        field: "weight_schema.components.encoding".to_owned(),
190                        reason: format!(
191                            "affine dense component `{}` must be a floating-point value component",
192                            component.id
193                        ),
194                    });
195                }
196            }
197            component
198                .physical_bytes()
199                .map_err(|error| VNextError::InvalidModelConfig {
200                    family_id: family_id.to_string(),
201                    field: "weight_schema.components.dimensions".to_owned(),
202                    reason: error.to_string(),
203                })?;
204            let role_encoding_valid = match component.role {
205                WeightComponentRole::TransformSigns => {
206                    component.dimensions.len() == 1
207                        && matches!(
208                            component.encoding,
209                            WeightEncoding::Dense {
210                                element_type: ElementType::F32
211                            }
212                        )
213                }
214                WeightComponentRole::Scales => matches!(
215                    component.encoding,
216                    WeightEncoding::Dense {
217                        element_type: ElementType::U8
218                            | ElementType::F16
219                            | ElementType::Bf16
220                            | ElementType::F32
221                    }
222                ),
223                WeightComponentRole::ZeroPoints
224                | WeightComponentRole::Indices
225                | WeightComponentRole::Permutation => matches!(
226                    component.encoding,
227                    WeightEncoding::Dense {
228                        element_type: ElementType::U8
229                            | ElementType::U32
230                            | ElementType::I8
231                            | ElementType::I32
232                    }
233                ),
234                WeightComponentRole::PackedValues => {
235                    matches!(
236                        component.encoding,
237                        WeightEncoding::Quantized(_) | WeightEncoding::BlockQuantized(_)
238                    )
239                }
240                _ => true,
241            };
242            if !role_encoding_valid {
243                return Err(VNextError::InvalidModelConfig {
244                    family_id: family_id.to_string(),
245                    field: "weight_schema.components.encoding".to_owned(),
246                    reason: format!(
247                        "component `{}` encoding is incompatible with its structural role",
248                        component.id
249                    ),
250                });
251            }
252            components.insert(component.id.clone(), component);
253        }
254
255        let mut tensor_ids = BTreeSet::new();
256        let mut referenced_components = BTreeSet::new();
257        for tensor in &self.tensors {
258            if !tensor_ids.insert(tensor.id.clone())
259                || tensor.dimensions.is_empty()
260                || tensor.dimensions.iter().any(|extent| *extent == 0)
261            {
262                return Err(VNextError::InvalidModelConfig {
263                    family_id: family_id.to_string(),
264                    field: "weight_schema.tensors".to_owned(),
265                    reason: "logical weight identities and dimensions must be valid and unique"
266                        .to_owned(),
267                });
268            }
269            self.validate_physical_layout(
270                family_id,
271                tensor,
272                &components,
273                &mut referenced_components,
274            )?;
275        }
276        if let Some(component) = self
277            .components
278            .iter()
279            .find(|component| component.required && !referenced_components.contains(&component.id))
280        {
281            return Err(VNextError::InvalidModelConfig {
282                family_id: family_id.to_string(),
283                field: "weight_schema.components".to_owned(),
284                reason: format!(
285                    "required component `{}` is not referenced by a logical weight",
286                    component.id
287                ),
288            });
289        }
290        Ok(())
291    }
292
293    pub fn fingerprint(&self) -> Result<String, VNextError> {
294        let bytes = serde_json::to_vec(self).map_err(|error| VNextError::Serialization {
295            context: "fingerprint weight schema",
296            message: error.to_string(),
297        })?;
298        Ok(format!("{:x}", Sha256::digest(bytes)))
299    }
300
301    pub fn quantization_formats(&self) -> BTreeSet<super::QuantizationFormatId> {
302        self.components
303            .iter()
304            .filter_map(|component| match &component.encoding {
305                WeightEncoding::Quantized(spec) => Some(spec.format_id.clone()),
306                WeightEncoding::BlockQuantized(spec) => Some(spec.format_id.clone()),
307                WeightEncoding::Dense { .. } | WeightEncoding::DenseAffine { .. } => None,
308            })
309            .collect()
310    }
311
312    fn validate_physical_layout(
313        &self,
314        family_id: &ModelFamilyId,
315        tensor: &WeightTensorSpec,
316        components: &BTreeMap<WeightId, &WeightComponentSpec>,
317        referenced: &mut BTreeSet<WeightId>,
318    ) -> Result<(), VNextError> {
319        let mut validator = PhysicalLayoutValidator {
320            family_id,
321            tensor_id: &tensor.id,
322            components,
323            referenced,
324            visited_nodes: 0,
325            hadamard_active: false,
326        };
327        validator.validate_layout(
328            &tensor.physical_layout,
329            &tensor.dimensions,
330            tensor.logical_element_type,
331            1,
332        )
333    }
334
335    pub fn tensor(&self, weight_id: &WeightId) -> Option<&WeightTensorSpec> {
336        self.tensors.iter().find(|tensor| &tensor.id == weight_id)
337    }
338
339    /// Ordered physical components needed to materialize one logical value.
340    /// The order is the schema's component order and is therefore stable for
341    /// fingerprints and resource-plan evidence.
342    pub fn physical_component_refs(
343        &self,
344        weight_id: &WeightId,
345    ) -> Result<Vec<&WeightComponentSpec>, VNextError> {
346        let tensor = self
347            .tensor(weight_id)
348            .ok_or_else(|| VNextError::InvalidExecutionPlan {
349                reason: format!("unknown logical weight `{weight_id}`"),
350            })?;
351        let required = physical_component_ids(&tensor.physical_layout).map_err(|reason| {
352            VNextError::InvalidExecutionPlan {
353                reason: format!(
354                    "logical weight `{weight_id}` has invalid physical layout: {reason}"
355                ),
356            }
357        })?;
358        let result = self
359            .components
360            .iter()
361            .filter(|component| required.contains(&component.id))
362            .collect::<Vec<_>>();
363        if result.len() != required.len() {
364            return Err(VNextError::InvalidExecutionPlan {
365                reason: format!("logical weight `{weight_id}` references an unknown component"),
366            });
367        }
368        Ok(result)
369    }
370
371    pub fn physical_bytes(&self, weight_id: &WeightId) -> Result<u64, VNextError> {
372        self.physical_component_refs(weight_id)?
373            .into_iter()
374            .try_fold(0_u64, |total, component| {
375                total
376                    .checked_add(component.physical_bytes()?)
377                    .ok_or_else(|| VNextError::InvalidExecutionPlan {
378                        reason: format!("logical weight `{weight_id}` physical bytes overflow u64"),
379                    })
380            })
381    }
382
383    pub fn physical_resource_requirements(
384        &self,
385        weight_id: &WeightId,
386    ) -> Result<Vec<PhysicalWeightComponentRef>, VNextError> {
387        self.physical_component_refs(weight_id)?
388            .into_iter()
389            .map(|component| {
390                Ok(PhysicalWeightComponentRef {
391                    component_id: component.id.clone(),
392                    physical_dimensions: component.dimensions.clone(),
393                    resource_bytes: component.physical_bytes()?,
394                    element_type: component.physical_element_type(),
395                })
396            })
397            .collect()
398    }
399}
400
401impl ResolvedWeightBinding {
402    pub fn from_schema(schema: &WeightSchema, weight_id: &WeightId) -> Result<Self, VNextError> {
403        let tensor = schema
404            .tensor(weight_id)
405            .ok_or_else(|| VNextError::InvalidExecutionPlan {
406                reason: format!("unknown logical weight `{weight_id}`"),
407            })?;
408        let mut components = schema
409            .physical_component_refs(weight_id)?
410            .into_iter()
411            .map(|component| {
412                ResolvedWeightComponentLayout::from_parts(
413                    component.id.clone(),
414                    component.role,
415                    component.dimensions.clone(),
416                    component.encoding.clone(),
417                )
418            })
419            .collect::<Vec<_>>();
420        components.sort_by(|left, right| left.component_id().cmp(right.component_id()));
421        let binding = Self::from_parts(
422            weight_id.clone(),
423            schema.format_id.clone(),
424            schema.layout_id.clone(),
425            schema.version,
426            tensor.physical_layout.clone(),
427            components,
428        )?;
429        binding.validate_logical(&tensor.dimensions, tensor.logical_element_type)?;
430        Ok(binding)
431    }
432
433    pub fn validate_logical(
434        &self,
435        logical_dimensions: &[u64],
436        logical_element_type: ElementType,
437    ) -> Result<(), VNextError> {
438        ResolvedWeightLogicalValidation::validate_logical_contract(
439            self,
440            logical_dimensions,
441            logical_element_type,
442        )
443    }
444}
445
446impl ResolvedWeightLogicalValidation for ResolvedWeightBinding {
447    fn validate_logical_contract(
448        &self,
449        logical_dimensions: &[u64],
450        logical_element_type: ElementType,
451    ) -> Result<(), VNextError> {
452        self.validate_structure()?;
453        let schema = WeightSchema {
454            format_id: self.schema_format_id().clone(),
455            layout_id: self.layout_id().clone(),
456            version: self.schema_version(),
457            components: self
458                .components()
459                .iter()
460                .map(|component| WeightComponentSpec {
461                    id: component.component_id().clone(),
462                    role: component.role(),
463                    external_names: vec![format!("resolved.{}", component.component_id())],
464                    dimensions: component.physical_dimensions().to_vec(),
465                    encoding: component.encoding().clone(),
466                    required: true,
467                })
468                .collect(),
469            tensors: vec![WeightTensorSpec {
470                id: self.weight_id().clone(),
471                dimensions: logical_dimensions.to_vec(),
472                logical_element_type,
473                physical_layout: self.physical_layout().clone(),
474                required: true,
475            }],
476        };
477        schema.validate(&ModelFamilyId::new("family.resolved-weight-binding")?)
478    }
479}
480
481struct PhysicalLayoutValidator<'schema, 'references> {
482    family_id: &'schema ModelFamilyId,
483    tensor_id: &'schema WeightId,
484    components: &'schema BTreeMap<WeightId, &'schema WeightComponentSpec>,
485    referenced: &'references mut BTreeSet<WeightId>,
486    visited_nodes: usize,
487    hadamard_active: bool,
488}
489
490impl<'schema, 'references> PhysicalLayoutValidator<'schema, 'references> {
491    fn invalid(&self, reason: impl Into<String>) -> VNextError {
492        VNextError::InvalidModelConfig {
493            family_id: self.family_id.to_string(),
494            field: format!("weight_schema.tensors.{}.physical_layout", self.tensor_id),
495            reason: reason.into(),
496        }
497    }
498
499    fn visit_node(&mut self, depth: usize) -> Result<(), VNextError> {
500        if depth > MAX_PHYSICAL_WEIGHT_LAYOUT_DEPTH {
501            return Err(self.invalid(format!(
502                "physical layout depth exceeds {MAX_PHYSICAL_WEIGHT_LAYOUT_DEPTH}"
503            )));
504        }
505        self.visited_nodes = self
506            .visited_nodes
507            .checked_add(1)
508            .ok_or_else(|| self.invalid("physical layout node count overflows usize"))?;
509        if self.visited_nodes > MAX_PHYSICAL_WEIGHT_LAYOUT_NODES {
510            return Err(self.invalid(format!(
511                "physical layout node count exceeds {MAX_PHYSICAL_WEIGHT_LAYOUT_NODES}"
512            )));
513        }
514        Ok(())
515    }
516
517    fn component(
518        &self,
519        component_id: &WeightId,
520    ) -> Result<&'schema WeightComponentSpec, VNextError> {
521        self.components
522            .get(component_id)
523            .copied()
524            .ok_or_else(|| self.invalid(format!("unknown component `{component_id}`")))
525    }
526
527    fn bind_component(
528        &mut self,
529        binding: &PhysicalWeightComponentBinding,
530        semantic_dimensions: &[u64],
531        role: WeightComponentRole,
532        depth: usize,
533    ) -> Result<&'schema WeightComponentSpec, VNextError> {
534        self.visit_node(depth)?;
535        let component = self.component(&binding.component_id)?;
536        if component.role != role {
537            return Err(self.invalid(format!(
538                "component `{}` has role {:?}, expected {:?}",
539                component.id, component.role, role
540            )));
541        }
542        self.validate_storage(component, semantic_dimensions, &binding.storage)?;
543        if !self.referenced.insert(component.id.clone()) {
544            return Err(self.invalid(format!(
545                "component `{}` is referenced more than once in the physical layout tree",
546                component.id
547            )));
548        }
549        Ok(component)
550    }
551
552    fn validate_storage(
553        &self,
554        component: &WeightComponentSpec,
555        semantic_dimensions: &[u64],
556        storage: &PhysicalStorageLayout,
557    ) -> Result<(), VNextError> {
558        if semantic_dimensions.is_empty()
559            || semantic_dimensions.iter().any(|extent| *extent == 0)
560            || checked_elements(semantic_dimensions).is_none()
561        {
562            return Err(self.invalid(format!(
563                "component `{}` has an invalid or overflowing semantic shape",
564                component.id
565            )));
566        }
567        let raw_elements = checked_elements(&component.dimensions).ok_or_else(|| {
568            self.invalid(format!(
569                "component `{}` raw storage shape overflows u64",
570                component.id
571            ))
572        })?;
573        match storage {
574            PhysicalStorageLayout::Contiguous { padding } => {
575                let padded = self.resolve_padding(semantic_dimensions, padding)?;
576                if component.dimensions != padded {
577                    return Err(self.invalid(format!(
578                        "component `{}` contiguous shape {:?} differs from its explicit physical shape {:?}",
579                        component.id, padded, component.dimensions
580                    )));
581                }
582            }
583            PhysicalStorageLayout::Strided {
584                strides_in_elements,
585                padding,
586            } => {
587                let padded = self.resolve_padding(semantic_dimensions, padding)?;
588                let span = self.checked_strided_span(&padded, strides_in_elements, 1)?;
589                if span != raw_elements {
590                    return Err(self.invalid(format!(
591                        "component `{}` strided span {span} differs from its raw storage element count {raw_elements}",
592                        component.id
593                    )));
594                }
595            }
596            PhysicalStorageLayout::Tiled {
597                tile_shape,
598                axis_order,
599                tile_strides_in_elements,
600                padding,
601            } => {
602                let rank = semantic_dimensions.len();
603                if tile_shape.len() != rank
604                    || tile_shape.iter().any(|extent| *extent == 0)
605                    || !is_axis_permutation(axis_order, rank)
606                    || tile_strides_in_elements.len() != rank
607                {
608                    return Err(self.invalid(format!(
609                        "component `{}` tile shape, axis order, or strides do not match rank",
610                        component.id
611                    )));
612                }
613                let padded = self.resolve_padding(semantic_dimensions, padding)?;
614                let minimal_padded = semantic_dimensions
615                    .iter()
616                    .zip(tile_shape)
617                    .map(|(extent, tile)| checked_round_up(*extent, *tile))
618                    .collect::<Option<Vec<_>>>()
619                    .ok_or_else(|| {
620                        self.invalid(format!(
621                            "component `{}` tile padding overflows u64",
622                            component.id
623                        ))
624                    })?;
625                match padding {
626                    PhysicalWeightPadding::Exact if minimal_padded != semantic_dimensions => {
627                        return Err(self.invalid(format!(
628                            "component `{}` needs tile padding but declares exact storage",
629                            component.id
630                        )));
631                    }
632                    PhysicalWeightPadding::ZeroFill { .. } if padded != minimal_padded => {
633                        return Err(self.invalid(format!(
634                            "component `{}` tiled zero-fill shape is not the unique minimal padded shape",
635                            component.id
636                        )));
637                    }
638                    _ => {}
639                }
640                let semantic_grid = padded
641                    .iter()
642                    .zip(tile_shape)
643                    .map(|(extent, tile)| extent / tile)
644                    .collect::<Vec<_>>();
645                let physical_grid = axis_order
646                    .iter()
647                    .map(|axis| semantic_grid[*axis as usize])
648                    .collect::<Vec<_>>();
649                let tile_elements = checked_elements(tile_shape).ok_or_else(|| {
650                    self.invalid(format!(
651                        "component `{}` tile size overflows u64",
652                        component.id
653                    ))
654                })?;
655                let span = self.checked_strided_span(
656                    &physical_grid,
657                    tile_strides_in_elements,
658                    tile_elements,
659                )?;
660                if span != raw_elements {
661                    return Err(self.invalid(format!(
662                        "component `{}` tiled span {span} differs from its raw storage element count {raw_elements}",
663                        component.id
664                    )));
665                }
666            }
667        }
668        Ok(())
669    }
670
671    fn bind_transform_signs(
672        &mut self,
673        binding: &PhysicalWeightComponentBinding,
674        width: u64,
675        depth: usize,
676    ) -> Result<(), VNextError> {
677        self.visit_node(depth)?;
678        let component = self.component(&binding.component_id)?;
679        if component.role != WeightComponentRole::TransformSigns
680            || component.encoding
681                != (WeightEncoding::Dense {
682                    element_type: ElementType::F32,
683                })
684            || binding.storage != PhysicalStorageLayout::exact_contiguous()
685        {
686            return Err(
687                self.invalid("Hadamard signs require exact-contiguous TransformSigns F32 storage")
688            );
689        }
690        self.validate_storage(component, &[width], &binding.storage)?;
691        // This narrowly typed, immutable auxiliary resource can be shared.
692        // Ordinary value/scales/index bindings still use bind_component's
693        // duplicate-reference rejection, including across logical tensors.
694        self.referenced.insert(component.id.clone());
695        Ok(())
696    }
697
698    fn resolve_padding(
699        &self,
700        semantic_dimensions: &[u64],
701        padding: &PhysicalWeightPadding,
702    ) -> Result<Vec<u64>, VNextError> {
703        match padding {
704            PhysicalWeightPadding::Exact => Ok(semantic_dimensions.to_vec()),
705            PhysicalWeightPadding::ZeroFill { padded_dimensions } => {
706                if padded_dimensions.len() != semantic_dimensions.len()
707                    || padded_dimensions.iter().any(|extent| *extent == 0)
708                    || padded_dimensions
709                        .iter()
710                        .zip(semantic_dimensions)
711                        .any(|(padded, semantic)| padded < semantic)
712                    || padded_dimensions == semantic_dimensions
713                    || checked_elements(padded_dimensions).is_none()
714                {
715                    return Err(self.invalid(
716                        "zero-fill padding must explicitly enlarge a valid shape without shrinking any axis",
717                    ));
718                }
719                Ok(padded_dimensions.clone())
720            }
721        }
722    }
723
724    fn checked_strided_span(
725        &self,
726        dimensions: &[u64],
727        strides: &[u64],
728        base_span: u64,
729    ) -> Result<u64, VNextError> {
730        if dimensions.is_empty()
731            || dimensions.len() != strides.len()
732            || dimensions.iter().any(|extent| *extent == 0)
733            || strides.iter().any(|stride| *stride == 0)
734            || base_span == 0
735        {
736            return Err(self.invalid("strided storage dimensions and strides are invalid"));
737        }
738        let mut axes = dimensions
739            .iter()
740            .copied()
741            .zip(strides.iter().copied())
742            .filter(|(extent, _)| *extent > 1)
743            .collect::<Vec<_>>();
744        axes.sort_by_key(|(_, stride)| *stride);
745        let mut span = base_span;
746        for (extent, stride) in axes {
747            if stride < span {
748                return Err(
749                    self.invalid("strided storage aliases coordinates or overlaps physical tiles")
750                );
751            }
752            span = extent
753                .checked_sub(1)
754                .and_then(|count| count.checked_mul(stride))
755                .and_then(|addition| span.checked_add(addition))
756                .ok_or_else(|| self.invalid("strided storage span overflows u64"))?;
757        }
758        Ok(span)
759    }
760
761    fn grouped_dimensions(
762        &self,
763        semantic_dimensions: &[u64],
764        padding: &PhysicalWeightPadding,
765        group_axis: usize,
766        group_size: u64,
767    ) -> Result<Vec<u64>, VNextError> {
768        let axis_extent = semantic_dimensions[group_axis];
769        let minimal_axis = checked_round_up(axis_extent, group_size)
770            .ok_or_else(|| self.invalid("quantization group padding overflows u64"))?;
771        match padding {
772            PhysicalWeightPadding::Exact => {
773                if minimal_axis != axis_extent {
774                    return Err(self.invalid(
775                        "quantization groups require padding but exact storage was declared",
776                    ));
777                }
778                Ok(semantic_dimensions.to_vec())
779            }
780            PhysicalWeightPadding::ZeroFill { padded_dimensions } => {
781                if minimal_axis == axis_extent
782                    || padded_dimensions.len() != semantic_dimensions.len()
783                    || padded_dimensions.iter().enumerate().any(|(axis, extent)| {
784                        if axis == group_axis {
785                            *extent != minimal_axis
786                        } else {
787                            *extent != semantic_dimensions[axis]
788                        }
789                    })
790                {
791                    return Err(self.invalid(
792                        "quantization zero-fill must pad only the group axis to its unique minimal extent",
793                    ));
794                }
795                checked_elements(padded_dimensions)
796                    .is_some()
797                    .then(|| padded_dimensions.clone())
798                    .ok_or_else(|| self.invalid("quantization padded shape overflows u64"))
799            }
800        }
801    }
802
803    fn validate_dense_values(
804        &mut self,
805        binding: &PhysicalWeightComponentBinding,
806        semantic_dimensions: &[u64],
807        logical_element_type: ElementType,
808        depth: usize,
809    ) -> Result<(), VNextError> {
810        let component = self.bind_component(
811            binding,
812            semantic_dimensions,
813            WeightComponentRole::Values,
814            depth,
815        )?;
816        if component.dense_element_type() != Some(logical_element_type) {
817            return Err(self.invalid(format!(
818                "values component `{}` dtype differs from the logical tensor",
819                component.id
820            )));
821        }
822        Ok(())
823    }
824
825    fn validate_axis_component(
826        &mut self,
827        axis_component: &AxisWeightComponent,
828        semantic_dimensions: &[u64],
829        expected_axis: usize,
830        role: WeightComponentRole,
831        allow_narrow_integer: bool,
832        depth: usize,
833    ) -> Result<(), VNextError> {
834        if axis_component.axis as usize != expected_axis {
835            return Err(self.invalid(format!(
836                "axis component `{}` targets axis {}, expected {expected_axis}",
837                axis_component.component.component_id, axis_component.axis
838            )));
839        }
840        let axis_shape = [semantic_dimensions[expected_axis]];
841        let component = self.bind_component(&axis_component.component, &axis_shape, role, depth)?;
842        let integer_type_valid = component.dense_element_type().is_some_and(|element_type| {
843            if allow_narrow_integer {
844                matches!(
845                    element_type,
846                    ElementType::U8 | ElementType::U32 | ElementType::I8 | ElementType::I32
847                )
848            } else {
849                matches!(element_type, ElementType::U32 | ElementType::I32)
850            }
851        });
852        if !integer_type_valid {
853            return Err(self.invalid(format!(
854                "axis component `{}` must use an integer encoding valid for {:?}",
855                component.id, role
856            )));
857        }
858        Ok(())
859    }
860
861    fn validate_layout(
862        &mut self,
863        layout: &PhysicalWeightLayout,
864        semantic_dimensions: &[u64],
865        logical_element_type: ElementType,
866        depth: usize,
867    ) -> Result<(), VNextError> {
868        self.visit_node(depth)?;
869        if semantic_dimensions.is_empty()
870            || semantic_dimensions.iter().any(|extent| *extent == 0)
871            || checked_elements(semantic_dimensions).is_none()
872        {
873            return Err(self.invalid("logical layout shape is empty, zero, or overflowing"));
874        }
875        match layout {
876            PhysicalWeightLayout::Dense { component_id } => {
877                let binding =
878                    PhysicalWeightComponentBinding::exact_contiguous(component_id.clone());
879                self.validate_dense_values(
880                    &binding,
881                    semantic_dimensions,
882                    logical_element_type,
883                    depth,
884                )?;
885            }
886            PhysicalWeightLayout::Stored { component } => {
887                self.validate_dense_values(
888                    component,
889                    semantic_dimensions,
890                    logical_element_type,
891                    depth,
892                )?;
893            }
894            PhysicalWeightLayout::Composite { parts } => {
895                if parts.is_empty() {
896                    return Err(self.invalid("composite layout has no parts"));
897                }
898                let rank = semantic_dimensions.len();
899                let mut covered_elements = 0_u64;
900                for (index, part) in parts.iter().enumerate() {
901                    if part.logical_offsets.len() != rank
902                        || part.extents.len() != rank
903                        || part.extents.iter().any(|extent| *extent == 0)
904                        || part
905                            .logical_offsets
906                            .iter()
907                            .zip(&part.extents)
908                            .zip(semantic_dimensions)
909                            .any(|((offset, extent), logical)| {
910                                offset.checked_add(*extent).is_none_or(|end| end > *logical)
911                            })
912                    {
913                        return Err(self.invalid(format!(
914                            "composite part {index} has invalid semantic offsets or extents"
915                        )));
916                    }
917                    for previous in &parts[..index] {
918                        let overlaps = part
919                            .logical_offsets
920                            .iter()
921                            .zip(&part.extents)
922                            .zip(previous.logical_offsets.iter().zip(&previous.extents))
923                            .all(|((offset, extent), (other_offset, other_extent))| {
924                                offset.checked_add(*extent).is_some_and(|end| {
925                                    other_offset.checked_add(*other_extent).is_some_and(
926                                        |other_end| *offset < other_end && *other_offset < end,
927                                    )
928                                })
929                            });
930                        if overlaps {
931                            return Err(self.invalid("composite semantic placements overlap"));
932                        }
933                    }
934                    let part_elements = checked_elements(&part.extents)
935                        .ok_or_else(|| self.invalid("composite part size overflows u64"))?;
936                    covered_elements = covered_elements
937                        .checked_add(part_elements)
938                        .ok_or_else(|| self.invalid("composite coverage overflows u64"))?;
939                    self.validate_layout(
940                        &part.layout,
941                        &part.extents,
942                        logical_element_type,
943                        depth + 1,
944                    )?;
945                }
946                if covered_elements != checked_elements(semantic_dimensions).unwrap() {
947                    return Err(self.invalid(
948                        "composite semantic placements do not cover the logical tensor exactly",
949                    ));
950                }
951            }
952            PhysicalWeightLayout::Quantized {
953                packed_values,
954                packed_dimensions,
955                scales,
956                zero_points,
957                zero_point_packed_dimensions,
958                axis_indices,
959                permutation,
960                codebook,
961                group_axis,
962                group_padding,
963            } => {
964                if !matches!(
965                    logical_element_type,
966                    ElementType::F16 | ElementType::Bf16 | ElementType::F32
967                ) {
968                    return Err(
969                        self.invalid("quantized logical weight dtype must be floating point")
970                    );
971                }
972                let axis = *group_axis as usize;
973                if axis >= semantic_dimensions.len() {
974                    return Err(self.invalid("quantization group axis is out of range"));
975                }
976                let quantization = {
977                    let component = self.component(&packed_values.component_id)?;
978                    let WeightEncoding::Quantized(spec) = &component.encoding else {
979                        return Err(self.invalid(
980                            "packed-values component does not carry a quantization spec",
981                        ));
982                    };
983                    spec.clone()
984                };
985                let group_size = quantization
986                    .grouping
987                    .resolved_size(semantic_dimensions[axis]);
988                if group_size == 0 {
989                    return Err(self.invalid(
990                        "two-dimensional block grouping requires a block-grid quantized layout",
991                    ));
992                }
993                let grouped_dimensions =
994                    self.grouped_dimensions(semantic_dimensions, group_padding, axis, group_size)?;
995                let packed_bytes = checked_elements(&grouped_dimensions)
996                    .and_then(|elements| {
997                        elements.checked_mul(u64::from(quantization.bits_per_weight))
998                    })
999                    .and_then(|bits| bits.checked_add(7))
1000                    .map(|bits| bits / 8)
1001                    .ok_or_else(|| self.invalid("packed-values size overflows u64"))?;
1002                if checked_elements(packed_dimensions) != Some(packed_bytes) {
1003                    return Err(self.invalid(format!(
1004                        "packed-values semantic shape contains {} storage bytes, expected {packed_bytes}",
1005                        checked_elements(packed_dimensions)
1006                            .map_or_else(|| "an overflowing number of".to_owned(), |value| value.to_string())
1007                    )));
1008                }
1009                let packed = self.bind_component(
1010                    packed_values,
1011                    packed_dimensions,
1012                    WeightComponentRole::PackedValues,
1013                    depth,
1014                )?;
1015                if packed.encoding != WeightEncoding::Quantized(quantization.clone()) {
1016                    return Err(self.invalid(
1017                        "packed-values encoding changed while validating the quantized tree",
1018                    ));
1019                }
1020
1021                let mut group_shape = grouped_dimensions;
1022                group_shape[axis] /= group_size;
1023                let scales_component =
1024                    self.bind_component(scales, &group_shape, WeightComponentRole::Scales, depth)?;
1025                if scales_component.dense_element_type() != Some(quantization.scale_type) {
1026                    return Err(
1027                        self.invalid("scale component dtype differs from the quantization spec")
1028                    );
1029                }
1030                match (
1031                    quantization.zero_point_type,
1032                    zero_points,
1033                    zero_point_packed_dimensions,
1034                ) {
1035                    (Some(expected_type), Some(binding), Some(packed_dimensions)) => {
1036                        let expected_bytes = checked_elements(&group_shape)
1037                            .and_then(|elements| {
1038                                elements.checked_mul(u64::from(quantization.bits_per_weight))
1039                            })
1040                            .and_then(|bits| bits.checked_add(7))
1041                            .map(|bits| bits / 8)
1042                            .ok_or_else(|| self.invalid("packed zero-point size overflows u64"))?;
1043                        let component = self.bind_component(
1044                            binding,
1045                            packed_dimensions,
1046                            WeightComponentRole::ZeroPoints,
1047                            depth,
1048                        )?;
1049                        if component.dense_element_type() != Some(expected_type)
1050                            || component.physical_bytes()? != expected_bytes
1051                        {
1052                            return Err(self.invalid(
1053                                "packed zero-point component differs from its quantization contract",
1054                            ));
1055                        }
1056                    }
1057                    (Some(expected_type), Some(binding), None) => {
1058                        let component = self.bind_component(
1059                            binding,
1060                            &group_shape,
1061                            WeightComponentRole::ZeroPoints,
1062                            depth,
1063                        )?;
1064                        if component.dense_element_type() != Some(expected_type) {
1065                            return Err(self.invalid(
1066                                "zero-point component dtype differs from the quantization spec",
1067                            ));
1068                        }
1069                    }
1070                    (None, None, None) => {}
1071                    _ => {
1072                        return Err(self.invalid(
1073                            "zero-point component presence differs from the quantization spec",
1074                        ));
1075                    }
1076                }
1077                if let Some(axis_indices) = axis_indices {
1078                    self.validate_axis_component(
1079                        axis_indices,
1080                        semantic_dimensions,
1081                        axis,
1082                        WeightComponentRole::Indices,
1083                        true,
1084                        depth,
1085                    )?;
1086                }
1087                if let Some(permutation) = permutation {
1088                    self.validate_axis_component(
1089                        permutation,
1090                        semantic_dimensions,
1091                        axis,
1092                        WeightComponentRole::Permutation,
1093                        false,
1094                        depth,
1095                    )?;
1096                }
1097                if let Some(codebook) = codebook {
1098                    let entries = 1_u64
1099                        .checked_shl(u32::from(quantization.bits_per_weight))
1100                        .ok_or_else(|| self.invalid("codebook size overflows u64"))?;
1101                    let component = self.bind_component(
1102                        codebook,
1103                        &[entries],
1104                        WeightComponentRole::Codebook,
1105                        depth,
1106                    )?;
1107                    if component.dense_element_type() != Some(logical_element_type) {
1108                        return Err(
1109                            self.invalid("codebook dtype differs from the logical tensor dtype")
1110                        );
1111                    }
1112                }
1113            }
1114            PhysicalWeightLayout::QuantizedBlockGrid {
1115                packed_values,
1116                packed_dimensions,
1117                scales,
1118                block_axes,
1119            } => {
1120                if !matches!(
1121                    logical_element_type,
1122                    ElementType::F16 | ElementType::Bf16 | ElementType::F32
1123                ) {
1124                    return Err(self.invalid(
1125                        "block-grid quantized logical weight dtype must be floating point",
1126                    ));
1127                }
1128                let axes = [block_axes[0] as usize, block_axes[1] as usize];
1129                if axes[0] >= axes[1] || axes[1] >= semantic_dimensions.len() {
1130                    return Err(self.invalid(
1131                        "block-grid quantization axes must be distinct, in range, and ascending",
1132                    ));
1133                }
1134                let quantization = {
1135                    let component = self.component(&packed_values.component_id)?;
1136                    let WeightEncoding::Quantized(spec) = &component.encoding else {
1137                        return Err(self.invalid(
1138                            "block-grid packed-values component does not carry a quantization spec",
1139                        ));
1140                    };
1141                    spec.clone()
1142                };
1143                let block_shape = quantization.grouping.block_shape_2d().ok_or_else(|| {
1144                    self.invalid(
1145                        "block-grid packed-values quantization spec must carry a two-dimensional block shape",
1146                    )
1147                })?;
1148                if quantization.zero_point_type.is_some() {
1149                    return Err(self.invalid(
1150                        "block-grid quantization does not support an implicit zero-point component",
1151                    ));
1152                }
1153
1154                let packed_bytes = checked_elements(semantic_dimensions)
1155                    .and_then(|elements| {
1156                        elements.checked_mul(u64::from(quantization.bits_per_weight))
1157                    })
1158                    .and_then(|bits| bits.checked_add(7))
1159                    .map(|bits| bits / 8)
1160                    .ok_or_else(|| self.invalid("block-grid packed-values size overflows u64"))?;
1161                if packed_dimensions.is_empty()
1162                    || checked_elements(packed_dimensions) != Some(packed_bytes)
1163                {
1164                    return Err(self.invalid(format!(
1165                        "block-grid packed-values semantic shape contains {} storage bytes, expected {packed_bytes}",
1166                        checked_elements(packed_dimensions).map_or_else(
1167                            || "an invalid or overflowing number of".to_owned(),
1168                            |value| value.to_string(),
1169                        )
1170                    )));
1171                }
1172                let packed = self.bind_component(
1173                    packed_values,
1174                    packed_dimensions,
1175                    WeightComponentRole::PackedValues,
1176                    depth,
1177                )?;
1178                if packed.encoding != WeightEncoding::Quantized(quantization.clone()) {
1179                    return Err(self.invalid(
1180                        "packed-values encoding changed while validating the block-grid tree",
1181                    ));
1182                }
1183
1184                let mut scale_dimensions = semantic_dimensions.to_vec();
1185                for (axis, block_size) in axes.into_iter().zip(block_shape) {
1186                    scale_dimensions[axis] =
1187                        semantic_dimensions[axis].div_ceil(u64::from(block_size.get()));
1188                }
1189                let scales_component = self.bind_component(
1190                    scales,
1191                    &scale_dimensions,
1192                    WeightComponentRole::Scales,
1193                    depth,
1194                )?;
1195                if scales_component.dense_element_type() != Some(quantization.scale_type) {
1196                    return Err(self.invalid(
1197                        "block-grid scale component dtype differs from the quantization spec",
1198                    ));
1199                }
1200            }
1201            PhysicalWeightLayout::BlockQuantized {
1202                blocks,
1203                block_axis,
1204                block_padding,
1205            } => {
1206                if !matches!(
1207                    logical_element_type,
1208                    ElementType::F16 | ElementType::Bf16 | ElementType::F32
1209                ) {
1210                    return Err(
1211                        self.invalid("block-quantized logical weight dtype must be floating point")
1212                    );
1213                }
1214                let axis = *block_axis as usize;
1215                if axis >= semantic_dimensions.len() {
1216                    return Err(self.invalid("block quantization axis is out of range"));
1217                }
1218                let quantization = {
1219                    let component = self.component(&blocks.component_id)?;
1220                    let WeightEncoding::BlockQuantized(spec) = &component.encoding else {
1221                        return Err(self
1222                            .invalid("block component does not carry a block quantization spec"));
1223                    };
1224                    spec.clone()
1225                };
1226                let mut block_dimensions = self.grouped_dimensions(
1227                    semantic_dimensions,
1228                    block_padding,
1229                    axis,
1230                    u64::from(quantization.logical_values_per_block),
1231                )?;
1232                block_dimensions[axis] /= u64::from(quantization.logical_values_per_block);
1233                let component = self.bind_component(
1234                    blocks,
1235                    &block_dimensions,
1236                    WeightComponentRole::PackedValues,
1237                    depth,
1238                )?;
1239                if component.encoding != WeightEncoding::BlockQuantized(quantization) {
1240                    return Err(
1241                        self.invalid("block encoding changed while validating the physical layout")
1242                    );
1243                }
1244            }
1245            PhysicalWeightLayout::Hadamard { values, transform } => {
1246                if self.hadamard_active {
1247                    return Err(self.invalid("nested Hadamard transforms are not supported"));
1248                }
1249                if semantic_dimensions.len() < 2
1250                    || !matches!(
1251                        logical_element_type,
1252                        ElementType::F16 | ElementType::Bf16 | ElementType::F32
1253                    )
1254                    || (matches!(
1255                        transform.application,
1256                        HadamardApplication::AfterEmbeddingLookup
1257                    ) && semantic_dimensions.len() != 2)
1258                {
1259                    return Err(self.invalid("Hadamard requires floating-point matrices; embedding lookup requires rank two"));
1260                }
1261                let width = *semantic_dimensions.last().unwrap();
1262                transform
1263                    .validate(width)
1264                    .map_err(|error| self.invalid(error.to_string()))?;
1265                if let HadamardSigns::Explicit(signs) = &transform.signs {
1266                    self.bind_transform_signs(signs, width, depth)?;
1267                }
1268                self.hadamard_active = true;
1269                let result = self.validate_layout(
1270                    values,
1271                    semantic_dimensions,
1272                    logical_element_type,
1273                    depth + 1,
1274                );
1275                self.hadamard_active = false;
1276                result?;
1277            }
1278            PhysicalWeightLayout::AxisReshapePermutation {
1279                values,
1280                axis,
1281                logical_offset,
1282                extent,
1283                reshape,
1284                stored_axis_order,
1285            } => {
1286                let axis = *axis as usize;
1287                let end = logical_offset.checked_add(*extent);
1288                let reshape_rank = reshape.len();
1289                let order_is_permutation = stored_axis_order.len() == reshape_rank
1290                    && stored_axis_order
1291                        .iter()
1292                        .all(|axis| (*axis as usize) < reshape_rank)
1293                    && stored_axis_order
1294                        .iter()
1295                        .copied()
1296                        .collect::<BTreeSet<_>>()
1297                        .len()
1298                        == reshape_rank;
1299                let order_is_identity = stored_axis_order
1300                    .iter()
1301                    .filter(|stored| reshape[**stored as usize] > 1)
1302                    .copied()
1303                    .eq(reshape
1304                        .iter()
1305                        .enumerate()
1306                        .filter_map(|(axis, extent)| (*extent > 1).then_some(axis as u32)));
1307                if axis >= semantic_dimensions.len()
1308                    || *extent == 0
1309                    || end.is_none_or(|end| end > semantic_dimensions[axis])
1310                    || reshape_rank < 2
1311                    || reshape.iter().any(|dimension| *dimension == 0)
1312                    || checked_elements(reshape) != Some(*extent)
1313                    || !order_is_permutation
1314                    || order_is_identity
1315                {
1316                    return Err(self.invalid(
1317                        "axis reshape permutation has invalid range, shape, or stored axis order",
1318                    ));
1319                }
1320                self.validate_layout(values, semantic_dimensions, logical_element_type, depth + 1)?;
1321            }
1322            PhysicalWeightLayout::Indexed {
1323                indices,
1324                values,
1325                source_axis_extent,
1326            } => {
1327                let axis = indices.axis as usize;
1328                if axis >= semantic_dimensions.len() || *source_axis_extent == 0 {
1329                    return Err(self.invalid("indexed layout axis or source extent is invalid"));
1330                }
1331                self.validate_axis_component(
1332                    indices,
1333                    semantic_dimensions,
1334                    axis,
1335                    WeightComponentRole::Indices,
1336                    true,
1337                    depth,
1338                )?;
1339                let mut source_dimensions = semantic_dimensions.to_vec();
1340                source_dimensions[axis] = *source_axis_extent;
1341                checked_elements(&source_dimensions)
1342                    .ok_or_else(|| self.invalid("indexed source semantic shape overflows u64"))?;
1343                self.validate_layout(values, &source_dimensions, logical_element_type, depth + 1)?;
1344            }
1345            PhysicalWeightLayout::ExpertStack {
1346                experts,
1347                expert_axis,
1348            } => {
1349                let axis = *expert_axis as usize;
1350                if axis >= semantic_dimensions.len() {
1351                    return Err(self.invalid("expert stack axis is out of range"));
1352                }
1353                let expected_count = usize::try_from(semantic_dimensions[axis]).map_err(|_| {
1354                    self.invalid("expert stack count does not fit the platform usize")
1355                })?;
1356                if experts.is_empty() || experts.len() != expected_count {
1357                    return Err(self
1358                        .invalid("expert stack child count differs from its logical expert axis"));
1359                }
1360                let mut expert_dimensions = semantic_dimensions.to_vec();
1361                expert_dimensions.remove(axis);
1362                if expert_dimensions.is_empty() {
1363                    return Err(self.invalid(
1364                        "expert stack children must retain at least one tensor dimension",
1365                    ));
1366                }
1367                for expert in experts {
1368                    self.validate_layout(
1369                        expert,
1370                        &expert_dimensions,
1371                        logical_element_type,
1372                        depth + 1,
1373                    )?;
1374                }
1375            }
1376        }
1377        Ok(())
1378    }
1379}
1380
1381fn is_axis_permutation(axis_order: &[u32], rank: usize) -> bool {
1382    axis_order.len() == rank
1383        && axis_order.iter().all(|axis| (*axis as usize) < rank)
1384        && axis_order.iter().copied().collect::<BTreeSet<_>>().len() == rank
1385}
1386
1387fn checked_round_up(extent: u64, multiple: u64) -> Option<u64> {
1388    if extent == 0 || multiple == 0 {
1389        return None;
1390    }
1391    extent
1392        .checked_add(multiple.checked_sub(1)?)
1393        .map(|rounded| rounded / multiple * multiple)
1394}
1395
1396#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1397pub struct ProgramTensorSpec {
1398    pub dimensions: Vec<u64>,
1399    pub element_type: ElementType,
1400    pub layout: ResolvedTensorLayout,
1401}
1402
1403impl ProgramTensorSpec {
1404    pub fn validate(&self, field: &str) -> Result<(), VNextError> {
1405        super::ResolvedTensorSpec::new(
1406            self.dimensions.clone(),
1407            self.element_type,
1408            self.layout.clone(),
1409        )
1410        .map(|_| ())
1411        .map_err(|error| VNextError::InvalidExecutionPlan {
1412            reason: format!("{field} is invalid: {error}"),
1413        })
1414    }
1415
1416    pub fn byte_len(&self) -> Result<u64, VNextError> {
1417        checked_elements(&self.dimensions)
1418            .and_then(|elements| elements.checked_mul(self.element_type.size_bytes()))
1419            .ok_or_else(|| VNextError::InvalidExecutionPlan {
1420                reason: "program tensor byte size overflows u64".to_owned(),
1421            })
1422    }
1423}
1424
1425#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1426pub struct WeightReference {
1427    pub weight_id: WeightId,
1428    pub value_id: ProgramValueId,
1429    pub tensor: ProgramTensorSpec,
1430}
1431
1432#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
1433pub struct StateSpec {
1434    pub id: StateId,
1435    pub value_id: ProgramValueId,
1436    /// Logical tensor view consumed by one operation. It does not select a
1437    /// physical allocator, addressability profile, or backing pool.
1438    pub tensor: ProgramTensorSpec,
1439    pub lifetime: StateLifetime,
1440    pub capacity_demand: StateCapacityDemand,
1441    /// Initial contents required when a new logical state scope acquires
1442    /// physical backing. This is semantic model state, not an allocator hint.
1443    pub initialization: StateInitialization,
1444    /// Explicit semantic continuation contract. A capacity formula never grants
1445    /// checkpoint eligibility, and an undeclared contract stays unsupported.
1446    #[serde(skip_serializing_if = "StateCheckpointCapability::is_unsupported")]
1447    pub checkpoint: StateCheckpointCapability,
1448}
1449
1450#[derive(Deserialize)]
1451#[serde(deny_unknown_fields)]
1452struct StateSpecWire {
1453    id: StateId,
1454    value_id: ProgramValueId,
1455    tensor: ProgramTensorSpec,
1456    lifetime: StateLifetime,
1457    capacity_demand: StateCapacityDemand,
1458    initialization: StateInitialization,
1459    #[serde(default)]
1460    checkpoint: StateCheckpointCapability,
1461}
1462
1463impl<'de> Deserialize<'de> for StateSpec {
1464    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1465    where
1466        D: Deserializer<'de>,
1467    {
1468        let wire = StateSpecWire::deserialize(deserializer)?;
1469        wire.tensor
1470            .validate("state_spec.tensor")
1471            .and_then(|()| wire.capacity_demand.validate(wire.tensor.byte_len()?))
1472            .and_then(|()| wire.checkpoint.validate_lifetime(wire.lifetime))
1473            .map_err(serde::de::Error::custom)?;
1474        Ok(Self {
1475            id: wire.id,
1476            value_id: wire.value_id,
1477            tensor: wire.tensor,
1478            lifetime: wire.lifetime,
1479            capacity_demand: wire.capacity_demand,
1480            initialization: wire.initialization,
1481            checkpoint: wire.checkpoint,
1482        })
1483    }
1484}
1485
1486#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1487#[serde(rename_all = "snake_case")]
1488pub enum StateLifetime {
1489    Request,
1490    Sequence,
1491    Step,
1492}
1493
1494/// Backend-neutral capacity formula for semantic state. This deliberately says
1495/// nothing about pages, blocks, allocator kind, or provider-visible regions.
1496/// Concrete physical storage is selected only while building an execution
1497/// plan from provider requirements, runtime offers, and typed policy.
1498#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
1499#[serde(rename_all = "snake_case")]
1500pub enum StateCapacityDemand {
1501    FixedPerScope,
1502    TokenScaled {
1503        bytes_per_token: u64,
1504        maximum_tokens: u64,
1505    },
1506}
1507
1508#[derive(Deserialize)]
1509#[serde(rename_all = "snake_case", deny_unknown_fields)]
1510enum StateCapacityDemandWire {
1511    FixedPerScope,
1512    TokenScaled {
1513        bytes_per_token: u64,
1514        maximum_tokens: u64,
1515    },
1516}
1517
1518impl<'de> Deserialize<'de> for StateCapacityDemand {
1519    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1520    where
1521        D: Deserializer<'de>,
1522    {
1523        let demand = match StateCapacityDemandWire::deserialize(deserializer)? {
1524            StateCapacityDemandWire::FixedPerScope => Self::FixedPerScope,
1525            StateCapacityDemandWire::TokenScaled {
1526                bytes_per_token,
1527                maximum_tokens,
1528            } => Self::TokenScaled {
1529                bytes_per_token,
1530                maximum_tokens,
1531            },
1532        };
1533        demand.validate(1).map_err(serde::de::Error::custom)?;
1534        Ok(demand)
1535    }
1536}
1537
1538impl StateCapacityDemand {
1539    pub fn validate(self, tensor_minimum_bytes: u64) -> Result<(), VNextError> {
1540        let valid = match self {
1541            Self::FixedPerScope => tensor_minimum_bytes > 0,
1542            Self::TokenScaled {
1543                bytes_per_token,
1544                maximum_tokens,
1545            } => {
1546                bytes_per_token >= tensor_minimum_bytes
1547                    && maximum_tokens > 0
1548                    && bytes_per_token.checked_mul(maximum_tokens).is_some()
1549            }
1550        };
1551        if !valid {
1552            return Err(VNextError::InvalidExecutionPlan {
1553                reason: "state resource demand is zero, smaller than its tensor, or overflows u64"
1554                    .to_owned(),
1555            });
1556        }
1557        Ok(())
1558    }
1559
1560    pub fn minimum_bytes(self, tensor_minimum_bytes: u64) -> Result<u64, VNextError> {
1561        self.validate(tensor_minimum_bytes)?;
1562        Ok(match self {
1563            Self::FixedPerScope => tensor_minimum_bytes,
1564            Self::TokenScaled {
1565                bytes_per_token, ..
1566            } => bytes_per_token,
1567        })
1568    }
1569
1570    pub fn theoretical_bytes(self, tensor_minimum_bytes: u64) -> Result<u64, VNextError> {
1571        self.validate(tensor_minimum_bytes)?;
1572        match self {
1573            Self::FixedPerScope => Ok(tensor_minimum_bytes),
1574            Self::TokenScaled {
1575                bytes_per_token,
1576                maximum_tokens,
1577            } => bytes_per_token.checked_mul(maximum_tokens).ok_or_else(|| {
1578                VNextError::InvalidExecutionPlan {
1579                    reason: "token-scaled state demand overflows u64".to_owned(),
1580                }
1581            }),
1582        }
1583    }
1584}
1585
1586#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1587#[serde(rename_all = "snake_case")]
1588pub enum ProgramNodeWorkSpec {
1589    Fixed,
1590    Tokens { value_id: ProgramValueId, axis: u32 },
1591}
1592
1593impl ProgramNodeWorkSpec {
1594    pub fn tokens(value_id: ProgramValueId, axis: u32) -> Self {
1595        Self::Tokens { value_id, axis }
1596    }
1597}
1598
1599#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1600pub struct ProgramNode {
1601    pub id: NodeId,
1602    pub operation_id: OperationId,
1603    pub required_version: ContractVersion,
1604    pub work: ProgramNodeWorkSpec,
1605    pub inputs: Vec<ProgramValueId>,
1606    pub outputs: Vec<ProgramValueId>,
1607    pub attributes: BTreeMap<AttributeId, SemanticValue>,
1608}
1609
1610#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1611pub struct ProgramBlock {
1612    pub id: String,
1613    pub nodes: Vec<ProgramNode>,
1614}
1615
1616/// Backend-free semantic program for a model family.
1617#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
1618pub struct ModelProgram {
1619    family_id: ModelFamilyId,
1620    inputs: Vec<ProgramValueId>,
1621    blocks: Vec<ProgramBlock>,
1622    states: Vec<StateSpec>,
1623    weights: Vec<WeightReference>,
1624    outputs: Vec<ProgramValueId>,
1625    #[serde(skip_serializing_if = "Option::is_none")]
1626    checkpoint_inputs: Option<ProgramCheckpointInputs>,
1627}
1628
1629#[derive(Deserialize)]
1630#[serde(deny_unknown_fields)]
1631struct ModelProgramWire {
1632    family_id: ModelFamilyId,
1633    inputs: Vec<ProgramValueId>,
1634    blocks: Vec<ProgramBlock>,
1635    states: Vec<StateSpec>,
1636    weights: Vec<WeightReference>,
1637    outputs: Vec<ProgramValueId>,
1638    #[serde(default)]
1639    checkpoint_inputs: Option<ProgramCheckpointInputs>,
1640}
1641
1642impl<'de> Deserialize<'de> for ModelProgram {
1643    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1644    where
1645        D: Deserializer<'de>,
1646    {
1647        let wire = ModelProgramWire::deserialize(deserializer)?;
1648        let program = Self::new(
1649            wire.family_id,
1650            wire.inputs,
1651            wire.blocks,
1652            wire.states,
1653            wire.weights,
1654            wire.outputs,
1655        )
1656        .map_err(serde::de::Error::custom)?;
1657        match wire.checkpoint_inputs {
1658            Some(inputs) => program
1659                .with_checkpoint_inputs(inputs)
1660                .map_err(serde::de::Error::custom),
1661            None => Ok(program),
1662        }
1663    }
1664}
1665
1666impl ModelProgram {
1667    pub fn new(
1668        family_id: ModelFamilyId,
1669        inputs: Vec<ProgramValueId>,
1670        blocks: Vec<ProgramBlock>,
1671        mut states: Vec<StateSpec>,
1672        mut weights: Vec<WeightReference>,
1673        outputs: Vec<ProgramValueId>,
1674    ) -> Result<Self, VNextError> {
1675        if blocks.is_empty() {
1676            return Err(VNextError::InvalidModelConfig {
1677                family_id: family_id.to_string(),
1678                field: "program.blocks".to_owned(),
1679                reason: "at least one block is required".to_owned(),
1680            });
1681        }
1682        let mut known_values = BTreeSet::new();
1683        if inputs.is_empty()
1684            || inputs
1685                .iter()
1686                .any(|input| !known_values.insert(input.clone()))
1687        {
1688            return Err(VNextError::InvalidModelConfig {
1689                family_id: family_id.to_string(),
1690                field: "program.inputs".to_owned(),
1691                reason: "input identities must be non-empty and unique".to_owned(),
1692            });
1693        }
1694        let mut block_ids = BTreeSet::new();
1695        let mut node_ids = BTreeSet::new();
1696        for state in &states {
1697            state.checkpoint.validate_lifetime(state.lifetime)?;
1698            let tensor_valid = state
1699                .tensor
1700                .validate(&format!("program.states.{}.tensor", state.id))
1701                .and_then(|()| state.capacity_demand.validate(state.tensor.byte_len()?));
1702            if tensor_valid.is_err() || !known_values.insert(state.value_id.clone()) {
1703                return Err(VNextError::InvalidModelConfig {
1704                    family_id: family_id.to_string(),
1705                    field: "program.states.value_id".to_owned(),
1706                    reason: format!("duplicate value `{}`", state.value_id),
1707                });
1708            }
1709        }
1710        let mut weight_ids = BTreeSet::new();
1711        for weight in &weights {
1712            if !weight_ids.insert(weight.weight_id.clone())
1713                || !known_values.insert(weight.value_id.clone())
1714            {
1715                return Err(VNextError::InvalidModelConfig {
1716                    family_id: family_id.to_string(),
1717                    field: "program.weights".to_owned(),
1718                    reason: format!(
1719                        "duplicate weight `{}` or value `{}`",
1720                        weight.weight_id, weight.value_id
1721                    ),
1722                });
1723            }
1724            weight
1725                .tensor
1726                .validate(&format!("program.weights.{}.tensor", weight.weight_id))?;
1727        }
1728        for block in &blocks {
1729            if block.id.is_empty() || block.nodes.is_empty() || !block_ids.insert(block.id.clone())
1730            {
1731                return Err(VNextError::InvalidModelConfig {
1732                    family_id: family_id.to_string(),
1733                    field: "program.blocks.id".to_owned(),
1734                    reason: "block identities must be non-empty and unique".to_owned(),
1735                });
1736            }
1737            for node in &block.nodes {
1738                if node.required_version.major == 0 || node.outputs.is_empty() {
1739                    return Err(VNextError::InvalidModelConfig {
1740                        family_id: family_id.to_string(),
1741                        field: "program.nodes.contract".to_owned(),
1742                        reason: format!("node `{}` has an invalid version or no outputs", node.id),
1743                    });
1744                }
1745                for value in node.attributes.values() {
1746                    value.validate(&format!("program node `{}` attributes", node.id))?;
1747                }
1748                if !node_ids.insert(node.id.clone()) {
1749                    return Err(VNextError::InvalidModelConfig {
1750                        family_id: family_id.to_string(),
1751                        field: "program.nodes.id".to_owned(),
1752                        reason: format!("duplicate node `{}`", node.id),
1753                    });
1754                }
1755                if let ProgramNodeWorkSpec::Tokens { value_id, .. } = &node.work {
1756                    let source_count = node
1757                        .inputs
1758                        .iter()
1759                        .chain(&node.outputs)
1760                        .filter(|candidate| *candidate == value_id)
1761                        .count();
1762                    let is_state_or_weight = states.iter().any(|state| state.value_id == *value_id)
1763                        || weights.iter().any(|weight| weight.value_id == *value_id);
1764                    if source_count != 1 || is_state_or_weight {
1765                        return Err(VNextError::InvalidModelConfig {
1766                            family_id: family_id.to_string(),
1767                            field: "program.nodes.work".to_owned(),
1768                            reason: format!(
1769                                "node `{}` token work source must identify one activation binding",
1770                                node.id
1771                            ),
1772                        });
1773                    }
1774                }
1775                if node
1776                    .inputs
1777                    .iter()
1778                    .any(|input| !known_values.contains(input))
1779                {
1780                    return Err(VNextError::InvalidModelConfig {
1781                        family_id: family_id.to_string(),
1782                        field: "program.nodes.inputs".to_owned(),
1783                        reason: format!("node `{}` references an unknown input", node.id),
1784                    });
1785                }
1786                for output in &node.outputs {
1787                    if !known_values.insert(output.clone()) {
1788                        return Err(VNextError::InvalidModelConfig {
1789                            family_id: family_id.to_string(),
1790                            field: "program.nodes.outputs".to_owned(),
1791                            reason: format!("value `{output}` has multiple producers"),
1792                        });
1793                    }
1794                }
1795            }
1796        }
1797        let mut state_ids = BTreeSet::new();
1798        if states
1799            .iter()
1800            .any(|state| !state_ids.insert(state.id.clone()))
1801        {
1802            return Err(VNextError::InvalidModelConfig {
1803                family_id: family_id.to_string(),
1804                field: "program.states.id".to_owned(),
1805                reason: "state identities must be unique".to_owned(),
1806            });
1807        }
1808        let mut output_ids = BTreeSet::new();
1809        if outputs.is_empty()
1810            || outputs
1811                .iter()
1812                .any(|output| !known_values.contains(output) || !output_ids.insert(output.clone()))
1813        {
1814            return Err(VNextError::InvalidModelConfig {
1815                family_id: family_id.to_string(),
1816                field: "program.outputs".to_owned(),
1817                reason: "program outputs must be non-empty, known, and unique".to_owned(),
1818            });
1819        }
1820        states.sort_by(|left, right| left.id.cmp(&right.id));
1821        weights.sort_by(|left, right| left.weight_id.cmp(&right.weight_id));
1822        Ok(Self {
1823            family_id,
1824            inputs,
1825            blocks,
1826            states,
1827            weights,
1828            outputs,
1829            checkpoint_inputs: None,
1830        })
1831    }
1832
1833    pub fn with_checkpoint_inputs(
1834        mut self,
1835        inputs: ProgramCheckpointInputs,
1836    ) -> Result<Self, VNextError> {
1837        if !inputs.covers(&self.inputs) {
1838            return Err(VNextError::InvalidExecutionPlan {
1839                reason: "checkpoint input declaration must cover exactly every program input"
1840                    .to_owned(),
1841            });
1842        }
1843        self.checkpoint_inputs = Some(inputs);
1844        Ok(self)
1845    }
1846
1847    pub fn checkpoint_inputs(&self) -> Option<&ProgramCheckpointInputs> {
1848        self.checkpoint_inputs.as_ref()
1849    }
1850
1851    pub fn family_id(&self) -> &ModelFamilyId {
1852        &self.family_id
1853    }
1854
1855    pub fn inputs(&self) -> &[ProgramValueId] {
1856        &self.inputs
1857    }
1858
1859    pub fn blocks(&self) -> &[ProgramBlock] {
1860        &self.blocks
1861    }
1862
1863    pub fn states(&self) -> &[StateSpec] {
1864        &self.states
1865    }
1866
1867    pub fn weights(&self) -> &[WeightReference] {
1868        &self.weights
1869    }
1870
1871    pub fn outputs(&self) -> &[ProgramValueId] {
1872        &self.outputs
1873    }
1874
1875    pub fn fingerprint(&self) -> Result<String, VNextError> {
1876        let bytes = serde_json::to_vec(self).map_err(|error| VNextError::Serialization {
1877            context: "serialize model program",
1878            message: error.to_string(),
1879        })?;
1880        Ok(format!("{:x}", Sha256::digest(bytes)))
1881    }
1882}
1883
1884impl WeightSchema {
1885    pub fn validate_program_references(
1886        &self,
1887        family_id: &ModelFamilyId,
1888        program: &ModelProgram,
1889    ) -> Result<(), VNextError> {
1890        if program.family_id() != family_id {
1891            return Err(VNextError::InvalidModelConfig {
1892                family_id: family_id.to_string(),
1893                field: "program.family_id".to_owned(),
1894                reason: "program family does not match the weight schema owner".to_owned(),
1895            });
1896        }
1897        let schema_weights = self
1898            .tensors
1899            .iter()
1900            .map(|tensor| (&tensor.id, tensor.required))
1901            .collect::<BTreeMap<_, _>>();
1902        let referenced_weights = program
1903            .weights()
1904            .iter()
1905            .map(|reference| &reference.weight_id)
1906            .collect::<BTreeSet<_>>();
1907        if let Some(weight_id) = referenced_weights
1908            .iter()
1909            .find(|weight_id| !schema_weights.contains_key(**weight_id))
1910        {
1911            return Err(VNextError::InvalidModelConfig {
1912                family_id: family_id.to_string(),
1913                field: "program.weights".to_owned(),
1914                reason: format!("program references unknown weight `{weight_id}`"),
1915            });
1916        }
1917        if let Some(weight_id) = schema_weights.iter().find_map(|(weight_id, required)| {
1918            (*required && !referenced_weights.contains(weight_id)).then_some(*weight_id)
1919        }) {
1920            return Err(VNextError::InvalidModelConfig {
1921                family_id: family_id.to_string(),
1922                field: "program.weights".to_owned(),
1923                reason: format!("program does not reference required weight `{weight_id}`"),
1924            });
1925        }
1926        for reference in program.weights() {
1927            let tensor = self.tensor(&reference.weight_id).ok_or_else(|| {
1928                VNextError::InvalidModelConfig {
1929                    family_id: family_id.to_string(),
1930                    field: "program.weights".to_owned(),
1931                    reason: format!(
1932                        "program references unknown weight `{}`",
1933                        reference.weight_id
1934                    ),
1935                }
1936            })?;
1937            if reference.tensor.dimensions != tensor.dimensions
1938                || reference.tensor.element_type != tensor.logical_element_type
1939            {
1940                return Err(VNextError::InvalidModelConfig {
1941                    family_id: family_id.to_string(),
1942                    field: format!("program.weights.{}.tensor", reference.weight_id),
1943                    reason: "program value shape or dtype differs from the logical weight schema"
1944                        .to_owned(),
1945                });
1946            }
1947        }
1948        Ok(())
1949    }
1950}
1951
1952#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1953pub struct TemplateMetadata {
1954    pub template: String,
1955    pub source_file: String,
1956    pub sha256: String,
1957}
1958
1959#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
1960#[serde(rename_all = "snake_case")]
1961pub enum SpecialTokenRole {
1962    Bos,
1963    Eos,
1964    Pad,
1965    Stop,
1966}
1967
1968#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
1969pub struct SpecialTokenCollision {
1970    first: SpecialTokenRole,
1971    second: SpecialTokenRole,
1972}
1973
1974#[derive(Deserialize)]
1975#[serde(deny_unknown_fields)]
1976struct SpecialTokenCollisionWire {
1977    first: SpecialTokenRole,
1978    second: SpecialTokenRole,
1979}
1980
1981impl<'de> Deserialize<'de> for SpecialTokenCollision {
1982    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
1983    where
1984        D: Deserializer<'de>,
1985    {
1986        let wire = SpecialTokenCollisionWire::deserialize(deserializer)?;
1987        Self::new(wire.first, wire.second).map_err(serde::de::Error::custom)
1988    }
1989}
1990
1991impl SpecialTokenCollision {
1992    pub fn new(first: SpecialTokenRole, second: SpecialTokenRole) -> Result<Self, VNextError> {
1993        if first == second {
1994            return Err(VNextError::InvalidExecutionPlan {
1995                reason: "a special-token collision must name two different roles".to_owned(),
1996            });
1997        }
1998        let (first, second) = if first < second {
1999            (first, second)
2000        } else {
2001            (second, first)
2002        };
2003        Ok(Self { first, second })
2004    }
2005
2006    pub const fn first(&self) -> SpecialTokenRole {
2007        self.first
2008    }
2009
2010    pub const fn second(&self) -> SpecialTokenRole {
2011        self.second
2012    }
2013}
2014
2015#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
2016pub struct SpecialTokenCollisionPolicy {
2017    allowed: BTreeSet<SpecialTokenCollision>,
2018}
2019
2020#[derive(Deserialize)]
2021#[serde(deny_unknown_fields)]
2022struct SpecialTokenCollisionPolicyWire {
2023    allowed: BTreeSet<SpecialTokenCollision>,
2024}
2025
2026impl<'de> Deserialize<'de> for SpecialTokenCollisionPolicy {
2027    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2028    where
2029        D: Deserializer<'de>,
2030    {
2031        let wire = SpecialTokenCollisionPolicyWire::deserialize(deserializer)?;
2032        Ok(Self::new(wire.allowed))
2033    }
2034}
2035
2036impl SpecialTokenCollisionPolicy {
2037    pub fn new(allowed: BTreeSet<SpecialTokenCollision>) -> Self {
2038        Self { allowed }
2039    }
2040
2041    pub fn require_distinct() -> Self {
2042        Self {
2043            allowed: BTreeSet::new(),
2044        }
2045    }
2046
2047    pub fn allows(&self, left: SpecialTokenRole, right: SpecialTokenRole) -> bool {
2048        SpecialTokenCollision::new(left, right)
2049            .is_ok_and(|collision| self.allowed.contains(&collision))
2050    }
2051
2052    pub fn allowed(&self) -> &BTreeSet<SpecialTokenCollision> {
2053        &self.allowed
2054    }
2055}
2056
2057#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2058pub struct SpecialTokenMetadata {
2059    pub bos_token_id: Option<u32>,
2060    pub eos_token_ids: BTreeSet<u32>,
2061    pub pad_token_id: Option<u32>,
2062    pub collision_policy: SpecialTokenCollisionPolicy,
2063}
2064
2065#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2066pub struct ModelSemanticMetadata {
2067    pub template: TemplateMetadata,
2068    pub special_tokens: SpecialTokenMetadata,
2069}
2070
2071/// Compile-time model family provider with a typed, validated configuration.
2072pub trait ModelFamilyProvider: Send + Sync + 'static {
2073    type Config: Clone + Send + Sync + Serialize + DeserializeOwned + 'static;
2074
2075    fn family_id(&self) -> &ModelFamilyId;
2076
2077    fn external_metadata_ids(&self) -> BTreeSet<ExternalModelMetadataId>;
2078
2079    fn validate_config_identity(
2080        &self,
2081        raw: &serde_json::Value,
2082        config: &Self::Config,
2083    ) -> Result<(), VNextError>;
2084
2085    /// Returns the exact external metadata identity represented by `config`.
2086    /// Every provider must make this selection explicit; core never assumes a
2087    /// singleton catalog row is the intended typed identity.
2088    fn validated_external_metadata_id(
2089        &self,
2090        raw: &serde_json::Value,
2091        config: &Self::Config,
2092    ) -> Result<ExternalModelMetadataId, VNextError>;
2093
2094    fn parse_config(&self, raw: &serde_json::Value) -> Result<Self::Config, VNextError>;
2095
2096    fn weight_schema(&self, config: &Self::Config) -> Result<WeightSchema, VNextError>;
2097
2098    fn numerical_profiles(
2099        &self,
2100        config: &Self::Config,
2101    ) -> Result<FamilyNumericalProfiles, VNextError>;
2102
2103    /// Specialization may change logical decoding dtype, never the source's
2104    /// physical components, layouts, dimensions or tensor identities.
2105    fn specialize_weight_schema(
2106        &self,
2107        _config: &Self::Config,
2108        source: &WeightSchema,
2109        _profile: &NumericalExecutionProfile,
2110    ) -> Result<WeightSchema, VNextError> {
2111        Ok(source.clone())
2112    }
2113
2114    fn semantic_program(
2115        &self,
2116        config: &Self::Config,
2117        profile: &NumericalExecutionProfile,
2118    ) -> Result<ModelProgram, VNextError>;
2119
2120    fn semantic_metadata(&self, config: &Self::Config)
2121        -> Result<ModelSemanticMetadata, VNextError>;
2122}
2123
2124/// Trusted source/semantic definition with no executable program. The typed
2125/// configuration is retained privately so preparation does not parse or
2126/// serialize it a second time, and cannot accept caller-constructed typed data.
2127#[derive(Clone)]
2128pub struct ModelFamilyDefinition {
2129    parts: ModelFamilyDefinitionParts,
2130    provider_authority: Arc<()>,
2131    preparation: Arc<dyn DefinedFamilyPreparation>,
2132}
2133
2134/// The generic implementation retains its configuration's concrete Rust type.
2135/// Only the preparation operation crosses the heterogeneous catalog boundary.
2136trait DefinedFamilyPreparation: Send + Sync {
2137    fn prepare(
2138        &self,
2139        definition: &ModelFamilyDefinition,
2140        profile: &NumericalExecutionProfile,
2141    ) -> Result<PreparedModelFamily, VNextError>;
2142}
2143
2144struct TypedDefinedFamily<P: ModelFamilyProvider> {
2145    provider: Arc<P>,
2146    config: P::Config,
2147}
2148
2149#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
2150struct ModelFamilyDefinitionParts {
2151    family_id: ModelFamilyId,
2152    external_metadata_id: ExternalModelMetadataId,
2153    canonical_config: serde_json::Value,
2154    weight_schema: WeightSchema,
2155    metadata: ModelSemanticMetadata,
2156    numerical_profiles: FamilyNumericalProfiles,
2157}
2158
2159impl std::fmt::Debug for ModelFamilyDefinition {
2160    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2161        formatter
2162            .debug_struct("ModelFamilyDefinition")
2163            .field("parts", &self.parts)
2164            .finish_non_exhaustive()
2165    }
2166}
2167
2168impl ModelFamilyDefinition {
2169    pub fn family_id(&self) -> &ModelFamilyId {
2170        &self.parts.family_id
2171    }
2172    pub fn external_metadata_id(&self) -> &ExternalModelMetadataId {
2173        &self.parts.external_metadata_id
2174    }
2175    pub fn canonical_config(&self) -> &serde_json::Value {
2176        &self.parts.canonical_config
2177    }
2178    pub fn weight_schema(&self) -> &WeightSchema {
2179        &self.parts.weight_schema
2180    }
2181    pub fn metadata(&self) -> &ModelSemanticMetadata {
2182        &self.parts.metadata
2183    }
2184    pub fn numerical_profiles(&self) -> &FamilyNumericalProfiles {
2185        &self.parts.numerical_profiles
2186    }
2187
2188    pub fn fingerprint(&self) -> Result<String, VNextError> {
2189        let bytes = serde_json::to_vec(&self.parts).map_err(|error| VNextError::Serialization {
2190            context: "serialize model family definition",
2191            message: error.to_string(),
2192        })?;
2193        Ok(format!("{:x}", Sha256::digest(bytes)))
2194    }
2195}
2196
2197/// Maximum raw JSON bytes accepted before decoding a prepared family package.
2198pub const MAX_PREPARED_MODEL_FAMILY_WIRE_BYTES: usize = 16 * 1024 * 1024;
2199pub const PREPARED_MODEL_FAMILY_WIRE_VERSION: u32 = 2;
2200
2201#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
2202pub struct PreparedModelFamily {
2203    wire_version: u32,
2204    family_id: ModelFamilyId,
2205    external_metadata_id: ExternalModelMetadataId,
2206    canonical_config: serde_json::Value,
2207    config_fingerprint: String,
2208    numerical_profile: NumericalExecutionProfile,
2209    weight_schema: WeightSchema,
2210    program: ModelProgram,
2211    metadata: ModelSemanticMetadata,
2212}
2213
2214/// Serialized prepared packages are evidence, not trusted runtime objects.
2215/// Rehydration must resolve the typed provider again and reproduce every field.
2216#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
2217pub struct UnvalidatedPreparedModelFamily {
2218    wire_version: u32,
2219    family_id: ModelFamilyId,
2220    external_metadata_id: ExternalModelMetadataId,
2221    canonical_config: serde_json::Value,
2222    config_fingerprint: String,
2223    numerical_profile: NumericalExecutionProfile,
2224    weight_schema: WeightSchema,
2225    program: ModelProgram,
2226    metadata: ModelSemanticMetadata,
2227}
2228
2229/// Crate-private serde shape used by both the top-level decoder and nested
2230/// resolved-plan wire. Public code can only obtain the explicit unvalidated
2231/// package through a byte-bounded decoder.
2232#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
2233pub(crate) struct PreparedModelFamilyWire {
2234    wire_version: u32,
2235    family_id: ModelFamilyId,
2236    external_metadata_id: ExternalModelMetadataId,
2237    canonical_config: serde_json::Value,
2238    config_fingerprint: String,
2239    numerical_profile: NumericalExecutionProfile,
2240    weight_schema: WeightSchema,
2241    program: ModelProgram,
2242    metadata: ModelSemanticMetadata,
2243}
2244
2245#[derive(Deserialize, Serialize)]
2246#[serde(deny_unknown_fields)]
2247struct PreparedModelFamilyWireFields {
2248    wire_version: u32,
2249    family_id: ModelFamilyId,
2250    external_metadata_id: ExternalModelMetadataId,
2251    canonical_config: serde_json::Value,
2252    config_fingerprint: String,
2253    numerical_profile: NumericalExecutionProfile,
2254    weight_schema: WeightSchema,
2255    program: ModelProgram,
2256    metadata: ModelSemanticMetadata,
2257}
2258
2259impl<'de> Deserialize<'de> for PreparedModelFamilyWire {
2260    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2261    where
2262        D: Deserializer<'de>,
2263    {
2264        let raw = serde_json::Value::deserialize(deserializer)?;
2265        let fields =
2266            PreparedModelFamilyWireFields::deserialize(&raw).map_err(serde::de::Error::custom)?;
2267        let canonical = serde_json::to_value(&fields).map_err(serde::de::Error::custom)?;
2268        if canonical != raw {
2269            return Err(serde::de::Error::custom(
2270                "prepared model family wire contains unknown or non-canonical nested fields",
2271            ));
2272        }
2273        if fields.wire_version != PREPARED_MODEL_FAMILY_WIRE_VERSION {
2274            return Err(serde::de::Error::custom(
2275                "incompatible prepared-family wire version; resolve the model again",
2276            ));
2277        }
2278        Ok(Self {
2279            wire_version: fields.wire_version,
2280            family_id: fields.family_id,
2281            external_metadata_id: fields.external_metadata_id,
2282            canonical_config: fields.canonical_config,
2283            config_fingerprint: fields.config_fingerprint,
2284            numerical_profile: fields.numerical_profile,
2285            weight_schema: fields.weight_schema,
2286            program: fields.program,
2287            metadata: fields.metadata,
2288        })
2289    }
2290}
2291
2292impl From<PreparedModelFamilyWire> for UnvalidatedPreparedModelFamily {
2293    fn from(wire: PreparedModelFamilyWire) -> Self {
2294        Self {
2295            wire_version: wire.wire_version,
2296            family_id: wire.family_id,
2297            external_metadata_id: wire.external_metadata_id,
2298            canonical_config: wire.canonical_config,
2299            config_fingerprint: wire.config_fingerprint,
2300            numerical_profile: wire.numerical_profile,
2301            weight_schema: wire.weight_schema,
2302            program: wire.program,
2303            metadata: wire.metadata,
2304        }
2305    }
2306}
2307
2308impl UnvalidatedPreparedModelFamily {
2309    pub fn revalidate(
2310        self,
2311        registry: &dyn ModelFamilyRegistry,
2312    ) -> Result<PreparedModelFamily, VNextError> {
2313        let registration = registry.resolve(&self.family_id)?;
2314        if registration.family_id() != &self.family_id {
2315            return Err(VNextError::InvalidModelConfig {
2316                family_id: self.family_id.to_string(),
2317                field: "registration.family_id".to_owned(),
2318                reason: "registry returned a registration for a different family".to_owned(),
2319            });
2320        }
2321        let metadata_registration = registry.resolve_external(&self.external_metadata_id)?;
2322        if !std::ptr::eq(registration, metadata_registration) {
2323            return Err(VNextError::InvalidModelConfig {
2324                family_id: self.family_id.to_string(),
2325                field: "external_metadata_id".to_owned(),
2326                reason: "external metadata identity resolves to a different family registration"
2327                    .to_owned(),
2328            });
2329        }
2330        let rebuilt = registration
2331            .prepare_with_profile(&self.canonical_config, &self.numerical_profile.id)?;
2332        let exact_match = rebuilt.wire_version == self.wire_version
2333            && rebuilt.numerical_profile == self.numerical_profile
2334            && rebuilt.family_id == self.family_id
2335            && rebuilt.external_metadata_id == self.external_metadata_id
2336            && rebuilt.canonical_config == self.canonical_config
2337            && rebuilt.config_fingerprint == self.config_fingerprint
2338            && rebuilt.weight_schema == self.weight_schema
2339            && rebuilt.program == self.program
2340            && rebuilt.metadata == self.metadata;
2341        if !exact_match {
2342            return Err(VNextError::InvalidModelConfig {
2343                family_id: self.family_id.to_string(),
2344                field: "prepared_package".to_owned(),
2345                reason: "serialized package differs from the typed provider reconstruction"
2346                    .to_owned(),
2347            });
2348        }
2349        Ok(rebuilt)
2350    }
2351}
2352
2353impl PreparedModelFamily {
2354    fn from_canonical_config(
2355        family_id: ModelFamilyId,
2356        external_metadata_id: ExternalModelMetadataId,
2357        canonical_config: serde_json::Value,
2358        mut weight_schema: WeightSchema,
2359        mut numerical_profile: NumericalExecutionProfile,
2360        program: ModelProgram,
2361        metadata: ModelSemanticMetadata,
2362    ) -> Result<Self, VNextError> {
2363        if !canonical_config.is_object()
2364            || canonicalize_json(canonical_config.clone()) != canonical_config
2365        {
2366            return Err(VNextError::InvalidModelConfig {
2367                family_id: family_id.to_string(),
2368                field: "config".to_owned(),
2369                reason: "prepared config must be a canonical JSON object".to_owned(),
2370            });
2371        }
2372        let config_bytes =
2373            serde_json::to_vec(&canonical_config).map_err(|error| VNextError::Serialization {
2374                context: "serialize canonical model family config",
2375                message: error.to_string(),
2376            })?;
2377        let config_fingerprint = format!("{:x}", Sha256::digest(config_bytes));
2378        weight_schema.validate(&family_id)?;
2379        weight_schema.normalize();
2380        weight_schema.validate(&family_id)?;
2381        if program.family_id() != &family_id {
2382            return Err(VNextError::InvalidModelConfig {
2383                family_id: family_id.to_string(),
2384                field: "program.family_id".to_owned(),
2385                reason: "program family does not match prepared family".to_owned(),
2386            });
2387        }
2388        weight_schema.validate_program_references(&family_id, &program)?;
2389        numerical_profile.normalize();
2390        numerical_profile.validate_program(&program)?;
2391        Self::validate_metadata(&family_id, &metadata)?;
2392        Ok(Self {
2393            wire_version: PREPARED_MODEL_FAMILY_WIRE_VERSION,
2394            family_id,
2395            external_metadata_id,
2396            canonical_config,
2397            config_fingerprint,
2398            numerical_profile,
2399            weight_schema,
2400            program,
2401            metadata,
2402        })
2403    }
2404
2405    fn validate_metadata(
2406        family_id: &ModelFamilyId,
2407        metadata: &ModelSemanticMetadata,
2408    ) -> Result<(), VNextError> {
2409        let source = metadata.template.source_file.as_str();
2410        let valid_source = !source.is_empty()
2411            && !source.starts_with('/')
2412            && !source.contains('\\')
2413            && source
2414                .split('/')
2415                .all(|component| !matches!(component, "" | "." | ".."));
2416        if metadata.template.template.is_empty()
2417            || !valid_source
2418            || !is_canonical_sha256(&metadata.template.sha256)
2419            || metadata.special_tokens.eos_token_ids.is_empty()
2420        {
2421            return Err(VNextError::InvalidModelConfig {
2422                family_id: family_id.to_string(),
2423                field: "semantic_metadata".to_owned(),
2424                reason: "template, source, checksum, and end tokens must be explicit and valid"
2425                    .to_owned(),
2426            });
2427        }
2428        Ok(())
2429    }
2430
2431    pub fn family_id(&self) -> &ModelFamilyId {
2432        &self.family_id
2433    }
2434
2435    pub fn external_metadata_id(&self) -> &ExternalModelMetadataId {
2436        &self.external_metadata_id
2437    }
2438
2439    pub fn canonical_config(&self) -> &serde_json::Value {
2440        &self.canonical_config
2441    }
2442
2443    pub fn config_fingerprint(&self) -> &str {
2444        &self.config_fingerprint
2445    }
2446
2447    pub fn weight_schema(&self) -> &WeightSchema {
2448        &self.weight_schema
2449    }
2450
2451    pub fn numerical_profile(&self) -> &NumericalExecutionProfile {
2452        &self.numerical_profile
2453    }
2454
2455    pub fn program(&self) -> &ModelProgram {
2456        &self.program
2457    }
2458
2459    pub fn metadata(&self) -> &ModelSemanticMetadata {
2460        &self.metadata
2461    }
2462
2463    pub fn fingerprint(&self) -> Result<String, VNextError> {
2464        let bytes = serde_json::to_vec(self).map_err(|error| VNextError::Serialization {
2465            context: "serialize prepared model family",
2466            message: error.to_string(),
2467        })?;
2468        Ok(format!("{:x}", Sha256::digest(bytes)))
2469    }
2470
2471    pub fn decode_untrusted(bytes: &[u8]) -> Result<UnvalidatedPreparedModelFamily, VNextError> {
2472        if bytes.len() > MAX_PREPARED_MODEL_FAMILY_WIRE_BYTES {
2473            return Err(VNextError::Serialization {
2474                context: "decode untrusted prepared model family",
2475                message: format!(
2476                    "payload has {} bytes; maximum is {MAX_PREPARED_MODEL_FAMILY_WIRE_BYTES}",
2477                    bytes.len()
2478                ),
2479            });
2480        }
2481        serde_json::from_slice::<PreparedModelFamilyWire>(bytes)
2482            .map(Into::into)
2483            .map_err(|error| VNextError::Serialization {
2484                context: "decode untrusted prepared model family",
2485                message: error.to_string(),
2486            })
2487    }
2488
2489    pub fn from_json_validated(
2490        bytes: &[u8],
2491        registry: &dyn ModelFamilyRegistry,
2492    ) -> Result<Self, VNextError> {
2493        Self::decode_untrusted(bytes)?.revalidate(registry)
2494    }
2495}
2496
2497fn canonicalize_json(value: serde_json::Value) -> serde_json::Value {
2498    match value {
2499        serde_json::Value::Array(values) => {
2500            serde_json::Value::Array(values.into_iter().map(canonicalize_json).collect())
2501        }
2502        serde_json::Value::Object(values) => {
2503            let sorted = values
2504                .into_iter()
2505                .map(|(key, value)| (key, canonicalize_json(value)))
2506                .collect::<BTreeMap<_, _>>();
2507            serde_json::Value::Object(sorted.into_iter().collect())
2508        }
2509        other => other,
2510    }
2511}
2512
2513fn validate_raw_config_consumed(
2514    family_id: &ModelFamilyId,
2515    raw: &serde_json::Value,
2516    typed: &serde_json::Value,
2517) -> Result<(), VNextError> {
2518    // Typed serialization may add explicit provider defaults. Every caller-
2519    // supplied value must still survive at the same path with the same JSON
2520    // value; serde's default unknown-field behavior cannot erase input here.
2521    fn walk(raw: &serde_json::Value, typed: &serde_json::Value, path: &str) -> Option<String> {
2522        match (raw, typed) {
2523            (serde_json::Value::Object(raw), serde_json::Value::Object(typed)) => {
2524                for (key, raw_value) in raw {
2525                    let next = if path.is_empty() {
2526                        format!("/{key}")
2527                    } else {
2528                        format!("{path}/{key}")
2529                    };
2530                    let Some(typed_value) = typed.get(key) else {
2531                        return Some(next);
2532                    };
2533                    if let Some(rejected) = walk(raw_value, typed_value, &next) {
2534                        return Some(rejected);
2535                    }
2536                }
2537                None
2538            }
2539            (serde_json::Value::Array(raw), serde_json::Value::Array(typed))
2540                if raw.len() == typed.len() =>
2541            {
2542                raw.iter()
2543                    .zip(typed)
2544                    .enumerate()
2545                    .find_map(|(index, (raw, typed))| walk(raw, typed, &format!("{path}/{index}")))
2546            }
2547            _ if raw == typed => None,
2548            _ => Some(path.to_owned()),
2549        }
2550    }
2551
2552    if !raw.is_object() || !typed.is_object() {
2553        return Err(VNextError::InvalidModelConfig {
2554            family_id: family_id.to_string(),
2555            field: "config".to_owned(),
2556            reason: "raw and typed model configurations must be JSON objects".to_owned(),
2557        });
2558    }
2559    if let Some(path) = walk(raw, typed, "") {
2560        return Err(VNextError::InvalidModelConfig {
2561            family_id: family_id.to_string(),
2562            field: if path.is_empty() {
2563                "config".to_owned()
2564            } else {
2565                path
2566            },
2567            reason: "raw configuration field was ignored or changed by typed parsing".to_owned(),
2568        });
2569    }
2570    Ok(())
2571}
2572
2573fn is_canonical_sha256(value: &str) -> bool {
2574    value.len() == 64
2575        && value
2576            .bytes()
2577            .all(|byte| byte.is_ascii_digit() || (b'a'..=b'f').contains(&byte))
2578}
2579
2580/// Object-safe loading-time trampoline for a heterogeneous family catalog.
2581/// The raw JSON exists only at the configuration boundary; a typed provider
2582/// validates it before producing the backend-free package.
2583pub trait ModelFamilyRegistration: Send + Sync {
2584    fn family_id(&self) -> &ModelFamilyId;
2585
2586    fn external_metadata_ids(&self) -> BTreeSet<ExternalModelMetadataId>;
2587
2588    fn define(&self, raw_config: &serde_json::Value) -> Result<ModelFamilyDefinition, VNextError>;
2589
2590    fn prepare(
2591        &self,
2592        definition: &ModelFamilyDefinition,
2593        profile: &NumericalProfileId,
2594    ) -> Result<PreparedModelFamily, VNextError>;
2595
2596    fn prepare_with_profile(
2597        &self,
2598        raw_config: &serde_json::Value,
2599        profile: &NumericalProfileId,
2600    ) -> Result<PreparedModelFamily, VNextError> {
2601        self.prepare(&self.define(raw_config)?, profile)
2602    }
2603}
2604
2605pub struct TypedFamilyRegistration<P> {
2606    provider: Arc<P>,
2607    authority: Arc<()>,
2608}
2609
2610impl<P> TypedFamilyRegistration<P> {
2611    pub fn new(provider: P) -> Self {
2612        Self {
2613            provider: Arc::new(provider),
2614            authority: Arc::new(()),
2615        }
2616    }
2617}
2618
2619impl<P: ModelFamilyProvider> ModelFamilyRegistration for TypedFamilyRegistration<P> {
2620    fn family_id(&self) -> &ModelFamilyId {
2621        self.provider.family_id()
2622    }
2623
2624    fn external_metadata_ids(&self) -> BTreeSet<ExternalModelMetadataId> {
2625        self.provider.external_metadata_ids()
2626    }
2627
2628    fn define(&self, raw_config: &serde_json::Value) -> Result<ModelFamilyDefinition, VNextError> {
2629        let external_metadata_ids = self.provider.external_metadata_ids();
2630        if external_metadata_ids.is_empty() {
2631            return Err(VNextError::InvalidModelConfig {
2632                family_id: self.provider.family_id().to_string(),
2633                field: "external_metadata_ids".to_owned(),
2634                reason: "model family must declare at least one external metadata identity"
2635                    .to_owned(),
2636            });
2637        }
2638        let config = self.provider.parse_config(raw_config)?;
2639        let external_metadata_id = self
2640            .provider
2641            .validated_external_metadata_id(raw_config, &config)?;
2642        if !external_metadata_ids.contains(&external_metadata_id) {
2643            return Err(VNextError::InvalidModelConfig {
2644                family_id: self.provider.family_id().to_string(),
2645                field: "external_metadata_id".to_owned(),
2646                reason: format!(
2647                    "provider selected undeclared external metadata identity `{external_metadata_id}`"
2648                ),
2649            });
2650        }
2651        let typed_config = canonicalize_json(serde_json::to_value(&config).map_err(|error| {
2652            VNextError::Serialization {
2653                context: "serialize typed model configuration",
2654                message: error.to_string(),
2655            }
2656        })?);
2657        validate_raw_config_consumed(self.provider.family_id(), raw_config, &typed_config)?;
2658        let mut weight_schema = self.provider.weight_schema(&config)?;
2659        weight_schema.validate(self.provider.family_id())?;
2660        weight_schema.normalize();
2661        let metadata = self.provider.semantic_metadata(&config)?;
2662        PreparedModelFamily::validate_metadata(self.provider.family_id(), &metadata)?;
2663        let numerical_profiles = self.provider.numerical_profiles(&config)?;
2664        if numerical_profiles
2665            .profiles()
2666            .iter()
2667            .any(|profile| &profile.family_id != self.provider.family_id())
2668        {
2669            return Err(VNextError::InvalidModelConfig {
2670                family_id: self.provider.family_id().to_string(),
2671                field: "numerical_profiles".to_owned(),
2672                reason: "profile catalog belongs to another model family".to_owned(),
2673            });
2674        }
2675        Ok(ModelFamilyDefinition {
2676            parts: ModelFamilyDefinitionParts {
2677                family_id: self.provider.family_id().clone(),
2678                external_metadata_id,
2679                canonical_config: typed_config,
2680                weight_schema,
2681                metadata,
2682                numerical_profiles,
2683            },
2684            provider_authority: self.authority.clone(),
2685            preparation: Arc::new(TypedDefinedFamily {
2686                provider: self.provider.clone(),
2687                config,
2688            }),
2689        })
2690    }
2691
2692    fn prepare(
2693        &self,
2694        definition: &ModelFamilyDefinition,
2695        profile: &NumericalProfileId,
2696    ) -> Result<PreparedModelFamily, VNextError> {
2697        let incompatible = || VNextError::InvalidModelConfig {
2698            family_id: self.provider.family_id().to_string(),
2699            field: "definition".to_owned(),
2700            reason: "definition was produced by another family/provider".to_owned(),
2701        };
2702        if !Arc::ptr_eq(&definition.provider_authority, &self.authority)
2703            || definition.family_id() != self.provider.family_id()
2704        {
2705            return Err(incompatible());
2706        }
2707        let selected = definition.numerical_profiles().resolve(profile)?;
2708        definition.preparation.prepare(definition, selected)
2709    }
2710}
2711
2712impl<P: ModelFamilyProvider> DefinedFamilyPreparation for TypedDefinedFamily<P> {
2713    fn prepare(
2714        &self,
2715        definition: &ModelFamilyDefinition,
2716        selected: &NumericalExecutionProfile,
2717    ) -> Result<PreparedModelFamily, VNextError> {
2718        let weight_schema = self.provider.specialize_weight_schema(
2719            &self.config,
2720            definition.weight_schema(),
2721            selected,
2722        )?;
2723        let mut physical_identity = weight_schema.clone();
2724        physical_identity.normalize();
2725        for tensor in &mut physical_identity.tensors {
2726            let source = definition
2727                .weight_schema()
2728                .tensor(&tensor.id)
2729                .ok_or_else(|| VNextError::InvalidModelConfig {
2730                    family_id: self.provider.family_id().to_string(),
2731                    field: "numerical_profile.weight_schema".to_owned(),
2732                    reason: "numerical specialization introduced a source tensor".to_owned(),
2733                })?;
2734            tensor.logical_element_type = source.logical_element_type;
2735        }
2736        if &physical_identity != definition.weight_schema() {
2737            return Err(VNextError::InvalidModelConfig {
2738                family_id: self.provider.family_id().to_string(),
2739                field: "numerical_profile.weight_schema".to_owned(),
2740                reason: "numerical specialization changed source physical identity".to_owned(),
2741            });
2742        }
2743        let program = self.provider.semantic_program(&self.config, selected)?;
2744        PreparedModelFamily::from_canonical_config(
2745            self.provider.family_id().clone(),
2746            definition.external_metadata_id().clone(),
2747            definition.canonical_config().clone(),
2748            weight_schema,
2749            selected.clone(),
2750            program,
2751            definition.metadata().clone(),
2752        )
2753    }
2754}
2755
2756pub trait ModelFamilyRegistry: Send + Sync {
2757    /// Returns the complete trusted catalog. Core owns all identity lookup so a
2758    /// registry cannot silently fall back or hide ambiguous registrations.
2759    fn registrations(&self) -> Vec<&dyn ModelFamilyRegistration>;
2760}
2761
2762impl dyn ModelFamilyRegistry + '_ {
2763    pub fn resolve(
2764        &self,
2765        family_id: &ModelFamilyId,
2766    ) -> Result<&dyn ModelFamilyRegistration, VNextError> {
2767        let matches = self
2768            .registrations()
2769            .into_iter()
2770            .filter(|registration| registration.family_id() == family_id)
2771            .collect::<Vec<_>>();
2772        match matches.as_slice() {
2773            [] => Err(VNextError::UnknownModelFamily {
2774                family_id: family_id.to_string(),
2775            }),
2776            [registration] => Ok(*registration),
2777            _ => Err(VNextError::AmbiguousModelFamilyRegistration {
2778                identity_kind: "internal family",
2779                identity: family_id.to_string(),
2780                matches: matches.len(),
2781            }),
2782        }
2783    }
2784
2785    pub fn resolve_external(
2786        &self,
2787        metadata_id: &ExternalModelMetadataId,
2788    ) -> Result<&dyn ModelFamilyRegistration, VNextError> {
2789        let matches = self
2790            .registrations()
2791            .into_iter()
2792            .filter(|registration| registration.external_metadata_ids().contains(metadata_id))
2793            .collect::<Vec<_>>();
2794        match matches.as_slice() {
2795            [] => Err(VNextError::UnknownExternalModelMetadata {
2796                metadata_id: metadata_id.to_string(),
2797            }),
2798            [registration] => Ok(*registration),
2799            _ => Err(VNextError::AmbiguousModelFamilyRegistration {
2800                identity_kind: "external metadata",
2801                identity: metadata_id.to_string(),
2802                matches: matches.len(),
2803            }),
2804        }
2805    }
2806}
2807
2808#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2809pub struct TokenizerDescriptor {
2810    pub tokenizer_id: TokenizerId,
2811    pub source_file: String,
2812    pub sha256: String,
2813    pub vocabulary_size: u64,
2814}