griptape-nodes 0.63.10__py3-none-any.whl → 0.64.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/common/node_executor.py +95 -171
- griptape_nodes/exe_types/connections.py +51 -2
- griptape_nodes/exe_types/flow.py +3 -3
- griptape_nodes/exe_types/node_types.py +330 -202
- griptape_nodes/exe_types/param_components/artifact_url/__init__.py +1 -0
- griptape_nodes/exe_types/param_components/artifact_url/public_artifact_url_parameter.py +155 -0
- griptape_nodes/exe_types/param_components/progress_bar_component.py +1 -1
- griptape_nodes/exe_types/param_types/parameter_string.py +27 -0
- griptape_nodes/machines/control_flow.py +64 -203
- griptape_nodes/machines/dag_builder.py +85 -238
- griptape_nodes/machines/parallel_resolution.py +9 -236
- griptape_nodes/machines/sequential_resolution.py +133 -11
- griptape_nodes/retained_mode/events/agent_events.py +2 -0
- griptape_nodes/retained_mode/events/flow_events.py +5 -6
- griptape_nodes/retained_mode/events/node_events.py +151 -1
- griptape_nodes/retained_mode/events/workflow_events.py +10 -0
- griptape_nodes/retained_mode/managers/agent_manager.py +33 -1
- griptape_nodes/retained_mode/managers/flow_manager.py +213 -290
- griptape_nodes/retained_mode/managers/library_manager.py +24 -7
- griptape_nodes/retained_mode/managers/node_manager.py +400 -77
- griptape_nodes/retained_mode/managers/version_compatibility_manager.py +113 -69
- griptape_nodes/retained_mode/managers/workflow_manager.py +45 -10
- griptape_nodes/servers/mcp.py +32 -0
- griptape_nodes/version_compatibility/versions/v0_63_8/__init__.py +1 -0
- griptape_nodes/version_compatibility/versions/v0_63_8/deprecated_nodegroup_parameters.py +105 -0
- {griptape_nodes-0.63.10.dist-info → griptape_nodes-0.64.1.dist-info}/METADATA +3 -1
- {griptape_nodes-0.63.10.dist-info → griptape_nodes-0.64.1.dist-info}/RECORD +31 -28
- griptape_nodes/version_compatibility/workflow_versions/__init__.py +0 -1
- /griptape_nodes/version_compatibility/{workflow_versions → versions}/v0_7_0/__init__.py +0 -0
- /griptape_nodes/version_compatibility/{workflow_versions → versions}/v0_7_0/local_executor_argument_addition.py +0 -0
- {griptape_nodes-0.63.10.dist-info → griptape_nodes-0.64.1.dist-info}/WHEEL +0 -0
- {griptape_nodes-0.63.10.dist-info → griptape_nodes-0.64.1.dist-info}/entry_points.txt +0 -0
|
@@ -20,7 +20,7 @@ from griptape_nodes.exe_types.node_types import (
|
|
|
20
20
|
EndLoopNode,
|
|
21
21
|
ErrorProxyNode,
|
|
22
22
|
NodeDependencies,
|
|
23
|
-
|
|
23
|
+
NodeGroupNode,
|
|
24
24
|
NodeResolutionState,
|
|
25
25
|
StartLoopNode,
|
|
26
26
|
)
|
|
@@ -60,15 +60,24 @@ from griptape_nodes.retained_mode.events.library_events import (
|
|
|
60
60
|
GetLibraryMetadataResultSuccess,
|
|
61
61
|
)
|
|
62
62
|
from griptape_nodes.retained_mode.events.node_events import (
|
|
63
|
+
AddNodesToNodeGroupRequest,
|
|
64
|
+
AddNodesToNodeGroupResultFailure,
|
|
65
|
+
AddNodesToNodeGroupResultSuccess,
|
|
63
66
|
BatchSetNodeMetadataRequest,
|
|
64
67
|
BatchSetNodeMetadataResultFailure,
|
|
65
68
|
BatchSetNodeMetadataResultSuccess,
|
|
66
69
|
CanResetNodeToDefaultsRequest,
|
|
67
70
|
CanResetNodeToDefaultsResultFailure,
|
|
68
71
|
CanResetNodeToDefaultsResultSuccess,
|
|
72
|
+
CreateNodeGroupRequest,
|
|
73
|
+
CreateNodeGroupResultFailure,
|
|
74
|
+
CreateNodeGroupResultSuccess,
|
|
69
75
|
CreateNodeRequest,
|
|
70
76
|
CreateNodeResultFailure,
|
|
71
77
|
CreateNodeResultSuccess,
|
|
78
|
+
DeleteNodeGroupRequest,
|
|
79
|
+
DeleteNodeGroupResultFailure,
|
|
80
|
+
DeleteNodeGroupResultSuccess,
|
|
72
81
|
DeleteNodeRequest,
|
|
73
82
|
DeleteNodeResultFailure,
|
|
74
83
|
DeleteNodeResultSuccess,
|
|
@@ -96,6 +105,9 @@ from griptape_nodes.retained_mode.events.node_events import (
|
|
|
96
105
|
ListParametersOnNodeRequest,
|
|
97
106
|
ListParametersOnNodeResultFailure,
|
|
98
107
|
ListParametersOnNodeResultSuccess,
|
|
108
|
+
RemoveNodeFromNodeGroupRequest,
|
|
109
|
+
RemoveNodeFromNodeGroupResultFailure,
|
|
110
|
+
RemoveNodeFromNodeGroupResultSuccess,
|
|
99
111
|
ResetNodeToDefaultsRequest,
|
|
100
112
|
ResetNodeToDefaultsResultFailure,
|
|
101
113
|
ResetNodeToDefaultsResultSuccess,
|
|
@@ -200,6 +212,14 @@ class NodeManager:
|
|
|
200
212
|
self._name_to_parent_flow_name = {}
|
|
201
213
|
|
|
202
214
|
event_manager.assign_manager_to_request_type(CreateNodeRequest, self.on_create_node_request)
|
|
215
|
+
event_manager.assign_manager_to_request_type(CreateNodeGroupRequest, self.on_create_node_group_request)
|
|
216
|
+
event_manager.assign_manager_to_request_type(
|
|
217
|
+
AddNodesToNodeGroupRequest, self.on_add_nodes_to_node_group_request
|
|
218
|
+
)
|
|
219
|
+
event_manager.assign_manager_to_request_type(
|
|
220
|
+
RemoveNodeFromNodeGroupRequest, self.on_remove_node_from_node_group_request
|
|
221
|
+
)
|
|
222
|
+
event_manager.assign_manager_to_request_type(DeleteNodeGroupRequest, self.on_delete_node_group_request)
|
|
203
223
|
event_manager.assign_manager_to_request_type(DeleteNodeRequest, self.on_delete_node_request)
|
|
204
224
|
event_manager.assign_manager_to_request_type(
|
|
205
225
|
GetNodeResolutionStateRequest, self.on_get_node_resolution_state_request
|
|
@@ -474,6 +494,288 @@ class NodeManager:
|
|
|
474
494
|
result_details=ResultDetails(message=details, level=log_level),
|
|
475
495
|
)
|
|
476
496
|
|
|
497
|
+
def on_create_node_group_request(self, request: CreateNodeGroupRequest) -> ResultPayload: # noqa: C901
|
|
498
|
+
"""Handle CreateNodeGroupRequest to create a new NodeGroupNode."""
|
|
499
|
+
flow_name = request.flow_name
|
|
500
|
+
flow = None
|
|
501
|
+
|
|
502
|
+
if flow_name is None:
|
|
503
|
+
if not GriptapeNodes.ContextManager().has_current_flow():
|
|
504
|
+
details = "Attempted to create NodeGroup in the Current Context. Failed because the Current Context was empty."
|
|
505
|
+
return CreateNodeGroupResultFailure(result_details=details)
|
|
506
|
+
flow = GriptapeNodes.ContextManager().get_current_flow()
|
|
507
|
+
flow_name = flow.name
|
|
508
|
+
|
|
509
|
+
if flow is None:
|
|
510
|
+
flow_mgr = GriptapeNodes.FlowManager()
|
|
511
|
+
try:
|
|
512
|
+
flow = flow_mgr.get_flow_by_name(flow_name)
|
|
513
|
+
except KeyError as err:
|
|
514
|
+
details = f"Attempted to create NodeGroup. Failed when attempting to find the parent Flow. Error: {err}"
|
|
515
|
+
return CreateNodeGroupResultFailure(result_details=details)
|
|
516
|
+
|
|
517
|
+
requested_node_group_name = request.node_group_name
|
|
518
|
+
if requested_node_group_name is None:
|
|
519
|
+
requested_node_group_name = "NodeGroup"
|
|
520
|
+
|
|
521
|
+
obj_mgr = GriptapeNodes.ObjectManager()
|
|
522
|
+
final_node_group_name = obj_mgr.generate_name_for_object(
|
|
523
|
+
type_name="NodeGroupNode", requested_name=requested_node_group_name
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
try:
|
|
527
|
+
# Create metadata with required keys for serialization
|
|
528
|
+
metadata = request.metadata if request.metadata else {}
|
|
529
|
+
metadata["node_type"] = "NodeGroupNode"
|
|
530
|
+
|
|
531
|
+
node_group = NodeGroupNode(name=final_node_group_name, metadata=metadata)
|
|
532
|
+
except Exception as err:
|
|
533
|
+
details = f"Could not create NodeGroup '{final_node_group_name}': {err}"
|
|
534
|
+
return CreateNodeGroupResultFailure(result_details=details)
|
|
535
|
+
|
|
536
|
+
if request.node_names_to_add:
|
|
537
|
+
nodes_to_add = []
|
|
538
|
+
for node_name in request.node_names_to_add:
|
|
539
|
+
try:
|
|
540
|
+
node = self.get_node_by_name(node_name)
|
|
541
|
+
except KeyError:
|
|
542
|
+
details = f"Attempted to add node '{node_name}' to NodeGroup '{final_node_group_name}'. Failed because node was not found."
|
|
543
|
+
return CreateNodeGroupResultFailure(result_details=details)
|
|
544
|
+
nodes_to_add.append(node)
|
|
545
|
+
# Add Nodes manually here, so we don't have to add the NodeGroup and remove it if it fails.
|
|
546
|
+
try:
|
|
547
|
+
node_group.add_nodes_to_group(nodes_to_add)
|
|
548
|
+
except Exception:
|
|
549
|
+
details = f"Failed to add nodes to NodeGroup '{final_node_group_name}'."
|
|
550
|
+
return CreateNodeGroupResultFailure(result_details=details)
|
|
551
|
+
flow.add_node(node_group)
|
|
552
|
+
obj_mgr.add_object_by_name(node_group.name, node_group)
|
|
553
|
+
self._name_to_parent_flow_name[node_group.name] = flow_name
|
|
554
|
+
if request.flow_name is None:
|
|
555
|
+
details = (
|
|
556
|
+
f"Successfully created NodeGroup '{final_node_group_name}' in the Current Context (Flow '{flow_name}')"
|
|
557
|
+
)
|
|
558
|
+
else:
|
|
559
|
+
details = f"Successfully created NodeGroup '{final_node_group_name}' in Flow '{flow_name}'"
|
|
560
|
+
|
|
561
|
+
return CreateNodeGroupResultSuccess(
|
|
562
|
+
node_group_name=node_group.name, result_details=ResultDetails(message=details, level=logging.DEBUG)
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
def _get_flow_for_node_group_operation(self, flow_name: str | None) -> AddNodesToNodeGroupResultFailure | None:
|
|
566
|
+
"""Get the flow for a node group operation."""
|
|
567
|
+
if flow_name is None:
|
|
568
|
+
if not GriptapeNodes.ContextManager().has_current_flow():
|
|
569
|
+
details = "Attempted to add node to NodeGroup in the Current Context. Failed because the Current Context was empty."
|
|
570
|
+
return AddNodesToNodeGroupResultFailure(result_details=details)
|
|
571
|
+
else:
|
|
572
|
+
try:
|
|
573
|
+
GriptapeNodes.FlowManager().get_flow_by_name(flow_name)
|
|
574
|
+
except KeyError as err:
|
|
575
|
+
details = (
|
|
576
|
+
f"Attempted to add node to NodeGroup. Failed when attempting to find the parent Flow. Error: {err}"
|
|
577
|
+
)
|
|
578
|
+
return AddNodesToNodeGroupResultFailure(result_details=details)
|
|
579
|
+
return None
|
|
580
|
+
|
|
581
|
+
def _get_nodes_for_group_operation(
|
|
582
|
+
self, node_names: list[str], node_group_name: str
|
|
583
|
+
) -> list[BaseNode] | AddNodesToNodeGroupResultFailure:
|
|
584
|
+
"""Get the list of nodes to add to a group.
|
|
585
|
+
|
|
586
|
+
Collects all errors and returns them together if multiple nodes fail.
|
|
587
|
+
"""
|
|
588
|
+
obj_mgr = GriptapeNodes.ObjectManager()
|
|
589
|
+
nodes = []
|
|
590
|
+
errors = []
|
|
591
|
+
|
|
592
|
+
for node_name in node_names:
|
|
593
|
+
try:
|
|
594
|
+
node = obj_mgr.get_object_by_name(node_name)
|
|
595
|
+
except KeyError:
|
|
596
|
+
errors.append(f"Node '{node_name}' was not found")
|
|
597
|
+
continue
|
|
598
|
+
|
|
599
|
+
if not isinstance(node, BaseNode):
|
|
600
|
+
errors.append(f"'{node_name}' is not a node")
|
|
601
|
+
continue
|
|
602
|
+
|
|
603
|
+
nodes.append(node)
|
|
604
|
+
|
|
605
|
+
if errors:
|
|
606
|
+
details = f"Attempted to add nodes to NodeGroup '{node_group_name}'. Failed for the following nodes: {'; '.join(errors)}"
|
|
607
|
+
return AddNodesToNodeGroupResultFailure(result_details=details)
|
|
608
|
+
|
|
609
|
+
return nodes
|
|
610
|
+
|
|
611
|
+
def _get_node_group(
|
|
612
|
+
self, node_group_name: str, node_names: list[str]
|
|
613
|
+
) -> NodeGroupNode | AddNodesToNodeGroupResultFailure:
|
|
614
|
+
"""Get the NodeGroup node."""
|
|
615
|
+
try:
|
|
616
|
+
node_group = GriptapeNodes.ObjectManager().get_object_by_name(node_group_name)
|
|
617
|
+
except KeyError:
|
|
618
|
+
details = f"Attempted to add nodes '{node_names}' to NodeGroup '{node_group_name}'. Failed because NodeGroup was not found."
|
|
619
|
+
return AddNodesToNodeGroupResultFailure(result_details=details)
|
|
620
|
+
|
|
621
|
+
if not isinstance(node_group, NodeGroupNode):
|
|
622
|
+
details = f"Attempted to add nodes '{node_names}' to '{node_group_name}'. Failed because '{node_group_name}' is not a NodeGroup."
|
|
623
|
+
return AddNodesToNodeGroupResultFailure(result_details=details)
|
|
624
|
+
|
|
625
|
+
return node_group
|
|
626
|
+
|
|
627
|
+
def on_add_nodes_to_node_group_request(self, request: AddNodesToNodeGroupRequest) -> ResultPayload:
|
|
628
|
+
"""Handle AddNodeToNodeGroupRequest to add a node to an existing NodeGroup."""
|
|
629
|
+
flow_result = self._get_flow_for_node_group_operation(request.flow_name)
|
|
630
|
+
if isinstance(flow_result, AddNodesToNodeGroupResultFailure):
|
|
631
|
+
return flow_result
|
|
632
|
+
|
|
633
|
+
nodes_result = self._get_nodes_for_group_operation(request.node_names, request.node_group_name)
|
|
634
|
+
if isinstance(nodes_result, AddNodesToNodeGroupResultFailure):
|
|
635
|
+
return nodes_result
|
|
636
|
+
nodes = nodes_result
|
|
637
|
+
|
|
638
|
+
node_group_result = self._get_node_group(request.node_group_name, request.node_names)
|
|
639
|
+
if isinstance(node_group_result, AddNodesToNodeGroupResultFailure):
|
|
640
|
+
return node_group_result
|
|
641
|
+
node_group = node_group_result
|
|
642
|
+
|
|
643
|
+
try:
|
|
644
|
+
node_group.add_nodes_to_group(nodes)
|
|
645
|
+
except Exception as err:
|
|
646
|
+
details = f"Attempted to add node '{request.node_names}' to NodeGroup '{request.node_group_name}'. Failed with error: {err}"
|
|
647
|
+
return AddNodesToNodeGroupResultFailure(result_details=details)
|
|
648
|
+
|
|
649
|
+
details = f"Successfully added node '{request.node_names}' to NodeGroup '{request.node_group_name}'"
|
|
650
|
+
return AddNodesToNodeGroupResultSuccess(
|
|
651
|
+
result_details=ResultDetails(message=details, level=logging.DEBUG),
|
|
652
|
+
)
|
|
653
|
+
|
|
654
|
+
def _get_flow_for_remove_operation(self, flow_name: str | None) -> RemoveNodeFromNodeGroupResultFailure | None:
|
|
655
|
+
"""Get the flow for a remove node from group operation."""
|
|
656
|
+
if flow_name is None:
|
|
657
|
+
if not GriptapeNodes.ContextManager().has_current_flow():
|
|
658
|
+
details = "Attempted to remove nodes from NodeGroup in the Current Context. Failed because the Current Context was empty."
|
|
659
|
+
return RemoveNodeFromNodeGroupResultFailure(result_details=details)
|
|
660
|
+
else:
|
|
661
|
+
try:
|
|
662
|
+
GriptapeNodes.FlowManager().get_flow_by_name(flow_name)
|
|
663
|
+
except KeyError as err:
|
|
664
|
+
details = f"Attempted to remove nodes from NodeGroup. Failed when attempting to find the parent Flow. Error: {err}"
|
|
665
|
+
return RemoveNodeFromNodeGroupResultFailure(result_details=details)
|
|
666
|
+
return None
|
|
667
|
+
|
|
668
|
+
def _get_nodes_for_remove_operation(
|
|
669
|
+
self, node_names: list[str], node_group_name: str
|
|
670
|
+
) -> list[BaseNode] | RemoveNodeFromNodeGroupResultFailure:
|
|
671
|
+
"""Get the list of nodes to remove from a group.
|
|
672
|
+
|
|
673
|
+
Collects all errors and returns them together if multiple nodes fail.
|
|
674
|
+
"""
|
|
675
|
+
obj_mgr = GriptapeNodes.ObjectManager()
|
|
676
|
+
nodes = []
|
|
677
|
+
errors = []
|
|
678
|
+
|
|
679
|
+
for node_name in node_names:
|
|
680
|
+
try:
|
|
681
|
+
node = obj_mgr.get_object_by_name(node_name)
|
|
682
|
+
except KeyError:
|
|
683
|
+
errors.append(f"Node '{node_name}' was not found")
|
|
684
|
+
continue
|
|
685
|
+
|
|
686
|
+
if not isinstance(node, BaseNode):
|
|
687
|
+
errors.append(f"'{node_name}' is not a node")
|
|
688
|
+
continue
|
|
689
|
+
|
|
690
|
+
nodes.append(node)
|
|
691
|
+
|
|
692
|
+
if errors:
|
|
693
|
+
details = f"Attempted to remove nodes from NodeGroup '{node_group_name}'. Failed for the following nodes: {'; '.join(errors)}"
|
|
694
|
+
return RemoveNodeFromNodeGroupResultFailure(result_details=details)
|
|
695
|
+
|
|
696
|
+
return nodes
|
|
697
|
+
|
|
698
|
+
def _get_node_group_for_remove(
|
|
699
|
+
self, node_group_name: str, node_names: list[str]
|
|
700
|
+
) -> NodeGroupNode | RemoveNodeFromNodeGroupResultFailure:
|
|
701
|
+
"""Get the NodeGroup node for remove operation."""
|
|
702
|
+
try:
|
|
703
|
+
node_group = GriptapeNodes.ObjectManager().get_object_by_name(node_group_name)
|
|
704
|
+
except KeyError:
|
|
705
|
+
details = f"Attempted to remove nodes '{node_names}' from NodeGroup '{node_group_name}'. Failed because NodeGroup was not found."
|
|
706
|
+
return RemoveNodeFromNodeGroupResultFailure(result_details=details)
|
|
707
|
+
|
|
708
|
+
if not isinstance(node_group, NodeGroupNode):
|
|
709
|
+
details = f"Attempted to remove nodes '{node_names}' from '{node_group_name}'. Failed because '{node_group_name}' is not a NodeGroup."
|
|
710
|
+
return RemoveNodeFromNodeGroupResultFailure(result_details=details)
|
|
711
|
+
|
|
712
|
+
return node_group
|
|
713
|
+
|
|
714
|
+
def on_remove_node_from_node_group_request(self, request: RemoveNodeFromNodeGroupRequest) -> ResultPayload:
|
|
715
|
+
"""Handle RemoveNodeFromNodeGroupRequest to remove nodes from an existing NodeGroup."""
|
|
716
|
+
flow_result = self._get_flow_for_remove_operation(request.flow_name)
|
|
717
|
+
if isinstance(flow_result, RemoveNodeFromNodeGroupResultFailure):
|
|
718
|
+
return flow_result
|
|
719
|
+
|
|
720
|
+
nodes_result = self._get_nodes_for_remove_operation(request.node_names, request.node_group_name)
|
|
721
|
+
if isinstance(nodes_result, RemoveNodeFromNodeGroupResultFailure):
|
|
722
|
+
return nodes_result
|
|
723
|
+
nodes = nodes_result
|
|
724
|
+
|
|
725
|
+
node_group_result = self._get_node_group_for_remove(request.node_group_name, request.node_names)
|
|
726
|
+
if isinstance(node_group_result, RemoveNodeFromNodeGroupResultFailure):
|
|
727
|
+
return node_group_result
|
|
728
|
+
node_group = node_group_result
|
|
729
|
+
|
|
730
|
+
try:
|
|
731
|
+
node_group.remove_nodes_from_group(nodes)
|
|
732
|
+
except ValueError as err:
|
|
733
|
+
details = f"Attempted to remove nodes '{request.node_names}' from NodeGroup '{request.node_group_name}'. Failed with error: {err}"
|
|
734
|
+
return RemoveNodeFromNodeGroupResultFailure(result_details=details)
|
|
735
|
+
|
|
736
|
+
details = f"Successfully removed nodes '{request.node_names}' from NodeGroup '{request.node_group_name}'"
|
|
737
|
+
return RemoveNodeFromNodeGroupResultSuccess(
|
|
738
|
+
result_details=ResultDetails(message=details, level=logging.DEBUG),
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
def on_delete_node_group_request(self, request: DeleteNodeGroupRequest) -> ResultPayload:
|
|
742
|
+
"""Handle DeleteNodeGroupRequest to delete a NodeGroup and remove all its nodes."""
|
|
743
|
+
# Get the NodeGroup
|
|
744
|
+
obj_mgr = GriptapeNodes.ObjectManager()
|
|
745
|
+
try:
|
|
746
|
+
node_group = obj_mgr.get_object_by_name(request.node_group_name)
|
|
747
|
+
except KeyError:
|
|
748
|
+
details = (
|
|
749
|
+
f"Attempted to delete NodeGroup '{request.node_group_name}'. Failed because NodeGroup was not found."
|
|
750
|
+
)
|
|
751
|
+
return DeleteNodeGroupResultFailure(result_details=details)
|
|
752
|
+
|
|
753
|
+
if not isinstance(node_group, NodeGroupNode):
|
|
754
|
+
details = (
|
|
755
|
+
f"Attempted to delete '{request.node_group_name}' as NodeGroup. Failed because it is not a NodeGroup."
|
|
756
|
+
)
|
|
757
|
+
return DeleteNodeGroupResultFailure(result_details=details)
|
|
758
|
+
|
|
759
|
+
# Remove all nodes from the group first
|
|
760
|
+
if node_group.nodes:
|
|
761
|
+
nodes_to_remove = list(node_group.nodes.values())
|
|
762
|
+
try:
|
|
763
|
+
node_group.remove_nodes_from_group(nodes_to_remove)
|
|
764
|
+
except ValueError as err:
|
|
765
|
+
details = f"Attempted to delete NodeGroup '{request.node_group_name}'. Failed to remove nodes from group: {err}"
|
|
766
|
+
return DeleteNodeGroupResultFailure(result_details=details)
|
|
767
|
+
|
|
768
|
+
# Now delete the NodeGroup node itself
|
|
769
|
+
delete_node_request = DeleteNodeRequest(node_name=request.node_group_name)
|
|
770
|
+
delete_result = self.on_delete_node_request(delete_node_request)
|
|
771
|
+
|
|
772
|
+
if delete_result.failed():
|
|
773
|
+
details = f"Attempted to delete NodeGroup '{request.node_group_name}'. Failed to delete the NodeGroup node: {delete_result.result_details}"
|
|
774
|
+
return DeleteNodeGroupResultFailure(result_details=details)
|
|
775
|
+
|
|
776
|
+
details = f"Successfully deleted NodeGroup '{request.node_group_name}'"
|
|
777
|
+
return DeleteNodeGroupResultSuccess(result_details=ResultDetails(message=details, level=logging.DEBUG))
|
|
778
|
+
|
|
477
779
|
def cancel_conditionally(
|
|
478
780
|
self, parent_flow: ControlFlow, parent_flow_name: str, node: BaseNode
|
|
479
781
|
) -> ResultPayload | None:
|
|
@@ -1610,6 +1912,14 @@ class NodeManager:
|
|
|
1610
1912
|
details = f"Attempted to set parameter '{param_name}' value on node '{node_name}'. Failed because the Node was locked."
|
|
1611
1913
|
return SetParameterValueResultFailure(result_details=details)
|
|
1612
1914
|
|
|
1915
|
+
# Let versioning system potentially squelch removed parameters.
|
|
1916
|
+
# This check must run BEFORE we validate parameter existence, since removed parameters won't exist.
|
|
1917
|
+
version_compat_result = GriptapeNodes.VersionCompatibilityManager().check_set_parameter_version_compatibility(
|
|
1918
|
+
node, param_name, request.value
|
|
1919
|
+
)
|
|
1920
|
+
if version_compat_result is not None:
|
|
1921
|
+
return version_compat_result
|
|
1922
|
+
|
|
1613
1923
|
# Handle ErrorProxyNode parameter value requests
|
|
1614
1924
|
if isinstance(node, ErrorProxyNode):
|
|
1615
1925
|
if request.initial_setup:
|
|
@@ -1624,36 +1934,10 @@ class NodeManager:
|
|
|
1624
1934
|
# Reject runtime parameter value changes on ErrorProxy
|
|
1625
1935
|
details = f"Cannot set parameter '{param_name}' on placeholder node '{node_name}'. This placeholder preserves your workflow structure but doesn't allow parameter changes, as they could cause issues when the original node is restored."
|
|
1626
1936
|
return SetParameterValueResultFailure(result_details=details)
|
|
1627
|
-
elif isinstance(node, NodeGroupProxyNode):
|
|
1628
|
-
# For NodeGroupProxyNode, set the value on both the proxy AND the original node
|
|
1629
|
-
node.set_parameter_value(param_name, request.value)
|
|
1630
|
-
|
|
1631
|
-
# Forward the value to the original node if this proxy parameter maps to one
|
|
1632
|
-
result = None
|
|
1633
|
-
if param_name in node._proxy_param_to_node_param:
|
|
1634
|
-
original_node, original_param_name = node._proxy_param_to_node_param[param_name]
|
|
1635
|
-
result = GriptapeNodes.handle_request(
|
|
1636
|
-
SetParameterValueRequest(
|
|
1637
|
-
parameter_name=original_param_name,
|
|
1638
|
-
node_name=original_node.name,
|
|
1639
|
-
value=request.value,
|
|
1640
|
-
data_type=request.data_type,
|
|
1641
|
-
incoming_connection_source_node_name=request.incoming_connection_source_node_name,
|
|
1642
|
-
incoming_connection_source_parameter_name=request.incoming_connection_source_parameter_name,
|
|
1643
|
-
)
|
|
1644
|
-
)
|
|
1645
|
-
logger.debug(
|
|
1646
|
-
"Forwarded parameter value from proxy '%s.%s' to original '%s.%s'",
|
|
1647
|
-
node.name,
|
|
1648
|
-
param_name,
|
|
1649
|
-
original_node.name,
|
|
1650
|
-
original_param_name,
|
|
1651
|
-
)
|
|
1652
|
-
details = f"Attempted to set parameter value for '{node_name}.{param_name}'. Successfully set value on the NodeGroupProxyNode '{node_name}', but failed to set value on the original node'."
|
|
1653
|
-
return result if result else SetParameterValueResultFailure(result_details=details)
|
|
1654
1937
|
|
|
1655
1938
|
# Does the Parameter actually exist on the Node?
|
|
1656
1939
|
parameter = node.get_parameter_by_name(param_name)
|
|
1940
|
+
|
|
1657
1941
|
if parameter is None:
|
|
1658
1942
|
details = f"Attempted to set parameter value for '{node_name}.{param_name}'. Failed because no parameter with that name could be found."
|
|
1659
1943
|
|
|
@@ -2144,55 +2428,81 @@ class NodeManager:
|
|
|
2144
2428
|
|
|
2145
2429
|
# This is our current dude.
|
|
2146
2430
|
with GriptapeNodes.ContextManager().node(node=node):
|
|
2147
|
-
#
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
# Call LibraryManager directly to avoid error toasts when library is unavailable (expected for ErrorProxyNode)
|
|
2152
|
-
# Per https://github.com/griptape-ai/griptape-nodes/issues/1940
|
|
2153
|
-
library_metadata_result = GriptapeNodes.LibraryManager().get_library_metadata_request(
|
|
2154
|
-
library_metadata_request
|
|
2155
|
-
)
|
|
2156
|
-
|
|
2157
|
-
if not isinstance(library_metadata_result, GetLibraryMetadataResultSuccess):
|
|
2158
|
-
if isinstance(node, ErrorProxyNode):
|
|
2159
|
-
# For ErrorProxyNode, use descriptive message when original library unavailable
|
|
2160
|
-
library_version = "<version unavailable; workflow was saved when library was unable to be loaded>"
|
|
2161
|
-
library_details = LibraryNameAndVersion(library_name=library_used, library_version=library_version)
|
|
2162
|
-
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."
|
|
2163
|
-
logger.warning(details)
|
|
2164
|
-
else:
|
|
2165
|
-
# For regular nodes, this is still an error
|
|
2166
|
-
details = f"Attempted to serialize Node '{node_name}' to commands. Failed to get metadata for library '{library_used}'."
|
|
2167
|
-
return SerializeNodeToCommandsResultFailure(result_details=details)
|
|
2431
|
+
# Handle NodeGroupNode specially - skip library lookup entirely
|
|
2432
|
+
if isinstance(node, NodeGroupNode):
|
|
2433
|
+
# NodeGroupNode doesn't have a library dependency
|
|
2434
|
+
library_details = None
|
|
2168
2435
|
else:
|
|
2169
|
-
|
|
2170
|
-
|
|
2436
|
+
# Get the library and version details for regular nodes
|
|
2437
|
+
library_used = node.metadata["library"]
|
|
2438
|
+
# Get the library metadata so we can get the version.
|
|
2439
|
+
library_metadata_request = GetLibraryMetadataRequest(library=library_used)
|
|
2440
|
+
# Call LibraryManager directly to avoid error toasts when library is unavailable (expected for ErrorProxyNode)
|
|
2441
|
+
# Per https://github.com/griptape-ai/griptape-nodes/issues/1940
|
|
2442
|
+
library_metadata_result = GriptapeNodes.LibraryManager().get_library_metadata_request(
|
|
2443
|
+
library_metadata_request
|
|
2444
|
+
)
|
|
2171
2445
|
|
|
2172
|
-
|
|
2446
|
+
if not isinstance(library_metadata_result, GetLibraryMetadataResultSuccess):
|
|
2447
|
+
if isinstance(node, ErrorProxyNode):
|
|
2448
|
+
# For ErrorProxyNode, use descriptive message when original library unavailable
|
|
2449
|
+
library_version = (
|
|
2450
|
+
"<version unavailable; workflow was saved when library was unable to be loaded>"
|
|
2451
|
+
)
|
|
2452
|
+
library_details = LibraryNameAndVersion(
|
|
2453
|
+
library_name=library_used, library_version=library_version
|
|
2454
|
+
)
|
|
2455
|
+
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."
|
|
2456
|
+
logger.warning(details)
|
|
2457
|
+
else:
|
|
2458
|
+
# For regular nodes, this is still an error
|
|
2459
|
+
details = f"Attempted to serialize Node '{node_name}' to commands. Failed to get metadata for library '{library_used}'."
|
|
2460
|
+
return SerializeNodeToCommandsResultFailure(result_details=details)
|
|
2461
|
+
else:
|
|
2462
|
+
library_version = library_metadata_result.metadata.library_version
|
|
2463
|
+
library_details = LibraryNameAndVersion(library_name=library_used, library_version=library_version)
|
|
2173
2464
|
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2465
|
+
# Handle NodeGroupNode specially - emit CreateNodeGroupRequest instead
|
|
2466
|
+
if isinstance(node, NodeGroupNode):
|
|
2467
|
+
create_node_request = CreateNodeGroupRequest(
|
|
2468
|
+
node_group_name=node_name,
|
|
2469
|
+
node_names_to_add=list(node.nodes),
|
|
2470
|
+
metadata=copy.deepcopy(node.metadata),
|
|
2471
|
+
)
|
|
2177
2472
|
else:
|
|
2178
|
-
|
|
2179
|
-
|
|
2473
|
+
# For non-NodeGroupNode, library_details should always be set
|
|
2474
|
+
if library_details is None:
|
|
2475
|
+
details = f"Attempted to serialize Node '{node_name}' to commands. Library details missing."
|
|
2476
|
+
return SerializeNodeToCommandsResultFailure(result_details=details)
|
|
2180
2477
|
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2478
|
+
# Handle ErrorProxyNode serialization - serialize as original node type
|
|
2479
|
+
if isinstance(node, ErrorProxyNode):
|
|
2480
|
+
serialized_node_type = node.original_node_type
|
|
2481
|
+
serialized_library_name = node.original_library_name
|
|
2482
|
+
else:
|
|
2483
|
+
serialized_node_type = node.__class__.__name__
|
|
2484
|
+
serialized_library_name = library_details.library_name
|
|
2485
|
+
|
|
2486
|
+
# Get the creation details for regular nodes
|
|
2487
|
+
create_node_request = CreateNodeRequest(
|
|
2488
|
+
node_type=serialized_node_type,
|
|
2489
|
+
node_name=node_name,
|
|
2490
|
+
specific_library_name=serialized_library_name,
|
|
2491
|
+
metadata=copy.deepcopy(node.metadata),
|
|
2492
|
+
# If it is actively resolving, mark as unresolved.
|
|
2493
|
+
resolution=node.state.value,
|
|
2494
|
+
initial_setup=True,
|
|
2495
|
+
)
|
|
2191
2496
|
|
|
2192
2497
|
# We're going to compare this node instance vs. a canonical one. Rez that one up.
|
|
2193
2498
|
# For ErrorProxyNode, we can't create a reference node, so skip comparison
|
|
2194
2499
|
if isinstance(node, ErrorProxyNode):
|
|
2195
2500
|
reference_node = None
|
|
2501
|
+
elif isinstance(node, NodeGroupNode):
|
|
2502
|
+
# For NodeGroupNode, create a fresh reference instance the same way we create NodeGroupNodes
|
|
2503
|
+
reference_node = NodeGroupNode(
|
|
2504
|
+
name="REFERENCE NODE", metadata={"library": "griptape_nodes", "node_type": "NodeGroupNode"}
|
|
2505
|
+
)
|
|
2196
2506
|
else:
|
|
2197
2507
|
reference_node = type(node)(name="REFERENCE NODE")
|
|
2198
2508
|
|
|
@@ -2255,6 +2565,7 @@ class NodeManager:
|
|
|
2255
2565
|
)
|
|
2256
2566
|
if set_param_value_requests is not None:
|
|
2257
2567
|
set_value_commands.extend(set_param_value_requests)
|
|
2568
|
+
|
|
2258
2569
|
# now check if locked
|
|
2259
2570
|
if node.lock:
|
|
2260
2571
|
lock_command = SetLockNodeStateRequest(node_name=None, lock=True)
|
|
@@ -2267,8 +2578,9 @@ class NodeManager:
|
|
|
2267
2578
|
# Ensure we always have a NodeDependencies object, even if empty
|
|
2268
2579
|
node_dependencies = NodeDependencies()
|
|
2269
2580
|
|
|
2270
|
-
# Add the library dependency to the node dependencies
|
|
2271
|
-
|
|
2581
|
+
# Add the library dependency to the node dependencies (if applicable)
|
|
2582
|
+
if library_details is not None:
|
|
2583
|
+
node_dependencies.libraries.add(library_details)
|
|
2272
2584
|
|
|
2273
2585
|
# Hooray
|
|
2274
2586
|
serialized_node_commands = SerializedNodeCommands(
|
|
@@ -2397,12 +2709,21 @@ class NodeManager:
|
|
|
2397
2709
|
# Issue the creation command first.
|
|
2398
2710
|
create_node_request = request.serialized_node_commands.create_node_command
|
|
2399
2711
|
create_node_result = GriptapeNodes().handle_request(create_node_request)
|
|
2400
|
-
if not isinstance(create_node_result, CreateNodeResultSuccess):
|
|
2401
|
-
|
|
2712
|
+
if not isinstance(create_node_result, (CreateNodeResultSuccess, CreateNodeGroupResultSuccess)):
|
|
2713
|
+
req_node_name = (
|
|
2714
|
+
create_node_request.node_group_name
|
|
2715
|
+
if isinstance(create_node_request, CreateNodeGroupRequest)
|
|
2716
|
+
else create_node_request.node_name
|
|
2717
|
+
)
|
|
2718
|
+
details = f"Attempted to deserialize a serialized set of Node Creation commands. Failed to create node '{req_node_name}'."
|
|
2402
2719
|
return DeserializeNodeFromCommandsResultFailure(result_details=details)
|
|
2403
2720
|
|
|
2404
2721
|
# Adopt the newly-created node as our current context.
|
|
2405
|
-
node_name =
|
|
2722
|
+
node_name = (
|
|
2723
|
+
create_node_result.node_group_name
|
|
2724
|
+
if isinstance(create_node_result, CreateNodeGroupResultSuccess)
|
|
2725
|
+
else create_node_result.node_name
|
|
2726
|
+
)
|
|
2406
2727
|
node = GriptapeNodes.ObjectManager().attempt_get_object_by_name_as_type(node_name, BaseNode)
|
|
2407
2728
|
if node is None:
|
|
2408
2729
|
details = f"Attempted to deserialize a serialized set of Node Creation commands. Failed to get node '{node_name}'."
|
|
@@ -2683,7 +3004,7 @@ class NodeManager:
|
|
|
2683
3004
|
node: BaseNode,
|
|
2684
3005
|
unique_parameter_uuid_to_values: dict[SerializedNodeCommands.UniqueParameterValueUUID, Any],
|
|
2685
3006
|
serialized_parameter_value_tracker: SerializedParameterValueTracker,
|
|
2686
|
-
create_node_request: CreateNodeRequest,
|
|
3007
|
+
create_node_request: CreateNodeRequest | CreateNodeGroupRequest,
|
|
2687
3008
|
) -> list[SerializedNodeCommands.IndirectSetParameterValueCommand] | None:
|
|
2688
3009
|
"""Generates code to save a parameter value for a node in a Griptape workflow.
|
|
2689
3010
|
|
|
@@ -2735,8 +3056,9 @@ class NodeManager:
|
|
|
2735
3056
|
if internal_command is None:
|
|
2736
3057
|
details = f"Attempted to serialize set value for parameter '{parameter.name}' on node '{node.name}'. The set value will not be restored in anything that attempts to deserialize or save this node. The value for this parameter was not serialized because it did not match Griptape Nodes' criteria for serializability. To remedy, either update the value's type to support serializability or mark the parameter as not serializable by setting serializable=False when creating the parameter."
|
|
2737
3058
|
logger.warning(details)
|
|
2738
|
-
# Set node to unresolved when serialization fails
|
|
2739
|
-
create_node_request
|
|
3059
|
+
# Set node to unresolved when serialization fails (only for CreateNodeRequest)
|
|
3060
|
+
if isinstance(create_node_request, CreateNodeRequest):
|
|
3061
|
+
create_node_request.resolution = NodeResolutionState.UNRESOLVED.value
|
|
2740
3062
|
else:
|
|
2741
3063
|
commands.append(internal_command)
|
|
2742
3064
|
if output_value is not None:
|
|
@@ -2752,8 +3074,9 @@ class NodeManager:
|
|
|
2752
3074
|
if output_command is None:
|
|
2753
3075
|
details = f"Attempted to serialize output value for parameter '{parameter.name}' on node '{node.name}'. The output value will not be restored in anything that attempts to deserialize or save this node. The value for this parameter was not serialized because it did not match Griptape Nodes' criteria for serializability. To remedy, either update the value's type to support serializability or mark the parameter as not serializable by setting serializable=False when creating the parameter."
|
|
2754
3076
|
logger.warning(details)
|
|
2755
|
-
# Set node to unresolved when serialization fails
|
|
2756
|
-
create_node_request
|
|
3077
|
+
# Set node to unresolved when serialization fails (only for CreateNodeRequest)
|
|
3078
|
+
if isinstance(create_node_request, CreateNodeRequest):
|
|
3079
|
+
create_node_request.resolution = NodeResolutionState.UNRESOLVED.value
|
|
2757
3080
|
else:
|
|
2758
3081
|
commands.append(output_command)
|
|
2759
3082
|
return commands if commands else None
|