griptape-nodes 0.41.0__py3-none-any.whl → 0.42.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 (49) hide show
  1. griptape_nodes/app/__init__.py +1 -5
  2. griptape_nodes/app/app.py +12 -9
  3. griptape_nodes/app/app_sessions.py +132 -36
  4. griptape_nodes/app/watch.py +3 -1
  5. griptape_nodes/drivers/storage/local_storage_driver.py +3 -2
  6. griptape_nodes/exe_types/flow.py +68 -368
  7. griptape_nodes/machines/control_flow.py +16 -13
  8. griptape_nodes/machines/node_resolution.py +16 -14
  9. griptape_nodes/node_library/workflow_registry.py +2 -2
  10. griptape_nodes/retained_mode/events/agent_events.py +70 -8
  11. griptape_nodes/retained_mode/events/app_events.py +132 -11
  12. griptape_nodes/retained_mode/events/arbitrary_python_events.py +23 -0
  13. griptape_nodes/retained_mode/events/base_events.py +7 -25
  14. griptape_nodes/retained_mode/events/config_events.py +87 -11
  15. griptape_nodes/retained_mode/events/connection_events.py +56 -5
  16. griptape_nodes/retained_mode/events/context_events.py +27 -4
  17. griptape_nodes/retained_mode/events/execution_events.py +99 -14
  18. griptape_nodes/retained_mode/events/flow_events.py +165 -7
  19. griptape_nodes/retained_mode/events/library_events.py +193 -15
  20. griptape_nodes/retained_mode/events/logger_events.py +11 -0
  21. griptape_nodes/retained_mode/events/node_events.py +242 -22
  22. griptape_nodes/retained_mode/events/object_events.py +40 -4
  23. griptape_nodes/retained_mode/events/os_events.py +13 -2
  24. griptape_nodes/retained_mode/events/parameter_events.py +212 -8
  25. griptape_nodes/retained_mode/events/secrets_events.py +59 -7
  26. griptape_nodes/retained_mode/events/static_file_events.py +57 -4
  27. griptape_nodes/retained_mode/events/validation_events.py +39 -4
  28. griptape_nodes/retained_mode/events/workflow_events.py +188 -17
  29. griptape_nodes/retained_mode/griptape_nodes.py +46 -323
  30. griptape_nodes/retained_mode/managers/agent_manager.py +1 -1
  31. griptape_nodes/retained_mode/managers/engine_identity_manager.py +146 -0
  32. griptape_nodes/retained_mode/managers/event_manager.py +14 -2
  33. griptape_nodes/retained_mode/managers/flow_manager.py +749 -64
  34. griptape_nodes/retained_mode/managers/library_manager.py +112 -2
  35. griptape_nodes/retained_mode/managers/node_manager.py +34 -31
  36. griptape_nodes/retained_mode/managers/object_manager.py +11 -3
  37. griptape_nodes/retained_mode/managers/os_manager.py +70 -1
  38. griptape_nodes/retained_mode/managers/secrets_manager.py +4 -0
  39. griptape_nodes/retained_mode/managers/session_manager.py +328 -0
  40. griptape_nodes/retained_mode/managers/settings.py +7 -0
  41. griptape_nodes/retained_mode/managers/workflow_manager.py +523 -454
  42. griptape_nodes/retained_mode/retained_mode.py +44 -0
  43. griptape_nodes/retained_mode/utils/engine_identity.py +141 -27
  44. {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.42.0.dist-info}/METADATA +2 -2
  45. {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.42.0.dist-info}/RECORD +48 -47
  46. griptape_nodes/retained_mode/utils/session_persistence.py +0 -105
  47. {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.42.0.dist-info}/WHEEL +0 -0
  48. {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.42.0.dist-info}/entry_points.txt +0 -0
  49. {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.42.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,19 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- from queue import Queue
5
4
  from typing import TYPE_CHECKING, NamedTuple
6
5
 
7
- from griptape.events import EventBus
8
-
9
- from griptape_nodes.exe_types.connections import Connections
10
- from griptape_nodes.exe_types.core_types import ParameterTypeBuiltin
11
- from griptape_nodes.exe_types.node_types import NodeResolutionState, StartLoopNode, StartNode
12
- from griptape_nodes.machines.control_flow import CompleteState, ControlFlowMachine
13
- from griptape_nodes.retained_mode.events.base_events import ExecutionEvent, ExecutionGriptapeNodeEvent
14
- from griptape_nodes.retained_mode.events.execution_events import ControlFlowCancelledEvent
15
-
16
6
  if TYPE_CHECKING:
7
+ from queue import Queue
8
+
17
9
  from griptape_nodes.exe_types.core_types import Parameter
18
10
  from griptape_nodes.exe_types.node_types import BaseNode
19
11
 
@@ -28,22 +20,16 @@ class CurrentNodes(NamedTuple):
28
20
  current_resolving_node: str | None
29
21
 
30
22
 
31
- # The flow will own all of the nodes and the connections
23
+ # The flow will own all of the nodes
32
24
  class ControlFlow:
33
25
  name: str
34
- connections: Connections
35
26
  nodes: dict[str, BaseNode]
36
- control_flow_machine: ControlFlowMachine
37
- single_node_resolution: bool
38
- flow_queue: Queue[BaseNode]
27
+ metadata: dict
39
28
 
40
- def __init__(self, name: str) -> None:
29
+ def __init__(self, name: str, metadata: dict | None = None) -> None:
41
30
  self.name = name
42
- self.connections = Connections()
43
31
  self.nodes = {}
44
- self.control_flow_machine = ControlFlowMachine(self)
45
- self.single_node_resolution = False
46
- self.flow_queue = Queue()
32
+ self.metadata = metadata or {}
47
33
 
48
34
  def add_node(self, node: BaseNode) -> None:
49
35
  self.nodes[node.name] = node
@@ -59,15 +45,25 @@ class ControlFlow:
59
45
  target_parameter: Parameter,
60
46
  ) -> bool:
