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.
- griptape_nodes/app/__init__.py +1 -5
- griptape_nodes/app/app.py +12 -9
- griptape_nodes/app/app_sessions.py +132 -36
- griptape_nodes/app/watch.py +3 -1
- griptape_nodes/drivers/storage/local_storage_driver.py +3 -2
- griptape_nodes/exe_types/flow.py +68 -368
- griptape_nodes/machines/control_flow.py +16 -13
- griptape_nodes/machines/node_resolution.py +16 -14
- griptape_nodes/node_library/workflow_registry.py +2 -2
- griptape_nodes/retained_mode/events/agent_events.py +70 -8
- griptape_nodes/retained_mode/events/app_events.py +132 -11
- griptape_nodes/retained_mode/events/arbitrary_python_events.py +23 -0
- griptape_nodes/retained_mode/events/base_events.py +7 -25
- griptape_nodes/retained_mode/events/config_events.py +87 -11
- griptape_nodes/retained_mode/events/connection_events.py +56 -5
- griptape_nodes/retained_mode/events/context_events.py +27 -4
- griptape_nodes/retained_mode/events/execution_events.py +99 -14
- griptape_nodes/retained_mode/events/flow_events.py +165 -7
- griptape_nodes/retained_mode/events/library_events.py +193 -15
- griptape_nodes/retained_mode/events/logger_events.py +11 -0
- griptape_nodes/retained_mode/events/node_events.py +242 -22
- griptape_nodes/retained_mode/events/object_events.py +40 -4
- griptape_nodes/retained_mode/events/os_events.py +13 -2
- griptape_nodes/retained_mode/events/parameter_events.py +212 -8
- griptape_nodes/retained_mode/events/secrets_events.py +59 -7
- griptape_nodes/retained_mode/events/static_file_events.py +57 -4
- griptape_nodes/retained_mode/events/validation_events.py +39 -4
- griptape_nodes/retained_mode/events/workflow_events.py +188 -17
- griptape_nodes/retained_mode/griptape_nodes.py +46 -323
- griptape_nodes/retained_mode/managers/agent_manager.py +1 -1
- griptape_nodes/retained_mode/managers/engine_identity_manager.py +146 -0
- griptape_nodes/retained_mode/managers/event_manager.py +14 -2
- griptape_nodes/retained_mode/managers/flow_manager.py +749 -64
- griptape_nodes/retained_mode/managers/library_manager.py +112 -2
- griptape_nodes/retained_mode/managers/node_manager.py +34 -31
- griptape_nodes/retained_mode/managers/object_manager.py +11 -3
- griptape_nodes/retained_mode/managers/os_manager.py +70 -1
- griptape_nodes/retained_mode/managers/secrets_manager.py +4 -0
- griptape_nodes/retained_mode/managers/session_manager.py +328 -0
- griptape_nodes/retained_mode/managers/settings.py +7 -0
- griptape_nodes/retained_mode/managers/workflow_manager.py +523 -454
- griptape_nodes/retained_mode/retained_mode.py +44 -0
- griptape_nodes/retained_mode/utils/engine_identity.py +141 -27
- {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.42.0.dist-info}/METADATA +2 -2
- {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.42.0.dist-info}/RECORD +48 -47
- griptape_nodes/retained_mode/utils/session_persistence.py +0 -105
- {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.42.0.dist-info}/WHEEL +0 -0
- {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.42.0.dist-info}/entry_points.txt +0 -0
- {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.42.0.dist-info}/licenses/LICENSE +0 -0
griptape_nodes/exe_types/flow.py
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
236
|
-
|
|
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
|
-
|
|
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
|
-
|
|
378
|
-
|
|
379
|
-
|
|
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
|
-
|
|
404
|
-
|
|
405
|
-
|
|
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
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
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
|
|
36
|
-
self.resolution_machine = NodeResolutionMachine(
|
|
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
|
-
|
|
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
|
-
|
|
47
|
-
node
|
|
48
|
-
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
|
172
|
-
context = ControlFlowContext(
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
449
|
-
resolution_context = ResolutionContext(
|
|
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.
|
|
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):
|