griptape-nodes 0.68.1__py3-none-any.whl → 0.70.0__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.
@@ -168,7 +168,11 @@ class Client:
168
168
  logger.debug("WebSocket client connected")
169
169
  except TimeoutError as e:
170
170
  logger.error("Failed to connect WebSocket client: timeout")
171
- msg = "Connection timeout"
171
+ msg = (
172
+ "Connection timeout - failed to connect to Nodes API. "
173
+ "This usually indicates an invalid or missing GT_CLOUD_API_KEY. "
174
+ "Please verify your API key is correct."
175
+ )
172
176
  raise ConnectionError(msg) from e
173
177
 
174
178
  async def _disconnect(self) -> None:
griptape_nodes/app/app.py CHANGED
@@ -3,11 +3,14 @@ from __future__ import annotations
3
3
  import asyncio
4
4
  import json
5
5
  import logging
6
+ import sys
6
7
  import threading
7
8
  from dataclasses import dataclass
8
9
 
10
+ from rich.align import Align
9
11
  from rich.console import Console
10
12
  from rich.logging import RichHandler
13
+ from rich.panel import Panel
11
14
 
12
15
  from griptape_nodes.api_client import Client
13
16
  from griptape_nodes.retained_mode.events import app_events, execution_events
@@ -113,6 +116,35 @@ logging.basicConfig(
113
116
  console = Console()
114
117
 
115
118
 
119
+ def _ensure_api_key() -> str:
120
+ """Verify that GT_CLOUD_API_KEY is set, exit with clear error message if not.
121
+
122
+ Returns:
123
+ The API key value
124
+
125
+ Raises:
126
+ SystemExit: If API key is missing or empty
127
+ """
128
+ secrets_manager = griptape_nodes.SecretsManager()
129
+ api_key = secrets_manager.get_secret("GT_CLOUD_API_KEY", should_error_on_not_found=False)
130
+
131
+ if not api_key:
132
+ message = Panel(
133
+ Align.center(
134
+ "[bold red]Nodes API key is not set, please run [code]gtn init[/code] with a valid key:[/bold red]\n"
135
+ "[code]gtn init --api-key <your key>[/code]\n\n"
136
+ "[bold red]You can generate a new key from [/bold red][bold blue][link=https://nodes.griptape.ai]https://nodes.griptape.ai[/link][/bold blue]",
137
+ ),
138
+ title="[red]X[/red] Missing Nodes API Key",
139
+ border_style="red",
140
+ padding=(1, 4),
141
+ )
142
+ console.print(message)
143
+ sys.exit(1)
144
+
145
+ return api_key
146
+
147
+
116
148
  def start_app() -> None:
117
149
  """Legacy sync entry point - runs async app."""
118
150
  try:
@@ -125,6 +157,9 @@ def start_app() -> None:
125
157
 
126
158
  async def astart_app() -> None:
127
159
  """New async app entry point."""
160
+ # Verify API key is set before starting
161
+ _ensure_api_key()
162
+
128
163
  # Initialize event queue in main thread
129
164
  griptape_nodes.EventManager().initialize_queue()
130
165
 
@@ -154,6 +189,22 @@ def _start_websocket_connection() -> None:
154
189
 
155
190
  # Run the async WebSocket tasks
156
191
  loop.run_until_complete(_run_websocket_tasks())
192
+ except ConnectionError:
193
+ # Connection failed - likely due to invalid/missing API key
194
+ message = Panel(
195
+ Align.center(
196
+ "[bold red]Failed to connect to Nodes API.[/bold red]\n\n"
197
+ "This usually indicates an invalid or missing [code]GT_CLOUD_API_KEY[/code].\n\n"
198
+ "[bold red]Please verify your API key:[/bold red]\n"
199
+ "[code]gtn init --api-key <your key>[/code]\n\n"
200
+ "[bold red]You can generate a new key from [/bold red][bold blue][link=https://nodes.griptape.ai]https://nodes.griptape.ai[/link][/bold blue]",
201
+ ),
202
+ title="[red]X[/red] Connection Failed",
203
+ border_style="red",
204
+ padding=(1, 4),
205
+ )
206
+ console.print(message)
207
+ sys.exit(1)
157
208
  except Exception as e:
158
209
  logger.error("WebSocket thread error: %s", e)
159
210
  raise
@@ -65,8 +65,7 @@ class BaseNodeGroup(BaseNode):
65
65
  Args:
66
66
  nodes: A list of nodes to add to this group
67
67
  """
68
- for node in nodes:
69
- self.nodes[node.name] = node
68
+ self._add_nodes_to_group_dict(nodes)
70
69
 
71
70
  node_names_in_group = set(self.nodes.keys())
72
71
  self.metadata["node_names_in_group"] = list(node_names_in_group)
@@ -93,3 +92,24 @@ class BaseNodeGroup(BaseNode):
93
92
  if node.name not in self.nodes:
94
93
  msg = f"Node {node.name} is not in node group {self.name}"
95
94
  raise ValueError(msg)
95
+
96
+ def handle_child_node_rename(self, old_name: str, new_name: str) -> None:
97
+ """Update group membership when a child node is renamed.
98
+
99
+ Args:
100
+ old_name: The old name of the child node
101
+ new_name: The new name of the child node
102
+ """
103
+ if old_name not in self.nodes:
104
+ return
105
+
106
+ # Update the nodes dictionary
107
+ node = self.nodes.pop(old_name)
108
+ self.nodes[new_name] = node
109
+
110
+ # Update the metadata
111
+ node_names_in_group = self.metadata.get("node_names_in_group", [])
112
+ if old_name in node_names_in_group:
113
+ node_names_in_group.remove(old_name)
114
+ node_names_in_group.append(new_name)
115
+ self.metadata["node_names_in_group"] = node_names_in_group
@@ -1623,7 +1623,7 @@ class EndNode(BaseNode):
1623
1623
  self,
1624
1624
  was_successful_modes={ParameterMode.PROPERTY},
1625
1625
  result_details_modes={ParameterMode.INPUT},
1626
- parameter_group_initially_collapsed=False,
1626
+ parameter_group_initially_collapsed=True,
1627
1627
  result_details_placeholder="Details about the completion or failure will be shown here.",
1628
1628
  )
1629
1629
 
@@ -83,6 +83,17 @@ class ExecutionStatusComponent:
83
83
  """
84
84
  return self._status_group
85
85
 
86
+ def _update_status_group_display_label(self, *, show_warning: bool) -> None:
87
+ """Update the Status ParameterGroup display label with optional warning emoji."""
88
+ if show_warning:
89
+ self._status_group.update_ui_options_key("display_name", "⚠️ Status")
90
+ return
91
+
92
+ # Remove display_name to use default "Status" from name
93
+ ui_options = self._status_group.ui_options.copy()
94
+ ui_options.pop("display_name", None)
95
+ self._status_group.ui_options = ui_options
96
+
86
97
  def set_execution_result(self, *, was_successful: bool, result_details: str) -> None:
87
98
  """Set the execution result values.
88
99
 
@@ -92,6 +103,16 @@ class ExecutionStatusComponent:
92
103
  """
93
104
  self._update_parameter_value(self._was_successful, was_successful)
94
105
  self._update_parameter_value(self._result_details, result_details)
106
+ # Update display label: show warning emoji only on actual failure (not placeholder/initialization messages)
107
+ # While string matching is not the best practice, it's currently the only way to check for placeholder messages.
108
+ # TODO: use a better approach to check for placeholder messages. https://github.com/griptape-ai/griptape-nodes/issues/3686
109
+ is_placeholder = result_details in (
110
+ "<Results will appear when the node executes>",
111
+ "Beginning execution...",
112
+ "",
113
+ )
114
+ show_warning = not was_successful and not is_placeholder
115
+ self._update_status_group_display_label(show_warning=show_warning)
95
116
 
96
117
  def clear_execution_status(self, initial_message: str | None = None) -> None:
97
118
  """Clear execution status and reset parameters.
@@ -102,6 +123,8 @@ class ExecutionStatusComponent:
102
123
  if initial_message is None:
103
124
  initial_message = ""
104
125
  self.set_execution_result(was_successful=False, result_details=initial_message)
126
+ # Reset to "Status" during execution (not a failure yet)
127
+ self._update_status_group_display_label(show_warning=False)
105
128
 
106
129
  def append_to_result_details(self, additional_text: str, separator: str = "\n") -> None:
107
130
  """Append text to the existing result_details.
@@ -20,7 +20,7 @@ class LogParameter:
20
20
  output_type="str",
21
21
  allowed_modes={ParameterMode.OUTPUT},
22
22
  tooltip="logs",
23
- ui_options={"multiline": True},
23
+ ui_options={"multiline": True, "placeholder_text": ""},
24
24
  )