61
47
  if source_node.name in self.nodes and target_node.name in self.nodes:
62
- return self.connections.add_connection(source_node, source_parameter, target_node, target_parameter)
48
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
49
+
50
+ return (
51
+ GriptapeNodes.FlowManager()
52
+ .get_connections()
53
+ .add_connection(source_node, source_parameter, target_node, target_parameter)
54
+ )
63
55
  return False
64
56
 
65
57
  def remove_connection(
66
58
  self, source_node: BaseNode, source_parameter: Parameter, target_node: BaseNode, target_parameter: Parameter
67
59
  ) -> bool:
68
60
  if source_node.name in self.nodes and target_node.name in self.nodes:
69
- return self.connections.remove_connection(
70
- source_node.name, source_parameter.name, target_node.name, target_parameter.name
61
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
62
+
63
+ return (
64
+ GriptapeNodes.FlowManager()
65
+ .get_connections()
66
+ .remove_connection(source_node.name, source_parameter.name, target_node.name, target_parameter.name)
71
67
  )
72
68
  return False
73
69
 
@@ -78,343 +74,24 @@ class ControlFlow:
78
74
  target_node: BaseNode,
79
75
  target_parameter: Parameter,
80
76
  ) -> bool:
81
- if source_node.name in self.nodes and target_node.name in self.nodes:
82
- connected_node_tuple = self.get_connected_output_parameters(node=source_node, param=source_parameter)
83
- if connected_node_tuple is not None:
84
- for connected_node_values in connected_node_tuple:
85
- connected_node, connected_param = connected_node_values
86
- if connected_node is target_node and connected_param is target_parameter:
87
- return True
88
- return False
77
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
89
78
 
