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::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 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}