griptape-nodes 0.65.1__py3-none-any.whl → 0.65.2__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/common/node_executor.py +14 -14
- griptape_nodes/exe_types/node_groups/__init__.py +6 -0
- griptape_nodes/exe_types/node_groups/base_node_group.py +31 -0
- griptape_nodes/exe_types/node_groups/subflow_node_group.py +826 -0
- griptape_nodes/exe_types/node_types.py +0 -805
- griptape_nodes/machines/control_flow.py +4 -4
- griptape_nodes/node_library/library_registry.py +1 -0
- griptape_nodes/node_library/workflow_registry.py +1 -1
- griptape_nodes/retained_mode/events/flow_events.py +1 -1
- griptape_nodes/retained_mode/events/node_events.py +11 -77
- griptape_nodes/retained_mode/managers/flow_manager.py +72 -79
- griptape_nodes/retained_mode/managers/node_manager.py +113 -193
- griptape_nodes/retained_mode/managers/workflow_manager.py +5 -8
- griptape_nodes/version_compatibility/versions/v0_63_8/deprecated_nodegroup_parameters.py +2 -2
- {griptape_nodes-0.65.1.dist-info → griptape_nodes-0.65.2.dist-info}/METADATA +1 -1
- {griptape_nodes-0.65.1.dist-info → griptape_nodes-0.65.2.dist-info}/RECORD +18 -15
- {griptape_nodes-0.65.1.dist-info → griptape_nodes-0.65.2.dist-info}/WHEEL +1 -1
- {griptape_nodes-0.65.1.dist-info → griptape_nodes-0.65.2.dist-info}/entry_points.txt +0 -0
|
@@ -19,13 +19,13 @@ from griptape_nodes.exe_types.core_types import (
|
|
|
19
19
|
ParameterTypeBuiltin,
|
|
20
20
|
)
|
|
21
21
|
from griptape_nodes.exe_types.flow import ControlFlow
|
|
22
|
+
from griptape_nodes.exe_types.node_groups import SubflowNodeGroup
|
|
22
23
|
from griptape_nodes.exe_types.node_types import (
|
|
23
24
|
LOCAL_EXECUTION,
|
|
24
25
|
PRIVATE_EXECUTION,
|
|
25
26
|
BaseNode,
|
|
26
27
|
ErrorProxyNode,
|
|
27
28
|
NodeDependencies,
|
|
28
|
-
NodeGroupNode,
|
|
29
29
|
NodeResolutionState,
|
|
30
30
|
TransformedParameterValue,
|
|
31
31
|
)
|
|
@@ -57,6 +57,7 @@ from griptape_nodes.retained_mode.events.execution_events import (
|
|
|
57
57
|
StartFlowResultFailure,
|
|
58
58
|
)
|
|
59
59
|
from griptape_nodes.retained_mode.events.flow_events import (
|
|
60
|
+
DeleteFlowRequest,
|
|
60
61
|
ListNodesInFlowRequest,
|
|
61
62
|
ListNodesInFlowResultSuccess,
|
|
62
63
|
)
|
|
@@ -74,15 +75,9 @@ from griptape_nodes.retained_mode.events.node_events import (
|
|
|
74
75
|
CanResetNodeToDefaultsRequest,
|
|
75
76
|
CanResetNodeToDefaultsResultFailure,
|
|
76
77
|
CanResetNodeToDefaultsResultSuccess,
|
|
77
|
-
CreateNodeGroupRequest,
|
|
78
|
-
CreateNodeGroupResultFailure,
|
|
79
|
-
CreateNodeGroupResultSuccess,
|
|
80
78
|
CreateNodeRequest,
|
|
81
79
|
CreateNodeResultFailure,
|
|
82
80
|
CreateNodeResultSuccess,
|
|
83
|
-
DeleteNodeGroupRequest,
|
|
84
|
-
DeleteNodeGroupResultFailure,
|
|
85
|
-
DeleteNodeGroupResultSuccess,
|
|
86
81
|
DeleteNodeRequest,
|
|
87
82
|
DeleteNodeResultFailure,
|
|
88
83
|
DeleteNodeResultSuccess,
|
|
@@ -218,14 +213,12 @@ class NodeManager:
|
|
|
218
213
|
self._name_to_parent_flow_name = {}
|
|
219
214
|
|
|
220
215
|
event_manager.assign_manager_to_request_type(CreateNodeRequest, self.on_create_node_request)
|
|
221
|
-
event_manager.assign_manager_to_request_type(CreateNodeGroupRequest, self.on_create_node_group_request)
|
|
222
216
|
event_manager.assign_manager_to_request_type(
|
|
223
217
|
AddNodesToNodeGroupRequest, self.on_add_nodes_to_node_group_request
|
|
224
218
|
)
|
|
225
219
|
event_manager.assign_manager_to_request_type(
|
|
226
220
|
RemoveNodeFromNodeGroupRequest, self.on_remove_node_from_node_group_request
|
|
227
221
|
)
|
|
228
|
-
event_manager.assign_manager_to_request_type(DeleteNodeGroupRequest, self.on_delete_node_group_request)
|
|
229
222
|
event_manager.assign_manager_to_request_type(DeleteNodeRequest, self.on_delete_node_request)
|
|
230
223
|
event_manager.assign_manager_to_request_type(MoveNodeToNewFlowRequest, self.on_move_node_to_new_flow_request)
|
|
231
224
|
event_manager.assign_manager_to_request_type(
|
|
@@ -494,6 +487,33 @@ class NodeManager:
|
|
|
494
487
|
node.end_node = end_node
|
|
495
488
|
end_node.start_node = node
|
|
496
489
|
|
|
490
|
+
# Handle node_names_to_add for SubflowNodeGroup nodes
|
|
491
|
+
if request.node_names_to_add:
|
|
492
|
+
if isinstance(node, SubflowNodeGroup):
|
|
493
|
+
nodes_to_add = []
|
|
494
|
+
for node_name in request.node_names_to_add:
|
|
495
|
+
try:
|
|
496
|
+
existing_node = self.get_node_by_name(node_name)
|
|
497
|
+
nodes_to_add.append(existing_node)
|
|
498
|
+
except KeyError:
|
|
499
|
+
warning_details = (
|
|
500
|
+
f"Attempted to add node '{node_name}' to NodeGroup '{node.name}'. "
|
|
501
|
+
f"Failed because node was not found."
|
|
502
|
+
)
|
|
503
|
+
logger.warning(warning_details)
|
|
504
|
+
if nodes_to_add:
|
|
505
|
+
try:
|
|
506
|
+
node.add_nodes_to_group(nodes_to_add)
|
|
507
|
+
except Exception as err:
|
|
508
|
+
warning_msg = f"Failed to add nodes to NodeGroup '{node.name}': {err}"
|
|
509
|
+
logger.warning(warning_msg)
|
|
510
|
+
else:
|
|
511
|
+
warning_details = (
|
|
512
|
+
f"Attempted to add nodes '{request.node_names_to_add}' to Node '{node.name}'. "
|
|
513
|
+
f"Failed because node is not a SubflowNodeGroup."
|
|
514
|
+
)
|
|
515
|
+
logger.warning(warning_details)
|
|
516
|
+
|
|
497
517
|
return CreateNodeResultSuccess(
|
|
498
518
|
node_name=node.name,
|
|
499
519
|
node_type=node.__class__.__name__,
|
|
@@ -502,74 +522,6 @@ class NodeManager:
|
|
|
502
522
|
result_details=ResultDetails(message=details, level=log_level),
|
|
503
523
|
)
|
|
504
524
|
|
|
505
|
-
def on_create_node_group_request(self, request: CreateNodeGroupRequest) -> ResultPayload: # noqa: C901
|
|
506
|
-
"""Handle CreateNodeGroupRequest to create a new NodeGroupNode."""
|
|
507
|
-
flow_name = request.flow_name
|
|
508
|
-
flow = None
|
|
509
|
-
|
|
510
|
-
if flow_name is None:
|
|
511
|
-
if not GriptapeNodes.ContextManager().has_current_flow():
|
|
512
|
-
details = "Attempted to create NodeGroup in the Current Context. Failed because the Current Context was empty."
|
|
513
|
-
return CreateNodeGroupResultFailure(result_details=details)
|
|
514
|
-
flow = GriptapeNodes.ContextManager().get_current_flow()
|
|
515
|
-
flow_name = flow.name
|
|
516
|
-
|
|
517
|
-
if flow is None:
|
|
518
|
-
flow_mgr = GriptapeNodes.FlowManager()
|
|
519
|
-
try:
|
|
520
|
-
flow = flow_mgr.get_flow_by_name(flow_name)
|
|
521
|
-
except KeyError as err:
|
|
522
|
-
details = f"Attempted to create NodeGroup. Failed when attempting to find the parent Flow. Error: {err}"
|
|
523
|
-
return CreateNodeGroupResultFailure(result_details=details)
|
|
524
|
-
|
|
525
|
-
requested_node_group_name = request.node_group_name
|
|
526
|
-
if requested_node_group_name is None:
|
|
527
|
-
requested_node_group_name = "NodeGroup"
|
|
528
|
-
|
|
529
|
-
obj_mgr = GriptapeNodes.ObjectManager()
|
|
530
|
-
final_node_group_name = obj_mgr.generate_name_for_object(
|
|
531
|
-
type_name="NodeGroupNode", requested_name=requested_node_group_name
|
|
532
|
-
)
|
|
533
|
-
|
|
534
|
-
try:
|
|
535
|
-
# Create metadata with required keys for serialization
|
|
536
|
-
metadata = request.metadata if request.metadata else {}
|
|
537
|
-
metadata["node_type"] = "NodeGroupNode"
|
|
538
|
-
|
|
539
|
-
node_group = NodeGroupNode(name=final_node_group_name, metadata=metadata)
|
|
540
|
-
except Exception as err:
|
|
541
|
-
details = f"Could not create NodeGroup '{final_node_group_name}': {err}"
|
|
542
|
-
return CreateNodeGroupResultFailure(result_details=details)
|
|
543
|
-
# Add node to the flow so it is discoverable.
|
|
544
|
-
flow.add_node(node_group)
|
|
545
|
-
obj_mgr.add_object_by_name(node_group.name, node_group)
|
|
546
|
-
self._name_to_parent_flow_name[node_group.name] = flow_name
|
|
547
|
-
if request.node_names_to_add:
|
|
548
|
-
nodes_to_add = []
|
|
549
|
-
for node_name in request.node_names_to_add:
|
|
550
|
-
try:
|
|
551
|
-
node = self.get_node_by_name(node_name)
|
|
552
|
-
nodes_to_add.append(node)
|
|
553
|
-
except KeyError:
|
|
554
|
-
details = f"Attempted to add node '{node_name}' to NodeGroup '{final_node_group_name}'. Failed because node was not found."
|
|
555
|
-
logger.warning(details)
|
|
556
|
-
# Add Nodes manually here, so we don't have to add the NodeGroup and remove it if it fails.
|
|
557
|
-
try:
|
|
558
|
-
node_group.add_nodes_to_group(nodes_to_add)
|
|
559
|
-
except Exception as err:
|
|
560
|
-
msg = f"Failed to add nodes to NodeGroup '{final_node_group_name}': {err}"
|
|
561
|
-
logger.warning(msg)
|
|
562
|
-
if request.flow_name is None:
|
|
563
|
-
details = (
|
|
564
|
-
f"Successfully created NodeGroup '{final_node_group_name}' in the Current Context (Flow '{flow_name}')"
|
|
565
|
-
)
|
|
566
|
-
else:
|
|
567
|
-
details = f"Successfully created NodeGroup '{final_node_group_name}' in Flow '{flow_name}'"
|
|
568
|
-
|
|
569
|
-
return CreateNodeGroupResultSuccess(
|
|
570
|
-
node_group_name=node_group.name, result_details=ResultDetails(message=details, level=logging.DEBUG)
|
|
571
|
-
)
|
|
572
|
-
|
|
573
525
|
def _get_flow_for_node_group_operation(self, flow_name: str | None) -> AddNodesToNodeGroupResultFailure | None:
|
|
574
526
|
"""Get the flow for a node group operation."""
|
|
575
527
|
if flow_name is None:
|
|
@@ -618,7 +570,7 @@ class NodeManager:
|
|
|
618
570
|
|
|
619
571
|
def _get_node_group(
|
|
620
572
|
self, node_group_name: str, node_names: list[str]
|
|
621
|
-
) ->
|
|
573
|
+
) -> SubflowNodeGroup | AddNodesToNodeGroupResultFailure:
|
|
622
574
|
"""Get the NodeGroup node."""
|
|
623
575
|
try:
|
|
624
576
|
node_group = GriptapeNodes.ObjectManager().get_object_by_name(node_group_name)
|
|
@@ -626,7 +578,7 @@ class NodeManager:
|
|
|
626
578
|
details = f"Attempted to add nodes '{node_names}' to NodeGroup '{node_group_name}'. Failed because NodeGroup was not found."
|
|
627
579
|
return AddNodesToNodeGroupResultFailure(result_details=details)
|
|
628
580
|
|
|
629
|
-
if not isinstance(node_group,
|
|
581
|
+
if not isinstance(node_group, SubflowNodeGroup):
|
|
630
582
|
details = f"Attempted to add nodes '{node_names}' to '{node_group_name}'. Failed because '{node_group_name}' is not a NodeGroup."
|
|
631
583
|
return AddNodesToNodeGroupResultFailure(result_details=details)
|
|
632
584
|
|
|
@@ -705,7 +657,7 @@ class NodeManager:
|
|
|
705
657
|
|
|
706
658
|
def _get_node_group_for_remove(
|
|
707
659
|
self, node_group_name: str, node_names: list[str]
|
|
708
|
-
) ->
|
|
660
|
+
) -> SubflowNodeGroup | RemoveNodeFromNodeGroupResultFailure:
|
|
709
661
|
"""Get the NodeGroup node for remove operation."""
|
|
710
662
|
try:
|
|
711
663
|
node_group = GriptapeNodes.ObjectManager().get_object_by_name(node_group_name)
|
|
@@ -713,7 +665,7 @@ class NodeManager:
|
|
|
713
665
|
details = f"Attempted to remove nodes '{node_names}' from NodeGroup '{node_group_name}'. Failed because NodeGroup was not found."
|
|
714
666
|
return RemoveNodeFromNodeGroupResultFailure(result_details=details)
|
|
715
667
|
|
|
716
|
-
if not isinstance(node_group,
|
|
668
|
+
if not isinstance(node_group, SubflowNodeGroup):
|
|
717
669
|
details = f"Attempted to remove nodes '{node_names}' from '{node_group_name}'. Failed because '{node_group_name}' is not a NodeGroup."
|
|
718
670
|
return RemoveNodeFromNodeGroupResultFailure(result_details=details)
|
|
719
671
|
|
|
@@ -746,58 +698,6 @@ class NodeManager:
|
|
|
746
698
|
result_details=ResultDetails(message=details, level=logging.DEBUG),
|
|
747
699
|
)
|
|
748
700
|
|
|
749
|
-
def on_delete_node_group_request(self, request: DeleteNodeGroupRequest) -> ResultPayload:
|
|
750
|
-
"""Handle DeleteNodeGroupRequest to delete a NodeGroup and remove all its nodes."""
|
|
751
|
-
# Get the NodeGroup
|
|
752
|
-
obj_mgr = GriptapeNodes.ObjectManager()
|
|
753
|
-
try:
|
|
754
|
-
node_group = obj_mgr.get_object_by_name(request.node_group_name)
|
|
755
|
-
except KeyError:
|
|
756
|
-
details = (
|
|
757
|
-
f"Attempted to delete NodeGroup '{request.node_group_name}'. Failed because NodeGroup was not found."
|
|
758
|
-
)
|
|
759
|
-
return DeleteNodeGroupResultFailure(result_details=details)
|
|
760
|
-
|
|
761
|
-
if not isinstance(node_group, NodeGroupNode):
|
|
762
|
-
details = (
|
|
763
|
-
f"Attempted to delete '{request.node_group_name}' as NodeGroup. Failed because it is not a NodeGroup."
|
|
764
|
-
)
|
|
765
|
-
return DeleteNodeGroupResultFailure(result_details=details)
|
|
766
|
-
|
|
767
|
-
# Remove all nodes from the group first
|
|
768
|
-
if node_group.nodes:
|
|
769
|
-
nodes_to_remove = list(node_group.nodes.values())
|
|
770
|
-
try:
|
|
771
|
-
node_group.remove_nodes_from_group(nodes_to_remove)
|
|
772
|
-
except ValueError as err:
|
|
773
|
-
details = f"Attempted to delete NodeGroup '{request.node_group_name}'. Failed to remove nodes from group: {err}"
|
|
774
|
-
return DeleteNodeGroupResultFailure(result_details=details)
|
|
775
|
-
|
|
776
|
-
# Get the subflow name before deleting the NodeGroup
|
|
777
|
-
subflow_name = node_group.metadata.get("subflow_name")
|
|
778
|
-
|
|
779
|
-
# Now delete the NodeGroup node itself
|
|
780
|
-
delete_node_request = DeleteNodeRequest(node_name=request.node_group_name)
|
|
781
|
-
delete_result = self.on_delete_node_request(delete_node_request)
|
|
782
|
-
|
|
783
|
-
if delete_result.failed():
|
|
784
|
-
details = f"Attempted to delete NodeGroup '{request.node_group_name}'. Failed to delete the NodeGroup node: {delete_result.result_details}"
|
|
785
|
-
return DeleteNodeGroupResultFailure(result_details=details)
|
|
786
|
-
|
|
787
|
-
# Delete the subflow last if it exists
|
|
788
|
-
if subflow_name is not None:
|
|
789
|
-
from griptape_nodes.retained_mode.events.flow_events import DeleteFlowRequest
|
|
790
|
-
|
|
791
|
-
delete_flow_request = DeleteFlowRequest(flow_name=subflow_name)
|
|
792
|
-
delete_flow_result = GriptapeNodes.handle_request(delete_flow_request)
|
|
793
|
-
|
|
794
|
-
if delete_flow_result.failed():
|
|
795
|
-
details = f"Attempted to delete NodeGroup '{request.node_group_name}'. Failed to delete subflow '{subflow_name}': {delete_flow_result.result_details}"
|
|
796
|
-
return DeleteNodeGroupResultFailure(result_details=details)
|
|
797
|
-
|
|
798
|
-
details = f"Successfully deleted NodeGroup '{request.node_group_name}'"
|
|
799
|
-
return DeleteNodeGroupResultSuccess(result_details=ResultDetails(message=details, level=logging.DEBUG))
|
|
800
|
-
|
|
801
701
|
def cancel_conditionally(
|
|
802
702
|
self, parent_flow: ControlFlow, parent_flow_name: str, node: BaseNode
|
|
803
703
|
) -> ResultPayload | None:
|
|
@@ -879,6 +779,17 @@ class NodeManager:
|
|
|
879
779
|
cancel_result = self.cancel_conditionally(parent_flow, parent_flow_name, node)
|
|
880
780
|
if cancel_result is not None:
|
|
881
781
|
return cancel_result
|
|
782
|
+
|
|
783
|
+
subflow_name = None
|
|
784
|
+
# Remove nodes from the node group (if it is one) before deleting connections.
|
|
785
|
+
if isinstance(node, SubflowNodeGroup):
|
|
786
|
+
try:
|
|
787
|
+
subflow_name = node.delete_group()
|
|
788
|
+
except ValueError as err:
|
|
789
|
+
details = (
|
|
790
|
+
f"Attempted to delete NodeGroup '{request.node_name}'. Failed to remove nodes from group: {err}"
|
|
791
|
+
)
|
|
792
|
+
return DeleteNodeResultFailure(result_details=details)
|
|
882
793
|
# Remove all connections from this Node using a loop to handle cascading deletions
|
|
883
794
|
any_connections_remain = True
|
|
884
795
|
while any_connections_remain:
|
|
@@ -927,13 +838,12 @@ class NodeManager:
|
|
|
927
838
|
return DeleteNodeResultFailure(result_details=details)
|
|
928
839
|
|
|
929
840
|
# Check if it's in a node group
|
|
930
|
-
if isinstance(node.parent_group,
|
|
841
|
+
if isinstance(node.parent_group, SubflowNodeGroup):
|
|
931
842
|
try:
|
|
932
843
|
node.parent_group.delete_nodes_from_group([node])
|
|
933
844
|
except ValueError as e:
|
|
934
845
|
details = f"Attempted to delete a Node '{node_name}'. Failed to remove it from the node group: {e}"
|
|
935
846
|
return DeleteNodeResultFailure(result_details=details)
|
|
936
|
-
# Remove from the owning Flow
|
|
937
847
|
parent_flow.remove_node(node.name)
|
|
938
848
|
|
|
939
849
|
# Now remove the record keeping
|
|
@@ -944,6 +854,18 @@ class NodeManager:
|
|
|
944
854
|
if request.node_name is None:
|
|
945
855
|
GriptapeNodes.ContextManager().pop_node()
|
|
946
856
|
|
|
857
|
+
# Delete subflow if it has one and it still exists
|
|
858
|
+
# Note: The subflow may have already been deleted if we're being called as part of
|
|
859
|
+
# a parent flow deletion (which deletes child flows before nodes)
|
|
860
|
+
if subflow_name is not None:
|
|
861
|
+
subflow = GriptapeNodes.ObjectManager().attempt_get_object_by_name_as_type(subflow_name, ControlFlow)
|
|
862
|
+
if subflow is not None:
|
|
863
|
+
delete_flow_request = DeleteFlowRequest(flow_name=subflow_name)
|
|
864
|
+
delete_flow_result = GriptapeNodes.handle_request(delete_flow_request)
|
|
865
|
+
|
|
866
|
+
if delete_flow_result.failed():
|
|
867
|
+
details = f"Attempted to delete NodeGroup '{request.node_name}'. Failed to delete subflow '{subflow_name}': {delete_flow_result.result_details}"
|
|
868
|
+
return DeleteNodeResultFailure(result_details=details)
|
|
947
869
|
details = f"Successfully deleted Node '{node_name}'."
|
|
948
870
|
return DeleteNodeResultSuccess(result_details=details)
|
|
949
871
|
|
|
@@ -2540,61 +2462,67 @@ class NodeManager:
|
|
|
2540
2462
|
|
|
2541
2463
|
# This is our current dude.
|
|
2542
2464
|
with GriptapeNodes.ContextManager().node(node=node):
|
|
2543
|
-
#
|
|
2544
|
-
library_used = ""
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
if isinstance(node,
|
|
2548
|
-
# NodeGroupNode doesn't have a library dependency
|
|
2465
|
+
# Get the library and version details for all nodes
|
|
2466
|
+
library_used = node.metadata["library"]
|
|
2467
|
+
# For SubflowNodeGroup, also check if execution environment uses a special library
|
|
2468
|
+
execution_env_library_details = None
|
|
2469
|
+
if isinstance(node, SubflowNodeGroup):
|
|
2549
2470
|
execution_env = node.get_parameter_value(node.execution_environment.name)
|
|
2550
2471
|
if execution_env not in (LOCAL_EXECUTION, PRIVATE_EXECUTION):
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2472
|
+
# Get library details for the execution environment library
|
|
2473
|
+
exec_env_metadata_request = GetLibraryMetadataRequest(library=execution_env)
|
|
2474
|
+
exec_env_metadata_result = GriptapeNodes.LibraryManager().get_library_metadata_request(
|
|
2475
|
+
exec_env_metadata_request
|
|
2476
|
+
)
|
|
2477
|
+
if isinstance(exec_env_metadata_result, GetLibraryMetadataResultSuccess):
|
|
2478
|
+
exec_env_library_version = exec_env_metadata_result.metadata.library_version
|
|
2479
|
+
execution_env_library_details = LibraryNameAndVersion(
|
|
2480
|
+
library_name=execution_env, library_version=exec_env_library_version
|
|
2481
|
+
)
|
|
2557
2482
|
# Get the library metadata so we can get the version.
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
)
|
|
2483
|
+
library_metadata_request = GetLibraryMetadataRequest(library=library_used)
|
|
2484
|
+
# Call LibraryManager directly to avoid error toasts when library is unavailable (expected for ErrorProxyNode)
|
|
2485
|
+
# Per https://github.com/griptape-ai/griptape-nodes/issues/1940
|
|
2486
|
+
library_metadata_result = GriptapeNodes.LibraryManager().get_library_metadata_request(
|
|
2487
|
+
library_metadata_request
|
|
2488
|
+
)
|
|
2565
2489
|
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
"<version unavailable; workflow was saved when library was unable to be loaded>"
|
|
2571
|
-
)
|
|
2572
|
-
library_details = LibraryNameAndVersion(
|
|
2573
|
-
library_name=library_used, library_version=library_version
|
|
2574
|
-
)
|
|
2575
|
-
details = f"Serializing Node '{node_name}' (original type: {node.original_node_type}) with unavailable library '{library_used}'. Saving as ErrorProxy with placeholder version. Fix the missing library and reload the workflow to restore the original node."
|
|
2576
|
-
logger.warning(details)
|
|
2577
|
-
else:
|
|
2578
|
-
# For regular nodes, this is still an error
|
|
2579
|
-
details = f"Attempted to serialize Node '{node_name}' to commands. Failed to get metadata for library '{library_used}'."
|
|
2580
|
-
return SerializeNodeToCommandsResultFailure(result_details=details)
|
|
2581
|
-
else:
|
|
2582
|
-
library_version = library_metadata_result.metadata.library_version
|
|
2490
|
+
if not isinstance(library_metadata_result, GetLibraryMetadataResultSuccess):
|
|
2491
|
+
if isinstance(node, ErrorProxyNode):
|
|
2492
|
+
# For ErrorProxyNode, use descriptive message when original library unavailable
|
|
2493
|
+
library_version = "<version unavailable; workflow was saved when library was unable to be loaded>"
|
|
2583
2494
|
library_details = LibraryNameAndVersion(library_name=library_used, library_version=library_version)
|
|
2495
|
+
details = f"Serializing Node '{node_name}' (original type: {node.original_node_type}) with unavailable library '{library_used}'. Saving as ErrorProxy with placeholder version. Fix the missing library and reload the workflow to restore the original node."
|
|
2496
|
+
logger.warning(details)
|
|
2497
|
+
else:
|
|
2498
|
+
# For regular nodes, this is still an error
|
|
2499
|
+
details = f"Attempted to serialize Node '{node_name}' to commands. Failed to get metadata for library '{library_used}'."
|
|
2500
|
+
return SerializeNodeToCommandsResultFailure(result_details=details)
|
|
2501
|
+
else:
|
|
2502
|
+
library_version = library_metadata_result.metadata.library_version
|
|
2503
|
+
library_details = LibraryNameAndVersion(library_name=library_used, library_version=library_version)
|
|
2504
|
+
|
|
2505
|
+
# Handle SubflowNodeGroup specially - serialize like normal nodes but preserve node group behavior
|
|
2506
|
+
if isinstance(node, SubflowNodeGroup):
|
|
2507
|
+
# For non-SubflowNodeGroup, library_details should always be set
|
|
2508
|
+
if library_details is None:
|
|
2509
|
+
details = f"Attempted to serialize Node '{node_name}' to commands. Library details missing."
|
|
2510
|
+
return SerializeNodeToCommandsResultFailure(result_details=details)
|
|
2584
2511
|
|
|
2585
|
-
# Handle NodeGroupNode specially - emit CreateNodeGroupRequest instead
|
|
2586
|
-
if isinstance(node, NodeGroupNode):
|
|
2587
2512
|
# Remove node_names_in_group from metadata - it's redundant and will be regenerated
|
|
2588
2513
|
metadata_copy = copy.deepcopy(node.metadata)
|
|
2589
2514
|
metadata_copy.pop("node_names_in_group", None)
|
|
2590
2515
|
|
|
2591
|
-
|
|
2592
|
-
|
|
2516
|
+
# Serialize like a normal node but add node group specific fields
|
|
2517
|
+
create_node_request = CreateNodeRequest(
|
|
2518
|
+
node_type=node.__class__.__name__,
|
|
2519
|
+
specific_library_name=library_details.library_name,
|
|
2520
|
+
node_name=node_name,
|
|
2593
2521
|
node_names_to_add=list(node.nodes),
|
|
2594
2522
|
metadata=metadata_copy,
|
|
2595
2523
|
)
|
|
2596
2524
|
else:
|
|
2597
|
-
# For non-
|
|
2525
|
+
# For non-SubflowNodeGroup, library_details should always be set
|
|
2598
2526
|
if library_details is None:
|
|
2599
2527
|
details = f"Attempted to serialize Node '{node_name}' to commands. Library details missing."
|
|
2600
2528
|
return SerializeNodeToCommandsResultFailure(result_details=details)
|
|
@@ -2622,11 +2550,6 @@ class NodeManager:
|
|
|
2622
2550
|
# For ErrorProxyNode, we can't create a reference node, so skip comparison
|
|
2623
2551
|
if isinstance(node, ErrorProxyNode):
|
|
2624
2552
|
reference_node = None
|
|
2625
|
-
elif isinstance(node, NodeGroupNode):
|
|
2626
|
-
# For NodeGroupNode, create a fresh reference instance the same way we create NodeGroupNodes
|
|
2627
|
-
reference_node = NodeGroupNode(
|
|
2628
|
-
name="REFERENCE NODE", metadata={"library": "griptape_nodes", "node_type": "NodeGroupNode"}
|
|
2629
|
-
)
|
|
2630
2553
|
else:
|
|
2631
2554
|
reference_node = type(node)(name="REFERENCE NODE")
|
|
2632
2555
|
|
|
@@ -2706,12 +2629,17 @@ class NodeManager:
|
|
|
2706
2629
|
if library_details is not None:
|
|
2707
2630
|
node_dependencies.libraries.add(library_details)
|
|
2708
2631
|
|
|
2632
|
+
# For SubflowNodeGroup, also add execution environment library dependency if present
|
|
2633
|
+
if execution_env_library_details is not None:
|
|
2634
|
+
node_dependencies.libraries.add(execution_env_library_details)
|
|
2635
|
+
|
|
2709
2636
|
# Hooray
|
|
2710
2637
|
serialized_node_commands = SerializedNodeCommands(
|
|
2711
2638
|
create_node_command=create_node_request,
|
|
2712
2639
|
element_modification_commands=element_modification_commands,
|
|
2713
2640
|
node_dependencies=node_dependencies,
|
|
2714
2641
|
lock_node_command=lock_command,
|
|
2642
|
+
is_node_group=isinstance(node, SubflowNodeGroup),
|
|
2715
2643
|
)
|
|
2716
2644
|
details = f"Successfully serialized node '{node_name}' into commands."
|
|
2717
2645
|
result = SerializeNodeToCommandsResultSuccess(
|
|
@@ -2833,21 +2761,13 @@ class NodeManager:
|
|
|
2833
2761
|
# Issue the creation command first.
|
|
2834
2762
|
create_node_request = request.serialized_node_commands.create_node_command
|
|
2835
2763
|
create_node_result = GriptapeNodes().handle_request(create_node_request)
|
|
2836
|
-
if not isinstance(create_node_result,
|
|
2837
|
-
req_node_name =
|
|
2838
|
-
create_node_request.node_group_name
|
|
2839
|
-
if isinstance(create_node_request, CreateNodeGroupRequest)
|
|
2840
|
-
else create_node_request.node_name
|
|
2841
|
-
)
|
|
2764
|
+
if not isinstance(create_node_result, CreateNodeResultSuccess):
|
|
2765
|
+
req_node_name = create_node_request.node_name
|
|
2842
2766
|
details = f"Attempted to deserialize a serialized set of Node Creation commands. Failed to create node '{req_node_name}'."
|
|
2843
2767
|
return DeserializeNodeFromCommandsResultFailure(result_details=details)
|
|
2844
2768
|
|
|
2845
2769
|
# Adopt the newly-created node as our current context.
|
|
2846
|
-
node_name =
|
|
2847
|
-
create_node_result.node_group_name
|
|
2848
|
-
if isinstance(create_node_result, CreateNodeGroupResultSuccess)
|
|
2849
|
-
else create_node_result.node_name
|
|
2850
|
-
)
|
|
2770
|
+
node_name = create_node_result.node_name
|
|
2851
2771
|
node = GriptapeNodes.ObjectManager().attempt_get_object_by_name_as_type(node_name, BaseNode)
|
|
2852
2772
|
if node is None:
|
|
2853
2773
|
details = f"Attempted to deserialize a serialized set of Node Creation commands. Failed to get node '{node_name}'."
|
|
@@ -3128,7 +3048,7 @@ class NodeManager:
|
|
|
3128
3048
|
node: BaseNode,
|
|
3129
3049
|
unique_parameter_uuid_to_values: dict[SerializedNodeCommands.UniqueParameterValueUUID, Any],
|
|
3130
3050
|
serialized_parameter_value_tracker: SerializedParameterValueTracker,
|
|
3131
|
-
create_node_request: CreateNodeRequest
|
|
3051
|
+
create_node_request: CreateNodeRequest,
|
|
3132
3052
|
) -> list[SerializedNodeCommands.IndirectSetParameterValueCommand] | None:
|
|
3133
3053
|
"""Generates code to save a parameter value for a node in a Griptape workflow.
|
|
3134
3054
|
|
|
@@ -58,7 +58,6 @@ from griptape_nodes.retained_mode.events.library_events import (
|
|
|
58
58
|
ListRegisteredLibrariesRequest,
|
|
59
59
|
ListRegisteredLibrariesResultSuccess,
|
|
60
60
|
)
|
|
61
|
-
from griptape_nodes.retained_mode.events.node_events import CreateNodeGroupRequest
|
|
62
61
|
from griptape_nodes.retained_mode.events.object_events import ClearAllObjectStateRequest
|
|
63
62
|
from griptape_nodes.retained_mode.events.os_events import (
|
|
64
63
|
ExistingFilePolicy,
|
|
@@ -1846,13 +1845,12 @@ class WorkflowManager:
|
|
|
1846
1845
|
)
|
|
1847
1846
|
|
|
1848
1847
|
# Separate regular nodes from NodeGroup nodes in main flow
|
|
1849
|
-
from griptape_nodes.retained_mode.events.node_events import CreateNodeGroupRequest
|
|
1850
1848
|
|
|
1851
1849
|
regular_node_commands = []
|
|
1852
1850
|
node_group_commands = []
|
|
1853
1851
|
for serialized_node_command in serialized_flow_commands.serialized_node_commands:
|
|
1854
|
-
|
|
1855
|
-
if
|
|
1852
|
+
# Check if this is a NodeGroup by checking the SerializedNodeCommands flag
|
|
1853
|
+
if serialized_node_command.is_node_group:
|
|
1856
1854
|
node_group_commands.append(serialized_node_command)
|
|
1857
1855
|
else:
|
|
1858
1856
|
regular_node_commands.append(serialized_node_command)
|
|
@@ -3158,7 +3156,6 @@ class WorkflowManager:
|
|
|
3158
3156
|
import_recorder.add_from_import("griptape_nodes.node_library.library_registry", "NodeDeprecationMetadata")
|
|
3159
3157
|
import_recorder.add_from_import("griptape_nodes.node_library.library_registry", "IconVariant")
|
|
3160
3158
|
import_recorder.add_from_import("griptape_nodes.retained_mode.events.node_events", "CreateNodeRequest")
|
|
3161
|
-
import_recorder.add_from_import("griptape_nodes.retained_mode.events.node_events", "CreateNodeGroupRequest")
|
|
3162
3159
|
import_recorder.add_from_import(
|
|
3163
3160
|
"griptape_nodes.retained_mode.events.parameter_events", "AddParameterToNodeRequest"
|
|
3164
3161
|
)
|
|
@@ -3180,8 +3177,8 @@ class WorkflowManager:
|
|
|
3180
3177
|
for field in fields(create_node_request):
|
|
3181
3178
|
field_value = getattr(create_node_request, field.name)
|
|
3182
3179
|
if field_value != field.default:
|
|
3183
|
-
# Special handling for
|
|
3184
|
-
if
|
|
3180
|
+
# Special handling for node_names_to_add - these are now UUIDs, convert to variable references
|
|
3181
|
+
if field_value is create_node_request.node_names_to_add and field_value:
|
|
3185
3182
|
# field_value is now a list of UUIDs (converted in _serialize_package_nodes_for_local_execution)
|
|
3186
3183
|
# Convert each UUID to an AST Name node referencing the generated variable
|
|
3187
3184
|
node_var_ast_list = []
|
|
@@ -3208,7 +3205,7 @@ class WorkflowManager:
|
|
|
3208
3205
|
create_node_request_args.append(
|
|
3209
3206
|
ast.keyword(arg=field.name, value=ast.Constant(value=field_value, lineno=1, col_offset=0))
|
|
3210
3207
|
)
|
|
3211
|
-
# Get the actual request class name (CreateNodeRequest
|
|
3208
|
+
# Get the actual request class name (CreateNodeRequest)
|
|
3212
3209
|
request_class_name = type(create_node_request).__name__
|
|
3213
3210
|
# Handle the create node command and assign to node name
|
|
3214
3211
|
create_node_call_ast = ast.Assign(
|
|
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING, Any, ClassVar
|
|
|
7
7
|
|
|
8
8
|
import semver
|
|
9
9
|
|
|
10
|
-
from griptape_nodes.exe_types.
|
|
10
|
+
from griptape_nodes.exe_types.node_groups import SubflowNodeGroup
|
|
11
11
|
from griptape_nodes.retained_mode.events.app_events import (
|
|
12
12
|
GetEngineVersionRequest,
|
|
13
13
|
GetEngineVersionResultSuccess,
|
|
@@ -56,7 +56,7 @@ class DeprecatedNodeGroupParametersCheck(SetParameterVersionCompatibilityCheck):
|
|
|
56
56
|
return False
|
|
57
57
|
|
|
58
58
|
# Check if node is NOT a NodeGroup (we only block for non-NodeGroup nodes)
|
|
59
|
-
if isinstance(node,
|
|
59
|
+
if isinstance(node, SubflowNodeGroup):
|
|
60
60
|
return False
|
|
61
61
|
|
|
62
62
|
# Check if current engine version is >= 0.63.8
|