90
- def start_flow(self, start_node: BaseNode | None = None, debug_mode: bool = False) -> None: # noqa: FBT001, FBT002
91
- if self.check_for_existing_running_flow():
92
- # If flow already exists, throw an error
93
- errormsg = "This workflow is already in progress. Please wait for the current process to finish before starting again."
94
- raise RuntimeError(errormsg)
95
-
96
- if start_node is None:
97
- if self.flow_queue.empty():
98
- errormsg = "No Flow exists. You must create at least one control connection."
99
- raise RuntimeError(errormsg)
100
- start_node = self.flow_queue.get()
101
-
102
- try:
103
- self.control_flow_machine.start_flow(start_node, debug_mode)
104
- self.flow_queue.task_done()
105
- except Exception:
106
- if self.check_for_existing_running_flow():
107
- self.cancel_flow_run()
108
- raise
109
-
110
- def check_for_existing_running_flow(self) -> bool:
111
- if self.control_flow_machine._current_state is not CompleteState and self.control_flow_machine._current_state:
112
- # Flow already exists in progress
113
- return True
114
- return bool(
115
- not self.control_flow_machine._context.resolution_machine.is_complete()
116
- and self.control_flow_machine._context.resolution_machine.is_started()
117
- )
118
-
119
- def resolve_singular_node(self, node: BaseNode, debug_mode: bool = False) -> None: # noqa: FBT001, FBT002
120
- # Set that we are only working on one node right now! no other stepping allowed
121
- if self.check_for_existing_running_flow():
122
- # If flow already exists, throw an error
123
- errormsg = f"This workflow is already in progress. Please wait for the current process to finish before starting {node.name} again."
124
- raise RuntimeError(errormsg)
125
- self.single_node_resolution = True
126
- # Get the node resolution machine for the current flow!
127
- self.control_flow_machine._context.current_node = node
128
- resolution_machine = self.control_flow_machine._context.resolution_machine
129
- # Set debug mode
130
- resolution_machine.change_debug_mode(debug_mode)
131
- # Resolve the node.
132
- node.state = NodeResolutionState.UNRESOLVED
133
- resolution_machine.resolve_node(node)
134
- # decide if we can change it back to normal flow mode!
135
- if resolution_machine.is_complete():
136
- self.single_node_resolution = False
137
- self.control_flow_machine._context.current_node = None
138
-
139
- def single_execution_step(self, change_debug_mode: bool) -> None: # noqa: FBT001
140
- # do a granular step
141
- if not self.check_for_existing_running_flow():
142
- if self.flow_queue.empty():
143
- errormsg = "Flow has not yet been started. Cannot step while no flow has begun."
144
- raise RuntimeError(errormsg)
145
- start_node = self.flow_queue.get()
146
- self.control_flow_machine.start_flow(start_node, debug_mode=True)
147
- start_node = self.flow_queue.task_done()
148
- return
149
- self.control_flow_machine.granular_step(change_debug_mode)
150
- resolution_machine = self.control_flow_machine._context.resolution_machine
151
- if self.single_node_resolution:
152
- resolution_machine = self.control_flow_machine._context.resolution_machine
153
- if resolution_machine.is_complete():
154
- self.single_node_resolution = False
155
-
156
- def single_node_step(self) -> None:
157
- # It won't call single_node_step without an existing flow running from US.
158
- if not self.check_for_existing_running_flow():
159
- if self.flow_queue.empty():
160
- errormsg = "Flow has not yet been started. Cannot step while no flow has begun."
161
- raise RuntimeError(errormsg)
162
- start_node = self.flow_queue.get()
163
- self.control_flow_machine.start_flow(start_node, debug_mode=True)
164
- start_node = self.flow_queue.task_done()
165
- return
166
- # Step over a whole node
167
- if self.single_node_resolution:
168
- msg = "Cannot step through the Control Flow in Single Node Execution"
169
- raise RuntimeError(msg)
170
- self.control_flow_machine.node_step()
171
- # Start the next resolution step now please.
172
- if not self.check_for_existing_running_flow() and not self.flow_queue.empty():
173
- start_node = self.flow_queue.get()
174
- self.flow_queue.task_done()
175
- self.control_flow_machine.start_flow(start_node, debug_mode=True)
176
-
177
- def continue_executing(self) -> None:
178
- if not self.check_for_existing_running_flow():
179
- if self.flow_queue.empty():
180
- errormsg = "Flow has not yet been started. Cannot step while no flow has begun."
181
- raise RuntimeError(errormsg)
182
- start_node = self.flow_queue.get()
183
- self.flow_queue.task_done()
184
- self.control_flow_machine.start_flow(start_node, debug_mode=False)
185
- return
186
- # Turn all debugging to false and continue on
187
- self.control_flow_machine.change_debug_mode(False)
188
- if self.single_node_resolution:
189
- if self.control_flow_machine._context.resolution_machine.is_complete():
190
- self.single_node_resolution = False
191
- else:
192
- self.control_flow_machine._context.resolution_machine.update()
193
- else:
194
- self.control_flow_machine.node_step()
195
- # Now it is done executing. make sure it's actually done?
196
- if not self.check_for_existing_running_flow() and not self.flow_queue.empty():
197
- start_node = self.flow_queue.get()
198
- self.flow_queue.task_done()
199
- self.control_flow_machine.start_flow(start_node, debug_mode=False)
200
-
201
- def cancel_flow_run(self) -> None:
202
- if not self.check_for_existing_running_flow():
203
- errormsg = "Flow has not yet been started. Cannot cancel flow that hasn't begun."
204
- raise RuntimeError(errormsg)
205
- self.clear_flow_queue()
206
- self.control_flow_machine.reset_machine()
207
- # Reset control flow machine
208
- self.single_node_resolution = False
209
- logger.debug("Cancelling flow run")
210
-
211
- EventBus.publish_event(
212
- ExecutionGriptapeNodeEvent(wrapped_event=ExecutionEvent(payload=ControlFlowCancelledEvent()))
213
- )
214
-
215
- def unresolve_whole_flow(self) -> None:
216
- for node in self.nodes.values():
217
- node.make_node_unresolved(current_states_to_trigger_change_event=None)
218
-
219
- def flow_state(self) -> CurrentNodes:
220
- if not self.check_for_existing_running_flow():
221
- msg = "Flow hasn't started."
222
- raise RuntimeError(msg)
223
- current_control_node = (
224
- self.control_flow_machine._context.current_node.name
225
- if self.control_flow_machine._context.current_node is not None
226
- else None
227
- )
228
- focus_stack_for_node = self.control_flow_machine._context.resolution_machine._context.focus_stack
229
- current_resolving_node = focus_stack_for_node[-1].node.name if len(focus_stack_for_node) else None
230
- return CurrentNodes(current_control_node, current_resolving_node)
231
-
232
- def clear_flow_queue(self) -> None:
233
- self.flow_queue.queue.clear()
79
+ return GriptapeNodes.FlowManager().has_connection(source_node, source_parameter, target_node, target_parameter)
234
80
 
