griptape-nodes 0.43.1__py3-none-any.whl → 0.44.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.
Files changed (131) hide show
  1. griptape_nodes/__init__.py +41 -51
  2. griptape_nodes/app/.python-version +0 -0
  3. griptape_nodes/app/__init__.py +0 -0
  4. griptape_nodes/app/api.py +35 -6
  5. griptape_nodes/app/app.py +0 -0
  6. griptape_nodes/app/watch.py +0 -0
  7. griptape_nodes/bootstrap/__init__.py +0 -0
  8. griptape_nodes/bootstrap/workflow_executors/__init__.py +0 -0
  9. griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +7 -1
  10. griptape_nodes/bootstrap/workflow_executors/subprocess_workflow_executor.py +90 -0
  11. griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +7 -1
  12. griptape_nodes/drivers/__init__.py +0 -0
  13. griptape_nodes/drivers/storage/__init__.py +0 -0
  14. griptape_nodes/drivers/storage/base_storage_driver.py +53 -0
  15. griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +49 -2
  16. griptape_nodes/drivers/storage/local_storage_driver.py +37 -0
  17. griptape_nodes/drivers/storage/storage_backend.py +0 -0
  18. griptape_nodes/exe_types/__init__.py +0 -0
  19. griptape_nodes/exe_types/connections.py +0 -0
  20. griptape_nodes/exe_types/core_types.py +113 -8
  21. griptape_nodes/exe_types/flow.py +0 -0
  22. griptape_nodes/exe_types/node_types.py +1 -0
  23. griptape_nodes/exe_types/type_validator.py +0 -0
  24. griptape_nodes/machines/__init__.py +0 -0
  25. griptape_nodes/machines/control_flow.py +5 -4
  26. griptape_nodes/machines/fsm.py +0 -0
  27. griptape_nodes/machines/node_resolution.py +110 -74
  28. griptape_nodes/mcp_server/__init__.py +0 -0
  29. griptape_nodes/mcp_server/server.py +16 -8
  30. griptape_nodes/mcp_server/ws_request_manager.py +0 -0
  31. griptape_nodes/node_library/__init__.py +0 -0
  32. griptape_nodes/node_library/advanced_node_library.py +0 -0
  33. griptape_nodes/node_library/library_registry.py +0 -0
  34. griptape_nodes/node_library/workflow_registry.py +0 -0
  35. griptape_nodes/py.typed +0 -0
  36. griptape_nodes/retained_mode/__init__.py +0 -0
  37. griptape_nodes/retained_mode/events/__init__.py +0 -0
  38. griptape_nodes/retained_mode/events/agent_events.py +0 -0
  39. griptape_nodes/retained_mode/events/app_events.py +0 -6
  40. griptape_nodes/retained_mode/events/arbitrary_python_events.py +0 -0
  41. griptape_nodes/retained_mode/events/base_events.py +6 -7
  42. griptape_nodes/retained_mode/events/config_events.py +0 -0
  43. griptape_nodes/retained_mode/events/connection_events.py +0 -0
  44. griptape_nodes/retained_mode/events/context_events.py +0 -0
  45. griptape_nodes/retained_mode/events/execution_events.py +0 -0
  46. griptape_nodes/retained_mode/events/flow_events.py +2 -1
  47. griptape_nodes/retained_mode/events/generate_request_payload_schemas.py +0 -0
  48. griptape_nodes/retained_mode/events/library_events.py +0 -0
  49. griptape_nodes/retained_mode/events/logger_events.py +0 -0
  50. griptape_nodes/retained_mode/events/node_events.py +36 -0
  51. griptape_nodes/retained_mode/events/object_events.py +0 -0
  52. griptape_nodes/retained_mode/events/os_events.py +98 -6
  53. griptape_nodes/retained_mode/events/parameter_events.py +0 -0
  54. griptape_nodes/retained_mode/events/payload_registry.py +0 -0
  55. griptape_nodes/retained_mode/events/secrets_events.py +0 -0
  56. griptape_nodes/retained_mode/events/static_file_events.py +0 -0
  57. griptape_nodes/retained_mode/events/validation_events.py +0 -0
  58. griptape_nodes/retained_mode/events/workflow_events.py +0 -0
  59. griptape_nodes/retained_mode/griptape_nodes.py +1 -4
  60. griptape_nodes/retained_mode/managers/__init__.py +0 -0
  61. griptape_nodes/retained_mode/managers/agent_manager.py +0 -0
  62. griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +0 -0
  63. griptape_nodes/retained_mode/managers/config_manager.py +1 -1
  64. griptape_nodes/retained_mode/managers/context_manager.py +0 -0
  65. griptape_nodes/retained_mode/managers/engine_identity_manager.py +0 -0
  66. griptape_nodes/retained_mode/managers/event_manager.py +0 -0
  67. griptape_nodes/retained_mode/managers/flow_manager.py +6 -0
  68. griptape_nodes/retained_mode/managers/library_lifecycle/__init__.py +0 -0
  69. griptape_nodes/retained_mode/managers/library_lifecycle/data_models.py +0 -0
  70. griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +0 -0
  71. griptape_nodes/retained_mode/managers/library_lifecycle/library_fsm.py +0 -0
  72. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/__init__.py +0 -0
  73. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/base.py +0 -0
  74. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/github.py +0 -0
  75. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/local_file.py +0 -0
  76. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/package.py +0 -0
  77. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/sandbox.py +0 -0
  78. griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance.py +0 -0
  79. griptape_nodes/retained_mode/managers/library_lifecycle/library_status.py +0 -0
  80. griptape_nodes/retained_mode/managers/library_manager.py +2 -8
  81. griptape_nodes/retained_mode/managers/node_manager.py +76 -5
  82. griptape_nodes/retained_mode/managers/object_manager.py +0 -0
  83. griptape_nodes/retained_mode/managers/operation_manager.py +0 -0
  84. griptape_nodes/retained_mode/managers/os_manager.py +133 -8
  85. griptape_nodes/retained_mode/managers/secrets_manager.py +0 -0
  86. griptape_nodes/retained_mode/managers/session_manager.py +0 -0
  87. griptape_nodes/retained_mode/managers/settings.py +0 -0
  88. griptape_nodes/retained_mode/managers/static_files_manager.py +0 -0
  89. griptape_nodes/retained_mode/managers/version_compatibility_manager.py +0 -0
  90. griptape_nodes/retained_mode/managers/workflow_manager.py +54 -5
  91. griptape_nodes/retained_mode/retained_mode.py +0 -0
  92. griptape_nodes/retained_mode/utils/__init__.py +0 -0
  93. griptape_nodes/retained_mode/utils/engine_identity.py +0 -0
  94. griptape_nodes/retained_mode/utils/name_generator.py +0 -0
  95. griptape_nodes/traits/__init__.py +0 -0
  96. griptape_nodes/traits/add_param_button.py +0 -0
  97. griptape_nodes/traits/button.py +0 -0
  98. griptape_nodes/traits/clamp.py +0 -0
  99. griptape_nodes/traits/compare.py +0 -0
  100. griptape_nodes/traits/compare_images.py +0 -0
  101. griptape_nodes/traits/file_system_picker.py +18 -0
  102. griptape_nodes/traits/minmax.py +0 -0
  103. griptape_nodes/traits/options.py +0 -0
  104. griptape_nodes/traits/slider.py +0 -0
  105. griptape_nodes/traits/trait_registry.py +0 -0
  106. griptape_nodes/traits/traits.json +0 -0
  107. griptape_nodes/updater/__init__.py +0 -0
  108. griptape_nodes/updater/__main__.py +0 -0
  109. griptape_nodes/utils/__init__.py +0 -0
  110. griptape_nodes/utils/dict_utils.py +0 -0
  111. griptape_nodes/utils/image_preview.py +0 -0
  112. griptape_nodes/utils/metaclasses.py +0 -0
  113. griptape_nodes/utils/version_utils.py +51 -0
  114. griptape_nodes/version_compatibility/__init__.py +0 -0
  115. griptape_nodes/version_compatibility/versions/__init__.py +0 -0
  116. griptape_nodes/version_compatibility/versions/v0_39_0/__init__.py +0 -0
  117. griptape_nodes/version_compatibility/versions/v0_39_0/modified_parameters_set_removal.py +0 -0
  118. {griptape_nodes-0.43.1.dist-info → griptape_nodes-0.44.0.dist-info}/METADATA +1 -1
  119. {griptape_nodes-0.43.1.dist-info → griptape_nodes-0.44.0.dist-info}/RECORD +31 -39
  120. {griptape_nodes-0.43.1.dist-info → griptape_nodes-0.44.0.dist-info}/WHEEL +1 -1
  121. griptape_nodes/bootstrap/bootstrap_script.py +0 -54
  122. griptape_nodes/bootstrap/post_build_install_script.sh +0 -3
  123. griptape_nodes/bootstrap/pre_build_install_script.sh +0 -4
  124. griptape_nodes/bootstrap/register_libraries_script.py +0 -32
  125. griptape_nodes/bootstrap/structure_config.yaml +0 -15
  126. griptape_nodes/bootstrap/workflow_runners/__init__.py +0 -1
  127. griptape_nodes/bootstrap/workflow_runners/bootstrap_workflow_runner.py +0 -28
  128. griptape_nodes/bootstrap/workflow_runners/local_workflow_runner.py +0 -237
  129. griptape_nodes/bootstrap/workflow_runners/subprocess_workflow_runner.py +0 -62
  130. griptape_nodes/bootstrap/workflow_runners/workflow_runner.py +0 -11
  131. {griptape_nodes-0.43.1.dist-info → griptape_nodes-0.44.0.dist-info}/entry_points.txt +0 -0
