Skip to main content

ferrum_interfaces/vnext/execution/
policy.rs

1use super::{
2    canonical_fingerprint, invalid_plan, CapabilityCatalog, CompletionRetentionSpec,
3    ContractVersion, DynamicStorageProfile, ExecutionWeightPlan, PlanNodeResolution,
4    PreparedModelFamily, ReusableExecutionPolicy, Serialize, TrustedExecutionWeightPlan,
5    VNextError,
6};
7use crate::vnext::ExecutionDeterminismRequirement;
8use ferrum_types::AttentionExecutionPolicy;
9
10/// Typed policy selected before planning. Memory capacity is part of the
11/// public policy contract so a plan cannot depend on an undocumented env var.
12pub trait RuntimePolicy:
13    Clone + Send + Sync + Serialize + serde::de::DeserializeOwned + std::fmt::Debug + 'static
14{
15    fn version(&self) -> ContractVersion;
16
17    /// Policy memory ceiling before the explicit reserve is subtracted. It may
18    /// be lower than, but never exceed, the raw device capacity.
19    fn memory_capacity_bytes(&self) -> u64;
20
21    fn memory_reserve_bytes(&self) -> u64;
22
23    /// A non-zero protocol ceiling only. Planning must not reserve, claim,
24    /// iterate, or materialize resources up to this value.
25    fn maximum_active_sequences(&self) -> u32;
26
27    /// A non-zero ceiling for tokens selected into one scheduler step. This is
28    /// not installed capacity: admission still evaluates the exact step shape
29    /// against then-live backing before any provider work is encoded.
30    fn maximum_scheduled_tokens(&self) -> u64;
31
32    /// Provider-family policy sealed into the plan fingerprint. Physical
33    /// kernel variants remain an invocation-time provider decision.
34    fn attention_execution_policy(&self) -> AttentionExecutionPolicy;
35
36    /// Minimum repeatability and reusable-execution equivalence that every
37    /// selected operation provider must prove.
38    fn execution_determinism_requirement(&self) -> ExecutionDeterminismRequirement;
39
40    /// Ordered, non-empty allowlist used by planning after intersecting every
41    /// selected provider requirement with the concrete runtime offers.
42    fn dynamic_storage_profile_order(&self) -> &[DynamicStorageProfile];
43
44    /// Optional reusable-execution capacity policy selected before planning.
45    /// Core treats class identities as opaque and owns their memory derivation.
46    fn reusable_execution_policy(&self) -> Option<&ReusableExecutionPolicy>;
47
48    fn validate(&self) -> Result<(), VNextError>;
49}
50
51pub fn canonical_runtime_policy_fingerprint<P: RuntimePolicy>(
52    policy: &P,
53) -> Result<String, VNextError> {
54    policy.validate()?;
55    canonical_fingerprint(policy, "fingerprint validated runtime policy")
56}
57
58pub struct PlanBuildRequest<'a, P: RuntimePolicy> {
59    pub(super) family: &'a PreparedModelFamily,
60    pub(super) capabilities: &'a CapabilityCatalog,
61    pub(super) policy: &'a P,
62    pub(super) node_resolutions: Vec<PlanNodeResolution>,
63    pub(super) completion_retention: CompletionRetentionSpec,
64    pub(super) execution_weights: TrustedExecutionWeightPlan,
65}
66
67impl<'a, P: RuntimePolicy> PlanBuildRequest<'a, P> {
68    pub fn new(
69        family: &'a PreparedModelFamily,
70        capabilities: &'a CapabilityCatalog,
71        policy: &'a P,
72        node_resolutions: Vec<PlanNodeResolution>,
73    ) -> Result<Self, VNextError> {
74        if node_resolutions.is_empty() {
75            return Err(invalid_plan("plan build request has no node resolutions"));
76        }
77        policy.validate()?;
78        Ok(Self {
79            family,
80            capabilities,
81            policy,
82            node_resolutions,
83            completion_retention: CompletionRetentionSpec::default(),
84            execution_weights: TrustedExecutionWeightPlan::identity(family)?,
85        })
86    }
87
88    pub fn with_completion_retention(
89        mut self,
90        completion_retention: CompletionRetentionSpec,
91    ) -> Result<Self, VNextError> {
92        let produced_values = self
93            .family
94            .program()
95            .blocks()
96            .iter()
97            .flat_map(|block| block.nodes.iter())
98            .flat_map(|node| node.outputs.iter())
99            .collect::<std::collections::BTreeSet<_>>();
100        if completion_retention
101            .values()
102            .iter()
103            .any(|value_id| !produced_values.contains(value_id))
104        {
105            return Err(invalid_plan(
106                "completion retention must reference a semantic node output",
107            ));
108        }
109        self.completion_retention = completion_retention;
110        Ok(self)
111    }
112
113    pub fn with_execution_weights(
114        mut self,
115        execution_weights: TrustedExecutionWeightPlan,
116    ) -> Result<Self, VNextError> {
117        execution_weights.validate_against_catalog(self.family, self.capabilities)?;
118        self.execution_weights = execution_weights;
119        Ok(self)
120    }
121
122    pub fn family(&self) -> &PreparedModelFamily {
123        self.family
124    }
125
126    pub fn capabilities(&self) -> &CapabilityCatalog {
127        self.capabilities
128    }
129
130    pub fn policy(&self) -> &P {
131        self.policy
132    }
133
134    pub fn execution_weights(&self) -> &ExecutionWeightPlan {
135        self.execution_weights.plan()
136    }
137}