235
- def get_connected_output_parameters(self, node: BaseNode, param: Parameter) -> list[tuple[BaseNode, Parameter]]:
236
- connections = []
237
- if node.name in self.connections.outgoing_index:
238
- outgoing_params = self.connections.outgoing_index[node.name]
239
- if param.name in outgoing_params:
240
- for connection_id in outgoing_params[param.name]:
241
- connection = self.connections.connections[connection_id]
242
- connections.append((connection.target_node, connection.target_parameter))
243
- return connections
81
+ def clear_execution_queue(self) -> None:
82
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
244
83
 
245
- def get_connected_input_parameters(self, node: BaseNode, param: Parameter) -> list[tuple[BaseNode, Parameter]]:
246
- connections = []
247
- if node.name in self.connections.incoming_index:
248
- incoming_params = self.connections.incoming_index[node.name]
249
- if param.name in incoming_params:
250
- for connection_id in incoming_params[param.name]:
251
- connection = self.connections.connections[connection_id]
252
- connections.append((connection.source_node, connection.source_parameter))
253
- return connections
254
-
255
- def get_connected_output_from_node(self, node: BaseNode) -> list[tuple[BaseNode, Parameter]]:
256
- connections = []
257
- if node.name in self.connections.outgoing_index:
258
- connection_ids = [
259
- item for value_list in self.connections.outgoing_index[node.name].values() for item in value_list
260
- ]
261
- for connection_id in connection_ids:
262
- connection = self.connections.connections[connection_id]
263
- connections.append((connection.target_node, connection.target_parameter))
264
- return connections
265
-
266
- def get_connected_input_from_node(self, node: BaseNode) -> list[tuple[BaseNode, Parameter]]:
267
- connections = []
268
- if node.name in self.connections.incoming_index:
269
- connection_ids = [
270
- item for value_list in self.connections.incoming_index[node.name].values() for item in value_list
271
- ]
272
- for connection_id in connection_ids:
273
- connection = self.connections.connections[connection_id]
274
- connections.append((connection.source_node, connection.source_parameter))
275
- return connections
276
-
277
- def get_start_node_queue(self) -> Queue | None: # noqa: C901, PLR0912
278
- # check all nodes in flow
279
- # add them all to a stack. We're calling this only if no flow was specified, so we're running them all.
280
- self.flow_queue = Queue()
281
- # if no nodes, no flow.
282
- if not len(self.nodes):
283
- return None
284
- data_nodes = []
285
- valid_data_nodes = []
286
- start_nodes = []
287
- control_nodes = []
288
- for node in self.nodes.values():
289
- # if it's a start node, start here! Return the first one!
290
- if isinstance(node, StartNode):
291
- start_nodes.append(node)
292
- continue
293
- # no start nodes. let's find the first control node.
294
- # if it's a control node, there could be a flow.
295
- control_param = False
296
- for parameter in node.parameters:
297
- if ParameterTypeBuiltin.CONTROL_TYPE.value == parameter.output_type:
298
- control_param = True
299
- break
300
- if not control_param:
301
- # saving this for later
302
- data_nodes.append(node)
303
- # If this node doesn't have a control connection..
304
- continue
305
- cn_mgr = self.connections
306
- # check if it has an incoming connection. If it does, it's not a start node
307
- has_control_connection = False
308
- if node.name in cn_mgr.incoming_index:
309
- for param_name in cn_mgr.incoming_index[node.name]:
310
- param = node.get_parameter_by_name(param_name)
311
- if param and ParameterTypeBuiltin.CONTROL_TYPE.value == param.output_type:
312
- # there is a control connection coming in
313
- has_control_connection = True
314
- break
315
- # if there is a connection coming in, isn't a start.
316
- if has_control_connection and not isinstance(node, StartLoopNode):
317
- continue
318
- # Does it have an outgoing connection?
319
- if node.name in cn_mgr.outgoing_index:
320
- # If one of the outgoing connections is control, add it. otherwise don't.
321
- for param_name in cn_mgr.outgoing_index[node.name]:
322
- param = node.get_parameter_by_name(param_name)
323
- if param and ParameterTypeBuiltin.CONTROL_TYPE.value == param.output_type:
324
- control_nodes.append(node)
325
- break
326
- else:
327
- control_nodes.append(node)
328
-
329
- # If we've gotten to this point, there are no control parameters
330
- # Let's return a data node that has no OUTGOING data connections!
331
- for node in data_nodes:
332
- cn_mgr = self.connections
333
- # check if it has an outgoing connection. We don't want it to (that means we get the most resolution)
334
- if node.name not in cn_mgr.outgoing_index:
335
- valid_data_nodes.append(node)
336
- # ok now
337
- for node in start_nodes:
338
- self.flow_queue.put(node)
339
- for node in control_nodes:
340
- self.flow_queue.put(node)
341
- for node in valid_data_nodes:
342
- self.flow_queue.put(node)
343
-
344
- return self.flow_queue
345
-
346
- def get_start_node_from_node(self, node: BaseNode) -> BaseNode | None:
347
- # backwards chain in control outputs.
348
- if node not in self.nodes.values():
349
- return None
350
- # Go back through incoming control connections to get the start node
351
- curr_node = node
352
- prev_node = self.get_prev_node(curr_node)
353
- # Fencepost loop - get the first previous node name and then we go
354
- while prev_node:
355
- curr_node = prev_node
356
- prev_node = self.get_prev_node(prev_node)
357
- return curr_node
358
-
359
- def get_prev_node(self, node: BaseNode) -> BaseNode | None:
360
- if node.name in self.connections.incoming_index:
361
- parameters = self.connections.incoming_index[node.name]
362
- for parameter_name in parameters:
363
- parameter = node.get_parameter_by_name(parameter_name)
364
- if parameter and ParameterTypeBuiltin.CONTROL_TYPE.value == parameter.output_type:
365
- # this is a control connection
366
- connection_ids = self.connections.incoming_index[node.name][parameter_name]
367
- for connection_id in connection_ids:
368
- connection = self.connections.connections[connection_id]
369
- return connection.get_source_node()
370
- return None
371
-
372
- def stop_flow_breakpoint(self, node: BaseNode) -> None:
373
- # This will prevent the flow from continuing on.
374
- node.stop_flow = True
84
+ GriptapeNodes.FlowManager().clear_execution_queue()
375
85
 
