griptape-nodes 0.52.0__py3-none-any.whl → 0.53.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. griptape_nodes/__init__.py +6 -943
  2. griptape_nodes/__main__.py +6 -0
  3. griptape_nodes/app/api.py +1 -12
  4. griptape_nodes/app/app.py +256 -209
  5. griptape_nodes/cli/__init__.py +1 -0
  6. griptape_nodes/cli/commands/__init__.py +1 -0
  7. griptape_nodes/cli/commands/config.py +71 -0
  8. griptape_nodes/cli/commands/engine.py +80 -0
  9. griptape_nodes/cli/commands/init.py +548 -0
  10. griptape_nodes/cli/commands/libraries.py +90 -0
  11. griptape_nodes/cli/commands/self.py +117 -0
  12. griptape_nodes/cli/main.py +46 -0
  13. griptape_nodes/cli/shared.py +84 -0
  14. griptape_nodes/common/__init__.py +1 -0
  15. griptape_nodes/common/directed_graph.py +55 -0
  16. griptape_nodes/drivers/storage/local_storage_driver.py +7 -2
  17. griptape_nodes/exe_types/core_types.py +60 -2
  18. griptape_nodes/exe_types/node_types.py +38 -24
  19. griptape_nodes/machines/control_flow.py +86 -22
  20. griptape_nodes/machines/fsm.py +10 -1
  21. griptape_nodes/machines/parallel_resolution.py +570 -0
  22. griptape_nodes/machines/{node_resolution.py → sequential_resolution.py} +22 -51
  23. griptape_nodes/mcp_server/server.py +1 -1
  24. griptape_nodes/retained_mode/events/base_events.py +2 -2
  25. griptape_nodes/retained_mode/events/node_events.py +4 -3
  26. griptape_nodes/retained_mode/griptape_nodes.py +25 -12
  27. griptape_nodes/retained_mode/managers/agent_manager.py +9 -5
  28. griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +3 -1
  29. griptape_nodes/retained_mode/managers/context_manager.py +6 -5
  30. griptape_nodes/retained_mode/managers/flow_manager.py +117 -204
  31. griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +1 -1
  32. griptape_nodes/retained_mode/managers/library_manager.py +35 -25
  33. griptape_nodes/retained_mode/managers/node_manager.py +81 -199
  34. griptape_nodes/retained_mode/managers/object_manager.py +11 -5
  35. griptape_nodes/retained_mode/managers/os_manager.py +24 -9
  36. griptape_nodes/retained_mode/managers/secrets_manager.py +8 -4
  37. griptape_nodes/retained_mode/managers/settings.py +32 -1
  38. griptape_nodes/retained_mode/managers/static_files_manager.py +8 -3
  39. griptape_nodes/retained_mode/managers/sync_manager.py +8 -5
  40. griptape_nodes/retained_mode/managers/workflow_manager.py +110 -122
  41. griptape_nodes/traits/add_param_button.py +1 -1
  42. griptape_nodes/traits/button.py +216 -6
  43. griptape_nodes/traits/color_picker.py +66 -0
  44. griptape_nodes/traits/traits.json +4 -0
  45. {griptape_nodes-0.52.0.dist-info → griptape_nodes-0.53.0.dist-info}/METADATA +2 -1
  46. {griptape_nodes-0.52.0.dist-info → griptape_nodes-0.53.0.dist-info}/RECORD +48 -34
  47. {griptape_nodes-0.52.0.dist-info → griptape_nodes-0.53.0.dist-info}/WHEEL +0 -0
  48. {griptape_nodes-0.52.0.dist-info → griptape_nodes-0.53.0.dist-info}/entry_points.txt +0 -0
@@ -9,7 +9,8 @@ from griptape_nodes.exe_types.core_types import Parameter
9
9
  from griptape_nodes.exe_types.node_types import BaseNode, NodeResolutionState
10
10
  from griptape_nodes.exe_types.type_validator import TypeValidator
11
11
  from griptape_nodes.machines.fsm import FSM, State
12
- from griptape_nodes.machines.node_resolution import NodeResolutionMachine
12
+ from griptape_nodes.machines.parallel_resolution import ParallelResolutionMachine
13
+ from griptape_nodes.machines.sequential_resolution import SequentialResolutionMachine
13
14
  from griptape_nodes.retained_mode.events.base_events import ExecutionEvent, ExecutionGriptapeNodeEvent
