Skip to main content

ferrum_interfaces/vnext/execution/
planner.rs

1use super::{
2    CapabilityCatalog, DeviceDescriptor, ExecutionPlan, PlanBuildRequest, RuntimePolicy, VNextError,
3};
4
5/// Minimal trusted view consumed by resource admission and operation dispatch.
6/// Product resolution may wrap this with tokenizer, sampling, source, and API
7/// policy, but those concerns do not enter the device execution boundary.
8pub trait ExecutablePlanView {
9    fn execution_plan(&self) -> &ExecutionPlan;
10    fn device(&self) -> &DeviceDescriptor;
11    fn capabilities(&self) -> &CapabilityCatalog;
12}
13
14/// Owned executable produced directly by the planner for a concrete runtime
15/// composition root. It is intentionally smaller than a product-level
16/// `ResolvedModelPlan`.
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct ExecutablePlan {
19    plan: ExecutionPlan,
20    capabilities: CapabilityCatalog,
21}
22
23impl ExecutablePlan {
24    pub fn new(plan: ExecutionPlan, capabilities: CapabilityCatalog) -> Result<Self, VNextError> {
25        let catalog_fingerprint = capabilities.fingerprint()?;
26        if plan.payload().device_id() != &capabilities.device().id
27            || plan.payload().device_runtime_implementation_fingerprint()
28                != capabilities.device().runtime_implementation_fingerprint
29            || plan.payload().capability_catalog_fingerprint() != catalog_fingerprint
30        {
31            return Err(VNextError::InvalidExecutionPlan {
32                reason: "executable plan differs from its device capability catalog".to_owned(),
33            });
34        }
35        Ok(Self { plan, capabilities })
36    }
37
38    pub fn into_parts(self) -> (ExecutionPlan, CapabilityCatalog) {
39        (self.plan, self.capabilities)
40    }
41}
42
43impl ExecutablePlanView for ExecutablePlan {
44    fn execution_plan(&self) -> &ExecutionPlan {
45        &self.plan
46    }
47
48    fn device(&self) -> &DeviceDescriptor {
49        self.capabilities.device()
50    }
51
52    fn capabilities(&self) -> &CapabilityCatalog {
53        &self.capabilities
54    }
55}
56
57/// Pure planner boundary. Execution consumes the immutable plan and performs
58/// no capability/backend selection in the token loop.
59pub trait ExecutionPlanner: Send + Sync {
60    type Policy: RuntimePolicy;
61
62    fn build_plan(
63        &self,
64        request: PlanBuildRequest<'_, Self::Policy>,
65    ) -> Result<ExecutionPlan, VNextError>;
66}