25
25
  )
26
26
 
@@ -68,6 +68,10 @@ class StdoutCapture:
68
68
  def flush(self) -> None:
69
69
  self._original_stdout.flush()
70
70
 
71
+ def isatty(self) -> bool:
72
+ # Return False to prevent libraries from outputting ANSI color codes
73
+ return False
74
+
71
75
  def __enter__(self) -> "StdoutCapture":
72
76
  sys.stdout = self
73
77
  return self
@@ -3,6 +3,8 @@ from typing import Any
3
3
 
4
4
  from griptape_nodes.exe_types.core_types import Parameter, ParameterMode
5
5
  from griptape_nodes.exe_types.node_types import BaseNode
6
+ from griptape_nodes.exe_types.param_types.parameter_bool import ParameterBool
7
+ from griptape_nodes.exe_types.param_types.parameter_int import ParameterInt
6
8
 
7
9
 
8
10
  class SeedParameter:
@@ -10,24 +12,20 @@ class SeedParameter:
10
12
  self._node = node
11
13
  self._max_seed = max_seed
12
14
 
13
- def add_input_parameters(self) -> None:
14
- self._node.add_parameter(
15
- Parameter(
16
- name="randomize_seed",
17
- type="bool",
18
- output_type="bool",
19
- tooltip="randomize the seed on each run",
20
- default_value=False,
21
- )
15
+ def add_input_parameters(self, *, inside_param_group: bool = False) -> None:
16
+ randomize_seed_parameter = ParameterBool(
17
+ name="randomize_seed",
18
+ tooltip="randomize the seed on each run",
19
+ default_value=False,
22
20
  )
23
- self._node.add_parameter(
24
- Parameter(
25
- name="seed",
26
- type="int",
27
- tooltip="seed",
28
- default_value=42,
29
- )
21
+ seed_parameter = ParameterInt(
22
+ name="seed",
23
+ tooltip="the seed to use for the generation",
24
+ default_value=42,
30
25
  )
26
+ if not inside_param_group:
27
+ self._node.add_parameter(randomize_seed_parameter)
28
+ self._node.add_parameter(seed_parameter)
31
29
 
32
30
  def remove_input_parameters(self) -> None:
33
31
  self._node.remove_parameter_element_by_name("randomize_seed")
@@ -390,8 +390,6 @@ class GetAllNodeInfoResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure)
390
390
  class SetLockNodeStateRequest(WorkflowNotAlteredMixin, RequestPayload):
391
391
  """Lock a node.
392
392
 
393
- Use when: Implementing locking functionality, preventing changes to nodes.
394
-
395
393
  Args:
396
394
  node_name: Name of the node to lock
397
395
  lock: Whether to lock or unlock the node. If true, the node will be locked, otherwise it will be unlocked.
@@ -418,6 +416,39 @@ class SetLockNodeStateResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailur
418
416
  """Node failed to lock."""
419
417
 
420
418
 
419
+ @dataclass
420
+ @PayloadRegistry.register
421
+ class BatchSetNodeLockStateRequest(WorkflowNotAlteredMixin, RequestPayload):
422
+ """Set lock state for multiple nodes in a single request.
423
+
424
+ Use when: Locking or unlocking multiple nodes at once, consistent with BatchSetNodeMetadataRequest.
425
+
426
+ Args:
427
+ node_names: Names of nodes to lock/unlock.
428
+ lock: Whether to lock (True) or unlock (False) the nodes.
429
+
430
+ Results: BatchSetNodeLockStateResultSuccess | BatchSetNodeLockStateResultFailure
431
+ """
432
+
433
+ node_names: list[str]
434
+ lock: bool
435
+
436
+
437
+ @dataclass
438
+ @PayloadRegistry.register
439
+ class BatchSetNodeLockStateResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
440
+ """Batch node lock state update completed successfully."""
441
+
442
+ updated_nodes: list[str]
443
+ failed_nodes: dict[str, str] = field(default_factory=dict)
444
+
445
+
446
+ @dataclass
447
+ @PayloadRegistry.register
448
+ class BatchSetNodeLockStateResultFailure(ResultPayloadFailure):
449
+ """Batch node lock state update failed. Common causes: all nodes not found."""
450
+
451
+
421
452
  # A Node's state can be serialized to a sequence of commands that the engine runs.
422
453
  @dataclass
423
454
  class SerializedNodeCommands:
@@ -533,6 +564,7 @@ class SerializeNodeToCommandsRequest(RequestPayload):
533
564
  serialized_parameter_value_tracker: SerializedParameterValueTracker = field(
534
565
  default_factory=SerializedParameterValueTracker
535
566
  )
567
+ use_pickling: bool = False
536
568
 
537
569
 
538
570
  @dataclass
@@ -617,7 +649,8 @@ class SerializeSelectedNodesToCommandsResultSuccess(WorkflowNotAlteredMixin, Res
617
649
 
618
650
  # They will be passed with node_name, timestamp
619
651
  # Could be a flow command if it's all nodes in a flow.
620
- serialized_selected_node_commands: SerializedSelectedNodesCommands
652
+ serialized_selected_node_commands: str
653
+ pickled_values: dict[str, str]
621
654
 
622
655
 
623
656
  @dataclass
@@ -643,6 +676,8 @@ class DeserializeSelectedNodesFromCommandsRequest(WorkflowNotAlteredMixin, Reque
643
676
  Results: DeserializeSelectedNodesFromCommandsResultSuccess (with node names) | DeserializeSelectedNodesFromCommandsResultFailure (deserialization error)
644
677
  """
645
678
 
679
+ deserialize_commands: str
680
+ pickled_values: dict[str, str]
646
681
  positions: list[NewPosition] | None = None
647
682
 
648
683
 
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- from typing import TYPE_CHECKING, Any
4
+ from typing import TYPE_CHECKING
5
5
 
6
6
  from griptape_nodes.exe_types.flow import ControlFlow
7
7
  from griptape_nodes.retained_mode.events.context_events import (
@@ -18,8 +18,6 @@ if TYPE_CHECKING:
18
18
  from griptape_nodes.exe_types.core_types import BaseNodeElement
19
19
  from griptape_nodes.exe_types.node_types import BaseNode
20
20
  from griptape_nodes.retained_mode.events.base_events import ResultPayload
21
- from griptape_nodes.retained_mode.events.flow_events import SerializedFlowCommands
22
- from griptape_nodes.retained_mode.events.node_events import SerializedSelectedNodesCommands
23
21
  from griptape_nodes.retained_mode.managers.event_manager import EventManager
24
22
 
25
23
  logger = logging.getLogger("griptape_nodes")
@@ -34,7 +32,6 @@ class ContextManager:
34
32
  """
35
33
 
36
34
  _workflow_stack: list[ContextManager.WorkflowContextState]
37
- _clipboard: ClipBoard
38
35
 
39
36
  class WorkflowContextError(Exception):
40
37
  """Base exception for workflow context errors."""
@@ -242,32 +239,9 @@ class ContextManager:
242
239
  ) -> None:
243
240
  self._manager.pop_element()
244
241
 
245
- class ClipBoard:
246
- """Keeps Commands for Copying or Pasting."""
247
-
248
- # Contains flow, node, parameter, connections
249
- flow_commands: SerializedFlowCommands | None
250
- # Contains node and Parameter and relevant connections
251
- node_commands: SerializedSelectedNodesCommands | None
252
- parameter_uuid_to_values: dict[str, Any] | None
253
-
254
- def __init__(self) -> None:
255
- self.flow_commands = None
256
- self.node_commands = None
257
- self.parameter_uuid_to_values = None
258
-
259
- def clear(self) -> None:
260
- del self.flow_commands
261
- self.flow_commands = None
262
- del self.node_commands
263
- self.node_commands = None
264
- if self.parameter_uuid_to_values is not None:
265
- self.parameter_uuid_to_values.clear()
266
-
267
242
  def __init__(self, event_manager: EventManager) -> None:
268
243
  """Initialize the context manager with empty workflow and flow stacks."""
269
244
  self._workflow_stack = []
270
- self._clipboard = self.ClipBoard()
271
245
  event_manager.assign_manager_to_request_type(
272
246
  request_type=SetWorkflowContextRequest, callback=self.on_set_workflow_context_request
273
247
  )
@@ -17,7 +17,7 @@ from griptape_nodes.exe_types.core_types import (
17
17
  ParameterTypeBuiltin,
18
18
  )
19
19
  from griptape_nodes.exe_types.flow import ControlFlow
20
- from griptape_nodes.exe_types.node_groups import SubflowNodeGroup
20
+ from griptape_nodes.exe_types.node_groups import BaseNodeGroup, SubflowNodeGroup
21
21
  from griptape_nodes.exe_types.node_types import (
22
22
  BaseNode,
23
23
  ErrorProxyNode,
@@ -1530,9 +1530,9 @@ class FlowManager:
1530
1530
  if node_name is not None:
1531
1531
  node_name_to_uuid[node_name] = serialize_result.serialized_node_commands.node_uuid
1532
1532
 
1533
- # SubflowNodeGroups must be serialized LAST because they reference child node names via node_names_to_add
1533
+ # BaseNodeGroups must be serialized LAST because they reference child node names via node_names_to_add
1534
1534
  # If we deserialize a NodeGroup before its children, the child nodes won't exist yet
1535
- if isinstance(node, SubflowNodeGroup):
1535
+ if isinstance(node, BaseNodeGroup):
1536
1536
  serialized_node_group_commands.append(serialize_result.serialized_node_commands)
1537
1537
  else:
1538
1538
  serialized_node_commands.append(serialize_result.serialized_node_commands)
@@ -1543,7 +1543,7 @@ class FlowManager:
1543
1543
  serialize_result.set_parameter_value_commands
1544
1544
  )
1545
1545
 
1546
- # Update SubflowNodeGroup commands to use UUIDs instead of names in node_names_to_add
1546
+ # Update BaseNodeGroup commands to use UUIDs instead of names in node_names_to_add
1547
1547
  # This allows workflow generation to directly look up variable names from UUIDs
1548
1548
 
1549
1549
  for node_group_command in serialized_node_group_commands:
@@ -3224,9 +3224,9 @@ class FlowManager:
3224
3224
  # Store the serialized node's UUID for correlation to connections and setting parameter values later.
3225
3225
  node_name_to_uuid[node_name] = serialized_node.node_uuid
3226
3226
 
3227
- # SubflowNodeGroups must be serialized LAST because CreateNodeGroupRequest references child node names
3227
+ # BaseNodeGroups must be serialized LAST because CreateNodeGroupRequest references child node names
3228
3228
  # If we deserialize a NodeGroup before its children, the child nodes won't exist yet
3229
- if isinstance(node, SubflowNodeGroup):
3229
+ if isinstance(node, BaseNodeGroup):
3230
3230
  serialized_node_group_commands.append(serialized_node)
3231
3231
  else:
3232
3232
  serialized_node_commands.append(serialized_node)
@@ -3299,7 +3299,7 @@ class FlowManager:
3299
3299
  # This ensures child nodes exist before their parent NodeGroups are created during deserialization
3300
3300
  serialized_node_commands.extend(serialized_node_group_commands)
3301
3301
 
3302
- # Update SubflowNodeGroup commands to use UUIDs instead of names in node_names_to_add
3302
+ # Update BaseNodeGroup commands to use UUIDs instead of names in node_names_to_add
3303
3303
  # This allows workflow generation to directly look up variable names from UUIDs
3304
3304
  # Build a complete node name to UUID map including nodes from all subflows
3305
3305
  complete_node_name_to_uuid = dict(node_name_to_uuid) # Start with current flow's nodes
@@ -70,6 +70,9 @@ from griptape_nodes.retained_mode.events.node_events import (
70
70
  AddNodesToNodeGroupRequest,
71
71
  AddNodesToNodeGroupResultFailure,
72
72
  AddNodesToNodeGroupResultSuccess,
73
+ BatchSetNodeLockStateRequest,
74
+ BatchSetNodeLockStateResultFailure,
75
+ BatchSetNodeLockStateResultSuccess,
73
76
  BatchSetNodeMetadataRequest,
74
77
  BatchSetNodeMetadataResultFailure,
75
78
  BatchSetNodeMetadataResultSuccess,
@@ -292,6 +295,9 @@ class NodeManager:
292
295
  CanResetNodeToDefaultsRequest, self.on_can_reset_node_to_defaults_request
293
296
  )
294
297
  event_manager.assign_manager_to_request_type(ResetNodeToDefaultsRequest, self.on_reset_node_to_defaults_request)
298
+ event_manager.assign_manager_to_request_type(
299
+ BatchSetNodeLockStateRequest, self.on_batch_set_lock_node_state_request
300
+ )
295
301
 
296
302
  def handle_node_rename(self, old_name: str, new_name: str) -> None:
297
303
  # Get the node itself
@@ -317,6 +323,12 @@ class NodeManager:
317
323
  connection.source_node.name = new_name
318
324
  temp = connections.outgoing_index.pop(old_name)
319
325
  connections.outgoing_index[new_name] = temp
326
+
327
+ # Update parent group membership if node belongs to a group
328
+ parent_group = node.parent_group
329
+ if parent_group is not None and isinstance(parent_group, BaseNodeGroup):
330
+ parent_group.handle_child_node_rename(old_name, new_name)
331
+
320
332
  # update the node in the flow!
321
333
  flow.remove_node(old_name)
322
334
  node.name = new_name
@@ -2631,9 +2643,8 @@ class NodeManager:
2631
2643
  library_version = library_metadata_result.metadata.library_version
2632
2644
  library_details = LibraryNameAndVersion(library_name=library_used, library_version=library_version)
2633
2645
 
2634
- # Handle SubflowNodeGroup specially - serialize like normal nodes but preserve node group behavior
2635
- if isinstance(node, SubflowNodeGroup):
2636
- # For non-SubflowNodeGroup, library_details should always be set
2646
+ # Handle BaseNodeGroup specially - serialize like normal nodes but preserve node group behavior
2647
+ if isinstance(node, BaseNodeGroup):
2637
2648
  if library_details is None:
2638
2649
  details = f"Attempted to serialize Node '{node_name}' to commands. Library details missing."
2639
2650
  return SerializeNodeToCommandsResultFailure(result_details=details)
@@ -2651,7 +2662,6 @@ class NodeManager:
2651
2662
  metadata=metadata_copy,
2652
2663
  )
2653
2664
  else:
2654
- # For non-SubflowNodeGroup, library_details should always be set
2655
2665
  if library_details is None:
2656
2666
  details = f"Attempted to serialize Node '{node_name}' to commands. Library details missing."
2657
2667
  return SerializeNodeToCommandsResultFailure(result_details=details)
@@ -2771,6 +2781,7 @@ class NodeManager:
2771
2781
  unique_parameter_uuid_to_values=request.unique_parameter_uuid_to_values,
2772
2782
  serialized_parameter_value_tracker=request.serialized_parameter_value_tracker,
2773
2783
  create_node_request=create_node_request,
2784
+ use_pickling=request.use_pickling,
2774
2785
  )
2775
2786
  if set_param_value_requests is not None:
2776
2787
  set_value_commands.extend(set_param_value_requests)
@@ -2973,6 +2984,7 @@ class NodeManager:
2973
2984
  node_name=node_name,
2974
2985
  unique_parameter_uuid_to_values=unique_uuid_to_values,
2975
2986
  serialized_parameter_value_tracker=serialized_parameter_value_tracker,
2987
+ use_pickling=True,
2976
2988
  )
2977
2989
  )
2978
2990
  if not isinstance(result, SerializeNodeToCommandsResultSuccess):
@@ -3019,22 +3031,55 @@ class NodeManager:
3019
3031
  set_parameter_value_commands=parameter_commands,
3020
3032
  set_lock_commands_per_node=lock_commands,
3021
3033
  )
3022
- # Set everything in the clipboard if requested
3023
- if request.copy_to_clipboard:
3024
- GriptapeNodes.ContextManager()._clipboard.node_commands = final_result
3025
- GriptapeNodes.ContextManager()._clipboard.parameter_uuid_to_values = unique_uuid_to_values
3034
+
3035
+ # Encode pickled bytes to latin-1 strings for JSON serialization
3036
+ encoded_values = {}
3037
+ for uuid, value in unique_uuid_to_values.items():
3038
+ if isinstance(value, bytes):
3039
+ # Pickled bytes - encode as latin-1 string for transport
3040
+ encoded_values[uuid] = value.decode("latin1")
3041
+ else:
3042
+ # Non-pickled value - keep as-is (for backward compatibility)
3043
+ encoded_values[uuid] = value
3044
+
3045
+ # Pickle the commands object and encode as latin-1 string for transport
3046
+ pickled_commands_bytes = pickle.dumps(final_result)
3047
+ pickled_commands_string = pickled_commands_bytes.decode("latin1")
3026
3048
  return SerializeSelectedNodesToCommandsResultSuccess(
3027
- final_result,
3049
+ pickled_commands_string, # Send pickled string instead of object
3050
+ pickled_values=encoded_values,
3028
3051
  result_details=f"Successfully serialized {len(request.nodes_to_serialize)} selected nodes to commands.",
3029
3052
  )
3030
3053
 
3031
- def on_deserialize_selected_nodes_from_commands( # noqa: C901, PLR0912
3054
+ def on_deserialize_selected_nodes_from_commands( # noqa: C901, PLR0912, PLR0915
3032
3055
  self,
3033
3056
  request: DeserializeSelectedNodesFromCommandsRequest,
3034
3057
  ) -> ResultPayload:
3035
- commands = GriptapeNodes.ContextManager()._clipboard.node_commands
3036
- if commands is None:
3037
- return DeserializeSelectedNodesFromCommandsResultFailure(result_details="No Node Commands Found")
3058
+ # Decode latin-1 encoded pickled strings back to Python objects
3059
+ decoded_values = {}
3060
+ if request.pickled_values:
3061
+ for uuid, latin1_string in request.pickled_values.items():
3062
+ if isinstance(latin1_string, str):
3063
+ try:
3064
+ # Decode: latin-1 string → bytes → unpickled object
3065
+ pickled_bytes = latin1_string.encode("latin1")
3066
+ decoded_values[uuid] = pickle.loads(pickled_bytes) # noqa: S301 Expecting this from the GUI.
3067
+ except Exception:
3068
+ details = f"Failed to unpickle parameter value for UUID {uuid}"
3069
+ logger.warning(details)
3070
+ # Keep original value if unpickling fails
3071
+ decoded_values[uuid] = latin1_string
3072
+ else:
3073
+ # Not a string, keep as-is
3074
+ decoded_values[uuid] = latin1_string
3075
+
3076
+ # Unpickle the commands string into SerializedSelectedNodesCommands
3077
+ try:
3078
+ pickled_commands_bytes = request.deserialize_commands.encode("latin1")
3079
+ commands = pickle.loads(pickled_commands_bytes) # noqa: S301 Expecting this from the GUI.
3080
+ except Exception as e:
3081
+ details = f"Failed to unpickle commands: {e}"
3082
+ return DeserializeSelectedNodesFromCommandsResultFailure(result_details=details)
3038
3083
  connections = commands.serialized_connection_commands
3039
3084
  node_uuid_to_name = {}
3040
3085
  # Enumerate because positions is in the same order as the node commands.
@@ -3068,10 +3113,9 @@ class NodeManager:
3068
3113
  param_request = parameter_command.set_parameter_value_command
3069
3114
  # Set the Node name
3070
3115
  param_request.node_name = result.node_name
3071
- # Set the new value
3072
- table = GriptapeNodes.ContextManager()._clipboard.parameter_uuid_to_values
3073
- if table and parameter_command.unique_value_uuid in table:
3074
- value = table[parameter_command.unique_value_uuid]
3116
+ # Set the new value from decoded_values
3117
+ if decoded_values and parameter_command.unique_value_uuid in decoded_values:
3118
+ value = decoded_values[parameter_command.unique_value_uuid]
3075
3119
  # Using try-except-pass instead of contextlib.suppress because it's clearer.
3076
3120
  try: # noqa: SIM105
3077
3121
  # If we're pasting multiple times - we need to create a new copy for each paste so they don't all have the same reference.
@@ -3109,13 +3153,20 @@ class NodeManager:
3109
3153
  )
3110
3154
 
3111
3155
  def on_duplicate_selected_nodes(self, request: DuplicateSelectedNodesRequest) -> ResultPayload:
3112
- result = GriptapeNodes.handle_request(
3156
+ serialize_result = GriptapeNodes.handle_request(
3113
3157
  SerializeSelectedNodesToCommandsRequest(nodes_to_serialize=request.nodes_to_duplicate)
3114
3158
  )
3115
- if result.failed():
3159
+ if not isinstance(serialize_result, SerializeSelectedNodesToCommandsResultSuccess):
3116
3160
  details = "Failed to serialized selected nodes."
3117
3161
  return DuplicateSelectedNodesResultFailure(result_details=details)
3118
- result = GriptapeNodes.handle_request(DeserializeSelectedNodesFromCommandsRequest(positions=request.positions))
3162
+
3163
+ # Pass the pickled commands and values to deserialization
3164
+ deserialize_request = DeserializeSelectedNodesFromCommandsRequest(
3165
+ deserialize_commands=serialize_result.serialized_selected_node_commands,
3166
+ pickled_values=serialize_result.pickled_values,
3167
+ positions=request.positions,
3168
+ )
3169
+ result = GriptapeNodes.handle_request(deserialize_request)
3119
3170
  if not isinstance(result, DeserializeSelectedNodesFromCommandsResultSuccess):
3120
3171
  details = "Failed to deserialize selected nodes."
3121
3172
  return DuplicateSelectedNodesResultFailure(result_details=details)
@@ -3167,6 +3218,7 @@ class NodeManager:
3167
3218
  node_name: str,
3168
3219
  *,
3169
3220
  is_output: bool,
3221
+ use_pickling: bool = False,
3170
3222
  ) -> SerializedNodeCommands.IndirectSetParameterValueCommand | None:
3171
3223
  try:
3172
3224
  hash(value)
@@ -3201,12 +3253,20 @@ class NodeManager:
3201
3253
  return None
3202
3254
  # The value should be serialized. Add it to the map of uniques.
3203
3255
  unique_uuid = SerializedNodeCommands.UniqueParameterValueUUID(str(uuid4()))
3204
- try:
3205
- unique_parameter_uuid_to_values[unique_uuid] = copy.deepcopy(value)
3206
- except Exception:
3207
- details = f"Attempted to serialize parameter '{parameter_name}` on node '{node_name}'. The parameter value could not be copied. It will be serialized by value. If problems arise from this, ensure the type '{type(value)}' works with copy.deepcopy()."
3208
- logger.warning(details)
3209
- unique_parameter_uuid_to_values[unique_uuid] = value
3256
+
3257
+ if use_pickling:
3258
+ # Use pickle serialization via WorkflowManager
3259
+ workflow_manager = GriptapeNodes.WorkflowManager()
3260
+ pickled_bytes = workflow_manager._patch_and_pickle_object(value)
3261
+ unique_parameter_uuid_to_values[unique_uuid] = pickled_bytes
3262
+ else:
3263
+ # Use existing deep copy approach
3264
+ try:
3265
+ unique_parameter_uuid_to_values[unique_uuid] = copy.deepcopy(value)
3266
+ except Exception:
3267
+ details = f"Attempted to serialize parameter '{parameter_name}` on node '{node_name}'. The parameter value could not be copied. It will be serialized by value. If problems arise from this, ensure the type '{type(value)}' works with copy.deepcopy()."
3268
+ logger.warning(details)
3269
+ unique_parameter_uuid_to_values[unique_uuid] = value
3210
3270
  serialized_parameter_value_tracker.add_as_serializable(value_id, unique_uuid)
3211
3271
 
3212
3272
  # Serialize it
@@ -3223,12 +3283,14 @@ class NodeManager:
3223
3283
  return indirect_set_value_command
3224
3284
 
3225
3285
  @staticmethod
3226
- def handle_parameter_value_saving(
3286
+ def handle_parameter_value_saving( # noqa: PLR0913
3227
3287
  parameter: Parameter,
3228
3288
  node: BaseNode,
3229
3289
  unique_parameter_uuid_to_values: dict[SerializedNodeCommands.UniqueParameterValueUUID, Any],
3230
3290
  serialized_parameter_value_tracker: SerializedParameterValueTracker,
3231
3291
  create_node_request: CreateNodeRequest,
3292
+ *,
3293
+ use_pickling: bool = False,
3232
3294
  ) -> list[SerializedNodeCommands.IndirectSetParameterValueCommand] | None:
3233
3295
  """Generates code to save a parameter value for a node in a Griptape workflow.
3234
3296
 
@@ -3246,6 +3308,7 @@ class NodeManager:
3246
3308
  unique_parameter_uuid_to_values (dict[SerializedNodeCommands.UniqueParameterValueUUID, Any]): Dictionary mapping unique value UUIDs to values
3247
3309
  serialized_parameter_value_tracker (SerializedParameterValueTracker): Object mapping maintaining value hashes to unique value UUIDs, and non-serializable values
3248
3310
  create_node_request (CreateNodeRequest): The node creation request that will be modified if serialization fails
3311
+ use_pickling (bool): If True, use pickle-based serialization; if False, use deep copy
3249
3312
 
3250
3313
  Returns:
3251
3314
  None (if no value to be serialized) or an IndirectSetParameterValueCommand linking the value to the unique value map
@@ -3282,6 +3345,7 @@ class NodeManager:
3282
3345
  is_output=False,
3283
3346
  parameter_name=parameter.name,
3284
3347
  node_name=node.name,
3348
+ use_pickling=use_pickling,
3285
3349
  )
3286
3350
  if internal_command is None:
3287
3351
  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."
@@ -3300,6 +3364,7 @@ class NodeManager:
3300
3364
  is_output=True,
3301
3365
  parameter_name=parameter.name,
3302
3366
  node_name=node.name,
3367
+ use_pickling=use_pickling,
3303
3368
  )