376
86
  def get_connections_on_node(self, node: BaseNode) -> list[BaseNode] | None:
377
- # get all of the connection ids
378
- connected_nodes = []
379
- # Handle outgoing connections
380
- if node.name in self.connections.outgoing_index:
381
- outgoing_params = self.connections.outgoing_index[node.name]
382
- outgoing_connection_ids = []
383
- for connection_ids in outgoing_params.values():
384
- outgoing_connection_ids = outgoing_connection_ids + connection_ids
385
- for connection_id in outgoing_connection_ids:
386
- connection = self.connections.connections[connection_id]
387
- if connection.source_node not in connected_nodes:
388
- connected_nodes.append(connection.target_node)
389
- # Handle incoming connections
390
- if node.name in self.connections.incoming_index:
391
- incoming_params = self.connections.incoming_index[node.name]
392
- incoming_connection_ids = []
393
- for connection_ids in incoming_params.values():
394
- incoming_connection_ids = incoming_connection_ids + connection_ids
395
- for connection_id in incoming_connection_ids:
396
- connection = self.connections.connections[connection_id]
397
- if connection.source_node not in connected_nodes:
398
- connected_nodes.append(connection.source_node)
399
- # Return all connected nodes. No duplicates
400
- return connected_nodes
87
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
88
+
89
+ return GriptapeNodes.FlowManager().get_connections_on_node(self, node)
401
90
 
