Skip to content

Commit 488bf2e

Browse files
extended arium feature
- added foreachnode and ariumnode - added ExecutableNode protocol - cleaning up arium memory after run
1 parent ae78fcb commit 488bf2e

7 files changed

Lines changed: 463 additions & 27 deletions

File tree

flo_ai/flo_ai/arium/arium.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from flo_ai.tool.base_tool import Tool
88
from flo_ai.arium.models import StartNode, EndNode
99
from flo_ai.arium.events import AriumEventType, AriumEvent
10+
from flo_ai.arium.nodes import AriumNode, ForEachNode
1011
from flo_ai.utils.logger import logger
1112
from flo_ai.utils.variable_extractor import (
1213
extract_variables_from_inputs,
@@ -73,14 +74,16 @@ async def run(
7374

7475
# Execute the workflow with event support
7576
result = await self._execute_graph(
76-
resolved_inputs, event_callback, events_filter
77+
resolved_inputs, event_callback, events_filter, variables
7778
)
7879

7980
# Emit workflow completed event
8081
self._emit_event(
8182
AriumEventType.WORKFLOW_COMPLETED, event_callback, events_filter
8283
)
8384

85+
self.memory = MessageMemory() # cleanup the graph (if used as AriumNode multiple times in graph, then the same instance is used for now hence we need to cleanup memory)
86+
8487
return result
8588

8689
except Exception as e:
@@ -118,6 +121,7 @@ async def _execute_graph(
118121
inputs: List[str | ImageMessage | DocumentMessage],
119122
event_callback: Optional[Callable[[AriumEvent], None]] = None,
120123
events_filter: Optional[List[AriumEventType]] = None,
124+
variables: Optional[Dict[str, Any]] = None,
121125
):
122126
[self.memory.add(msg) for msg in inputs]
123127

@@ -162,11 +166,16 @@ async def _execute_graph(
162166
)
163167
# execute current node
164168
result = await self._execute_node(
165-
current_node, event_callback, events_filter
169+
current_node, event_callback, events_filter, variables
166170
)
167171

168-
# update results to memory
169-
self._add_to_memory(result)
172+
if isinstance(result, List): # for each node will give results array
173+
for item in result:
174+
# update each item in result to memory
175+
self._add_to_memory(item)
176+
else:
177+
# update results to memory
178+
self._add_to_memory(result)
170179

171180
# find next node post current node
172181
# Prepare execution context for router functions
@@ -301,6 +310,7 @@ async def _execute_node(
301310
node: Agent | Tool | StartNode | EndNode,
302311
event_callback: Optional[Callable[[AriumEvent], None]] = None,
303312
events_filter: Optional[List[AriumEventType]] = None,
313+
variables: Optional[Dict[str, Any]] = None,
304314
):
305315
"""
306316
Execute a single node with optional event emission.
@@ -318,6 +328,10 @@ async def _execute_node(
318328
node_type = 'agent'
319329
elif isinstance(node, Tool):
320330
node_type = 'tool'
331+
elif isinstance(node, ForEachNode):
332+
node_type = 'foreach'
333+
elif isinstance(node, AriumNode):
334+
node_type = 'arium'
321335
elif isinstance(node, StartNode):
322336
node_type = 'start'
323337
elif isinstance(node, EndNode):
@@ -342,7 +356,16 @@ async def _execute_node(
342356
# Variables are already resolved, pass empty dict to avoid re-processing
343357
result = await node.run(self.memory.get(), variables={})
344358
elif isinstance(node, Tool):
345-
result = await node.execute()
359+
# result = await node.execute() # as Tool is also an ExecutableNode now
360+
result = await node.run(inputs=[], variables=None)
361+
elif isinstance(node, ForEachNode):
362+
result = await node.run(
363+
inputs=self.memory.get(),
364+
variables=variables,
365+
)
366+
elif isinstance(node, AriumNode):
367+
# AriumNode execution
368+
result = await node.run(inputs=self.memory.get(), variables=variables)
346369
elif isinstance(node, StartNode):
347370
result = None
348371
elif isinstance(node, EndNode):

flo_ai/flo_ai/arium/base.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import inspect
22
from functools import partial
3+
from flo_ai.arium.nodes import AriumNode, ForEachNode
4+
from flo_ai.arium.protocols import ExecutableNode
35
from flo_ai.models.agent import Agent
46
from flo_ai.tool.base_tool import Tool
57
from flo_ai.utils.logger import logger
@@ -12,13 +14,13 @@ class BaseArium:
1214
def __init__(self):
1315
self.start_node_name = '__start__'
1416
self.end_node_names: set = set() # Support multiple end nodes
15-
self.nodes: Dict[str, Agent | Tool | StartNode | EndNode] = dict()
17+
self.nodes: Dict[str, ExecutableNode | StartNode | EndNode] = dict()
1618
self.edges: Dict[str, Edge] = dict()
1719

18-
def add_nodes(self, agents: List[Agent | Tool | StartNode | EndNode]):
20+
def add_nodes(self, agents: List[ExecutableNode | StartNode | EndNode]):
1921
self.nodes.update({agent.name: agent for agent in agents})
2022

21-
def start_at(self, node: Agent | Tool | StartNode | EndNode):
23+
def start_at(self, node: ExecutableNode):
2224
start_node = StartNode()
2325
if start_node.name in self.nodes:
2426
raise ValueError(f'Start node {start_node.name} already exists')
@@ -27,7 +29,7 @@ def start_at(self, node: Agent | Tool | StartNode | EndNode):
2729
router_fn=partial(default_router, to_node=node.name), to_nodes=[node.name]
2830
)
2931

30-
def add_end_to(self, node: Agent | Tool | StartNode | EndNode):
32+
def add_end_to(self, node: ExecutableNode):
3133
# Create a unique end node name for this specific node
3234
end_node_name = f'__end__{node.name}__'
3335
end_node = EndNode()
@@ -378,5 +380,9 @@ def _get_node_type(self, node) -> str:
378380
return 'agent'
379381
elif isinstance(node, Tool):
380382
return 'tool'
383+
elif isinstance(node, ForEachNode):
384+
return 'foreach'
385+
elif isinstance(node, AriumNode):
386+
return 'arium'
381387
else:
382388
return 'unknown'

0 commit comments

Comments
 (0)