griptape-nodes 0.63.10__py3-none-any.whl → 0.64.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. griptape_nodes/common/node_executor.py +95 -171
  2. griptape_nodes/exe_types/connections.py +51 -2
  3. griptape_nodes/exe_types/flow.py +3 -3
  4. griptape_nodes/exe_types/node_types.py +330 -202
  5. griptape_nodes/exe_types/param_components/artifact_url/__init__.py +1 -0
  6. griptape_nodes/exe_types/param_components/artifact_url/public_artifact_url_parameter.py +155 -0
  7. griptape_nodes/exe_types/param_components/progress_bar_component.py +1 -1
  8. griptape_nodes/exe_types/param_types/parameter_string.py +27 -0
  9. griptape_nodes/machines/control_flow.py +64 -203
  10. griptape_nodes/machines/dag_builder.py +85 -238
  11. griptape_nodes/machines/parallel_resolution.py +9 -236
  12. griptape_nodes/machines/sequential_resolution.py +133 -11
  13. griptape_nodes/retained_mode/events/agent_events.py +2 -0
  14. griptape_nodes/retained_mode/events/flow_events.py +5 -6
  15. griptape_nodes/retained_mode/events/node_events.py +151 -1
  16. griptape_nodes/retained_mode/events/workflow_events.py +10 -0
  17. griptape_nodes/retained_mode/managers/agent_manager.py +33 -1
  18. griptape_nodes/retained_mode/managers/flow_manager.py +213 -290
  19. griptape_nodes/retained_mode/managers/library_manager.py +24 -7
  20. griptape_nodes/retained_mode/managers/node_manager.py +400 -77
  21. griptape_nodes/retained_mode/managers/version_compatibility_manager.py +113 -69
  22. griptape_nodes/retained_mode/managers/workflow_manager.py +45 -10
  23. griptape_nodes/servers/mcp.py +32 -0
  24. griptape_nodes/version_compatibility/versions/v0_63_8/__init__.py +1 -0
  25. griptape_nodes/version_compatibility/versions/v0_63_8/deprecated_nodegroup_parameters.py +105 -0
  26. {griptape_nodes-0.63.10.dist-info → griptape_nodes-0.64.1.dist-info}/METADATA +3 -1
  27. {griptape_nodes-0.63.10.dist-info → griptape_nodes-0.64.1.dist-info}/RECORD +31 -28
  28. griptape_nodes/version_compatibility/workflow_versions/__init__.py +0 -1
  29. /griptape_nodes/version_compatibility/{workflow_versions → versions}/v0_7_0/__init__.py +0 -0
  30. /griptape_nodes/version_compatibility/{workflow_versions → versions}/v0_7_0/local_executor_argument_addition.py +0 -0
  31. {griptape_nodes-0.63.10.dist-info → griptape_nodes-0.64.1.dist-info}/WHEEL +0 -0
  32. {griptape_nodes-0.63.10.dist-info → griptape_nodes-0.64.1.dist-info}/entry_points.txt +0 -0
@@ -4,7 +4,7 @@ import importlib
4
4
  import logging
5
5
  from abc import ABC, abstractmethod
6
6
  from pathlib import Path
7
- from typing import TYPE_CHECKING, NamedTuple
7
+ from typing import TYPE_CHECKING, Any, NamedTuple
8
8
 
9
9
  import semver
10
10
 
@@ -35,8 +35,13 @@ from griptape_nodes.retained_mode.managers.fitness_problems.workflows.node_type_
35
35
  from griptape_nodes.retained_mode.managers.library_lifecycle.library_status import LibraryStatus
36
36
 
37
37
  if TYPE_CHECKING:
38
+ from griptape_nodes.exe_types.node_types import BaseNode
38
39
  from griptape_nodes.node_library.library_registry import LibrarySchema
39
40
  from griptape_nodes.node_library.workflow_registry import WorkflowMetadata
41
+ from griptape_nodes.retained_mode.events.parameter_events import (
42
+ SetParameterValueResultFailure,
43
+ SetParameterValueResultSuccess,
44
+ )
40
45
  from griptape_nodes.retained_mode.managers.event_manager import EventManager