@@ -244,6 +244,11 @@ class BaseNodeElement:
244
244
  "name": self.name,
245
245
  "node_name": self._node_context.name,
246
246
  }
247
+ # If ui_options changed, send the complete ui_options from to_dict()
248
+ complete_dict = self.to_dict()
249
+ if "ui_options" in complete_dict:
250
+ self._changes["ui_options"] = complete_dict["ui_options"]
251
+
247
252
  event_data.update(self._changes)
248
253
 
249
254
  # Publish the event
@@ -384,13 +389,97 @@ class ParameterMessage(BaseNodeElement):
384
389
  type VariantType = Literal["info", "warning", "error", "success", "tip", "none"]
385
390
 
386
391
  element_type: str = field(default_factory=lambda: ParameterMessage.__name__)
387
- variant: VariantType
388
- title: str | None = None
389
- value: str
390
- button_link: str | None = None
391
- button_text: str | None = None
392
- full_width: bool = False
393
- ui_options: dict = field(default_factory=dict)
392
+ _variant: VariantType = field(init=False)
393
+ _title: str | None = field(default=None, init=False)
394
+ _value: str = field(init=False)
395
+ _button_link: str | None = field(default=None, init=False)
396
+ _button_text: str | None = field(default=None, init=False)
397
+ _full_width: bool = field(default=False, init=False)
398
+ _ui_options: dict = field(default_factory=dict, init=False)
399
+
400
+ def __init__( # noqa: PLR0913
401
+ self,
402
+ variant: VariantType,
403
+ value: str,
404
+ *,
405
+ title: str | None = None,
406
+ button_link: str | None = None,
407
+ button_text: str | None = None,
408
+ full_width: bool = False,
409
+ ui_options: dict | None = None,
410
+ **kwargs,
411
+ ):
412
+ super().__init__(element_type=ParameterMessage.__name__, **kwargs)
413
+ self._variant = variant
414
+ self._title = title
415
+ self._value = value
416
+ self._button_link = button_link
417
+ self._button_text = button_text
418
+ self._full_width = full_width
419
+ self._ui_options = ui_options or {}
420
+
421
+ @property
422
+ def variant(self) -> VariantType:
423
+ return self._variant
424
+
425
+ @variant.setter
426
+ @BaseNodeElement.emits_update_on_write
427
+ def variant(self, value: VariantType) -> None:
428
+ self._variant = value
429
+
430
+ @property
431
+ def title(self) -> str | None:
432
+ return self._title
433
+
434
+ @title.setter
435
+ @BaseNodeElement.emits_update_on_write
436
+ def title(self, value: str | None) -> None:
437
+ self._title = value
438
+
439
+ @property
440
+ def value(self) -> str:
441
+ return self._value
442
+
443
+ @value.setter
444
+ @BaseNodeElement.emits_update_on_write
445
+ def value(self, value: str) -> None:
446
+ self._value = value
447
+
448
+ @property
449
+ def button_link(self) -> str | None:
450
+ return self._button_link
451
+
452
+ @button_link.setter
453
+ @BaseNodeElement.emits_update_on_write
454
+ def button_link(self, value: str | None) -> None:
455
+ self._button_link = value
456
+
457
+ @property
458
+ def button_text(self) -> str | None:
459
+ return self._button_text
460
+
461
+ @button_text.setter
462
+ @BaseNodeElement.emits_update_on_write
463
+ def button_text(self, value: str | None) -> None:
464
+ self._button_text = value
465
+
466
+ @property
467
+ def full_width(self) -> bool:
468
+ return self._full_width
469
+
470
+ @full_width.setter
471
+ @BaseNodeElement.emits_update_on_write
472
+ def full_width(self, value: bool) -> None:
473
+ self._full_width = value
474
+
475
+ @property
476
+ def ui_options(self) -> dict:
477
+ return self._ui_options
478
+
479
+ @ui_options.setter
480
+ @BaseNodeElement.emits_update_on_write
481
+ def ui_options(self, value: dict) -> None:
482
+ self._ui_options = value
394
483
 
