use super::{
canonical_fingerprint, invalid_plan, CapabilityCatalog, CompletionRetentionSpec,
ContractVersion, DynamicStorageProfile, ExecutionWeightPlan, PlanNodeResolution,
PreparedModelFamily, ReusableExecutionPolicy, Serialize, TrustedExecutionWeightPlan,
VNextError,
};
use crate::vnext::ExecutionDeterminismRequirement;
use ferrum_types::AttentionExecutionPolicy;
pub trait RuntimePolicy:
Clone + Send + Sync + Serialize + serde::de::DeserializeOwned + std::fmt::Debug + 'static
{
fn version(&self) -> ContractVersion;
fn memory_capacity_bytes(&self) -> u64;
fn memory_reserve_bytes(&self) -> u64;
fn maximum_active_sequences(&self) -> u32;
fn maximum_scheduled_tokens(&self) -> u64;
fn attention_execution_policy(&self) -> AttentionExecutionPolicy;
fn execution_determinism_requirement(&self) -> ExecutionDeterminismRequirement;
fn dynamic_storage_profile_order(&self) -> &[DynamicStorageProfile];
fn reusable_execution_policy(&self) -> Option<&ReusableExecutionPolicy>;
fn validate(&self) -> Result<(), VNextError>;
}
pub fn canonical_runtime_policy_fingerprint<P: RuntimePolicy>(
policy: &P,
) -> Result<String, VNextError> {
policy.validate()?;
canonical_fingerprint(policy, "fingerprint validated runtime policy")
}
pub struct PlanBuildRequest<'a, P: RuntimePolicy> {
pub(super) family: &'a PreparedModelFamily,
pub(super) capabilities: &'a CapabilityCatalog,
pub(super) policy: &'a P,
pub(super) node_resolutions: Vec<PlanNodeResolution>,
pub(super) completion_retention: CompletionRetentionSpec,
pub(super) execution_weights: TrustedExecutionWeightPlan,
}
impl<'a, P: RuntimePolicy> PlanBuildRequest<'a, P> {
pub fn new(
family: &'a PreparedModelFamily,
capabilities: &'a CapabilityCatalog,
policy: &'a P,
node_resolutions: Vec<PlanNodeResolution>,
) -> Result<Self, VNextError> {
if node_resolutions.is_empty() {
return Err(invalid_plan("plan build request has no node resolutions"));
}
policy.validate()?;
Ok(Self {
family,
capabilities,
policy,
node_resolutions,
completion_retention: CompletionRetentionSpec::default(),
execution_weights: TrustedExecutionWeightPlan::identity(family)?,
})
}
pub fn with_completion_retention(
mut self,
completion_retention: CompletionRetentionSpec,
) -> Result<Self, VNextError> {
let produced_values = self
.family
.program()
.blocks()
.iter()
.flat_map(|block| block.nodes.iter())
.flat_map(|node| node.outputs.iter())
.collect::<std::collections::BTreeSet<_>>();
if completion_retention
.values()
.iter()
.any(|value_id| !produced_values.contains(value_id))
{
return Err(invalid_plan(
"completion retention must reference a semantic node output",
));
}
self.completion_retention = completion_retention;
Ok(self)
}
pub fn with_execution_weights(
mut self,
execution_weights: TrustedExecutionWeightPlan,
) -> Result<Self, VNextError> {
execution_weights.validate_against_catalog(self.family, self.capabilities)?;
self.execution_weights = execution_weights;
Ok(self)
}
pub fn family(&self) -> &PreparedModelFamily {
self.family
}
pub fn capabilities(&self) -> &CapabilityCatalog {
self.capabilities
}
pub fn policy(&self) -> &P {
self.policy
}
pub fn execution_weights(&self) -> &ExecutionWeightPlan {
self.execution_weights.plan()
}
}