griptape-nodes 0.40.0__py3-none-any.whl → 0.42.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/__init__.py +1 -5
- griptape_nodes/app/app.py +12 -9
- griptape_nodes/app/app_sessions.py +132 -36
- griptape_nodes/app/watch.py +3 -1
- griptape_nodes/drivers/storage/local_storage_driver.py +3 -2
- griptape_nodes/exe_types/flow.py +68 -368
- griptape_nodes/machines/control_flow.py +16 -13
- griptape_nodes/machines/node_resolution.py +16 -14
- griptape_nodes/node_library/workflow_registry.py +2 -2
- griptape_nodes/retained_mode/events/agent_events.py +70 -8
- griptape_nodes/retained_mode/events/app_events.py +132 -11
- griptape_nodes/retained_mode/events/arbitrary_python_events.py +23 -0
- griptape_nodes/retained_mode/events/base_events.py +7 -25
- 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/library_events.py +193 -15
- griptape_nodes/retained_mode/events/logger_events.py +11 -0
- griptape_nodes/retained_mode/events/node_events.py +243 -22
- griptape_nodes/retained_mode/events/object_events.py +40 -4
- griptape_nodes/retained_mode/events/os_events.py +13 -2
- griptape_nodes/retained_mode/events/parameter_events.py +212 -8
- 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 +46 -323
- griptape_nodes/retained_mode/managers/agent_manager.py +1 -1
- 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 +749 -64
- griptape_nodes/retained_mode/managers/library_manager.py +112 -2
- griptape_nodes/retained_mode/managers/node_manager.py +35 -32
- griptape_nodes/retained_mode/managers/object_manager.py +11 -3
- griptape_nodes/retained_mode/managers/os_manager.py +70 -1
- 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/workflow_manager.py +523 -454
- griptape_nodes/retained_mode/retained_mode.py +44 -0
- griptape_nodes/retained_mode/utils/engine_identity.py +141 -27
- {griptape_nodes-0.40.0.dist-info → griptape_nodes-0.42.0.dist-info}/METADATA +2 -2
- {griptape_nodes-0.40.0.dist-info → griptape_nodes-0.42.0.dist-info}/RECORD +48 -47
- griptape_nodes/retained_mode/utils/session_persistence.py +0 -105
- {griptape_nodes-0.40.0.dist-info → griptape_nodes-0.42.0.dist-info}/WHEEL +0 -0
- {griptape_nodes-0.40.0.dist-info → griptape_nodes-0.42.0.dist-info}/entry_points.txt +0 -0
- {griptape_nodes-0.40.0.dist-info → griptape_nodes-0.42.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -58,6 +58,9 @@ from griptape_nodes.retained_mode.events.library_events import (
|
|
|
58
58
|
GetNodeMetadataFromLibraryRequest,
|
|
59
59
|
GetNodeMetadataFromLibraryResultFailure,
|
|
60
60
|
GetNodeMetadataFromLibraryResultSuccess,
|
|
61
|
+
ListCapableLibraryEventHandlersRequest,
|
|
62
|
+
ListCapableLibraryEventHandlersResultFailure,
|
|
63
|
+
ListCapableLibraryEventHandlersResultSuccess,
|
|
61
64
|
ListCategoriesInLibraryRequest,
|
|
62
65
|
ListCategoriesInLibraryResultFailure,
|
|
63
66
|
ListCategoriesInLibraryResultSuccess,
|
|
@@ -85,19 +88,36 @@ from griptape_nodes.retained_mode.events.library_events import (
|
|
|
85
88
|
UnloadLibraryFromRegistryResultSuccess,
|
|
86
89
|
)
|
|
87
90
|
from griptape_nodes.retained_mode.events.object_events import ClearAllObjectStateRequest
|
|
91
|
+
from griptape_nodes.retained_mode.events.payload_registry import PayloadRegistry
|
|
88
92
|
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
89
93
|
from griptape_nodes.retained_mode.managers.os_manager import OSManager
|
|
90
94
|
|
|
91
95
|
if TYPE_CHECKING:
|
|
96
|
+
from collections.abc import Callable
|
|
92
97
|
from types import ModuleType
|
|
93
98
|
|
|
94
99
|
from griptape_nodes.node_library.advanced_node_library import AdvancedNodeLibrary
|
|
95
|
-
from griptape_nodes.retained_mode.events.base_events import ResultPayload
|
|
100
|
+
from griptape_nodes.retained_mode.events.base_events import Payload, RequestPayload, ResultPayload
|
|
96
101
|
from griptape_nodes.retained_mode.managers.event_manager import EventManager
|
|
97
102
|
|
|
98
103
|
logger = logging.getLogger("griptape_nodes")
|
|
99
104
|
|
|
100
105
|
|
|
106
|
+
def _find_griptape_uv_bin() -> str:
|
|
107
|
+
"""Find the uv binary, checking dedicated Griptape installation first, then system uv.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
Path to the uv binary to use
|
|
111
|
+
"""
|
|
112
|
+
# Check for dedicated Griptape uv installation first
|
|
113
|
+
dedicated_uv_path = xdg_data_home() / "griptape_nodes" / "bin" / "uv"
|
|
114
|
+
if dedicated_uv_path.exists():
|
|
115
|
+
return str(dedicated_uv_path)
|
|
116
|
+
|
|
117
|
+
# Fall back to system uv installation
|
|
118
|
+
return uv.find_uv_bin()
|
|
119
|
+
|
|
120
|
+
|
|
101
121
|
class LibraryManager:
|
|
102
122
|
SANDBOX_LIBRARY_NAME = "Sandbox Library"
|
|
103
123
|
|
|
@@ -124,6 +144,13 @@ class LibraryManager:
|
|
|
124
144
|
|
|
125
145
|
_library_file_path_to_info: dict[str, LibraryInfo]
|
|
126
146
|
|
|
147
|
+
@dataclass
|
|
148
|
+
class RegisteredEventHandler:
|
|
149
|
+
"""Information regarding an event handler from a registered library."""
|
|
150
|
+
|
|
151
|
+
handler: Callable[[RequestPayload], ResultPayload]
|
|
152
|
+
library_data: LibrarySchema
|
|
153
|
+
|
|
127
154
|
# Stable module namespace mappings for workflow serialization
|
|
128
155
|
# These mappings ensure that dynamically loaded modules can be reliably imported
|
|
129
156
|
# in generated workflow code by providing stable, predictable import paths.
|
|
@@ -148,10 +175,14 @@ class LibraryManager:
|
|
|
148
175
|
self._dynamic_to_stable_module_mapping = {}
|
|
149
176
|
self._stable_to_dynamic_module_mapping = {}
|
|
150
177
|
self._library_to_stable_modules = {}
|
|
178
|
+
self._library_event_handler_mappings: dict[type[Payload], dict[str, LibraryManager.RegisteredEventHandler]] = {}
|
|
151
179
|
|
|
152
180
|
event_manager.assign_manager_to_request_type(
|
|
153
181
|
ListRegisteredLibrariesRequest, self.on_list_registered_libraries_request
|
|
154
182
|
)
|
|
183
|
+
event_manager.assign_manager_to_request_type(
|
|
184
|
+
ListCapableLibraryEventHandlersRequest, self.on_list_capable_event_handlers
|
|
185
|
+
)
|
|
155
186
|
event_manager.assign_manager_to_request_type(
|
|
156
187
|
ListNodeTypesInLibraryRequest, self.on_list_node_types_in_library_request
|
|
157
188
|
)
|
|
@@ -277,6 +308,33 @@ class LibraryManager:
|
|
|
277
308
|
return library_info
|
|
278
309
|
return None
|
|
279
310
|
|
|
311
|
+
def on_register_event_handler(
|
|
312
|
+
self,
|
|
313
|
+
request_type: type[RequestPayload],
|
|
314
|
+
handler: Callable[[RequestPayload], ResultPayload],
|
|
315
|
+
library_data: LibrarySchema,
|
|
316
|
+
) -> None:
|
|
317
|
+
"""Register an event handler for a specific request type from a library."""
|
|
318
|
+
if self._library_event_handler_mappings.get(request_type) is None:
|
|
319
|
+
self._library_event_handler_mappings[request_type] = {}
|
|
320
|
+
self._library_event_handler_mappings[request_type][library_data.name] = LibraryManager.RegisteredEventHandler(
|
|
321
|
+
handler=handler, library_data=library_data
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
def get_registered_event_handlers(self, request_type: type[Payload]) -> dict[str, RegisteredEventHandler]:
|
|
325
|
+
"""Get all registered event handlers for a specific request type."""
|
|
326
|
+
return self._library_event_handler_mappings.get(request_type, {})
|
|
327
|
+
|
|
328
|
+
def on_list_capable_event_handlers(self, request: ListCapableLibraryEventHandlersRequest) -> ResultPayload:
|
|
329
|
+
"""Get all registered event handlers for a specific request type."""
|
|
330
|
+
request_type = PayloadRegistry.get_type(request.request_type)
|
|
331
|
+
if request_type is None:
|
|
332
|
+
return ListCapableLibraryEventHandlersResultFailure(
|
|
333
|
+
exception=KeyError(f"Request type '{request.request_type}' is not registered in the PayloadRegistry.")
|
|
334
|
+
)
|
|
335
|
+
handler_mappings = self.get_registered_event_handlers(request_type)
|
|
336
|
+
return ListCapableLibraryEventHandlersResultSuccess(handlers=list(handler_mappings.keys()))
|
|
337
|
+
|
|
280
338
|
def on_list_registered_libraries_request(self, _request: ListRegisteredLibrariesRequest) -> ResultPayload:
|
|
281
339
|
# Make a COPY of the list
|
|
282
340
|
snapshot_list = LibraryRegistry.list_libraries()
|
|
@@ -742,6 +800,28 @@ class LibraryManager:
|
|
|
742
800
|
logger.error(details)
|
|
743
801
|
return RegisterLibraryFromFileResultFailure()
|
|
744
802
|
if self._can_write_to_venv_location(library_venv_python_path):
|
|
803
|
+
# Check disk space before installing dependencies
|
|
804
|
+
config_manager = GriptapeNodes.ConfigManager()
|
|
805
|
+
min_space_gb = config_manager.get_config_value("minimum_disk_space_gb_libraries")
|
|
806
|
+
if not OSManager.check_available_disk_space(Path(venv_path), min_space_gb):
|
|
807
|
+
error_msg = OSManager.format_disk_space_error(Path(venv_path))
|
|
808
|
+
logger.error(
|
|
809
|
+
"Attempted to load Library JSON from '%s'. Failed when installing dependencies (requires %.1f GB): %s",
|
|
810
|
+
json_path,
|
|
811
|
+
min_space_gb,
|
|
812
|
+
error_msg,
|
|
813
|
+
)
|
|
814
|
+
self._library_file_path_to_info[file_path] = LibraryManager.LibraryInfo(
|
|
815
|
+
library_path=file_path,
|
|
816
|
+
library_name=library_data.name,
|
|
817
|
+
library_version=library_version,
|
|
818
|
+
status=LibraryManager.LibraryStatus.UNUSABLE,
|
|
819
|
+
problems=[
|
|
820
|
+
f"Insufficient disk space for dependencies (requires {min_space_gb} GB): {error_msg}"
|
|
821
|
+
],
|
|
822
|
+
)
|
|
823
|
+
return RegisterLibraryFromFileResultFailure()
|
|
824
|
+
|
|
745
825
|
# Grab the python executable from the virtual environment so that we can pip install there
|
|
746
826
|
logger.info(
|
|
747
827
|
"Installing dependencies for library '%s' with pip in venv at %s", library_data.name, venv_path
|
|
@@ -771,6 +851,7 @@ class LibraryManager:
|
|
|
771
851
|
except subprocess.CalledProcessError as e:
|
|
772
852
|
# Failed to create the library
|
|
773
853
|
error_details = f"return code={e.returncode}, stdout={e.stdout}, stderr={e.stderr}"
|
|
854
|
+
|
|
774
855
|
self._library_file_path_to_info[file_path] = LibraryManager.LibraryInfo(
|
|
775
856
|
library_path=file_path,
|
|
776
857
|
library_name=library_data.name,
|
|
@@ -863,10 +944,23 @@ class LibraryManager:
|
|
|
863
944
|
logger.error(details)
|
|
864
945
|
return RegisterLibraryFromRequirementSpecifierResultFailure()
|
|
865
946
|
if self._can_write_to_venv_location(library_python_venv_path):
|
|
947
|
+
# Check disk space before installing dependencies
|
|
948
|
+
config_manager = GriptapeNodes.ConfigManager()
|
|
949
|
+
min_space_gb = config_manager.get_config_value("minimum_disk_space_gb_libraries")
|
|
950
|
+
if not OSManager.check_available_disk_space(Path(venv_path), min_space_gb):
|
|
951
|
+
error_msg = OSManager.format_disk_space_error(Path(venv_path))
|
|
952
|
+
logger.error(
|
|
953
|
+
"Attempted to install library '%s'. Failed when installing dependencies (requires %.1f GB): %s",
|
|
954
|
+
request.requirement_specifier,
|
|
955
|
+
min_space_gb,
|
|
956
|
+
error_msg,
|
|
957
|
+
)
|
|
958
|
+
return RegisterLibraryFromRequirementSpecifierResultFailure()
|
|
959
|
+
|
|
866
960
|
logger.info("Installing dependency '%s' with pip in venv at %s", package_name, venv_path)
|
|
867
961
|
subprocess.run( # noqa: S603
|
|
868
962
|
[
|
|
869
|
-
|
|
963
|
+
_find_griptape_uv_bin(),
|
|
870
964
|
"pip",
|
|
871
965
|
"install",
|
|
872
966
|
request.requirement_specifier,
|
|
@@ -922,6 +1016,19 @@ class LibraryManager:
|
|
|
922
1016
|
if library_venv_path.exists():
|
|
923
1017
|
logger.debug("Virtual environment already exists at %s", library_venv_path)
|
|
924
1018
|
else:
|
|
1019
|
+
# Check disk space before creating virtual environment
|
|
1020
|
+
config_manager = GriptapeNodes.ConfigManager()
|
|
1021
|
+
min_space_gb = config_manager.get_config_value("minimum_disk_space_gb_libraries")
|
|
1022
|
+
if not OSManager.check_available_disk_space(library_venv_path.parent, min_space_gb):
|
|
1023
|
+
error_msg = OSManager.format_disk_space_error(library_venv_path.parent)
|
|
1024
|
+
logger.error(
|
|
1025
|
+
"Attempted to create virtual environment (requires %.1f GB). Failed: %s", min_space_gb, error_msg
|
|
1026
|
+
)
|
|
1027
|
+
error_message = (
|
|
1028
|
+
f"Disk space error creating virtual environment (requires {min_space_gb} GB): {error_msg}"
|
|
1029
|
+
)
|
|
1030
|
+
raise RuntimeError(error_message)
|
|
1031
|
+
|
|
925
1032
|
try:
|
|
926
1033
|
logger.info("Creating virtual environment at %s with Python %s", library_venv_path, python_version)
|
|
927
1034
|
subprocess.run( # noqa: S603
|
|
@@ -1441,6 +1548,9 @@ class LibraryManager:
|
|
|
1441
1548
|
self._remove_missing_libraries_from_config(config_category=user_libraries_section)
|
|
1442
1549
|
|
|
1443
1550
|
def on_app_initialization_complete(self, _payload: AppInitializationComplete) -> None:
|
|
1551
|
+
GriptapeNodes.EngineIdentityManager().initialize_engine_id()
|
|
1552
|
+
GriptapeNodes.SessionManager().get_saved_session_id()
|
|
1553
|
+
|
|
1444
1554
|
# App just got init'd. See if there are library JSONs to load!
|
|
1445
1555
|
self.load_all_libraries_from_config()
|
|
1446
1556
|
|
|
@@ -185,23 +185,24 @@ class NodeManager:
|
|
|
185
185
|
# Get all connections for this node and update them.
|
|
186
186
|
flow_name = self.get_node_parent_flow_by_name(old_name)
|
|
187
187
|
flow = GriptapeNodes.FlowManager().get_flow_by_name(flow_name)
|
|
188
|
+
connections = GriptapeNodes.FlowManager().get_connections()
|
|
188
189
|
# Get all incoming and outgoing connections and update them.
|
|
189
|
-
if old_name in
|
|
190
|
-
incoming_connections =
|
|
190
|
+
if old_name in connections.incoming_index:
|
|
191
|
+
incoming_connections = connections.incoming_index[old_name]
|
|
191
192
|
for connection_ids in incoming_connections.values():
|
|
192
193
|
for connection_id in connection_ids:
|
|
193
|
-
connection =
|
|
194
|
+
connection = connections.connections[connection_id]
|
|
194
195
|
connection.target_node.name = new_name
|
|
195
|
-
temp =
|
|
196
|
-
|
|
197
|
-
if old_name in
|
|
198
|
-
outgoing_connections =
|
|
196
|
+
temp = connections.incoming_index.pop(old_name)
|
|
197
|
+
connections.incoming_index[new_name] = temp
|
|
198
|
+
if old_name in connections.outgoing_index:
|
|
199
|
+
outgoing_connections = connections.outgoing_index[old_name]
|
|
199
200
|
for connection_ids in outgoing_connections.values():
|
|
200
201
|
for connection_id in connection_ids:
|
|
201
|
-
connection =
|
|
202
|
+
connection = connections.connections[connection_id]
|
|
202
203
|
connection.source_node.name = new_name
|
|
203
|
-
temp =
|
|
204
|
-
|
|
204
|
+
temp = connections.outgoing_index.pop(old_name)
|
|
205
|
+
connections.outgoing_index[new_name] = temp
|
|
205
206
|
# update the node in the flow!
|
|
206
207
|
flow.remove_node(old_name)
|
|
207
208
|
node.name = new_name
|
|
@@ -395,11 +396,11 @@ class NodeManager:
|
|
|
395
396
|
This method also clears the flow queue regardless of whether cancellation occurred,
|
|
396
397
|
to ensure the specified node is not processed in the future.
|
|
397
398
|
"""
|
|
398
|
-
if
|
|
399
|
+
if GriptapeNodes.FlowManager().check_for_existing_running_flow():
|
|
399
400
|
# get the current node executing / resolving
|
|
400
401
|
# if it's in connected nodes, cancel flow.
|
|
401
402
|
# otherwise, leave it.
|
|
402
|
-
control_node_name, resolving_node_name =
|
|
403
|
+
control_node_name, resolving_node_name = GriptapeNodes.FlowManager().flow_state(parent_flow)
|
|
403
404
|
connected_nodes = parent_flow.get_all_connected_nodes(node)
|
|
404
405
|
cancelled = False
|
|
405
406
|
if control_node_name is not None:
|
|
@@ -423,8 +424,8 @@ class NodeManager:
|
|
|
423
424
|
)
|
|
424
425
|
logger.error(details)
|
|
425
426
|
return DeleteNodeResultFailure()
|
|
426
|
-
# Clear the queue, because we don't want to
|
|
427
|
-
parent_flow.
|
|
427
|
+
# Clear the execution queue, because we don't want to hit this node eventually.
|
|
428
|
+
parent_flow.clear_execution_queue()
|
|
428
429
|
return None
|
|
429
430
|
|
|
430
431
|
def on_delete_node_request(self, request: DeleteNodeRequest) -> ResultPayload: # noqa: C901, PLR0911 (complex logic, lots of edge cases)
|
|
@@ -640,7 +641,7 @@ class NodeManager:
|
|
|
640
641
|
|
|
641
642
|
parent_flow_name = self._name_to_parent_flow_name[node_name]
|
|
642
643
|
try:
|
|
643
|
-
|
|
644
|
+
GriptapeNodes.FlowManager().get_flow_by_name(parent_flow_name)
|
|
644
645
|
except KeyError as err:
|
|
645
646
|
details = f"Attempted to list Connections for a Node '{node_name}'. Error: {err}"
|
|
646
647
|
logger.error(details)
|
|
@@ -649,7 +650,7 @@ class NodeManager:
|
|
|
649
650
|
return result
|
|
650
651
|
|
|
651
652
|
# Kinda gross, but let's do it
|
|
652
|
-
connection_mgr =
|
|
653
|
+
connection_mgr = GriptapeNodes.FlowManager().get_connections()
|
|
653
654
|
# get outgoing connections
|
|
654
655
|
outgoing_connections_list = []
|
|
655
656
|
if node_name in connection_mgr.outgoing_index:
|
|
@@ -1429,7 +1430,7 @@ class NodeManager:
|
|
|
1429
1430
|
return SetParameterValueResultFailure()
|
|
1430
1431
|
if not request.initial_setup and modified:
|
|
1431
1432
|
try:
|
|
1432
|
-
|
|
1433
|
+
GriptapeNodes.FlowManager().get_connections().unresolve_future_nodes(node)
|
|
1433
1434
|
except Exception as err:
|
|
1434
1435
|
details = f"Attempted to set parameter value for '{node_name}.{request.parameter_name}'. Failed because Exception: {err}"
|
|
1435
1436
|
logger.error(details)
|
|
@@ -1752,12 +1753,12 @@ class NodeManager:
|
|
|
1752
1753
|
details = f'Failed to fetch parent flow for "{node_name}"'
|
|
1753
1754
|
logger.error(details)
|
|
1754
1755
|
return ResolveNodeResultFailure(validation_exceptions=[])
|
|
1755
|
-
if
|
|
1756
|
+
if GriptapeNodes.FlowManager().check_for_existing_running_flow():
|
|
1756
1757
|
details = f"Failed to resolve from node '{node_name}'. Flow is already running."
|
|
1757
1758
|
logger.error(details)
|
|
1758
1759
|
return ResolveNodeResultFailure(validation_exceptions=[])
|
|
1759
1760
|
try:
|
|
1760
|
-
|
|
1761
|
+
GriptapeNodes.FlowManager().get_connections().unresolve_future_nodes(node)
|
|
1761
1762
|
except Exception as e:
|
|
1762
1763
|
details = f'Failed to mark future nodes dirty. Unable to kick off flow from "{node_name}": {e}'
|
|
1763
1764
|
logger.error(details)
|
|
@@ -1783,7 +1784,7 @@ class NodeManager:
|
|
|
1783
1784
|
logger.error(details)
|
|
1784
1785
|
return StartFlowResultFailure(validation_exceptions=[e])
|
|
1785
1786
|
try:
|
|
1786
|
-
|
|
1787
|
+
GriptapeNodes.FlowManager().resolve_singular_node(flow, node, debug_mode)
|
|
1787
1788
|
except Exception as e:
|
|
1788
1789
|
details = f'Failed to resolve "{node_name}". Error: {e}'
|
|
1789
1790
|
logger.error(details)
|
|
@@ -2098,15 +2099,16 @@ class NodeManager:
|
|
|
2098
2099
|
parameter_commands[result.serialized_node_commands.node_uuid] = result.set_parameter_value_commands
|
|
2099
2100
|
try:
|
|
2100
2101
|
flow_name = self.get_node_parent_flow_by_name(node_name)
|
|
2101
|
-
|
|
2102
|
+
GriptapeNodes.FlowManager().get_flow_by_name(flow_name)
|
|
2102
2103
|
except Exception:
|
|
2103
2104
|
details = f"Attempted to serialize a selection of Nodes. Failed to get the flow of node {node_name}. Cannot serialize connections for this node."
|
|
2104
2105
|
logger.warning(details)
|
|
2105
2106
|
continue
|
|
2106
|
-
|
|
2107
|
+
connections = GriptapeNodes.FlowManager().get_connections()
|
|
2108
|
+
if node_name in connections.outgoing_index:
|
|
2107
2109
|
node_connections = [
|
|
2108
|
-
|
|
2109
|
-
for category_dict in
|
|
2110
|
+
connections.connections[connection_id]
|
|
2111
|
+
for category_dict in connections.outgoing_index[node_name].values()
|
|
2110
2112
|
for connection_id in category_dict
|
|
2111
2113
|
]
|
|
2112
2114
|
for connection in node_connections:
|
|
@@ -2215,7 +2217,7 @@ class NodeManager:
|
|
|
2215
2217
|
details = "Failed to serialized selected nodes."
|
|
2216
2218
|
logger.error(details)
|
|
2217
2219
|
return DuplicateSelectedNodesResultFailure()
|
|
2218
|
-
result = GriptapeNodes.handle_request(DeserializeSelectedNodesFromCommandsRequest(positions=
|
|
2220
|
+
result = GriptapeNodes.handle_request(DeserializeSelectedNodesFromCommandsRequest(positions=request.positions))
|
|
2219
2221
|
if not isinstance(result, DeserializeSelectedNodesFromCommandsResultSuccess):
|
|
2220
2222
|
details = "Failed to deserialize selected nodes."
|
|
2221
2223
|
logger.error(details)
|
|
@@ -2420,22 +2422,23 @@ class NodeManager:
|
|
|
2420
2422
|
|
|
2421
2423
|
# Get all connections for this node
|
|
2422
2424
|
flow_name = self.get_node_parent_flow_by_name(node_name)
|
|
2423
|
-
|
|
2425
|
+
GriptapeNodes.FlowManager().get_flow_by_name(flow_name)
|
|
2426
|
+
connections = GriptapeNodes.FlowManager().get_connections()
|
|
2424
2427
|
|
|
2425
2428
|
# Update connections that reference this parameter
|
|
2426
|
-
if node_name in
|
|
2427
|
-
incoming_connections =
|
|
2429
|
+
if node_name in connections.incoming_index:
|
|
2430
|
+
incoming_connections = connections.incoming_index[node_name]
|
|
2428
2431
|
for connection_ids in incoming_connections.values():
|
|
2429
2432
|
for connection_id in connection_ids:
|
|
2430
|
-
connection =
|
|
2433
|
+
connection = connections.connections[connection_id]
|
|
2431
2434
|
if connection.target_parameter.name == request.parameter_name:
|
|
2432
2435
|
connection.target_parameter.name = request.new_parameter_name
|
|
2433
2436
|
|
|
2434
|
-
if node_name in
|
|
2435
|
-
outgoing_connections =
|
|
2437
|
+
if node_name in connections.outgoing_index:
|
|
2438
|
+
outgoing_connections = connections.outgoing_index[node_name]
|
|
2436
2439
|
for connection_ids in outgoing_connections.values():
|
|
2437
2440
|
for connection_id in connection_ids:
|
|
2438
|
-
connection =
|
|
2441
|
+
connection = connections.connections[connection_id]
|
|
2439
2442
|
if connection.source_parameter.name == request.parameter_name:
|
|
2440
2443
|
connection.source_parameter.name = request.new_parameter_name
|
|
2441
2444
|
|
|
@@ -100,7 +100,7 @@ class ObjectManager:
|
|
|
100
100
|
logger.log(level=log_level, msg=details)
|
|
101
101
|
return RenameObjectResultSuccess(final_name=final_name)
|
|
102
102
|
|
|
103
|
-
def on_clear_all_object_state_request(self, request: ClearAllObjectStateRequest) -> ResultPayload:
|
|
103
|
+
def on_clear_all_object_state_request(self, request: ClearAllObjectStateRequest) -> ResultPayload: # noqa: C901
|
|
104
104
|
if not request.i_know_what_im_doing:
|
|
105
105
|
logger.warning(
|
|
106
106
|
"Attempted to clear all object state and delete everything. Failed because they didn't know what they were doing."
|
|
@@ -109,14 +109,22 @@ class ObjectManager:
|
|
|
109
109
|
# Let's try and clear it all.
|
|
110
110
|
# Cancel any running flows.
|
|
111
111
|
flows = self.get_filtered_subset(type=ControlFlow)
|
|
112
|
-
for flow_name
|
|
113
|
-
if
|
|
112
|
+
for flow_name in flows:
|
|
113
|
+
if GriptapeNodes.FlowManager().check_for_existing_running_flow():
|
|
114
114
|
result = GriptapeNodes.handle_request(CancelFlowRequest(flow_name=flow_name))
|
|
115
115
|
if not result.succeeded():
|
|
116
116
|
details = f"Attempted to clear all object state and delete everything. Failed because running flow '{flow_name}' could not cancel."
|
|
117
117
|
logger.error(details)
|
|
118
118
|
return ClearAllObjectStateResultFailure()
|
|
119
119
|
|
|
120
|
+
try:
|
|
121
|
+
# Reset global execution state first to eliminate all references before deletion
|
|
122
|
+
GriptapeNodes.FlowManager().reset_global_execution_state()
|
|
123
|
+
except Exception as e:
|
|
124
|
+
details = f"Attempted to reset global execution state. Failed with exception: {e}"
|
|
125
|
+
logger.error(details)
|
|
126
|
+
return ClearAllObjectStateResultFailure()
|
|
127
|
+
|
|
120
128
|
try:
|
|
121
129
|
# Delete the existing flows, which will clear all nodes and connections.
|
|
122
130
|
GriptapeNodes.clear_data()
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
|
+
import shutil
|
|
3
4
|
import subprocess
|
|
4
5
|
import sys
|
|
5
6
|
from pathlib import Path
|
|
6
|
-
from typing import Any
|
|
7
|
+
from typing import Any, NamedTuple
|
|
7
8
|
|
|
8
9
|
from rich.console import Console
|
|
9
10
|
|
|
@@ -19,6 +20,14 @@ console = Console()
|
|
|
19
20
|
logger = logging.getLogger("griptape_nodes")
|
|
20
21
|
|
|
21
22
|
|
|
23
|
+
class DiskSpaceInfo(NamedTuple):
|
|
24
|
+
"""Disk space information in bytes."""
|
|
25
|
+
|
|
26
|
+
total: int
|
|
27
|
+
used: int
|
|
28
|
+
free: int
|
|
29
|
+
|
|
30
|
+
|
|
22
31
|
class OSManager:
|
|
23
32
|
"""A class to manage OS-level scenarios.
|
|
24
33
|
|
|
@@ -129,3 +138,63 @@ class OSManager:
|
|
|
129
138
|
except Exception as e:
|
|
130
139
|
logger.error("Exception occurred when trying to open file: %s", type(e).__name__)
|
|
131
140
|
return OpenAssociatedFileResultFailure()
|
|
141
|
+
|
|
142
|
+
@staticmethod
|
|
143
|
+
def get_disk_space_info(path: Path) -> DiskSpaceInfo:
|
|
144
|
+
"""Get disk space information for a given path.
|
|
145
|
+
|
|
146
|
+
Args:
|
|
147
|
+
path: The path to check disk space for.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
DiskSpaceInfo with total, used, and free disk space in bytes.
|
|
151
|
+
"""
|
|
152
|
+
stat = shutil.disk_usage(path)
|
|
153
|
+
return DiskSpaceInfo(total=stat.total, used=stat.used, free=stat.free)
|
|
154
|
+
|
|
155
|
+
@staticmethod
|
|
156
|
+
def check_available_disk_space(path: Path, required_gb: float) -> bool:
|
|
157
|
+
"""Check if there is sufficient disk space available.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
path: The path to check disk space for.
|
|
161
|
+
required_gb: The minimum disk space required in GB.
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
True if sufficient space is available, False otherwise.
|
|
165
|
+
"""
|
|
166
|
+
try:
|
|
167
|
+
disk_info = OSManager.get_disk_space_info(path)
|
|
168
|
+
required_bytes = int(required_gb * 1024 * 1024 * 1024) # Convert GB to bytes
|
|
169
|
+
return disk_info.free >= required_bytes # noqa: TRY300
|
|
170
|
+
except OSError:
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
@staticmethod
|
|
174
|
+
def format_disk_space_error(path: Path, exception: Exception | None = None) -> str:
|
|
175
|
+
"""Format a user-friendly disk space error message.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
path: The path where the disk space issue occurred.
|
|
179
|
+
exception: The original exception, if any.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
A formatted error message with disk space information.
|
|
183
|
+
"""
|
|
184
|
+
try:
|
|
185
|
+
disk_info = OSManager.get_disk_space_info(path)
|
|
186
|
+
free_gb = disk_info.free / (1024**3)
|
|
187
|
+
used_gb = disk_info.used / (1024**3)
|
|
188
|
+
total_gb = disk_info.total / (1024**3)
|
|
189
|
+
|
|
190
|
+
error_msg = f"Insufficient disk space at {path}. "
|
|
191
|
+
error_msg += f"Available: {free_gb:.2f} GB, Used: {used_gb:.2f} GB, Total: {total_gb:.2f} GB. "
|
|
192
|
+
|
|
193
|
+
if exception:
|
|
194
|
+
error_msg += f"Error: {exception}"
|
|
195
|
+
else:
|
|
196
|
+
error_msg += "Please free up disk space and try again."
|
|
197
|
+
|
|
198
|
+
return error_msg # noqa: TRY300
|
|
199
|
+
except OSError:
|
|
200
|
+
return f"Disk space error at {path}. Unable to retrieve disk space information."
|
|
@@ -32,6 +32,10 @@ class SecretsManager:
|
|
|
32
32
|
def __init__(self, config_manager: ConfigManager, event_manager: EventManager | None = None) -> None:
|
|
33
33
|
self.config_manager = config_manager
|
|
34
34
|
|
|
35
|
+
# So that users can access secrets directly via `os.environ`
|
|
36
|
+
load_dotenv(self.workspace_env_path, override=False)
|
|
37
|
+
load_dotenv(ENV_VAR_PATH, override=False)
|
|
38
|
+
|
|
35
39
|
# Register all our listeners.
|
|
36
40
|
if event_manager is not None:
|
|
37
41
|
event_manager.assign_manager_to_request_type(GetSecretValueRequest, self.on_handle_get_secret_request)
|