14
15
  from griptape_nodes.retained_mode.events.execution_events import (
15
16
  ControlFlowResolvedEvent,
@@ -17,6 +18,7 @@ from griptape_nodes.retained_mode.events.execution_events import (
17
18
  SelectedControlOutputEvent,
18
19
  )
19
20
  from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
21
+ from griptape_nodes.retained_mode.managers.settings import WorkflowExecutionMode
20
22
 
21
23
 
22
24
  @dataclass
@@ -38,12 +40,23 @@ logger = logging.getLogger("griptape_nodes")
38
40
  class ControlFlowContext:
39
41
  flow: ControlFlow
40
42
  current_node: BaseNode | None
41
- resolution_machine: NodeResolutionMachine
43
+ resolution_machine: ParallelResolutionMachine | SequentialResolutionMachine
42
44
  selected_output: Parameter | None
43
45
  paused: bool = False
44
-
45
- def __init__(self) -> None:
46
- self.resolution_machine = NodeResolutionMachine()
46
+ flow_name: str
47
+
48
+ def __init__(
49
+ self,
50
+ flow_name: str,
51
+ max_nodes_in_parallel: int,
52
+ *,
53
+ execution_type: WorkflowExecutionMode = WorkflowExecutionMode.SEQUENTIAL,
54
+ ) -> None:
55
+ self.flow_name = flow_name
56
+ if execution_type == WorkflowExecutionMode.PARALLEL:
57
+ self.resolution_machine = ParallelResolutionMachine(flow_name, max_nodes_in_parallel)
58
+ else:
59
+ self.resolution_machine = SequentialResolutionMachine()
47
60
  self.current_node = None
48
61
 
49
62
  def get_next_node(self, output_parameter: Parameter) -> NextNodeInfo | None:
@@ -66,11 +79,11 @@ class ControlFlowContext:
66
79
  return NextNodeInfo(node=node, entry_parameter=None)
67
80
  return None
68
81
 
69
- def reset(self) -> None:
82
+ def reset(self, *, cancel: bool = False) -> None:
70
83
  if self.current_node:
71
84
  self.current_node.clear_node()
72
85
  self.current_node = None
73
- self.resolution_machine.reset_machine()
86
+ self.resolution_machine.reset_machine(cancel=cancel)
74
87
  self.selected_output = None
75
88
  self.paused = False
76
89
 
@@ -194,11 +207,18 @@ class CompleteState(State):
194
207
 
195
208
  # MACHINE TIME!!!
196
209
  class ControlFlowMachine(FSM[ControlFlowContext]):
197
- def __init__(self) -> None:
198
- context = ControlFlowContext()
210
+ def __init__(self, flow_name: str) -> None:
211
+ execution_type = GriptapeNodes.ConfigManager().get_config_value(
212
+ "workflow_execution_mode", default=WorkflowExecutionMode.SEQUENTIAL
213
+ )
214
+ max_nodes_in_parallel = GriptapeNodes.ConfigManager().get_config_value("max_nodes_in_parallel", default=5)
215
+ context = ControlFlowContext(flow_name, max_nodes_in_parallel, execution_type=execution_type)
199
216
  super().__init__(context)
200
217
 
201
218
  async def start_flow(self, start_node: BaseNode, debug_mode: bool = False) -> None: # noqa: FBT001, FBT002
219
+ # If using DAG resolution, process data_nodes from queue first
220
+ if isinstance(self._context.resolution_machine, ParallelResolutionMachine):
221
+ await self._process_data_nodes_for_dag()
202
222
  self._context.current_node = start_node
203
223
  # Set entry control parameter for initial node (None for workflow start)
204
224
  start_node.set_entry_control_parameter(None)
@@ -214,31 +234,75 @@ class ControlFlowMachine(FSM[ControlFlowContext]):
214
234
 
215
235
  def change_debug_mode(self, debug_mode: bool) -> None: # noqa: FBT001
216
236
  self._context.paused = debug_mode
217
- self._context.resolution_machine.change_debug_mode(debug_mode)
237
+ self._context.resolution_machine.change_debug_mode(debug_mode=debug_mode)
218
238
 
219
239
  async def granular_step(self, change_debug_mode: bool) -> None: # noqa: FBT001
220
240
  resolution_machine = self._context.resolution_machine
241
+
221
242
  if change_debug_mode:
222
- resolution_machine.change_debug_mode(True)
243
+ resolution_machine.change_debug_mode(debug_mode=True)
223
244
  await resolution_machine.update()
224
245
 
225
- # Tick the control flow if the resolution machine inside it isn't busy.
226
- if resolution_machine.is_complete() or not resolution_machine.is_started(): # noqa: SIM102
246
+ # Tick the control flow if the current machine isn't busy
247
+ if self._current_state is ResolveNodeState and ( # noqa: SIM102
248
+ resolution_machine.is_complete() or not resolution_machine.is_started()
249
+ ):
227
250
  # Don't tick ourselves if we are already complete.
228
251
  if self._current_state is not None:
229
252
  await self.update()
230
253
 
231
254
  async def node_step(self) -> None:
232
255
  resolution_machine = self._context.resolution_machine
233
- resolution_machine.change_debug_mode(False)
234
- await resolution_machine.update()
235
256
 
236
- # Tick the control flow if the resolution machine inside it isn't busy.
237
- if resolution_machine.is_complete() or not resolution_machine.is_started(): # noqa: SIM102
238
- # Don't tick ourselves if we are already complete.
239
- if self._current_state is not None:
240
- await self.update()
257
+ resolution_machine.change_debug_mode(debug_mode=False)
258
+
259
+ # If we're in the resolution phase, step the resolution machine
260
+ if self._current_state is ResolveNodeState:
261
+ await resolution_machine.update()
262
+
263
+ # Tick the control flow if the current machine isn't busy
264
+ if self._current_state is ResolveNodeState and (
265
+ resolution_machine.is_complete() or not resolution_machine.is_started()
266
+ ):
267
+ await self.update()
241
268
 
242
- def reset_machine(self) -> None:
243
- self._context.reset()
269
+ async def _process_data_nodes_for_dag(self) -> None:
270
+ """Process data_nodes from the global queue to build unified DAG.
271
+
272
+ This method identifies data_nodes in the execution queue and processes
273
+ their dependencies into the DAG resolution machine.
274
+ """
275
+ if not isinstance(self._context.resolution_machine, ParallelResolutionMachine):
276
+ return
277
+ # Get the global flow queue
278
+ flow_manager = GriptapeNodes.FlowManager()
279
+ queue_items = list(flow_manager.global_flow_queue.queue)
280
+
281
+ # Find data_nodes and remove them from queue
282
+ data_nodes = []
283
+ for item in queue_items:
284
+ from griptape_nodes.retained_mode.managers.flow_manager import DagExecutionType
285
+
286
+ if item.dag_execution_type == DagExecutionType.DATA_NODE:
287
+ data_nodes.append(item.node)
288
+ flow_manager.global_flow_queue.queue.remove(item)
289
+
290
+ # Build DAG for each data node
291
+ for node in data_nodes:
292
+ node.state = NodeResolutionState.UNRESOLVED
293
+ await self._context.resolution_machine.resolve_node(node, build_only=True)
294
+
295
+ # Run resolution until complete for this node's subgraph
296
+ while not self._context.resolution_machine.is_complete():
297
+ await self._context.resolution_machine.update()
298
+
299
+ # Reset the machine state to allow adding more nodes
300
+ self._context.resolution_machine.current_state = None
301
+
302
+ def reset_machine(self, *, cancel: bool = False) -> None:
303
+ self._context.reset(cancel=cancel)
244
304
  self._current_state = None
305
+
306
+ @property
307
+ def resolution_machine(self) -> ParallelResolutionMachine | SequentialResolutionMachine:
308
+ return self._context.resolution_machine
@@ -34,9 +34,18 @@ class FSM[T]:
34
34
  # Enter the initial state.
35
35
  await self.transition_state(initial_state)
36
36
 
37
- def get_current_state(self) -> type[State] | None:
37
+ @property
38
+ def current_state(self) -> type[State] | None:
38
39
  return self._current_state
39
40
 
41
+ @current_state.setter
42
+ def current_state(self, value: type[State] | None) -> None:
43
+ self._current_state = value
44
+
45
+ @property
46
+ def context(self) -> T:
47
+ return self._context
48
+
40
49
  async def transition_state(self, new_state: type[State] | None) -> None:
41
50
  while new_state is not None:
42
51
  # Exit the current state.