402
91
  def get_all_connected_nodes(self, node: BaseNode) -> list[BaseNode]:
403
- discovered = {}
404
- processed = {}
405
- queue = Queue()
406
- queue.put(node)
407
- discovered[node] = True
408
- while not queue.empty():
409
- curr_node = queue.get()
410
- processed[curr_node] = True
411
- next_nodes = self.get_connections_on_node(curr_node)
412
- if next_nodes:
413
- for next_node in next_nodes:
414
- if next_node not in discovered:
415
- discovered[next_node] = True
416
- queue.put(next_node)
417
- return list(processed.keys())
92
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
93
+
94
+ return GriptapeNodes.FlowManager().get_all_connected_nodes(self, node)
418
95
 
419
96
  def get_node_dependencies(self, node: BaseNode) -> list[BaseNode]:
420
97
  """Get all upstream nodes that the given node depends on.
@@ -428,18 +105,41 @@ class ControlFlow:
428
105
  Returns:
429
106
  list[BaseNode]: A list of all nodes that the given node depends on, including the node itself (as the first element)
430
107
  """
431
- node_list = [node]
432
- node_queue = Queue()
433
- node_queue.put(node)
434
- while not node_queue.empty():
435
- curr_node = node_queue.get()
436
- input_connections = self.get_connected_input_from_node(curr_node)
437
- if input_connections:
438
- for input_node, input_parameter in input_connections:
439
- if (
440
- ParameterTypeBuiltin.CONTROL_TYPE.value != input_parameter.output_type
441
- and input_node not in node_list
442
- ):
443
- node_list.append(input_node)
444
- node_queue.put(input_node)
445
- return node_list
108
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
109
+
110
+ return GriptapeNodes.FlowManager().get_node_dependencies(self, node)
111
+
112
+ def get_connected_output_parameters(self, node: BaseNode, param: Parameter) -> list[tuple[BaseNode, Parameter]]:
113
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
114
+
115
+ return GriptapeNodes.FlowManager().get_connected_output_parameters(node, param)
116
+
117
+ def get_connected_input_parameters(self, node: BaseNode, param: Parameter) -> list[tuple[BaseNode, Parameter]]:
118
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
119
+
120
+ return GriptapeNodes.FlowManager().get_connected_input_parameters(self, node, param)
121
+
122
+ def get_connected_output_from_node(self, node: BaseNode) -> list[tuple[BaseNode, Parameter]]:
123
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
124
+
125
+ return GriptapeNodes.FlowManager().get_connected_output_from_node(self, node)
126
+
127
+ def get_connected_input_from_node(self, node: BaseNode) -> list[tuple[BaseNode, Parameter]]:
128
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
129
+
130
+ return GriptapeNodes.FlowManager().get_connected_input_from_node(self, node)
131
+
132
+ def get_start_node_queue(self) -> Queue | None:
133
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
134
+
135
+ return GriptapeNodes.FlowManager().get_start_node_queue()
136
+
137
+ def get_start_node_from_node(self, node: BaseNode) -> BaseNode | None:
138
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
139
+
140
+ return GriptapeNodes.FlowManager().get_start_node_from_node(self, node)
141
+
142
+ def get_prev_node(self, node: BaseNode) -> BaseNode | None:
143
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
144
+
145
+ return GriptapeNodes.FlowManager().get_prev_node(self, node)
@@ -32,20 +32,21 @@ class ControlFlowContext:
32
32
  selected_output: Parameter | None
