griptape-nodes 0.64.11__py3-none-any.whl → 0.65.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- griptape_nodes/app/app.py +25 -5
- griptape_nodes/cli/commands/init.py +65 -54
- griptape_nodes/cli/commands/libraries.py +92 -85
- griptape_nodes/cli/commands/self.py +121 -0
- griptape_nodes/common/node_executor.py +2142 -101
- griptape_nodes/exe_types/base_iterative_nodes.py +1004 -0
- griptape_nodes/exe_types/connections.py +114 -19
- griptape_nodes/exe_types/core_types.py +225 -7
- griptape_nodes/exe_types/flow.py +3 -3
- griptape_nodes/exe_types/node_types.py +681 -225
- griptape_nodes/exe_types/param_components/README.md +414 -0
- griptape_nodes/exe_types/param_components/api_key_provider_parameter.py +200 -0
- griptape_nodes/exe_types/param_components/huggingface/huggingface_model_parameter.py +2 -0
- griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_file_parameter.py +79 -5
- griptape_nodes/exe_types/param_types/parameter_button.py +443 -0
- griptape_nodes/machines/control_flow.py +84 -38
- griptape_nodes/machines/dag_builder.py +148 -70
- griptape_nodes/machines/parallel_resolution.py +61 -35
- griptape_nodes/machines/sequential_resolution.py +11 -113
- griptape_nodes/retained_mode/events/app_events.py +1 -0
- griptape_nodes/retained_mode/events/base_events.py +16 -13
- griptape_nodes/retained_mode/events/connection_events.py +3 -0
- griptape_nodes/retained_mode/events/execution_events.py +35 -0
- griptape_nodes/retained_mode/events/flow_events.py +15 -2
- griptape_nodes/retained_mode/events/library_events.py +347 -0
- griptape_nodes/retained_mode/events/node_events.py +48 -0
- griptape_nodes/retained_mode/events/os_events.py +86 -3
- griptape_nodes/retained_mode/events/project_events.py +15 -1
- griptape_nodes/retained_mode/events/workflow_events.py +48 -1
- griptape_nodes/retained_mode/griptape_nodes.py +6 -2
- griptape_nodes/retained_mode/managers/config_manager.py +10 -8
- griptape_nodes/retained_mode/managers/event_manager.py +168 -0
- griptape_nodes/retained_mode/managers/fitness_problems/libraries/__init__.py +2 -0
- griptape_nodes/retained_mode/managers/fitness_problems/libraries/old_xdg_location_warning_problem.py +43 -0
- griptape_nodes/retained_mode/managers/flow_manager.py +664 -123
- griptape_nodes/retained_mode/managers/library_manager.py +1142 -138
- griptape_nodes/retained_mode/managers/model_manager.py +2 -3
- griptape_nodes/retained_mode/managers/node_manager.py +148 -25
- griptape_nodes/retained_mode/managers/object_manager.py +3 -1
- griptape_nodes/retained_mode/managers/operation_manager.py +3 -1
- griptape_nodes/retained_mode/managers/os_manager.py +1158 -122
- griptape_nodes/retained_mode/managers/secrets_manager.py +2 -3
- griptape_nodes/retained_mode/managers/settings.py +21 -1
- griptape_nodes/retained_mode/managers/sync_manager.py +2 -3
- griptape_nodes/retained_mode/managers/workflow_manager.py +358 -104
- griptape_nodes/retained_mode/retained_mode.py +3 -3
- griptape_nodes/traits/button.py +44 -2
- griptape_nodes/traits/file_system_picker.py +2 -2
- griptape_nodes/utils/file_utils.py +101 -0
- griptape_nodes/utils/git_utils.py +1236 -0
- griptape_nodes/utils/library_utils.py +122 -0
- {griptape_nodes-0.64.11.dist-info → griptape_nodes-0.65.1.dist-info}/METADATA +2 -1
- {griptape_nodes-0.64.11.dist-info → griptape_nodes-0.65.1.dist-info}/RECORD +55 -47
- {griptape_nodes-0.64.11.dist-info → griptape_nodes-0.65.1.dist-info}/WHEEL +1 -1
- {griptape_nodes-0.64.11.dist-info → griptape_nodes-0.65.1.dist-info}/entry_points.txt +0 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from dataclasses import dataclass
|
|
3
3
|
from enum import StrEnum
|
|
4
|
+
from typing import NamedTuple
|
|
4
5
|
|
|
6
|
+
from griptape_nodes.exe_types.base_iterative_nodes import BaseIterativeEndNode, BaseIterativeStartNode
|
|
5
7
|
from griptape_nodes.exe_types.core_types import Parameter, ParameterMode, ParameterTypeBuiltin
|
|
6
|
-
from griptape_nodes.exe_types.node_types import BaseNode, Connection,
|
|
8
|
+
from griptape_nodes.exe_types.node_types import BaseNode, Connection, NodeResolutionState
|
|
7
9
|
|
|
8
10
|
logger = logging.getLogger("griptape_nodes")
|
|
9
11
|
|
|
@@ -13,6 +15,11 @@ class Direction(StrEnum):
|
|
|
13
15
|
DOWNSTREAM = "downstream"
|
|
14
16
|
|
|
15
17
|
|
|
18
|
+
class ConnectionData(NamedTuple):
|
|
19
|
+
node: BaseNode
|
|
20
|
+
parameter: Parameter
|
|
21
|
+
|
|
22
|
+
|
|
16
23
|
@dataclass
|
|
17
24
|
class Connections:
|
|
18
25
|
# store connections as IDs
|
|
@@ -33,6 +40,8 @@ class Connections:
|
|
|
33
40
|
source_parameter: Parameter,
|
|
34
41
|
target_node: BaseNode,
|
|
35
42
|
target_parameter: Parameter,
|
|
43
|
+
*,
|
|
44
|
+
is_node_group_internal: bool = False,
|
|
36
45
|
) -> Connection:
|
|
37
46
|
if ParameterMode.OUTPUT not in source_parameter.get_mode():
|
|
38
47
|
errormsg = f"Output Connection not allowed on Parameter '{source_parameter.name}'."
|
|
@@ -44,7 +53,13 @@ class Connections:
|
|
|
44
53
|
if self.connection_allowed(source_node, source_parameter, is_source=True) and self.connection_allowed(
|
|
45
54
|
target_node, target_parameter, is_source=False
|
|
46
55
|
):
|
|
47
|
-
connection = Connection(
|
|
56
|
+
connection = Connection(
|
|
57
|
+
source_node,
|
|
58
|
+
source_parameter,
|
|
59
|
+
target_node,
|
|
60
|
+
target_parameter,
|
|
61
|
+
is_node_group_internal=is_node_group_internal,
|
|
62
|
+
)
|
|
48
63
|
# New index management.
|
|
49
64
|
connection_id = id(connection)
|
|
50
65
|
# Add connection to our dict here
|
|
@@ -106,14 +121,14 @@ class Connections:
|
|
|
106
121
|
return connection is None
|
|
107
122
|
|
|
108
123
|
def _get_connected_node_for_end_loop_control(
|
|
109
|
-
self, end_loop_node:
|
|
110
|
-
) ->
|
|
111
|
-
"""For
|
|
124
|
+
self, end_loop_node: BaseIterativeEndNode, control_parameter: Parameter
|
|
125
|
+
) -> ConnectionData | None:
|
|
126
|
+
"""For a BaseIterativeEndNode and its control parameter, finds the connected node and parameter.
|
|
112
127
|
|
|
113
|
-
It checks both outgoing connections (where
|
|
114
|
-
and incoming connections (where
|
|
128
|
+
It checks both outgoing connections (where BaseIterativeEndNode's parameter is a source)
|
|
129
|
+
and incoming connections (where BaseIterativeEndNode's parameter is a target).
|
|
115
130
|
"""
|
|
116
|
-
# Check if the
|
|
131
|
+
# Check if the BaseIterativeEndNode's control parameter is a source for an outgoing connection
|
|
117
132
|
if ParameterMode.OUTPUT in control_parameter.allowed_modes:
|
|
118
133
|
outgoing_connections_for_node = self.outgoing_index.get(end_loop_node.name, {})
|
|
119
134
|
connection_ids_as_source = outgoing_connections_for_node.get(control_parameter.name, [])
|
|
@@ -121,23 +136,23 @@ class Connections:
|
|
|
121
136
|
connection_id = connection_ids_as_source[0]
|
|
122
137
|
connection = self.connections.get(connection_id)
|
|
123
138
|
if connection:
|
|
124
|
-
return connection.target_node, connection.target_parameter
|
|
139
|
+
return ConnectionData(connection.target_node, connection.target_parameter)
|
|
125
140
|
elif ParameterMode.INPUT in control_parameter.allowed_modes:
|
|
126
|
-
# Check if the
|
|
141
|
+
# Check if the BaseIterativeEndNode's control parameter is a target for an incoming connection
|
|
127
142
|
incoming_connections_for_node = self.incoming_index.get(end_loop_node.name, {})
|
|
128
143
|
connection_ids_as_target = incoming_connections_for_node.get(control_parameter.name, [])
|
|
129
144
|
if connection_ids_as_target:
|
|
130
145
|
for connection_id in connection_ids_as_target:
|
|
131
146
|
connection = self.connections.get(connection_id)
|
|
132
|
-
if connection and isinstance(connection.source_node,
|
|
133
|
-
return connection.source_node, connection.source_parameter
|
|
147
|
+
if connection and isinstance(connection.source_node, BaseIterativeStartNode):
|
|
148
|
+
return ConnectionData(connection.source_node, connection.source_parameter)
|
|
134
149
|
return None # No connection found for this control parameter
|
|
135
150
|
|
|
136
|
-
def get_connected_node(
|
|
137
|
-
self, node: BaseNode, parameter: Parameter, direction: Direction | None = None
|
|
138
|
-
) ->
|
|
151
|
+
def get_connected_node( # noqa: C901, Need if checks.
|
|
152
|
+
self, node: BaseNode, parameter: Parameter, direction: Direction | None = None, *, include_internal: bool = True
|
|
153
|
+
) -> ConnectionData | None:
|
|
139
154
|
# Check to see if we should be getting the next connection or the previous connection based on the parameter.
|
|
140
|
-
# Override this method for
|
|
155
|
+
# Override this method for BaseIterativeEndNodes - these might have to go backwards or forwards.
|
|
141
156
|
if direction is not None:
|
|
142
157
|
# We've added direction as an override, since we sometimes need to get connections in a certain direction regardless of parameter types.
|
|
143
158
|
if direction == Direction.UPSTREAM:
|
|
@@ -145,7 +160,10 @@ class Connections:
|
|
|
145
160
|
elif direction == Direction.DOWNSTREAM:
|
|
146
161
|
connections = self.outgoing_index
|
|
147
162
|
else:
|
|
148
|
-
if
|
|
163
|
+
if (
|
|
164
|
+
isinstance(node, BaseIterativeEndNode)
|
|
165
|
+
and ParameterTypeBuiltin.CONTROL_TYPE.value == parameter.output_type
|
|
166
|
+
):
|
|
149
167
|
return self._get_connected_node_for_end_loop_control(node, parameter)
|
|
150
168
|
if ParameterTypeBuiltin.CONTROL_TYPE.value == parameter.output_type:
|
|
151
169
|
connections = self.outgoing_index
|
|
@@ -176,11 +194,14 @@ class Connections:
|
|
|
176
194
|
connection_id = connection_id[0]
|
|
177
195
|
if connection_id in self.connections:
|
|
178
196
|
connection = self.connections[connection_id]
|
|
197
|
+
# We don't traverse internal NodeGroup connections when include_internal is False.
|
|
198
|
+
if connection.is_node_group_internal and not include_internal:
|
|
199
|
+
return None
|
|
179
200
|
if direction == Direction.DOWNSTREAM:
|
|
180
201
|
# Return the target (next place to go)
|
|
181
|
-
return connection.target_node, connection.target_parameter
|
|
202
|
+
return ConnectionData(connection.target_node, connection.target_parameter)
|
|
182
203
|
# Return the source (next place to chain back to)
|
|
183
|
-
return connection.source_node, connection.source_parameter
|
|
204
|
+
return ConnectionData(connection.source_node, connection.source_parameter)
|
|
184
205
|
return None
|
|
185
206
|
|
|
186
207
|
def remove_connection_by_object(self, conn: Connection) -> bool:
|
|
@@ -308,3 +329,77 @@ class Connections:
|
|
|
308
329
|
)
|
|
309
330
|
)
|
|
310
331
|
self.unresolve_future_nodes(target_node)
|
|
332
|
+
|
|
333
|
+
def get_outgoing_connections_to_node(self, node: BaseNode, to_node: BaseNode) -> dict[str, list[Connection]]:
|
|
334
|
+
connections = {}
|
|
335
|
+
if node.name in self.outgoing_index:
|
|
336
|
+
parameters = self.outgoing_index[node.name]
|
|
337
|
+
for parameter, connection_ids in parameters.items():
|
|
338
|
+
for conn_id in connection_ids:
|
|
339
|
+
connection = self.connections[conn_id]
|
|
340
|
+
if connection.target_node.name == to_node.name:
|
|
341
|
+
if parameter not in connections:
|
|
342
|
+
connections[parameter] = [connection]
|
|
343
|
+
else:
|
|
344
|
+
connections[parameter].append(connection)
|
|
345
|
+
return connections
|
|
346
|
+
|
|
347
|
+
def get_incoming_connections_from_node(self, node: BaseNode, from_node: BaseNode) -> dict[str, list[Connection]]:
|
|
348
|
+
connections = {}
|
|
349
|
+
if node.name in self.incoming_index:
|
|
350
|
+
parameters = self.incoming_index[node.name]
|
|
351
|
+
for parameter, connection_ids in parameters.items():
|
|
352
|
+
for conn_id in connection_ids:
|
|
353
|
+
connection = self.connections[conn_id]
|
|
354
|
+
if connection.source_node.name == from_node.name:
|
|
355
|
+
if parameter not in connections:
|
|
356
|
+
connections[parameter] = [connection]
|
|
357
|
+
else:
|
|
358
|
+
connections[parameter].append(connection)
|
|
359
|
+
return connections
|
|
360
|
+
|
|
361
|
+
def get_outgoing_connections_from_parameter(self, node: BaseNode, parameter: Parameter) -> list[Connection]:
|
|
362
|
+
connections = []
|
|
363
|
+
if node.name in self.outgoing_index and parameter.name in self.outgoing_index[node.name]:
|
|
364
|
+
for conn_id in self.outgoing_index[node.name][parameter.name]:
|
|
365
|
+
connections.append(self.connections[conn_id]) # noqa: PERF401
|
|
366
|
+
return connections
|
|
367
|
+
|
|
368
|
+
def get_incoming_connections_to_parameter(self, node: BaseNode, parameter: Parameter) -> list[Connection]:
|
|
369
|
+
connections = []
|
|
370
|
+
if node.name in self.incoming_index and parameter.name in self.incoming_index[node.name]:
|
|
371
|
+
for conn_id in self.incoming_index[node.name][parameter.name]:
|
|
372
|
+
connections.append(self.connections[conn_id]) # noqa: PERF401
|
|
373
|
+
return connections
|
|
374
|
+
|
|
375
|
+
def get_all_outgoing_connections(self, node: BaseNode) -> list[Connection]:
|
|
376
|
+
"""Get all outgoing connections from a node.
|
|
377
|
+
|
|
378
|
+
Args:
|
|
379
|
+
node: The node to get outgoing connections for
|
|
380
|
+
|
|
381
|
+
Returns:
|
|
382
|
+
List of all outgoing connections from the node
|
|
383
|
+
"""
|
|
384
|
+
connections = []
|
|
385
|
+
if node.name in self.outgoing_index:
|
|
386
|
+
for connection_ids in self.outgoing_index[node.name].values():
|
|
387
|
+
for conn_id in connection_ids:
|
|
388
|
+
connections.append(self.connections[conn_id]) # noqa: PERF401, Keeping loop for understanding.
|
|
389
|
+
return connections
|
|
390
|
+
|
|
391
|
+
def get_all_incoming_connections(self, node: BaseNode) -> list[Connection]:
|
|
392
|
+
"""Get all incoming connections to a node.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
node: The node to get incoming connections for
|
|
396
|
+
|
|
397
|
+
Returns:
|
|
398
|
+
List of all incoming connections to the node
|
|
399
|
+
"""
|
|
400
|
+
connections = []
|
|
401
|
+
if node.name in self.incoming_index:
|
|
402
|
+
for connection_ids in self.incoming_index[node.name].values():
|
|
403
|
+
for conn_id in connection_ids:
|
|
404
|
+
connections.append(self.connections[conn_id]) # noqa: PERF401, Keeping loop for understanding.
|
|
405
|
+
return connections
|
|
@@ -479,10 +479,56 @@ class BaseNodeElement:
|
|
|
479
479
|
# No child handled it, return None (indicating no handler)
|
|
480
480
|
return None
|
|
481
481
|
|
|
482
|
+
def get_node(self) -> BaseNode | None:
|
|
483
|
+
"""Get the node context associated with this element.
|
|
484
|
+
|
|
485
|
+
Returns:
|
|
486
|
+
BaseNode | None: The parent node that owns this element, or None if no node context is set.
|
|
487
|
+
"""
|
|
488
|
+
return self._node_context
|
|
489
|
+
|
|
482
490
|
|
|
483
491
|
class UIOptionsMixin:
|
|
484
492
|
"""Mixin providing UI options update functionality for classes with ui_options."""
|
|
485
493
|
|
|
494
|
+
def _validate_ui_option_conflict(
|
|
495
|
+
self,
|
|
496
|
+
ui_options_dict: dict,
|
|
497
|
+
param_name: str,
|
|
498
|
+
param_value: Any,
|
|
499
|
+
) -> None:
|
|
500
|
+
"""Validate that explicit parameter doesn't conflict with ui_options dict.
|
|
501
|
+
|
|
502
|
+
Logs a warning if there's a conflict and the ui_options value will be used.
|
|
503
|
+
|
|
504
|
+
Args:
|
|
505
|
+
ui_options_dict: The ui_options dictionary to check
|
|
506
|
+
param_name: Name of the parameter (e.g., "hide", "markdown")
|
|
507
|
+
param_value: Value of the explicit parameter
|
|
508
|
+
"""
|
|
509
|
+
if param_name not in ui_options_dict:
|
|
510
|
+
return
|
|
511
|
+
|
|
512
|
+
dict_value = ui_options_dict[param_name]
|
|
513
|
+
|
|
514
|
+
if param_value != dict_value:
|
|
515
|
+
# Get element name for better error messages
|
|
516
|
+
element_name = getattr(self, "name", None)
|
|
517
|
+
class_name = self.__class__.__name__
|
|
518
|
+
|
|
519
|
+
# Build element part
|
|
520
|
+
if element_name:
|
|
521
|
+
element_part = f"{class_name} '{element_name}'"
|
|
522
|
+
else:
|
|
523
|
+
element_part = class_name
|
|
524
|
+
|
|
525
|
+
msg = (
|
|
526
|
+
f"{element_part}: Conflicting values for '{param_name}'. "
|
|
527
|
+
f'Explicit parameter {param_name}={param_value!r} conflicts with ui_options["{param_name}"]={dict_value!r}. '
|
|
528
|
+
f"The value from ui_options will be used. Please contact the library author to fix this issue."
|
|
529
|
+
)
|
|
530
|
+
logger.warning(msg)
|
|
531
|
+
|
|
486
532
|
def update_ui_options_key(self, key: str, value: Any) -> None:
|
|
487
533
|
"""Update a single UI option key."""
|
|
488
534
|
ui_options = self.ui_options
|
|
@@ -558,10 +604,15 @@ class ParameterMessage(BaseNodeElement, UIOptionsMixin):
|
|
|
558
604
|
button_variant: ButtonVariantType = "outline",
|
|
559
605
|
button_align: ButtonAlignType = "full-width",
|
|
560
606
|
full_width: bool = False,
|
|
607
|
+
markdown: bool | None = None,
|
|
608
|
+
hide: bool | None = None,
|
|
561
609
|
ui_options: dict | None = None,
|
|
562
610
|
traits: set[Trait.__class__ | Trait] | None = None,
|
|
563
611
|
**kwargs,
|
|
564
612
|
):
|
|
613
|
+
# Remove markdown and hide from kwargs to prevent passing them to parent class
|
|
614
|
+
kwargs.pop("markdown", None)
|
|
615
|
+
kwargs.pop("hide", None)
|
|
565
616
|
super().__init__(element_type=ParameterMessage.__name__, **kwargs)
|
|
566
617
|
self._variant = variant
|
|
567
618
|
self._title = title
|
|
@@ -575,6 +626,21 @@ class ParameterMessage(BaseNodeElement, UIOptionsMixin):
|
|
|
575
626
|
self._full_width = full_width
|
|
576
627
|
self._ui_options = ui_options or {}
|
|
577
628
|
|
|
629
|
+
# Validate that explicit parameters don't conflict with ui_options (only if not None)
|
|
630
|
+
if markdown is not None:
|
|
631
|
+
self._validate_ui_option_conflict(
|
|
632
|
+
ui_options_dict=self._ui_options, param_name="markdown", param_value=markdown
|
|
633
|
+
)
|
|
634
|
+
if hide is not None:
|
|
635
|
+
self._validate_ui_option_conflict(ui_options_dict=self._ui_options, param_name="hide", param_value=hide)
|
|
636
|
+
|
|
637
|
+
# Add common UI options if explicitly provided (not None) and NOT already in ui_options
|
|
638
|
+
# (ui_options always wins in case of conflict)
|
|
639
|
+
if markdown is not None and "markdown" not in self._ui_options:
|
|
640
|
+
self._ui_options["markdown"] = markdown
|
|
641
|
+
if hide is not None and "hide" not in self._ui_options:
|
|
642
|
+
self._ui_options["hide"] = hide
|
|
643
|
+
|
|
578
644
|
# Handle traits if provided
|
|
579
645
|
if traits:
|
|
580
646
|
for trait in traits:
|
|
@@ -676,6 +742,44 @@ class ParameterMessage(BaseNodeElement, UIOptionsMixin):
|
|
|
676
742
|
def button_align(self, value: ButtonAlignType) -> None:
|
|
677
743
|
self._button_align = value
|
|
678
744
|
|
|
745
|
+
@property
|
|
746
|
+
def markdown(self) -> bool:
|
|
747
|
+
"""Get whether markdown rendering is enabled.
|
|
748
|
+
|
|
749
|
+
Returns:
|
|
750
|
+
True if markdown rendering is enabled, False otherwise
|
|
751
|
+
"""
|
|
752
|
+
return self.ui_options.get("markdown", False)
|
|
753
|
+
|
|
754
|
+
@markdown.setter
|
|
755
|
+
@BaseNodeElement.emits_update_on_write
|
|
756
|
+
def markdown(self, value: bool) -> None:
|
|
757
|
+
"""Set whether to enable markdown rendering.
|
|
758
|
+
|
|
759
|
+
Args:
|
|
760
|
+
value: True to enable markdown rendering, False to disable it
|
|
761
|
+
"""
|
|
762
|
+
self.update_ui_options_key("markdown", value)
|
|
763
|
+
|
|
764
|
+
@property
|
|
765
|
+
def hide(self) -> bool:
|
|
766
|
+
"""Get whether the message is hidden in the UI.
|
|
767
|
+
|
|
768
|
+
Returns:
|
|
769
|
+
True if the message should be hidden, False otherwise
|
|
770
|
+
"""
|
|
771
|
+
return self.ui_options.get("hide", False)
|
|
772
|
+
|
|
773
|
+
@hide.setter
|
|
774
|
+
@BaseNodeElement.emits_update_on_write
|
|
775
|
+
def hide(self, value: bool) -> None:
|
|
776
|
+
"""Set whether to hide the message in the UI.
|
|
777
|
+
|
|
778
|
+
Args:
|
|
779
|
+
value: True to hide the message, False to show it
|
|
780
|
+
"""
|
|
781
|
+
self.update_ui_options_key("hide", value)
|
|
782
|
+
|
|
679
783
|
@property
|
|
680
784
|
def ui_options(self) -> dict:
|
|
681
785
|
return self._ui_options
|
|
@@ -907,6 +1011,103 @@ class ParameterGroup(BaseNodeElement, UIOptionsMixin):
|
|
|
907
1011
|
return super().remove_child(child)
|
|
908
1012
|
|
|
909
1013
|
|
|
1014
|
+
class ParameterButtonGroup(BaseNodeElement, UIOptionsMixin):
|
|
1015
|
+
"""UI element for grouping buttons together in a row (similar to shadcn ButtonGroup).
|
|
1016
|
+
|
|
1017
|
+
This class creates a button group container that displays buttons horizontally
|
|
1018
|
+
with proper spacing and styling, similar to shadcn/ui's ButtonGroup component.
|
|
1019
|
+
|
|
1020
|
+
Example:
|
|
1021
|
+
with ParameterButtonGroup(name="actions", orientation="horizontal") as button_group:
|
|
1022
|
+
ParameterButton(
|
|
1023
|
+
name="save",
|
|
1024
|
+
label="Save",
|
|
1025
|
+
variant="default",
|
|
1026
|
+
)
|
|
1027
|
+
ParameterButton(
|
|
1028
|
+
name="cancel",
|
|
1029
|
+
label="Cancel",
|
|
1030
|
+
variant="secondary",
|
|
1031
|
+
)
|
|
1032
|
+
"""
|
|
1033
|
+
|
|
1034
|
+
def __init__(
|
|
1035
|
+
self,
|
|
1036
|
+
name: str,
|
|
1037
|
+
ui_options: dict | None = None,
|
|
1038
|
+
*,
|
|
1039
|
+
orientation: Literal["horizontal", "vertical"] = "horizontal",
|
|
1040
|
+
**kwargs,
|
|
1041
|
+
):
|
|
1042
|
+
super().__init__(name=name, element_type="ParameterButtonGroup", **kwargs)
|
|
1043
|
+
if ui_options is None:
|
|
1044
|
+
ui_options = {}
|
|
1045
|
+
else:
|
|
1046
|
+
ui_options = ui_options.copy()
|
|
1047
|
+
|
|
1048
|
+
# Set button group specific UI options
|
|
1049
|
+
ui_options["button_group"] = True
|
|
1050
|
+
ui_options["orientation"] = orientation
|
|
1051
|
+
|
|
1052
|
+
self._ui_options = ui_options
|
|
1053
|
+
self._orientation: Literal["horizontal", "vertical"] = orientation
|
|
1054
|
+
|
|
1055
|
+
@property
|
|
1056
|
+
def ui_options(self) -> dict:
|
|
1057
|
+
return self._ui_options
|
|
1058
|
+
|
|
1059
|
+
@ui_options.setter
|
|
1060
|
+
@BaseNodeElement.emits_update_on_write
|
|
1061
|
+
def ui_options(self, value: dict) -> None:
|
|
1062
|
+
self._ui_options = value
|
|
1063
|
+
|
|
1064
|
+
@property
|
|
1065
|
+
def orientation(self) -> Literal["horizontal", "vertical"]:
|
|
1066
|
+
"""Get the button group orientation.
|
|
1067
|
+
|
|
1068
|
+
Returns:
|
|
1069
|
+
"horizontal" for buttons in a row, "vertical" for buttons in a column
|
|
1070
|
+
"""
|
|
1071
|
+
return self._orientation
|
|
1072
|
+
|
|
1073
|
+
@orientation.setter
|
|
1074
|
+
@BaseNodeElement.emits_update_on_write
|
|
1075
|
+
def orientation(self, value: Literal["horizontal", "vertical"]) -> None:
|
|
1076
|
+
"""Set the button group orientation.
|
|
1077
|
+
|
|
1078
|
+
Args:
|
|
1079
|
+
value: "horizontal" for buttons in a row, "vertical" for buttons in a column
|
|
1080
|
+
"""
|
|
1081
|
+
self._orientation = value
|
|
1082
|
+
self.update_ui_options_key("orientation", value)
|
|
1083
|
+
|
|
1084
|
+
def to_dict(self) -> dict[str, Any]:
|
|
1085
|
+
"""Returns a nested dictionary representation of this button group and its children."""
|
|
1086
|
+
our_dict = super().to_dict()
|
|
1087
|
+
our_dict["name"] = self.name
|
|
1088
|
+
our_dict["ui_options"] = self.ui_options
|
|
1089
|
+
return our_dict
|
|
1090
|
+
|
|
1091
|
+
def to_event(self, node: BaseNode) -> dict:
|
|
1092
|
+
event_data = super().to_event(node)
|
|
1093
|
+
event_data["ui_options"] = self.ui_options
|
|
1094
|
+
return event_data
|
|
1095
|
+
|
|
1096
|
+
def add_child(self, child: BaseNodeElement) -> None:
|
|
1097
|
+
child.parent_group_name = self.name
|
|
1098
|
+
return super().add_child(child)
|
|
1099
|
+
|
|
1100
|
+
def remove_child(self, child: BaseNodeElement | str) -> None:
|
|
1101
|
+
if isinstance(child, str):
|
|
1102
|
+
child_from_str = self.find_element_by_name(child)
|
|
1103
|
+
if child_from_str is not None and isinstance(child_from_str, BaseNodeElement):
|
|
1104
|
+
child_from_str.parent_group_name = None
|
|
1105
|
+
return super().remove_child(child_from_str)
|
|
1106
|
+
else:
|
|
1107
|
+
child.parent_group_name = None
|
|
1108
|
+
return super().remove_child(child)
|
|
1109
|
+
|
|
1110
|
+
|
|
910
1111
|
# TODO: https://github.com/griptape-ai/griptape-nodes/issues/856
|
|
911
1112
|
class ParameterBase(BaseNodeElement, ABC):
|
|
912
1113
|
@property
|
|
@@ -1012,9 +1213,9 @@ class Parameter(BaseNodeElement, UIOptionsMixin):
|
|
|
1012
1213
|
traits: set[Trait.__class__ | Trait] | None = None, # We are going to make these children.
|
|
1013
1214
|
ui_options: dict | None = None,
|
|
1014
1215
|
*,
|
|
1015
|
-
hide: bool =
|
|
1016
|
-
hide_label: bool =
|
|
1017
|
-
hide_property: bool =
|
|
1216
|
+
hide: bool | None = None,
|
|
1217
|
+
hide_label: bool | None = None,
|
|
1218
|
+
hide_property: bool | None = None,
|
|
1018
1219
|
allow_input: bool = True,
|
|
1019
1220
|
allow_property: bool = True,
|
|
1020
1221
|
allow_output: bool = True,
|
|
@@ -1092,12 +1293,25 @@ class Parameter(BaseNodeElement, UIOptionsMixin):
|
|
|
1092
1293
|
else:
|
|
1093
1294
|
self._ui_options = ui_options.copy()
|
|
1094
1295
|
|
|
1095
|
-
#
|
|
1096
|
-
if hide:
|
|
1296
|
+
# Validate that explicit parameters don't conflict with ui_options (only if not None)
|
|
1297
|
+
if hide is not None:
|
|
1298
|
+
self._validate_ui_option_conflict(ui_options_dict=self._ui_options, param_name="hide", param_value=hide)
|
|
1299
|
+
if hide_label is not None:
|
|
1300
|
+
self._validate_ui_option_conflict(
|
|
1301
|
+
ui_options_dict=self._ui_options, param_name="hide_label", param_value=hide_label
|
|
1302
|
+
)
|
|
1303
|
+
if hide_property is not None:
|
|
1304
|
+
self._validate_ui_option_conflict(
|
|
1305
|
+
ui_options_dict=self._ui_options, param_name="hide_property", param_value=hide_property
|
|
1306
|
+
)
|
|
1307
|
+
|
|
1308
|
+
# Add common UI options if explicitly provided (not None) and NOT already in ui_options
|
|
1309
|
+
# (ui_options always wins in case of conflict)
|
|
1310
|
+
if hide is not None and "hide" not in self._ui_options:
|
|
1097
1311
|
self._ui_options["hide"] = hide
|
|
1098
|
-
if hide_label:
|
|
1312
|
+
if hide_label is not None and "hide_label" not in self._ui_options:
|
|
1099
1313
|
self._ui_options["hide_label"] = hide_label
|
|
1100
|
-
if hide_property:
|
|
1314
|
+
if hide_property is not None and "hide_property" not in self._ui_options:
|
|
1101
1315
|
self._ui_options["hide_property"] = hide_property
|
|
1102
1316
|
if traits:
|
|
1103
1317
|
for trait in traits:
|
|
@@ -1753,6 +1967,7 @@ class ParameterContainer(Parameter, ABC):
|
|
|
1753
1967
|
converters: list[Callable[[Any], Any]] | None = None,
|
|
1754
1968
|
validators: list[Callable[[Parameter, Any], None]] | None = None,
|
|
1755
1969
|
*,
|
|
1970
|
+
hide: bool = False,
|
|
1756
1971
|
settable: bool = True,
|
|
1757
1972
|
user_defined: bool = False,
|
|
1758
1973
|
element_id: str | None = None,
|
|
@@ -1773,6 +1988,7 @@ class ParameterContainer(Parameter, ABC):
|
|
|
1773
1988
|
traits=traits,
|
|
1774
1989
|
converters=converters,
|
|
1775
1990
|
validators=validators,
|
|
1991
|
+
hide=hide,
|
|
1776
1992
|
settable=settable,
|
|
1777
1993
|
user_defined=user_defined,
|
|
1778
1994
|
element_id=element_id,
|
|
@@ -1821,6 +2037,7 @@ class ParameterList(ParameterContainer):
|
|
|
1821
2037
|
converters: list[Callable[[Any], Any]] | None = None,
|
|
1822
2038
|
validators: list[Callable[[Parameter, Any], None]] | None = None,
|
|
1823
2039
|
*,
|
|
2040
|
+
hide: bool = False,
|
|
1824
2041
|
settable: bool = True,
|
|
1825
2042
|
user_defined: bool = False,
|
|
1826
2043
|
element_id: str | None = None,
|
|
@@ -1860,6 +2077,7 @@ class ParameterList(ParameterContainer):
|
|
|
1860
2077
|
traits=traits,
|
|
1861
2078
|
converters=converters,
|
|
1862
2079
|
validators=validators,
|
|
2080
|
+
hide=hide,
|
|
1863
2081
|
settable=settable,
|
|
1864
2082
|
user_defined=user_defined,
|
|
1865
2083
|
element_id=element_id,
|
griptape_nodes/exe_types/flow.py
CHANGED
|
@@ -81,17 +81,17 @@ class ControlFlow:
|
|
|
81
81
|
def clear_execution_queue(self) -> None:
|
|
82
82
|
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
83
83
|
|
|
84
|
-
GriptapeNodes.FlowManager().clear_execution_queue()
|
|
84
|
+
GriptapeNodes.FlowManager().clear_execution_queue(self)
|
|
85
85
|
|
|
86
86
|
def get_connections_on_node(self, node: BaseNode) -> list[BaseNode] | None:
|
|
87
87
|
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
88
88
|
|
|
89
|
-
return GriptapeNodes.FlowManager().get_connections_on_node(
|
|
89
|
+
return GriptapeNodes.FlowManager().get_connections_on_node(node)
|
|
90
90
|
|
|
91
91
|
def get_all_connected_nodes(self, node: BaseNode) -> list[BaseNode]:
|
|
92
92
|
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
93
93
|
|
|
94
|
-
return GriptapeNodes.FlowManager().get_all_connected_nodes(
|
|
94
|
+
return GriptapeNodes.FlowManager().get_all_connected_nodes(node)
|
|
95
95
|
|
|
96
96
|
def get_node_dependencies(self, node: BaseNode) -> list[BaseNode]:
|
|
97
97
|
"""Get all upstream nodes that the given node depends on.
|