ferrum_interfaces/vnext/execution/
policy.rs1use super::{
2 canonical_fingerprint, invalid_plan, CapabilityCatalog, CompletionRetentionSpec,
3 ContractVersion, DynamicStorageProfile, ExecutionWeightPlan, PlanNodeResolution,
4 PreparedModelFamily, ReusableExecutionPolicy, Serialize, TrustedExecutionWeightPlan,
5 VNextError,
6};
7use crate::vnext::{CheckpointCapacityPolicy, ExecutionDeterminismRequirement};
8use ferrum_types::AttentionExecutionPolicy;
9
10pub trait RuntimePolicy:
13 Clone + Send + Sync + Serialize + serde::de::DeserializeOwned + std::fmt::Debug + 'static
14{
15 fn version(&self) -> ContractVersion;
16
17 fn memory_capacity_bytes(&self) -> u64;
20
21 fn memory_reserve_bytes(&self) -> u64;
22
23 fn maximum_active_sequences(&self) -> u32;
26
27 fn maximum_scheduled_tokens(&self) -> u64;
31
32 fn attention_execution_policy(&self) -> AttentionExecutionPolicy;
35
36 fn execution_determinism_requirement(&self) -> ExecutionDeterminismRequirement;
39
40 fn dynamic_storage_profile_order(&self) -> &[DynamicStorageProfile];
43
44 fn reusable_execution_policy(&self) -> Option<&ReusableExecutionPolicy>;
47
48 fn checkpoint_capacity_policy(&self) -> Option<&CheckpointCapacityPolicy> {
51 None
52 }
53
54 fn validate(&self) -> Result<(), VNextError>;
55}
56
57pub fn canonical_runtime_policy_fingerprint<P: RuntimePolicy>(
58 policy: &P,
59) -> Result<String, VNextError> {
60 policy.validate()?;
61 canonical_fingerprint(policy, "fingerprint validated runtime policy")
62}
63
64pub struct PlanBuildRequest<'a, P: RuntimePolicy> {
65 pub(super) family: &'a PreparedModelFamily,
66 pub(super) capabilities: &'a CapabilityCatalog,
67 pub(super) policy: &'a P,
68 pub(super) node_resolutions: Vec<PlanNodeResolution>,
69 pub(super) completion_retention: CompletionRetentionSpec,
70 pub(super) execution_weights: TrustedExecutionWeightPlan,
71}
72
73impl<'a, P: RuntimePolicy> PlanBuildRequest<'a, P> {
74 pub fn new(
75 family: &'a PreparedModelFamily,
76 capabilities: &'a CapabilityCatalog,
77 policy: &'a P,
78 node_resolutions: Vec<PlanNodeResolution>,
79 ) -> Result<Self, VNextError> {
80 if node_resolutions.is_empty() {
81 return Err(invalid_plan("plan build request has no node resolutions"));
82 }
83 policy.validate()?;
84 Ok(Self {
85 family,
86 capabilities,
87 policy,
88 node_resolutions,
89 completion_retention: CompletionRetentionSpec::default(),
90 execution_weights: TrustedExecutionWeightPlan::identity(family)?,
91 })
92 }
93
94 pub fn with_completion_retention(
95 mut self,
96 completion_retention: CompletionRetentionSpec,
97 ) -> Result<Self, VNextError> {
98 let produced_values = self
99 .family
100 .program()
101 .blocks()
102 .iter()
103 .flat_map(|block| block.nodes.iter())
104 .flat_map(|node| node.outputs.iter())
105 .collect::<std::collections::BTreeSet<_>>();
106 if completion_retention
107 .values()
108 .iter()
109 .any(|value_id| !produced_values.contains(value_id))
110 {
111 return Err(invalid_plan(
112 "completion retention must reference a semantic node output",
113 ));
114 }
115 self.completion_retention = completion_retention;
116 Ok(self)
117 }
118
119 pub fn with_execution_weights(
120 mut self,
121 execution_weights: TrustedExecutionWeightPlan,
122 ) -> Result<Self, VNextError> {
123 execution_weights.validate_against_catalog(self.family, self.capabilities)?;
124 self.execution_weights = execution_weights;
125 Ok(self)
126 }
127
128 pub fn family(&self) -> &PreparedModelFamily {
129 self.family
130 }
131
132 pub fn capabilities(&self) -> &CapabilityCatalog {
133 self.capabilities
134 }
135
136 pub fn policy(&self) -> &P {
137 self.policy
138 }
139
140 pub fn execution_weights(&self) -> &ExecutionWeightPlan {
141 self.execution_weights.plan()
142 }
143}