zen_engine/nodes/decision/
mod.rs1use crate::decision_graph::graph::{DecisionGraph, DecisionGraphConfig};
2use crate::nodes::{NodeContext, NodeContextExt, NodeError, NodeHandler, NodeResult};
3use crate::EvaluationError;
4use std::cell::RefCell;
5use std::ops::Deref;
6use std::rc::Rc;
7use zen_types::decision::{DecisionNodeContent, TransformAttributes};
8use zen_types::variable::{ToVariable, Variable};
9
10#[derive(Debug, Clone, Default)]
11pub struct DecisionNodeHandler {
12 decision_graph: Rc<RefCell<Option<DecisionGraph>>>,
13}
14
15pub type DecisionNodeData = DecisionNodeContent;
16pub type DecisionNodeTrace = Variable;
17
18impl NodeHandler for DecisionNodeHandler {
19 type NodeData = DecisionNodeData;
20 type TraceData = DecisionNodeTrace;
21
22 fn transform_attributes(
23 &self,
24 ctx: &NodeContext<Self::NodeData, Self::TraceData>,
25 ) -> Option<TransformAttributes> {
26 Some(ctx.node.transform_attributes.clone())
27 }
28
29 async fn after_transform_attributes(
30 &self,
31 _ctx: &NodeContext<Self::NodeData, Self::TraceData>,
32 ) -> Result<(), NodeError> {
33 if let Some(graph) = self.decision_graph.borrow_mut().as_mut() {
34 graph.reset_graph();
35 };
36
37 Ok(())
38 }
39
40 async fn handle(&self, ctx: NodeContext<Self::NodeData, Self::TraceData>) -> NodeResult {
41 let mut decision_graph_ref = self.decision_graph.borrow_mut();
42
43 if decision_graph_ref.is_none() {
44 let loader = ctx.extensions.loader();
45 let sub_decision = loader.load(ctx.node.key.deref()).await.node_context(&ctx)?;
46 let sub_kind = sub_decision.kind();
47 let Some(sub_graph) = sub_decision.into_graph_arc() else {
48 return ctx.error(format!(
49 "sub-decision '{}' is a {sub_kind}, expected graph",
50 ctx.node.key
51 ));
52 };
53
54 let sub_graph =
55 if sub_graph.compiled_cache.is_some() && sub_graph.resolved_schemas.is_some() {
56 sub_graph
57 } else {
58 let mut owned = (*sub_graph).clone();
59 owned.compile();
60 let _ = owned.resolve_schemas(loader).await;
61 std::sync::Arc::new(owned)
62 };
63
64 let mut extensions = ctx.extensions.clone();
65 extensions.compiled_cache = sub_graph.compiled_cache.clone();
66 extensions.dt_indexes = sub_graph.dt_indexes.clone();
67 extensions.validator_cache =
68 std::sync::Arc::new(std::cell::OnceCell::from(sub_graph.validator_cache.clone()));
69
70 let dg = DecisionGraph::try_new(DecisionGraphConfig {
71 content: sub_graph,
72 extensions,
73 trace: ctx.config.trace,
74 iteration: ctx.iteration + 1,
75 max_depth: ctx.config.max_depth,
76 })
77 .node_context(&ctx)?;
78
79 *decision_graph_ref = Some(dg);
80 }
81
82 let Some(decision_graph) = decision_graph_ref.as_mut() else {
83 return ctx.error("Failed to initialize decision graph".to_string());
84 };
85
86 decision_graph.set_parent_nodes(ctx.nodes.clone());
87
88 let evaluate_result = Box::pin(decision_graph.evaluate(ctx.input.clone())).await;
89 match evaluate_result {
90 Ok(result) => {
91 ctx.trace(|trace| {
92 *trace = result
93 .trace
94 .and_then(|t| t.into_graph())
95 .as_ref()
96 .map(|m| m.to_variable())
97 .unwrap_or(Variable::Null);
98 });
99
100 ctx.success(result.result)
101 }
102 Err(err) => {
103 if let EvaluationError::NodeError { trace, .. } = err.deref() {
104 ctx.trace(|t| *t = trace.to_variable());
105 }
106
107 ctx.error(err.to_string())
108 }
109 }
110 }
111}