41
46
  from griptape_nodes.retained_mode.managers.fitness_problems.libraries.library_problem import LibraryProblem
42
47
  from griptape_nodes.retained_mode.managers.fitness_problems.workflows.workflow_problem import WorkflowProblem
@@ -83,6 +88,38 @@ class WorkflowVersionCompatibilityCheck(ABC):
83
88
  """Perform the workflow compatibility check."""
84
89
 
85
90
 
91
+ class SetParameterVersionCompatibilityCheck(ABC):
92
+ """Abstract base class for runtime parameter set version compatibility checks."""
93
+
94
+ @abstractmethod
95
+ def applies_to_set_parameter(self, node: BaseNode, parameter_name: str, value: Any) -> bool:
96
+ """Return True if this check applies to the given parameter set operation.
97
+
98
+ Args:
99
+ node: The node instance
100
+ parameter_name: Name of the parameter being set
101
+ value: The value being set
102
+
103
+ Returns:
104
+ True if this check should be performed for this parameter
105
+ """
106
+
107
+ @abstractmethod
108
+ def set_parameter_value(
109
+ self, node: BaseNode, parameter_name: str, value: Any
110
+ ) -> SetParameterValueResultSuccess | SetParameterValueResultFailure:
111
+ """Handle setting the parameter value with version compatibility logic.
112
+
113
+ Args:
114
+ node: The node instance
115
+ parameter_name: Name of the parameter being set
116
+ value: The value being set
117
+
118
+ Returns:
119
+ SetParameterValueResultSuccess or SetParameterValueResultFailure
120
+ """
121
+
122
+
86
123
  class VersionCompatibilityManager:
87
124
  """Manages version compatibility checks for libraries and other components."""
88
125
 
@@ -90,89 +127,74 @@ class VersionCompatibilityManager:
90
127
  self._event_manager = event_manager
91
128
  self._compatibility_checks: list[LibraryVersionCompatibilityCheck] = []
92
129
  self._workflow_compatibility_checks: list[WorkflowVersionCompatibilityCheck] = []
130
+ self._set_parameter_compatibility_checks: list[SetParameterVersionCompatibilityCheck] = []
93
131
  self._discover_version_checks()
94
132
 
95
133
  def _discover_version_checks(self) -> None:
96
- """Automatically discover and register library and workflow version compatibility checks."""
97
- self._discover_library_version_checks()
98
- self._discover_workflow_version_checks()
134
+ """Automatically discover and register all version compatibility checks from the versions/ directory."""
135
+ try:
136
+ import griptape_nodes.version_compatibility.versions as versions_module
99
137
 
100
- def _discover_library_version_checks(self) -> None:
101
- """Discover and register library version compatibility checks."""
102
- # Get the path to the version_compatibility/versions directory
103
- import griptape_nodes.version_compatibility.versions as versions_module
138
+ if versions_module.__file__ is None:
139
+ logger.debug("No version compatibility checks directory found, skipping discovery")
140
+ return
104
141
 
105
- versions_path = Path(versions_module.__file__).parent
142
+ versions_path = Path(versions_module.__file__).parent
106
143
 
107
- # Iterate through version directories
108
- for version_dir in versions_path.iterdir():
109
- if version_dir.is_dir() and not version_dir.name.startswith("__"):
110
- self._discover_checks_in_version_dir(version_dir)
144
+ # Iterate through version directories (e.g., v0_39_0, v0_63_8)
145
+ for version_dir in versions_path.iterdir():
146
+ if not version_dir.is_dir() or version_dir.name.startswith("__"):
147
+ continue
111
148
 
