mf_engine/
decision.rs

1use crate::engine::EvaluationOptions;
2use crate::handler::custom_node_adapter::{CustomNodeAdapter, NoopCustomNode};
3use crate::handler::graph::{
4    DecisionGraph, DecisionGraphConfig, DecisionGraphResponse,
5};
6use crate::loader::{CachedLoader, DecisionLoader, NoopLoader};
7use crate::model::DecisionContent;
8use crate::util::validator_cache::ValidatorCache;
9use crate::{DecisionGraphValidationError, EvaluationError};
10use std::sync::Arc;
11use mf_expression::variable::Variable;
12
13/// Represents a JDM decision which can be evaluated
14#[derive(Debug, Clone)]
15pub struct Decision<Loader, CustomNode>
16where
17    Loader: DecisionLoader + 'static,
18    CustomNode: CustomNodeAdapter + 'static,
19{
20    content: Arc<DecisionContent>,
21    loader: Arc<Loader>,
22    adapter: Arc<CustomNode>,
23
24    validator_cache: ValidatorCache,
25}
26
27impl From<DecisionContent> for Decision<NoopLoader, NoopCustomNode> {
28    fn from(value: DecisionContent) -> Self {
29        Self {
30            content: value.into(),
31            loader: NoopLoader::default().into(),
32            adapter: NoopCustomNode::default().into(),
33
34            validator_cache: Default::default(),
35        }
36    }
37}
38
39impl From<Arc<DecisionContent>> for Decision<NoopLoader, NoopCustomNode> {
40    fn from(value: Arc<DecisionContent>) -> Self {
41        Self {
42            content: value,
43            loader: NoopLoader::default().into(),
44            adapter: NoopCustomNode::default().into(),
45
46            validator_cache: Default::default(),
47        }
48    }
49}
50
51impl<L, A> Decision<L, A>
52where
53    L: DecisionLoader + 'static,
54    A: CustomNodeAdapter + 'static,
55{
56    pub fn with_loader<Loader>(
57        self,
58        loader: Arc<Loader>,
59    ) -> Decision<Loader, A>
60    where
61        Loader: DecisionLoader,
62    {
63        Decision {
64            loader,
65            adapter: self.adapter,
66            content: self.content,
67            validator_cache: self.validator_cache,
68        }
69    }
70
71    pub fn with_adapter<Adapter>(
72        self,
73        adapter: Arc<Adapter>,
74    ) -> Decision<L, Adapter>
75    where
76        Adapter: CustomNodeAdapter,
77    {
78        Decision {
79            loader: self.loader,
80            adapter,
81            content: self.content,
82            validator_cache: self.validator_cache,
83        }
84    }
85
86    /// Evaluates a decision using an in-memory reference stored in struct
87    pub async fn evaluate(
88        &self,
89        context: Variable,
90    ) -> Result<DecisionGraphResponse, Box<EvaluationError>> {
91        self.evaluate_with_opts(context, Default::default()).await
92    }
93
94    /// 使用 State 评估决策 - 通过 thread_local 设置 State
95    pub async fn evaluate_with_state<S: Send + Sync + 'static>(
96        &self,
97        context: Variable,
98        state: Arc<S>,
99    ) -> Result<DecisionGraphResponse, Box<EvaluationError>> {
100        // 使用 StateGuard 自动管理 State 生命周期
101        let _guard = mf_expression::functions::StateGuard::new(state);
102
103        // 执行评估
104        let result = self.evaluate(context).await;
105        result
106    }
107
108    /// Evaluates a decision using in-memory reference with advanced options
109    pub async fn evaluate_with_opts(
110        &self,
111        context: Variable,
112        options: EvaluationOptions,
113    ) -> Result<DecisionGraphResponse, Box<EvaluationError>> {
114        let mut decision_graph = DecisionGraph::try_new(DecisionGraphConfig {
115            content: self.content.clone(),
116            max_depth: options.max_depth.unwrap_or(5),
117            trace: options.trace.unwrap_or_default(),
118            loader: Arc::new(CachedLoader::from(self.loader.clone())),
119            adapter: self.adapter.clone(),
120            iteration: 0,
121            validator_cache: Some(self.validator_cache.clone()),
122        })?;
123
124        let response = decision_graph.evaluate(context).await?;
125
126        Ok(response)
127    }
128
129    pub fn validate(&self) -> Result<(), DecisionGraphValidationError> {
130        let decision_graph = DecisionGraph::try_new(DecisionGraphConfig {
131            content: self.content.clone(),
132            max_depth: 1,
133            trace: false,
134            loader: Arc::new(CachedLoader::from(self.loader.clone())),
135            adapter: self.adapter.clone(),
136            iteration: 0,
137            validator_cache: Some(self.validator_cache.clone()),
138        })?;
139
140        decision_graph.validate()
141    }
142}