395
484
  def to_dict(self) -> dict[str, Any]:
396
485
  data = super().to_dict()
@@ -594,7 +683,7 @@ class Parameter(BaseNodeElement):
594
683
  if not element_id:
595
684
  element_id = str(uuid.uuid4().hex)
596
685
  if not element_type:
597
- element_type = BaseNodeElement.__name__
686
+ element_type = self.__class__.__name__
598
687
  super().__init__(element_id=element_id, element_type=element_type)
599
688
  self.name = name
600
689
  self.tooltip = tooltip
@@ -951,6 +1040,7 @@ class ControlParameter(Parameter, ABC):
951
1040
  traits: set[Trait.__class__ | Trait] | None = None,
952
1041
  converters: list[Callable[[Any], Any]] | None = None,
953
1042
  validators: list[Callable[[Parameter, Any], None]] | None = None,
1043
+ ui_options: dict | None = None,
954
1044
  *,
955
1045
  user_defined: bool = False,
956
1046
  ):
@@ -970,6 +1060,7 @@ class ControlParameter(Parameter, ABC):
970
1060
  traits=traits,
971
1061
  converters=converters,
972
1062
  validators=validators,
1063
+ ui_options=ui_options,
973
1064
  user_defined=user_defined,
974
1065
  element_type=self.__class__.__name__,
