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.memory_budget().bytes() {
134 return Err(WorkflowError::MemoryRefused {
135 estimated: workflow.explanation.estimated_peak_bytes,
136 budget: context.memory_budget().bytes(),
137 });
138 }
139 let _reservation = context
140 .try_reserve(workflow.explanation.estimated_peak_bytes)
141 .map_err(|_| WorkflowError::MemoryRefused {
142 estimated: workflow.explanation.estimated_peak_bytes,
143 budget: context.memory_budget().bytes(),
144 })?;
145 let output_nodes = workflow
146 .outputs
147 .iter()
148 .map(|(_, node, _)| *node)
149 .collect::<BTreeSet<_>>();
150 let capacity = workflow
151 .nodes
152 .iter()
153 .map(|node| node.index)
154 .max()
155 .map_or(0, |maximum| maximum + 1);
156 let mut values = vec![None; capacity];
157 let mut uses = workflow.use_counts.clone();
158 for node in &workflow.nodes {
159 if context.cancellation().is_cancelled() {
160 return Err(WorkflowError::Cancelled);
161 }
162 let value = if let Some(name) = &node.input_name {
163 let Some(input) = inputs.values.get(name) else {
164 return Err(WorkflowError::MissingInput(name.clone()));
165 };
166 if input.type_id != node.type_id {
167 return Err(WorkflowError::InputType {
168 name: name.clone(),
169 expected: node.type_name,
170 });
171 }
172 Arc::clone(&input.value)
173 } else {
174 let arguments = node
175 .dependencies
176 .iter()
177 .map(|dependency| {
178 values
179 .get(*dependency)
180 .and_then(Option::as_ref)
181 .cloned()
182 .ok_or(WorkflowError::InternalTypeMismatch {
183 expected: type_name::<()>(),
184 })
185 })
186 .collect::<Result<Vec<_>, _>>()?;
187 let Some(kernel) = &node.kernel else {
188 return Err(WorkflowError::InternalTypeMismatch {
189 expected: node.type_name,
190 });
191 };
192 match kernel(&arguments, context) {
193 Ok(value) => value,
194 Err(error @ WorkflowError::Operation { .. }) => return Err(error),
195 Err(error) => {
196 return Err(WorkflowError::Operation {
197 operation: node.operation,
198 message: error.to_string().into(),
199 });
200 }
201 }
202 };
203 values[node.index] = Some(value);
204 for dependency in &node.dependencies {
205 uses[*dependency] = uses[*dependency].saturating_sub(1);
206 if uses[*dependency] == 0 && !output_nodes.contains(dependency) {
207 values[*dependency] = None;
208 }
209 }
210 }
211 let mut results = BTreeMap::new();
212 for (name, node, _) in &workflow.outputs {
213 let Some(value) = values.get(*node).and_then(Option::as_ref) else {
214 return Err(WorkflowError::MissingResult(name.clone()));
215 };
216 results.insert(name.clone(), Arc::clone(value));
217 }
218 Ok(WorkflowResults {
219 graph_id: workflow.graph_id,
220 values: results,
221 })
222}