1use serde::Serialize;
2use std::collections::{BTreeMap, BTreeSet};
3
4use crate::vnext::{
5 PreparedModelFamily, ResolvedModelPlan, StaticInitializationReceipt, VNextError,
6 WeightComponentRole, WeightEncoding, WeightId, IDENTITY_WEIGHT_MATERIALIZER_ID,
7};
8
9use super::foundation::canonical_fingerprint;
10
11pub const PROVIDER_ATTRIBUTION_WITNESS_SCHEMA: &str = "ferrum.vnext.provider-attribution.v1";
12pub const PROVIDER_ATTRIBUTION_STATIC_BASIS: &str =
13 "resolved_plan_and_completed_static_initialization";
14pub const PROVIDER_ATTRIBUTION_STATIC_FALLBACK_BASIS: &str =
15 "fail_closed_quantized_execution_component_and_vnext_plan_binding";
16
17fn invalid_attribution(reason: impl Into<String>) -> VNextError {
18 VNextError::InvalidExecutionPlan {
19 reason: format!("provider attribution: {}", reason.into()),
20 }
21}
22
23fn is_quantized_values(role: WeightComponentRole, encoding: &WeightEncoding) -> bool {
24 role == WeightComponentRole::PackedValues
25 && matches!(
26 encoding,
27 WeightEncoding::Quantized(_) | WeightEncoding::BlockQuantized(_)
28 )
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
32struct ProviderAttributionDenominatorMaterial<'a> {
33 operations: &'a BTreeSet<String>,
34 quant_tensors: &'a BTreeSet<String>,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct QuantizedProviderAttributionDenominator {
42 quant_tensors: BTreeSet<String>,
43 operations: BTreeSet<String>,
44 tensor_operations: BTreeMap<String, String>,
45 source_component_tensors: BTreeMap<WeightId, BTreeSet<String>>,
46 source_quantization_format_ids: BTreeSet<String>,
47 sha256: String,
48}
49
50impl QuantizedProviderAttributionDenominator {
51 pub fn from_prepared_family(family: &PreparedModelFamily) -> Result<Option<Self>, VNextError> {
52 let schema = family.weight_schema();
53 let weights_by_value = family
54 .program()
55 .weights()
56 .iter()
57 .map(|weight| (&weight.value_id, &weight.weight_id))
58 .collect::<BTreeMap<_, _>>();
59 let components_by_id = schema
60 .components
61 .iter()
62 .map(|component| (&component.id, component))
63 .collect::<BTreeMap<_, _>>();
64
65 let mut tensor_operation_sets = BTreeMap::<String, BTreeSet<String>>::new();
66 let mut source_component_tensors = BTreeMap::<WeightId, BTreeSet<String>>::new();
67 let mut source_quantization_format_ids = BTreeSet::new();
68 for node in family
69 .program()
70 .blocks()
71 .iter()
72 .flat_map(|block| &block.nodes)
73 {
74 for input in &node.inputs {
75 let Some(weight_id) = weights_by_value.get(input) else {
76 continue;
77 };
78 for component in schema.physical_component_refs(weight_id)? {
79 if !is_quantized_values(component.role, &component.encoding) {
80 continue;
81 }
82 let format_id = match &component.encoding {
83 WeightEncoding::Quantized(spec) => spec.format_id.as_str(),
84 WeightEncoding::BlockQuantized(spec) => spec.format_id.as_str(),
85 WeightEncoding::Dense { .. } | WeightEncoding::DenseAffine { .. } => {
86 unreachable!("filtered quantized source component")
87 }
88 };
89 source_quantization_format_ids.insert(format_id.to_owned());
90 let names = source_component_tensors
91 .entry(component.id.clone())
92 .or_default();
93 for external_name in &component.external_names {
94 names.insert(external_name.clone());
95 tensor_operation_sets
96 .entry(external_name.clone())
97 .or_default()
98 .insert(node.operation_id.to_string());
99 }
100 }
101 }
102 }
103 if tensor_operation_sets.is_empty() {
104 return Ok(None);
105 }
106
107 let referenced_source_components = source_component_tensors.keys().collect::<BTreeSet<_>>();
108 let unowned_quantized_component = components_by_id.values().find(|component| {
109 component.required
110 && is_quantized_values(component.role, &component.encoding)
111 && !referenced_source_components.contains(&component.id)
112 });
113 if let Some(component) = unowned_quantized_component {
114 return Err(invalid_attribution(format!(
115 "required quantized source component `{}` has no semantic operation owner",
116 component.id
117 )));
118 }
119
120 let mut tensor_operations = BTreeMap::new();
121 for (tensor, operations) in tensor_operation_sets {
122 if operations.len() != 1 {
123 return Err(invalid_attribution(format!(
124 "quantized source tensor `{tensor}` does not have exactly one operation owner"
125 )));
126 }
127 tensor_operations.insert(
128 tensor,
129 operations
130 .into_iter()
131 .next()
132 .expect("one checked operation owner"),
133 );
134 }
135 let quant_tensors = tensor_operations.keys().cloned().collect::<BTreeSet<_>>();
136 let operations = tensor_operations.values().cloned().collect::<BTreeSet<_>>();
137 let sha256 = canonical_fingerprint(
138 &ProviderAttributionDenominatorMaterial {
139 operations: &operations,
140 quant_tensors: &quant_tensors,
141 },
142 "fingerprint quantized provider attribution denominator",
143 )?;
144 Ok(Some(Self {
145 quant_tensors,
146 operations,
147 tensor_operations,
148 source_component_tensors,
149 source_quantization_format_ids,
150 sha256,
151 }))
152 }
153
154 pub fn quant_tensor_count(&self) -> usize {
155 self.quant_tensors.len()
156 }
157
158 pub fn operation_count(&self) -> usize {
159 self.operations.len()
160 }
161
162 pub fn item_count(&self) -> usize {
163 self.quant_tensor_count() + self.operation_count()
164 }
165
166 pub fn sha256(&self) -> &str {
167 &self.sha256
168 }
169
170 pub fn source_quantization_format_ids(&self) -> &BTreeSet<String> {
171 &self.source_quantization_format_ids
172 }
173}
174
175#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
176struct ProviderMappingRow {
177 quant_tensor: String,
178 node_id: String,
179 operation_id: String,
180 provider_id: String,
181 provider_implementation_fingerprint: String,
182}
183
184#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
185pub struct OperationProviderAttributionBinding {
186 operation_id: String,
187 provider_id: String,
188 provider_implementation_fingerprint: String,
189 quant_tensor_count: u64,
190}
191
192#[derive(Debug, Clone, PartialEq, Serialize)]
193pub struct ProviderAttributionCounts {
194 expected_quant_tensor_count: u64,
195 attributed_quant_tensor_count: u64,
196 expected_operation_count: u64,
197 attributed_operation_count: u64,
198 expected_item_count: u64,
199 attributed_item_count: u64,
200 percent: f64,
201 denominator_sha256: String,
202}
203
204impl ProviderAttributionCounts {
205 pub const fn expected_quant_tensor_count(&self) -> u64 {
206 self.expected_quant_tensor_count
207 }
208
209 pub const fn attributed_quant_tensor_count(&self) -> u64 {
210 self.attributed_quant_tensor_count
211 }
212
213 pub const fn expected_operation_count(&self) -> u64 {
214 self.expected_operation_count
215 }
216
217 pub const fn attributed_operation_count(&self) -> u64 {
218 self.attributed_operation_count
219 }
220
221 pub const fn expected_item_count(&self) -> u64 {
222 self.expected_item_count
223 }
224
225 pub const fn attributed_item_count(&self) -> u64 {
226 self.attributed_item_count
227 }
228
229 pub const fn percent(&self) -> f64 {
230 self.percent
231 }
232
233 pub fn denominator_sha256(&self) -> &str {
234 &self.denominator_sha256
235 }
236}
237
238#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
239pub struct ProviderAttributionFallbackCounts {
240 silent: u64,
241 dense: u64,
242 legacy: u64,
243}
244
245impl ProviderAttributionFallbackCounts {
246 pub const fn silent(&self) -> u64 {
247 self.silent
248 }
249
250 pub const fn dense(&self) -> u64 {
251 self.dense
252 }
253
254 pub const fn legacy(&self) -> u64 {
255 self.legacy
256 }
257}
258
259#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
260pub struct ProviderAttributionBinding {
261 prepared_family_fingerprint: String,
262 source_schema_fingerprint: String,
263 source_weight_format_id: String,
264 source_weight_layout_id: String,
265 source_quantization_format_ids: BTreeSet<String>,
266 execution_weight_plan_fingerprint: String,
267 execution_schema_fingerprint: String,
268 execution_weight_format_id: String,
269 execution_weight_layout_id: String,
270 execution_quantization_format_ids: BTreeSet<String>,
271 materializer_id: String,
272 materializer_version: super::ContractVersion,
273 materializer_implementation_fingerprint: String,
274 execution_contract_fingerprint: Option<String>,
275 quality_vector_digest: Option<String>,
276 quality_artifact_sha256: Option<String>,
277 resolved_plan_fingerprint: String,
278 plan_id: String,
279 plan_hash: String,
280 operation_provider_bindings: Vec<OperationProviderAttributionBinding>,
281 provider_mapping_sha256: String,
282 static_initialized_resource_count: u64,
283 static_uploaded_component_count: u64,
284 static_imported_component_count: u64,
285}
286
287impl ProviderAttributionBinding {
288 pub fn execution_contract_fingerprint(&self) -> Option<&str> {
289 self.execution_contract_fingerprint.as_deref()
290 }
291
292 pub fn quality_vector_digest(&self) -> Option<&str> {
293 self.quality_vector_digest.as_deref()
294 }
295
296 pub fn plan_hash(&self) -> &str {
297 &self.plan_hash
298 }
299
300 pub fn provider_mapping_sha256(&self) -> &str {
301 &self.provider_mapping_sha256
302 }
303}
304
305#[derive(Debug, Clone, PartialEq, Serialize)]
313pub struct StaticProviderAttributionWitness {
314 schema: &'static str,
315 attribution_basis: &'static str,
316 fallback_basis: &'static str,
317 provider_attribution: ProviderAttributionCounts,
318 fallback_counts: ProviderAttributionFallbackCounts,
319 binding: ProviderAttributionBinding,
320}
321
322impl StaticProviderAttributionWitness {
323 pub fn from_completed_static_initialization(
324 resolved: &ResolvedModelPlan,
325 receipt: &StaticInitializationReceipt,
326 ) -> Result<Option<Self>, VNextError> {
327 let family = &resolved.parts().prepared_family;
328 let plan = resolved.execution_plan();
329 let payload = plan.payload();
330 let execution_weights = payload.execution_weights();
331 if execution_weights.materializer_id().as_str() == IDENTITY_WEIGHT_MATERIALIZER_ID
336 && execution_weights.approximate_quality_approval().is_none()
337 {
338 return Ok(None);
339 }
340 let Some(denominator) =
341 QuantizedProviderAttributionDenominator::from_prepared_family(family)?
342 else {
343 return Ok(None);
344 };
345 let source_components = family
346 .weight_schema()
347 .components
348 .iter()
349 .map(|component| (&component.id, component))
350 .collect::<BTreeMap<_, _>>();
351 let mut mapping_rows = BTreeSet::new();
352 let mut attributed_tensors = BTreeSet::new();
353 let mut attributed_operations = BTreeSet::new();
354
355 for node in payload.nodes() {
356 for value in node.values() {
357 let Some(weight) = value.weight() else {
358 continue;
359 };
360 for execution_component in weight.components() {
361 let source_ids = execution_weights
362 .component_sources()
363 .get(execution_component.component_id())
364 .ok_or_else(|| {
365 invalid_attribution(format!(
366 "execution component `{}` has no source provenance",
367 execution_component.component_id()
368 ))
369 })?;
370 let denominator_source_ids = source_ids
371 .iter()
372 .filter(|source_id| {
373 denominator
374 .source_component_tensors
375 .contains_key(*source_id)
376 })
377 .collect::<Vec<_>>();
378 if denominator_source_ids.is_empty() {
379 continue;
380 }
381 if !is_quantized_values(
382 execution_component.role(),
383 execution_component.encoding(),
384 ) {
385 if matches!(
386 execution_component.role(),
387 WeightComponentRole::Values | WeightComponentRole::PackedValues
388 ) {
389 return Err(invalid_attribution(format!(
390 "denominator source components map to dense execution values `{}`",
391 execution_component.component_id()
392 )));
393 }
394 continue;
395 }
396 for source_id in denominator_source_ids {
397 let Some(tensors) = denominator.source_component_tensors.get(source_id)
398 else {
399 unreachable!("filtered denominator source component")
400 };
401 let source_component =
402 source_components.get(source_id).ok_or_else(|| {
403 invalid_attribution(format!(
404 "source component `{source_id}` is absent from the prepared family"
405 ))
406 })?;
407 if !is_quantized_values(source_component.role, &source_component.encoding) {
408 return Err(invalid_attribution(format!(
409 "quantized execution component `{}` maps a denominator tensor through non-quantized source `{source_id}`",
410 execution_component.component_id()
411 )));
412 }
413 for tensor in tensors {
414 let expected_operation = denominator
415 .tensor_operations
416 .get(tensor)
417 .expect("denominator tensor has one operation owner");
418 if node.operation_id().as_str() != expected_operation {
419 return Err(invalid_attribution(format!(
420 "quantized source tensor `{tensor}` is bound to operation `{}` instead of `{expected_operation}`",
421 node.operation_id()
422 )));
423 }
424 attributed_tensors.insert(tensor.clone());
425 attributed_operations.insert(expected_operation.clone());
426 mapping_rows.insert(ProviderMappingRow {
427 quant_tensor: tensor.clone(),
428 node_id: node.id().to_string(),
429 operation_id: expected_operation.clone(),
430 provider_id: node.selection().selected_provider().to_string(),
431 provider_implementation_fingerprint: node
432 .provider_implementation_fingerprint()
433 .to_owned(),
434 });
435 }
436 }
437 }
438 }
439 }
440 if attributed_tensors != denominator.quant_tensors
441 || attributed_operations != denominator.operations
442 {
443 return Err(invalid_attribution(format!(
444 "resolved quantized provider mapping covers {}/{} tensors and {}/{} operations",
445 attributed_tensors.len(),
446 denominator.quant_tensors.len(),
447 attributed_operations.len(),
448 denominator.operations.len()
449 )));
450 }
451 if mapping_rows.len() != denominator.quant_tensors.len() {
452 return Err(invalid_attribution(format!(
453 "resolved provider mapping has {} rows for {} denominator tensors",
454 mapping_rows.len(),
455 denominator.quant_tensors.len()
456 )));
457 }
458
459 let provider_mapping_sha256 =
460 canonical_fingerprint(&mapping_rows, "fingerprint quantized provider mapping")?;
461 let mut operation_provider_tensors =
462 BTreeMap::<(String, String, String), BTreeSet<String>>::new();
463 for row in &mapping_rows {
464 operation_provider_tensors
465 .entry((
466 row.operation_id.clone(),
467 row.provider_id.clone(),
468 row.provider_implementation_fingerprint.clone(),
469 ))
470 .or_default()
471 .insert(row.quant_tensor.clone());
472 }
473 let operation_provider_bindings = operation_provider_tensors
474 .into_iter()
475 .map(
476 |((operation_id, provider_id, provider_implementation_fingerprint), tensors)| {
477 Ok(OperationProviderAttributionBinding {
478 operation_id,
479 provider_id,
480 provider_implementation_fingerprint,
481 quant_tensor_count: u64::try_from(tensors.len()).map_err(|_| {
482 invalid_attribution("operation quant tensor count exceeds u64")
483 })?,
484 })
485 },
486 )
487 .collect::<Result<Vec<_>, VNextError>>()?;
488 if operation_provider_bindings.len() != denominator.operations.len() {
489 return Err(invalid_attribution(format!(
490 "{} denominator operations resolve to {} provider bindings",
491 denominator.operations.len(),
492 operation_provider_bindings.len()
493 )));
494 }
495 let family_fingerprint = family.fingerprint()?;
496 let source_schema_fingerprint = family.weight_schema().fingerprint()?;
497 if execution_weights.source_schema_fingerprint() != source_schema_fingerprint {
498 return Err(invalid_attribution(
499 "execution weight plan source schema differs from the live prepared family",
500 ));
501 }
502 let approval = execution_weights.approximate_quality_approval();
503 let expected_quant_tensor_count = u64::try_from(denominator.quant_tensors.len())
504 .map_err(|_| invalid_attribution("quant tensor count exceeds u64"))?;
505 let expected_operation_count = u64::try_from(denominator.operations.len())
506 .map_err(|_| invalid_attribution("operation count exceeds u64"))?;
507 let expected_item_count = expected_quant_tensor_count
508 .checked_add(expected_operation_count)
509 .ok_or_else(|| invalid_attribution("denominator item count exceeds u64"))?;
510 let provider_attribution = ProviderAttributionCounts {
511 expected_quant_tensor_count,
512 attributed_quant_tensor_count: expected_quant_tensor_count,
513 expected_operation_count,
514 attributed_operation_count: expected_operation_count,
515 expected_item_count,
516 attributed_item_count: expected_item_count,
517 percent: 100.0,
518 denominator_sha256: denominator.sha256,
519 };
520 let binding = ProviderAttributionBinding {
521 prepared_family_fingerprint: family_fingerprint,
522 source_schema_fingerprint,
523 source_weight_format_id: family.weight_schema().format_id.to_string(),
524 source_weight_layout_id: family.weight_schema().layout_id.to_string(),
525 source_quantization_format_ids: denominator.source_quantization_format_ids,
526 execution_weight_plan_fingerprint: execution_weights.fingerprint()?,
527 execution_schema_fingerprint: execution_weights.schema().fingerprint()?,
528 execution_weight_format_id: execution_weights.schema().format_id.to_string(),
529 execution_weight_layout_id: execution_weights.schema().layout_id.to_string(),
530 execution_quantization_format_ids: execution_weights
531 .schema()
532 .quantization_formats()
533 .into_iter()
534 .map(|format| format.to_string())
535 .collect(),
536 materializer_id: execution_weights.materializer_id().to_string(),
537 materializer_version: execution_weights.materializer_version(),
538 materializer_implementation_fingerprint: execution_weights
539 .materializer_implementation_fingerprint()
540 .to_owned(),
541 execution_contract_fingerprint: approval
542 .map(|approval| approval.execution_contract_fingerprint().to_owned()),
543 quality_vector_digest: approval
544 .map(|approval| approval.quality_vector_digest().to_owned()),
545 quality_artifact_sha256: approval.map(|approval| approval.artifact_sha256().to_owned()),
546 resolved_plan_fingerprint: resolved.fingerprint().to_owned(),
547 plan_id: payload.plan_id().to_string(),
548 plan_hash: plan.plan_hash().to_string(),
549 operation_provider_bindings,
550 provider_mapping_sha256,
551 static_initialized_resource_count: u64::try_from(receipt.initialized_resource_count())
552 .map_err(|_| invalid_attribution("initialized resource count exceeds u64"))?,
553 static_uploaded_component_count: u64::try_from(receipt.uploaded_component_count())
554 .map_err(|_| invalid_attribution("uploaded component count exceeds u64"))?,
555 static_imported_component_count: u64::try_from(receipt.imported_component_count())
556 .map_err(|_| invalid_attribution("imported component count exceeds u64"))?,
557 };
558 Ok(Some(Self {
559 schema: PROVIDER_ATTRIBUTION_WITNESS_SCHEMA,
560 attribution_basis: PROVIDER_ATTRIBUTION_STATIC_BASIS,
561 fallback_basis: PROVIDER_ATTRIBUTION_STATIC_FALLBACK_BASIS,
562 provider_attribution,
563 fallback_counts: ProviderAttributionFallbackCounts {
564 silent: 0,
565 dense: 0,
566 legacy: 0,
567 },
568 binding,
569 }))
570 }
571
572 pub fn provider_attribution(&self) -> &ProviderAttributionCounts {
573 &self.provider_attribution
574 }
575
576 pub fn fallback_counts(&self) -> &ProviderAttributionFallbackCounts {
577 &self.fallback_counts
578 }
579
580 pub fn binding(&self) -> &ProviderAttributionBinding {
581 &self.binding
582 }
583}
584
585#[cfg(test)]
586mod tests {
587 use super::*;
588 use sha2::{Digest, Sha256};
589
590 #[test]
591 fn denominator_fingerprint_matches_the_release_canonical_json_contract() {
592 let operations = BTreeSet::from([
593 "operation.causal_paged_attention".to_owned(),
594 "operation.dense_swiglu".to_owned(),
595 "operation.gated_delta_recurrent_attention".to_owned(),
596 ]);
597 let quant_tensors = BTreeSet::from([
598 "model.layers.0.mlp.down_proj.weight".to_owned(),
599 "model.layers.0.mlp.gate_proj.weight".to_owned(),
600 ]);
601 let digest = canonical_fingerprint(
602 &ProviderAttributionDenominatorMaterial {
603 operations: &operations,
604 quant_tensors: &quant_tensors,
605 },
606 "test provider attribution denominator",
607 )
608 .unwrap();
609 let expected = Sha256::digest(
610 br#"{"operations":["operation.causal_paged_attention","operation.dense_swiglu","operation.gated_delta_recurrent_attention"],"quant_tensors":["model.layers.0.mlp.down_proj.weight","model.layers.0.mlp.gate_proj.weight"]}"#,
611 );
612 assert_eq!(digest, format!("{expected:x}"));
613 }
614}