33
33
  paused: bool = False
34
34
 
35
- def __init__(self, flow: ControlFlow) -> None:
36
- self.resolution_machine = NodeResolutionMachine(flow)
37
- self.flow = flow
35
+ def __init__(self) -> None:
36
+ self.resolution_machine = NodeResolutionMachine()
38
37
  self.current_node = None
39
38
 
40
39
  def get_next_node(self, output_parameter: Parameter) -> BaseNode | None:
41
40
  if self.current_node is not None:
42
- node = self.flow.connections.get_connected_node(self.current_node, output_parameter)
41
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
42
+
43
+ node = GriptapeNodes.FlowManager().get_connections().get_connected_node(self.current_node, output_parameter)
43
44
  if node is not None:
44
45
  node, _ = node
45
- # Continue Execution to the next node that needs to be executed.
46
- elif not self.flow.flow_queue.empty():
47
- node = self.flow.flow_queue.get()
48
- self.flow.flow_queue.task_done()
46
+ # Continue Execution to the next node that needs to be executed using global execution queue
47
+ else:
48
+ # Get the next node in the execution queue, or None if queue is empty
49
+ node = GriptapeNodes.FlowManager().get_next_node_from_execution_queue()
49
50
  return node
50
51
  return None
51
52
 
@@ -124,9 +125,11 @@ class NextNodeState(State):
124
125
  )
125
126
  )
126
127
  )
127
- elif not context.flow.flow_queue.empty():
128
- next_node = context.flow.flow_queue.get()
129
- context.flow.flow_queue.task_done()
128
+ else:
129
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
130
+
131
+ # Get the next node in the execution queue, or None if queue is empty
132
+ next_node = GriptapeNodes.FlowManager().get_next_node_from_execution_queue()
130
133
  # The parameter that will be evaluated next
131
134
  if next_node is None:
132
135
  # If no node attached
@@ -168,8 +171,8 @@ class CompleteState(State):
168
171
 
169
172
  # MACHINE TIME!!!
170
173
  class ControlFlowMachine(FSM[ControlFlowContext]):
171
- def __init__(self, flow: ControlFlow) -> None:
172
- context = ControlFlowContext(flow)
174
+ def __init__(self) -> None:
175
+ context = ControlFlowContext()
173
176
  super().__init__(context)
174
177
 
175
178
  def start_flow(self, start_node: BaseNode, debug_mode: bool = False) -> None: # noqa: FBT001, FBT002
@@ -4,7 +4,7 @@ import logging
4
4
  from collections.abc import Generator
5
5
  from concurrent.futures import Future, ThreadPoolExecutor
6
6
  from dataclasses import dataclass
7
- from typing import TYPE_CHECKING, Any
7
+ from typing import Any
8
8
 
9
9
  from griptape.events import EventBus
10
10
  from griptape.utils import with_contextvars
@@ -31,10 +31,6 @@ from griptape_nodes.retained_mode.events.parameter_events import (
31
31
  SetParameterValueRequest,
32
32
  )
33
33
 
34
- if TYPE_CHECKING:
35
- from griptape_nodes.exe_types.flow import ControlFlow
36
-
37
-
38
34
  logger = logging.getLogger("griptape_nodes")
39
35
 
40
36
 
@@ -47,12 +43,10 @@ class Focus:
47
43
 
48
44
  # This is on a per-node basis
49
45
  class ResolutionContext:
50
- flow: ControlFlow
51
46
  focus_stack: list[Focus]
52
47
  paused: bool
53
48
 
54
- def __init__(self, flow: ControlFlow) -> None:
55
- self.flow = flow
49
+ def __init__(self) -> None:
56
50
  self.focus_stack = []