3304
3369
  if output_command is None:
3305
3370
  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."
@@ -3588,6 +3653,30 @@ class NodeManager:
3588
3653
  result_details=f"Successfully set lock state to {node.lock} for node '{node_name}'.",
3589
3654
  )
3590
3655
 
3656
+ def on_batch_set_lock_node_state_request(self, request: BatchSetNodeLockStateRequest) -> ResultPayload:
3657
+ updated: list[str] = []
3658
+ failed: dict[str, str] = {}
3659
+ for name in request.node_names:
3660
+ try:
3661
+ node = self.get_node_by_name(name)
3662
+ except ValueError as err:
3663
+ failed[name] = f"Node not found. Error: {err}"
3664
+ continue
3665
+ node.lock = request.lock
3666
+ updated.append(name)
3667
+
3668
+ if not updated:
3669
+ details = f"Failed to update any nodes. Failed: {failed}"
3670
+ return BatchSetNodeLockStateResultFailure(result_details=details)
3671
+ details = f"Successfully set lock state to {request.lock} for nodes: {', '.join(updated)}." + (
3672
+ f" Failed: {failed}" if failed else ""
3673
+ )
3674
+ return BatchSetNodeLockStateResultSuccess(
3675
+ updated_nodes=updated,
3676
+ failed_nodes=failed,
3677
+ result_details=details,
3678
+ )
3679
+
3591
3680
  def on_send_node_message_request(self, request: SendNodeMessageRequest) -> ResultPayload:
3592
3681
  """Handle a SendNodeMessageRequest by calling the node's message callback.
3593
3682
 
@@ -149,7 +149,6 @@ class ObjectManager:
149
149
  context_mgr.pop_node()
150
150
  context_mgr.pop_flow()
151
151
  context_mgr.pop_workflow()
152
- context_mgr._clipboard.clear()
153
152
 
154
153
  # Clear all local workflow variables
155
154
  GriptapeNodes.VariablesManager().on_clear_object_state()
@@ -44,6 +44,9 @@ from griptape_nodes.retained_mode.events.library_events import (
44
44
  ListRegisteredLibrariesRequest,
45
45
  )
46
46
  from griptape_nodes.retained_mode.events.node_events import (
47
+ BatchSetNodeLockStateRequest,
48
+ BatchSetNodeLockStateResultFailure,
49
+ BatchSetNodeLockStateResultSuccess,
47
50
  CreateNodeRequest,
48
51
  CreateNodeResultFailure,
49
52
  DeleteNodeRequest,
@@ -388,6 +391,20 @@ class RetainedMode:
388
391
  result = GriptapeNodes().handle_request(request)
389
392
  return result
390
393
 
394
+ @classmethod
395
+ def batch_set_lock_node_state(
396
+ cls, *, node_names: list[str], lock: bool = True
397
+ ) -> BatchSetNodeLockStateResultSuccess | BatchSetNodeLockStateResultFailure:
398
+ """Sets the lock state of multiple nodes.
399
+
400
+ Args:
401
+ node_names (list[str]): Names of nodes to lock/unlock.
402
+ lock (bool): Whether to lock (True) or unlock (False) the nodes.
403
+ """
404
+ request = BatchSetNodeLockStateRequest(node_names=node_names, lock=lock)
405
+ result = GriptapeNodes().handle_request(request)
406
+ return result
407
+
391
408
  @classmethod
392
409
  def get_connections_for_node(cls, node_name: str) -> ResultPayload:
393
410
  """Gets all connections associated with a node.
