griptape-nodes 0.65.2__py3-none-any.whl → 0.65.3__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.
@@ -497,6 +497,16 @@ class SubflowNodeGroup(BaseNodeGroup, ABC):
497
497
  for outgoing_connection in outgoing_connection_list:
498
498
  proxy_parameter = outgoing_connection.target_parameter
499
499
  remap_connections = connections.get_outgoing_connections_from_parameter(self, proxy_parameter)
500
+
501
+ # Check if proxy has other incoming connections besides this one
502
+ # If so, we should keep the proxy and its outgoing connections
503
+ incoming_to_proxy = connections.get_incoming_connections_to_parameter(self, proxy_parameter)
504
+ other_incoming_exists = any(
505
+ conn.source_node.name != node.name or conn.source_parameter.name != parameter_name
506
+ for conn in incoming_to_proxy
507
+ )
508
+
509
+ # Delete the connection from this node to proxy
500
510
  delete_result = GriptapeNodes.FlowManager().on_delete_connection_request(
501
511
  DeleteConnectionRequest(
502
512
  source_parameter_name=parameter_name,
@@ -509,19 +519,8 @@ class SubflowNodeGroup(BaseNodeGroup, ABC):
509
519
  msg = f"{self.name}: Failed to delete internal outgoing connection from {node.name}.{parameter_name} to proxy {proxy_parameter.name}: {delete_result.result_details}"
510
520
  raise RuntimeError(msg)
511
521
 
522
+ # Create direct connections from this node to target nodes
512
523
  for connection in remap_connections:
513
- delete_result = GriptapeNodes.FlowManager().on_delete_connection_request(
514
- DeleteConnectionRequest(
515
- source_parameter_name=connection.source_parameter.name,
516
- target_parameter_name=connection.target_parameter.name,
517
- source_node_name=connection.source_node.name,
518
- target_node_name=connection.target_node.name,
519
- )
520
- )
521
- if delete_result.failed():
522
- msg = f"{self.name}: Failed to delete external connection from proxy {proxy_parameter.name} to {connection.target_node.name}.{connection.target_parameter.name}: {delete_result.result_details}"
523
- raise RuntimeError(msg)
524
-
525
524
  create_result = GriptapeNodes.FlowManager().on_create_connection_request(
526
525
  CreateConnectionRequest(
527
526
  source_parameter_name=parameter_name,
@@ -534,7 +533,22 @@ class SubflowNodeGroup(BaseNodeGroup, ABC):
534
533
  msg = f"{self.name}: Failed to create direct outgoing connection from {node.name}.{parameter_name} to {connection.target_node.name}.{connection.target_parameter.name}: {create_result.result_details}"
535
534
  raise RuntimeError(msg)
536
535
 
537
- self._cleanup_proxy_parameter(proxy_parameter, "right_parameters")
536
+ # Only delete outgoing connections from proxy and clean up if no other incoming connections exist
537
+ if not other_incoming_exists:
538
+ for connection in remap_connections:
539
+ delete_result = GriptapeNodes.FlowManager().on_delete_connection_request(
540
+ DeleteConnectionRequest(
541
+ source_parameter_name=connection.source_parameter.name,
542
+ target_parameter_name=connection.target_parameter.name,
543
+ source_node_name=connection.source_node.name,
544
+ target_node_name=connection.target_node.name,
545
+ )
546
+ )
547
+ if delete_result.failed():
548
+ msg = f"{self.name}: Failed to delete external connection from proxy {proxy_parameter.name} to {connection.target_node.name}.{connection.target_parameter.name}: {delete_result.result_details}"
549
+ raise RuntimeError(msg)
550
+
551
+ self._cleanup_proxy_parameter(proxy_parameter, "right_parameters")
538
552
 
539
553
  def _remap_incoming_connections(self, node: BaseNode, connections: Connections) -> None:
540
554
  """Remap incoming connections that go through proxy parameters.
@@ -550,6 +564,16 @@ class SubflowNodeGroup(BaseNodeGroup, ABC):
550
564
  for incoming_connection in incoming_connection_list:
551
565
  proxy_parameter = incoming_connection.source_parameter
552
566
  remap_connections = connections.get_incoming_connections_to_parameter(self, proxy_parameter)
567
+
568
+ # Check if proxy has other outgoing connections besides this one
569
+ # If so, we should keep the proxy and its incoming connections
570
+ outgoing_from_proxy = connections.get_outgoing_connections_from_parameter(self, proxy_parameter)
571
+ other_outgoing_exists = any(
572
+ conn.target_node.name != node.name or conn.target_parameter.name != parameter_name
573
+ for conn in outgoing_from_proxy
574
+ )
575
+
576
+ # Delete the connection from proxy to this node
553
577
  delete_result = GriptapeNodes.FlowManager().on_delete_connection_request(
554
578
  DeleteConnectionRequest(
555
579
  source_parameter_name=proxy_parameter.name,
@@ -562,19 +586,8 @@ class SubflowNodeGroup(BaseNodeGroup, ABC):
562
586
  msg = f"{self.name}: Failed to delete internal incoming connection from proxy {proxy_parameter.name} to {node.name}.{parameter_name}: {delete_result.result_details}"
563
587
  raise RuntimeError(msg)
564
588
 
589
+ # Create direct connections from source nodes to this node
565
590
  for connection in remap_connections:
566
- delete_result = GriptapeNodes.FlowManager().on_delete_connection_request(
567
- DeleteConnectionRequest(
568
- source_parameter_name=connection.source_parameter.name,
569
- target_parameter_name=proxy_parameter.name,
570
- source_node_name=connection.source_node.name,
571
- target_node_name=self.name,
572
- )
573
- )
574
- if delete_result.failed():
575
- msg = f"{self.name}: Failed to delete external connection from {connection.source_node.name}.{connection.source_parameter.name} to proxy {proxy_parameter.name}: {delete_result.result_details}"
576
- raise RuntimeError(msg)
577
-
578
591
  create_result = GriptapeNodes.FlowManager().on_create_connection_request(
579
592
  CreateConnectionRequest(
580
593
  source_parameter_name=connection.source_parameter.name,
@@ -587,7 +600,22 @@ class SubflowNodeGroup(BaseNodeGroup, ABC):
587
600
  msg = f"{self.name}: Failed to create direct incoming connection from {connection.source_node.name}.{connection.source_parameter.name} to {node.name}.{parameter_name}: {create_result.result_details}"
588
601
  raise RuntimeError(msg)
589
602
 
590
- self._cleanup_proxy_parameter(proxy_parameter, "left_parameters")
603
+ # Only delete incoming connections to proxy and clean up if no other outgoing connections exist
604
+ if not other_outgoing_exists:
605
+ for connection in remap_connections:
606
+ delete_result = GriptapeNodes.FlowManager().on_delete_connection_request(
607
+ DeleteConnectionRequest(
608
+ source_parameter_name=connection.source_parameter.name,
609
+ target_parameter_name=proxy_parameter.name,
610
+ source_node_name=connection.source_node.name,
611
+ target_node_name=self.name,
612
+ )
613
+ )
614
+ if delete_result.failed():
615
+ msg = f"{self.name}: Failed to delete external connection from {connection.source_node.name}.{connection.source_parameter.name} to proxy {proxy_parameter.name}: {delete_result.result_details}"
616
+ raise RuntimeError(msg)
617
+
618
+ self._cleanup_proxy_parameter(proxy_parameter, "left_parameters")
591
619
 
592
620
  def remap_to_internal(self, nodes: list[BaseNode], connections: Connections) -> None:
593
621
  """Remap connections that are now internal after adding nodes to the group.
@@ -672,22 +700,175 @@ class SubflowNodeGroup(BaseNodeGroup, ABC):
672
700
  connections: Connections object from FlowManager
673
701
  node_names_in_group: Set of all node names currently in the group
674
702
  """
703
+ # Group outgoing connections by (source_node, source_parameter) to reuse proxy parameters
704
+ # Skip connections that already go to the NodeGroup itself (existing proxy parameters)
705
+ outgoing_by_source: dict[tuple[str, str], list[Connection]] = {}
675
706
  for node in nodes:
676
707
  outgoing_connections = connections.get_all_outgoing_connections(node)
677
708
  for conn in outgoing_connections:
678
- if conn.target_node.name not in node_names_in_group:
679
- self.map_external_connection(
680
- conn=conn,
681
- is_incoming=False,
682
- )
683
-
709
+ if conn.target_node.name not in node_names_in_group and conn.target_node.name != self.name:
710
+ key = (conn.source_node.name, conn.source_parameter.name)
711
+ outgoing_by_source.setdefault(key, []).append(conn)
712
+
713
+ # Group incoming connections by (source_node, source_parameter) to reuse proxy parameters
714
+ # This ensures that when an external node connects to multiple internal nodes,
715
+ # they share a single proxy parameter
716
+ # Skip connections that already come from the NodeGroup itself (existing proxy parameters)
717
+ incoming_by_source: dict[tuple[str, str], list[Connection]] = {}
718
+ for node in nodes:
684
719
  incoming_connections = connections.get_all_incoming_connections(node)
685
720
  for conn in incoming_connections:
686
- if conn.source_node.name not in node_names_in_group:
687
- self.map_external_connection(
688
- conn=conn,
689
- is_incoming=True,
690
- )
721
+ if conn.source_node.name not in node_names_in_group and conn.source_node.name != self.name:
722
+ key = (conn.source_node.name, conn.source_parameter.name)
723
+ incoming_by_source.setdefault(key, []).append(conn)
724
+
725
+ # Map outgoing connections - one proxy parameter per source parameter
726
+ for conn_list in outgoing_by_source.values():
727
+ self._map_external_connections_group(conn_list, is_incoming=False)
728
+
729
+ # Map incoming connections - one proxy parameter per source parameter
730
+ for conn_list in incoming_by_source.values():
731
+ self._map_external_connections_group(conn_list, is_incoming=True)
732
+
733
+ def _map_external_connections_group(self, conn_list: list[Connection], *, is_incoming: bool) -> None:
734
+ """Map a group of external connections that share the same external parameter.
735
+
736
+ Creates a single proxy parameter and connects all nodes through it.
737
+ If an existing proxy parameter already handles the same internal source,
738
+ it will be reused instead of creating a new one.
739
+
740
+ Args:
741
+ conn_list: List of connections sharing the same external parameter
742
+ is_incoming: True if these are incoming connections to the group
743
+ """
744
+ if not conn_list:
745
+ return
746
+
747
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
748
+
749
+ # All connections share the same external parameter
750
+ # For outgoing: the internal (source) parameter is shared
751
+ # For incoming: the external (source) parameter is shared
752
+ first_conn = conn_list[0]
753
+ # Use source_parameter in both cases since we group by source
754
+ grouped_parameter = first_conn.source_parameter
755
+
756
+ # Check if there's an existing proxy parameter we can reuse
757
+ existing_proxy = self._find_existing_proxy_for_source(
758
+ first_conn.source_node, first_conn.source_parameter, is_incoming=is_incoming
759
+ )
760
+
761
+ # Delete all original connections first
762
+ for conn in conn_list:
763
+ request = DeleteConnectionRequest(
764
+ conn.source_parameter.name,
765
+ conn.target_parameter.name,
766
+ conn.source_node.name,
767
+ conn.target_node.name,
768
+ )
769
+ result = GriptapeNodes.handle_request(request)
770
+ if not isinstance(result, DeleteConnectionResultSuccess):
771
+ logger.warning(
772
+ "%s failed to delete connection from %s.%s to %s.%s",
773
+ self.name,
774
+ conn.source_node.name,
775
+ conn.source_parameter.name,
776
+ conn.target_node.name,
777
+ conn.target_parameter.name,
778
+ )
779
+
780
+ # Use existing proxy or create a new one
781
+ if existing_proxy is not None:
782
+ proxy_parameter = existing_proxy
783
+ else:
784
+ proxy_parameter = self._create_proxy_parameter_for_connection(grouped_parameter, is_incoming=is_incoming)
785
+
786
+ # Create connections for all external nodes through the single proxy
787
+ for conn in conn_list:
788
+ self._create_connections_for_proxy_single(proxy_parameter, conn, is_incoming=is_incoming)
789
+
790
+ def _find_existing_proxy_for_source(
791
+ self, source_node: BaseNode, source_parameter: Parameter, *, is_incoming: bool
792
+ ) -> Parameter | None:
793
+ """Find an existing proxy parameter that already handles the given source.
794
+
795
+ For outgoing connections (is_incoming=False):
796
+ Looks for a right-side proxy that has an incoming connection from the
797
+ same internal source node/parameter.
798
+
799
+ For incoming connections (is_incoming=True):
800
+ Looks for a left-side proxy that has an incoming connection from the
801
+ same external source node/parameter.
802
+
803
+ Args:
804
+ source_node: The source node of the connection
805
+ source_parameter: The source parameter of the connection
806
+ is_incoming: True if looking for incoming connection proxies
807
+
808
+ Returns:
809
+ The existing proxy parameter if found, None otherwise
810
+ """
811
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
812
+
813
+ connections = GriptapeNodes.FlowManager().get_connections()
814
+
815
+ # Determine which proxy parameters to check based on direction
816
+ if is_incoming:
817
+ proxy_param_names = self.metadata.get("left_parameters", [])
818
+ else:
819
+ proxy_param_names = self.metadata.get("right_parameters", [])
820
+
821
+ for proxy_name in proxy_param_names:
822
+ proxy_param = self.get_parameter_by_name(proxy_name)
823
+ if proxy_param is None:
824
+ continue
825
+
826
+ # Check incoming connections to this proxy parameter
827
+ incoming_to_proxy = connections.get_incoming_connections_to_parameter(self, proxy_param)
828
+ for conn in incoming_to_proxy:
829
+ if conn.source_node.name == source_node.name and conn.source_parameter.name == source_parameter.name:
830
+ return proxy_param
831
+
832
+ return None
833
+
834
+ def _create_connections_for_proxy_single(
835
+ self, proxy_parameter: Parameter, old_connection: Connection, *, is_incoming: bool
836
+ ) -> None:
837
+ """Create connections for a single external connection through a proxy parameter.
838
+
839
+ Unlike create_connections_for_proxy, this assumes the proxy parameter already exists
840
+ and is being shared by multiple connections.
841
+
842
+ Args:
843
+ proxy_parameter: The proxy parameter to connect through
844
+ old_connection: The original connection being remapped
845
+ is_incoming: True if this is an incoming connection to the group
846
+ """
847
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
848
+
849
+ create_first_connection = CreateConnectionRequest(
850
+ source_parameter_name=old_connection.source_parameter.name,
851
+ target_parameter_name=proxy_parameter.name,
852
+ source_node_name=old_connection.source_node.name,
853
+ target_node_name=self.name,
854
+ is_node_group_internal=not is_incoming,
855
+ )
856
+ create_second_connection = CreateConnectionRequest(
857
+ source_parameter_name=proxy_parameter.name,
858
+ target_parameter_name=old_connection.target_parameter.name,
859
+ source_node_name=self.name,
860
+ target_node_name=old_connection.target_node.name,
861
+ is_node_group_internal=is_incoming,
862
+ )
863
+
864
+ # Track connections for cleanup
865
+ if proxy_parameter.name not in self._proxy_param_to_connections:
866
+ self._proxy_param_to_connections[proxy_parameter.name] = 2
867
+ else:
868
+ self._proxy_param_to_connections[proxy_parameter.name] += 2
869
+
870
+ GriptapeNodes.handle_request(create_first_connection)
871
+ GriptapeNodes.handle_request(create_second_connection)
691
872
 
692
873
  def _validate_nodes_in_group(self, nodes: list[BaseNode]) -> None:
693
874
  """Validate that all nodes are in the group."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: griptape-nodes
3
- Version: 0.65.2
3
+ Version: 0.65.3
4
4
  Summary: Add your description here
5
5
  Requires-Dist: griptape>=1.8.12
6
6
  Requires-Dist: pydantic>=2.10.6
@@ -69,7 +69,7 @@ griptape_nodes/exe_types/core_types.py,sha256=8MHHCuv2726T1DAuI3WIeyD9C__hMyYZB5
69
69
  griptape_nodes/exe_types/flow.py,sha256=2iAh3vN5wnMVxTc5jcPBg9TSiASq1DGIm5jgpO9Bdq4,5732
70
70
  griptape_nodes/exe_types/node_groups/__init__.py,sha256=vcZVzmN6yDlxlwf857ndAFchPQf7cXi_zn1HyiknX_4,210
71
71
  griptape_nodes/exe_types/node_groups/base_node_group.py,sha256=Vx3pQ6gxy-0t8x_DetVdFtaQU8PAYi71bFTepkeDOeg,841
72
- griptape_nodes/exe_types/node_groups/subflow_node_group.py,sha256=kYULd4lXp1zISl7CIciZcUTbMFAqX9AlWnNo-9O2jww,39143
72
+ griptape_nodes/exe_types/node_groups/subflow_node_group.py,sha256=ZyAYLU2_HaaPFczOw0CQYm74vmE0Dx0LtxV_FkAsAk0,48366
73
73
  griptape_nodes/exe_types/node_types.py,sha256=pPljG84zJ6Wd6yzcRjnK0n1805b-Jyzxx7-NFox66QE,84227
74
74
  griptape_nodes/exe_types/param_components/README.md,sha256=mcNFxJIan9CGTnecsrJ8mkHC55dlA3fb7k4HFznou-k,14850
75
75
  griptape_nodes/exe_types/param_components/__init__.py,sha256=ocm75WnsgiD6ozKVGFhoH9cQe_FEzeF2osxrRujOes0,60
@@ -277,7 +277,7 @@ griptape_nodes/version_compatibility/versions/v0_63_8/__init__.py,sha256=1VJswR2
277
277
  griptape_nodes/version_compatibility/versions/v0_63_8/deprecated_nodegroup_parameters.py,sha256=MqrmFszysezVEqsrGXrAK7VKjkVaxUtkpHysg15MDeM,4401
278
278
  griptape_nodes/version_compatibility/versions/v0_7_0/__init__.py,sha256=IzPPmGK86h2swfGGTOHyVcBIlOng6SjgWQzlbf3ngmo,51
279
279
  griptape_nodes/version_compatibility/versions/v0_7_0/local_executor_argument_addition.py,sha256=Thx8acnbw5OychhwEEj9aFxvbPe7Wgn4V9ZmZ7KRZqc,2082
280
- griptape_nodes-0.65.2.dist-info/WHEEL,sha256=93kfTGt3a0Dykt_T-gsjtyS5_p8F_d6CE1NwmBOirzo,79
281
- griptape_nodes-0.65.2.dist-info/entry_points.txt,sha256=qvevqd3BVbAV5TcantnAm0ouqaqYKhsRO3pkFymWLWM,82
282
- griptape_nodes-0.65.2.dist-info/METADATA,sha256=JxbTOsQh4UNRNC-DPS5OMmgH7U251pP_mVmURe66_ms,5374
283
- griptape_nodes-0.65.2.dist-info/RECORD,,
280
+ griptape_nodes-0.65.3.dist-info/WHEEL,sha256=93kfTGt3a0Dykt_T-gsjtyS5_p8F_d6CE1NwmBOirzo,79
281
+ griptape_nodes-0.65.3.dist-info/entry_points.txt,sha256=qvevqd3BVbAV5TcantnAm0ouqaqYKhsRO3pkFymWLWM,82
282
+ griptape_nodes-0.65.3.dist-info/METADATA,sha256=-qaV69DGa0V9Fm6dW0Y8tPFho-Zmprges164yrh-FUQ,5374
283
+ griptape_nodes-0.65.3.dist-info/RECORD,,