griptape-nodes 0.64.10__py3-none-any.whl → 0.65.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.
- griptape_nodes/app/app.py +25 -5
- griptape_nodes/cli/commands/init.py +65 -54
- griptape_nodes/cli/commands/libraries.py +92 -85
- griptape_nodes/cli/commands/self.py +121 -0
- griptape_nodes/common/node_executor.py +2142 -101
- griptape_nodes/exe_types/base_iterative_nodes.py +1004 -0
- griptape_nodes/exe_types/connections.py +114 -19
- griptape_nodes/exe_types/core_types.py +225 -7
- griptape_nodes/exe_types/flow.py +3 -3
- griptape_nodes/exe_types/node_types.py +681 -225
- griptape_nodes/exe_types/param_components/README.md +414 -0
- griptape_nodes/exe_types/param_components/api_key_provider_parameter.py +200 -0
- griptape_nodes/exe_types/param_components/huggingface/huggingface_model_parameter.py +2 -0
- griptape_nodes/exe_types/param_components/huggingface/huggingface_repo_file_parameter.py +79 -5
- griptape_nodes/exe_types/param_types/parameter_button.py +443 -0
- griptape_nodes/machines/control_flow.py +77 -38
- griptape_nodes/machines/dag_builder.py +148 -70
- griptape_nodes/machines/parallel_resolution.py +61 -35
- griptape_nodes/machines/sequential_resolution.py +11 -113
- griptape_nodes/retained_mode/events/app_events.py +1 -0
- griptape_nodes/retained_mode/events/base_events.py +16 -13
- griptape_nodes/retained_mode/events/connection_events.py +3 -0
- griptape_nodes/retained_mode/events/execution_events.py +35 -0
- griptape_nodes/retained_mode/events/flow_events.py +15 -2
- griptape_nodes/retained_mode/events/library_events.py +347 -0
- griptape_nodes/retained_mode/events/node_events.py +48 -0
- griptape_nodes/retained_mode/events/os_events.py +86 -3
- griptape_nodes/retained_mode/events/project_events.py +15 -1
- griptape_nodes/retained_mode/events/workflow_events.py +48 -1
- griptape_nodes/retained_mode/griptape_nodes.py +6 -2
- griptape_nodes/retained_mode/managers/config_manager.py +10 -8
- griptape_nodes/retained_mode/managers/event_manager.py +168 -0
- griptape_nodes/retained_mode/managers/fitness_problems/libraries/__init__.py +2 -0
- griptape_nodes/retained_mode/managers/fitness_problems/libraries/old_xdg_location_warning_problem.py +43 -0
- griptape_nodes/retained_mode/managers/flow_manager.py +664 -123
- griptape_nodes/retained_mode/managers/library_manager.py +1143 -139
- griptape_nodes/retained_mode/managers/model_manager.py +2 -3
- griptape_nodes/retained_mode/managers/node_manager.py +148 -25
- griptape_nodes/retained_mode/managers/object_manager.py +3 -1
- griptape_nodes/retained_mode/managers/operation_manager.py +3 -1
- griptape_nodes/retained_mode/managers/os_manager.py +1158 -122
- griptape_nodes/retained_mode/managers/secrets_manager.py +2 -3
- griptape_nodes/retained_mode/managers/settings.py +21 -1
- griptape_nodes/retained_mode/managers/sync_manager.py +2 -3
- griptape_nodes/retained_mode/managers/workflow_manager.py +358 -104
- griptape_nodes/retained_mode/retained_mode.py +3 -3
- griptape_nodes/traits/button.py +44 -2
- griptape_nodes/traits/file_system_picker.py +2 -2
- griptape_nodes/utils/file_utils.py +101 -0
- griptape_nodes/utils/git_utils.py +1226 -0
- griptape_nodes/utils/library_utils.py +122 -0
- {griptape_nodes-0.64.10.dist-info → griptape_nodes-0.65.0.dist-info}/METADATA +2 -1
- {griptape_nodes-0.64.10.dist-info → griptape_nodes-0.65.0.dist-info}/RECORD +55 -47
- {griptape_nodes-0.64.10.dist-info → griptape_nodes-0.65.0.dist-info}/WHEEL +1 -1
- {griptape_nodes-0.64.10.dist-info → griptape_nodes-0.65.0.dist-info}/entry_points.txt +0 -0
|
@@ -928,7 +928,7 @@ class RetainedMode:
|
|
|
928
928
|
)
|
|
929
929
|
result = GriptapeNodes().handle_request(request)
|
|
930
930
|
|
|
931
|
-
if
|
|
931
|
+
if result.failed():
|
|
932
932
|
return False, result
|
|
933
933
|
|
|
934
934
|
# Navigate through indices
|
|
@@ -983,7 +983,7 @@ class RetainedMode:
|
|
|
983
983
|
)
|
|
984
984
|
result = GriptapeNodes().handle_request(request)
|
|
985
985
|
|
|
986
|
-
if
|
|
986
|
+
if result.failed():
|
|
987
987
|
return result
|
|
988
988
|
|
|
989
989
|
# Navigate to the proper location and set the value
|
|
@@ -1169,7 +1169,7 @@ class RetainedMode:
|
|
|
1169
1169
|
)
|
|
1170
1170
|
result = GriptapeNodes().handle_request(request)
|
|
1171
1171
|
|
|
1172
|
-
if
|
|
1172
|
+
if result.failed():
|
|
1173
1173
|
logger.error(
|
|
1174
1174
|
'set_value failed for "%s.%s", failed to get value for container "%s".',
|
|
1175
1175
|
node,
|
griptape_nodes/traits/button.py
CHANGED
|
@@ -104,6 +104,7 @@ class Button(Trait):
|
|
|
104
104
|
loading_icon: str | None = None
|
|
105
105
|
loading_icon_class: str | None = None
|
|
106
106
|
tooltip: str | None = None
|
|
107
|
+
button_link: str | None = None
|
|
107
108
|
|
|
108
109
|
element_id: str = field(default_factory=lambda: "Button")
|
|
109
110
|
on_click_callback: OnClickCallback | None = field(default=None, init=False)
|
|
@@ -124,6 +125,7 @@ class Button(Trait):
|
|
|
124
125
|
loading_icon: str | None = None,
|
|
125
126
|
loading_icon_class: str | None = None,
|
|
126
127
|
tooltip: str | None = None,
|
|
128
|
+
button_link: str | None = None,
|
|
127
129
|
on_click: OnClickCallback | None = None,
|
|
128
130
|
get_button_state: GetButtonStateCallback | None = None,
|
|
129
131
|
) -> None:
|
|
@@ -140,9 +142,42 @@ class Button(Trait):
|
|
|
140
142
|
self.loading_icon = loading_icon
|
|
141
143
|
self.loading_icon_class = loading_icon_class
|
|
142
144
|
self.tooltip = tooltip
|
|
143
|
-
self.
|
|
145
|
+
self.button_link = button_link
|
|
146
|
+
|
|
147
|
+
# Validate that both button_link and on_click are not provided simultaneously
|
|
148
|
+
if button_link is not None and on_click is not None:
|
|
149
|
+
error_msg = (
|
|
150
|
+
"Cannot specify both 'button_link' and 'on_click' for Button. "
|
|
151
|
+
"Use 'button_link' for simple URL navigation or 'on_click' for custom behavior."
|
|
152
|
+
)
|
|
153
|
+
raise ValueError(error_msg)
|
|
154
|
+
|
|
155
|
+
# If button_link is provided and no custom on_click handler, create a default handler
|
|
156
|
+
if button_link is not None:
|
|
157
|
+
self.on_click_callback = self._create_button_link_handler(button_link)
|
|
158
|
+
else:
|
|
159
|
+
self.on_click_callback = on_click
|
|
144
160
|
self.get_button_state_callback = get_button_state
|
|
145
161
|
|
|
162
|
+
def _create_button_link_handler(self, url: str) -> OnClickCallback:
|
|
163
|
+
"""Create a default handler for button_link URLs."""
|
|
164
|
+
|
|
165
|
+
def handler(
|
|
166
|
+
button: Button, # noqa: ARG001
|
|
167
|
+
button_details: ButtonDetailsMessagePayload,
|
|
168
|
+
) -> NodeMessageResult:
|
|
169
|
+
return NodeMessageResult(
|
|
170
|
+
success=True,
|
|
171
|
+
details="Opening URL",
|
|
172
|
+
response=OnClickMessageResultPayload(
|
|
173
|
+
button_details=button_details,
|
|
174
|
+
href=url,
|
|
175
|
+
),
|
|
176
|
+
altered_workflow_state=False,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
return handler
|
|
180
|
+
|
|
146
181
|
@classmethod
|
|
147
182
|
def get_trait_keys(cls) -> list[str]:
|
|
148
183
|
return ["button", "addbutton"]
|
|
@@ -195,7 +230,7 @@ class Button(Trait):
|
|
|
195
230
|
|
|
196
231
|
return options
|
|
197
232
|
|
|
198
|
-
def on_message_received(self, message_type: str, message: NodeMessagePayload | None) -> NodeMessageResult | None: # noqa: PLR0911
|
|
233
|
+
def on_message_received(self, message_type: str, message: NodeMessagePayload | None) -> NodeMessageResult | None: # noqa: C901, PLR0911, PLR0912
|
|
199
234
|
"""Handle messages sent to this button trait.
|
|
200
235
|
|
|
201
236
|
Args:
|
|
@@ -211,6 +246,13 @@ class Button(Trait):
|
|
|
211
246
|
try:
|
|
212
247
|
# Pre-fill button details with current state and pass to callback
|
|
213
248
|
button_details = self.get_button_details()
|
|
249
|
+
# Include original message's data if present (for payloadData support)
|
|
250
|
+
if message is not None:
|
|
251
|
+
# Handle both NodeMessagePayload objects and dict messages
|
|
252
|
+
if isinstance(message, NodeMessagePayload) and message.data is not None:
|
|
253
|
+
button_details.data = message.data
|
|
254
|
+
elif isinstance(message, dict) and "data" in message:
|
|
255
|
+
button_details.data = message["data"]
|
|
214
256
|
result = self.on_click_callback(self, button_details)
|
|
215
257
|
|
|
216
258
|
# If callback returns None, provide optimistic success result
|
|
@@ -16,7 +16,7 @@ class FileSystemPicker(Trait):
|
|
|
16
16
|
include_patterns: list[str] = field(default_factory=list)
|
|
17
17
|
max_file_size: int | None = None
|
|
18
18
|
min_file_size: int | None = None
|
|
19
|
-
workspace_only: bool =
|
|
19
|
+
workspace_only: bool = False
|
|
20
20
|
initial_path: str | None = None
|
|
21
21
|
allow_create: bool = False
|
|
22
22
|
allow_rename: bool = False
|
|
@@ -34,7 +34,7 @@ class FileSystemPicker(Trait):
|
|
|
34
34
|
include_patterns: list[str] | None = None,
|
|
35
35
|
max_file_size: int | None = None,
|
|
36
36
|
min_file_size: int | None = None,
|
|
37
|
-
workspace_only: bool =
|
|
37
|
+
workspace_only: bool = False,
|
|
38
38
|
initial_path: str | None = None,
|
|
39
39
|
allow_create: bool = False,
|
|
40
40
|
allow_rename: bool = False,
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Utilities for file and directory operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
from fnmatch import fnmatch
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def find_file_in_directory(directory: Path, pattern: str) -> Path | None:
|
|
14
|
+
"""Search directory recursively for a file matching the given pattern.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
directory: Directory to search in
|
|
18
|
+
pattern: Glob pattern to match files against (e.g., '*.json', '*library*.json')
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
Path to the first matching file if found, None otherwise.
|
|
22
|
+
Logs a warning if multiple files match the pattern.
|
|
23
|
+
|
|
24
|
+
Examples:
|
|
25
|
+
>>> find_file_in_directory(Path("/workspace"), "config.json")
|
|
26
|
+
Path("/workspace/subdir/config.json")
|
|
27
|
+
>>> find_file_in_directory(Path("/workspace"), "*library*.json")
|
|
28
|
+
Path("/workspace/libs/my_library.json")
|
|
29
|
+
>>> find_file_in_directory(Path("/empty"), "missing.txt")
|
|
30
|
+
None
|
|
31
|
+
"""
|
|
32
|
+
if not directory.exists():
|
|
33
|
+
logger.debug("Directory does not exist: %s", directory)
|
|
34
|
+
return None
|
|
35
|
+
|
|
36
|
+
if not directory.is_dir():
|
|
37
|
+
logger.debug("Path is not a directory: %s", directory)
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
matches = []
|
|
41
|
+
for root, _, files_found in os.walk(directory):
|
|
42
|
+
for file in files_found:
|
|
43
|
+
if fnmatch(file, pattern):
|
|
44
|
+
found_path = Path(root) / file
|
|
45
|
+
matches.append(found_path)
|
|
46
|
+
|
|
47
|
+
if not matches:
|
|
48
|
+
logger.debug("No files matching pattern '%s' found in directory: %s", pattern, directory)
|
|
49
|
+
return None
|
|
50
|
+
|
|
51
|
+
if len(matches) > 1:
|
|
52
|
+
for _match in matches:
|
|
53
|
+
pass
|
|
54
|
+
logger.warning(
|
|
55
|
+
"Found multiple files matching pattern '%s' in %s, using first one at %s",
|
|
56
|
+
pattern,
|
|
57
|
+
directory,
|
|
58
|
+
matches[0],
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
logger.debug("Found file matching pattern '%s' at: %s", pattern, matches[0])
|
|
62
|
+
return matches[0]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def find_all_files_in_directory(directory: Path, pattern: str) -> list[Path]:
|
|
66
|
+
"""Search directory recursively for all files matching the given pattern.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
directory: Directory to search in
|
|
70
|
+
pattern: Glob pattern to match files against (e.g., '*.json', '*library*.json')
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
List of all matching file paths. Returns empty list if none found.
|
|
74
|
+
|
|
75
|
+
Examples:
|
|
76
|
+
>>> find_all_files_in_directory(Path("/workspace"), "*.json")
|
|
77
|
+
[Path("/workspace/a.json"), Path("/workspace/sub/b.json")]
|
|
78
|
+
>>> find_all_files_in_directory(Path("/empty"), "*.txt")
|
|
79
|
+
[]
|
|
80
|
+
"""
|
|
81
|
+
if not directory.exists():
|
|
82
|
+
logger.debug("Directory does not exist: %s", directory)
|
|
83
|
+
return []
|
|
84
|
+
|
|
85
|
+
if not directory.is_dir():
|
|
86
|
+
logger.debug("Path is not a directory: %s", directory)
|
|
87
|
+
return []
|
|
88
|
+
|
|
89
|
+
matches = []
|
|
90
|
+
for root, _, files_found in os.walk(directory):
|
|
91
|
+
for file in files_found:
|
|
92
|
+
if fnmatch(file, pattern):
|
|
93
|
+
found_path = Path(root) / file
|
|
94
|
+
matches.append(found_path)
|
|
95
|
+
|
|
96
|
+
if not matches:
|
|
97
|
+
logger.debug("No files matching pattern '%s' found in directory: %s", pattern, directory)
|
|
98
|
+
else:
|
|
99
|
+
logger.debug("Found %d file(s) matching pattern '%s' in directory: %s", len(matches), pattern, directory)
|
|
100
|
+
|
|
101
|
+
return matches
|