@@ -1,13 +1,13 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: griptape-nodes
3
- Version: 0.68.1
3
+ Version: 0.70.0
4
4
  Summary: Add your description here
5
- Requires-Dist: griptape>=1.8.12
5
+ Requires-Dist: griptape>=1.9.0
6
6
  Requires-Dist: pydantic>=2.10.6
7
7
  Requires-Dist: python-dotenv>=1.0.1
8
8
  Requires-Dist: xdg-base-dirs>=6.0.2
9
9
  Requires-Dist: httpx>=0.28.0,<1.0.0
10
- Requires-Dist: websockets>=15.0.1,<16.0.0
10
+ Requires-Dist: websockets>=15.0.1,<17.0.0
11
11
  Requires-Dist: tomlkit>=0.13.2
12
12
  Requires-Dist: uv>=0.6.16
13
13
  Requires-Dist: fastapi>=0.115.12
@@ -1,11 +1,11 @@
1
1
  griptape_nodes/__init__.py,sha256=WxWjICLxwuyZGDpPUyCmj047GbN7PIspi4YMWgmrrQc,671
2
2
  griptape_nodes/__main__.py,sha256=fJhor6_1A27abwbnceyNheONP1iXkPzjjsV5jBEfF2M,144
3
3
  griptape_nodes/api_client/__init__.py,sha256=9iyLcPbcXjxxLhhCSYQ5zgvlwxceyrNXDE8YhOIVH8g,216
