Skip to main content

ferrum_interfaces/vnext/execution/
attribution.rs

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/// Canonical M0-compatible denominator derived from the prepared semantic
38/// family. Tensor names remain private; the public witness exposes only this
39/// commitment and its exact tensor/operation cardinalities.
40#[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/// Compact, fail-closed witness for quantized provider attribution.
306///
307/// This first version is intentionally plan-static: construction requires the
308/// receipt that only exists after atomic static initialization completes. It
309/// proves every M0 denominator tensor is mapped through a quantized execution
310/// component to one selected vNext plan node/provider. It does not claim
311/// per-request normal-retirement coverage.
312#[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        // The v1 witness assumes one operation owner per source tensor. The
332        // identity path can legally reuse a tied quantized embedding for both
333        // token embedding and logits, so this optional witness must not block
334        // startup on that path. Other exact materializers still emit it.
335        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}