griptape-nodes 0.60.3__py3-none-any.whl → 0.61.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 (47) hide show
  1. griptape_nodes/bootstrap/workflow_publishers/local_workflow_publisher.py +0 -1
  2. griptape_nodes/common/macro_parser/__init__.py +16 -1
  3. griptape_nodes/common/macro_parser/core.py +15 -3
  4. griptape_nodes/common/macro_parser/exceptions.py +99 -0
  5. griptape_nodes/common/macro_parser/formats.py +13 -4
  6. griptape_nodes/common/macro_parser/matching.py +5 -2
  7. griptape_nodes/common/macro_parser/parsing.py +48 -8
  8. griptape_nodes/common/macro_parser/resolution.py +23 -5
  9. griptape_nodes/common/project_templates/__init__.py +49 -0
  10. griptape_nodes/common/project_templates/default_project_template.py +92 -0
  11. griptape_nodes/common/project_templates/defaults/README.md +36 -0
  12. griptape_nodes/common/project_templates/defaults/project_template.yml +89 -0
  13. griptape_nodes/common/project_templates/directory.py +67 -0
  14. griptape_nodes/common/project_templates/loader.py +341 -0
  15. griptape_nodes/common/project_templates/project.py +252 -0
  16. griptape_nodes/common/project_templates/situation.py +155 -0
  17. griptape_nodes/common/project_templates/validation.py +140 -0
  18. griptape_nodes/exe_types/core_types.py +36 -3
  19. griptape_nodes/exe_types/node_types.py +4 -2
  20. griptape_nodes/exe_types/param_components/progress_bar_component.py +57 -0
  21. griptape_nodes/exe_types/param_types/parameter_audio.py +243 -0
  22. griptape_nodes/exe_types/param_types/parameter_image.py +243 -0
  23. griptape_nodes/exe_types/param_types/parameter_three_d.py +215 -0
  24. griptape_nodes/exe_types/param_types/parameter_video.py +243 -0
  25. griptape_nodes/node_library/workflow_registry.py +1 -1
  26. griptape_nodes/retained_mode/events/execution_events.py +41 -0
  27. griptape_nodes/retained_mode/events/node_events.py +90 -1
  28. griptape_nodes/retained_mode/events/os_events.py +108 -0
  29. griptape_nodes/retained_mode/events/parameter_events.py +1 -1
  30. griptape_nodes/retained_mode/events/project_events.py +413 -0
  31. griptape_nodes/retained_mode/events/workflow_events.py +19 -1
  32. griptape_nodes/retained_mode/griptape_nodes.py +9 -1
  33. griptape_nodes/retained_mode/managers/agent_manager.py +18 -24
  34. griptape_nodes/retained_mode/managers/event_manager.py +6 -9
  35. griptape_nodes/retained_mode/managers/flow_manager.py +63 -0
  36. griptape_nodes/retained_mode/managers/library_manager.py +55 -42
  37. griptape_nodes/retained_mode/managers/mcp_manager.py +14 -6
  38. griptape_nodes/retained_mode/managers/node_manager.py +232 -0
  39. griptape_nodes/retained_mode/managers/os_manager.py +346 -1
  40. griptape_nodes/retained_mode/managers/project_manager.py +617 -0
  41. griptape_nodes/retained_mode/managers/settings.py +6 -0
  42. griptape_nodes/retained_mode/managers/workflow_manager.py +17 -71
  43. griptape_nodes/traits/button.py +18 -0
  44. {griptape_nodes-0.60.3.dist-info → griptape_nodes-0.61.0.dist-info}/METADATA +5 -3
  45. {griptape_nodes-0.60.3.dist-info → griptape_nodes-0.61.0.dist-info}/RECORD +47 -31
  46. {griptape_nodes-0.60.3.dist-info → griptape_nodes-0.61.0.dist-info}/WHEEL +1 -1
  47. {griptape_nodes-0.60.3.dist-info → griptape_nodes-0.61.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,243 @@
1
+ """ParameterVideo component for video inputs with enhanced UI options."""
2
+
3
+ from collections.abc import Callable
4
+ from typing import Any
5
+
6
+ from griptape_nodes.exe_types.core_types import Parameter, ParameterMode, Trait
7
+
8
+
9
+ class ParameterVideo(Parameter):
10
+ """A specialized Parameter class for video inputs with enhanced UI options.
11
+
12
+ This class provides a convenient way to create video parameters with common
13
+ UI customizations like file browser, webcam capture, and video editing.
14
+ It exposes these UI options as direct properties for easy runtime modification.
15
+
16
+ Example:
17
+ param = ParameterVideo(
18
+ name="input_video",
19
+ tooltip="Select a video",
20
+ clickable_file_browser=True,
21
+ webcam_capture_video=True,
22
+ edit_video=True
23
+ )
24
+ param.pulse_on_run = True # Change UI options at runtime
25
+ """
26
+
27
+ def __init__( # noqa: PLR0913
28
+ self,
29
+ name: str,
30
+ tooltip: str | None = None,
31
+ *,
32
+ type: str = "VideoUrlArtifact", # noqa: A002, ARG002
33
+ input_types: list[str] | None = None, # noqa: ARG002
34
+ output_type: str = "VideoUrlArtifact", # noqa: ARG002
35
+ default_value: Any = None,
36
+ tooltip_as_input: str | None = None,
37
+ tooltip_as_property: str | None = None,
38
+ tooltip_as_output: str | None = None,
39
+ allowed_modes: set[ParameterMode] | None = None,
40
+ traits: set[type[Trait] | Trait] | None = None,
41
+ converters: list[Callable[[Any], Any]] | None = None,
42
+ validators: list[Callable[[Parameter, Any], None]] | None = None,
43
+ ui_options: dict | None = None,
44
+ pulse_on_run: bool = False,
45
+ clickable_file_browser: bool = True,
46
+ webcam_capture_video: bool = False,
47
+ edit_video: bool = False,
48
+ accept_any: bool = True,
49
+ hide: bool = False,
50
+ hide_label: bool = False,
51
+ hide_property: bool = False,
52
+ allow_input: bool = True,
53
+ allow_property: bool = True,
54
+ allow_output: bool = True,
55
+ settable: bool = True,
56
+ serializable: bool = True,
57
+ user_defined: bool = False,
58
+ element_id: str | None = None,
59
+ element_type: str | None = None,
60
+ parent_container_name: str | None = None,
61
+ ) -> None:
62
+ """Initialize a video parameter with enhanced UI options.
63
+
64
+ Args:
65
+ name: Parameter name
66
+ tooltip: Parameter tooltip
67
+ type: Parameter type (ignored, always "VideoUrlArtifact" for ParameterVideo)
68
+ input_types: Allowed input types (ignored, set based on accept_any)
69
+ output_type: Output type (ignored, always "VideoUrlArtifact" for ParameterVideo)
70
+ default_value: Default parameter value
71
+ tooltip_as_input: Tooltip for input mode
72
+ tooltip_as_property: Tooltip for property mode
73
+ tooltip_as_output: Tooltip for output mode
74
+ allowed_modes: Allowed parameter modes
75
+ traits: Parameter traits
76
+ converters: Parameter converters
77
+ validators: Parameter validators
78
+ ui_options: Dictionary of UI options
79
+ pulse_on_run: Whether to pulse the parameter on run
80
+ clickable_file_browser: Whether to show clickable file browser
81
+ webcam_capture_video: Whether to enable webcam capture
82
+ edit_video: Whether to enable video editing functionality
83
+ accept_any: Whether to accept any input type and convert to video (default: True)
84
+ hide: Whether to hide the entire parameter
85
+ hide_label: Whether to hide the parameter label
86
+ hide_property: Whether to hide the parameter in property mode
87
+ allow_input: Whether to allow input mode
88
+ allow_property: Whether to allow property mode
89
+ allow_output: Whether to allow output mode
90
+ settable: Whether the parameter is settable
91
+ serializable: Whether the parameter is serializable
92
+ user_defined: Whether the parameter is user-defined
93
+ element_id: Element ID
94
+ element_type: Element type
95
+ parent_container_name: Name of parent container
96
+ """
97
+ # Build ui_options dictionary from the provided UI-specific parameters
98
+ if ui_options is None:
99
+ ui_options = {}
100
+ else:
101
+ ui_options = ui_options.copy()
102
+
103
+ # Add video-specific UI options if they have values
104
+ if pulse_on_run:
105
+ ui_options["pulse_on_run"] = pulse_on_run
106
+ if clickable_file_browser:
107
+ ui_options["clickable_file_browser"] = clickable_file_browser
108
+ if webcam_capture_video:
109
+ ui_options["webcam_capture_video"] = webcam_capture_video
110
+ if edit_video:
111
+ ui_options["edit_video"] = edit_video
112
+
113
+ # Auto-disable clickable_file_browser if neither input nor property modes are allowed
114
+ if not allow_input and not allow_property and clickable_file_browser:
115
+ ui_options.pop("clickable_file_browser", None)
116
+
117
+ # Set up input types based on accept_any setting
118
+ if accept_any:
119
+ final_input_types = ["any"]
120
+ else:
121
+ final_input_types = ["VideoUrlArtifact"]
122
+
123
+ # Call parent with explicit parameters, following ControlParameter pattern
124
+ super().__init__(
125
+ name=name,
126
+ tooltip=tooltip,
127
+ type="VideoUrlArtifact", # Always a VideoUrlArtifact type for ParameterVideo
128
+ input_types=final_input_types,
129
+ output_type="VideoUrlArtifact", # Always output as VideoUrlArtifact
130
+ default_value=default_value,
131
+ tooltip_as_input=tooltip_as_input,
132
+ tooltip_as_property=tooltip_as_property,
133
+ tooltip_as_output=tooltip_as_output,
134
+ allowed_modes=allowed_modes,
135
+ traits=traits,
136
+ converters=converters,
137
+ validators=validators,
138
+ ui_options=ui_options,
139
+ hide=hide,
140
+ hide_label=hide_label,
141
+ hide_property=hide_property,
142
+ allow_input=allow_input,
143
+ allow_property=allow_property,
144
+ allow_output=allow_output,
145
+ settable=settable,
146
+ serializable=serializable,
147
+ user_defined=user_defined,
148
+ element_id=element_id,
149
+ element_type=element_type,
150
+ parent_container_name=parent_container_name,
151
+ )
152
+
153
+ @property
154
+ def pulse_on_run(self) -> bool:
155
+ """Get whether pulse on run is enabled.
156
+
157
+ Returns:
158
+ True if pulse on run is enabled, False otherwise
159
+ """
160
+ return self.ui_options.get("pulse_on_run", False)
161
+
162
+ @pulse_on_run.setter
163
+ def pulse_on_run(self, value: bool) -> None:
164
+ """Set whether pulse on run is enabled.
165
+
166
+ Args:
167
+ value: Whether to enable pulse on run
168
+ """
169
+ if value:
170
+ self.update_ui_options_key("pulse_on_run", value)
171
+ else:
172
+ ui_options = self.ui_options.copy()
173
+ ui_options.pop("pulse_on_run", None)
174
+ self.ui_options = ui_options
175
+
176
+ @property
177
+ def clickable_file_browser(self) -> bool:
178
+ """Get whether clickable file browser is enabled.
179
+
180
+ Returns:
181
+ True if clickable file browser is enabled, False otherwise
182
+ """
183
+ return self.ui_options.get("clickable_file_browser", False)
184
+
185
+ @clickable_file_browser.setter
186
+ def clickable_file_browser(self, value: bool) -> None:
187
+ """Set whether clickable file browser is enabled.
188
+
189
+ Args:
190
+ value: Whether to enable clickable file browser
191
+ """
192
+ if value:
193
+ self.update_ui_options_key("clickable_file_browser", value)
194
+ else:
195
+ ui_options = self.ui_options.copy()
196
+ ui_options.pop("clickable_file_browser", None)
197
+ self.ui_options = ui_options
198
+
199
+ @property
200
+ def webcam_capture_video(self) -> bool:
201
+ """Get whether webcam capture video is enabled.
202
+
203
+ Returns:
204
+ True if webcam capture video is enabled, False otherwise
205
+ """
206
+ return self.ui_options.get("webcam_capture_video", False)
207
+
208
+ @webcam_capture_video.setter
209
+ def webcam_capture_video(self, value: bool) -> None:
210
+ """Set whether webcam capture video is enabled.
211
+
212
+ Args:
213
+ value: Whether to enable webcam capture video
214
+ """
215
+ if value:
216
+ self.update_ui_options_key("webcam_capture_video", value)
217
+ else:
218
+ ui_options = self.ui_options.copy()
219
+ ui_options.pop("webcam_capture_video", None)
220
+ self.ui_options = ui_options
221
+
222
+ @property
223
+ def edit_video(self) -> bool:
224
+ """Get whether edit video is enabled.
225
+
226
+ Returns:
227
+ True if edit video is enabled, False otherwise
228
+ """
229
+ return self.ui_options.get("edit_video", False)
230
+
231
+ @edit_video.setter
232
+ def edit_video(self, value: bool) -> None:
233
+ """Set whether edit video is enabled.
234
+
235
+ Args:
236
+ value: Whether to enable edit video
237
+ """
238
+ if value:
239
+ self.update_ui_options_key("edit_video", value)
240
+ else:
241
+ ui_options = self.ui_options.copy()
242
+ ui_options.pop("edit_video", None)
243
+ self.ui_options = ui_options
@@ -45,7 +45,7 @@ class WorkflowShape(BaseModel):
45
45
 
46
46
 
47
47
  class WorkflowMetadata(BaseModel):
48
- LATEST_SCHEMA_VERSION: ClassVar[str] = "0.10.0"
48
+ LATEST_SCHEMA_VERSION: ClassVar[str] = "0.11.0"
49
49
 
50
50
  name: str
51
51
  schema_version: str
@@ -95,6 +95,47 @@ class StartFlowResultFailure(ResultPayloadFailure):
95
95
  validation_exceptions: list[Exception]
96
96
 
97
97
 
98
+ @dataclass
99
+ @PayloadRegistry.register
100
+ class StartFlowFromNodeRequest(RequestPayload):
101
+ """Start executing a flow from a specific node.
102
+
103
+ Use when: Resuming execution from a particular node, debugging specific parts of a flow,
104
+ re-running portions of a workflow, implementing custom execution control.
105
+
106
+ Args:
107
+ flow_name: Name of the flow to start (deprecated)
108
+ node_name: Name of the node to start execution from
109
+ debug_mode: Whether to run in debug mode (default: False)
110
+ pickle_control_flow_result: If this is true, the final ControlFLowResolvedEvent will be pickled to be picked up from inside a subprocess
111
+
112
+ Results: StartFlowFromNodeResultSuccess | StartFlowFromNodeResultFailure (with validation exceptions)
113
+ """
114
+
115
+ flow_name: str | None = None
116
+ node_name: str | None = None
117
+ debug_mode: bool = False
118
+ pickle_control_flow_result: bool = False
119
+
120
+
121
+ @dataclass
122
+ @PayloadRegistry.register
123
+ class StartFlowFromNodeResultSuccess(WorkflowAlteredMixin, ResultPayloadSuccess):
124
+ """Flow started from node successfully. Execution is now running from the specified node."""
125
+
126
+
127
+ @dataclass
128
+ @PayloadRegistry.register
129
+ class StartFlowFromNodeResultFailure(ResultPayloadFailure):
130
+ """Flow start from node failed. Contains validation errors that prevented execution.
131
+
132
+ Args:
133
+ validation_exceptions: List of validation errors that occurred
134
+ """
135
+
136
+ validation_exceptions: list[Exception]
137
+
138
+
98
139
  @dataclass
99
140
  @PayloadRegistry.register
100
141
  class CancelFlowRequest(RequestPayload):
@@ -10,7 +10,11 @@ from griptape_nodes.exe_types.node_types import NodeResolutionState
10
10
  if TYPE_CHECKING:
11
11
  from griptape_nodes.exe_types.core_types import NodeMessagePayload
12
12
  from griptape_nodes.exe_types.node_types import NodeDependencies
13
- from griptape_nodes.retained_mode.events.connection_events import ListConnectionsForNodeResultSuccess
13
+ from griptape_nodes.retained_mode.events.connection_events import (
14
+ IncomingConnection,
15
+ ListConnectionsForNodeResultSuccess,
16
+ OutgoingConnection,
17
+ )
14
18
  from griptape_nodes.retained_mode.events.parameter_events import (
15
19
  GetParameterDetailsResultSuccess,
16
20
  GetParameterValueResultSuccess,
@@ -819,3 +823,88 @@ class GetFlowForNodeResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure)
819
823
 
820
824
  Common causes: node not found, node not assigned to any flow.
821
825
  """
826
+
827
+
828
+ @dataclass
829
+ @PayloadRegistry.register
830
+ class CanResetNodeToDefaultsRequest(RequestPayload):
831
+ """Check if a node can be reset to its default state.
832
+
833
+ Use when: Need to validate whether a node reset operation is allowed before attempting it,
834
+ implementing UI state (enabled/disabled reset button), or providing user feedback.
835
+ Checks for conditions that would prevent reset (locked state, missing metadata, etc.).
836
+
837
+ Args:
838
+ node_name: Name of the node to check (None for current context node)
839
+
840
+ Results: CanResetNodeToDefaultsResultSuccess (with can_reset flag and reason) | CanResetNodeToDefaultsResultFailure (validation failed)
841
+ """
842
+
843
+ node_name: str | None = None
844
+
845
+
846
+ @dataclass
847
+ @PayloadRegistry.register
848
+ class CanResetNodeToDefaultsResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
849
+ """Node reset check completed successfully.
850
+
851
+ Args:
852
+ can_reset: True if the node can be reset to defaults, False otherwise
853
+ editor_tooltip_reason: Optional explanation if node cannot be reset (e.g., "Cannot reset locked node")
854
+ """
855
+
856
+ can_reset: bool
857
+ editor_tooltip_reason: str | None = None
858
+
859
+
860
+ @dataclass
861
+ @PayloadRegistry.register
862
+ class CanResetNodeToDefaultsResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
863
+ """Node reset check failed.
864
+
865
+ Common causes: node not found, no current context, no library metadata.
866
+ """
867
+
868
+
869
+ @dataclass
870
+ @PayloadRegistry.register
871
+ class ResetNodeToDefaultsRequest(RequestPayload):
872
+ """Reset a node to its default state while preserving connections where possible.
873
+
874
+ Use when: Need to reset a node's configuration back to defaults, clear customizations,
875
+ fix broken node state, or restore a node to its initial state. Creates a fresh instance
876
+ of the same node type and reconnects it to the workflow.
877
+
878
+ Args:
879
+ node_name: Name of the node to reset (None for current context node)
880
+
881
+ Results: ResetNodeToDefaultsResultSuccess (with reconnection status) | ResetNodeToDefaultsResultFailure (reset failed)
882
+ """
883
+
884
+ node_name: str | None = None
885
+
886
+
887
+ @dataclass
888
+ @PayloadRegistry.register
889
+ class ResetNodeToDefaultsResultSuccess(WorkflowAlteredMixin, ResultPayloadSuccess):
890
+ """Node reset to defaults successfully.
891
+
892
+ Args:
893
+ node_name: Name of the reset node
894
+ failed_incoming_connections: List of incoming connections that failed to reconnect
895
+ failed_outgoing_connections: List of outgoing connections that failed to reconnect
896
+ """
897
+
898
+ node_name: str
899
+ failed_incoming_connections: list[IncomingConnection]
900
+ failed_outgoing_connections: list[OutgoingConnection]
901
+
902
+
903
+ @dataclass
904
+ @PayloadRegistry.register
905
+ class ResetNodeToDefaultsResultFailure(ResultPayloadFailure):
906
+ """Node reset to defaults failed.
907
+
908
+ Common causes: node not found, no current context, failed to create new node,
909
+ failed to delete old node, failed to rename new node.
910
+ """
@@ -366,3 +366,111 @@ class WriteFileResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
366
366
  """
367
367
 
368
368
  failure_reason: FileIOFailureReason
369
+
370
+
371
+ @dataclass
372
+ @PayloadRegistry.register
373
+ class CopyTreeRequest(RequestPayload):
374
+ """Copy an entire directory tree from source to destination.
375
+
376
+ Use when: Copying directories recursively, backing up directory structures,
377
+ duplicating folder hierarchies with all contents.
378
+
379
+ Args:
380
+ source_path: Path to the source directory to copy
381
+ destination_path: Path where the directory tree should be copied
382
+ symlinks: If True, copy symbolic links as links (default: False)
383
+ ignore_dangling_symlinks: If True, ignore dangling symlinks (default: False)
384
+ dirs_exist_ok: If True, allow destination to exist (default: False)
385
+ ignore_patterns: List of glob patterns to ignore (e.g., ["__pycache__", "*.pyc", ".git"])
386
+
387
+ Results: CopyTreeResultSuccess | CopyTreeResultFailure
388
+ """
389
+
390
+ source_path: str
391
+ destination_path: str
392
+ symlinks: bool = False
393
+ ignore_dangling_symlinks: bool = False
394
+ dirs_exist_ok: bool = False
395
+ ignore_patterns: list[str] | None = None
396
+
397
+
398
+ @dataclass
399
+ @PayloadRegistry.register
400
+ class CopyTreeResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
401
+ """Directory tree copied successfully.
402
+
403
+ Attributes:
404
+ source_path: Source path that was copied
405
+ destination_path: Destination path where tree was copied
406
+ files_copied: Number of files copied
407
+ total_bytes_copied: Total bytes copied
408
+ """
409
+
410
+ source_path: str
411
+ destination_path: str
412
+ files_copied: int
413
+ total_bytes_copied: int
414
+
415
+
416
+ @dataclass
417
+ @PayloadRegistry.register
418
+ class CopyTreeResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
419
+ """Directory tree copy failed.
420
+
421
+ Attributes:
422
+ failure_reason: Classification of why the copy failed
423
+ result_details: Human-readable error message (inherited from ResultPayloadFailure)
424
+ """
425
+
426
+ failure_reason: FileIOFailureReason
427
+
428
+
429
+ @dataclass
430
+ @PayloadRegistry.register
431
+ class CopyFileRequest(RequestPayload):
432
+ """Copy a single file from source to destination.
433
+
434
+ Use when: Copying individual files, duplicating files,
435
+ backing up single files.
436
+
437
+ Args:
438
+ source_path: Path to the source file to copy
439
+ destination_path: Path where the file should be copied
440
+ overwrite: If True, overwrite destination if it exists (default: False)
441
+
442
+ Results: CopyFileResultSuccess | CopyFileResultFailure
443
+ """
444
+
445
+ source_path: str
446
+ destination_path: str
447
+ overwrite: bool = False
448
+
449
+
450
+ @dataclass
451
+ @PayloadRegistry.register
452
+ class CopyFileResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
453
+ """File copied successfully.
454
+
455
+ Attributes:
456
+ source_path: Source path that was copied
457
+ destination_path: Destination path where file was copied
458
+ bytes_copied: Number of bytes copied
459
+ """
460
+
461
+ source_path: str
462
+ destination_path: str
463
+ bytes_copied: int
464
+
465
+
466
+ @dataclass
467
+ @PayloadRegistry.register
468
+ class CopyFileResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
469
+ """File copy failed.
470
+
471
+ Attributes:
472
+ failure_reason: Classification of why the copy failed
473
+ result_details: Human-readable error message (inherited from ResultPayloadFailure)
474
+ """
475
+
476
+ failure_reason: FileIOFailureReason
@@ -161,7 +161,7 @@ class SetParameterValueRequest(RequestPayload):
161
161
  """
162
162
 
163
163
  parameter_name: str
164
- value: Any
164
+ value: str | int | float | bool | dict | None
165
165
  # If node name is None, use the Current Context
166
166
  node_name: str | None = None
167
167
  data_type: str | None = None