aimx/
context.rs

1//! Evaluation context for expression evaluation within AIM workflows.
2//!
3//! The context module provides the runtime environment for evaluating AIMX expressions
4//! within the context of AIM workflows. It handles:
5//!
6//! - Variable resolution across multi-level workflow hierarchies
7//! - Function and method dispatch through the global function registry
8//! - Closure parameter management for functional programming constructs
9//! - Caching of evaluation results within a single evaluation session
10//! - Circular reference detection to prevent infinite loops
11//! - Inference operations for agentic workflow execution
12//!
13//! # Context Architecture
14//!
15//! The module defines two primary components:
16//!
17//! 1. [`ContextLike`] - A trait that defines the interface for evaluation contexts
18//! 2. [`Context`] - A complete implementation that integrates with AIM workflows
19//!
20//! The `Context` struct implements the `ContextLike` trait and provides a full
21//! implementation for workflow-based expression evaluation with proper scoping,
22//! caching, and inference capabilities.
23//!
24//! # Workflow Integration
25//!
26//! The context maintains references to workflows at different levels:
27//! - `workflow`: The current read-only workflow context
28//! - `workflow_mut`: A mutable workflow for making changes during inference
29//! - `scope`: The current reference scope for resolving relative references
30//!
31//! When evaluating expressions within workflows, the context automatically handles:
32//! - Multi-part reference resolution (e.g., `parent.child.value`)
33//! - Type promotion and validation for assignments
34//! - Caching of computed values within a session
35//! - Circular reference detection to prevent stack overflows
36//!
37//! # Thread Safety
38//!
39//! Each thread should use its own context instance to evaluate a workflow .
40//! Context instances are not thread-safe and should not be shared between threads.
41//! 
42use crate::{
43    Expression, ExpressionLike, FunctionRegistry, Node, Reference, Rule, Typedef, Value, Workflow, WorkflowLike, aim::{get_config, get_lock_manager}, get_workspace, inference::{generate_prompt, parse_response, send_request, validate_responses},
44};
45use anyhow::{anyhow, Result};
46use std::{
47    collections::{HashMap, HashSet},
48    mem::replace,
49    sync::{Arc, Mutex},
50};
51
52/// Trait for evaluation contexts that provide runtime environment for expression evaluation.
53///
54/// This trait defines the interface that evaluation contexts must implement.
55/// A context provides variable values, handles function calls, manages closure parameters,
56/// and provides the runtime environment for expression evaluation.
57///
58/// The ContextLike trait is designed to be implemented by context types that need to:
59/// - Resolve variable references during evaluation
60/// - Handle function and method calls
61/// - Manage closure parameters for functional programming constructs
62/// - Support inference operations for agentic workflows
63///
64/// # Implementation Requirements
65///
66/// Implementors must manage:
67/// - Variable resolution through [`get_referenced`](ContextLike::get_referenced) and [`set_referenced`](ContextLike::set_referenced)
68/// - Function dispatch through [`function_call`](ContextLike::function_call) and [`method_call`](ContextLike::method_call)
69/// - Closure parameter management through [`start_closure`](ContextLike::start_closure), [`set_key`](ContextLike::set_key), [`set_value`](ContextLike::set_value), and [`end_closure`](ContextLike::end_closure)
70/// - Inference operations through [`inference_call`](ContextLike::inference_call)
71///
72/// See the [`Context`] struct for a complete implementation that supports
73/// workflow-based evaluation with proper scoping and caching.
74pub trait ContextLike {
75
76    /// Start a new closure and save the current stack.
77    fn start_closure(&mut self) -> [(String, Value); 2];
78
79    /// Set the key of the indexed mapped variable used by element-wise functions like `map`.
80    ///
81    /// # Arguments
82    ///
83    /// * `index` - The index key to set (0 or 1)
84    /// * `identifier` - The key to set
85    fn set_key(&mut self, index: usize, identifier: &str);
86
87    /// Set the value of the index mapped variable used by element-wise functions like `map`.
88    ///
89    /// # Arguments
90    ///
91    /// * `index` - The index value to set (0 or 1)
92    /// * `value` - The value to set
93    fn set_value(&mut self, index: usize, value: &Value);
94
95    /// End the closure and restore the previous stack.
96    fn end_closure(&mut self, stack: [(String, Value); 2]);
97
98    /// Get the value of a referenced variable or expression.
99    ///
100    /// # Arguments
101    ///
102    /// * `reference` - The reference to resolve
103    ///
104    /// # Returns
105    ///
106    /// Returns the value of the referenced variable or an error if the reference
107    /// cannot be resolved.
108    fn get_referenced(&mut self, reference: &Reference) -> Result<Value>;
109
110    /// Set the value of a referenced variable.
111    ///
112    /// # Arguments
113    ///
114    /// * `reference` - The reference to set
115    /// * `value` - The value to assign
116    ///
117    /// # Returns
118    ///
119    /// Returns `Ok(())` on success or an error if the referenced variable cannot be set.
120    fn set_referenced(&mut self, reference: &Reference, value: Value) -> Result<()>;
121
122    /// Call a standalone function.
123    ///
124    /// # Arguments
125    /// * `name` - The name of the function to call
126    /// * `arg` - The argument(s) to pass to the function (Value can be Empty, Literal, or Array)
127    ///
128    /// # Returns
129    ///
130    /// Returns the result of the function call or an error if the function is not found
131    /// or if there's an error during execution.
132    fn function_call(&mut self, name: &str, arg: Value) -> Result<Value>;
133
134    /// Call a method on a value.
135    ///
136    /// # Arguments
137    /// * `name` - The name of the method to call
138    /// * `value` - The value on which to call the method
139    /// * `arg` - The argument(s) to pass to the method (Value can be Empty, Literal, or Array)
140    ///
141    /// # Returns
142    ///
143    /// Returns the result of the method call or an error if the method is not found
144    /// or if there's an error during execution.
145    fn method_call(&mut self, name: &str, value: Value, arg: Value) -> Result<Value>;
146
147    /// Run inference on a referenced workflow.
148    ///
149    /// # Arguments
150    /// * `reference` - The workflow reference
151    /// * `arg` - The argument(s) to pass to the inference call (Value can be Empty, Literal, or Array)
152    ///
153    /// # Returns
154    ///
155    /// Returns the result of the inference call or an error if the workflow is not found
156    /// or if there's an error during execution.
157    fn inference_call(&mut self, reference: &Reference, arg: Value) -> Result<Value>;
158}
159
160enum Stack {
161    Reader(Reference, Arc<Workflow>),
162    Writer(Reference, Arc<Workflow>, Workflow),
163}
164
165/// A default context for evaluating expressions within AIM workflows.
166///
167/// The `Context` struct provides a complete implementation of the [`ContextLike`] trait
168/// that integrates with the AIM workflow system. It supports:
169///
170/// - Variable resolution across multi-level workflow hierarchies
171/// - Function and method dispatch through the global function registry
172/// - Closure parameter management for functional programming constructs
173/// - Caching of evaluation results within a single evaluation session
174/// - Circular reference detection to prevent infinite loops
175/// - Inference operations for agentic workflow execution
176///
177/// # Workflow Integration
178///
179/// The context maintains references to workflows at different levels:
180/// - `workflow`: The current read-only workflow context
181/// - `workflow_mut`: A mutable workflow for making changes during inference
182/// - `scope`: The current reference scope for resolving relative references
183///
184/// When evaluating expressions within workflows, the context automatically handles:
185/// - Multi-part reference resolution (e.g., `parent.child.value`)
186/// - Type promotion and validation for assignments
187/// - Caching of computed values within a session
188/// - Circular reference detection to prevent stack overflows
189///
190/// # Thread Safety
191///
192/// Context instances are not thread-safe and should not be shared between threads.
193/// Each evaluation session should use its own context instance.
194///
195/// # Examples
196///
197/// Basic usage with predefined values:
198///
199/// ```rust
200/// use aimx::{Context, Literal, Value};
201///
202/// let context = Context::new()
203///     .with_value("x", Value::Literal(Literal::Number(42.0)))
204///     .with_value("name", Value::Literal(Literal::Text("Alice".to_string())));
205/// ```
206///
207/// Evaluating expressions within workflows:
208///
209/// ```rust
210/// use aimx::{Context, aimx_parse, ExpressionLike};
211///
212/// let mut context = Context::new();
213/// // Expressions are evaluated within the context of the workspace workflows
214/// let expression = aimx_parse("some_workflow_value + 10");
215/// let result = expression.evaluate(&mut context);
216/// ```
217#[derive(Debug)]
218pub struct Context {
219    scope: Reference,
220    workflow: Arc<Workflow>,
221    workflow_mut: Option<Workflow>,
222    lock_guard: Option<Arc<Mutex<()>>>,
223    parameters: [(String, Value); 2],
224    /// Rules that have already been entered on the current call‑stack.
225    visited: HashSet<Reference>,
226    /// Caches results for the current evaluation.
227    cache: HashMap<Reference, Value>,
228}
229
230impl Context {
231    pub fn new() -> Self {
232        // Convert WorkflowLike to Workflow
233        let workflow = get_workspace()
234                    .as_any()
235                    .downcast_ref::<Workflow>()
236                    .map(|workspace| Arc::new(workspace.clone())).unwrap();
237        Self {
238            scope: Reference::new("_"),
239            workflow,
240            workflow_mut: None,
241            lock_guard: None,
242            parameters: [
243                (String::new(), Value::Empty),
244                (String::new(), Value::Empty)
245            ],
246            visited: HashSet::new(),
247            cache: HashMap::new(),
248        }
249    }
250
251    /// Gets read-only access to the current workflow.
252    ///
253    /// Returns a clone of the Arc reference to the current workflow, allowing
254    /// read-only access to workflow data without affecting the context's ownership.
255    pub fn workflow(&self) -> Arc<Workflow> {
256        self.workflow.clone()
257    }
258
259    /// Gets mutable access to the workflow if available.
260    ///
261    /// Returns a mutable reference to the workflow if one is currently being
262    /// modified, or None if only read-only access is available.
263    pub fn workflow_mut(&mut self) -> Option<&mut Workflow> {
264        self.workflow_mut.as_mut()
265    }
266
267    /// Sets mutable access to a workflow.
268    ///
269    /// This method is used to provide a workflow that can be modified during
270    /// evaluation, typically when performing inference operations that need
271    /// to update workflow values. Note that this requires appropriate write
272    /// permissions from the lock manager.
273    ///
274    /// # Arguments
275    ///
276    /// * `workflow` - The workflow to make mutable
277    pub fn set_workflow_mut(&mut self, workflow: Workflow) {
278        self.workflow_mut = Some(workflow);
279    }
280
281    /// Clears mutable workflow access.
282    ///
283    /// This method releases the mutable workflow reference, reverting to
284    /// read-only access for subsequent operations.
285    pub fn clear_workflow_mut(&mut self) {
286        self.workflow_mut = None;
287    }
288
289    /// Begin a writable session on the current workflow.
290    ///
291    /// This method acquires a write lock on the current workflow scope and
292    /// prepares the context for making modifications. It's typically used
293    /// when an inference operation needs to update workflow values.
294    pub fn writable_start(&mut self) {
295        if self.workflow_mut.is_none() {
296            let lock_manager = get_lock_manager();
297            self.lock_guard = Some(lock_manager.acquire_workflow_lock(self.scope.clone()));
298            let workflow = self.workflow.clone();
299            self.workflow_mut = Some((*workflow).clone());
300        }
301    }
302
303    /// End a writable session and commit changes.
304    ///
305    /// This method releases the write lock and commits any changes made to
306    /// the workflow during the writable session. The modified workflow is
307    /// atomically replaced with the new version.
308    pub fn writable_end(&mut self) {
309        // Atomically replace the old node workflow with the new workflow.
310        if let Ok((_, node, _)) = self.dereference(&self.scope) {
311            if let Some(workflow_mut) = self.workflow_mut.take() {
312                node.set_workflow(workflow_mut);
313            }            
314        }
315        // Release the lock by taking ownership and letting it drop
316        let _ = self.lock_guard.take();
317    }
318
319
320    /// Start a new evaluation session.
321    ///
322    /// This method clears the visited set and cache, preparing the context
323    /// for a new evaluation session. It's important to call this method
324    /// when starting a new independent evaluation to avoid conflicts with
325    /// cached values from previous evaluations.
326    pub fn new_session(&mut self) {
327        self.visited.clear();
328        self.cache.clear();
329    }
330
331    fn inference_start(&mut self, scope: Reference) -> Stack {
332        let old_scope = replace(&mut self.scope, scope);
333        if let Some(workflow_mut) = self.workflow_mut.take() {
334            Stack::Writer(old_scope, self.workflow.clone(), workflow_mut)
335        }
336        else {
337            Stack::Reader(old_scope, self.workflow.clone())
338        }
339    }
340
341    fn inference_end(&mut self, stack: Stack) {
342        match stack {
343            Stack::Reader(scope, workflow) => {
344                self.scope = scope;
345                self.workflow = workflow
346            }
347            Stack::Writer(scope, workflow, workflow_mut) => {
348                self.scope = scope;
349                self.workflow = workflow;
350                self.workflow_mut = Some(workflow_mut)
351            }
352        }
353    }
354
355    /// Add a predefined value to the context for testing purposes.
356    ///
357    /// This method creates a rule with the given identifier and value and adds it
358    /// to the workspace. This is primarily used for testing expressions that reference
359    /// predefined variables.
360    ///
361    /// # Arguments
362    ///
363    /// * `identifier` - The identifier name for the value
364    /// * `value` - The value to associate with the identifier
365    ///
366    /// # Returns
367    ///
368    /// Returns the modified context for method chaining.
369    pub fn with_value(mut self, identifier: &str, value: Value) -> Self {
370        let typedef = value.get_type().unwrap_or(Typedef::Any);
371        let rule = Rule::new(identifier.to_string(), typedef, Expression::Empty, value);
372        
373        // Try mutable workflow first
374        if let Some(ref mut workflow) = self.workflow_mut {
375            workflow.append_or_update(Some(rule));
376        }
377        // Otherwise try read-only workflow
378        else {
379            // Clone the workflow for modification
380            let mut workflow_clone = (*self.workflow).clone();
381            workflow_clone.append_or_update(Some(rule));
382            self.workflow = Arc::new(workflow_clone);
383        }
384        self
385    }
386
387    // Dereference the Rule and return a normalized reference to it.
388    fn normalize(&mut self, reference: &Reference) -> Result<Reference> {
389        match reference {
390            Reference::One(one) => {
391                // Try writable workflow
392                if let Some(workflow_mut) = &self.workflow_mut {
393                    if workflow_mut.get_rule(one).is_some() {
394                        return self.scope.normalize(reference)
395                    }
396                }
397                // Try read-only workflow
398                if self.workflow.get_rule(one).is_some() {
399                    return self.scope.normalize(reference)
400                }
401                // Try workspace
402                if get_workspace().get_rule(one).is_some() {
403                    return Ok(reference.clone())
404                }
405            }
406            Reference::Two(one, two) => {
407                // Try writable workflow
408                if let Some(workflow_mut) = &self.workflow_mut {
409                    if let Some(rule) = &workflow_mut.get_rule(one) {
410                        if let Some(node) = rule.get_node() {
411                            if node.get_workflow_like().get_rule(two).is_some() {
412                                return self.scope.normalize(reference)
413                            }
414                        }
415                    }
416                }
417                // Try read-only workflow
418                if let Some(rule) = self.workflow.get_rule(one) {
419                    if let Some(node) = rule.get_node() {
420                        if node.get_workflow_like().get_rule(two).is_some() {
421                            return self.scope.normalize(reference)
422                        }
423                    }
424                }
425                // Try workspace
426                if let Some(rule) = &get_workspace().get_rule(one) {
427                    if let Some(node) = rule.get_node() {
428                        if node.get_workflow_like().get_rule(two).is_some() {
429                            return Ok(reference.clone())
430                        }
431                    }
432                }
433            }
434            Reference::Three(one, two, three) => {
435                if let Some(rule) = &get_workspace().get_rule(one) {
436                    if let Some(node) = rule.get_node() {
437                        if let Some(rule) = &node.get_workflow_like().get_rule(two) {
438                            if let Some(node) = rule.get_node() {
439                                if node.get_workflow_like().get_rule(three).is_some() {
440                                    return Ok(reference.clone())
441                                }
442                            }
443                        }
444                    }
445                }
446            }
447        }
448        Err(anyhow!("Undefined locator ~{}", reference))
449    }
450
451    fn dereference_and_evaluate(&mut self, reference: &Reference) -> Result<Value> {
452        match reference {
453            Reference::One(one) => {
454                // Try writable workflow
455                if let Some(workflow) = &self.workflow_mut {
456                    if let Some(rule) = &workflow.get_rule(one) {
457                        return rule.evaluate(self);
458                    }
459                }
460                // Try read-only workflow
461                if let Some(rule) = self.workflow.get_rule(one) {
462                    return rule.evaluate(self);
463                }
464                // Try workspace
465                if let Some(rule) = &get_workspace().get_rule(one) {
466                    return rule.evaluate(self);
467                }
468            }
469            Reference::Two(one, two) => {
470                // Try writable workflow
471                if let Some(workflow) = &self.workflow_mut {
472                    if let Some(rule) = &workflow.get_rule(one) {
473                        if let Some(node) = rule.get_node() {
474                            if let Some(rule) = &node.get_workflow_like().get_rule(two) {
475                                return rule.evaluate(self);
476                            }
477                        }
478                    }
479                }
480                // Try read-only workflow
481                if let Some(rule) = self.workflow.get_rule(one) {
482                    if let Some(node) = rule.get_node() {
483                        if let Some(rule) = &node.get_workflow_like().get_rule(two) {
484                            return rule.evaluate(self);
485                        }
486                    }
487                }
488                // Try workspace
489                if let Some(rule) = &get_workspace().get_rule(one) {
490                    if let Some(node) = rule.get_node() {
491                        if let Some(rule) = &node.get_workflow_like().get_rule(two) {
492                            return rule.evaluate(self);
493                        }
494                    }
495                }
496            }
497            Reference::Three(one, two, three) => {
498                if let Some(rule) = &get_workspace().get_rule(one) {
499                    if let Some(node) = rule.get_node() {
500                        if let Some(rule) = &node.get_workflow_like().get_rule(two) {
501                            if let Some(node) = rule.get_node() {
502                                if let Some(rule) = &node.get_workflow_like().get_rule(three) {
503                                    return rule.evaluate(self);
504                                }
505                            }
506                        }
507                    }
508                }
509            }
510        }
511        Err(anyhow!("Undefined locator ~{}", reference))
512    }
513
514    fn dereference_typedef(&mut self, reference: &Reference) -> Result<(Reference, Typedef)> {
515        match reference {
516            Reference::One(one) => {
517                // Try writable workflow
518                if let Some(workflow) = &self.workflow_mut {
519                    if let Some(rule) = &workflow.get_rule(one) {
520                        let normalized = self.scope.normalize(reference)?;
521                        return Ok((normalized, rule.typedef().clone()));
522                    }
523                }
524                // Try read-only workflow
525                if let Some(rule) = self.workflow.get_rule(one) {
526                    let normalized = self.scope.normalize(reference)?;
527                    return Ok((normalized, rule.typedef().clone()));
528                }
529                // Try workspace
530                if let Some(rule) = &get_workspace().get_rule(one) {
531                    return Ok((reference.clone(), rule.typedef().clone()));
532                }
533            }
534            Reference::Two(one, two) => {
535                // Try writable workflow
536                if let Some(workflow) = &self.workflow_mut {
537                    if let Some(rule) = &workflow.get_rule(one) {
538                        if let Some(node) = rule.get_node() {
539                            if let Some(rule) = &node.get_workflow_like().get_rule(two) {
540                                let normalized = self.scope.normalize(reference)?;
541                                return Ok((normalized, rule.typedef().clone()));
542                            }
543                        }
544                    }
545                }
546                // Try read-only workflow
547                if let Some(rule) = self.workflow.get_rule(one) {
548                    if let Some(node) = rule.get_node() {
549                        if let Some(rule) = &node.get_workflow_like().get_rule(two) {
550                            let normalized = self.scope.normalize(reference)?;
551                            return Ok((normalized, rule.typedef().clone()));
552                        }
553                    }
554                }
555                // Try workspace
556                if let Some(rule) = &get_workspace().get_rule(one) {
557                    if let Some(node) = rule.get_node() {
558                        if let Some(rule) = &node.get_workflow_like().get_rule(two) {
559                            return Ok((reference.clone(), rule.typedef().clone()));
560                        }
561                    }
562                }
563            }
564            Reference::Three(one, two, three) => {
565                if let Some(rule) = &get_workspace().get_rule(one) {
566                    if let Some(node) = rule.get_node() {
567                        if let Some(rule) = &node.get_workflow_like().get_rule(two) {
568                            if let Some(node) = rule.get_node() {
569                                if let Some(rule) = &node.get_workflow_like().get_rule(three) {
570                                    return Ok((reference.clone(), rule.typedef().clone()));
571                                }
572                            }
573                        }
574                    }
575                }
576            }
577        }
578        Err(anyhow!("Undefined locator ~{}", reference))
579    }
580
581    // Dereference the Node and return a normalized reference to it.
582    fn dereference(&self, reference: &Reference) -> Result<(Reference, Node, bool)> {
583        match reference {
584            Reference::One(one) => {
585                // Try writable workflow
586                if let Some(workflow) = &self.workflow_mut {
587                    if let Some(rule) = &workflow.get_rule(one) {
588                        if let Some(node) = rule.get_node() {
589                            let normalized = self.scope.normalize(reference)?;
590                            return Ok((normalized, node, true));
591                        }
592                    }
593                }
594                // Try read-only workflow
595                if let Some(rule) = self.workflow.get_rule(one) {
596                    if let Some(node) = rule.get_node() {
597                        let normalized = self.scope.normalize(reference)?;
598                        return Ok((normalized, node, true));
599                    }
600                }
601                // Try workspace
602                if let Some(rule) = &get_workspace().get_rule(one) {
603                    if let Some(node) = rule.get_node() {
604                        return Ok((reference.clone(), node, false));
605                    }
606                }
607            }
608            Reference::Two(one, two) => {
609                // Try writable workflow
610                if let Some(workflow) = &self.workflow_mut {
611                    if let Some(rule) = &workflow.get_rule(one) {
612                        if let Some(node) = rule.get_node() {
613                            if let Some(rule) = &node.get_workflow_like().get_rule(two) {
614                                if let Some(node) = rule.get_node() {
615                                    let normalized = self.scope.normalize(reference)?;
616                                    return Ok((normalized, node, true));
617                                }
618                            }
619                        }
620                    }
621                }
622                // Try read-only workflow
623                if let Some(rule) = self.workflow.get_rule(one) {
624                    if let Some(node) = rule.get_node() {
625                        if let Some(rule) = &node.get_workflow_like().get_rule(two) {
626                            if let Some(node) = rule.get_node() {
627                                let normalized = self.scope.normalize(reference)?;
628                                return Ok((normalized, node, true));
629                            }
630                        }
631                    }
632                }
633                // Try workspace
634                if let Some(rule) = &get_workspace().get_rule(one) {
635                    if let Some(node) = rule.get_node() {
636                        if let Some(rule) = &node.get_workflow_like().get_rule(two) {
637                            if let Some(node) = rule.get_node() {
638                                return Ok((reference.clone(), node, false));
639                            }
640                        }
641                    }
642                }
643            }
644            Reference::Three(one, two, three) => {
645                if let Some(rule) = &get_workspace().get_rule(one) {
646                    if let Some(node) = rule.get_node() {
647                        if let Some(rule) = &node.get_workflow_like().get_rule(two) {
648                            if let Some(node) = rule.get_node() {
649                                if let Some(rule) = &node.get_workflow_like().get_rule(three) {
650                                    if let Some(node) = rule.get_node() {
651                                        return Ok((reference.clone(), node, false));
652                                    }
653                                }
654                            }
655                        }
656                    }
657                }
658            }
659        }
660        Err(anyhow!("Undefined locator {} within {} scope~{}", reference, &self.scope, reference))
661    }
662}
663
664impl ContextLike for Context {
665
666    /// Start a new closure and save the current parameter stack.
667    fn start_closure(&mut self) -> [(String, Value); 2] {
668        replace(&mut self.parameters, [
669                (String::new(), Value::Empty),
670                (String::new(), Value::Empty)
671            ])
672    }
673
674    /// Set the key of the indexed mapped variable used by element-wise functions like `map`.
675    ///
676    /// # Arguments
677    ///
678    /// * `index` - The index key to set (0 or 1)
679    /// * `identifier` - The key to set
680    fn set_key(&mut self, index: usize, identifier: &str) {
681        if self.parameters[index].0 != *identifier {
682            self.parameters[index].0 = identifier.to_string();
683        }
684    }
685
686    /// Set the value of the index mapped variable used by element-wise functions like `map`.
687    ///
688    /// # Arguments
689    ///
690    /// * `index` - The index value to set (0 or 1)
691    /// * `value` - The value to set
692    fn set_value(&mut self, index: usize, value: &Value) {
693        self.parameters[index].1 = value.clone();
694    }
695
696    /// End the closure and restore the previous parameter stack.
697    fn end_closure(&mut self, stack: [(String, Value); 2]) {
698        self.parameters = stack;
699    }
700    /// Get the value of a referenced variable.
701    ///
702    /// This implementation only supports single-part references (default identifiers).
703    /// Multi-part references (e.g., `object.property`) are not supported in this
704    /// simplified context.
705    ///
706    /// # Arguments
707    ///
708    /// * `reference` - The reference to resolve
709    ///
710    /// # Returns
711    ///
712    /// Returns a `Result<Value>` containing the referenced value or an error
713    /// if the reference cannot be resolved.
714    fn get_referenced(&mut self, reference: &Reference) -> Result<Value> {
715        // Check closure parameters first
716        if *reference == self.parameters[0].0 {
717            Ok(self.parameters[0].1.clone())
718        } else if *reference == self.parameters[1].0 {
719            Ok(self.parameters[1].1.clone())
720        } else {
721            // Get a normalized reference for the cache key
722            let normalized = self.normalize(reference)?;
723            // 1️⃣ Cycle detection
724            if !self.visited.insert(normalized.clone()) {
725                // The key was already present → we are revisiting this rule on the stack.
726                return Err(anyhow!("Circular reference detected ~{}", normalized));
727            }
728            // 2️⃣ Cache lookup – if the value has already been computed in this run,
729            //    just return the cached copy.
730            if let Some(cached) = self.cache.get(&normalized) {
731                // Pop the visited entry before returning.
732                self.visited.remove(&normalized);
733                return Ok(cached.clone());
734            }
735            // 3️⃣ Dereference and evaluate the rule
736            match self.dereference_and_evaluate(reference) {
737                Ok(value) => {
738                    // 4️⃣ Clean up the visited reference (pop stack frame)
739                    self.visited.remove(&normalized);
740                    // 5️⃣ Cache value for the rest of this evaluation.
741                    self.cache.insert(normalized, value.clone());
742                    Ok(value)
743                }
744                Err(e) => {
745                    self.visited.remove(&normalized);
746                    Err(e)
747                }
748            }
749        }
750    }
751
752    /// Set the value of a referenced variable.
753    ///
754    /// # Arguments
755    ///
756    /// * `reference` - The reference to set
757    /// * `literal` - The value to assign
758    ///
759    /// # Returns
760    ///
761    /// Returns `Ok(())` if the referenced value was set or an error if the 
762    /// reference cannot be resolved.
763    fn set_referenced(&mut self, reference: &Reference, value: Value) -> Result<()> {
764        // Check closure parameters first
765        if *reference == self.parameters[0].0 {
766            self.parameters[0].1 = value;
767            Ok(())
768        } else if *reference == self.parameters[1].0 {
769            self.parameters[1].1 = value;
770            Ok(())
771        } else {
772             // Find the referenced rule's typedef
773            match self.dereference_typedef(reference) {
774                Ok((normalized, typedef)) => {
775                    // Promote type
776                    let value = match value.to_type(&typedef) {
777                        Ok(v) => v,
778                        Err(e) => return Err(anyhow!("{}~{}", e, reference))
779                    };
780                    match reference {
781                        Reference::One(one) => {
782                            // Try mutable workflow first
783                            if let Some(workflow_mut) = self.workflow_mut() {
784                                if let Some(rule) = workflow_mut.get_rule_mut(one) {
785                                    return rule.set_value(value);
786                                }
787                            }
788                            /* NOTE: I don't think we want to modify other workflows here
789                            Try read-only workflow (clone and modify)
790                            else if let Some(workflow) = self.workflow.as_ref() {
791                                let mut workflow_clone = (**workflow).clone();
792                                if let Some(rule) = workflow_clone.get_rule_mut(one) {
793                                    rule.set_value(value)?;
794                                    // Update the workflow in context
795                                    self.workflow = Some(Arc::new(workflow_clone));
796                                    return Ok(());
797                                }
798                            } */
799                        }
800                        _ => {}
801                    }
802                    // Cache the referenced assignment instead
803                    self.cache.insert(normalized, value);
804                    Ok(())
805                }
806                Err(e) => Err(e),
807            }
808        }
809    }
810
811    /// Call a standalone function.
812    ///
813    /// Delegates function calls to the singleton function registry.
814    ///
815    /// # Arguments
816    ///
817    /// * `name` - The name of the function to call
818    /// * `arg` - The argument(s) to pass to the function
819    ///
820    /// # Returns
821    ///
822    /// Returns a `Result<Value>` containing the function result or an error
823    /// if the function is not found or execution fails.
824    fn function_call(&mut self, name: &str, arg: Value) -> Result<Value> {
825        let registry = FunctionRegistry::read_lock()?;
826        registry.function_call(self, name, arg)
827    }
828
829    /// Call a method on a value.
830    ///
831    /// Delegates method calls to the singleton function registry.
832    ///
833    /// # Arguments
834    ///
835    /// * `name` - The name of the method to call
836    /// * `value` - The value on which to call the method
837    /// * `arg` - The argument(s) to pass to the method
838    ///
839    /// # Returns
840    ///
841    /// Returns a `Result<Value>` containing the method result or an error
842    /// if the method is not found or execution fails.
843    fn method_call(&mut self, name: &str, value: Value, arg: Value) -> Result<Value> {
844        let registry = FunctionRegistry::read_lock()?;
845        registry.method_call(self, name, value, arg)
846    }
847
848    fn inference_call(&mut self, reference: &Reference, _arg: Value) -> Result<Value> {
849        let (scope, node, is_descendent) = self.dereference(reference)?;
850        if &self.scope == &scope {
851            return Err(anyhow!("Circular inference detected ~{}", scope));
852        }
853        
854        // 1️⃣ Acquire file-specific write lock ONLY when this is a descendant workflow
855        let lock_manager = get_lock_manager();
856        let _lock_guard = if is_descendent {
857            Some(lock_manager.acquire_workflow_lock(scope.clone()))
858        } else {
859            None
860        };
861  
862        // 2️⃣ Push current state onto stack and switch to the new node workflow
863        let stack = self.inference_start(scope);
864        // If this is a descendant workflow we will write to it
865        self.workflow_mut = if is_descendent {
866            // Clone a writable workflow.
867            Some(node.get_workflow_mut())
868        } else {
869            None
870        };
871        // Get a reference to the read-only workflow.
872        self.workflow = node.get_workflow().clone();
873    
874        // 3️⃣ Assign arguments
875        // TODO add argument list to node?
876
877        // Create a provider for Ollama
878        let config = get_config().read().unwrap();
879        let provider = config.get_provider();
880
881        // 4️⃣ Generate prompt
882        if let Ok((system_prompt, user_prompt))
883            = generate_prompt(self.workflow.clone(), &provider.capability) {
884
885            // 5️⃣ Send inference request
886            match send_request(&provider, &system_prompt, &user_prompt) {
887                Ok(inference) => {
888                    if let Some(responses) = parse_response(inference.text()) {
889                        // 6️⃣ Validate response
890                        let _ = validate_responses(self.workflow.clone(), self, responses);
891                    }
892                    //let input = inference.finish();
893                }
894                _ => {}
895            }
896        }
897
898        // 7️⃣ Evaluate workflow expressions
899        node.get_workflow().evaluate(self);
900        
901        // 8️⃣ Save changes
902        if is_descendent {
903            if let Some(workflow_mut) = &mut self.workflow_mut {
904                let _ = workflow_mut.save();
905            }
906            // Atomically replace the old node workflow with the new workflow.
907            if let Some(workflow_mut) = self.workflow_mut.take() {
908                node.set_workflow(workflow_mut);
909            }
910        }
911
912        // 9️⃣ Pop previous state from stack
913        self.inference_end(stack);
914
915        // Return results
916        Ok(Value::Empty)
917        // _lock_guard is dropped here, releasing the lock if it was acquired.
918    }
919}
920