griptape-nodes 0.57.0__py3-none-any.whl → 0.58.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/api_client/__init__.py +9 -0
- griptape_nodes/api_client/client.py +279 -0
- griptape_nodes/api_client/request_client.py +273 -0
- griptape_nodes/app/app.py +57 -150
- griptape_nodes/bootstrap/utils/python_subprocess_executor.py +1 -1
- griptape_nodes/bootstrap/workflow_executors/local_session_workflow_executor.py +22 -50
- griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +6 -1
- griptape_nodes/bootstrap/workflow_executors/subprocess_workflow_executor.py +27 -46
- griptape_nodes/bootstrap/workflow_executors/utils/subprocess_script.py +7 -0
- griptape_nodes/bootstrap/workflow_publishers/local_workflow_publisher.py +3 -1
- griptape_nodes/bootstrap/workflow_publishers/subprocess_workflow_publisher.py +3 -1
- griptape_nodes/bootstrap/workflow_publishers/utils/subprocess_script.py +16 -1
- griptape_nodes/common/node_executor.py +466 -0
- griptape_nodes/drivers/storage/base_storage_driver.py +0 -11
- griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +7 -25
- griptape_nodes/drivers/storage/local_storage_driver.py +2 -2
- griptape_nodes/exe_types/connections.py +37 -9
- griptape_nodes/exe_types/core_types.py +1 -1
- griptape_nodes/exe_types/node_types.py +115 -22
- griptape_nodes/machines/control_flow.py +48 -7
- griptape_nodes/machines/parallel_resolution.py +98 -29
- griptape_nodes/machines/sequential_resolution.py +61 -22
- griptape_nodes/node_library/library_registry.py +24 -1
- griptape_nodes/node_library/workflow_registry.py +38 -2
- griptape_nodes/retained_mode/events/execution_events.py +8 -1
- griptape_nodes/retained_mode/events/flow_events.py +90 -3
- griptape_nodes/retained_mode/events/node_events.py +17 -10
- griptape_nodes/retained_mode/events/workflow_events.py +5 -0
- griptape_nodes/retained_mode/griptape_nodes.py +16 -219
- griptape_nodes/retained_mode/managers/config_manager.py +0 -46
- griptape_nodes/retained_mode/managers/engine_identity_manager.py +225 -74
- griptape_nodes/retained_mode/managers/flow_manager.py +1276 -230
- griptape_nodes/retained_mode/managers/library_manager.py +7 -8
- griptape_nodes/retained_mode/managers/node_manager.py +197 -9
- griptape_nodes/retained_mode/managers/secrets_manager.py +26 -0
- griptape_nodes/retained_mode/managers/session_manager.py +264 -227
- griptape_nodes/retained_mode/managers/settings.py +4 -38
- griptape_nodes/retained_mode/managers/static_files_manager.py +3 -3
- griptape_nodes/retained_mode/managers/version_compatibility_manager.py +135 -6
- griptape_nodes/retained_mode/managers/workflow_manager.py +206 -78
- griptape_nodes/servers/mcp.py +23 -15
- griptape_nodes/utils/async_utils.py +36 -0
- griptape_nodes/utils/dict_utils.py +8 -2
- griptape_nodes/version_compatibility/versions/v0_39_0/modified_parameters_set_removal.py +11 -6
- griptape_nodes/version_compatibility/workflow_versions/v0_7_0/local_executor_argument_addition.py +12 -5
- {griptape_nodes-0.57.0.dist-info → griptape_nodes-0.58.0.dist-info}/METADATA +4 -3
- {griptape_nodes-0.57.0.dist-info → griptape_nodes-0.58.0.dist-info}/RECORD +49 -47
- {griptape_nodes-0.57.0.dist-info → griptape_nodes-0.58.0.dist-info}/WHEEL +1 -1
- griptape_nodes/retained_mode/utils/engine_identity.py +0 -245
- griptape_nodes/servers/ws_request_manager.py +0 -268
- {griptape_nodes-0.57.0.dist-info → griptape_nodes-0.58.0.dist-info}/entry_points.txt +0 -0
|
@@ -2,22 +2,65 @@
|
|
|
2
2
|
|
|
3
3
|
Centralizes engine identity management, providing a consistent interface for
|
|
4
4
|
engine ID and name operations.
|
|
5
|
+
Handles engine ID, name storage, and generation for unique engine identification.
|
|
6
|
+
Supports multiple engines with selection via GTN_ENGINE_ID environment variable.
|
|
5
7
|
"""
|
|
6
8
|
|
|
7
|
-
import
|
|
8
|
-
from pathlib import Path
|
|
9
|
+
from __future__ import annotations
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
import json
|
|
12
|
+
import logging
|
|
13
|
+
import os
|
|
14
|
+
import uuid
|
|
15
|
+
from datetime import UTC, datetime
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
17
|
+
|
|
18
|
+
from pydantic import BaseModel
|
|
19
|
+
from xdg_base_dirs import xdg_data_home
|
|
20
|
+
|
|
21
|
+
from griptape_nodes.retained_mode.events.app_events import (
|
|
22
|
+
GetEngineNameRequest,
|
|
23
|
+
GetEngineNameResultFailure,
|
|
24
|
+
GetEngineNameResultSuccess,
|
|
25
|
+
SetEngineNameRequest,
|
|
26
|
+
SetEngineNameResultFailure,
|
|
27
|
+
SetEngineNameResultSuccess,
|
|
28
|
+
)
|
|
29
|
+
from griptape_nodes.retained_mode.events.base_events import (
|
|
30
|
+
BaseEvent,
|
|
31
|
+
ResultDetails,
|
|
32
|
+
ResultPayload,
|
|
33
|
+
)
|
|
34
|
+
from griptape_nodes.retained_mode.utils.name_generator import generate_engine_name
|
|
35
|
+
|
|
36
|
+
if TYPE_CHECKING:
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
|
|
39
|
+
from griptape_nodes.retained_mode.managers.event_manager import EventManager
|
|
13
40
|
|
|
14
41
|
logger = logging.getLogger("griptape_nodes")
|
|
15
42
|
|
|
16
43
|
|
|
44
|
+
class EngineData(BaseModel):
|
|
45
|
+
"""Represents a single engine's data."""
|
|
46
|
+
|
|
47
|
+
id: str
|
|
48
|
+
name: str
|
|
49
|
+
created_at: str
|
|
50
|
+
updated_at: str | None = None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class EnginesStorage(BaseModel):
|
|
54
|
+
"""Represents the engines storage structure."""
|
|
55
|
+
|
|
56
|
+
engines: list[EngineData]
|
|
57
|
+
default_engine_id: str | None = None
|
|
58
|
+
|
|
59
|
+
|
|
17
60
|
class EngineIdentityManager:
|
|
18
61
|
"""Manages engine identity and active engine state."""
|
|
19
62
|
|
|
20
|
-
|
|
63
|
+
_ENGINE_DATA_FILE = "engines.json"
|
|
21
64
|
|
|
22
65
|
def __init__(self, event_manager: EventManager | None = None) -> None:
|
|
23
66
|
"""Initialize the EngineIdentityManager.
|
|
@@ -25,122 +68,230 @@ class EngineIdentityManager:
|
|
|
25
68
|
Args:
|
|
26
69
|
event_manager: The EventManager instance to use for event handling.
|
|
27
70
|
"""
|
|
71
|
+
self._active_engine_id: str | None = None
|
|
72
|
+
self._engines_data = self._load_engines_data()
|
|
73
|
+
self._current_engine_data = self._get_or_initialize_engine_data()
|
|
74
|
+
|
|
28
75
|
if event_manager is not None:
|
|
29
|
-
|
|
30
|
-
|
|
76
|
+
event_manager.assign_manager_to_request_type(GetEngineNameRequest, self.handle_get_engine_name_request)
|
|
77
|
+
event_manager.assign_manager_to_request_type(SetEngineNameRequest, self.handle_set_engine_name_request)
|
|
31
78
|
|
|
32
|
-
@
|
|
33
|
-
def
|
|
79
|
+
@property
|
|
80
|
+
def active_engine_id(self) -> str | None:
|
|
34
81
|
"""Get the active engine ID.
|
|
35
82
|
|
|
36
83
|
Returns:
|
|
37
84
|
str | None: The active engine ID or None if not set
|
|
38
85
|
"""
|
|
39
|
-
return
|
|
86
|
+
return self._active_engine_id
|
|
40
87
|
|
|
41
|
-
@
|
|
42
|
-
def
|
|
88
|
+
@active_engine_id.setter
|
|
89
|
+
def active_engine_id(self, engine_id: str) -> None:
|
|
43
90
|
"""Set the active engine ID.
|
|
44
91
|
|
|
45
92
|
Args:
|
|
46
93
|
engine_id: The engine ID to set as active
|
|
47
94
|
"""
|
|
48
|
-
|
|
95
|
+
self._active_engine_id = engine_id
|
|
49
96
|
logger.debug("Set active engine ID to: %s", engine_id)
|
|
50
97
|
|
|
51
|
-
@
|
|
52
|
-
def
|
|
53
|
-
"""
|
|
54
|
-
if cls._active_engine_id is None:
|
|
55
|
-
engine_id = EngineIdentity.get_engine_id()
|
|
56
|
-
BaseEvent._engine_id = engine_id
|
|
57
|
-
cls._active_engine_id = engine_id
|
|
58
|
-
logger.debug("Initialized engine ID: %s", engine_id)
|
|
59
|
-
|
|
60
|
-
return cls._active_engine_id
|
|
61
|
-
|
|
62
|
-
@classmethod
|
|
63
|
-
def get_engine_data(cls) -> dict:
|
|
64
|
-
"""Get the current engine data, creating default if it doesn't exist.
|
|
98
|
+
@property
|
|
99
|
+
def engine_id(self) -> str:
|
|
100
|
+
"""Get the engine ID.
|
|
65
101
|
|
|
66
102
|
Returns:
|
|
67
|
-
|
|
103
|
+
str: The engine ID (UUID)
|
|
68
104
|
"""
|
|
69
|
-
return
|
|
105
|
+
return self._current_engine_data.id
|
|
70
106
|
|
|
71
|
-
@
|
|
72
|
-
def
|
|
107
|
+
@property
|
|
108
|
+
def engine_name(self) -> str:
|
|
73
109
|
"""Get the engine name.
|
|
74
110
|
|
|
75
111
|
Returns:
|
|
76
112
|
str: The engine name
|
|
77
113
|
"""
|
|
78
|
-
return
|
|
114
|
+
return self._current_engine_data.name
|
|
79
115
|
|
|
80
|
-
@
|
|
81
|
-
def
|
|
116
|
+
@engine_name.setter
|
|
117
|
+
def engine_name(self, engine_name: str) -> None:
|
|
82
118
|
"""Set and persist the current engine name.
|
|
83
119
|
|
|
84
120
|
Args:
|
|
85
121
|
engine_name: The new engine name to set
|
|
86
122
|
"""
|
|
87
|
-
|
|
123
|
+
# Update cached engine data
|
|
124
|
+
self._current_engine_data.name = engine_name
|
|
125
|
+
self._current_engine_data.updated_at = datetime.now(tz=UTC).isoformat()
|
|
126
|
+
|
|
127
|
+
# Save updated engine data
|
|
128
|
+
self._add_or_update_engine(self._current_engine_data)
|
|
88
129
|
logger.info("Updated engine name to: %s", engine_name)
|
|
89
130
|
|
|
90
|
-
@
|
|
91
|
-
def
|
|
131
|
+
@property
|
|
132
|
+
def all_engines(self) -> list[EngineData]:
|
|
92
133
|
"""Get all registered engines.
|
|
93
134
|
|
|
94
135
|
Returns:
|
|
95
|
-
list[
|
|
136
|
+
list[EngineData]: List of all engine data
|
|
96
137
|
"""
|
|
97
|
-
return
|
|
138
|
+
return self._engines_data.engines
|
|
98
139
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
140
|
+
def handle_get_engine_name_request(self, request: GetEngineNameRequest) -> ResultPayload: # noqa: ARG002
|
|
141
|
+
"""Handle requests to get the current engine name."""
|
|
142
|
+
try:
|
|
143
|
+
engine_name = self.engine_name
|
|
144
|
+
return GetEngineNameResultSuccess(
|
|
145
|
+
engine_name=engine_name, result_details="Engine name retrieved successfully."
|
|
146
|
+
)
|
|
147
|
+
except Exception as err:
|
|
148
|
+
error_message = f"Failed to get engine name: {err}"
|
|
149
|
+
logger.error(error_message)
|
|
150
|
+
return GetEngineNameResultFailure(error_message=error_message, result_details=error_message)
|
|
151
|
+
|
|
152
|
+
def handle_set_engine_name_request(self, request: SetEngineNameRequest) -> ResultPayload:
|
|
153
|
+
"""Handle requests to set a new engine name."""
|
|
154
|
+
try:
|
|
155
|
+
if not request.engine_name or not request.engine_name.strip():
|
|
156
|
+
error_message = "Engine name cannot be empty"
|
|
157
|
+
logger.warning(error_message)
|
|
158
|
+
return SetEngineNameResultFailure(error_message=error_message, result_details=error_message)
|
|
159
|
+
|
|
160
|
+
self.engine_name = request.engine_name.strip()
|
|
161
|
+
details = f"Engine name set to: {request.engine_name.strip()}"
|
|
162
|
+
return SetEngineNameResultSuccess(
|
|
163
|
+
engine_name=request.engine_name.strip(),
|
|
164
|
+
result_details=ResultDetails(message=details, level=logging.INFO),
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
except Exception as err:
|
|
168
|
+
error_message = f"Failed to set engine name: {err}"
|
|
169
|
+
logger.error(error_message)
|
|
170
|
+
return SetEngineNameResultFailure(error_message=error_message, result_details=error_message)
|
|
171
|
+
|
|
172
|
+
def _get_or_initialize_engine_data(self) -> EngineData:
|
|
173
|
+
"""Get the current engine data, creating default if it doesn't exist.
|
|
102
174
|
|
|
103
175
|
Returns:
|
|
104
|
-
|
|
176
|
+
EngineData: The current engine data
|
|
105
177
|
"""
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
178
|
+
engine_data = None
|
|
179
|
+
|
|
180
|
+
# Step 1: Determine which engine ID to use
|
|
181
|
+
target_engine_id = os.getenv("GTN_ENGINE_ID")
|
|
182
|
+
if not target_engine_id:
|
|
183
|
+
# Use default or first available
|
|
184
|
+
if self._engines_data.default_engine_id:
|
|
185
|
+
target_engine_id = self._engines_data.default_engine_id
|
|
186
|
+
elif self._engines_data.engines:
|
|
187
|
+
engine_data = self._engines_data.engines[0]
|
|
188
|
+
else:
|
|
189
|
+
# No engines exist, will create new one
|
|
190
|
+
target_engine_id = str(uuid.uuid4())
|
|
191
|
+
|
|
192
|
+
# Step 2: Try to find existing engine if we don't already have one
|
|
193
|
+
if engine_data is None and target_engine_id is not None:
|
|
194
|
+
engine_data = self._find_engine_by_id(self._engines_data, target_engine_id)
|
|
195
|
+
|
|
196
|
+
# Step 3: Create new engine if not found
|
|
197
|
+
if engine_data is None:
|
|
198
|
+
# If target_engine_id is still None, generate a new UUID
|
|
199
|
+
if target_engine_id is None:
|
|
200
|
+
target_engine_id = str(uuid.uuid4())
|
|
201
|
+
engine_data = EngineData(
|
|
202
|
+
id=target_engine_id,
|
|
203
|
+
name=generate_engine_name(),
|
|
204
|
+
created_at=datetime.now(tz=UTC).isoformat(),
|
|
205
|
+
)
|
|
206
|
+
self._add_or_update_engine(engine_data)
|
|
207
|
+
|
|
208
|
+
# Register engine with BaseEvent
|
|
209
|
+
BaseEvent._engine_id = engine_data.id
|
|
210
|
+
self._active_engine_id = engine_data.id
|
|
211
|
+
logger.debug("Initialized engine ID: %s", engine_data.id)
|
|
212
|
+
return engine_data
|
|
213
|
+
|
|
214
|
+
def _add_or_update_engine(self, engine_data: EngineData) -> None:
|
|
215
|
+
"""Add or update an engine in the engines data structure.
|
|
111
216
|
|
|
112
217
|
Args:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
Raises:
|
|
116
|
-
ValueError: If engine_id is not found in registered engines
|
|
218
|
+
engine_data: The engine data to add or update
|
|
117
219
|
"""
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
220
|
+
# Find existing engine
|
|
221
|
+
existing_engine = self._find_engine_by_id(self._engines_data, engine_data.id)
|
|
222
|
+
|
|
223
|
+
if existing_engine:
|
|
224
|
+
# Update existing engine
|
|
225
|
+
existing_engine.name = engine_data.name
|
|
226
|
+
existing_engine.created_at = engine_data.created_at
|
|
227
|
+
existing_engine.updated_at = datetime.now(tz=UTC).isoformat()
|
|
228
|
+
else:
|
|
229
|
+
# Add new engine
|
|
230
|
+
self._engines_data.engines.append(engine_data)
|
|
231
|
+
|
|
232
|
+
# Set as default if it's the first engine
|
|
233
|
+
if self._engines_data.default_engine_id is None and len(self._engines_data.engines) == 1:
|
|
234
|
+
self._engines_data.default_engine_id = engine_data.id
|
|
235
|
+
|
|
236
|
+
self._save_engines_data(self._engines_data)
|
|
124
237
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
"""Get the path where engine data is stored (for debugging/inspection).
|
|
238
|
+
def _load_engines_data(self) -> EnginesStorage:
|
|
239
|
+
"""Load engines data from storage.
|
|
128
240
|
|
|
129
241
|
Returns:
|
|
130
|
-
|
|
242
|
+
EnginesStorage: Engines data structure with engines array and default_engine_id
|
|
131
243
|
"""
|
|
132
|
-
|
|
244
|
+
engine_data_file = self._get_engine_data_file()
|
|
133
245
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
246
|
+
if engine_data_file.exists():
|
|
247
|
+
try:
|
|
248
|
+
with engine_data_file.open("r") as f:
|
|
249
|
+
data = json.load(f)
|
|
250
|
+
if isinstance(data, dict) and "engines" in data:
|
|
251
|
+
return EnginesStorage.model_validate(data)
|
|
252
|
+
except (json.JSONDecodeError, OSError):
|
|
253
|
+
pass
|
|
254
|
+
|
|
255
|
+
return EnginesStorage(engines=[], default_engine_id=None)
|
|
256
|
+
|
|
257
|
+
def _save_engines_data(self, engines_data: EnginesStorage) -> None:
|
|
258
|
+
"""Save engines data to storage.
|
|
259
|
+
|
|
260
|
+
Args:
|
|
261
|
+
engines_data: Engines data structure to save
|
|
262
|
+
"""
|
|
263
|
+
engine_data_dir = self._get_engine_data_dir()
|
|
264
|
+
engine_data_dir.mkdir(parents=True, exist_ok=True)
|
|
265
|
+
|
|
266
|
+
engine_data_file = self._get_engine_data_file()
|
|
267
|
+
with engine_data_file.open("w") as f:
|
|
268
|
+
json.dump(engines_data.model_dump(exclude_none=True), f, indent=2)
|
|
269
|
+
|
|
270
|
+
# Update in-memory copy
|
|
271
|
+
self._engines_data = engines_data
|
|
272
|
+
|
|
273
|
+
@staticmethod
|
|
274
|
+
def _get_engine_data_dir() -> Path:
|
|
275
|
+
"""Get the XDG data directory for engine identity storage."""
|
|
276
|
+
return xdg_data_home() / "griptape_nodes"
|
|
277
|
+
|
|
278
|
+
@staticmethod
|
|
279
|
+
def _get_engine_data_file() -> Path:
|
|
280
|
+
"""Get the path to the engine data storage file."""
|
|
281
|
+
return EngineIdentityManager._get_engine_data_dir() / EngineIdentityManager._ENGINE_DATA_FILE
|
|
282
|
+
|
|
283
|
+
@staticmethod
|
|
284
|
+
def _find_engine_by_id(engines_data: EnginesStorage, engine_id: str) -> EngineData | None:
|
|
285
|
+
"""Find an engine by ID in the engines data.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
engines_data: The engines data structure
|
|
289
|
+
engine_id: The engine ID to find
|
|
137
290
|
|
|
138
291
|
Returns:
|
|
139
|
-
|
|
292
|
+
EngineData | None: The engine data if found, None otherwise
|
|
140
293
|
"""
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
raise RuntimeError(msg)
|
|
146
|
-
return engine_id
|
|
294
|
+
for engine in engines_data.engines:
|
|
295
|
+
if engine.id == engine_id:
|
|
296
|
+
return engine
|
|
297
|
+
return None
|