griptape-nodes 0.41.0__py3-none-any.whl → 0.43.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/__init__.py +0 -0
- griptape_nodes/app/.python-version +0 -0
- griptape_nodes/app/__init__.py +1 -10
- griptape_nodes/app/api.py +199 -0
- griptape_nodes/app/app.py +140 -222
- griptape_nodes/app/watch.py +4 -2
- griptape_nodes/bootstrap/__init__.py +0 -0
- griptape_nodes/bootstrap/bootstrap_script.py +0 -0
- griptape_nodes/bootstrap/register_libraries_script.py +0 -0
- griptape_nodes/bootstrap/structure_config.yaml +0 -0
- griptape_nodes/bootstrap/workflow_executors/__init__.py +0 -0
- griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +0 -0
- griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +0 -0
- griptape_nodes/bootstrap/workflow_runners/__init__.py +0 -0
- griptape_nodes/bootstrap/workflow_runners/bootstrap_workflow_runner.py +0 -0
- griptape_nodes/bootstrap/workflow_runners/local_workflow_runner.py +0 -0
- griptape_nodes/bootstrap/workflow_runners/subprocess_workflow_runner.py +6 -2
- griptape_nodes/bootstrap/workflow_runners/workflow_runner.py +0 -0
- griptape_nodes/drivers/__init__.py +0 -0
- griptape_nodes/drivers/storage/__init__.py +0 -0
- griptape_nodes/drivers/storage/base_storage_driver.py +0 -0
- griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +0 -0
- griptape_nodes/drivers/storage/local_storage_driver.py +5 -3
- griptape_nodes/drivers/storage/storage_backend.py +0 -0
- griptape_nodes/exe_types/__init__.py +0 -0
- griptape_nodes/exe_types/connections.py +0 -0
- griptape_nodes/exe_types/core_types.py +0 -0
- griptape_nodes/exe_types/flow.py +68 -368
- griptape_nodes/exe_types/node_types.py +17 -1
- griptape_nodes/exe_types/type_validator.py +0 -0
- griptape_nodes/machines/__init__.py +0 -0
- griptape_nodes/machines/control_flow.py +52 -20
- griptape_nodes/machines/fsm.py +16 -2
- griptape_nodes/machines/node_resolution.py +16 -14
- griptape_nodes/mcp_server/__init__.py +1 -0
- griptape_nodes/mcp_server/server.py +126 -0
- griptape_nodes/mcp_server/ws_request_manager.py +268 -0
- griptape_nodes/node_library/__init__.py +0 -0
- griptape_nodes/node_library/advanced_node_library.py +0 -0
- griptape_nodes/node_library/library_registry.py +0 -0
- griptape_nodes/node_library/workflow_registry.py +2 -2
- griptape_nodes/py.typed +0 -0
- griptape_nodes/retained_mode/__init__.py +0 -0
- griptape_nodes/retained_mode/events/__init__.py +0 -0
- griptape_nodes/retained_mode/events/agent_events.py +70 -8
- griptape_nodes/retained_mode/events/app_events.py +137 -12
- griptape_nodes/retained_mode/events/arbitrary_python_events.py +23 -0
- griptape_nodes/retained_mode/events/base_events.py +13 -31
- griptape_nodes/retained_mode/events/config_events.py +87 -11
- griptape_nodes/retained_mode/events/connection_events.py +56 -5
- griptape_nodes/retained_mode/events/context_events.py +27 -4
- griptape_nodes/retained_mode/events/execution_events.py +99 -14
- griptape_nodes/retained_mode/events/flow_events.py +165 -7
- griptape_nodes/retained_mode/events/generate_request_payload_schemas.py +0 -0
- griptape_nodes/retained_mode/events/library_events.py +195 -17
- griptape_nodes/retained_mode/events/logger_events.py +11 -0
- griptape_nodes/retained_mode/events/node_events.py +242 -22
- griptape_nodes/retained_mode/events/object_events.py +40 -4
- griptape_nodes/retained_mode/events/os_events.py +116 -3
- griptape_nodes/retained_mode/events/parameter_events.py +212 -8
- griptape_nodes/retained_mode/events/payload_registry.py +0 -0
- griptape_nodes/retained_mode/events/secrets_events.py +59 -7
- griptape_nodes/retained_mode/events/static_file_events.py +57 -4
- griptape_nodes/retained_mode/events/validation_events.py +39 -4
- griptape_nodes/retained_mode/events/workflow_events.py +188 -17
- griptape_nodes/retained_mode/griptape_nodes.py +89 -363
- griptape_nodes/retained_mode/managers/__init__.py +0 -0
- griptape_nodes/retained_mode/managers/agent_manager.py +49 -23
- griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +0 -0
- griptape_nodes/retained_mode/managers/config_manager.py +0 -0
- griptape_nodes/retained_mode/managers/context_manager.py +0 -0
- griptape_nodes/retained_mode/managers/engine_identity_manager.py +146 -0
- griptape_nodes/retained_mode/managers/event_manager.py +14 -2
- griptape_nodes/retained_mode/managers/flow_manager.py +751 -64
- griptape_nodes/retained_mode/managers/library_lifecycle/__init__.py +45 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/data_models.py +191 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +346 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_fsm.py +439 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/__init__.py +17 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/base.py +82 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/github.py +116 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/local_file.py +352 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/package.py +104 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/sandbox.py +155 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance.py +18 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_status.py +12 -0
- griptape_nodes/retained_mode/managers/library_manager.py +255 -40
- griptape_nodes/retained_mode/managers/node_manager.py +120 -103
- griptape_nodes/retained_mode/managers/object_manager.py +11 -3
- griptape_nodes/retained_mode/managers/operation_manager.py +0 -0
- griptape_nodes/retained_mode/managers/os_manager.py +582 -8
- griptape_nodes/retained_mode/managers/secrets_manager.py +4 -0
- griptape_nodes/retained_mode/managers/session_manager.py +328 -0
- griptape_nodes/retained_mode/managers/settings.py +7 -0
- griptape_nodes/retained_mode/managers/static_files_manager.py +0 -0
- griptape_nodes/retained_mode/managers/version_compatibility_manager.py +2 -2
- griptape_nodes/retained_mode/managers/workflow_manager.py +722 -456
- griptape_nodes/retained_mode/retained_mode.py +44 -0
- griptape_nodes/retained_mode/utils/__init__.py +0 -0
- griptape_nodes/retained_mode/utils/engine_identity.py +141 -27
- griptape_nodes/retained_mode/utils/name_generator.py +0 -0
- griptape_nodes/traits/__init__.py +0 -0
- griptape_nodes/traits/add_param_button.py +0 -0
- griptape_nodes/traits/button.py +0 -0
- griptape_nodes/traits/clamp.py +0 -0
- griptape_nodes/traits/compare.py +0 -0
- griptape_nodes/traits/compare_images.py +0 -0
- griptape_nodes/traits/file_system_picker.py +127 -0
- griptape_nodes/traits/minmax.py +0 -0
- griptape_nodes/traits/options.py +0 -0
- griptape_nodes/traits/slider.py +0 -0
- griptape_nodes/traits/trait_registry.py +0 -0
- griptape_nodes/traits/traits.json +0 -0
- griptape_nodes/updater/__init__.py +2 -2
- griptape_nodes/updater/__main__.py +0 -0
- griptape_nodes/utils/__init__.py +0 -0
- griptape_nodes/utils/dict_utils.py +0 -0
- griptape_nodes/utils/image_preview.py +128 -0
- griptape_nodes/utils/metaclasses.py +0 -0
- griptape_nodes/version_compatibility/__init__.py +0 -0
- griptape_nodes/version_compatibility/versions/__init__.py +0 -0
- griptape_nodes/version_compatibility/versions/v0_39_0/__init__.py +0 -0
- griptape_nodes/version_compatibility/versions/v0_39_0/modified_parameters_set_removal.py +5 -5
- griptape_nodes-0.43.0.dist-info/METADATA +90 -0
- griptape_nodes-0.43.0.dist-info/RECORD +129 -0
- griptape_nodes-0.43.0.dist-info/WHEEL +4 -0
- {griptape_nodes-0.41.0.dist-info → griptape_nodes-0.43.0.dist-info}/entry_points.txt +1 -0
- griptape_nodes/app/app_sessions.py +0 -458
- griptape_nodes/retained_mode/utils/session_persistence.py +0 -105
- griptape_nodes-0.41.0.dist-info/METADATA +0 -78
- griptape_nodes-0.41.0.dist-info/RECORD +0 -112
- griptape_nodes-0.41.0.dist-info/WHEEL +0 -4
- griptape_nodes-0.41.0.dist-info/licenses/LICENSE +0 -201
|
@@ -32,8 +32,10 @@ from griptape_nodes.retained_mode.events.execution_events import (
|
|
|
32
32
|
from griptape_nodes.retained_mode.events.flow_events import (
|
|
33
33
|
CreateFlowRequest,
|
|
34
34
|
DeleteFlowRequest,
|
|
35
|
+
GetFlowMetadataRequest,
|
|
35
36
|
ListFlowsInFlowRequest,
|
|
36
37
|
ListNodesInFlowRequest,
|
|
38
|
+
SetFlowMetadataRequest,
|
|
37
39
|
)
|
|
38
40
|
from griptape_nodes.retained_mode.events.library_events import (
|
|
39
41
|
GetNodeMetadataFromLibraryRequest,
|
|
@@ -1247,6 +1249,48 @@ class RetainedMode:
|
|
|
1247
1249
|
request = GetFlowStateRequest(flow_name=flow_name)
|
|
1248
1250
|
return GriptapeNodes().handle_request(request)
|
|
1249
1251
|
|
|
1252
|
+
@classmethod
|
|
1253
|
+
def get_metadata_for_flow(cls, flow_name: str) -> ResultPayload:
|
|
1254
|
+
"""Retrieves metadata associated with a flow.
|
|
1255
|
+
|
|
1256
|
+
Flow metadata can include UI position, display name, tags, and other custom properties.
|
|
1257
|
+
|
|
1258
|
+
Args:
|
|
1259
|
+
flow_name (str): Name of the flow to get metadata for.
|
|
1260
|
+
|
|
1261
|
+
Returns:
|
|
1262
|
+
ResultPayload: Contains the flow's metadata.
|
|
1263
|
+
|
|
1264
|
+
Example:
|
|
1265
|
+
# Get flow metadata
|
|
1266
|
+
result = cmd.get_metadata_for_flow("my_flow")
|
|
1267
|
+
"""
|
|
1268
|
+
request = GetFlowMetadataRequest(flow_name=flow_name)
|
|
1269
|
+
result = GriptapeNodes().handle_request(request)
|
|
1270
|
+
return result
|
|
1271
|
+
|
|
1272
|
+
@classmethod
|
|
1273
|
+
def set_metadata_for_flow(cls, flow_name: str, metadata: dict[Any, Any]) -> ResultPayload:
|
|
1274
|
+
"""Sets metadata for a flow.
|
|
1275
|
+
|
|
1276
|
+
Args:
|
|
1277
|
+
flow_name (str): Name of the flow to set metadata for.
|
|
1278
|
+
metadata (dict): Dictionary containing the metadata to set.
|
|
1279
|
+
|
|
1280
|
+
Returns:
|
|
1281
|
+
ResultPayload: Contains the result of the metadata update operation.
|
|
1282
|
+
|
|
1283
|
+
Example:
|
|
1284
|
+
# Set flow position
|
|
1285
|
+
metadata = {
|
|
1286
|
+
"position": {"x": 100, "y": 200}
|
|
1287
|
+
}
|
|
1288
|
+
result = cmd.set_metadata_for_flow("my_flow", metadata)
|
|
1289
|
+
"""
|
|
1290
|
+
request = SetFlowMetadataRequest(flow_name=flow_name, metadata=metadata)
|
|
1291
|
+
result = GriptapeNodes().handle_request(request)
|
|
1292
|
+
return result
|
|
1293
|
+
|
|
1250
1294
|
# ARBITRARY PYTHON EXECUTION
|
|
1251
1295
|
@classmethod
|
|
1252
1296
|
def run_arbitrary_python(cls, python_str: str) -> ResultPayload:
|
|
File without changes
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
"""Manages engine identity for
|
|
1
|
+
"""Manages engine identity for multiple engines per machine.
|
|
2
2
|
|
|
3
3
|
Handles engine ID, name storage, and generation for unique engine identification.
|
|
4
|
+
Supports multiple engines with selection via GTN_ENGINE_ID environment variable.
|
|
4
5
|
"""
|
|
5
6
|
|
|
6
7
|
import json
|
|
@@ -15,9 +16,9 @@ from .name_generator import generate_engine_name
|
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
class EngineIdentity:
|
|
18
|
-
"""Manages engine identity for
|
|
19
|
+
"""Manages engine identity for multiple engines per machine."""
|
|
19
20
|
|
|
20
|
-
_ENGINE_DATA_FILE = "
|
|
21
|
+
_ENGINE_DATA_FILE = "engines.json"
|
|
21
22
|
|
|
22
23
|
@classmethod
|
|
23
24
|
def _get_engine_data_dir(cls) -> Path:
|
|
@@ -30,11 +31,11 @@ class EngineIdentity:
|
|
|
30
31
|
return cls._get_engine_data_dir() / cls._ENGINE_DATA_FILE
|
|
31
32
|
|
|
32
33
|
@classmethod
|
|
33
|
-
def
|
|
34
|
-
"""Load
|
|
34
|
+
def _load_engines_data(cls) -> dict:
|
|
35
|
+
"""Load engines data from storage.
|
|
35
36
|
|
|
36
37
|
Returns:
|
|
37
|
-
dict:
|
|
38
|
+
dict: Engines data structure with engines array and default_engine_id
|
|
38
39
|
"""
|
|
39
40
|
engine_data_file = cls._get_engine_data_file()
|
|
40
41
|
|
|
@@ -42,48 +43,124 @@ class EngineIdentity:
|
|
|
42
43
|
try:
|
|
43
44
|
with engine_data_file.open("r") as f:
|
|
44
45
|
data = json.load(f)
|
|
45
|
-
if isinstance(data, dict):
|
|
46
|
+
if isinstance(data, dict) and "engines" in data:
|
|
46
47
|
return data
|
|
47
48
|
except (json.JSONDecodeError, OSError):
|
|
48
|
-
# If file is corrupted, return empty dict
|
|
49
49
|
pass
|
|
50
50
|
|
|
51
|
-
return {}
|
|
51
|
+
return {"engines": [], "default_engine_id": None}
|
|
52
52
|
|
|
53
53
|
@classmethod
|
|
54
|
-
def
|
|
55
|
-
"""Save
|
|
54
|
+
def _save_engines_data(cls, engines_data: dict) -> None:
|
|
55
|
+
"""Save engines data to storage.
|
|
56
56
|
|
|
57
57
|
Args:
|
|
58
|
-
|
|
58
|
+
engines_data: Engines data structure to save
|
|
59
59
|
"""
|
|
60
60
|
engine_data_dir = cls._get_engine_data_dir()
|
|
61
61
|
engine_data_dir.mkdir(parents=True, exist_ok=True)
|
|
62
62
|
|
|
63
63
|
engine_data_file = cls._get_engine_data_file()
|
|
64
64
|
with engine_data_file.open("w") as f:
|
|
65
|
-
json.dump(
|
|
65
|
+
json.dump(engines_data, f, indent=2)
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def _get_selected_engine_id(cls) -> str | None:
|
|
69
|
+
"""Get the selected engine ID from environment variable or default.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
str | None: The selected engine ID or None if not specified
|
|
73
|
+
"""
|
|
74
|
+
return os.getenv("GTN_ENGINE_ID")
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def _find_engine_by_id(cls, engines_data: dict, engine_id: str) -> dict | None:
|
|
78
|
+
"""Find an engine by ID in the engines data.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
engines_data: The engines data structure
|
|
82
|
+
engine_id: The engine ID to find
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
dict | None: The engine data if found, None otherwise
|
|
86
|
+
"""
|
|
87
|
+
for engine in engines_data.get("engines", []):
|
|
88
|
+
if engine.get("id") == engine_id:
|
|
89
|
+
return engine
|
|
90
|
+
return None
|
|
66
91
|
|
|
67
92
|
@classmethod
|
|
68
93
|
def get_engine_data(cls) -> dict:
|
|
69
|
-
"""Get the engine data, creating default if it doesn't exist.
|
|
94
|
+
"""Get the current engine data, creating default if it doesn't exist.
|
|
70
95
|
|
|
71
96
|
Returns:
|
|
72
|
-
dict: The engine data
|
|
97
|
+
dict: The current engine data
|
|
73
98
|
"""
|
|
74
|
-
|
|
99
|
+
engines_data = cls._load_engines_data()
|
|
100
|
+
|
|
101
|
+
# Determine which engine to use
|
|
102
|
+
selected_engine_id = cls._get_selected_engine_id()
|
|
75
103
|
|
|
76
|
-
if
|
|
77
|
-
#
|
|
104
|
+
if selected_engine_id:
|
|
105
|
+
# Use specified engine ID
|
|
106
|
+
engine_data = cls._find_engine_by_id(engines_data, selected_engine_id)
|
|
107
|
+
if engine_data:
|
|
108
|
+
return engine_data
|
|
109
|
+
# If specified engine not found, create it
|
|
110
|
+
engine_data = {
|
|
111
|
+
"id": selected_engine_id,
|
|
112
|
+
"name": generate_engine_name(),
|
|
113
|
+
"created_at": datetime.now(tz=UTC).isoformat(),
|
|
114
|
+
}
|
|
115
|
+
else:
|
|
116
|
+
# Use default engine (first one) or create new one
|
|
117
|
+
if engines_data.get("engines"):
|
|
118
|
+
default_id = engines_data.get("default_engine_id")
|
|
119
|
+
if default_id:
|
|
120
|
+
engine_data = cls._find_engine_by_id(engines_data, default_id)
|
|
121
|
+
if engine_data:
|
|
122
|
+
return engine_data
|
|
123
|
+
# Fall back to first engine
|
|
124
|
+
return engines_data["engines"][0]
|
|
125
|
+
|
|
126
|
+
# Create new engine
|
|
78
127
|
engine_data = {
|
|
79
|
-
"id":
|
|
128
|
+
"id": str(uuid.uuid4()),
|
|
80
129
|
"name": generate_engine_name(),
|
|
81
130
|
"created_at": datetime.now(tz=UTC).isoformat(),
|
|
82
131
|
}
|
|
83
|
-
cls._save_engine_data(engine_data)
|
|
84
132
|
|
|
133
|
+
# Add or update engine in the data structure
|
|
134
|
+
cls._add_or_update_engine(engine_data)
|
|
85
135
|
return engine_data
|
|
86
136
|
|
|
137
|
+
@classmethod
|
|
138
|
+
def _add_or_update_engine(cls, engine_data: dict) -> None:
|
|
139
|
+
"""Add or update an engine in the engines data structure.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
engine_data: The engine data to add or update
|
|
143
|
+
"""
|
|
144
|
+
engines_data = cls._load_engines_data()
|
|
145
|
+
|
|
146
|
+
# Find existing engine
|
|
147
|
+
engine_id = engine_data["id"]
|
|
148
|
+
existing_engine = cls._find_engine_by_id(engines_data, engine_id)
|
|
149
|
+
|
|
150
|
+
if existing_engine:
|
|
151
|
+
# Update existing engine
|
|
152
|
+
existing_engine.update(engine_data)
|
|
153
|
+
existing_engine["updated_at"] = datetime.now(tz=UTC).isoformat()
|
|
154
|
+
else:
|
|
155
|
+
# Add new engine
|
|
156
|
+
engines_data.setdefault("engines", []).append(engine_data)
|
|
157
|
+
|
|
158
|
+
# Set as default if it's the first engine
|
|
159
|
+
if not engines_data.get("default_engine_id") and len(engines_data["engines"]) == 1:
|
|
160
|
+
engines_data["default_engine_id"] = engine_id
|
|
161
|
+
|
|
162
|
+
cls._save_engines_data(engines_data)
|
|
163
|
+
|
|
87
164
|
@classmethod
|
|
88
165
|
def get_engine_id(cls) -> str:
|
|
89
166
|
"""Get the engine ID.
|
|
@@ -106,20 +183,57 @@ class EngineIdentity:
|
|
|
106
183
|
|
|
107
184
|
@classmethod
|
|
108
185
|
def set_engine_name(cls, engine_name: str) -> None:
|
|
109
|
-
"""Set and persist the engine name.
|
|
186
|
+
"""Set and persist the current engine name.
|
|
110
187
|
|
|
111
188
|
Args:
|
|
112
189
|
engine_name: The new engine name to set
|
|
113
190
|
"""
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
# Ensure we have basic engine data
|
|
117
|
-
if not engine_data or "id" not in engine_data:
|
|
118
|
-
engine_data = cls.get_engine_data()
|
|
191
|
+
# Get current engine data
|
|
192
|
+
engine_data = cls.get_engine_data()
|
|
119
193
|
|
|
194
|
+
# Update the name
|
|
120
195
|
engine_data["name"] = engine_name
|
|
121
196
|
engine_data["updated_at"] = datetime.now(tz=UTC).isoformat()
|
|
122
|
-
|
|
197
|
+
|
|
198
|
+
# Save updated engine data
|
|
199
|
+
cls._add_or_update_engine(engine_data)
|
|
200
|
+
|
|
201
|
+
@classmethod
|
|
202
|
+
def get_all_engines(cls) -> list[dict]:
|
|
203
|
+
"""Get all registered engines.
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
list[dict]: List of all engine data
|
|
207
|
+
"""
|
|
208
|
+
engines_data = cls._load_engines_data()
|
|
209
|
+
return engines_data.get("engines", [])
|
|
210
|
+
|
|
211
|
+
@classmethod
|
|
212
|
+
def get_default_engine_id(cls) -> str | None:
|
|
213
|
+
"""Get the default engine ID.
|
|
214
|
+
|
|
215
|
+
Returns:
|
|
216
|
+
str | None: The default engine ID or None if not set
|
|
217
|
+
"""
|
|
218
|
+
engines_data = cls._load_engines_data()
|
|
219
|
+
return engines_data.get("default_engine_id")
|
|
220
|
+
|
|
221
|
+
@classmethod
|
|
222
|
+
def set_default_engine_id(cls, engine_id: str) -> None:
|
|
223
|
+
"""Set the default engine ID.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
engine_id: The engine ID to set as default
|
|
227
|
+
"""
|
|
228
|
+
engines_data = cls._load_engines_data()
|
|
229
|
+
|
|
230
|
+
# Verify the engine exists
|
|
231
|
+
if cls._find_engine_by_id(engines_data, engine_id):
|
|
232
|
+
engines_data["default_engine_id"] = engine_id
|
|
233
|
+
cls._save_engines_data(engines_data)
|
|
234
|
+
else:
|
|
235
|
+
msg = f"Engine with ID {engine_id} not found"
|
|
236
|
+
raise ValueError(msg)
|
|
123
237
|
|
|
124
238
|
@classmethod
|
|
125
239
|
def get_engine_data_file_path(cls) -> Path:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
griptape_nodes/traits/button.py
CHANGED
|
File without changes
|
griptape_nodes/traits/clamp.py
CHANGED
|
File without changes
|
griptape_nodes/traits/compare.py
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from griptape_nodes.exe_types.core_types import Parameter, Trait
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(eq=False)
|
|
9
|
+
class FileSystemPicker(Trait):
|
|
10
|
+
allow_files: bool = False
|
|
11
|
+
allow_directories: bool = True
|
|
12
|
+
multiple: bool = False
|
|
13
|
+
file_types: list[str] = field(default_factory=list)
|
|
14
|
+
file_extensions: list[str] = field(default_factory=list)
|
|
15
|
+
exclude_patterns: list[str] = field(default_factory=list)
|
|
16
|
+
include_patterns: list[str] = field(default_factory=list)
|
|
17
|
+
max_file_size: int | None = None
|
|
18
|
+
min_file_size: int | None = None
|
|
19
|
+
workspace_only: bool = True
|
|
20
|
+
initial_path: str | None = None
|
|
21
|
+
element_id: str = field(default_factory=lambda: "FileSystemPicker")
|
|
22
|
+
|
|
23
|
+
def __init__( # noqa: PLR0913
|
|
24
|
+
self,
|
|
25
|
+
*,
|
|
26
|
+
allow_files: bool = False,
|
|
27
|
+
allow_directories: bool = True,
|
|
28
|
+
multiple: bool = False,
|
|
29
|
+
file_types: list[str] | None = None,
|
|
30
|
+
file_extensions: list[str] | None = None,
|
|
31
|
+
exclude_patterns: list[str] | None = None,
|
|
32
|
+
include_patterns: list[str] | None = None,
|
|
33
|
+
max_file_size: int | None = None,
|
|
34
|
+
min_file_size: int | None = None,
|
|
35
|
+
workspace_only: bool = True,
|
|
36
|
+
initial_path: str | None = None,
|
|
37
|
+
) -> None:
|
|
38
|
+
super().__init__()
|
|
39
|
+
self.allow_files = allow_files
|
|
40
|
+
self.allow_directories = allow_directories
|
|
41
|
+
self.multiple = multiple
|
|
42
|
+
self.file_types = file_types or []
|
|
43
|
+
self.file_extensions = file_extensions or []
|
|
44
|
+
self.exclude_patterns = exclude_patterns or []
|
|
45
|
+
self.include_patterns = include_patterns or []
|
|
46
|
+
self.max_file_size = max_file_size
|
|
47
|
+
self.min_file_size = min_file_size
|
|
48
|
+
self.workspace_only = workspace_only
|
|
49
|
+
self.initial_path = initial_path
|
|
50
|
+
|
|
51
|
+
@classmethod
|
|
52
|
+
def get_trait_keys(cls) -> list[str]:
|
|
53
|
+
return ["fileSystemPicker", "file_picker", "folder_picker"]
|
|
54
|
+
|
|
55
|
+
def ui_options_for_trait(self) -> dict[str, Any]:
|
|
56
|
+
"""Generate the fileSystemPicker UI options dictionary."""
|
|
57
|
+
options: dict[str, Any] = {
|
|
58
|
+
"allowFiles": self.allow_files,
|
|
59
|
+
"allowDirectories": self.allow_directories,
|
|
60
|
+
"multiple": self.multiple,
|
|
61
|
+
"workspaceOnly": self.workspace_only,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# Add file types/extensions
|
|
65
|
+
if self.file_types:
|
|
66
|
+
options["fileTypes"] = self.file_types
|
|
67
|
+
elif self.file_extensions:
|
|
68
|
+
options["fileExtensions"] = self.file_extensions
|
|
69
|
+
|
|
70
|
+
# Add patterns
|
|
71
|
+
if self.exclude_patterns:
|
|
72
|
+
options["excludePatterns"] = self.exclude_patterns
|
|
73
|
+
if self.include_patterns:
|
|
74
|
+
options["includePatterns"] = self.include_patterns
|
|
75
|
+
|
|
76
|
+
# Add size limits
|
|
77
|
+
if self.max_file_size is not None:
|
|
78
|
+
options["maxFileSize"] = self.max_file_size
|
|
79
|
+
if self.min_file_size is not None:
|
|
80
|
+
options["minFileSize"] = self.min_file_size
|
|
81
|
+
|
|
82
|
+
# Add initial path
|
|
83
|
+
if self.initial_path:
|
|
84
|
+
options["initialPath"] = self.initial_path
|
|
85
|
+
|
|
86
|
+
return {"fileSystemPicker": options}
|
|
87
|
+
|
|
88
|
+
def validators_for_trait(self) -> list[Callable[[Parameter, Any], Any]]:
|
|
89
|
+
"""Validate file system picker configuration."""
|
|
90
|
+
|
|
91
|
+
def validate(param: Parameter, value: Any) -> None: # noqa: ARG001
|
|
92
|
+
# Validate that at least one selection type is enabled
|
|
93
|
+
if not self.allow_files and not self.allow_directories:
|
|
94
|
+
msg = "At least one of allow_files or allow_directories must be True"
|
|
95
|
+
raise ValueError(msg)
|
|
96
|
+
|
|
97
|
+
# Validate file size limits
|
|
98
|
+
if (
|
|
99
|
+
self.max_file_size is not None
|
|
100
|
+
and self.min_file_size is not None
|
|
101
|
+
and self.max_file_size < self.min_file_size
|
|
102
|
+
):
|
|
103
|
+
msg = "max_file_size cannot be less than min_file_size"
|
|
104
|
+
raise ValueError(msg)
|
|
105
|
+
|
|
106
|
+
# Validate that file types/extensions are valid
|
|
107
|
+
all_file_types = self.file_types + self.file_extensions
|
|
108
|
+
for file_type in all_file_types:
|
|
109
|
+
if not file_type.startswith("."):
|
|
110
|
+
msg = f"File type '{file_type}' must start with a dot (e.g., '.py')"
|
|
111
|
+
raise ValueError(msg)
|
|
112
|
+
|
|
113
|
+
return [validate]
|
|
114
|
+
|
|
115
|
+
def converters_for_trait(self) -> list[Callable]:
|
|
116
|
+
"""Convert file system picker values if needed."""
|
|
117
|
+
|
|
118
|
+
def converter(value: Any) -> Any:
|
|
119
|
+
# If value is a string and we expect a list, convert it
|
|
120
|
+
if isinstance(value, str) and self.multiple:
|
|
121
|
+
return [value] if value else []
|
|
122
|
+
return value
|
|
123
|
+
|
|
124
|
+
return [converter]
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# These Traits get added to a list on the parameter. When they are added they apply their functions to the parameter.
|
griptape_nodes/traits/minmax.py
CHANGED
|
File without changes
|
griptape_nodes/traits/options.py
CHANGED
|
File without changes
|
griptape_nodes/traits/slider.py
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -38,7 +38,7 @@ def _download_and_run_installer() -> None:
|
|
|
38
38
|
"""Runs the update commands for the engine."""
|
|
39
39
|
console.print("[bold green]Updating self...[/bold green]")
|
|
40
40
|
try:
|
|
41
|
-
subprocess.run(
|
|
41
|
+
subprocess.run(
|
|
42
42
|
["uv", "tool", "upgrade", "griptape-nodes"], # noqa: S607
|
|
43
43
|
text=True,
|
|
44
44
|
capture_output=True,
|
|
@@ -57,7 +57,7 @@ def _sync_libraries() -> None:
|
|
|
57
57
|
"""Syncs the libraries for the engine."""
|
|
58
58
|
console.print("[bold green]Syncing libraries...[/bold green]")
|
|
59
59
|
try:
|
|
60
|
-
subprocess.run(
|
|
60
|
+
subprocess.run(
|
|
61
61
|
["griptape-nodes", "libraries", "sync"], # noqa: S607
|
|
62
62
|
text=True,
|
|
63
63
|
capture_output=True,
|
|
File without changes
|
griptape_nodes/utils/__init__.py
CHANGED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"""Image preview utilities for generating thumbnails and previews."""
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import io
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from PIL import Image
|
|
8
|
+
|
|
9
|
+
from griptape_nodes.retained_mode.griptape_nodes import logger
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_image_preview(
|
|
13
|
+
image_path: Path, max_width: int = 512, max_height: int = 512, quality: int = 85, image_format: str = "WEBP"
|
|
14
|
+
) -> str | None:
|
|
15
|
+
"""Create a small preview image from a file path.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
image_path: Path to the image file
|
|
19
|
+
max_width: Maximum width for the preview
|
|
20
|
+
max_height: Maximum height for the preview
|
|
21
|
+
quality: WebP quality (1-100)
|
|
22
|
+
image_format: Output format (WEBP, JPEG, PNG, etc.)
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Base64 encoded data URL of the preview, or None if failed
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
# Open and resize the image
|
|
29
|
+
with Image.open(image_path) as img:
|
|
30
|
+
# Convert to RGB if necessary (for WebP/JPEG output)
|
|
31
|
+
if image_format.upper() in ("WEBP", "JPEG") and img.mode in ("RGBA", "LA", "P"):
|
|
32
|
+
converted_img = img.convert("RGB")
|
|
33
|
+
else:
|
|
34
|
+
converted_img = img
|
|
35
|
+
|
|
36
|
+
# Calculate new size maintaining aspect ratio
|
|
37
|
+
converted_img.thumbnail((max_width, max_height), Image.Resampling.LANCZOS)
|
|
38
|
+
|
|
39
|
+
# Save to bytes buffer
|
|
40
|
+
buffer = io.BytesIO()
|
|
41
|
+
converted_img.save(buffer, format=image_format, quality=quality, optimize=True)
|
|
42
|
+
buffer.seek(0)
|
|
43
|
+
|
|
44
|
+
# Convert to base64
|
|
45
|
+
image_bytes = buffer.getvalue()
|
|
46
|
+
base64_data = base64.b64encode(image_bytes).decode("utf-8")
|
|
47
|
+
|
|
48
|
+
# Create data URL
|
|
49
|
+
mime_type = f"image/{image_format.lower()}"
|
|
50
|
+
data_url = f"data:{mime_type};base64,{base64_data}"
|
|
51
|
+
|
|
52
|
+
logger.debug(f"Created preview for {image_path}: {img.size} -> {len(image_bytes)} bytes")
|
|
53
|
+
return data_url
|
|
54
|
+
|
|
55
|
+
except Exception as e:
|
|
56
|
+
logger.warning(f"Failed to create preview for {image_path}: {e}")
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def create_image_preview_from_bytes(
|
|
61
|
+
image_bytes: bytes, max_width: int = 512, max_height: int = 512, quality: int = 85, image_format: str = "WEBP"
|
|
62
|
+
) -> str | None:
|
|
63
|
+
"""Create a small preview image from bytes.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
image_bytes: Raw image bytes
|
|
67
|
+
max_width: Maximum width for the preview
|
|
68
|
+
max_height: Maximum height for the preview
|
|
69
|
+
quality: WebP quality (1-100)
|
|
70
|
+
image_format: Output format (WEBP, JPEG, PNG, etc.)
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Base64 encoded data URL of the preview, or None if failed
|
|
74
|
+
"""
|
|
75
|
+
try:
|
|
76
|
+
# Open image from bytes
|
|
77
|
+
with Image.open(io.BytesIO(image_bytes)) as img:
|
|
78
|
+
# Convert to RGB if necessary (for WebP/JPEG output)
|
|
79
|
+
if image_format.upper() in ("WEBP", "JPEG") and img.mode in ("RGBA", "LA", "P"):
|
|
80
|
+
converted_img = img.convert("RGB")
|
|
81
|
+
else:
|
|
82
|
+
converted_img = img
|
|
83
|
+
|
|
84
|
+
# Calculate new size maintaining aspect ratio
|
|
85
|
+
converted_img.thumbnail((max_width, max_height), Image.Resampling.LANCZOS)
|
|
86
|
+
|
|
87
|
+
# Save to bytes buffer
|
|
88
|
+
buffer = io.BytesIO()
|
|
89
|
+
converted_img.save(buffer, format=image_format, quality=quality, optimize=True)
|
|
90
|
+
buffer.seek(0)
|
|
91
|
+
|
|
92
|
+
# Convert to base64
|
|
93
|
+
preview_bytes = buffer.getvalue()
|
|
94
|
+
base64_data = base64.b64encode(preview_bytes).decode("utf-8")
|
|
95
|
+
|
|
96
|
+
# Create data URL
|
|
97
|
+
mime_type = f"image/{image_format.lower()}"
|
|
98
|
+
data_url = f"data:{mime_type};base64,{base64_data}"
|
|
99
|
+
|
|
100
|
+
logger.debug(f"Created preview from bytes: {len(image_bytes)} -> {len(preview_bytes)} bytes")
|
|
101
|
+
return data_url
|
|
102
|
+
|
|
103
|
+
except Exception as e:
|
|
104
|
+
logger.warning(f"Failed to create preview from bytes: {e}")
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def get_image_info(image_path: Path) -> dict | None:
|
|
109
|
+
"""Get basic information about an image file.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
image_path: Path to the image file
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Dictionary with image info (width, height, format, mode), or None if failed
|
|
116
|
+
"""
|
|
117
|
+
try:
|
|
118
|
+
with Image.open(image_path) as img:
|
|
119
|
+
return {
|
|
120
|
+
"width": img.width,
|
|
121
|
+
"height": img.height,
|
|
122
|
+
"format": img.format,
|
|
123
|
+
"mode": img.mode,
|
|
124
|
+
"size_bytes": image_path.stat().st_size,
|
|
125
|
+
}
|
|
126
|
+
except Exception as e:
|
|
127
|
+
logger.warning(f"Failed to get image info for {image_path}: {e}")
|
|
128
|
+
return None
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -7,7 +7,7 @@ from griptape_nodes.retained_mode.events.app_events import (
|
|
|
7
7
|
GetEngineVersionResultSuccess,
|
|
8
8
|
)
|
|
9
9
|
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes, Version
|
|
10
|
-
from griptape_nodes.retained_mode.managers.
|
|
10
|
+
from griptape_nodes.retained_mode.managers.library_lifecycle.library_status import LibraryStatus
|
|
11
11
|
from griptape_nodes.retained_mode.managers.version_compatibility_manager import (
|
|
12
12
|
LibraryVersionCompatibilityCheck,
|
|
13
13
|
LibraryVersionCompatibilityIssue,
|
|
@@ -47,13 +47,13 @@ class ModifiedParametersSetRemovalCheck(LibraryVersionCompatibilityCheck):
|
|
|
47
47
|
message=f"This library (built for engine version {library_version_str}) is incompatible with Griptape Nodes 0.39+. "
|
|
48
48
|
"The 'modified_parameters_set' parameter has been removed from BaseNode methods: 'after_incoming_connection', 'after_outgoing_connection', 'after_incoming_connection_removed', 'after_outgoing_connection_removed', 'before_value_set', and 'after_value_set'. "
|
|
49
49
|
"If this library overrides any of these methods, it will not load or function properly. Please update to a newer version of this library or contact the library author immediately.",
|
|
50
|
-
severity=
|
|
50
|
+
severity=LibraryStatus.UNUSABLE,
|
|
51
51
|
),
|
|
52
52
|
LibraryVersionCompatibilityIssue(
|
|
53
53
|
message=f"This library (built for engine version {library_version_str}) is incompatible with Griptape Nodes 0.39+."
|
|
54
54
|
"The 'ui_options' field has been modified on all Elements. In order to function properly, all nodes must update ui_options by setting its value to a new dictionary. Updating ui_options by accessing the private field _ui_options will no longer create UI updates in the editor."
|
|
55
55
|
"If this library accesses the private _ui_options field, it will not update the editor properly. Please update to a newer version of this library or contact the library author immediately.",
|
|
56
|
-
severity=
|
|
56
|
+
severity=LibraryStatus.UNUSABLE,
|
|
57
57
|
),
|
|
58
58
|
]
|
|
59
59
|
if current_engine_version >= Version(0, 38, 0):
|
|
@@ -63,13 +63,13 @@ class ModifiedParametersSetRemovalCheck(LibraryVersionCompatibilityCheck):
|
|
|
63
63
|
message=f"WARNING: The 'modified_parameters_set' parameter will be removed in Griptape Nodes 0.39 from BaseNode methods: 'after_incoming_connection', 'after_outgoing_connection', 'after_incoming_connection_removed', 'after_outgoing_connection_removed', 'before_value_set', and 'after_value_set'. "
|
|
64
64
|
f"This library (built for engine version {library_version_str}) must be updated before the 0.39 release. "
|
|
65
65
|
"If this library overrides any of these methods, it will fail to load in 0.39. If not, no action is necessary. Please contact the library author to confirm whether this library is impacted.",
|
|
66
|
-
severity=
|
|
66
|
+
severity=LibraryStatus.FLAWED,
|
|
67
67
|
),
|
|
68
68
|
LibraryVersionCompatibilityIssue(
|
|
69
69
|
message="WARNING: The 'ui_options' field has been modified in Griptape Nodes 0.38 on all BaseNodeElements."
|
|
70
70
|
"In order to function properly, all nodes must update ui_options by setting its value to a new dictionary. Updating ui_options by accessing the private field _ui_options will no longer create UI updates in the editor."
|
|
71
71
|
"If this library accesses the private _ui_options field, it will not update the editor properly. Please update to a newer version of this library or contact the library author immediately.",
|
|
72
|
-
severity=
|
|
72
|
+
severity=LibraryStatus.FLAWED,
|
|
73
73
|
),
|
|
74
74
|
]
|
|
75
75
|
|