112
- def _discover_workflow_version_checks(self) -> None:
113
- """Discover and register workflow version compatibility checks."""
114
- try:
115
- import griptape_nodes.version_compatibility.workflow_versions as workflow_versions_module
149
+ # Iterate through Python files in the version directory
150
+ for check_file in version_dir.glob("*.py"):
151
+ if check_file.name.startswith("__"):
152
+ continue
153
+
154
+ # Import the module once
155
+ file_module_path = (
156
+ f"griptape_nodes.version_compatibility.versions.{version_dir.name}.{check_file.stem}"
157
+ )
158
+ try:
159
+ check_module = importlib.import_module(file_module_path)
160
+ except ImportError as e:
161
+ logger.debug("Failed to import check module %s: %s", file_module_path, e)
162
+ continue
116
163
 
117
- workflow_versions_path = Path(workflow_versions_module.__file__).parent
164
+ # Scan and register all check types in this module
165
+ self._register_checks_from_module(check_module)
118
166
 
119
- # Iterate through version directories
120
- for version_dir in workflow_versions_path.iterdir():
121
- if version_dir.is_dir() and not version_dir.name.startswith("__"):
122
- self._discover_workflow_checks_in_version_dir(version_dir)
123
167
  except ImportError:
124
- # workflow_versions directory doesn't exist yet, skip discovery
125
- logger.debug("No workflow version compatibility checks directory found, skipping workflow check discovery")
126
-
127
- def _discover_checks_in_version_dir(self, version_dir: Path) -> None:
128
- """Discover compatibility checks in a specific version directory."""
129
- # Iterate through Python files in the version directory
130
- for check_file in version_dir.glob("*.py"):
131
- if check_file.name.startswith("__"):
132
- continue
168
+ logger.debug("No version compatibility checks directory found, skipping discovery")
169
+
170
+ def _register_checks_from_module(self, check_module: Any) -> None:
171
+ """Register all version compatibility checks found in a module.
133
172
 
134
- # Import the module
135
- module_path = f"griptape_nodes.version_compatibility.versions.{version_dir.name}.{check_file.stem}"
136
- module = importlib.import_module(module_path)
137
-
138
- # Look for classes that inherit from LibraryVersionCompatibilityCheck
139
- for attr_name in dir(module):
140
- attr = getattr(module, attr_name)
141
- if (
142
- isinstance(attr, type)
143
- and issubclass(attr, LibraryVersionCompatibilityCheck)
144
- and attr is not LibraryVersionCompatibilityCheck
145
- ):
146
- check_instance = attr()
147
- self._compatibility_checks.append(check_instance)
148
- logger.debug("Registered library version compatibility check: %s", attr_name)
149
-
150
- def _discover_workflow_checks_in_version_dir(self, version_dir: Path) -> None:
151
- """Discover workflow compatibility checks in a specific version directory."""
152
- # Iterate through Python files in the version directory
153
- for check_file in version_dir.glob("*.py"):
154
- if check_file.name.startswith("__"):
173
+ Args:
174
+ check_module: The imported module to scan for check classes
175
+ """
176
+ for attr_name in dir(check_module):
177
+ attr = getattr(check_module, attr_name)
178
+ if not isinstance(attr, type):
155
179
  continue
156
180
 
157
- # Import the module
158
- module_path = f"griptape_nodes.version_compatibility.workflow_versions.{version_dir.name}.{check_file.stem}"
159
- try:
160
- module = importlib.import_module(module_path)
161
- except ImportError as e:
162
- logger.debug("Failed to import workflow compatibility check module %s: %s", module_path, e)
181
+ # Skip abstract base classes
182
+ if ABC in getattr(attr, "__bases__", ()):
163
183
  continue
164
184
 