975
1066
  )
@@ -980,6 +1071,7 @@ class ControlParameterInput(ControlParameter):
980
1071
  self,
981
1072
  tooltip: str | list[dict] = "Connection from previous node in the execution chain",
982
1073
  name: str = "exec_in",
1074
+ display_name: str | None = "Flow In",
983
1075
  tooltip_as_input: str | list[dict] | None = None,
984
1076
  tooltip_as_property: str | list[dict] | None = None,
985
1077
  tooltip_as_output: str | list[dict] | None = None,
@@ -992,6 +1084,11 @@ class ControlParameterInput(ControlParameter):
992
1084
  allowed_modes = {ParameterMode.INPUT}
993
1085
  input_types = [ParameterTypeBuiltin.CONTROL_TYPE.value]
994
1086
 
1087
+ if display_name is None:
1088
+ ui_options = None
1089
+ else:
1090
+ ui_options = {"display_name": display_name}
1091
+
995
1092
  # Call parent with a few explicit tweaks.
996
1093
  super().__init__(
997
1094
  name=name,
@@ -1005,6 +1102,7 @@ class ControlParameterInput(ControlParameter):
1005
1102
  traits=traits,
1006
1103
  converters=converters,
1007
1104
  validators=validators,
1105
+ ui_options=ui_options,
1008
1106
  user_defined=user_defined,
1009
1107
  )
1010
1108
 
@@ -1014,6 +1112,7 @@ class ControlParameterOutput(ControlParameter):
1014
1112
  self,
1015
1113
  tooltip: str | list[dict] = "Connection to the next node in the execution chain",
1016
1114
  name: str = "exec_out",
1115
+ display_name: str | None = "Flow Out",
1017
1116
  tooltip_as_input: str | list[dict] | None = None,
1018
1117
  tooltip_as_property: str | list[dict] | None = None,
1019
1118
  tooltip_as_output: str | list[dict] | None = None,
@@ -1026,6 +1125,11 @@ class ControlParameterOutput(ControlParameter):
1026
1125
  allowed_modes = {ParameterMode.OUTPUT}
1027
1126
  output_type = ParameterTypeBuiltin.CONTROL_TYPE.value
1028
1127
 
1128
+ if display_name is None:
1129
+ ui_options = None
1130
+ else:
1131
+ ui_options = {"display_name": display_name}
1132
+
1029
1133
  # Call parent with a few explicit tweaks.
1030
1134
  super().__init__(
1031
1135
  name=name,
@@ -1039,6 +1143,7 @@ class ControlParameterOutput(ControlParameter):
1039
1143
  traits=traits,
1040
1144
  converters=converters,
1041
1145
  validators=validators,
1146
+ ui_options=ui_options,
1042
1147
  user_defined=user_defined,
1043
1148
  )
1044
1149
 
File without changes
@@ -68,6 +68,7 @@ class BaseNode(ABC):
68
68
  _entry_control_parameter: Parameter | None = (
69
69
  None # The control input parameter used to enter this node during execution
70
70
  )