4
- griptape_nodes/api_client/client.py,sha256=3qmTLMeVTFeyO-XlxTUUWwsEgG41R8swE64EccotaJQ,9664
4
+ griptape_nodes/api_client/client.py,sha256=fNTs-xJL8deRWceGKBGOCeKwMt6Rd0vi-jCxniZk79E,9870
5
5
  griptape_nodes/api_client/request_client.py,sha256=Z5lJVoG2a9hudTAQTnniaCHbBko0X1FyoakiQMimq7g,9713
6
6
  griptape_nodes/app/.python-version,sha256=e1X45ntWI8S-8_ppEojalDfXnTq6FW3kjUgdsyrH0W0,5
7
7
  griptape_nodes/app/__init__.py,sha256=DB-DTsgcNnbmEClXEouwzGhrmo3gHBCWXB9BkPGpdQI,90
8
- griptape_nodes/app/app.py,sha256=48q09c2xDjvbf4Qa1_buQhnPjR2jqkXo428GeBRkw8g,16075
8
+ griptape_nodes/app/app.py,sha256=bbaRe1khQNRZ1fziDdeokkR-M3K7tvKDpCl4eVm-R4g,18014
9
9
  griptape_nodes/app/watch.py,sha256=hKVP_SuV9C17bH1h9o4uIVTKH-IL_-0iyHaNYmILTWU,1594
10
10
  griptape_nodes/bootstrap/__init__.py,sha256=ENv3SIzQ9TtlRrg1y4e4CnoBpJaFpFSkNpTFBV8X5Ls,25
11
11
  griptape_nodes/bootstrap/utils/__init__.py,sha256=tlNEApJLZazcBNhxkTdup4URwznnz4nZxjSaRfFrTBM,31
@@ -69,24 +69,24 @@ griptape_nodes/exe_types/core_types.py,sha256=XrMT0Rf0bPZBcx06f1GUTi5G_IwLgdyzGj
69
69
  griptape_nodes/exe_types/flow.py,sha256=2iAh3vN5wnMVxTc5jcPBg9TSiASq1DGIm5jgpO9Bdq4,5732
70
70
  griptape_nodes/exe_types/node_groups/__init__.py,sha256=u91XCSR4OAAr6x5kYq8i14mtHEWGQF_fSDUsWHmiOdY,346
71
71
  griptape_nodes/exe_types/node_groups/base_iterative_node_group.py,sha256=sPPy3Psl-Ku_R46upWm_8hIGFLNPeLpUBN_Sm4RzXzg,9655
72
- griptape_nodes/exe_types/node_groups/base_node_group.py,sha256=dJofcmERzYQSQ2KB7dC0rOKjbZKW8Ko8eD6r0smBu48,3209
72
+ griptape_nodes/exe_types/node_groups/base_node_group.py,sha256=loDdJKllRPpWSSk-jJpqbBto80yn5oZGc6Jl-VXa_8A,3955
73
73
  griptape_nodes/exe_types/node_groups/subflow_node_group.py,sha256=UlZS95TKUCWiHgg6z4Wm1t9xXfUjWhk0L8pB5cXMcV0,48361
74
- griptape_nodes/exe_types/node_types.py,sha256=pPljG84zJ6Wd6yzcRjnK0n1805b-Jyzxx7-NFox66QE,84227
74
+ griptape_nodes/exe_types/node_types.py,sha256=Hrzun9DN4FA-Yk-YQXBFyGrH1-kzszd9eVq4y6rmV5Y,84226
75
75
  griptape_nodes/exe_types/param_components/README.md,sha256=mcNFxJIan9CGTnecsrJ8mkHC55dlA3fb7k4HFznou-k,14850