57
51
  self.paused = False
58
52
 
@@ -90,7 +84,9 @@ class InitializeSpotlightState(State):
90
84
  if current_node.state == NodeResolutionState.UNRESOLVED:
91
85
  # Mark all future nodes unresolved.
92
86
  # TODO: https://github.com/griptape-ai/griptape-nodes/issues/862
93
- context.flow.connections.unresolve_future_nodes(current_node)
87
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
88
+
89
+ GriptapeNodes.FlowManager().get_connections().unresolve_future_nodes(current_node)
94
90
  current_node.initialize_spotlight()
95
91
  # Set node to resolving - we are now resolving this node.
96
92
  current_node.state = NodeResolutionState.RESOLVING
@@ -132,7 +128,9 @@ class EvaluateParameterState(State):
132
128
  def on_update(context: ResolutionContext) -> type[State] | None:
133
129
  current_node = context.focus_stack[-1].node
134
130
  current_parameter = current_node.get_current_parameter()
135
- connections = context.flow.connections
131
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
132
+
133
+ connections = GriptapeNodes.FlowManager().get_connections()
136
134
  if current_parameter is None:
137
135
  msg = "No current parameter set."
138
136
  raise ValueError(msg)
@@ -270,7 +268,10 @@ class ExecuteNodeState(State):
270
268
  )
271
269
  current_focus.process_generator = None
272
270
  current_focus.scheduled_value = None
273
- context.flow.cancel_flow_run()
271
+
272
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
273
+
274
+ GriptapeNodes.FlowManager().cancel_flow_run()
274
275
 
275
276
  EventBus.publish_event(
276
277
  ExecutionGriptapeNodeEvent(
@@ -320,7 +321,7 @@ class ExecuteNodeState(State):
320
321
  )
321
322
  )
322
323
  # Pass the value through to the new nodes.
323
- conn_output_nodes = context.flow.get_connected_output_parameters(current_node, parameter)
324
+ conn_output_nodes = GriptapeNodes.FlowManager().get_connected_output_parameters(current_node, parameter)
324
325
  for target_node, target_parameter in conn_output_nodes:
325
326
  GriptapeNodes.get_instance().handle_request(
326
327
  SetParameterValueRequest(
@@ -416,6 +417,7 @@ class ExecuteNodeState(State):
416
417
 
417
418
  # Once we've passed on the scheduled value, we should clear it out just in case
418
419
  current_focus.scheduled_value = None
420
+
419
421
  future = ExecuteNodeState.executor.submit(with_contextvars(func))
420
422
  future.add_done_callback(with_contextvars(on_future_done))
421
423
  except StopIteration:
@@ -445,8 +447,8 @@ class CompleteState(State):
445
447
  class NodeResolutionMachine(FSM[ResolutionContext]):
446
448
  """State machine for resolving node dependencies."""
447
449
 
448
- def __init__(self, flow: ControlFlow) -> None:
449
- resolution_context = ResolutionContext(flow) # Gets the flow
450
+ def __init__(self) -> None:
451
+ resolution_context = ResolutionContext()
450
452
  super().__init__(resolution_context)
451
453
 
452
454
  def resolve_node(self, node: BaseNode) -> None:
@@ -13,19 +13,19 @@ from griptape_nodes.utils.metaclasses import SingletonMeta
13
13
 
14
14
 
15
15
  class WorkflowMetadata(BaseModel):
16
- LATEST_SCHEMA_VERSION: ClassVar[str] = "0.4.0"
16
+ LATEST_SCHEMA_VERSION: ClassVar[str] = "0.6.0"
17
17
 
18
18
  name: str
19
19
  schema_version: str
20
20
  engine_version_created_with: str
21
21
  node_libraries_referenced: list[LibraryNameAndVersion]
22
+ workflows_referenced: list[str] | None = None
22
23
  description: str | None = None
23
24
  image: str | None = None
24
25
  is_griptape_provided: bool | None = False
25
26
  is_template: bool | None = False
26
27
  creation_date: datetime | None = Field(default=None)
27
28
  last_modified_date: datetime | None = Field(default=None)
28
- published_workflow_id: str | None = Field(default=None)
29
29
 
30
30
 
31
31
  class WorkflowRegistry(metaclass=SingletonMeta):