71
+ lock: bool = False # When lock is true, the node is locked and can't be modified. When lock is false, the node is unlocked and can be modified.
71
72
 
72
73
  @property
73
74
  def parameters(self) -> list[Parameter]:
File without changes
File without changes
@@ -88,11 +88,12 @@ class ResolveNodeState(State):
88
88
  return CompleteState
89
89
 
90
90
  # Mark the node unresolved, and broadcast an event to the GUI.
91
- context.current_node.make_node_unresolved(
92
- current_states_to_trigger_change_event=set(
93
- {NodeResolutionState.UNRESOLVED, NodeResolutionState.RESOLVED, NodeResolutionState.RESOLVING}
91
+ if not context.current_node.lock:
92
+ context.current_node.make_node_unresolved(
93
+ current_states_to_trigger_change_event=set(
94
+ {NodeResolutionState.UNRESOLVED, NodeResolutionState.RESOLVED, NodeResolutionState.RESOLVING}
95
+ )
94
96
  )
95
- )
96
97
  # Now broadcast that we have a current control node.
97
98
  EventBus.publish_event(
98
99
  ExecutionGriptapeNodeEvent(
File without changes
@@ -195,10 +195,56 @@ class ExecuteNodeState(State):
195
195
  EventBus.publish_event(ExecutionGriptapeNodeEvent(wrapped_event=ExecutionEvent(payload=payload)))
196
196
  current_node.parameter_output_values.clear()
197
197
 
198
+ @staticmethod
199
+ def collect_values_from_upstream_nodes(context: ResolutionContext) -> None:
200
+ """Collect output values from resolved upstream nodes and pass them to the current node.
201
+
202
+ This method iterates through all input parameters of the current node, finds their
203
+ connected upstream nodes, and if those nodes are resolved, retrieves their output
204
+ values and passes them through using SetParameterValueRequest.
205
+
206
+ Args:
207
+ context (ResolutionContext): The resolution context containing the focus stack
208
+ with the current node being processed.
209
+ """
210
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
211
+
212
+ current_node = context.focus_stack[-1].node
213
+ connections = GriptapeNodes.FlowManager().get_connections()
214
+
215
+ for parameter in current_node.parameters:
216
+ # Skip control type parameters
217
+ if ParameterTypeBuiltin.CONTROL_TYPE.value.lower() == parameter.output_type:
218
+ continue
219
+
220
+ # Get the connected upstream node for this parameter
221
+ upstream_connection = connections.get_connected_node(current_node, parameter)
222
+ if upstream_connection:
223
+ upstream_node, upstream_parameter = upstream_connection
224
+
225
+ # If the upstream node is resolved, collect its output value
226
+ if upstream_parameter.name in upstream_node.parameter_output_values:
227
+ output_value = upstream_node.parameter_output_values[upstream_parameter.name]
228
+
229
+ # Pass the value through using the same mechanism as normal resolution
230
+ GriptapeNodes.get_instance().handle_request(
231
+ SetParameterValueRequest(
232
+ parameter_name=parameter.name,
233
+ node_name=current_node.name,
234
+ value=output_value,
235
+ data_type=upstream_parameter.output_type,
236
+ )
237
+ )
238
+
198
239
  @staticmethod
199
240
  def on_enter(context: ResolutionContext) -> type[State] | None:
200
241
  current_node = context.focus_stack[-1].node
242
+
201
243
  # Clear all of the current output values
244
+ # if node is locked, don't clear anything. skip all of this.
245
+ if current_node.lock:
246
+ return ExecuteNodeState
247
+ ExecuteNodeState.collect_values_from_upstream_nodes(context)
202
248
  ExecuteNodeState.clear_parameter_output_values(context)
203
249
  for parameter in current_node.parameters:
204
250
  if ParameterTypeBuiltin.CONTROL_TYPE.value.lower() == parameter.output_type:
@@ -244,95 +290,85 @@ class ExecuteNodeState(State):
244
290
  # Once everything has been set
245
291
  current_focus = context.focus_stack[-1]
246
292
  current_node = current_focus.node
247
- # To set the event manager without circular import errors
248
- EventBus.publish_event(
249
- ExecutionGriptapeNodeEvent(
250
- wrapped_event=ExecutionEvent(payload=NodeStartProcessEvent(node_name=current_node.name))
251
- )
252
- )
253
- logger.info("Node '%s' is processing.", current_node.name)
254
-
255
- try:
256
- work_is_scheduled = ExecuteNodeState._process_node(current_focus)
257
- if work_is_scheduled:
258
- logger.debug("Pausing Node '%s' to run background work", current_node.name)
259
- return None
260
- except Exception as e:
261
- logger.exception("Error processing node '%s", current_node.name)
262
- msg = f"Canceling flow run. Node '{current_node.name}' encountered a problem: {e}"
263
- # Mark the node as unresolved, broadcasting to everyone.
264
- current_node.make_node_unresolved(
265
- current_states_to_trigger_change_event=set(
266
- {NodeResolutionState.UNRESOLVED, NodeResolutionState.RESOLVED, NodeResolutionState.RESOLVING}
267
- )
268
- )
269
- current_focus.process_generator = None
270
- current_focus.scheduled_value = None
271
-
272
- from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
273
-
274
- GriptapeNodes.FlowManager().cancel_flow_run()
275
-
293
+ # If the node is not locked, execute all of this.
294
+ if not current_node.lock:
295
+ # To set the event manager without circular import errors
276
296
  EventBus.publish_event(
277
297
  ExecutionGriptapeNodeEvent(
278
- wrapped_event=ExecutionEvent(payload=NodeFinishProcessEvent(node_name=current_node.name))
298
+ wrapped_event=ExecutionEvent(payload=NodeStartProcessEvent(node_name=current_node.name))
279
299
  )
280
300
  )
281
- raise RuntimeError(msg) from e
301
+ logger.info("Node '%s' is processing.", current_node.name)
282
302
 
283
- logger.info("Node '%s' finished processing.", current_node.name)
303
+ try:
304
+ work_is_scheduled = ExecuteNodeState._process_node(current_focus)
305
+ if work_is_scheduled:
306
+ logger.debug("Pausing Node '%s' to run background work", current_node.name)
307
+ return None
308
+ except Exception as e:
309
+ logger.exception("Error processing node '%s", current_node.name)
310
+ msg = f"Canceling flow run. Node '{current_node.name}' encountered a problem: {e}"
311
+ # Mark the node as unresolved, broadcasting to everyone.
312
+ current_node.make_node_unresolved(
313
+ current_states_to_trigger_change_event=set(
314
+ {NodeResolutionState.UNRESOLVED, NodeResolutionState.RESOLVED, NodeResolutionState.RESOLVING}
315
+ )
316
+ )
317
+ current_focus.process_generator = None
318
+ current_focus.scheduled_value = None
284
319
 
285
- EventBus.publish_event(
286
- ExecutionGriptapeNodeEvent(
287
- wrapped_event=ExecutionEvent(payload=NodeFinishProcessEvent(node_name=current_node.name))
288
- )
289
- )
290
- current_node.state = NodeResolutionState.RESOLVED
291
- details = f"'{current_node.name}' resolved."
320
+ from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
292
321
 
293
- logger.info(details)
322
+ GriptapeNodes.FlowManager().cancel_flow_run()
294
323
 
295
- # Serialization can be slow so only do it if the user wants debug details.
296
- if logger.level <= logging.DEBUG:
297
- logger.debug(
298
- "INPUTS: %s\nOUTPUTS: %s",
299
- TypeValidator.safe_serialize(current_node.parameter_values),
300
- TypeValidator.safe_serialize(current_node.parameter_output_values),
301
- )
324
+ EventBus.publish_event(
325
+ ExecutionGriptapeNodeEvent(
326
+ wrapped_event=ExecutionEvent(payload=NodeFinishProcessEvent(node_name=current_node.name))
327
+ )
328
+ )
329
+ raise RuntimeError(msg) from e
330
+
331
+ logger.info("Node '%s' finished processing.", current_node.name)
302
332
 
303
- for parameter_name, value in current_node.parameter_output_values.items():
304
- parameter = current_node.get_parameter_by_name(parameter_name)
305
- if parameter is None:
306
- err = f"Canceling flow run. Node '{current_node.name}' specified a Parameter '{parameter_name}', but no such Parameter could be found on that Node."
307
- raise KeyError(err)
308
- data_type = parameter.type
309
- if data_type is None:
310
- data_type = ParameterTypeBuiltin.NONE.value
311
333
  EventBus.publish_event(
312
334
  ExecutionGriptapeNodeEvent(
313
- wrapped_event=ExecutionEvent(
314
- payload=ParameterValueUpdateEvent(
315
- node_name=current_node.name,
316
- parameter_name=parameter_name,
317
- data_type=data_type,
318
- value=TypeValidator.safe_serialize(value),
319
- )
320
- ),
335
+ wrapped_event=ExecutionEvent(payload=NodeFinishProcessEvent(node_name=current_node.name))
321
336
  )
322
337
  )
323
- # Pass the value through to the new nodes.
324
- conn_output_nodes = GriptapeNodes.FlowManager().get_connected_output_parameters(current_node, parameter)
325
- for target_node, target_parameter in conn_output_nodes:
326
- GriptapeNodes.get_instance().handle_request(
327
- SetParameterValueRequest(
328
- parameter_name=target_parameter.name,
329
- node_name=target_node.name,
330
- value=value,
331
- data_type=parameter.output_type,
332
- )
338
+ current_node.state = NodeResolutionState.RESOLVED
339
+ details = f"'{current_node.name}' resolved."
340
+
341
+ logger.info(details)
342
+
343
+ # Serialization can be slow so only do it if the user wants debug details.
344
+ if logger.level <= logging.DEBUG:
345
+ logger.debug(
346
+ "INPUTS: %s\nOUTPUTS: %s",
347
+ TypeValidator.safe_serialize(current_node.parameter_values),
348
+ TypeValidator.safe_serialize(current_node.parameter_output_values),
333
349
  )
334
350
 
335
- # Output values should already be saved!
351
+ for parameter_name, value in current_node.parameter_output_values.items():
352
+ parameter = current_node.get_parameter_by_name(parameter_name)
353
+ if parameter is None:
354
+ err = f"Canceling flow run. Node '{current_node.name}' specified a Parameter '{parameter_name}', but no such Parameter could be found on that Node."
355
+ raise KeyError(err)
356
+ data_type = parameter.type
357
+ if data_type is None:
358
+ data_type = ParameterTypeBuiltin.NONE.value
359
+ EventBus.publish_event(
360
+ ExecutionGriptapeNodeEvent(
361
+ wrapped_event=ExecutionEvent(
362
+ payload=ParameterValueUpdateEvent(
363
+ node_name=current_node.name,
364
+ parameter_name=parameter_name,
365
+ data_type=data_type,
366
+ value=TypeValidator.safe_serialize(value),
367
+ )
368
+ ),
369
+ )
370
+ )
371
+ # Output values should already be saved!
336
372
  library = LibraryRegistry.get_libraries_with_node_type(current_node.__class__.__name__)
337
373
  if len(library) == 1:
338
374
  library_name = library[0]
File without changes
@@ -57,19 +57,21 @@ SUPPORTED_REQUEST_EVENTS: dict[str, type[RequestPayload]] = {
57
57
  "SetParameterValueRequest": SetParameterValueRequest,
58
58
  }
59
59
 
60
+ GTN_MCP_SERVER_HOST = os.getenv("GTN_MCP_SERVER_HOST", "localhost")
60
61
  GTN_MCP_SERVER_PORT = int(os.getenv("GTN_MCP_SERVER_PORT", "9927"))
62
+ GTN_MCP_SERVER_LOG_LEVEL = os.getenv("GTN_MCP_SERVER_LOG_LEVEL", "ERROR").lower()
61
63
 
62
64
  config_manager = ConfigManager()
63
65
  secrets_manager = SecretsManager(config_manager)
64
66
 
67
+ mcp_server_logger = logging.getLogger("griptape_nodes_mcp_server")
68
+ mcp_server_logger.addHandler(RichHandler(show_time=True, show_path=False, markup=True, rich_tracebacks=True))
69
+ mcp_server_logger.setLevel(logging.INFO)
70
+
65
71
 
66
72
  def main(api_key: str) -> None:
67
73
  """Main entry point for the Griptape Nodes MCP server."""
68
- mcp_server_logger = logging.getLogger("griptape_nodes_mcp_server")
69
- mcp_server_logger.addHandler(RichHandler(show_time=True, show_path=False, markup=True, rich_tracebacks=True))
70
- mcp_server_logger.setLevel(logging.INFO)
71
- mcp_server_logger.info("Starting MCP GTN server...")
72
-
74
+ mcp_server_logger.debug("Starting MCP GTN server...")
73
75
  # Give these a session ID
74
76
  connection_manager = WebSocketConnectionManager()
75
77
  request_manager = AsyncRequestManager(connection_manager, api_key)
@@ -108,11 +110,11 @@ def main(api_key: str) -> None:
108
110
  async def lifespan(_: FastAPI) -> AsyncIterator[None]:
109
111
  """Context manager for managing session manager lifecycle."""
110
112
  async with session_manager.run():
111
- mcp_server_logger.info("GTN MCP server started with StreamableHTTP session manager!")
113
+ mcp_server_logger.debug("GTN MCP server started with StreamableHTTP session manager!")
112
114
  try:
113
115
  yield
114
116
  finally:
115
- mcp_server_logger.info("GTN MCP server shutting down...")
117
+ mcp_server_logger.debug("GTN MCP server shutting down...")
116
118
 
117
119
  # Create an ASGI application using the transport
118
120
  mcp_server_app = FastAPI(lifespan=lifespan)
@@ -123,4 +125,10 @@ def main(api_key: str) -> None:
123
125
 
124
126
  mcp_server_app.mount("/mcp", app=handle_streamable_http)
125
127
 
126
- uvicorn.run(mcp_server_app, host="127.0.0.1", port=GTN_MCP_SERVER_PORT)
128
+ uvicorn.run(
129
+ mcp_server_app,
130
+ host=GTN_MCP_SERVER_HOST,
131
+ port=GTN_MCP_SERVER_PORT,
132
+ log_config=None,
133
+ log_level=GTN_MCP_SERVER_LOG_LEVEL,
134
+ )
File without changes
File without changes
File without changes
File without changes
File without changes
griptape_nodes/py.typed CHANGED
File without changes
File without changes
File without changes
File without changes
@@ -18,15 +18,9 @@ class AppStartSessionRequest(RequestPayload):
18
18
  Use when: Initializing client connections, beginning new workflow sessions,
19
19
  setting up isolated execution environments, managing session state.
20
20
 
21
- Args:
22
- session_id: Specific session ID to use (None for auto-generated)
23
-
24
21
  Results: AppStartSessionResultSuccess (with session ID) | AppStartSessionResultFailure (session creation error)
25
22
  """
26
23
 
27
- # TODO: https://github.com/griptape-ai/griptape-nodes/issues/1600
28
- session_id: str | None = None
29
-
30
24
 
31
25
  @dataclass
32
26
  @PayloadRegistry.register
@@ -10,7 +10,7 @@ from griptape.events import BaseEvent as GtBaseEvent
10
10
  from griptape.mixins.serializable_mixin import SerializableMixin
11
11
  from griptape.structures import Structure
12
12
  from griptape.tools import BaseTool
13
- from pydantic import BaseModel, Field
13
+ from pydantic import BaseModel, ConfigDict, Field
14
14
 
15
15
  if TYPE_CHECKING:
16
16
  import builtins
@@ -118,16 +118,15 @@ class BaseEvent(BaseModel, ABC):
118
118
  session_id: str | None = Field(default_factory=lambda: BaseEvent._session_id)
119
119
 
120
120
  # Custom JSON encoder for the payload
121
- class Config:
122
- """Pydantic configuration for the BaseEvent class."""
123
-
124
- arbitrary_types_allowed = True
125
- json_encoders: ClassVar[dict] = {
121
+ model_config = ConfigDict(
122
+ arbitrary_types_allowed=True,
123
+ json_encoders={
126
124
  # Use to_dict() methods for Griptape objects
127
125
  BaseArtifact: lambda obj: obj.to_dict(),
128
126
  BaseTool: lambda obj: obj.to_dict(),
129
127
  Structure: lambda obj: obj.to_dict(),
130
- }
128
+ },
129
+ )
131
130
 
132
131
  def dict(self, *args, **kwargs) -> dict[str, Any]:
133
132
  """Override dict to handle payload serialization and add event_type."""
File without changes
File without changes
File without changes
File without changes
@@ -9,7 +9,7 @@ from griptape_nodes.retained_mode.events.base_events import (
9
9
  WorkflowAlteredMixin,
10
10
  WorkflowNotAlteredMixin,
11
11
  )
12
- from griptape_nodes.retained_mode.events.node_events import SerializedNodeCommands
12
+ from griptape_nodes.retained_mode.events.node_events import SerializedNodeCommands, SetLockNodeStateRequest
13
13
  from griptape_nodes.retained_mode.events.payload_registry import PayloadRegistry
14
14
  from griptape_nodes.retained_mode.events.workflow_events import ImportWorkflowAsReferencedSubFlowRequest
15
15
 
@@ -229,6 +229,7 @@ class SerializedFlowCommands:
229
229
  set_parameter_value_commands: dict[
230
230
  SerializedNodeCommands.NodeUUID, list[SerializedNodeCommands.IndirectSetParameterValueCommand]
231
231
  ]
232
+ set_lock_commands_per_node: dict[SerializedNodeCommands.NodeUUID, SetLockNodeStateRequest]
232
233
  sub_flows_commands: list["SerializedFlowCommands"]
233
234
  referenced_workflows: set[str]
234
235
 
File without changes
File without changes