griptape-nodes 0.43.1__py3-none-any.whl → 0.45.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 +46 -52
- griptape_nodes/app/.python-version +0 -0
- griptape_nodes/app/__init__.py +0 -0
- griptape_nodes/app/api.py +37 -41
- griptape_nodes/app/app.py +70 -3
- griptape_nodes/app/watch.py +5 -2
- griptape_nodes/bootstrap/__init__.py +0 -0
- griptape_nodes/bootstrap/workflow_executors/__init__.py +0 -0
- griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +7 -1
- griptape_nodes/bootstrap/workflow_executors/subprocess_workflow_executor.py +90 -0
- griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +7 -1
- griptape_nodes/drivers/__init__.py +0 -0
- griptape_nodes/drivers/storage/__init__.py +0 -0
- griptape_nodes/drivers/storage/base_storage_driver.py +90 -0
- griptape_nodes/drivers/storage/griptape_cloud_storage_driver.py +48 -0
- griptape_nodes/drivers/storage/local_storage_driver.py +37 -0
- 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 +222 -17
- griptape_nodes/exe_types/flow.py +0 -0
- griptape_nodes/exe_types/node_types.py +20 -5
- griptape_nodes/exe_types/type_validator.py +0 -0
- griptape_nodes/machines/__init__.py +0 -0
- griptape_nodes/machines/control_flow.py +5 -4
- griptape_nodes/machines/fsm.py +0 -0
- griptape_nodes/machines/node_resolution.py +110 -74
- griptape_nodes/mcp_server/__init__.py +0 -0
- griptape_nodes/mcp_server/server.py +16 -8
- griptape_nodes/mcp_server/ws_request_manager.py +0 -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 +29 -0
- 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 +0 -0
- griptape_nodes/retained_mode/events/app_events.py +3 -8
- griptape_nodes/retained_mode/events/arbitrary_python_events.py +0 -0
- griptape_nodes/retained_mode/events/base_events.py +15 -7
- griptape_nodes/retained_mode/events/config_events.py +0 -0
- griptape_nodes/retained_mode/events/connection_events.py +0 -0
- griptape_nodes/retained_mode/events/context_events.py +0 -0
- griptape_nodes/retained_mode/events/execution_events.py +0 -0
- griptape_nodes/retained_mode/events/flow_events.py +2 -1
- griptape_nodes/retained_mode/events/generate_request_payload_schemas.py +0 -0
- griptape_nodes/retained_mode/events/library_events.py +0 -0
- griptape_nodes/retained_mode/events/logger_events.py +0 -0
- griptape_nodes/retained_mode/events/node_events.py +36 -0
- griptape_nodes/retained_mode/events/object_events.py +0 -0
- griptape_nodes/retained_mode/events/os_events.py +98 -6
- griptape_nodes/retained_mode/events/parameter_events.py +0 -0
- griptape_nodes/retained_mode/events/payload_registry.py +0 -0
- griptape_nodes/retained_mode/events/secrets_events.py +0 -0
- griptape_nodes/retained_mode/events/static_file_events.py +0 -0
- griptape_nodes/retained_mode/events/sync_events.py +60 -0
- griptape_nodes/retained_mode/events/validation_events.py +0 -0
- griptape_nodes/retained_mode/events/workflow_events.py +231 -0
- griptape_nodes/retained_mode/griptape_nodes.py +9 -4
- griptape_nodes/retained_mode/managers/__init__.py +0 -0
- griptape_nodes/retained_mode/managers/agent_manager.py +0 -0
- griptape_nodes/retained_mode/managers/arbitrary_code_exec_manager.py +0 -0
- griptape_nodes/retained_mode/managers/config_manager.py +1 -1
- griptape_nodes/retained_mode/managers/context_manager.py +0 -0
- griptape_nodes/retained_mode/managers/engine_identity_manager.py +0 -0
- griptape_nodes/retained_mode/managers/event_manager.py +0 -0
- griptape_nodes/retained_mode/managers/flow_manager.py +6 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/__init__.py +0 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/data_models.py +0 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +0 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_fsm.py +0 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/__init__.py +0 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/base.py +0 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/github.py +0 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/local_file.py +0 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/package.py +0 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/sandbox.py +0 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance.py +0 -0
- griptape_nodes/retained_mode/managers/library_lifecycle/library_status.py +0 -0
- griptape_nodes/retained_mode/managers/library_manager.py +8 -26
- griptape_nodes/retained_mode/managers/node_manager.py +78 -7
- griptape_nodes/retained_mode/managers/object_manager.py +0 -0
- griptape_nodes/retained_mode/managers/operation_manager.py +7 -0
- griptape_nodes/retained_mode/managers/os_manager.py +133 -8
- griptape_nodes/retained_mode/managers/secrets_manager.py +0 -0
- griptape_nodes/retained_mode/managers/session_manager.py +0 -0
- griptape_nodes/retained_mode/managers/settings.py +5 -0
- griptape_nodes/retained_mode/managers/static_files_manager.py +0 -0
- griptape_nodes/retained_mode/managers/sync_manager.py +498 -0
- griptape_nodes/retained_mode/managers/version_compatibility_manager.py +0 -0
- griptape_nodes/retained_mode/managers/workflow_manager.py +736 -33
- griptape_nodes/retained_mode/retained_mode.py +23 -0
- griptape_nodes/retained_mode/utils/__init__.py +0 -0
- griptape_nodes/retained_mode/utils/engine_identity.py +0 -0
- 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 +18 -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 +4 -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 +0 -0
- griptape_nodes/utils/metaclasses.py +0 -0
- griptape_nodes/utils/uv_utils.py +18 -0
- griptape_nodes/utils/version_utils.py +51 -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 +0 -0
- {griptape_nodes-0.43.1.dist-info → griptape_nodes-0.45.0.dist-info}/METADATA +2 -1
- {griptape_nodes-0.43.1.dist-info → griptape_nodes-0.45.0.dist-info}/RECORD +42 -47
- {griptape_nodes-0.43.1.dist-info → griptape_nodes-0.45.0.dist-info}/WHEEL +1 -1
- griptape_nodes/bootstrap/bootstrap_script.py +0 -54
- griptape_nodes/bootstrap/post_build_install_script.sh +0 -3
- griptape_nodes/bootstrap/pre_build_install_script.sh +0 -4
- griptape_nodes/bootstrap/register_libraries_script.py +0 -32
- griptape_nodes/bootstrap/structure_config.yaml +0 -15
- griptape_nodes/bootstrap/workflow_runners/__init__.py +0 -1
- griptape_nodes/bootstrap/workflow_runners/bootstrap_workflow_runner.py +0 -28
- griptape_nodes/bootstrap/workflow_runners/local_workflow_runner.py +0 -237
- griptape_nodes/bootstrap/workflow_runners/subprocess_workflow_runner.py +0 -62
- griptape_nodes/bootstrap/workflow_runners/workflow_runner.py +0 -11
- {griptape_nodes-0.43.1.dist-info → griptape_nodes-0.45.0.dist-info}/entry_points.txt +0 -0
|
@@ -81,6 +81,9 @@ from griptape_nodes.retained_mode.events.node_events import (
|
|
|
81
81
|
SerializeNodeToCommandsResultSuccess,
|
|
82
82
|
SerializeSelectedNodesToCommandsRequest,
|
|
83
83
|
SerializeSelectedNodesToCommandsResultSuccess,
|
|
84
|
+
SetLockNodeStateRequest,
|
|
85
|
+
SetLockNodeStateResultFailure,
|
|
86
|
+
SetLockNodeStateResultSuccess,
|
|
84
87
|
SetNodeMetadataRequest,
|
|
85
88
|
SetNodeMetadataResultFailure,
|
|
86
89
|
SetNodeMetadataResultSuccess,
|
|
@@ -178,6 +181,7 @@ class NodeManager:
|
|
|
178
181
|
DeserializeSelectedNodesFromCommandsRequest, self.on_deserialize_selected_nodes_from_commands
|
|
179
182
|
)
|
|
180
183
|
event_manager.assign_manager_to_request_type(DuplicateSelectedNodesRequest, self.on_duplicate_selected_nodes)
|
|
184
|
+
event_manager.assign_manager_to_request_type(SetLockNodeStateRequest, self.on_toggle_lock_node_request)
|
|
181
185
|
|
|
182
186
|
def handle_node_rename(self, old_name: str, new_name: str) -> None:
|
|
183
187
|
# Get the node itself
|
|
@@ -781,6 +785,13 @@ class NodeManager:
|
|
|
781
785
|
result = AddParameterToNodeResultFailure()
|
|
782
786
|
return result
|
|
783
787
|
|
|
788
|
+
# Check if node is locked
|
|
789
|
+
if node.lock:
|
|
790
|
+
details = f"Attempted to add Parameter '{request.parameter_name}' to Node '{node_name}'. Failed because the Node was locked."
|
|
791
|
+
logger.error(details)
|
|
792
|
+
result = AddParameterToNodeResultFailure()
|
|
793
|
+
return result
|
|
794
|
+
|
|
784
795
|
if request.parent_container_name and not request.initial_setup:
|
|
785
796
|
parameter = node.get_parameter_by_name(request.parent_container_name)
|
|
786
797
|
if parameter is None:
|
|
@@ -923,7 +934,13 @@ class NodeManager:
|
|
|
923
934
|
|
|
924
935
|
result = RemoveParameterFromNodeResultFailure()
|
|
925
936
|
return result
|
|
937
|
+
# Check if the node is locked
|
|
938
|
+
if node.lock:
|
|
939
|
+
details = f"Attempted to remove Element '{request.parameter_name}' from Node '{node_name}'. Failed because the Node was locked."
|
|
940
|
+
logger.error(details)
|
|
926
941
|
|
|
942
|
+
result = RemoveParameterFromNodeResultFailure()
|
|
943
|
+
return result
|
|
927
944
|
# Does the Element actually exist on the Node?
|
|
928
945
|
element = node.get_element_by_name_and_type(request.parameter_name)
|
|
929
946
|
if element is None:
|
|
@@ -1246,7 +1263,7 @@ class NodeManager:
|
|
|
1246
1263
|
|
|
1247
1264
|
return None
|
|
1248
1265
|
|
|
1249
|
-
def on_alter_parameter_details_request(self, request: AlterParameterDetailsRequest) -> ResultPayload: # noqa: C901
|
|
1266
|
+
def on_alter_parameter_details_request(self, request: AlterParameterDetailsRequest) -> ResultPayload: # noqa: C901, PLR0911
|
|
1250
1267
|
node_name = request.node_name
|
|
1251
1268
|
node = None
|
|
1252
1269
|
|
|
@@ -1269,6 +1286,12 @@ class NodeManager:
|
|
|
1269
1286
|
|
|
1270
1287
|
return AlterParameterDetailsResultFailure()
|
|
1271
1288
|
|
|
1289
|
+
# Is the node locked?
|
|
1290
|
+
if node.lock:
|
|
1291
|
+
details = f"Attempted to alter details for Parameter '{request.parameter_name}' from Node '{node_name}'. Failed because the Node was locked."
|
|
1292
|
+
logger.error(details)
|
|
1293
|
+
return AlterParameterDetailsResultFailure()
|
|
1294
|
+
|
|
1272
1295
|
# Does the Element actually exist on the Node?
|
|
1273
1296
|
element = node.get_element_by_name_and_type(request.parameter_name)
|
|
1274
1297
|
if element is None:
|
|
@@ -1397,6 +1420,12 @@ class NodeManager:
|
|
|
1397
1420
|
logger.error(details)
|
|
1398
1421
|
return SetParameterValueResultFailure()
|
|
1399
1422
|
|
|
1423
|
+
# Is the node locked?
|
|
1424
|
+
if node.lock:
|
|
1425
|
+
details = f"Attempted to set parameter '{param_name}' value on node '{node_name}'. Failed because the Node was locked."
|
|
1426
|
+
logger.error(details)
|
|
1427
|
+
return SetParameterValueResultFailure()
|
|
1428
|
+
|
|
1400
1429
|
# Does the Parameter actually exist on the Node?
|
|
1401
1430
|
parameter = node.get_parameter_by_name(param_name)
|
|
1402
1431
|
if parameter is None:
|
|
@@ -1406,8 +1435,8 @@ class NodeManager:
|
|
|
1406
1435
|
result = SetParameterValueResultFailure()
|
|
1407
1436
|
return result
|
|
1408
1437
|
|
|
1409
|
-
# Validate that parameters can be set at all
|
|
1410
|
-
if not parameter.settable:
|
|
1438
|
+
# Validate that parameters can be set at all (note: we want the value to be set during initial setup, but not after)
|
|
1439
|
+
if not parameter.settable and not request.initial_setup:
|
|
1411
1440
|
details = f"Attempted to set parameter value for '{node_name}.{request.parameter_name}'. Failed because that Parameter was flagged as not settable."
|
|
1412
1441
|
logger.error(details)
|
|
1413
1442
|
result = SetParameterValueResultFailure()
|
|
@@ -1596,6 +1625,7 @@ class NodeManager:
|
|
|
1596
1625
|
result = GetAllNodeInfoResultSuccess(
|
|
1597
1626
|
metadata=get_metadata_success.metadata,
|
|
1598
1627
|
node_resolution_state=get_resolution_state_success.state,
|
|
1628
|
+
locked=node.lock,
|
|
1599
1629
|
connections=list_connections_success,
|
|
1600
1630
|
element_id_to_value=element_id_to_value,
|
|
1601
1631
|
root_node_element=element_details,
|
|
@@ -1837,7 +1867,7 @@ class NodeManager:
|
|
|
1837
1867
|
validation_succeeded=(len(all_exceptions) == 0), exceptions=all_exceptions
|
|
1838
1868
|
)
|
|
1839
1869
|
|
|
1840
|
-
def on_serialize_node_to_commands(self, request: SerializeNodeToCommandsRequest) -> ResultPayload: # noqa: C901, PLR0912
|
|
1870
|
+
def on_serialize_node_to_commands(self, request: SerializeNodeToCommandsRequest) -> ResultPayload: # noqa: C901, PLR0912, PLR0915
|
|
1841
1871
|
node_name = request.node_name
|
|
1842
1872
|
node = None
|
|
1843
1873
|
|
|
@@ -1923,12 +1953,19 @@ class NodeManager:
|
|
|
1923
1953
|
)
|
|
1924
1954
|
if set_param_value_requests is not None:
|
|
1925
1955
|
set_value_commands.extend(set_param_value_requests)
|
|
1926
|
-
|
|
1956
|
+
else:
|
|
1957
|
+
create_node_request.resolution = NodeResolutionState.UNRESOLVED.value
|
|
1958
|
+
# now check if locked
|
|
1959
|
+
if node.lock:
|
|
1960
|
+
lock_command = SetLockNodeStateRequest(node_name=None, lock=True)
|
|
1961
|
+
else:
|
|
1962
|
+
lock_command = None
|
|
1927
1963
|
# Hooray
|
|
1928
1964
|
serialized_node_commands = SerializedNodeCommands(
|
|
1929
1965
|
create_node_command=create_node_request,
|
|
1930
1966
|
element_modification_commands=element_modification_commands,
|
|
1931
1967
|
node_library_details=library_details,
|
|
1968
|
+
lock_node_command=lock_command,
|
|
1932
1969
|
)
|
|
1933
1970
|
details = f"Successfully serialized node '{node_name}' into commands."
|
|
1934
1971
|
logger.debug(details)
|
|
@@ -2073,7 +2110,6 @@ class NodeManager:
|
|
|
2073
2110
|
details = f"Attempted to deserialize a serialized set of Node Creation commands. Failed to execute an element command for node '{node_name}'."
|
|
2074
2111
|
logger.error(details)
|
|
2075
2112
|
return DeserializeNodeFromCommandsResultFailure()
|
|
2076
|
-
|
|
2077
2113
|
details = f"Successfully deserialized a serialized set of Node Creation commands for node '{node_name}'."
|
|
2078
2114
|
logger.debug(details)
|
|
2079
2115
|
return DeserializeNodeFromCommandsResultSuccess(node_name=node_name)
|
|
@@ -2091,6 +2127,8 @@ class NodeManager:
|
|
|
2091
2127
|
connections_to_serialize = []
|
|
2092
2128
|
# This is also node_uuid to the parameter serialization command.
|
|
2093
2129
|
parameter_commands = {}
|
|
2130
|
+
# This is node_uuid to lock commands.
|
|
2131
|
+
lock_commands = {}
|
|
2094
2132
|
# I need to store node names and parameter names to UUID
|
|
2095
2133
|
unique_uuid_to_values = {}
|
|
2096
2134
|
# And track how values map into that map.
|
|
@@ -2111,6 +2149,7 @@ class NodeManager:
|
|
|
2111
2149
|
node_commands[node_name] = result.serialized_node_commands
|
|
2112
2150
|
node_name_to_uuid[node_name] = result.serialized_node_commands.node_uuid
|
|
2113
2151
|
parameter_commands[result.serialized_node_commands.node_uuid] = result.set_parameter_value_commands
|
|
2152
|
+
lock_commands[result.serialized_node_commands.node_uuid] = result.serialized_node_commands.lock_node_command
|
|
2114
2153
|
try:
|
|
2115
2154
|
flow_name = self.get_node_parent_flow_by_name(node_name)
|
|
2116
2155
|
GriptapeNodes.FlowManager().get_flow_by_name(flow_name)
|
|
@@ -2146,6 +2185,7 @@ class NodeManager:
|
|
|
2146
2185
|
serialized_node_commands=list(node_commands.values()),
|
|
2147
2186
|
serialized_connection_commands=serialized_connections,
|
|
2148
2187
|
set_parameter_value_commands=parameter_commands,
|
|
2188
|
+
set_lock_commands_per_node=lock_commands,
|
|
2149
2189
|
)
|
|
2150
2190
|
# Set everything in the clipboard!
|
|
2151
2191
|
GriptapeNodes.ContextManager()._clipboard.node_commands = final_result
|
|
@@ -2209,6 +2249,12 @@ class NodeManager:
|
|
|
2209
2249
|
if not set_parameter_result.succeeded():
|
|
2210
2250
|
details = f"Failed to set parameter value for {param_request.parameter_name} on node {param_request.node_name}"
|
|
2211
2251
|
logger.warning(details)
|
|
2252
|
+
lock_command = commands.set_lock_commands_per_node[node_command.node_uuid]
|
|
2253
|
+
if lock_command is not None:
|
|
2254
|
+
lock_node_result = GriptapeNodes.handle_request(lock_command)
|
|
2255
|
+
if not lock_node_result.succeeded():
|
|
2256
|
+
details = f"Failed to lock node {lock_command.node_name}"
|
|
2257
|
+
logger.warning(details)
|
|
2212
2258
|
# create Connections
|
|
2213
2259
|
for connection_command in connections:
|
|
2214
2260
|
connection_request = CreateConnectionRequest(
|
|
@@ -2389,7 +2435,7 @@ class NodeManager:
|
|
|
2389
2435
|
commands.append(output_command)
|
|
2390
2436
|
return commands if commands else None
|
|
2391
2437
|
|
|
2392
|
-
def on_rename_parameter_request(self, request: RenameParameterRequest) -> ResultPayload: # noqa: C901, PLR0912
|
|
2438
|
+
def on_rename_parameter_request(self, request: RenameParameterRequest) -> ResultPayload: # noqa: C901, PLR0911, PLR0912
|
|
2393
2439
|
"""Handle renaming a parameter on a node.
|
|
2394
2440
|
|
|
2395
2441
|
Args:
|
|
@@ -2415,6 +2461,12 @@ class NodeManager:
|
|
|
2415
2461
|
logger.error(details)
|
|
2416
2462
|
return RenameParameterResultFailure()
|
|
2417
2463
|
|
|
2464
|
+
# Is the node locked?
|
|
2465
|
+
if node.lock:
|
|
2466
|
+
details = f"Attempted to rename Parameter '{request.parameter_name}' on Node '{node_name}'. Failed because the Node is locked."
|
|
2467
|
+
logger.error(details)
|
|
2468
|
+
return RenameParameterResultFailure()
|
|
2469
|
+
|
|
2418
2470
|
# Get the parameter
|
|
2419
2471
|
parameter = node.get_parameter_by_name(request.parameter_name)
|
|
2420
2472
|
if parameter is None:
|
|
@@ -2469,3 +2521,22 @@ class NodeManager:
|
|
|
2469
2521
|
return RenameParameterResultSuccess(
|
|
2470
2522
|
old_parameter_name=old_name, new_parameter_name=request.new_parameter_name, node_name=node_name
|
|
2471
2523
|
)
|
|
2524
|
+
|
|
2525
|
+
def on_toggle_lock_node_request(self, request: SetLockNodeStateRequest) -> ResultPayload:
|
|
2526
|
+
node_name = request.node_name
|
|
2527
|
+
if node_name is None:
|
|
2528
|
+
if not GriptapeNodes.ContextManager().has_current_node():
|
|
2529
|
+
details = "Attempted to lock node in the Current Context. Failed because the Current Context was empty."
|
|
2530
|
+
logger.error(details)
|
|
2531
|
+
return SetLockNodeStateResultFailure()
|
|
2532
|
+
node = GriptapeNodes.ContextManager().get_current_node()
|
|
2533
|
+
node_name = node.name
|
|
2534
|
+
else:
|
|
2535
|
+
try:
|
|
2536
|
+
node = self.get_node_by_name(node_name)
|
|
2537
|
+
except ValueError as err:
|
|
2538
|
+
details = f"Attempted to lock node '{request.node_name}'. Failed because the Node could not be found. Error: {err}"
|
|
2539
|
+
logger.error(details)
|
|
2540
|
+
return SetLockNodeStateResultFailure()
|
|
2541
|
+
node.lock = request.lock
|
|
2542
|
+
return SetLockNodeStateResultSuccess(node_name=node_name, locked=node.lock)
|
|
File without changes
|
|
@@ -45,6 +45,7 @@ if TYPE_CHECKING:
|
|
|
45
45
|
GetNodeMetadataRequest,
|
|
46
46
|
GetNodeResolutionStateRequest,
|
|
47
47
|
ListParametersOnNodeRequest,
|
|
48
|
+
SetLockNodeStateRequest,
|
|
48
49
|
SetNodeMetadataRequest,
|
|
49
50
|
)
|
|
50
51
|
from griptape_nodes.retained_mode.events.parameter_events import (
|
|
@@ -417,6 +418,12 @@ class PayloadConverter:
|
|
|
417
418
|
"""Handle RenameParameterRequest payloads."""
|
|
418
419
|
return f"""cmd.rename_param(node_name="{payload.node_name}",parameter_name="{payload.parameter_name}",new_parameter_name="{payload.new_parameter_name}")"""
|
|
419
420
|
|
|
421
|
+
@staticmethod
|
|
422
|
+
def _handle_SetLockNodeStateRequest(payload: SetLockNodeStateRequest) -> str:
|
|
423
|
+
"""Handle SetLockNodeStateRequest payloads."""
|
|
424
|
+
node_name_param = f'node_name="{payload.node_name}"' if payload.node_name is not None else "node_name=None"
|
|
425
|
+
return f"""cmd.set_lock_node_state({node_name_param}, lock={payload.lock})"""
|
|
426
|
+
|
|
420
427
|
# GENERIC HANDLERS FOR PAYLOADS WITHOUT SPECIFIC HANDLERS
|
|
421
428
|
|
|
422
429
|
|
|
@@ -13,6 +13,9 @@ from rich.console import Console
|
|
|
13
13
|
|
|
14
14
|
from griptape_nodes.retained_mode.events.base_events import ResultPayload
|
|
15
15
|
from griptape_nodes.retained_mode.events.os_events import (
|
|
16
|
+
CreateFileRequest,
|
|
17
|
+
CreateFileResultFailure,
|
|
18
|
+
CreateFileResultSuccess,
|
|
16
19
|
FileSystemEntry,
|
|
17
20
|
ListDirectoryRequest,
|
|
18
21
|
ListDirectoryResultFailure,
|
|
@@ -23,6 +26,9 @@ from griptape_nodes.retained_mode.events.os_events import (
|
|
|
23
26
|
ReadFileRequest,
|
|
24
27
|
ReadFileResultFailure,
|
|
25
28
|
ReadFileResultSuccess,
|
|
29
|
+
RenameFileRequest,
|
|
30
|
+
RenameFileResultFailure,
|
|
31
|
+
RenameFileResultSuccess,
|
|
26
32
|
)
|
|
27
33
|
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes, logger
|
|
28
34
|
from griptape_nodes.retained_mode.managers.event_manager import EventManager
|
|
@@ -59,6 +65,14 @@ class OSManager:
|
|
|
59
65
|
request_type=ReadFileRequest, callback=self.on_read_file_request
|
|
60
66
|
)
|
|
61
67
|
|
|
68
|
+
event_manager.assign_manager_to_request_type(
|
|
69
|
+
request_type=CreateFileRequest, callback=self.on_create_file_request
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
event_manager.assign_manager_to_request_type(
|
|
73
|
+
request_type=RenameFileRequest, callback=self.on_rename_file_request
|
|
74
|
+
)
|
|
75
|
+
|
|
62
76
|
def _get_workspace_path(self) -> Path:
|
|
63
77
|
"""Get the workspace path from config."""
|
|
64
78
|
return GriptapeNodes.ConfigManager().workspace_path
|
|
@@ -235,7 +249,7 @@ class OSManager:
|
|
|
235
249
|
logger.error(msg)
|
|
236
250
|
return OpenAssociatedFileResultFailure()
|
|
237
251
|
|
|
238
|
-
# Sanitize and validate the file
|
|
252
|
+
# Sanitize and validate the path (file or directory)
|
|
239
253
|
try:
|
|
240
254
|
# Resolve the path (no workspace fallback for open requests)
|
|
241
255
|
path = self._resolve_file_path(file_path_str, workspace_only=False)
|
|
@@ -244,12 +258,12 @@ class OSManager:
|
|
|
244
258
|
logger.info(details)
|
|
245
259
|
return OpenAssociatedFileResultFailure()
|
|
246
260
|
|
|
247
|
-
if not path.exists()
|
|
248
|
-
details = f"
|
|
261
|
+
if not path.exists():
|
|
262
|
+
details = f"Path does not exist: '{path}'"
|
|
249
263
|
logger.info(details)
|
|
250
264
|
return OpenAssociatedFileResultFailure()
|
|
251
265
|
|
|
252
|
-
logger.info("Attempting to open: %s on platform: %s", path, sys.platform)
|
|
266
|
+
logger.info("Attempting to open path: %s on platform: %s", path, sys.platform)
|
|
253
267
|
|
|
254
268
|
try:
|
|
255
269
|
platform_name = sys.platform
|
|
@@ -257,7 +271,7 @@ class OSManager:
|
|
|
257
271
|
# Linter complains but this is the recommended way on Windows
|
|
258
272
|
# We can ignore this warning as we've validated the path
|
|
259
273
|
os.startfile(str(path)) # noqa: S606 # pyright: ignore[reportAttributeAccessIssue]
|
|
260
|
-
logger.info("
|
|
274
|
+
logger.info("Opened path on Windows: %s", path)
|
|
261
275
|
elif self.is_mac():
|
|
262
276
|
# On macOS, open should be in a standard location
|
|
263
277
|
subprocess.run( # noqa: S603
|
|
@@ -266,7 +280,7 @@ class OSManager:
|
|
|
266
280
|
capture_output=True,
|
|
267
281
|
text=True,
|
|
268
282
|
)
|
|
269
|
-
logger.info("
|
|
283
|
+
logger.info("Opened path on macOS: %s", path)
|
|
270
284
|
elif self.is_linux():
|
|
271
285
|
# Use full path to xdg-open to satisfy linter
|
|
272
286
|
# Common locations for xdg-open:
|
|
@@ -283,7 +297,7 @@ class OSManager:
|
|
|
283
297
|
capture_output=True,
|
|
284
298
|
text=True,
|
|
285
299
|
)
|
|
286
|
-
logger.info("
|
|
300
|
+
logger.info("Opened path on Linux: %s", path)
|
|
287
301
|
else:
|
|
288
302
|
details = f"Unsupported platform: '{platform_name}'"
|
|
289
303
|
logger.info(details)
|
|
@@ -299,9 +313,24 @@ class OSManager:
|
|
|
299
313
|
)
|
|
300
314
|
return OpenAssociatedFileResultFailure()
|
|
301
315
|
except Exception as e:
|
|
302
|
-
logger.error("Exception occurred when trying to open
|
|
316
|
+
logger.error("Exception occurred when trying to open path: %s", type(e).__name__)
|
|
303
317
|
return OpenAssociatedFileResultFailure()
|
|
304
318
|
|
|
319
|
+
def _detect_mime_type(self, file_path: Path) -> str | None:
|
|
320
|
+
"""Detect MIME type for a file. Returns None for directories or if detection fails."""
|
|
321
|
+
if file_path.is_dir():
|
|
322
|
+
return None
|
|
323
|
+
|
|
324
|
+
try:
|
|
325
|
+
mime_type, _ = mimetypes.guess_type(str(file_path), strict=True)
|
|
326
|
+
if mime_type is None:
|
|
327
|
+
mime_type = "text/plain"
|
|
328
|
+
return mime_type # noqa: TRY300
|
|
329
|
+
except Exception as e:
|
|
330
|
+
msg = f"MIME type detection failed for {file_path}: {e}"
|
|
331
|
+
logger.warning(msg)
|
|
332
|
+
return "text/plain"
|
|
333
|
+
|
|
305
334
|
def on_list_directory_request(self, request: ListDirectoryRequest) -> ResultPayload: # noqa: C901, PLR0911
|
|
306
335
|
"""Handle a request to list directory contents."""
|
|
307
336
|
try:
|
|
@@ -345,6 +374,7 @@ class OSManager:
|
|
|
345
374
|
stat = entry.stat()
|
|
346
375
|
# Get path relative to workspace if within workspace
|
|
347
376
|
is_entry_in_workspace, entry_path = self._validate_workspace_path(entry)
|
|
377
|
+
mime_type = self._detect_mime_type(entry)
|
|
348
378
|
entries.append(
|
|
349
379
|
FileSystemEntry(
|
|
350
380
|
name=entry.name,
|
|
@@ -352,6 +382,7 @@ class OSManager:
|
|
|
352
382
|
is_dir=entry.is_dir(),
|
|
353
383
|
size=stat.st_size,
|
|
354
384
|
modified_time=stat.st_mtime,
|
|
385
|
+
mime_type=mime_type,
|
|
355
386
|
)
|
|
356
387
|
)
|
|
357
388
|
except (OSError, PermissionError) as e:
|
|
@@ -703,3 +734,97 @@ class OSManager:
|
|
|
703
734
|
logger.error("Attempted to clean up old files from %s, but no files could be deleted.")
|
|
704
735
|
|
|
705
736
|
return removed_count > 0
|
|
737
|
+
|
|
738
|
+
def on_create_file_request(self, request: CreateFileRequest) -> ResultPayload:
|
|
739
|
+
"""Handle a request to create a file or directory."""
|
|
740
|
+
try:
|
|
741
|
+
# Get the full path using the new method
|
|
742
|
+
full_path_str = request.get_full_path()
|
|
743
|
+
|
|
744
|
+
# Determine if path is absolute (not constrained to workspace)
|
|
745
|
+
is_absolute = Path(full_path_str).is_absolute()
|
|
746
|
+
|
|
747
|
+
# If workspace_only is True and path is absolute, it's outside workspace
|
|
748
|
+
if request.workspace_only and is_absolute:
|
|
749
|
+
msg = f"Absolute path is outside workspace: {full_path_str}"
|
|
750
|
+
logger.error(msg)
|
|
751
|
+
return CreateFileResultFailure()
|
|
752
|
+
|
|
753
|
+
# Resolve path - if absolute, use as-is; if relative, align to workspace
|
|
754
|
+
if is_absolute:
|
|
755
|
+
file_path = Path(full_path_str).resolve()
|
|
756
|
+
else:
|
|
757
|
+
file_path = (self._get_workspace_path() / full_path_str).resolve()
|
|
758
|
+
|
|
759
|
+
# Check if it already exists - warn but treat as success
|
|
760
|
+
if file_path.exists():
|
|
761
|
+
msg = f"Path already exists: {file_path}"
|
|
762
|
+
logger.warning(msg)
|
|
763
|
+
return CreateFileResultSuccess(created_path=str(file_path))
|
|
764
|
+
|
|
765
|
+
# Create parent directories if needed
|
|
766
|
+
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
767
|
+
|
|
768
|
+
if request.is_directory:
|
|
769
|
+
file_path.mkdir()
|
|
770
|
+
logger.info("Created directory: %s", file_path)
|
|
771
|
+
# Create file with optional content
|
|
772
|
+
elif request.content is not None:
|
|
773
|
+
with file_path.open("w", encoding=request.encoding) as f:
|
|
774
|
+
f.write(request.content)
|
|
775
|
+
logger.info("Created file with content: %s", file_path)
|
|
776
|
+
else:
|
|
777
|
+
file_path.touch()
|
|
778
|
+
logger.info("Created empty file: %s", file_path)
|
|
779
|
+
|
|
780
|
+
return CreateFileResultSuccess(created_path=str(file_path))
|
|
781
|
+
|
|
782
|
+
except Exception as e:
|
|
783
|
+
path_info = request.get_full_path() if hasattr(request, "get_full_path") else str(request.path)
|
|
784
|
+
msg = f"Failed to create {'directory' if request.is_directory else 'file'} at {path_info}: {e}"
|
|
785
|
+
logger.error(msg)
|
|
786
|
+
return CreateFileResultFailure()
|
|
787
|
+
|
|
788
|
+
def on_rename_file_request(self, request: RenameFileRequest) -> ResultPayload:
|
|
789
|
+
"""Handle a request to rename a file or directory."""
|
|
790
|
+
try:
|
|
791
|
+
# Resolve and validate old path
|
|
792
|
+
old_path = self._resolve_file_path(request.old_path, workspace_only=request.workspace_only is True)
|
|
793
|
+
|
|
794
|
+
# Resolve and validate new path
|
|
795
|
+
new_path = self._resolve_file_path(request.new_path, workspace_only=request.workspace_only is True)
|
|
796
|
+
|
|
797
|
+
# Check if old path exists
|
|
798
|
+
if not old_path.exists():
|
|
799
|
+
msg = f"Source path does not exist: {old_path}"
|
|
800
|
+
logger.error(msg)
|
|
801
|
+
return RenameFileResultFailure()
|
|
802
|
+
|
|
803
|
+
# Check if new path already exists
|
|
804
|
+
if new_path.exists():
|
|
805
|
+
msg = f"Destination path already exists: {new_path}"
|
|
806
|
+
logger.error(msg)
|
|
807
|
+
return RenameFileResultFailure()
|
|
808
|
+
|
|
809
|
+
# Check workspace constraints for both paths
|
|
810
|
+
is_old_in_workspace, _ = self._validate_workspace_path(old_path)
|
|
811
|
+
is_new_in_workspace, _ = self._validate_workspace_path(new_path)
|
|
812
|
+
|
|
813
|
+
if request.workspace_only and (not is_old_in_workspace or not is_new_in_workspace):
|
|
814
|
+
msg = f"One or both paths are outside workspace: {old_path} -> {new_path}"
|
|
815
|
+
logger.error(msg)
|
|
816
|
+
return RenameFileResultFailure()
|
|
817
|
+
|
|
818
|
+
# Create parent directories for new path if needed
|
|
819
|
+
new_path.parent.mkdir(parents=True, exist_ok=True)
|
|
820
|
+
|
|
821
|
+
# Perform the rename operation
|
|
822
|
+
old_path.rename(new_path)
|
|
823
|
+
logger.info("Renamed: %s -> %s", old_path, new_path)
|
|
824
|
+
|
|
825
|
+
return RenameFileResultSuccess(old_path=str(old_path), new_path=str(new_path))
|
|
826
|
+
|
|
827
|
+
except Exception as e:
|
|
828
|
+
msg = f"Failed to rename {request.old_path} to {request.new_path}: {e}"
|
|
829
|
+
logger.error(msg)
|
|
830
|
+
return RenameFileResultFailure()
|
|
File without changes
|
|
File without changes
|
|
@@ -33,6 +33,7 @@ class AppEvents(BaseModel):
|
|
|
33
33
|
"SingleExecutionStepRequest",
|
|
34
34
|
"SingleNodeStepRequest",
|
|
35
35
|
"ContinueExecutionStepRequest",
|
|
36
|
+
"SetLockNodeStateRequest",
|
|
36
37
|
]
|
|
37
38
|
)
|
|
38
39
|
|
|
@@ -95,3 +96,7 @@ class Settings(BaseModel):
|
|
|
95
96
|
minimum_disk_space_gb_workflows: float = Field(
|
|
96
97
|
default=1.0, description="Minimum disk space in GB required for saving workflows"
|
|
97
98
|
)
|
|
99
|
+
synced_workflows_directory: str = Field(
|
|
100
|
+
default="synced_workflows",
|
|
101
|
+
description="Path to the synced workflows directory, relative to the workspace directory.",
|
|
102
|
+
)
|
|
File without changes
|