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.
@@ -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
- ) -> NodeGroupNode | AddNodesToNodeGroupResultFailure:
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, NodeGroupNode):
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
- ) -> NodeGroupNode | RemoveNodeFromNodeGroupResultFailure:
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, NodeGroupNode):
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, NodeGroupNode):
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
- # Handle NodeGroupNode specially - skip library lookup entirely
2544
- library_used = ""
2545
- lookup_metadata = False
2546
- library_details = None
2547
- if isinstance(node, NodeGroupNode):
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
- library_used = execution_env
2552
- lookup_metadata = True
2553
- else:
2554
- # Get the library and version details for regular nodes
2555
- library_used = node.metadata["library"]
2556
- lookup_metadata = True
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
- if lookup_metadata:
2559
- library_metadata_request = GetLibraryMetadataRequest(library=library_used)
2560
- # Call LibraryManager directly to avoid error toasts when library is unavailable (expected for ErrorProxyNode)
2561
- # Per https://github.com/griptape-ai/griptape-nodes/issues/1940
2562
- library_metadata_result = GriptapeNodes.LibraryManager().get_library_metadata_request(
2563
- library_metadata_request
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
- if not isinstance(library_metadata_result, GetLibraryMetadataResultSuccess):
2567
- if isinstance(node, ErrorProxyNode):
2568
- # For ErrorProxyNode, use descriptive message when original library unavailable
2569
- library_version = (
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
- create_node_request = CreateNodeGroupRequest(
2592
- node_group_name=node_name,
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-NodeGroupNode, library_details should always be set
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, (CreateNodeResultSuccess, CreateNodeGroupResultSuccess)):
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 | CreateNodeGroupRequest,
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
- create_cmd = serialized_node_command.create_node_command
1855
- if isinstance(create_cmd, CreateNodeGroupRequest):
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 CreateNodeGroupRequest.node_names_to_add - these are now UUIDs, convert to variable references
3184
- if isinstance(create_node_request, CreateNodeGroupRequest) and field.name == "node_names_to_add":
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 or CreateNodeGroupRequest)
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.node_types import NodeGroupNode
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, NodeGroupNode):
59
+ if isinstance(node, SubflowNodeGroup):
60
60
  return False
61
61
 
62
62
  # Check if current engine version is >= 0.63.8
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: griptape-nodes
3
- Version: 0.65.1
3
+ Version: 0.65.2
4
4
  Summary: Add your description here
5
5
  Requires-Dist: griptape>=1.8.12
6
6
  Requires-Dist: pydantic>=2.10.6