molframe_engine/
runtime.rs1use crate::compile::CompiledWorkflow;
4use crate::graph::{Output, Value, WorkflowError};
5use molframe_core::ExecutionContext;
6use std::any::{TypeId, type_name};
7use std::collections::{BTreeMap, BTreeSet};
8use std::sync::Arc;
9
10#[derive(Clone)]
11struct InputValue {
12 value: Value,
13 type_id: TypeId,
14}
15
16#[derive(Clone, Default)]
18pub struct WorkflowInputs {
19 values: BTreeMap<Box<str>, InputValue>,
20}
21
22impl std::fmt::Debug for WorkflowInputs {
23 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 formatter
25 .debug_struct("WorkflowInputs")
26 .field("names", &self.values.keys().collect::<Vec<_>>())
27 .finish()
28 }
29}
30
31impl WorkflowInputs {
32 #[must_use]
34 pub const fn new() -> Self {
35 Self {
36 values: BTreeMap::new(),
37 }
38 }
39
40 pub fn insert<T: Send + Sync + 'static>(&mut self, name: impl Into<Box<str>>, value: T) {
42 self.values.insert(
43 name.into(),
44 InputValue {
45 value: Arc::new(value),
46 type_id: TypeId::of::<T>(),
47 },
48 );
49 }
50}
51
52#[derive(Clone, Default)]
54pub struct WorkflowResults {
55 graph_id: u64,
56 values: BTreeMap<Box<str>, Value>,
57}
58
59impl std::fmt::Debug for WorkflowResults {
60 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61 formatter
62 .debug_struct("WorkflowResults")
63 .field("graph_id", &self.graph_id)
64 .field("names", &self.values.keys().collect::<Vec<_>>())
65 .finish()
66 }
67}
68
69impl WorkflowResults {
70 pub fn get<T: Send + Sync + 'static>(&self, output: &Output<T>) -> Result<&T, WorkflowError> {
77 if output.node.graph != self.graph_id {
78 return Err(WorkflowError::MissingResult(output.name.clone()));
79 }
80 self.get_named(&output.name)
81 }
82
83 pub fn get_shared<T: Send + Sync + 'static>(
92 &self,
93 output: &Output<T>,
94 ) -> Result<Arc<T>, WorkflowError> {
95 if output.node.graph != self.graph_id {
96 return Err(WorkflowError::MissingResult(output.name.clone()));
97 }
98 let Some(value) = self.values.get(&output.name) else {
99 return Err(WorkflowError::MissingResult(output.name.clone()));
100 };
101 Arc::clone(value)
102 .downcast::<T>()
103 .map_err(|_| WorkflowError::ResultType {
104 name: output.name.clone(),
105 })
106 }
107
108 pub fn get_named<T: Send + Sync + 'static>(&self, name: &str) -> Result<&T, WorkflowError> {
114 let Some(value) = self.values.get(name) else {
115 return Err(WorkflowError::MissingResult(name.into()));
116 };
117 value
118 .downcast_ref::<T>()
119 .ok_or_else(|| WorkflowError::ResultType { name: name.into() })
120 }
121
122 pub fn names(&self) -> impl ExactSizeIterator<Item = &str> {
124 self.values.keys().map(AsRef::as_ref)
125 }
126}
127
128pub(crate) fn execute(
129 workflow: &CompiledWorkflow,
130 inputs: &WorkflowInputs,
131 context: &ExecutionContext,
132) -> Result<WorkflowResults, WorkflowError> {
133 if workflow.explanation.estimated_peak_bytes > context.available_bytes() {
136 return Err(WorkflowError::MemoryRefused {
137 estimated: workflow.explanation.estimated_peak_bytes,
138 budget: context.memory_budget().bytes(),
139 });
140 }
141 let output_nodes = workflow
142 .outputs
143 .iter()
144 .map(|(_, node, _)| *node)
145 .collect::<BTreeSet<_>>();
146 let capacity = workflow
147 .nodes
148 .iter()
149 .map(|node| node.index)
150 .max()
151 .map_or(0, |maximum| maximum + 1);
152 let mut values = vec![None; capacity];
153 let mut uses = workflow.use_counts.clone();
154 for node in &workflow.nodes {
155 if context.cancellation().is_cancelled() {
156 return Err(WorkflowError::Cancelled);
157 }
158 let value = if let Some(name) = &node.input_name {
159 let Some(input) = inputs.values.get(name) else {
160 return Err(WorkflowError::MissingInput(name.clone()));
161 };
162 if input.type_id != node.type_id {
163 return Err(WorkflowError::InputType {
164 name: name.clone(),
165 expected: node.type_name,
166 });
167 }
168 Arc::clone(&input.value)
169 } else {
170 let arguments = node
171 .dependencies
172 .iter()
173 .map(|dependency| {
174 values
175 .get(*dependency)
176 .and_then(Option::as_ref)
177 .cloned()
178 .ok_or(WorkflowError::InternalTypeMismatch {
179 expected: type_name::<()>(),
180 })
181 })
182 .collect::<Result<Vec<_>, _>>()?;
183 let Some(kernel) = &node.kernel else {
184 return Err(WorkflowError::InternalTypeMismatch {
185 expected: node.type_name,
186 });
187 };
188 match kernel(&arguments, context) {
189 Ok(value) => value,
190 Err(error @ WorkflowError::Operation { .. }) => return Err(error),
191 Err(error) => {
192 return Err(WorkflowError::Operation {
193 operation: node.operation,
194 message: error.to_string().into(),
195 });
196 }
197 }
198 };
199 values[node.index] = Some(value);
200 for dependency in &node.dependencies {
201 uses[*dependency] = uses[*dependency].saturating_sub(1);
202 if uses[*dependency] == 0 && !output_nodes.contains(dependency) {
203 values[*dependency] = None;
204 }
205 }
206 }
207 let mut results = BTreeMap::new();
208 for (name, node, _) in &workflow.outputs {
209 let Some(value) = values.get(*node).and_then(Option::as_ref) else {
210 return Err(WorkflowError::MissingResult(name.clone()));
211 };
212 results.insert(name.clone(), Arc::clone(value));
213 }
214 Ok(WorkflowResults {
215 graph_id: workflow.graph_id,
216 values: results,
217 })
218}