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