76
76
  griptape_nodes/exe_types/param_components/__init__.py,sha256=ocm75WnsgiD6ozKVGFhoH9cQe_FEzeF2osxrRujOes0,60
77
77
  griptape_nodes/exe_types/param_components/api_key_provider_parameter.py,sha256=MHn5zYb2vEf4bGBfbnTjCfVYEbpZtXThb8JJAAMpuR8,7766
78
78
  griptape_nodes/exe_types/param_components/artifact_url/__init__.py,sha256=LKAGdP8VBSOTx8iq8kEvxZVDgMIS5TXSLntZqBEJ7yk,40
79
79
  griptape_nodes/exe_types/param_components/artifact_url/public_artifact_url_parameter.py,sha256=2A4Jn7Xgi6KPM087wnVhoH3k5GnqxLFt3MV56SwYIYI,6610
80
- griptape_nodes/exe_types/param_components/execution_status_component.py,sha256=eeUcUOOraGa6sDH_zg_QnGPoP0K-pm3fu90-KAtrmq8,5532
80
+ griptape_nodes/exe_types/param_components/execution_status_component.py,sha256=OBKTNW9q3-Ges6hxH--dyh1qbwU2O8WnwTNVRU_wW8s,6863
81
81
  griptape_nodes/exe_types/param_components/huggingface/__init__.py,sha256=YeVFck-9C341Bt7TiWEvPdD63o9qkaNu2KjmzT9CQOI,39
82
82
  griptape_nodes/exe_types/param_components/huggingface/huggingface_model_parameter.py,sha256=rEGN4kvblSAlRh7pD73U__wzAtVzDYWYlDC-k1nKtyM,6395
83
83
  griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_file_parameter.py,sha256=c61de3mbkc5FvVkf5vubtD0XfThDInTfibAtYhZKcKA,4265
84
84
  griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_parameter.py,sha256=zcBRQP0eZJloSdPQwJFw89B6QDHECnrosRZUy8mE2fM,1401
85
85
  griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_variant_parameter.py,sha256=FaaDZ4dvZJCnpqSOKfJ5nR1klgl07JZto2A7gxhUi3o,5290
86
86
  griptape_nodes/exe_types/param_components/huggingface/huggingface_utils.py,sha256=cE0Ht81kDcZjlN_2VoRirCA4zMlrG9GFlcptn-gC2tU,4696
87
- griptape_nodes/exe_types/param_components/log_parameter.py,sha256=K6XRWVIpL9MtQ6IZG3hYYwIv3sNqFxONxbVnKcA_Oes,4160
87
+ griptape_nodes/exe_types/param_components/log_parameter.py,sha256=_v6NH1lz9FrEKf2uMIOlwxzKTy5Vl3EAZai54lAMF1g,4313
88
88
  griptape_nodes/exe_types/param_components/progress_bar_component.py,sha256=GrAFTOrLNAx6q9zfOqiioPTG_NLHKhTe9NiZOo6zGSc,1949
89
- griptape_nodes/exe_types/param_components/seed_parameter.py,sha256=DUaYJTU8Jafu5sfibKJiO628813C37zMIJLYeXcoeuE,2119
89
+ griptape_nodes/exe_types/param_components/seed_parameter.py,sha256=I48cVAojrD5oX4CT7v0x9LX6MrKVklywkp8IjE5pGIo,2321
90
90
  griptape_nodes/exe_types/param_types/__init__.py,sha256=xEEmKvIFF6M7zVjQZCupbbv6SZKt-itD-rPtfRhxJVg,53
91
91
  griptape_nodes/exe_types/param_types/parameter_audio.py,sha256=9oIBtDg6miZDD_5y8VWr6XXjYhsRb9HjSKoaLdgqaHA,9534
92
92
  griptape_nodes/exe_types/param_types/parameter_bool.py,sha256=VS76CODOy7_1MJI9spxmi9VwL3fwCxRKaj_8zpqB7dw,8215
@@ -130,7 +130,7 @@ griptape_nodes/retained_mode/events/library_events.py,sha256=4NUc3uoLgofo1X9nrCV
130
130
  griptape_nodes/retained_mode/events/logger_events.py,sha256=jYlxzPomgCsJuPtJ0znWBhD8QJfC8qC4xfChDiuVuyg,705
131
131
  griptape_nodes/retained_mode/events/mcp_events.py,sha256=fs83jAQamhwNC9Zt8UNkBKd6iTids8531V_RMnGj__o,11008
132
132
  griptape_nodes/retained_mode/events/model_events.py,sha256=jXm-v-_Uxysn4MJ7a80_uIphrxTHgua3BGgHGyy3T_Y,9232
133
- griptape_nodes/retained_mode/events/node_events.py,sha256=vpILjBkgrPzF_KSow2EJCwcU-tDD78WEWM2DAAzhhiE,37720
133
+ griptape_nodes/retained_mode/events/node_events.py,sha256=9yTdTrFNd5JATvHk5x3ucznMou3ZzYxgpCgNjAF-GuU,38760
134
134
  griptape_nodes/retained_mode/events/object_events.py,sha256=cJaqEU73Lzf1RRxJrFqEpl8eTr-gDhKpXKywJ-vVCJQ,2631
135
135
  griptape_nodes/retained_mode/events/os_events.py,sha256=fVraPXCz48kDpCTttJFtsyfTfkaLSfTX57hexYSD7I8,23429
136
136
  griptape_nodes/retained_mode/events/parameter_events.py,sha256=TazVXxvfv64n3XJzToOQKiWQ1UIzrUAB4ri_QIvZ2ng,26968
@@ -148,7 +148,7 @@ griptape_nodes/retained_mode/managers/__init__.py,sha256=OTXysKusqYCQeAYwnVj4PbE
148
148
  griptape_nodes/retained_mode/managers/agent_manager.py,sha256=FOKrcHEBw6M-RmfWTWEQmV-9iy9cD1zHiiDUDMqJHE4,28307
149
149
  griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py,sha256=LyWzHQKmyDh-2zh2PIahdYyAthf_rMYs4zvhtVo-LsU,3423
150
150
  griptape_nodes/retained_mode/managers/config_manager.py,sha256=74AMeaCS0eOeFbzMk5I54MtB7-SNQbwlx_-O6zBBQRo,26739
151
- griptape_nodes/retained_mode/managers/context_manager.py,sha256=eb44_CAZhCg2iYIoodlAPpYc67tG3sHyq9dPNoiq_1s,23031
151
+ griptape_nodes/retained_mode/managers/context_manager.py,sha256=n-r6-Zzyq3-9QBAyIZO7w7YC7PGBuIouFLH44ef06E8,21956
152
152
  griptape_nodes/retained_mode/managers/engine_identity_manager.py,sha256=-31364A03kU0YmAx3nM7nHdWfKnkej5Cawt_Plbwa68,10526
153
153
  griptape_nodes/retained_mode/managers/event_manager.py,sha256=jiUX3WW6Hz0Z4Y1dK1X7fMwZawT3_BOKl_Dbiui-iNQ,26933