165
- # Look for classes that inherit from WorkflowVersionCompatibilityCheck
166
- for attr_name in dir(module):
167
- attr = getattr(module, attr_name)
168
- if (
169
- isinstance(attr, type)
170
- and issubclass(attr, WorkflowVersionCompatibilityCheck)
171
- and attr is not WorkflowVersionCompatibilityCheck
172
- ):
173
- check_instance = attr()
174
- self._workflow_compatibility_checks.append(check_instance)
175
- logger.debug("Registered workflow version compatibility check: %s", attr_name)
185
+ # Register based on which base class it inherits from
186
+ if issubclass(attr, LibraryVersionCompatibilityCheck):
187
+ check_instance = attr()
188
+ self._compatibility_checks.append(check_instance)
189
+ logger.debug("Registered library version compatibility check: %s", attr_name)
190
+ elif issubclass(attr, WorkflowVersionCompatibilityCheck):
191
+ check_instance = attr()
192
+ self._workflow_compatibility_checks.append(check_instance)
193
+ logger.debug("Registered workflow version compatibility check: %s", attr_name)
194
+ elif issubclass(attr, SetParameterVersionCompatibilityCheck):
195
+ check_instance = attr()
196
+ self._set_parameter_compatibility_checks.append(check_instance)
197
+ logger.debug("Registered set parameter version compatibility check: %s", attr_name)
176
198
 
