griptape-nodes 0.55.1__py3-none-any.whl → 0.56.1__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/app.py +10 -15
- griptape_nodes/app/watch.py +35 -67
- griptape_nodes/bootstrap/utils/__init__.py +1 -0
- griptape_nodes/bootstrap/utils/python_subprocess_executor.py +122 -0
- griptape_nodes/bootstrap/workflow_executors/local_session_workflow_executor.py +418 -0
- griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +37 -8
- griptape_nodes/bootstrap/workflow_executors/subprocess_workflow_executor.py +326 -0
- griptape_nodes/bootstrap/workflow_executors/utils/__init__.py +1 -0
- griptape_nodes/bootstrap/workflow_executors/utils/subprocess_script.py +51 -0
- griptape_nodes/bootstrap/workflow_publishers/__init__.py +1 -0
- griptape_nodes/bootstrap/workflow_publishers/local_workflow_publisher.py +43 -0
- griptape_nodes/bootstrap/workflow_publishers/subprocess_workflow_publisher.py +84 -0
- griptape_nodes/bootstrap/workflow_publishers/utils/__init__.py +1 -0
- griptape_nodes/bootstrap/workflow_publishers/utils/subprocess_script.py +54 -0
- griptape_nodes/cli/commands/engine.py +4 -15
- griptape_nodes/cli/commands/init.py +88 -0
- griptape_nodes/cli/commands/models.py +2 -0
- griptape_nodes/cli/main.py +6 -1
- griptape_nodes/cli/shared.py +1 -0
- griptape_nodes/exe_types/core_types.py +130 -0
- griptape_nodes/exe_types/node_types.py +125 -13
- griptape_nodes/machines/control_flow.py +10 -0
- griptape_nodes/machines/dag_builder.py +21 -2
- griptape_nodes/machines/parallel_resolution.py +25 -10
- griptape_nodes/node_library/workflow_registry.py +73 -3
- griptape_nodes/retained_mode/events/agent_events.py +2 -0
- griptape_nodes/retained_mode/events/base_events.py +18 -17
- griptape_nodes/retained_mode/events/execution_events.py +15 -3
- griptape_nodes/retained_mode/events/flow_events.py +63 -7
- griptape_nodes/retained_mode/events/mcp_events.py +363 -0
- griptape_nodes/retained_mode/events/node_events.py +3 -4
- griptape_nodes/retained_mode/events/resource_events.py +290 -0
- griptape_nodes/retained_mode/events/workflow_events.py +57 -2
- griptape_nodes/retained_mode/griptape_nodes.py +17 -1
- griptape_nodes/retained_mode/managers/agent_manager.py +67 -4
- griptape_nodes/retained_mode/managers/event_manager.py +31 -13
- griptape_nodes/retained_mode/managers/flow_manager.py +731 -33
- griptape_nodes/retained_mode/managers/library_manager.py +15 -23
- griptape_nodes/retained_mode/managers/mcp_manager.py +364 -0
- griptape_nodes/retained_mode/managers/model_manager.py +184 -83
- griptape_nodes/retained_mode/managers/node_manager.py +15 -4
- griptape_nodes/retained_mode/managers/os_manager.py +118 -1
- griptape_nodes/retained_mode/managers/resource_components/__init__.py +1 -0
- griptape_nodes/retained_mode/managers/resource_components/capability_field.py +41 -0
- griptape_nodes/retained_mode/managers/resource_components/comparator.py +18 -0
- griptape_nodes/retained_mode/managers/resource_components/resource_instance.py +236 -0
- griptape_nodes/retained_mode/managers/resource_components/resource_type.py +79 -0
- griptape_nodes/retained_mode/managers/resource_manager.py +306 -0
- griptape_nodes/retained_mode/managers/resource_types/__init__.py +1 -0
- griptape_nodes/retained_mode/managers/resource_types/cpu_resource.py +108 -0
- griptape_nodes/retained_mode/managers/resource_types/os_resource.py +87 -0
- griptape_nodes/retained_mode/managers/settings.py +45 -0
- griptape_nodes/retained_mode/managers/sync_manager.py +10 -3
- griptape_nodes/retained_mode/managers/workflow_manager.py +447 -263
- griptape_nodes/traits/multi_options.py +5 -1
- griptape_nodes/traits/options.py +10 -2
- {griptape_nodes-0.55.1.dist-info → griptape_nodes-0.56.1.dist-info}/METADATA +2 -2
- {griptape_nodes-0.55.1.dist-info → griptape_nodes-0.56.1.dist-info}/RECORD +60 -37
- {griptape_nodes-0.55.1.dist-info → griptape_nodes-0.56.1.dist-info}/WHEEL +1 -1
- {griptape_nodes-0.55.1.dist-info → griptape_nodes-0.56.1.dist-info}/entry_points.txt +0 -0
|
@@ -3,7 +3,8 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
from enum import StrEnum
|
|
5
5
|
from queue import Queue
|
|
6
|
-
from typing import TYPE_CHECKING, NamedTuple, cast
|
|
6
|
+
from typing import TYPE_CHECKING, Any, NamedTuple, cast
|
|
7
|
+
from uuid import uuid4
|
|
7
8
|
|
|
8
9
|
from griptape_nodes.exe_types.connections import Connections
|
|
9
10
|
from griptape_nodes.exe_types.core_types import (
|
|
@@ -14,11 +15,20 @@ from griptape_nodes.exe_types.core_types import (
|
|
|
14
15
|
ParameterTypeBuiltin,
|
|
15
16
|
)
|
|
16
17
|
from griptape_nodes.exe_types.flow import ControlFlow
|
|
17
|
-
from griptape_nodes.exe_types.node_types import
|
|
18
|
+
from griptape_nodes.exe_types.node_types import (
|
|
19
|
+
LOCAL_EXECUTION,
|
|
20
|
+
BaseNode,
|
|
21
|
+
ErrorProxyNode,
|
|
22
|
+
NodeDependencies,
|
|
23
|
+
NodeResolutionState,
|
|
24
|
+
StartLoopNode,
|
|
25
|
+
StartNode,
|
|
26
|
+
)
|
|
18
27
|
from griptape_nodes.machines.control_flow import CompleteState, ControlFlowMachine
|
|
19
28
|
from griptape_nodes.machines.dag_builder import DagBuilder
|
|
20
29
|
from griptape_nodes.machines.parallel_resolution import ParallelResolutionMachine
|
|
21
30
|
from griptape_nodes.machines.sequential_resolution import SequentialResolutionMachine
|
|
31
|
+
from griptape_nodes.node_library.library_registry import LibraryNameAndVersion, LibraryRegistry
|
|
22
32
|
from griptape_nodes.retained_mode.events.base_events import (
|
|
23
33
|
ExecutionEvent,
|
|
24
34
|
ExecutionGriptapeNodeEvent,
|
|
@@ -33,6 +43,10 @@ from griptape_nodes.retained_mode.events.connection_events import (
|
|
|
33
43
|
DeleteConnectionRequest,
|
|
34
44
|
DeleteConnectionResultFailure,
|
|
35
45
|
DeleteConnectionResultSuccess,
|
|
46
|
+
IncomingConnection,
|
|
47
|
+
ListConnectionsForNodeRequest,
|
|
48
|
+
ListConnectionsForNodeResultSuccess,
|
|
49
|
+
OutgoingConnection,
|
|
36
50
|
)
|
|
37
51
|
from griptape_nodes.retained_mode.events.execution_events import (
|
|
38
52
|
CancelFlowRequest,
|
|
@@ -48,6 +62,7 @@ from griptape_nodes.retained_mode.events.execution_events import (
|
|
|
48
62
|
GetIsFlowRunningRequest,
|
|
49
63
|
GetIsFlowRunningResultFailure,
|
|
50
64
|
GetIsFlowRunningResultSuccess,
|
|
65
|
+
InvolvedNodesEvent,
|
|
51
66
|
SingleExecutionStepRequest,
|
|
52
67
|
SingleExecutionStepResultFailure,
|
|
53
68
|
SingleExecutionStepResultSuccess,
|
|
@@ -88,6 +103,9 @@ from griptape_nodes.retained_mode.events.flow_events import (
|
|
|
88
103
|
ListNodesInFlowRequest,
|
|
89
104
|
ListNodesInFlowResultFailure,
|
|
90
105
|
ListNodesInFlowResultSuccess,
|
|
106
|
+
PackageNodeAsSerializedFlowRequest,
|
|
107
|
+
PackageNodeAsSerializedFlowResultFailure,
|
|
108
|
+
PackageNodeAsSerializedFlowResultSuccess,
|
|
91
109
|
SerializedFlowCommands,
|
|
92
110
|
SerializeFlowToCommandsRequest,
|
|
93
111
|
SerializeFlowToCommandsResultFailure,
|
|
@@ -97,14 +115,18 @@ from griptape_nodes.retained_mode.events.flow_events import (
|
|
|
97
115
|
SetFlowMetadataResultSuccess,
|
|
98
116
|
)
|
|
99
117
|
from griptape_nodes.retained_mode.events.node_events import (
|
|
118
|
+
CreateNodeRequest,
|
|
100
119
|
DeleteNodeRequest,
|
|
101
120
|
DeleteNodeResultFailure,
|
|
102
121
|
DeserializeNodeFromCommandsRequest,
|
|
122
|
+
SerializedNodeCommands,
|
|
103
123
|
SerializedParameterValueTracker,
|
|
104
124
|
SerializeNodeToCommandsRequest,
|
|
105
125
|
SerializeNodeToCommandsResultSuccess,
|
|
106
126
|
)
|
|
107
127
|
from griptape_nodes.retained_mode.events.parameter_events import (
|
|
128
|
+
AddParameterToNodeRequest,
|
|
129
|
+
AlterParameterDetailsRequest,
|
|
108
130
|
SetParameterValueRequest,
|
|
109
131
|
)
|
|
110
132
|
from griptape_nodes.retained_mode.events.validation_events import (
|
|
@@ -121,6 +143,7 @@ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
|
121
143
|
if TYPE_CHECKING:
|
|
122
144
|
from griptape_nodes.retained_mode.events.base_events import ResultPayload
|
|
123
145
|
from griptape_nodes.retained_mode.managers.event_manager import EventManager
|
|
146
|
+
from griptape_nodes.retained_mode.managers.workflow_manager import WorkflowShapeNodes
|
|
124
147
|
|
|
125
148
|
logger = logging.getLogger("griptape_nodes")
|
|
126
149
|
|
|
@@ -138,6 +161,39 @@ class QueueItem(NamedTuple):
|
|
|
138
161
|
dag_execution_type: DagExecutionType
|
|
139
162
|
|
|
140
163
|
|
|
164
|
+
class ConnectionAnalysis(NamedTuple):
|
|
165
|
+
"""Analysis of connections separated by type (data vs control) when packaging nodes."""
|
|
166
|
+
|
|
167
|
+
incoming_data_connections: list[IncomingConnection]
|
|
168
|
+
incoming_control_connections: list[IncomingConnection]
|
|
169
|
+
outgoing_data_connections: list[OutgoingConnection]
|
|
170
|
+
outgoing_control_connections: list[OutgoingConnection]
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class PackageNodeInfo(NamedTuple):
|
|
174
|
+
"""Information about the node being packaged."""
|
|
175
|
+
|
|
176
|
+
package_node: BaseNode
|
|
177
|
+
package_flow_name: str
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class PackagingStartNodeResult(NamedTuple):
|
|
181
|
+
"""Result of creating start node commands and data connections for flow packaging."""
|
|
182
|
+
|
|
183
|
+
start_node_commands: SerializedNodeCommands
|
|
184
|
+
start_to_package_data_connections: list[SerializedFlowCommands.IndirectConnectionSerialization]
|
|
185
|
+
input_shape_data: WorkflowShapeNodes
|
|
186
|
+
start_node_parameter_value_commands: list[SerializedNodeCommands.IndirectSetParameterValueCommand]
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class PackagingEndNodeResult(NamedTuple):
|
|
190
|
+
"""Result of creating end node commands and data connections for flow packaging."""
|
|
191
|
+
|
|
192
|
+
end_node_commands: SerializedNodeCommands
|
|
193
|
+
package_to_end_data_connections: list[SerializedFlowCommands.IndirectConnectionSerialization]
|
|
194
|
+
output_shape_data: WorkflowShapeNodes
|
|
195
|
+
|
|
196
|
+
|
|
141
197
|
class FlowManager:
|
|
142
198
|
_name_to_parent_name: dict[str, str | None]
|
|
143
199
|
_flow_to_referenced_workflow_name: dict[ControlFlow, str]
|
|
@@ -181,6 +237,9 @@ class FlowManager:
|
|
|
181
237
|
event_manager.assign_manager_to_request_type(
|
|
182
238
|
DeserializeFlowFromCommandsRequest, self.on_deserialize_flow_from_commands
|
|
183
239
|
)
|
|
240
|
+
event_manager.assign_manager_to_request_type(
|
|
241
|
+
PackageNodeAsSerializedFlowRequest, self.on_package_node_as_serialized_flow_request
|
|
242
|
+
)
|
|
184
243
|
event_manager.assign_manager_to_request_type(FlushParameterChangesRequest, self.on_flush_request)
|
|
185
244
|
|
|
186
245
|
self._name_to_parent_name = {}
|
|
@@ -1012,6 +1071,600 @@ class FlowManager:
|
|
|
1012
1071
|
result = DeleteConnectionResultSuccess(result_details=details)
|
|
1013
1072
|
return result
|
|
1014
1073
|
|
|
1074
|
+
def on_package_node_as_serialized_flow_request(self, request: PackageNodeAsSerializedFlowRequest) -> ResultPayload: # noqa: PLR0911
|
|
1075
|
+
"""Handle request to package a node as a serialized flow.
|
|
1076
|
+
|
|
1077
|
+
Creates a self-contained flow with Start node -> Package node -> End node structure,
|
|
1078
|
+
where artificial start/end nodes match the package node's connections.
|
|
1079
|
+
"""
|
|
1080
|
+
# Step 1: Validate package node and flow
|
|
1081
|
+
package_node_info = self._validate_package_node_and_flow(request=request)
|
|
1082
|
+
if isinstance(package_node_info, PackageNodeAsSerializedFlowResultFailure):
|
|
1083
|
+
return package_node_info
|
|
1084
|
+
|
|
1085
|
+
# Step 2: Validate library and get version
|
|
1086
|
+
library_version = self._validate_and_get_library_info(request=request)
|
|
1087
|
+
if isinstance(library_version, PackageNodeAsSerializedFlowResultFailure):
|
|
1088
|
+
return library_version
|
|
1089
|
+
|
|
1090
|
+
# Step 3: Analyze package node connections
|
|
1091
|
+
connection_analysis = self._analyze_package_node_connections(
|
|
1092
|
+
package_node=package_node_info.package_node,
|
|
1093
|
+
node_name=package_node_info.package_node.name,
|
|
1094
|
+
)
|
|
1095
|
+
if isinstance(connection_analysis, PackageNodeAsSerializedFlowResultFailure):
|
|
1096
|
+
return connection_analysis
|
|
1097
|
+
|
|
1098
|
+
# Step 4: Serialize the package node
|
|
1099
|
+
unique_parameter_uuid_to_values = {}
|
|
1100
|
+
serialized_parameter_value_tracker = SerializedParameterValueTracker()
|
|
1101
|
+
package_node = package_node_info.package_node
|
|
1102
|
+
# Set to LOCAL_EXECUTION before packaging to prevent recursive loop.
|
|
1103
|
+
previous_value = package_node.get_parameter_value("execution_environment")
|
|
1104
|
+
package_node.set_parameter_value("execution_environment", LOCAL_EXECUTION)
|
|
1105
|
+
serialized_package_result = self._serialize_package_node(
|
|
1106
|
+
node_name=package_node_info.package_node.name,
|
|
1107
|
+
package_node=package_node_info.package_node,
|
|
1108
|
+
unique_parameter_uuid_to_values=unique_parameter_uuid_to_values,
|
|
1109
|
+
serialized_parameter_value_tracker=serialized_parameter_value_tracker,
|
|
1110
|
+
)
|
|
1111
|
+
# Now that we've serialized the value as LOCAL_EXECUTION, we need to restore it to whatever it was before
|
|
1112
|
+
package_node.set_parameter_value("execution_environment", previous_value)
|
|
1113
|
+
if isinstance(serialized_package_result, PackageNodeAsSerializedFlowResultFailure):
|
|
1114
|
+
return serialized_package_result
|
|
1115
|
+
# Step 5: Create start node commands and data connections
|
|
1116
|
+
start_node_result = self._create_start_node_commands(
|
|
1117
|
+
request=request,
|
|
1118
|
+
incoming_data_connections=connection_analysis.incoming_data_connections,
|
|
1119
|
+
package_node=package_node_info.package_node,
|
|
1120
|
+
package_node_uuid=serialized_package_result.serialized_node_commands.node_uuid,
|
|
1121
|
+
library_version=library_version,
|
|
1122
|
+
unique_parameter_uuid_to_values=unique_parameter_uuid_to_values,
|
|
1123
|
+
serialized_parameter_value_tracker=serialized_parameter_value_tracker,
|
|
1124
|
+
)
|
|
1125
|
+
if isinstance(start_node_result, PackageNodeAsSerializedFlowResultFailure):
|
|
1126
|
+
return start_node_result
|
|
1127
|
+
|
|
1128
|
+
# Step 6: Create end node commands and data connections
|
|
1129
|
+
end_node_result = self._create_end_node_commands(
|
|
1130
|
+
request=request,
|
|
1131
|
+
package_node=package_node_info.package_node,
|
|
1132
|
+
package_node_uuid=serialized_package_result.serialized_node_commands.node_uuid,
|
|
1133
|
+
library_version=library_version,
|
|
1134
|
+
)
|
|
1135
|
+
if isinstance(end_node_result, PackageNodeAsSerializedFlowResultFailure):
|
|
1136
|
+
return end_node_result
|
|
1137
|
+
|
|
1138
|
+
# Step 7a: Create start node control flow connection
|
|
1139
|
+
start_control_connection_result = self._create_start_node_control_connection(
|
|
1140
|
+
entry_control_parameter_name=request.entry_control_parameter_name,
|
|
1141
|
+
start_node_uuid=start_node_result.start_node_commands.node_uuid,
|
|
1142
|
+
package_node_uuid=serialized_package_result.serialized_node_commands.node_uuid,
|
|
1143
|
+
package_node=package_node_info.package_node,
|
|
1144
|
+
)
|
|
1145
|
+
if isinstance(start_control_connection_result, PackageNodeAsSerializedFlowResultFailure):
|
|
1146
|
+
return start_control_connection_result
|
|
1147
|
+
|
|
1148
|
+
start_control_connections = [start_control_connection_result]
|
|
1149
|
+
|
|
1150
|
+
# Use only start control connections for now (end node control connections not implemented yet)
|
|
1151
|
+
control_flow_connections = start_control_connections
|
|
1152
|
+
|
|
1153
|
+
# Step 8: Assemble the complete serialized flow
|
|
1154
|
+
packaged_flow = self._assemble_serialized_flow(
|
|
1155
|
+
serialized_package_result=serialized_package_result,
|
|
1156
|
+
start_node_result=start_node_result,
|
|
1157
|
+
end_node_result=end_node_result,
|
|
1158
|
+
control_flow_connections=control_flow_connections,
|
|
1159
|
+
unique_parameter_uuid_to_values=unique_parameter_uuid_to_values,
|
|
1160
|
+
library_version=library_version,
|
|
1161
|
+
request=request,
|
|
1162
|
+
)
|
|
1163
|
+
|
|
1164
|
+
# Step 9: Build WorkflowShape from collected parameter shape data
|
|
1165
|
+
workflow_shape = GriptapeNodes.WorkflowManager().build_workflow_shape_from_parameter_info(
|
|
1166
|
+
input_node_params=start_node_result.input_shape_data, output_node_params=end_node_result.output_shape_data
|
|
1167
|
+
)
|
|
1168
|
+
# Return success result
|
|
1169
|
+
return PackageNodeAsSerializedFlowResultSuccess(
|
|
1170
|
+
result_details=f'Successfully packaged node "{package_node_info.package_node.name}" from flow "{package_node_info.package_flow_name}" as serialized flow with start node type "{request.start_node_type}" and end node type "{request.end_node_type}" from library "{request.start_end_specific_library_name}".',
|
|
1171
|
+
serialized_flow_commands=packaged_flow,
|
|
1172
|
+
workflow_shape=workflow_shape,
|
|
1173
|
+
)
|
|
1174
|
+
|
|
1175
|
+
def _validate_package_node_and_flow( # noqa: PLR0911
|
|
1176
|
+
self, request: PackageNodeAsSerializedFlowRequest
|
|
1177
|
+
) -> PackageNodeInfo | PackageNodeAsSerializedFlowResultFailure:
|
|
1178
|
+
"""Validate and retrieve the package node and its parent flow."""
|
|
1179
|
+
node_name = request.node_name
|
|
1180
|
+
package_node = None
|
|
1181
|
+
|
|
1182
|
+
if node_name is None:
|
|
1183
|
+
# First check if we have a current node
|
|
1184
|
+
if not GriptapeNodes.ContextManager().has_current_node():
|
|
1185
|
+
details = (
|
|
1186
|
+
"Attempted to package node from Current Context. Failed because the Current Context was empty."
|
|
1187
|
+
)
|
|
1188
|
+
return PackageNodeAsSerializedFlowResultFailure(result_details=details)
|
|
1189
|
+
|
|
1190
|
+
# Get the current node from context
|
|
1191
|
+
package_node = GriptapeNodes.ContextManager().get_current_node()
|
|
1192
|
+
node_name = package_node.name
|
|
1193
|
+
|
|
1194
|
+
if package_node is None:
|
|
1195
|
+
try:
|
|
1196
|
+
package_node = GriptapeNodes.NodeManager().get_node_by_name(node_name)
|
|
1197
|
+
except ValueError as err:
|
|
1198
|
+
details = f"Attempted to package node '{node_name}'. Failed because node does not exist. Error: {err}."
|
|
1199
|
+
return PackageNodeAsSerializedFlowResultFailure(result_details=details)
|
|
1200
|
+
|
|
1201
|
+
# Get the flow containing this node using the same pattern
|
|
1202
|
+
package_flow_name = GriptapeNodes.NodeManager().get_node_parent_flow_by_name(node_name)
|
|
1203
|
+
if package_flow_name is None:
|
|
1204
|
+
details = f"Attempted to package node '{node_name}'. Failed because node is not assigned to any flow."
|
|
1205
|
+
return PackageNodeAsSerializedFlowResultFailure(result_details=details)
|
|
1206
|
+
|
|
1207
|
+
try:
|
|
1208
|
+
self.get_flow_by_name(flow_name=package_flow_name)
|
|
1209
|
+
except KeyError as err:
|
|
1210
|
+
details = f"Attempted to package node '{node_name}' from flow '{package_flow_name}'. Failed because flow does not exist. Error: {err}."
|
|
1211
|
+
return PackageNodeAsSerializedFlowResultFailure(result_details=details)
|
|
1212
|
+
|
|
1213
|
+
# Validate entry control parameter if specified
|
|
1214
|
+
if request.entry_control_parameter_name is not None:
|
|
1215
|
+
entry_param = package_node.get_parameter_by_name(request.entry_control_parameter_name)
|
|
1216
|
+
if entry_param is None:
|
|
1217
|
+
details = f"Attempted to package node '{node_name}' with entry control parameter '{request.entry_control_parameter_name}'. Failed because the parameter does not exist on the node."
|
|
1218
|
+
return PackageNodeAsSerializedFlowResultFailure(result_details=details)
|
|
1219
|
+
|
|
1220
|
+
# Verify it's actually a control parameter
|
|
1221
|
+
if ParameterTypeBuiltin.CONTROL_TYPE.value not in entry_param.input_types:
|
|
1222
|
+
details = f"Attempted to package node '{node_name}' with entry control parameter '{request.entry_control_parameter_name}'. Failed because the parameter is not a control type parameter."
|
|
1223
|
+
return PackageNodeAsSerializedFlowResultFailure(result_details=details)
|
|
1224
|
+
|
|
1225
|
+
return PackageNodeInfo(package_node=package_node, package_flow_name=package_flow_name)
|
|
1226
|
+
|
|
1227
|
+
def _validate_and_get_library_info(
|
|
1228
|
+
self, request: PackageNodeAsSerializedFlowRequest
|
|
1229
|
+
) -> str | PackageNodeAsSerializedFlowResultFailure:
|
|
1230
|
+
"""Validate start/end node types exist in library and return library version."""
|
|
1231
|
+
# Early validation - ensure both start and end node types exist in the specified library
|
|
1232
|
+
try:
|
|
1233
|
+
start_end_library = LibraryRegistry.get_library_for_node_type(
|
|
1234
|
+
node_type=request.start_node_type, specific_library_name=request.start_end_specific_library_name
|
|
1235
|
+
)
|
|
1236
|
+
except KeyError as err:
|
|
1237
|
+
details = f"Attempted to package node with start node type '{request.start_node_type}' from library '{request.start_end_specific_library_name}'. Failed because start node type was not found in library. Error: {err}."
|
|
1238
|
+
return PackageNodeAsSerializedFlowResultFailure(result_details=details)
|
|
1239
|
+
|
|
1240
|
+
try:
|
|
1241
|
+
LibraryRegistry.get_library_for_node_type(
|
|
1242
|
+
node_type=request.end_node_type, specific_library_name=request.start_end_specific_library_name
|
|
1243
|
+
)
|
|
1244
|
+
except KeyError as err:
|
|
1245
|
+
details = f"Attempted to package node with end node type '{request.end_node_type}' from library '{request.start_end_specific_library_name}'. Failed because end node type was not found in library. Error: {err}."
|
|
1246
|
+
return PackageNodeAsSerializedFlowResultFailure(result_details=details)
|
|
1247
|
+
|
|
1248
|
+
# Get the actual library version
|
|
1249
|
+
start_end_library_metadata = start_end_library.get_metadata()
|
|
1250
|
+
return start_end_library_metadata.library_version
|
|
1251
|
+
|
|
1252
|
+
def _analyze_package_node_connections(
|
|
1253
|
+
self, package_node: BaseNode, node_name: str
|
|
1254
|
+
) -> ConnectionAnalysis | PackageNodeAsSerializedFlowResultFailure:
|
|
1255
|
+
"""Analyze package node connections and separate control from data connections."""
|
|
1256
|
+
# Get connection details using the efficient approach
|
|
1257
|
+
list_connections_request = ListConnectionsForNodeRequest(node_name=node_name)
|
|
1258
|
+
list_connections_result = GriptapeNodes.NodeManager().on_list_connections_for_node_request(
|
|
1259
|
+
list_connections_request
|
|
1260
|
+
)
|
|
1261
|
+
|
|
1262
|
+
if not isinstance(list_connections_result, ListConnectionsForNodeResultSuccess):
|
|
1263
|
+
details = f"Attempted to analyze connections for package node '{node_name}'. Failed because connection listing failed."
|
|
1264
|
+
return PackageNodeAsSerializedFlowResultFailure(result_details=details)
|
|
1265
|
+
|
|
1266
|
+
# Separate control connections from data connections based on package node's parameter types
|
|
1267
|
+
incoming_data_connections = []
|
|
1268
|
+
incoming_control_connections = []
|
|
1269
|
+
for incoming_conn in list_connections_result.incoming_connections:
|
|
1270
|
+
# Get the package node's parameter to check if it's a control type
|
|
1271
|
+
package_param = package_node.get_parameter_by_name(incoming_conn.target_parameter_name)
|
|
1272
|
+
if package_param and ParameterTypeBuiltin.CONTROL_TYPE.value in package_param.input_types:
|
|
1273
|
+
incoming_control_connections.append(incoming_conn)
|
|
1274
|
+
else:
|
|
1275
|
+
incoming_data_connections.append(incoming_conn)
|
|
1276
|
+
|
|
1277
|
+
outgoing_data_connections = []
|
|
1278
|
+
outgoing_control_connections = []
|
|
1279
|
+
for outgoing_conn in list_connections_result.outgoing_connections:
|
|
1280
|
+
# Get the package node's parameter to check if it's a control type
|
|
1281
|
+
package_param = package_node.get_parameter_by_name(outgoing_conn.source_parameter_name)
|
|
1282
|
+
if package_param and ParameterTypeBuiltin.CONTROL_TYPE.value == package_param.output_type:
|
|
1283
|
+
outgoing_control_connections.append(outgoing_conn)
|
|
1284
|
+
else:
|
|
1285
|
+
outgoing_data_connections.append(outgoing_conn)
|
|
1286
|
+
|
|
1287
|
+
return ConnectionAnalysis(
|
|
1288
|
+
incoming_data_connections=incoming_data_connections,
|
|
1289
|
+
incoming_control_connections=incoming_control_connections,
|
|
1290
|
+
outgoing_data_connections=outgoing_data_connections,
|
|
1291
|
+
outgoing_control_connections=outgoing_control_connections,
|
|
1292
|
+
)
|
|
1293
|
+
|
|
1294
|
+
def _serialize_package_node(
|
|
1295
|
+
self,
|
|
1296
|
+
node_name: str,
|
|
1297
|
+
package_node: BaseNode,
|
|
1298
|
+
unique_parameter_uuid_to_values: dict[SerializedNodeCommands.UniqueParameterValueUUID, Any],
|
|
1299
|
+
serialized_parameter_value_tracker: SerializedParameterValueTracker,
|
|
1300
|
+
) -> SerializeNodeToCommandsResultSuccess | PackageNodeAsSerializedFlowResultFailure:
|
|
1301
|
+
"""Serialize the package node to commands, adding OUTPUT mode to PROPERTY-only parameters."""
|
|
1302
|
+
# Use the provided parameter tracking structures
|
|
1303
|
+
|
|
1304
|
+
# Create serialization request for the package node
|
|
1305
|
+
serialize_node_request = SerializeNodeToCommandsRequest(
|
|
1306
|
+
node_name=node_name,
|
|
1307
|
+
unique_parameter_uuid_to_values=unique_parameter_uuid_to_values,
|
|
1308
|
+
serialized_parameter_value_tracker=serialized_parameter_value_tracker,
|
|
1309
|
+
)
|
|
1310
|
+
|
|
1311
|
+
# Execute the serialization
|
|
1312
|
+
serialize_node_result = GriptapeNodes.handle_request(serialize_node_request)
|
|
1313
|
+
if not isinstance(serialize_node_result, SerializeNodeToCommandsResultSuccess):
|
|
1314
|
+
details = f"Attempted to serialize package node '{node_name}'. Failed because node serialization failed."
|
|
1315
|
+
return PackageNodeAsSerializedFlowResultFailure(result_details=details)
|
|
1316
|
+
|
|
1317
|
+
# Add ALTER parameter commands for PROPERTY-only parameters to enable OUTPUT mode
|
|
1318
|
+
# We need these to emit their values back so that the orchestrator/caller
|
|
1319
|
+
# can reconcile the packaged node's values after it is executed.
|
|
1320
|
+
package_alter_parameter_commands = []
|
|
1321
|
+
for package_param in package_node.parameters:
|
|
1322
|
+
has_output_mode = ParameterMode.OUTPUT in package_param.allowed_modes
|
|
1323
|
+
has_property_mode = ParameterMode.PROPERTY in package_param.allowed_modes
|
|
1324
|
+
|
|
1325
|
+
# If has PROPERTY but not OUTPUT, add ALTER command to enable OUTPUT
|
|
1326
|
+
if has_property_mode and not has_output_mode:
|
|
1327
|
+
alter_param_request = AlterParameterDetailsRequest(
|
|
1328
|
+
parameter_name=package_param.name,
|
|
1329
|
+
node_name=package_node.name,
|
|
1330
|
+
mode_allowed_output=True,
|
|
1331
|
+
)
|
|
1332
|
+
package_alter_parameter_commands.append(alter_param_request)
|
|
1333
|
+
|
|
1334
|
+
# If we have alter parameter commands, append them to the existing element_modification_commands
|
|
1335
|
+
if package_alter_parameter_commands:
|
|
1336
|
+
serialize_node_result.serialized_node_commands.element_modification_commands.extend(
|
|
1337
|
+
package_alter_parameter_commands
|
|
1338
|
+
)
|
|
1339
|
+
|
|
1340
|
+
return serialize_node_result
|
|
1341
|
+
|
|
1342
|
+
def _create_start_node_commands( # noqa: PLR0913
|
|
1343
|
+
self,
|
|
1344
|
+
request: PackageNodeAsSerializedFlowRequest,
|
|
1345
|
+
incoming_data_connections: list[IncomingConnection],
|
|
1346
|
+
package_node: BaseNode,
|
|
1347
|
+
package_node_uuid: SerializedNodeCommands.NodeUUID,
|
|
1348
|
+
library_version: str,
|
|
1349
|
+
unique_parameter_uuid_to_values: dict[SerializedNodeCommands.UniqueParameterValueUUID, Any],
|
|
1350
|
+
serialized_parameter_value_tracker: SerializedParameterValueTracker,
|
|
1351
|
+
) -> PackagingStartNodeResult | PackageNodeAsSerializedFlowResultFailure:
|
|
1352
|
+
"""Create start node commands and connections for incoming data connections."""
|
|
1353
|
+
# Generate UUID and name for start node
|
|
1354
|
+
start_node_uuid = SerializedNodeCommands.NodeUUID(str(uuid4()))
|
|
1355
|
+
start_node_name = f"Start_Package_{package_node.name}"
|
|
1356
|
+
|
|
1357
|
+
# Build start node CreateNodeRequest
|
|
1358
|
+
start_create_node_command = CreateNodeRequest(
|
|
1359
|
+
node_type=request.start_node_type,
|
|
1360
|
+
specific_library_name=request.start_end_specific_library_name,
|
|
1361
|
+
node_name=start_node_name,
|
|
1362
|
+
metadata={},
|
|
1363
|
+
initial_setup=True,
|
|
1364
|
+
create_error_proxy_on_failure=False,
|
|
1365
|
+
)
|
|
1366
|
+
|
|
1367
|
+
# Create library details
|
|
1368
|
+
start_node_library_details = LibraryNameAndVersion(
|
|
1369
|
+
library_name=request.start_end_specific_library_name,
|
|
1370
|
+
library_version=library_version,
|
|
1371
|
+
)
|
|
1372
|
+
|
|
1373
|
+
# Create parameter modification commands and connection mappings for the start node based on incoming DATA connections
|
|
1374
|
+
start_node_parameter_commands = []
|
|
1375
|
+
start_to_package_data_connections = []
|
|
1376
|
+
start_node_parameter_value_commands = []
|
|
1377
|
+
input_shape_data: WorkflowShapeNodes = {}
|
|
1378
|
+
|
|
1379
|
+
for incoming_conn in incoming_data_connections:
|
|
1380
|
+
# Parameter name: use the package node's parameter name
|
|
1381
|
+
param_name = incoming_conn.target_parameter_name
|
|
1382
|
+
|
|
1383
|
+
# Get the source node to determine parameter type
|
|
1384
|
+
try:
|
|
1385
|
+
source_node = GriptapeNodes.NodeManager().get_node_by_name(incoming_conn.source_node_name)
|
|
1386
|
+
except ValueError as err:
|
|
1387
|
+
details = f"Attempted to package node '{package_node.name}'. Failed because source node '{incoming_conn.source_node_name}' from incoming connection could not be found. Error: {err}."
|
|
1388
|
+
return PackageNodeAsSerializedFlowResultFailure(result_details=details)
|
|
1389
|
+
|
|
1390
|
+
# Get the source parameter
|
|
1391
|
+
source_param = source_node.get_parameter_by_name(incoming_conn.source_parameter_name)
|
|
1392
|
+
if not source_param:
|
|
1393
|
+
details = f"Attempted to package node '{package_node.name}'. Failed because source parameter '{incoming_conn.source_parameter_name}' on node '{incoming_conn.source_node_name}' from incoming connection could not be found."
|
|
1394
|
+
return PackageNodeAsSerializedFlowResultFailure(result_details=details)
|
|
1395
|
+
|
|
1396
|
+
# Extract parameter shape info for workflow shape (inputs from external sources)
|
|
1397
|
+
param_shape_info = GriptapeNodes.WorkflowManager().extract_parameter_shape_info(
|
|
1398
|
+
source_param, include_control_params=True
|
|
1399
|
+
)
|
|
1400
|
+
if param_shape_info is not None:
|
|
1401
|
+
if start_node_name not in input_shape_data:
|
|
1402
|
+
input_shape_data[start_node_name] = {}
|
|
1403
|
+
input_shape_data[start_node_name][param_name] = param_shape_info
|
|
1404
|
+
|
|
1405
|
+
# Extract parameter value from source node to set on start node
|
|
1406
|
+
param_value_commands = GriptapeNodes.NodeManager().handle_parameter_value_saving(
|
|
1407
|
+
parameter=source_param,
|
|
1408
|
+
node=source_node,
|
|
1409
|
+
unique_parameter_uuid_to_values=unique_parameter_uuid_to_values,
|
|
1410
|
+
serialized_parameter_value_tracker=serialized_parameter_value_tracker,
|
|
1411
|
+
create_node_request=start_create_node_command,
|
|
1412
|
+
)
|
|
1413
|
+
if param_value_commands is not None:
|
|
1414
|
+
# Modify each command to target the start node parameter instead
|
|
1415
|
+
for param_value_command in param_value_commands:
|
|
1416
|
+
param_value_command.set_parameter_value_command.node_name = start_node_name
|
|
1417
|
+
param_value_command.set_parameter_value_command.parameter_name = param_name
|
|
1418
|
+
start_node_parameter_value_commands.append(param_value_command)
|
|
1419
|
+
|
|
1420
|
+
# Create parameter command for start node
|
|
1421
|
+
add_param_request = AddParameterToNodeRequest(
|
|
1422
|
+
node_name=start_node_name,
|
|
1423
|
+
parameter_name=param_name,
|
|
1424
|
+
# Use the source parameter's output_type as the type for our start node parameter
|
|
1425
|
+
# since we want to match what the original connection was providing
|
|
1426
|
+
type=source_param.output_type,
|
|
1427
|
+
default_value=None,
|
|
1428
|
+
tooltip=f"Parameter {param_name} from packaged flow",
|
|
1429
|
+
initial_setup=True,
|
|
1430
|
+
)
|
|
1431
|
+
start_node_parameter_commands.append(add_param_request)
|
|
1432
|
+
|
|
1433
|
+
# Create connection from start node to package node
|
|
1434
|
+
start_to_package_connection = SerializedFlowCommands.IndirectConnectionSerialization(
|
|
1435
|
+
source_node_uuid=start_node_uuid,
|
|
1436
|
+
source_parameter_name=param_name,
|
|
1437
|
+
target_node_uuid=package_node_uuid,
|
|
1438
|
+
target_parameter_name=param_name,
|
|
1439
|
+
)
|
|
1440
|
+
start_to_package_data_connections.append(start_to_package_connection)
|
|
1441
|
+
|
|
1442
|
+
# Build complete SerializedNodeCommands for start node
|
|
1443
|
+
start_node_dependencies = NodeDependencies()
|
|
1444
|
+
start_node_dependencies.libraries.add(start_node_library_details)
|
|
1445
|
+
|
|
1446
|
+
start_node_commands = SerializedNodeCommands(
|
|
1447
|
+
create_node_command=start_create_node_command,
|
|
1448
|
+
element_modification_commands=start_node_parameter_commands,
|
|
1449
|
+
node_dependencies=start_node_dependencies,
|
|
1450
|
+
node_uuid=start_node_uuid,
|
|
1451
|
+
)
|
|
1452
|
+
|
|
1453
|
+
return PackagingStartNodeResult(
|
|
1454
|
+
start_node_commands=start_node_commands,
|
|
1455
|
+
start_to_package_data_connections=start_to_package_data_connections,
|
|
1456
|
+
input_shape_data=input_shape_data,
|
|
1457
|
+
start_node_parameter_value_commands=start_node_parameter_value_commands,
|
|
1458
|
+
)
|
|
1459
|
+
|
|
1460
|
+
def _create_end_node_commands(
|
|
1461
|
+
self,
|
|
1462
|
+
request: PackageNodeAsSerializedFlowRequest,
|
|
1463
|
+
package_node: BaseNode,
|
|
1464
|
+
package_node_uuid: SerializedNodeCommands.NodeUUID,
|
|
1465
|
+
library_version: str,
|
|
1466
|
+
) -> PackagingEndNodeResult | PackageNodeAsSerializedFlowResultFailure:
|
|
1467
|
+
"""Create end node commands and connections for ALL package parameters that meet criteria."""
|
|
1468
|
+
# Generate UUID and name for end node
|
|
1469
|
+
end_node_uuid = SerializedNodeCommands.NodeUUID(str(uuid4()))
|
|
1470
|
+
end_node_name = f"End_Package_{package_node.name}"
|
|
1471
|
+
|
|
1472
|
+
# Build end node CreateNodeRequest
|
|
1473
|
+
end_create_node_command = CreateNodeRequest(
|
|
1474
|
+
node_type=request.end_node_type,
|
|
1475
|
+
specific_library_name=request.start_end_specific_library_name,
|
|
1476
|
+
node_name=end_node_name,
|
|
1477
|
+
metadata={},
|
|
1478
|
+
initial_setup=True,
|
|
1479
|
+
create_error_proxy_on_failure=False,
|
|
1480
|
+
)
|
|
1481
|
+
|
|
1482
|
+
# Create library details
|
|
1483
|
+
end_node_library_details = LibraryNameAndVersion(
|
|
1484
|
+
library_name=request.start_end_specific_library_name,
|
|
1485
|
+
library_version=library_version,
|
|
1486
|
+
)
|
|
1487
|
+
|
|
1488
|
+
# Process ALL package node parameters to create end node parameters and connections
|
|
1489
|
+
# Note: PROPERTY-only parameters are guaranteed to have OUTPUT mode after serialization
|
|
1490
|
+
end_node_parameter_commands = []
|
|
1491
|
+
package_to_end_data_connections = []
|
|
1492
|
+
output_shape_data: WorkflowShapeNodes = {}
|
|
1493
|
+
|
|
1494
|
+
for package_param in package_node.parameters:
|
|
1495
|
+
# Only ignore parameters that have ONLY INPUT mode (no OUTPUT or PROPERTY)
|
|
1496
|
+
has_output_mode = ParameterMode.OUTPUT in package_param.allowed_modes
|
|
1497
|
+
has_property_mode = ParameterMode.PROPERTY in package_param.allowed_modes
|
|
1498
|
+
|
|
1499
|
+
# Skip parameters that only have INPUT mode
|
|
1500
|
+
if not has_output_mode and not has_property_mode:
|
|
1501
|
+
continue
|
|
1502
|
+
|
|
1503
|
+
# Create prefixed parameter name for end node to avoid collisions
|
|
1504
|
+
end_param_name = f"{request.output_parameter_prefix}{package_param.name}"
|
|
1505
|
+
|
|
1506
|
+
# Extract parameter shape info for workflow shape (outputs to external consumers)
|
|
1507
|
+
param_shape_info = GriptapeNodes.WorkflowManager().extract_parameter_shape_info(
|
|
1508
|
+
package_param, include_control_params=True
|
|
1509
|
+
)
|
|
1510
|
+
if param_shape_info is not None:
|
|
1511
|
+
if end_node_name not in output_shape_data:
|
|
1512
|
+
output_shape_data[end_node_name] = {}
|
|
1513
|
+
output_shape_data[end_node_name][end_param_name] = param_shape_info
|
|
1514
|
+
|
|
1515
|
+
# Create parameter command for end node
|
|
1516
|
+
add_param_request = AddParameterToNodeRequest(
|
|
1517
|
+
node_name=end_node_name,
|
|
1518
|
+
parameter_name=end_param_name,
|
|
1519
|
+
# Use the package node's output_type as the type for our end node parameter
|
|
1520
|
+
type=package_param.output_type,
|
|
1521
|
+
default_value=None,
|
|
1522
|
+
tooltip=f"Output parameter {package_param.name} from packaged node {package_node.name}",
|
|
1523
|
+
initial_setup=True,
|
|
1524
|
+
)
|
|
1525
|
+
end_node_parameter_commands.append(add_param_request)
|
|
1526
|
+
|
|
1527
|
+
# Create connection from package node to end node
|
|
1528
|
+
package_to_end_connection = SerializedFlowCommands.IndirectConnectionSerialization(
|
|
1529
|
+
source_node_uuid=package_node_uuid,
|
|
1530
|
+
source_parameter_name=package_param.name,
|
|
1531
|
+
target_node_uuid=end_node_uuid,
|
|
1532
|
+
target_parameter_name=end_param_name,
|
|
1533
|
+
)
|
|
1534
|
+
package_to_end_data_connections.append(package_to_end_connection)
|
|
1535
|
+
|
|
1536
|
+
# Build complete SerializedNodeCommands for end node
|
|
1537
|
+
end_node_dependencies = NodeDependencies()
|
|
1538
|
+
end_node_dependencies.libraries.add(end_node_library_details)
|
|
1539
|
+
|
|
1540
|
+
end_node_commands = SerializedNodeCommands(
|
|
1541
|
+
create_node_command=end_create_node_command,
|
|
1542
|
+
element_modification_commands=end_node_parameter_commands,
|
|
1543
|
+
node_dependencies=end_node_dependencies,
|
|
1544
|
+
node_uuid=end_node_uuid,
|
|
1545
|
+
)
|
|
1546
|
+
|
|
1547
|
+
return PackagingEndNodeResult(
|
|
1548
|
+
end_node_commands=end_node_commands,
|
|
1549
|
+
package_to_end_data_connections=package_to_end_data_connections,
|
|
1550
|
+
output_shape_data=output_shape_data,
|
|
1551
|
+
)
|
|
1552
|
+
|
|
1553
|
+
def _create_start_node_control_connection(
|
|
1554
|
+
self,
|
|
1555
|
+
entry_control_parameter_name: str | None,
|
|
1556
|
+
start_node_uuid: SerializedNodeCommands.NodeUUID,
|
|
1557
|
+
package_node_uuid: SerializedNodeCommands.NodeUUID,
|
|
1558
|
+
package_node: BaseNode,
|
|
1559
|
+
) -> SerializedFlowCommands.IndirectConnectionSerialization | PackageNodeAsSerializedFlowResultFailure:
|
|
1560
|
+
"""Create control flow connection from start node to package node.
|
|
1561
|
+
|
|
1562
|
+
Connects the start node's first control output to the specified or first available package node control input.
|
|
1563
|
+
"""
|
|
1564
|
+
if entry_control_parameter_name is not None:
|
|
1565
|
+
# Case 1: Specific entry parameter name provided
|
|
1566
|
+
package_control_input_name = entry_control_parameter_name
|
|
1567
|
+
else:
|
|
1568
|
+
# Case 2: Find the first available control input parameter
|
|
1569
|
+
package_control_input_name = None
|
|
1570
|
+
for param in package_node.parameters:
|
|
1571
|
+
if ParameterTypeBuiltin.CONTROL_TYPE.value in param.input_types:
|
|
1572
|
+
package_control_input_name = param.name
|
|
1573
|
+
logger.warning(
|
|
1574
|
+
"No entry_control_parameter_name specified for packaging node '%s'. "
|
|
1575
|
+
"Using first available control input parameter: '%s'",
|
|
1576
|
+
package_node.name,
|
|
1577
|
+
package_control_input_name,
|
|
1578
|
+
)
|
|
1579
|
+
break
|
|
1580
|
+
|
|
1581
|
+
if package_control_input_name is None:
|
|
1582
|
+
details = f"Attempted to package node '{package_node.name}'. Failed because no control input parameters found on the node, so cannot create control flow connection."
|
|
1583
|
+
return PackageNodeAsSerializedFlowResultFailure(result_details=details)
|
|
1584
|
+
|
|
1585
|
+
# StartNode always has a control output parameter with name "exec_out"
|
|
1586
|
+
source_control_parameter_name = "exec_out"
|
|
1587
|
+
|
|
1588
|
+
# Create the connection
|
|
1589
|
+
control_connection = SerializedFlowCommands.IndirectConnectionSerialization(
|
|
1590
|
+
source_node_uuid=start_node_uuid,
|
|
1591
|
+
source_parameter_name=source_control_parameter_name,
|
|
1592
|
+
target_node_uuid=package_node_uuid,
|
|
1593
|
+
target_parameter_name=package_control_input_name,
|
|
1594
|
+
)
|
|
1595
|
+
return control_connection
|
|
1596
|
+
|
|
1597
|
+
def _assemble_serialized_flow( # noqa: PLR0913
|
|
1598
|
+
self,
|
|
1599
|
+
serialized_package_result: SerializeNodeToCommandsResultSuccess,
|
|
1600
|
+
start_node_result: PackagingStartNodeResult,
|
|
1601
|
+
end_node_result: PackagingEndNodeResult,
|
|
1602
|
+
control_flow_connections: list[SerializedFlowCommands.IndirectConnectionSerialization],
|
|
1603
|
+
unique_parameter_uuid_to_values: dict[SerializedNodeCommands.UniqueParameterValueUUID, Any],
|
|
1604
|
+
library_version: str,
|
|
1605
|
+
request: PackageNodeAsSerializedFlowRequest,
|
|
1606
|
+
) -> SerializedFlowCommands:
|
|
1607
|
+
"""Assemble the complete SerializedFlowCommands from all components."""
|
|
1608
|
+
# Combine all connections: Start->Package + Package->End + Control Flow
|
|
1609
|
+
all_connections = (
|
|
1610
|
+
start_node_result.start_to_package_data_connections
|
|
1611
|
+
+ end_node_result.package_to_end_data_connections
|
|
1612
|
+
+ control_flow_connections
|
|
1613
|
+
)
|
|
1614
|
+
|
|
1615
|
+
# Set up lock commands if needed
|
|
1616
|
+
set_lock_commands_per_node = {}
|
|
1617
|
+
if serialized_package_result.serialized_node_commands.lock_node_command:
|
|
1618
|
+
set_lock_commands_per_node[serialized_package_result.serialized_node_commands.node_uuid] = (
|
|
1619
|
+
serialized_package_result.serialized_node_commands.lock_node_command
|
|
1620
|
+
)
|
|
1621
|
+
|
|
1622
|
+
# Include all three nodes in the flow
|
|
1623
|
+
all_serialized_nodes = [
|
|
1624
|
+
start_node_result.start_node_commands,
|
|
1625
|
+
serialized_package_result.serialized_node_commands,
|
|
1626
|
+
end_node_result.end_node_commands,
|
|
1627
|
+
]
|
|
1628
|
+
|
|
1629
|
+
# Create a CreateFlowRequest for the packaged flow so that it can
|
|
1630
|
+
# run as a standalone workflow.
|
|
1631
|
+
package_flow_name = (
|
|
1632
|
+
f"Packaged_{serialized_package_result.serialized_node_commands.create_node_command.node_name}"
|
|
1633
|
+
)
|
|
1634
|
+
packaged_flow_metadata = {} # Keep it simple until we have reason to populate it
|
|
1635
|
+
|
|
1636
|
+
create_packaged_flow_request = CreateFlowRequest(
|
|
1637
|
+
parent_flow_name=None, # Standalone flow
|
|
1638
|
+
flow_name=package_flow_name,
|
|
1639
|
+
set_as_new_context=False, # Let deserializer decide
|
|
1640
|
+
metadata=packaged_flow_metadata,
|
|
1641
|
+
)
|
|
1642
|
+
|
|
1643
|
+
# Aggregate dependencies from the packaged nodes
|
|
1644
|
+
packaged_dependencies = self._aggregate_flow_dependencies(all_serialized_nodes, [])
|
|
1645
|
+
|
|
1646
|
+
# Add the start/end specific library dependency
|
|
1647
|
+
start_end_library_dependency = LibraryNameAndVersion(
|
|
1648
|
+
library_name=request.start_end_specific_library_name,
|
|
1649
|
+
library_version=library_version,
|
|
1650
|
+
)
|
|
1651
|
+
packaged_dependencies.libraries.add(start_end_library_dependency)
|
|
1652
|
+
|
|
1653
|
+
# Build the complete SerializedFlowCommands
|
|
1654
|
+
return SerializedFlowCommands(
|
|
1655
|
+
flow_initialization_command=create_packaged_flow_request,
|
|
1656
|
+
serialized_node_commands=all_serialized_nodes,
|
|
1657
|
+
serialized_connections=all_connections,
|
|
1658
|
+
unique_parameter_uuid_to_values=unique_parameter_uuid_to_values,
|
|
1659
|
+
set_parameter_value_commands={
|
|
1660
|
+
serialized_package_result.serialized_node_commands.node_uuid: serialized_package_result.set_parameter_value_commands,
|
|
1661
|
+
start_node_result.start_node_commands.node_uuid: start_node_result.start_node_parameter_value_commands,
|
|
1662
|
+
},
|
|
1663
|
+
set_lock_commands_per_node=set_lock_commands_per_node,
|
|
1664
|
+
sub_flows_commands=[],
|
|
1665
|
+
node_dependencies=packaged_dependencies,
|
|
1666
|
+
)
|
|
1667
|
+
|
|
1015
1668
|
async def on_start_flow_request(self, request: StartFlowRequest) -> ResultPayload: # noqa: C901, PLR0911, PLR0912
|
|
1016
1669
|
# which flow
|
|
1017
1670
|
flow_name = request.flow_name
|
|
@@ -1090,7 +1743,7 @@ class FlowManager:
|
|
|
1090
1743
|
details = f"Could not get flow state. Error: {err}"
|
|
1091
1744
|
return GetFlowStateResultFailure(result_details=details)
|
|
1092
1745
|
try:
|
|
1093
|
-
control_nodes, resolving_nodes
|
|
1746
|
+
control_nodes, resolving_nodes = self.flow_state(flow)
|
|
1094
1747
|
except Exception as e:
|
|
1095
1748
|
details = f"Failed to get flow state of flow with name {flow_name}. Exception occurred: {e} "
|
|
1096
1749
|
logger.exception(details)
|
|
@@ -1100,7 +1753,6 @@ class FlowManager:
|
|
|
1100
1753
|
control_nodes=control_nodes,
|
|
1101
1754
|
resolving_node=resolving_nodes,
|
|
1102
1755
|
result_details=details,
|
|
1103
|
-
involved_nodes=involved_nodes,
|
|
1104
1756
|
)
|
|
1105
1757
|
|
|
1106
1758
|
def on_cancel_flow_request(self, request: CancelFlowRequest) -> ResultPayload:
|
|
@@ -1264,6 +1916,31 @@ class FlowManager:
|
|
|
1264
1916
|
|
|
1265
1917
|
return ListFlowsInCurrentContextResultSuccess(flow_names=ret_list, result_details=details)
|
|
1266
1918
|
|
|
1919
|
+
def _aggregate_flow_dependencies(
|
|
1920
|
+
self, serialized_node_commands: list[SerializedNodeCommands], sub_flows_commands: list[SerializedFlowCommands]
|
|
1921
|
+
) -> NodeDependencies:
|
|
1922
|
+
"""Aggregate dependencies from nodes and sub-flows into a single NodeDependencies object.
|
|
1923
|
+
|
|
1924
|
+
Args:
|
|
1925
|
+
serialized_node_commands: List of serialized node commands to aggregate from
|
|
1926
|
+
sub_flows_commands: List of sub-flow commands to aggregate from
|
|
1927
|
+
|
|
1928
|
+
Returns:
|
|
1929
|
+
NodeDependencies object with all dependencies merged
|
|
1930
|
+
"""
|
|
1931
|
+
# Start with empty dependencies and aggregate into it
|
|
1932
|
+
aggregated_deps = NodeDependencies()
|
|
1933
|
+
|
|
1934
|
+
# Aggregate dependencies from all nodes
|
|
1935
|
+
for node_cmd in serialized_node_commands:
|
|
1936
|
+
aggregated_deps.aggregate_from(node_cmd.node_dependencies)
|
|
1937
|
+
|
|
1938
|
+
# Aggregate dependencies from all sub-flows
|
|
1939
|
+
for sub_flow_cmd in sub_flows_commands:
|
|
1940
|
+
aggregated_deps.aggregate_from(sub_flow_cmd.node_dependencies)
|
|
1941
|
+
|
|
1942
|
+
return aggregated_deps
|
|
1943
|
+
|
|
1267
1944
|
# TODO: https://github.com/griptape-ai/griptape-nodes/issues/861
|
|
1268
1945
|
# similar manager refactors: https://github.com/griptape-ai/griptape-nodes/issues/806
|
|
1269
1946
|
def on_serialize_flow_to_commands(self, request: SerializeFlowToCommandsRequest) -> ResultPayload: # noqa: C901, PLR0911, PLR0912, PLR0915
|
|
@@ -1285,12 +1962,6 @@ class FlowManager:
|
|
|
1285
1962
|
)
|
|
1286
1963
|
return SerializeFlowToCommandsResultFailure(result_details=details)
|
|
1287
1964
|
|
|
1288
|
-
# Track all node libraries that were in use by these Nodes
|
|
1289
|
-
node_libraries_in_use = set()
|
|
1290
|
-
|
|
1291
|
-
# Track all referenced workflows used by this flow and its sub-flows
|
|
1292
|
-
referenced_workflows_in_use = set()
|
|
1293
|
-
|
|
1294
1965
|
# Track all parameter values that were in use by these Nodes (maps UUID to Parameter value)
|
|
1295
1966
|
unique_parameter_uuid_to_values = {}
|
|
1296
1967
|
# And track how values map into that map.
|
|
@@ -1306,7 +1977,6 @@ class FlowManager:
|
|
|
1306
1977
|
workflow_name=referenced_workflow_name, # type: ignore[arg-type] # is_referenced_workflow() guarantees this is not None
|
|
1307
1978
|
imported_flow_metadata=flow.metadata,
|
|
1308
1979
|
)
|
|
1309
|
-
referenced_workflows_in_use.add(referenced_workflow_name) # type: ignore[arg-type] # is_referenced_workflow() guarantees this is not None
|
|
1310
1980
|
else:
|
|
1311
1981
|
# Always set set_as_new_context=False during serialization - let the workflow manager
|
|
1312
1982
|
# that loads this serialized flow decide whether to push it to context or not
|
|
@@ -1354,7 +2024,6 @@ class FlowManager:
|
|
|
1354
2024
|
node_name_to_uuid[node_name] = serialized_node.node_uuid
|
|
1355
2025
|
|
|
1356
2026
|
serialized_node_commands.append(serialized_node)
|
|
1357
|
-
node_libraries_in_use.add(serialized_node.node_library_details)
|
|
1358
2027
|
# Get the list of set value commands for THIS node.
|
|
1359
2028
|
set_value_commands_list = serialize_node_result.set_parameter_value_commands
|
|
1360
2029
|
if serialize_node_result.serialized_node_commands.lock_node_command is not None:
|
|
@@ -1403,20 +2072,22 @@ class FlowManager:
|
|
|
1403
2072
|
imported_flow_metadata=flow.metadata,
|
|
1404
2073
|
)
|
|
1405
2074
|
|
|
2075
|
+
# Create NodeDependencies with just the referenced workflow
|
|
2076
|
+
sub_flow_dependencies = NodeDependencies(
|
|
2077
|
+
referenced_workflows={referenced_workflow_name} # type: ignore[arg-type] # is_referenced_workflow() guarantees this is not None
|
|
2078
|
+
)
|
|
2079
|
+
|
|
1406
2080
|
serialized_flow = SerializedFlowCommands(
|
|
1407
|
-
node_libraries_used=set(),
|
|
1408
2081
|
flow_initialization_command=import_command,
|
|
1409
2082
|
serialized_node_commands=[],
|
|
1410
2083
|
serialized_connections=[],
|
|
1411
2084
|
unique_parameter_uuid_to_values={},
|
|
1412
2085
|
set_parameter_value_commands={},
|
|
2086
|
+
set_lock_commands_per_node={},
|
|
1413
2087
|
sub_flows_commands=[],
|
|
1414
|
-
|
|
2088
|
+
node_dependencies=sub_flow_dependencies,
|
|
1415
2089
|
)
|
|
1416
2090
|
sub_flow_commands.append(serialized_flow)
|
|
1417
|
-
|
|
1418
|
-
# Add this referenced workflow to our accumulation
|
|
1419
|
-
referenced_workflows_in_use.add(referenced_workflow_name) # type: ignore[arg-type] # is_referenced_workflow() guarantees this is not None
|
|
1420
2091
|
else:
|
|
1421
2092
|
# For standalone sub-flows, use the existing recursive serialization
|
|
1422
2093
|
with GriptapeNodes.ContextManager().flow(flow=flow):
|
|
@@ -1428,11 +2099,8 @@ class FlowManager:
|
|
|
1428
2099
|
serialized_flow = child_flow_result.serialized_flow_commands
|
|
1429
2100
|
sub_flow_commands.append(serialized_flow)
|
|
1430
2101
|
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
# Merge in all child flow referenced workflows.
|
|
1435
|
-
referenced_workflows_in_use.union(serialized_flow.referenced_workflows)
|
|
2102
|
+
# Aggregate all dependencies from nodes and sub-flows
|
|
2103
|
+
aggregated_dependencies = self._aggregate_flow_dependencies(serialized_node_commands, sub_flow_commands)
|
|
1436
2104
|
|
|
1437
2105
|
serialized_flow = SerializedFlowCommands(
|
|
1438
2106
|
flow_initialization_command=create_flow_request,
|
|
@@ -1442,8 +2110,7 @@ class FlowManager:
|
|
|
1442
2110
|
set_parameter_value_commands=set_parameter_value_commands_per_node,
|
|
1443
2111
|
set_lock_commands_per_node=set_lock_commands_per_node,
|
|
1444
2112
|
sub_flows_commands=sub_flow_commands,
|
|
1445
|
-
|
|
1446
|
-
referenced_workflows=referenced_workflows_in_use,
|
|
2113
|
+
node_dependencies=aggregated_dependencies,
|
|
1447
2114
|
)
|
|
1448
2115
|
details = f"Successfully serialized Flow '{flow_name}' into commands."
|
|
1449
2116
|
result = SerializeFlowToCommandsResultSuccess(serialized_flow_commands=serialized_flow, result_details=details)
|
|
@@ -1608,12 +2275,16 @@ class FlowManager:
|
|
|
1608
2275
|
# Initialize global control flow machine and DAG builder
|
|
1609
2276
|
|
|
1610
2277
|
self._global_control_flow_machine = ControlFlowMachine(flow.name)
|
|
2278
|
+
# Set off the request here.
|
|
1611
2279
|
try:
|
|
1612
2280
|
await self._global_control_flow_machine.start_flow(start_node, debug_mode)
|
|
1613
2281
|
except Exception:
|
|
1614
2282
|
if self.check_for_existing_running_flow():
|
|
1615
2283
|
self.cancel_flow_run()
|
|
1616
2284
|
raise
|
|
2285
|
+
GriptapeNodes.EventManager().put_event(
|
|
2286
|
+
ExecutionGriptapeNodeEvent(wrapped_event=ExecutionEvent(payload=InvolvedNodesEvent(involved_nodes=[])))
|
|
2287
|
+
)
|
|
1617
2288
|
|
|
1618
2289
|
def check_for_existing_running_flow(self) -> bool:
|
|
1619
2290
|
if self._global_control_flow_machine is None:
|
|
@@ -1639,6 +2310,9 @@ class FlowManager:
|
|
|
1639
2310
|
self._global_dag_builder.clear()
|
|
1640
2311
|
logger.debug("Cancelling flow run")
|
|
1641
2312
|
|
|
2313
|
+
GriptapeNodes.EventManager().put_event(
|
|
2314
|
+
ExecutionGriptapeNodeEvent(wrapped_event=ExecutionEvent(payload=InvolvedNodesEvent(involved_nodes=[])))
|
|
2315
|
+
)
|
|
1642
2316
|
GriptapeNodes.EventManager().put_event(
|
|
1643
2317
|
ExecutionGriptapeNodeEvent(wrapped_event=ExecutionEvent(payload=ControlFlowCancelledEvent()))
|
|
1644
2318
|
)
|
|
@@ -1720,6 +2394,13 @@ class FlowManager:
|
|
|
1720
2394
|
if self.check_for_existing_running_flow():
|
|
1721
2395
|
# Now we know something is running, it's ParallelResolutionMachine, and that we are in single_node_resolution.
|
|
1722
2396
|
self._global_dag_builder.add_node_with_dependencies(node, node.name)
|
|
2397
|
+
# Emit involved nodes update after adding node to DAG
|
|
2398
|
+
involved_nodes = list(self._global_dag_builder.node_to_reference.keys())
|
|
2399
|
+
GriptapeNodes.EventManager().put_event(
|
|
2400
|
+
ExecutionGriptapeNodeEvent(
|
|
2401
|
+
wrapped_event=ExecutionEvent(payload=InvolvedNodesEvent(involved_nodes=involved_nodes))
|
|
2402
|
+
)
|
|
2403
|
+
)
|
|
1723
2404
|
else:
|
|
1724
2405
|
# Set that we are only working on one node right now!
|
|
1725
2406
|
self._global_single_node_resolution = True
|
|
@@ -1733,10 +2414,29 @@ class FlowManager:
|
|
|
1733
2414
|
if isinstance(resolution_machine, ParallelResolutionMachine):
|
|
1734
2415
|
self._global_dag_builder.add_node_with_dependencies(node)
|
|
1735
2416
|
resolution_machine.context.dag_builder = self._global_dag_builder
|
|
1736
|
-
|
|
2417
|
+
involved_nodes = list(self._global_dag_builder.node_to_reference.keys())
|
|
2418
|
+
else:
|
|
2419
|
+
involved_nodes = list(flow.nodes.keys())
|
|
2420
|
+
# Send a InvolvedNodesRequest
|
|
2421
|
+
|
|
2422
|
+
GriptapeNodes.EventManager().put_event(
|
|
2423
|
+
ExecutionGriptapeNodeEvent(
|
|
2424
|
+
wrapped_event=ExecutionEvent(payload=InvolvedNodesEvent(involved_nodes=involved_nodes))
|
|
2425
|
+
)
|
|
2426
|
+
)
|
|
2427
|
+
try:
|
|
2428
|
+
await resolution_machine.resolve_node(node)
|
|
2429
|
+
except Exception as e:
|
|
2430
|
+
logger.exception("Exception during single node resolution")
|
|
2431
|
+
if self.check_for_existing_running_flow():
|
|
2432
|
+
self.cancel_flow_run()
|
|
2433
|
+
raise RuntimeError(e) from e
|
|
1737
2434
|
if resolution_machine.is_complete():
|
|
1738
2435
|
self._global_single_node_resolution = False
|
|
1739
2436
|
self._global_control_flow_machine.context.current_nodes = []
|
|
2437
|
+
GriptapeNodes.EventManager().put_event(
|
|
2438
|
+
ExecutionGriptapeNodeEvent(wrapped_event=ExecutionEvent(payload=InvolvedNodesEvent(involved_nodes=[])))
|
|
2439
|
+
)
|
|
1740
2440
|
|
|
1741
2441
|
async def single_execution_step(self, flow: ControlFlow, change_debug_mode: bool) -> None: # noqa: FBT001
|
|
1742
2442
|
# do a granular step
|
|
@@ -1794,12 +2494,11 @@ class FlowManager:
|
|
|
1794
2494
|
# Clear entry control parameter for new execution
|
|
1795
2495
|
node.set_entry_control_parameter(None)
|
|
1796
2496
|
|
|
1797
|
-
def flow_state(self, flow: ControlFlow) -> tuple[list[str] | None, list[str] | None
|
|
2497
|
+
def flow_state(self, flow: ControlFlow) -> tuple[list[str] | None, list[str] | None]: # noqa: ARG002
|
|
1798
2498
|
if not self.check_for_existing_running_flow():
|
|
1799
|
-
|
|
1800
|
-
raise RuntimeError(msg)
|
|
2499
|
+
return None, None
|
|
1801
2500
|
if self._global_control_flow_machine is None:
|
|
1802
|
-
return None, None
|
|
2501
|
+
return None, None
|
|
1803
2502
|
control_flow_context = self._global_control_flow_machine.context
|
|
1804
2503
|
current_control_nodes = (
|
|
1805
2504
|
[control_flow_node.name for control_flow_node in control_flow_context.current_nodes]
|
|
@@ -1812,13 +2511,12 @@ class FlowManager:
|
|
|
1812
2511
|
node.node_reference.name
|
|
1813
2512
|
for node in control_flow_context.resolution_machine.context.task_to_node.values()
|
|
1814
2513
|
]
|
|
1815
|
-
|
|
1816
|
-
return current_control_nodes, current_resolving_nodes, involved_nodes if len(involved_nodes) != 0 else None
|
|
2514
|
+
return current_control_nodes, current_resolving_nodes
|
|
1817
2515
|
if isinstance(control_flow_context.resolution_machine, SequentialResolutionMachine):
|
|
1818
2516
|
focus_stack_for_node = control_flow_context.resolution_machine.context.focus_stack
|
|
1819
2517
|
current_resolving_node = focus_stack_for_node[-1].node.name if len(focus_stack_for_node) else None
|
|
1820
|
-
return current_control_nodes, [current_resolving_node] if current_resolving_node else None
|
|
1821
|
-
return current_control_nodes, None
|
|
2518
|
+
return current_control_nodes, [current_resolving_node] if current_resolving_node else None
|
|
2519
|
+
return current_control_nodes, None
|
|
1822
2520
|
|
|
1823
2521
|
def get_start_node_from_node(self, flow: ControlFlow, node: BaseNode) -> BaseNode | None:
|
|
1824
2522
|
# backwards chain in control outputs.
|