154
154
  griptape_nodes/retained_mode/managers/fitness_problems/__init__.py,sha256=H_Wwfn1k_IaxwRxgbw3-seb1HWbcxl2Cvhw7wB7bCo8,75
@@ -201,12 +201,12 @@ griptape_nodes/retained_mode/managers/fitness_problems/workflows/node_type_not_f
201
201
  griptape_nodes/retained_mode/managers/fitness_problems/workflows/workflow_not_found_problem.py,sha256=kPhaIeDxp0duxJBJLKFiYp269kEDsCx3uVLnkKbF2gk,895
202
202
  griptape_nodes/retained_mode/managers/fitness_problems/workflows/workflow_problem.py,sha256=NLAoF_x7g5tL70qZtPVFI8xeM1jPZNsm7FKt9nkuGBc,542
203
203
  griptape_nodes/retained_mode/managers/fitness_problems/workflows/workflow_schema_version_problem.py,sha256=rUnbBBnGwgq5WTVPt2G4pGP3gVPoX5xvCdTkRPBmP04,1328
204
- griptape_nodes/retained_mode/managers/flow_manager.py,sha256=fY0xCXHiualDwVQJnNlPpMUnLz5bYWvr5D1_UvOyNVM,220269
204
+ griptape_nodes/retained_mode/managers/flow_manager.py,sha256=BKWLpc-udlZ4n1rlKINHFS-Jj6wBqoCSdD7x0FyNr5o,220266
205
205
  griptape_nodes/retained_mode/managers/library_manager.py,sha256=paogGavZj2MDOYvfUklhjORQg16YdpVGBxBncVSxtR0,192265
206
206
  griptape_nodes/retained_mode/managers/mcp_manager.py,sha256=AEHG1SqFc3g5hOImOHaH7ZjOkmgNAavmQgG9osXVGkE,16018
207
207
  griptape_nodes/retained_mode/managers/model_manager.py,sha256=3lj2X8vIvDSERPtR2VEXNFEWy_D8H6muxRvD-PEx8U8,44845
208
- griptape_nodes/retained_mode/managers/node_manager.py,sha256=KbThJ5fvBTuF9EEIqBT37Rop41_7o-irb_-KY8hkXVY,225038
209
- griptape_nodes/retained_mode/managers/object_manager.py,sha256=xABsjpE1_HLEIFwYj60zDOLi18EqrYUBuNaj37lUcH4,12921
208
+ griptape_nodes/retained_mode/managers/node_manager.py,sha256=BG0-z2-pDjzgS5VC3u3p7-jGUJ5nGSGSShbSv9WseoA,229244
209
+ griptape_nodes/retained_mode/managers/object_manager.py,sha256=ZpovmhnBKG_JCn4Pde9idlx2eBZjKvR7k9DHUYqD-r4,12882
210
210
  griptape_nodes/retained_mode/managers/operation_manager.py,sha256=4Vn_79vHrawy3wJVUx52tfblO4mURww58nb5RtCTpKU,20190
211
211
  griptape_nodes/retained_mode/managers/os_manager.py,sha256=Ttgj6R063p2Yf1lcdeSTB2xOTg94MIDCMPxO8FDczEQ,142397
212
212
  griptape_nodes/retained_mode/managers/project_manager.py,sha256=-K-anrxEVKInFMPLY-cV3ipqEh8hMELGHKbecJJJVa8,49035
@@ -229,7 +229,7 @@ griptape_nodes/retained_mode/managers/user_manager.py,sha256=JAOOKDhUfIbj0CJ5EHR
229
229
  griptape_nodes/retained_mode/managers/variable_manager.py,sha256=TnuqHSRK9Yiu_EtKxQksF9SyyQb72lbFQuTQZdpBxeE,24116
230
230
  griptape_nodes/retained_mode/managers/version_compatibility_manager.py,sha256=kaCtmNMqJW-mvLCFHf77wbBZbc3CgW2h8fYYkLkNrPs,17041
231
231
  griptape_nodes/retained_mode/managers/workflow_manager.py,sha256=n6jmr966Y_0MPd9aaOssX2CYgojf_mNRxC07990Anec,217769
232
- griptape_nodes/retained_mode/retained_mode.py,sha256=dG8FFpLXSxT49naOcphEq2aSRCEVoQyklWgaDljjuDA,71728
232
+ griptape_nodes/retained_mode/retained_mode.py,sha256=fJNAVyheqjXFOh8--VAmJBLWlNgMyRWNghG_0u8B-1w,72415
233
233
  griptape_nodes/retained_mode/utils/__init__.py,sha256=W5dvv8YwvVVq_8eVTgMd3Z_VB_Dtq1sIIVq8745QH_I,52
234
234
  griptape_nodes/retained_mode/utils/name_generator.py,sha256=IZLahtfP3XC79XApLdGoZ0IKKUkgiITpd16RK7NbyEs,2524
235
235
  griptape_nodes/retained_mode/variable_types.py,sha256=GVrSWMB3gEDAufSPOBXbNfIRhA9M43MoxpqLyuIg_HE,435
@@ -276,7 +276,7 @@ griptape_nodes/version_compatibility/versions/v0_65_5/__init__.py,sha256=4UyspOV
276
276
  griptape_nodes/version_compatibility/versions/v0_65_5/flux_2_removed_parameters.py,sha256=jOlmY5kKHXh8HPzAUtvwMJqzD4bP7pkE--yHUb7jTRA,3305
277
277
  griptape_nodes/version_compatibility/versions/v0_7_0/__init__.py,sha256=IzPPmGK86h2swfGGTOHyVcBIlOng6SjgWQzlbf3ngmo,51
278
278
  griptape_nodes/version_compatibility/versions/v0_7_0/local_executor_argument_addition.py,sha256=Thx8acnbw5OychhwEEj9aFxvbPe7Wgn4V9ZmZ7KRZqc,2082
279
- griptape_nodes-0.68.1.dist-info/WHEEL,sha256=eycQt0QpYmJMLKpE3X9iDk8R04v2ZF0x82ogq-zP6bQ,79
280
- griptape_nodes-0.68.1.dist-info/entry_points.txt,sha256=qvevqd3BVbAV5TcantnAm0ouqaqYKhsRO3pkFymWLWM,82
281
- griptape_nodes-0.68.1.dist-info/METADATA,sha256=c23iianw-Es7pl0SlHWlsx04xm_T9PMujNlLdfT2jDk,5374
282
- griptape_nodes-0.68.1.dist-info/RECORD,,
279
+ griptape_nodes-0.70.0.dist-info/WHEEL,sha256=XV0cjMrO7zXhVAIyyc8aFf1VjZ33Fen4IiJk5zFlC3g,80
280
+ griptape_nodes-0.70.0.dist-info/entry_points.txt,sha256=qvevqd3BVbAV5TcantnAm0ouqaqYKhsRO3pkFymWLWM,82
281
+ griptape_nodes-0.70.0.dist-info/METADATA,sha256=BF90w9kk-lZK3hgKOpZNlTFt_GubK2VhZ3g01FioCBQ,5373
282
+ griptape_nodes-0.70.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: uv 0.9.24
2
+ Generator: uv 0.9.26
3
3
  Root-Is-Purelib: true
4
- Tag: py3-none-any
4
+ Tag: py3-none-any