177
199
  def _check_library_for_deprecated_nodes(
178
200
  self, library_data: LibrarySchema
@@ -333,6 +355,28 @@ class VersionCompatibilityManager:
333
355
 
334
356
  return issues
335
357
 
358
+ def check_set_parameter_version_compatibility(
359
+ self, node: BaseNode, parameter_name: str, value: Any
360
+ ) -> SetParameterValueResultSuccess | SetParameterValueResultFailure | None:
361
+ """Check if a parameter set operation requires version compatibility handling.
362
+
363
+ Args:
364
+ node: The node instance
365
+ parameter_name: Name of the parameter being set
366
+ value: The value being set
367
+
368
+ Returns:
369
+ SetParameterValueResultSuccess, SetParameterValueResultFailure, or None if no check applies
370
+ """
371
+ # Iterate through registered checks and find the first one that applies
372
+ for check_instance in self._set_parameter_compatibility_checks:
373
+ if check_instance.applies_to_set_parameter(node, parameter_name, value):
374
+ # First matching check handles the parameter
375
+ return check_instance.set_parameter_value(node, parameter_name, value)
376
+
377
+ # No checks applied
378
+ return None
379
+
336
380
  def _get_current_engine_version(self) -> semver.VersionInfo:
337
381
  """Get the current engine version."""
338
382
  result = GriptapeNodes.handle_request(GetEngineVersionRequest())
@@ -14,6 +14,7 @@ from pathlib import Path
14
14
  from typing import TYPE_CHECKING, Any, ClassVar, NamedTuple, TypeVar, cast
15
15
 
16
16
  import aiofiles
17
+ import portalocker
17
18
  import semver
18
19
  import tomlkit
19
20
  from rich.box import HEAVY_EDGE
@@ -1269,9 +1270,12 @@ class WorkflowManager:
1269
1270
  success: bool
1270
1271
  error_details: str
1271
1272
 
1272
- async def _write_workflow_file(self, file_path: Path, content: str, file_name: str) -> WriteWorkflowFileResult:
1273
+ def _write_workflow_file(self, file_path: Path, content: str, file_name: str) -> WriteWorkflowFileResult:
1273
1274
  """Write workflow content to file with proper validation and error handling.
1274
1275
 
1276
+ Uses portalocker for exclusive file locking to prevent concurrent writes.
1277
+ First write wins - if another process is writing, this call fails immediately.
1278
+
1275
1279
  Args:
1276
1280
  file_path: Path where to write the file
1277
1281
  content: Content to write
@@ -1295,13 +1299,34 @@ class WorkflowManager:
1295
1299
  details = f"Attempted to save workflow '{file_name}'. Failed when creating directory structure: {e}"
1296
1300
  return self.WriteWorkflowFileResult(success=False, error_details=details)
1297
1301
 
1298
- # Write the file content
1302
+ # Write the file content with exclusive lock (non-blocking)
1303
+ error_details = None
1299
1304
  try:
1300
- async with aiofiles.open(file_path, "w", encoding="utf-8") as file:
1301
- await file.write(content)
1305
+ with portalocker.Lock(
1306
+ file_path,
1307
+ mode="w",
1308
+ encoding="utf-8",
1309
+ timeout=0, # Non-blocking: fail immediately if locked
1310
+ flags=portalocker.LockFlags.EXCLUSIVE,
1311
+ ) as fh:
1312
+ fh.write(content)
1313
+ except portalocker.LockException:
1314
+ error_details = (
1315
+ f"Attempted to save workflow '{file_name}'. Another process is currently writing to this workflow file"
1316
+ )
1317
+ except PermissionError as e:
1318
+ error_details = f"Attempted to save workflow '{file_name}'. Permission denied: {e}"
1319
+ except IsADirectoryError:
1320
+ error_details = f"Attempted to save workflow '{file_name}'. Path is a directory, not a file"
1321
+ except UnicodeEncodeError as e:
1322
+ error_details = f"Attempted to save workflow '{file_name}'. Content encoding error: {e}"
1302
1323
  except OSError as e:
1303
- details = f"Attempted to save workflow '{file_name}'. Failed when writing file content: {e}"
1304
- return self.WriteWorkflowFileResult(success=False, error_details=details)
1324
+ error_details = f"Attempted to save workflow '{file_name}'. OS error: {e}"
1325
+ except Exception as e:
1326
+ error_details = f"Attempted to save workflow '{file_name}'. Unexpected error: {type(e).__name__}: {e}"
1327
+
1328
+ if error_details:
1329
+ return self.WriteWorkflowFileResult(success=False, error_details=error_details)
1305
1330
 
1306
1331
  return self.WriteWorkflowFileResult(success=True, error_details="")
1307
1332
 
@@ -1546,7 +1571,10 @@ class WorkflowManager:
1546
1571
  return SaveWorkflowFileFromSerializedFlowResultFailure(result_details=details)
1547
1572
 
1548
1573
  # Write the workflow file
1549
- write_result = await self._write_workflow_file(file_path, final_code_output, request.file_name)
1574
+ # TODO: https://github.com/griptape-ai/griptape-nodes/issues/3169
1575
+ # This is a synchronous call within an async context and may block the event loop.
1576
+ # Consider using asyncio.to_thread() to run in a thread pool for better async performance.
1577
+ write_result = self._write_workflow_file(file_path, final_code_output, request.file_name)
1550
1578
  if not write_result.success:
1551
1579
  return SaveWorkflowFileFromSerializedFlowResultFailure(result_details=write_result.error_details)
1552
1580
 
@@ -2901,6 +2929,7 @@ class WorkflowManager:
2901
2929
  import_recorder.add_from_import("griptape_nodes.node_library.library_registry", "NodeDeprecationMetadata")
2902
2930
  import_recorder.add_from_import("griptape_nodes.node_library.library_registry", "IconVariant")
2903
2931
  import_recorder.add_from_import("griptape_nodes.retained_mode.events.node_events", "CreateNodeRequest")
2932
+ import_recorder.add_from_import("griptape_nodes.retained_mode.events.node_events", "CreateNodeGroupRequest")
2904
2933
  import_recorder.add_from_import(
2905
2934
  "griptape_nodes.retained_mode.events.parameter_events", "AddParameterToNodeRequest"
2906
2935
  )
@@ -2925,7 +2954,8 @@ class WorkflowManager:
2925
2954
  create_node_request_args.append(
2926
2955
  ast.keyword(arg=field.name, value=ast.Constant(value=field_value, lineno=1, col_offset=0))
2927
2956
  )
2928
-
2957
+ # Get the actual request class name (CreateNodeRequest or CreateNodeGroupRequest)
2958
+ request_class_name = type(create_node_request).__name__
2929
2959
  # Handle the create node command and assign to node name
2930
2960
  create_node_call_ast = ast.Assign(
2931
2961
  targets=[ast.Name(id=node_variable_name, ctx=ast.Store(), lineno=1, col_offset=0)],
@@ -2940,7 +2970,7 @@ class WorkflowManager:
2940
2970
  ),
2941
2971
  args=[
2942
2972
  ast.Call(
2943
- func=ast.Name(id="CreateNodeRequest", ctx=ast.Load(), lineno=1, col_offset=0),
2973
+ func=ast.Name(id=request_class_name, ctx=ast.Load(), lineno=1, col_offset=0),
2944
2974
  args=[],
2945
2975
  keywords=create_node_request_args,
2946
2976
  lineno=1,
@@ -2951,7 +2981,7 @@ class WorkflowManager:
2951
2981
  lineno=1,
2952
2982
  col_offset=0,
2953
2983
  ),
2954
- attr="node_name",
2984
+ attr="node_name" if request_class_name == "CreateNodeRequest" else "node_group_name",
2955
2985
  ctx=ast.Load(),
2956
2986
  lineno=1,
2957
2987
  col_offset=0,
@@ -3004,6 +3034,11 @@ class WorkflowManager:
3004
3034
 
3005
3035
  # Generate handle_request calls for element_modification_commands
3006
3036
  for element_command in serialized_node_command.element_modification_commands:
3037
+ # Add import for this element command type
3038
+ element_command_class_name = element_command.__class__.__name__
3039
+ element_command_module = element_command.__class__.__module__
3040
+ import_recorder.add_from_import(element_command_module, element_command_class_name)
3041
+
3007
3042
  # Strip default values from element_command
3008
3043
  element_command_args = []
3009
3044
  if is_dataclass(element_command):
@@ -23,23 +23,49 @@ from griptape_nodes.retained_mode.events.connection_events import (
23
23
  DeleteConnectionRequest,
24
24
  ListConnectionsForNodeRequest,
25
25
  )
26
+ from griptape_nodes.retained_mode.events.execution_events import (
27
+ ResolveNodeRequest,
28
+ StartFlowFromNodeRequest,
29
+ StartFlowRequest,
30
+ )
26
31
  from griptape_nodes.retained_mode.events.flow_events import ListNodesInFlowRequest
32
+ from griptape_nodes.retained_mode.events.library_events import (
33
+ ListCategoriesInLibraryRequest,
34
+ ListNodeTypesInLibraryRequest,
35
+ ListRegisteredLibrariesRequest,
36
+ )
27
37
  from griptape_nodes.retained_mode.events.node_events import (
28
38
  CreateNodeRequest,
29
39
  DeleteNodeRequest,
30
40
  GetNodeMetadataRequest,
31
41
  GetNodeResolutionStateRequest,
32
42
  ListParametersOnNodeRequest,
43
+ ResetNodeToDefaultsRequest,
44
+ SetLockNodeStateRequest,
33
45
  SetNodeMetadataRequest,
34
46
  )
47
+ from griptape_nodes.retained_mode.events.object_events import RenameObjectRequest
35
48
  from griptape_nodes.retained_mode.events.parameter_events import (
49
+ GetConnectionsForParameterRequest,
50
+ GetParameterDetailsRequest,
36
51
  GetParameterValueRequest,
37
52
  SetParameterValueRequest,
38
53
  )
54
+ from griptape_nodes.retained_mode.events.workflow_events import RunWorkflowWithCurrentStateRequest
39
55
  from griptape_nodes.retained_mode.managers.config_manager import ConfigManager
40
56
  from griptape_nodes.retained_mode.managers.secrets_manager import SecretsManager
41
57
 
42
58
  SUPPORTED_REQUEST_EVENTS: dict[str, type[RequestPayload]] = {
59
+ # Workflows
60
+ "RunWorkflowWithCurrentStateRequest": RunWorkflowWithCurrentStateRequest,
61
+ # Libraries
62
+ "ListRegisteredLibrariesRequest": ListRegisteredLibrariesRequest,
63
+ "ListNodeTypesInLibraryRequest": ListNodeTypesInLibraryRequest,
64
+ "ListCategoriesInLibraryRequest": ListCategoriesInLibraryRequest,
65
+ # Execution
66
+ "ResolveNodeRequest": ResolveNodeRequest,
67
+ "StartFlowRequest": StartFlowRequest,
68
+ "StartFlowFromNodeRequest": StartFlowFromNodeRequest,
43
69
  # Nodes
44
70
  "CreateNodeRequest": CreateNodeRequest,
45
71
  "DeleteNodeRequest": DeleteNodeRequest,
@@ -47,6 +73,10 @@ SUPPORTED_REQUEST_EVENTS: dict[str, type[RequestPayload]] = {
47
73
  "GetNodeResolutionStateRequest": GetNodeResolutionStateRequest,
48
74
  "GetNodeMetadataRequest": GetNodeMetadataRequest,
49
75
  "SetNodeMetadataRequest": SetNodeMetadataRequest,
76
+ "ResetNodeToDefaultsRequest": ResetNodeToDefaultsRequest,
77
+ "SetLockNodeStateRequest": SetLockNodeStateRequest,
78
+ # Objects
79
+ "RenameObjectRequest": RenameObjectRequest,
50
80
  # Connections
51
81
  "CreateConnectionRequest": CreateConnectionRequest,
52
82
  "DeleteConnectionRequest": DeleteConnectionRequest,
@@ -55,6 +85,8 @@ SUPPORTED_REQUEST_EVENTS: dict[str, type[RequestPayload]] = {
55
85
  "ListParametersOnNodeRequest": ListParametersOnNodeRequest,
56
86
  "GetParameterValueRequest": GetParameterValueRequest,
57
87
  "SetParameterValueRequest": SetParameterValueRequest,
88
+ "GetParameterDetailsRequest": GetParameterDetailsRequest,
89
+ "GetConnectionsForParameterRequest": GetConnectionsForParameterRequest,
58
90
  }
59
91
 
60
92
  GTN_MCP_SERVER_HOST = os.getenv("GTN_MCP_SERVER_HOST", "localhost")
@@ -0,0 +1 @@
1
+ """Version 0.63.8 parameter compatibility checks."""
@@ -0,0 +1,105 @@
1
+ """Check for deprecated job_group and execution_environment parameters."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import TYPE_CHECKING, Any, ClassVar
7
+
8
+ import semver
9
+
10
+ from griptape_nodes.exe_types.node_types import NodeGroupNode
11
+ from griptape_nodes.retained_mode.events.app_events import (
12
+ GetEngineVersionRequest,
13
+ GetEngineVersionResultSuccess,
14
+ )
15
+ from griptape_nodes.retained_mode.events.parameter_events import SetParameterValueResultSuccess
16
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
17
+ from griptape_nodes.retained_mode.managers.version_compatibility_manager import (
18
+ SetParameterVersionCompatibilityCheck,
19
+ )
20
+
21
+ if TYPE_CHECKING:
22
+ from griptape_nodes.exe_types.node_types import BaseNode
23
+
24
+ logger = logging.getLogger("griptape_nodes")
25
+
26
+
27
+ class DeprecatedNodeGroupParametersCheck(SetParameterVersionCompatibilityCheck):
28
+ """Check for deprecated job_group and execution_environment parameters.
29
+
30
+ These parameters were removed in engine version 0.63.8 for all nodes except NodeGroup.
31
+ This check intercepts attempts to set these parameters and logs a warning prompting
32
+ users to resave their workflows.
33
+ """
34
+
35
+ DEPRECATED_PARAMETERS: ClassVar[set[str]] = {"job_group", "execution_environment"}
36
+ REMOVAL_VERSION: ClassVar[semver.VersionInfo] = semver.VersionInfo(0, 63, 8)
37
+
38
+ def __init__(self) -> None:
39
+ """Initialize the check with an empty set of warned workflows."""
40
+ super().__init__()
41
+ self._warned_workflows: set[str] = set()
42
+
43
+ def applies_to_set_parameter(self, node: BaseNode, parameter_name: str, _value: Any) -> bool:
44
+ """Return True if this is a deprecated parameter on a non-NodeGroup node.
45
+
46
+ Args:
47
+ node: The node instance
48
+ parameter_name: Name of the parameter being set
49
+ _value: The value being set (unused)
50
+
51
+ Returns:
52
+ True if this check should handle this parameter
53
+ """
54
+ # Check parameter name first (fastest check)
55
+ if parameter_name not in self.DEPRECATED_PARAMETERS:
56
+ return False
57
+
58
+ # Check if node is NOT a NodeGroup (we only block for non-NodeGroup nodes)
59
+ if isinstance(node, NodeGroupNode):
60
+ return False
61
+
62
+ # Check if current engine version is >= 0.63.8
63
+ engine_version_result = GriptapeNodes.handle_request(GetEngineVersionRequest())
64
+ if not isinstance(engine_version_result, GetEngineVersionResultSuccess):
65
+ return False
66
+
67
+ current_version = semver.VersionInfo(
68
+ engine_version_result.major, engine_version_result.minor, engine_version_result.patch
69
+ )
70
+ return current_version >= self.REMOVAL_VERSION
71
+
72
+ def set_parameter_value(self, _node: BaseNode, parameter_name: str, _value: Any) -> SetParameterValueResultSuccess:
73
+ """Handle the deprecated parameter by logging a warning and returning success with empty value.
74
+
75
+ Args:
76
+ _node: The node instance (unused)
77
+ parameter_name: Name of the parameter being set
78
+ _value: The value being set (unused)
79
+
80
+ Returns:
81
+ SetParameterValueResultSuccess with empty list value
82
+ """
83
+ # Get the current workflow name to track warnings per workflow
84
+ workflow_name = GriptapeNodes.ContextManager().get_current_workflow_name()
85
+
86
+ # Check if we've already warned for this workflow
87
+ if workflow_name not in self._warned_workflows:
88
+ # Mark this workflow as warned
89
+ self._warned_workflows.add(workflow_name)
90
+
91
+ # Log warning with all deprecated parameters
92
+ deprecated_params_list = ", ".join(f"'{param}'" for param in self.DEPRECATED_PARAMETERS)
93
+ logger.warning(
94
+ "This workflow uses deprecated parameters (%s) that were removed in engine version %s. "
95
+ "Please resave your workflow and this warning will go away.",
96
+ deprecated_params_list,
97
+ self.REMOVAL_VERSION,
98
+ )
99
+
100
+ # Return success with None as the value (parameter doesn't exist, so no meaningful value to return)
101
+ return SetParameterValueResultSuccess(
102
+ finalized_value=None,
103
+ data_type="any",
104
+ result_details=f"Parameter '{parameter_name}' was removed in v{self.REMOVAL_VERSION}. Please resave this workflow.",
105
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: griptape-nodes
3
- Version: 0.63.10
3
+ Version: 0.64.1
4
4
  Summary: Add your description here
5
5
  Requires-Dist: griptape>=1.8.12
6
6
  Requires-Dist: pydantic>=2.10.6
@@ -27,6 +27,7 @@ Requires-Dist: aiofiles>=25.1.0
27
27
  Requires-Dist: aioshutil>=1.5
28
28
  Requires-Dist: ruamel-yaml>=0.18.15
29
29
  Requires-Dist: asyncio-thread-runner>=1.0
30
+ Requires-Dist: portalocker>=2.10.0
30
31
  Requires-Dist: austin-dist>=3.7.0 ; extra == 'profiling'
31
32
  Requires-Python: >=3.12.0, <3.13
32
33
  Provides-Extra: profiling
@@ -55,6 +56,7 @@ This repository contains the Griptape Nodes Engine - the local component that ru
55
56
  - **📚 Full Documentation:** [docs.griptapenodes.com](https://docs.griptapenodes.com)
56
57
  - **⚙️ Installation:** [docs.griptapenodes.com/en/stable/installation/](https://docs.griptapenodes.com/en/latest/installation/)
57
58
  - **🔧 Engine Configuration:** [docs.griptapenodes.com/en/stable/configuration/](https://docs.griptapenodes.com/en/latest/configuration/)
59
+ - **📋 Migration Guide:** [MIGRATION.md](MIGRATION.md) - Guide for migrating from deprecated nodes
58
60
 
59
61
  **🧩 Extending Griptape Nodes:**
60
62