griptape-nodes 0.51.2__py3-none-any.whl → 0.52.1__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 +5 -4
- griptape_nodes/app/api.py +22 -30
- griptape_nodes/app/app.py +374 -289
- griptape_nodes/app/watch.py +17 -2
- griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +66 -103
- griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +16 -4
- griptape_nodes/exe_types/core_types.py +16 -4
- griptape_nodes/exe_types/node_types.py +74 -16
- griptape_nodes/machines/control_flow.py +21 -26
- griptape_nodes/machines/fsm.py +16 -16
- griptape_nodes/machines/node_resolution.py +30 -119
- griptape_nodes/mcp_server/server.py +14 -10
- griptape_nodes/mcp_server/ws_request_manager.py +2 -2
- griptape_nodes/node_library/workflow_registry.py +5 -0
- griptape_nodes/retained_mode/events/base_events.py +12 -7
- griptape_nodes/retained_mode/events/execution_events.py +0 -6
- griptape_nodes/retained_mode/events/node_events.py +38 -0
- griptape_nodes/retained_mode/events/parameter_events.py +11 -0
- griptape_nodes/retained_mode/events/variable_events.py +361 -0
- griptape_nodes/retained_mode/events/workflow_events.py +35 -0
- griptape_nodes/retained_mode/griptape_nodes.py +61 -26
- griptape_nodes/retained_mode/managers/agent_manager.py +8 -9
- griptape_nodes/retained_mode/managers/event_manager.py +215 -74
- griptape_nodes/retained_mode/managers/flow_manager.py +39 -33
- griptape_nodes/retained_mode/managers/library_lifecycle/library_directory.py +14 -14
- griptape_nodes/retained_mode/managers/library_lifecycle/library_fsm.py +20 -20
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/base.py +1 -1
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/github.py +1 -1
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/local_file.py +4 -3
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/package.py +1 -1
- griptape_nodes/retained_mode/managers/library_lifecycle/library_provenance/sandbox.py +1 -1
- griptape_nodes/retained_mode/managers/library_manager.py +20 -19
- griptape_nodes/retained_mode/managers/node_manager.py +83 -8
- griptape_nodes/retained_mode/managers/object_manager.py +4 -0
- griptape_nodes/retained_mode/managers/settings.py +1 -0
- griptape_nodes/retained_mode/managers/sync_manager.py +3 -9
- griptape_nodes/retained_mode/managers/variable_manager.py +529 -0
- griptape_nodes/retained_mode/managers/workflow_manager.py +156 -50
- griptape_nodes/retained_mode/variable_types.py +18 -0
- griptape_nodes/utils/__init__.py +4 -0
- griptape_nodes/utils/async_utils.py +89 -0
- {griptape_nodes-0.51.2.dist-info → griptape_nodes-0.52.1.dist-info}/METADATA +2 -3
- {griptape_nodes-0.51.2.dist-info → griptape_nodes-0.52.1.dist-info}/RECORD +45 -42
- {griptape_nodes-0.51.2.dist-info → griptape_nodes-0.52.1.dist-info}/WHEEL +1 -1
- griptape_nodes/bootstrap/workflow_executors/subprocess_workflow_executor.py +0 -90
- {griptape_nodes-0.51.2.dist-info → griptape_nodes-0.52.1.dist-info}/entry_points.txt +0 -0
|
@@ -4,8 +4,6 @@ import logging
|
|
|
4
4
|
from queue import Queue
|
|
5
5
|
from typing import TYPE_CHECKING, cast
|
|
6
6
|
|
|
7
|
-
from griptape.events import EventBus
|
|
8
|
-
|
|
9
7
|
from griptape_nodes.exe_types.connections import Connections
|
|
10
8
|
from griptape_nodes.exe_types.core_types import (
|
|
11
9
|
Parameter,
|
|
@@ -869,6 +867,8 @@ class FlowManager:
|
|
|
869
867
|
node_name=target_node.name,
|
|
870
868
|
value=value,
|
|
871
869
|
data_type=source_param.type,
|
|
870
|
+
incoming_connection_source_node_name=source_node.name,
|
|
871
|
+
incoming_connection_source_parameter_name=source_param.name,
|
|
872
872
|
)
|
|
873
873
|
)
|
|
874
874
|
|
|
@@ -1031,7 +1031,7 @@ class FlowManager:
|
|
|
1031
1031
|
result = DeleteConnectionResultSuccess()
|
|
1032
1032
|
return result
|
|
1033
1033
|
|
|
1034
|
-
def on_start_flow_request(self, request: StartFlowRequest) -> ResultPayload: # noqa: C901, PLR0911, PLR0912
|
|
1034
|
+
async def on_start_flow_request(self, request: StartFlowRequest) -> ResultPayload: # noqa: C901, PLR0911, PLR0912
|
|
1035
1035
|
# which flow
|
|
1036
1036
|
flow_name = request.flow_name
|
|
1037
1037
|
debug_mode = request.debug_mode
|
|
@@ -1075,7 +1075,7 @@ class FlowManager:
|
|
|
1075
1075
|
self.get_start_node_queue() # initialize the start flow queue!
|
|
1076
1076
|
start_node = None
|
|
1077
1077
|
# Run Validation before starting a flow
|
|
1078
|
-
result = self.on_validate_flow_dependencies_request(
|
|
1078
|
+
result = await self.on_validate_flow_dependencies_request(
|
|
1079
1079
|
ValidateFlowDependenciesRequest(flow_name=flow_name, flow_node_name=start_node.name if start_node else None)
|
|
1080
1080
|
)
|
|
1081
1081
|
try:
|
|
@@ -1098,7 +1098,7 @@ class FlowManager:
|
|
|
1098
1098
|
return StartFlowResultFailure(validation_exceptions=[e], result_details=details)
|
|
1099
1099
|
# By now, it has been validated with no exceptions.
|
|
1100
1100
|
try:
|
|
1101
|
-
self.start_flow(flow, start_node, debug_mode)
|
|
1101
|
+
await self.start_flow(flow, start_node, debug_mode)
|
|
1102
1102
|
except Exception as e:
|
|
1103
1103
|
details = f"Failed to kick off flow with name {flow_name}. Exception occurred: {e} "
|
|
1104
1104
|
logger.error(details)
|
|
@@ -1157,7 +1157,7 @@ class FlowManager:
|
|
|
1157
1157
|
|
|
1158
1158
|
return CancelFlowResultSuccess()
|
|
1159
1159
|
|
|
1160
|
-
def on_single_node_step_request(self, request: SingleNodeStepRequest) -> ResultPayload:
|
|
1160
|
+
async def on_single_node_step_request(self, request: SingleNodeStepRequest) -> ResultPayload:
|
|
1161
1161
|
flow_name = request.flow_name
|
|
1162
1162
|
if not flow_name:
|
|
1163
1163
|
details = "Could not advance to the next step of a running workflow. No flow name was provided."
|
|
@@ -1173,7 +1173,7 @@ class FlowManager:
|
|
|
1173
1173
|
return SingleNodeStepResultFailure(validation_exceptions=[err], result_details=details)
|
|
1174
1174
|
try:
|
|
1175
1175
|
flow = self.get_flow_by_name(flow_name)
|
|
1176
|
-
self.single_node_step(flow)
|
|
1176
|
+
await self.single_node_step(flow)
|
|
1177
1177
|
except Exception as e:
|
|
1178
1178
|
details = f"Could not advance to the next step of a running workflow. Exception: {e}"
|
|
1179
1179
|
logger.error(details)
|
|
@@ -1185,7 +1185,7 @@ class FlowManager:
|
|
|
1185
1185
|
|
|
1186
1186
|
return SingleNodeStepResultSuccess()
|
|
1187
1187
|
|
|
1188
|
-
def on_single_execution_step_request(self, request: SingleExecutionStepRequest) -> ResultPayload:
|
|
1188
|
+
async def on_single_execution_step_request(self, request: SingleExecutionStepRequest) -> ResultPayload:
|
|
1189
1189
|
flow_name = request.flow_name
|
|
1190
1190
|
if not flow_name:
|
|
1191
1191
|
details = "Could not advance to the next step of a running workflow. No flow name was provided."
|
|
@@ -1201,7 +1201,7 @@ class FlowManager:
|
|
|
1201
1201
|
return SingleExecutionStepResultFailure(result_details=details)
|
|
1202
1202
|
change_debug_mode = request.request_id is not None
|
|
1203
1203
|
try:
|
|
1204
|
-
self.single_execution_step(flow, change_debug_mode)
|
|
1204
|
+
await self.single_execution_step(flow, change_debug_mode)
|
|
1205
1205
|
except Exception as e:
|
|
1206
1206
|
# We REALLY don't want to fail here, else we'll take the whole engine down
|
|
1207
1207
|
try:
|
|
@@ -1219,7 +1219,7 @@ class FlowManager:
|
|
|
1219
1219
|
|
|
1220
1220
|
return SingleExecutionStepResultSuccess()
|
|
1221
1221
|
|
|
1222
|
-
def on_continue_execution_step_request(self, request: ContinueExecutionStepRequest) -> ResultPayload:
|
|
1222
|
+
async def on_continue_execution_step_request(self, request: ContinueExecutionStepRequest) -> ResultPayload:
|
|
1223
1223
|
flow_name = request.flow_name
|
|
1224
1224
|
if not flow_name:
|
|
1225
1225
|
details = "Failed to continue execution step because no flow name was provided"
|
|
@@ -1234,7 +1234,7 @@ class FlowManager:
|
|
|
1234
1234
|
|
|
1235
1235
|
return ContinueExecutionStepResultFailure(result_details=details)
|
|
1236
1236
|
try:
|
|
1237
|
-
self.continue_executing(flow)
|
|
1237
|
+
await self.continue_executing(flow)
|
|
1238
1238
|
except Exception as e:
|
|
1239
1239
|
details = f"Failed to continue execution step. An exception occurred: {e}."
|
|
1240
1240
|
logger.error(details)
|
|
@@ -1265,7 +1265,7 @@ class FlowManager:
|
|
|
1265
1265
|
logger.debug(details)
|
|
1266
1266
|
return UnresolveFlowResultSuccess()
|
|
1267
1267
|
|
|
1268
|
-
def on_validate_flow_dependencies_request(self, request: ValidateFlowDependenciesRequest) -> ResultPayload:
|
|
1268
|
+
async def on_validate_flow_dependencies_request(self, request: ValidateFlowDependenciesRequest) -> ResultPayload:
|
|
1269
1269
|
flow_name = request.flow_name
|
|
1270
1270
|
# get the flow name
|
|
1271
1271
|
try:
|
|
@@ -1658,7 +1658,7 @@ class FlowManager:
|
|
|
1658
1658
|
node.emit_parameter_changes()
|
|
1659
1659
|
return FlushParameterChangesResultSuccess()
|
|
1660
1660
|
|
|
1661
|
-
def start_flow(self, flow: ControlFlow, start_node: BaseNode | None = None, debug_mode: bool = False) -> None: # noqa: FBT001, FBT002, ARG002
|
|
1661
|
+
async def start_flow(self, flow: ControlFlow, start_node: BaseNode | None = None, debug_mode: bool = False) -> None: # noqa: FBT001, FBT002, ARG002
|
|
1662
1662
|
if self.check_for_existing_running_flow():
|
|
1663
1663
|
# If flow already exists, throw an error
|
|
1664
1664
|
errormsg = "This workflow is already in progress. Please wait for the current process to finish before starting again."
|
|
@@ -1676,7 +1676,7 @@ class FlowManager:
|
|
|
1676
1676
|
self._global_control_flow_machine = ControlFlowMachine()
|
|
1677
1677
|
|
|
1678
1678
|
try:
|
|
1679
|
-
self._global_control_flow_machine.start_flow(start_node, debug_mode)
|
|
1679
|
+
await self._global_control_flow_machine.start_flow(start_node, debug_mode)
|
|
1680
1680
|
except Exception:
|
|
1681
1681
|
if self.check_for_existing_running_flow():
|
|
1682
1682
|
self.cancel_flow_run()
|
|
@@ -1707,7 +1707,7 @@ class FlowManager:
|
|
|
1707
1707
|
self._global_single_node_resolution = False
|
|
1708
1708
|
logger.debug("Cancelling flow run")
|
|
1709
1709
|
|
|
1710
|
-
|
|
1710
|
+
GriptapeNodes.EventManager().put_event(
|
|
1711
1711
|
ExecutionGriptapeNodeEvent(wrapped_event=ExecutionEvent(payload=ControlFlowCancelledEvent()))
|
|
1712
1712
|
)
|
|
1713
1713
|
|
|
@@ -1754,7 +1754,13 @@ class FlowManager:
|
|
|
1754
1754
|
return self._has_connection(source_node, source_parameter, target_node, target_parameter)
|
|
1755
1755
|
|
|
1756
1756
|
# Internal execution queue helper methods to consolidate redundant operations
|
|
1757
|
-
def _handle_flow_start_if_not_running(
|
|
1757
|
+
async def _handle_flow_start_if_not_running(
|
|
1758
|
+
self,
|
|
1759
|
+
flow: ControlFlow, # noqa: ARG002
|
|
1760
|
+
*,
|
|
1761
|
+
debug_mode: bool,
|
|
1762
|
+
error_message: str,
|
|
1763
|
+
) -> None:
|
|
1758
1764
|
"""Common logic for starting flow execution if not already running."""
|
|
1759
1765
|
if not self.check_for_existing_running_flow():
|
|
1760
1766
|
if self._global_flow_queue.empty():
|
|
@@ -1763,17 +1769,17 @@ class FlowManager:
|
|
|
1763
1769
|
self._global_flow_queue.task_done()
|
|
1764
1770
|
if self._global_control_flow_machine is None:
|
|
1765
1771
|
self._global_control_flow_machine = ControlFlowMachine()
|
|
1766
|
-
self._global_control_flow_machine.start_flow(start_node, debug_mode)
|
|
1772
|
+
await self._global_control_flow_machine.start_flow(start_node, debug_mode)
|
|
1767
1773
|
|
|
1768
|
-
def _handle_post_execution_queue_processing(self, *, debug_mode: bool) -> None:
|
|
1774
|
+
async def _handle_post_execution_queue_processing(self, *, debug_mode: bool) -> None:
|
|
1769
1775
|
"""Handle execution queue processing after execution completes."""
|
|
1770
1776
|
if not self.check_for_existing_running_flow() and not self._global_flow_queue.empty():
|
|
1771
1777
|
start_node = self._global_flow_queue.get()
|
|
1772
1778
|
self._global_flow_queue.task_done()
|
|
1773
1779
|
if self._global_control_flow_machine is not None:
|
|
1774
|
-
self._global_control_flow_machine.start_flow(start_node, debug_mode)
|
|
1780
|
+
await self._global_control_flow_machine.start_flow(start_node, debug_mode)
|
|
1775
1781
|
|
|
1776
|
-
def resolve_singular_node(self, flow: ControlFlow, node: BaseNode, debug_mode: bool = False) -> None: # noqa: FBT001, FBT002, ARG002
|
|
1782
|
+
async def resolve_singular_node(self, flow: ControlFlow, node: BaseNode, debug_mode: bool = False) -> None: # noqa: FBT001, FBT002, ARG002
|
|
1777
1783
|
# Set that we are only working on one node right now! no other stepping allowed
|
|
1778
1784
|
if self.check_for_existing_running_flow():
|
|
1779
1785
|
# If flow already exists, throw an error
|
|
@@ -1790,30 +1796,30 @@ class FlowManager:
|
|
|
1790
1796
|
resolution_machine.change_debug_mode(debug_mode)
|
|
1791
1797
|
# Resolve the node.
|
|
1792
1798
|
node.state = NodeResolutionState.UNRESOLVED
|
|
1793
|
-
resolution_machine.resolve_node(node)
|
|
1799
|
+
await resolution_machine.resolve_node(node)
|
|
1794
1800
|
# decide if we can change it back to normal flow mode!
|
|
1795
1801
|
if resolution_machine.is_complete():
|
|
1796
1802
|
self._global_single_node_resolution = False
|
|
1797
1803
|
self._global_control_flow_machine._context.current_node = None
|
|
1798
1804
|
|
|
1799
|
-
def single_execution_step(self, flow: ControlFlow, change_debug_mode: bool) -> None: # noqa: FBT001
|
|
1805
|
+
async def single_execution_step(self, flow: ControlFlow, change_debug_mode: bool) -> None: # noqa: FBT001
|
|
1800
1806
|
# do a granular step
|
|
1801
|
-
self._handle_flow_start_if_not_running(
|
|
1807
|
+
await self._handle_flow_start_if_not_running(
|
|
1802
1808
|
flow, debug_mode=True, error_message="Flow has not yet been started. Cannot step while no flow has begun."
|
|
1803
1809
|
)
|
|
1804
1810
|
if not self.check_for_existing_running_flow():
|
|
1805
1811
|
return
|
|
1806
1812
|
if self._global_control_flow_machine is not None:
|
|
1807
|
-
self._global_control_flow_machine.granular_step(change_debug_mode)
|
|
1813
|
+
await self._global_control_flow_machine.granular_step(change_debug_mode)
|
|
1808
1814
|
resolution_machine = self._global_control_flow_machine._context.resolution_machine
|
|
1809
1815
|
if self._global_single_node_resolution:
|
|
1810
1816
|
resolution_machine = self._global_control_flow_machine._context.resolution_machine
|
|
1811
1817
|
if resolution_machine.is_complete():
|
|
1812
1818
|
self._global_single_node_resolution = False
|
|
1813
1819
|
|
|
1814
|
-
def single_node_step(self, flow: ControlFlow) -> None:
|
|
1820
|
+
async def single_node_step(self, flow: ControlFlow) -> None:
|
|
1815
1821
|
# It won't call single_node_step without an existing flow running from US.
|
|
1816
|
-
self._handle_flow_start_if_not_running(
|
|
1822
|
+
await self._handle_flow_start_if_not_running(
|
|
1817
1823
|
flow, debug_mode=True, error_message="Flow has not yet been started. Cannot step while no flow has begun."
|
|
1818
1824
|
)
|
|
1819
1825
|
if not self.check_for_existing_running_flow():
|
|
@@ -1823,12 +1829,12 @@ class FlowManager:
|
|
|
1823
1829
|
msg = "Cannot step through the Control Flow in Single Node Execution"
|
|
1824
1830
|
raise RuntimeError(msg)
|
|
1825
1831
|
if self._global_control_flow_machine is not None:
|
|
1826
|
-
self._global_control_flow_machine.node_step()
|
|
1832
|
+
await self._global_control_flow_machine.node_step()
|
|
1827
1833
|
# Start the next resolution step now please.
|
|
1828
|
-
self._handle_post_execution_queue_processing(debug_mode=True)
|
|
1834
|
+
await self._handle_post_execution_queue_processing(debug_mode=True)
|
|
1829
1835
|
|
|
1830
|
-
def continue_executing(self, flow: ControlFlow) -> None:
|
|
1831
|
-
self._handle_flow_start_if_not_running(
|
|
1836
|
+
async def continue_executing(self, flow: ControlFlow) -> None:
|
|
1837
|
+
await self._handle_flow_start_if_not_running(
|
|
1832
1838
|
flow, debug_mode=False, error_message="Flow has not yet been started. Cannot step while no flow has begun."
|
|
1833
1839
|
)
|
|
1834
1840
|
if not self.check_for_existing_running_flow():
|
|
@@ -1840,11 +1846,11 @@ class FlowManager:
|
|
|
1840
1846
|
if self._global_control_flow_machine._context.resolution_machine.is_complete():
|
|
1841
1847
|
self._global_single_node_resolution = False
|
|
1842
1848
|
else:
|
|
1843
|
-
self._global_control_flow_machine._context.resolution_machine.update()
|
|
1849
|
+
await self._global_control_flow_machine._context.resolution_machine.update()
|
|
1844
1850
|
else:
|
|
1845
|
-
self._global_control_flow_machine.node_step()
|
|
1851
|
+
await self._global_control_flow_machine.node_step()
|
|
1846
1852
|
# Now it is done executing. make sure it's actually done?
|
|
1847
|
-
self._handle_post_execution_queue_processing(debug_mode=False)
|
|
1853
|
+
await self._handle_post_execution_queue_processing(debug_mode=False)
|
|
1848
1854
|
|
|
1849
1855
|
def unresolve_whole_flow(self, flow: ControlFlow) -> None:
|
|
1850
1856
|
for node in flow.nodes.values():
|
|
@@ -35,7 +35,7 @@ class LibraryDirectory:
|
|
|
35
35
|
# Own all FSM instances for library lifecycle management
|
|
36
36
|
self._provenance_to_fsm: dict[LibraryProvenance, LibraryLifecycleFSM] = {}
|
|
37
37
|
|
|
38
|
-
def discover_library(self, provenance: LibraryProvenance) -> None:
|
|
38
|
+
async def discover_library(self, provenance: LibraryProvenance) -> None:
|
|
39
39
|
"""Discover a library and its provenance.
|
|
40
40
|
|
|
41
41
|
Discovery is purely about cataloging - activation state is handled separately.
|
|
@@ -49,26 +49,26 @@ class LibraryDirectory:
|
|
|
49
49
|
self._discovered_libraries[provenance] = entry
|
|
50
50
|
|
|
51
51
|
# Create FSM and run evaluation automatically
|
|
52
|
-
self._create_fsm_and_evaluate(provenance)
|
|
52
|
+
await self._create_fsm_and_evaluate(provenance)
|
|
53
53
|
|
|
54
|
-
def add_curated_candidate(self, provenance: LibraryProvenance) -> None:
|
|
54
|
+
async def add_curated_candidate(self, provenance: LibraryProvenance) -> None:
|
|
55
55
|
"""Add a curated library candidate.
|
|
56
56
|
|
|
57
57
|
Curated libraries default to inactive and need to be activated by user.
|
|
58
58
|
"""
|
|
59
|
-
self.discover_library(provenance)
|
|
59
|
+
await self.discover_library(provenance)
|
|
60
60
|
|
|
61
61
|
# Set curated library as inactive by default
|
|
62
62
|
if provenance in self._discovered_libraries:
|
|
63
63
|
entry = self._discovered_libraries[provenance]
|
|
64
64
|
entry.active = False
|
|
65
65
|
|
|
66
|
-
def add_user_candidate(self, provenance: LibraryProvenance) -> None:
|
|
66
|
+
async def add_user_candidate(self, provenance: LibraryProvenance) -> None:
|
|
67
67
|
"""Add a user-supplied library candidate.
|
|
68
68
|
|
|
69
69
|
User libraries default to active.
|
|
70
70
|
"""
|
|
71
|
-
self.discover_library(provenance)
|
|
71
|
+
await self.discover_library(provenance)
|
|
72
72
|
|
|
73
73
|
# Set user library as active by default
|
|
74
74
|
if provenance in self._discovered_libraries:
|
|
@@ -202,7 +202,7 @@ class LibraryDirectory:
|
|
|
202
202
|
|
|
203
203
|
return blockers
|
|
204
204
|
|
|
205
|
-
def _create_fsm_and_evaluate(self, provenance: LibraryProvenance) -> None:
|
|
205
|
+
async def _create_fsm_and_evaluate(self, provenance: LibraryProvenance) -> None:
|
|
206
206
|
"""Create FSM for provenance and run through evaluation phase.
|
|
207
207
|
|
|
208
208
|
This method is called automatically when a library is discovered.
|
|
@@ -214,11 +214,11 @@ class LibraryDirectory:
|
|
|
214
214
|
self._provenance_to_fsm[provenance] = fsm
|
|
215
215
|
|
|
216
216
|
# Start the lifecycle and run through evaluation
|
|
217
|
-
fsm.start_lifecycle()
|
|
217
|
+
await fsm.start_lifecycle()
|
|
218
218
|
|
|
219
219
|
# Progress through inspection
|
|
220
220
|
if fsm.can_begin_inspection():
|
|
221
|
-
fsm.begin_inspection()
|
|
221
|
+
await fsm.begin_inspection()
|
|
222
222
|
else:
|
|
223
223
|
logger.error(
|
|
224
224
|
"Cannot inspect library '%s' - inspection step cannot proceed",
|
|
@@ -228,7 +228,7 @@ class LibraryDirectory:
|
|
|
228
228
|
|
|
229
229
|
# Progress through evaluation
|
|
230
230
|
if fsm.can_begin_evaluation():
|
|
231
|
-
fsm.begin_evaluation()
|
|
231
|
+
await fsm.begin_evaluation()
|
|
232
232
|
else:
|
|
233
233
|
logger.error(
|
|
234
234
|
"Cannot evaluate library '%s' - evaluation step cannot proceed",
|
|
@@ -247,7 +247,7 @@ class LibraryDirectory:
|
|
|
247
247
|
|
|
248
248
|
logger.debug("Completed FSM evaluation for library: %s", provenance.get_display_name())
|
|
249
249
|
|
|
250
|
-
def install_library(self, provenance: LibraryProvenance) -> bool:
|
|
250
|
+
async def install_library(self, provenance: LibraryProvenance) -> bool:
|
|
251
251
|
"""Install a library by running its FSM through the installation phase.
|
|
252
252
|
|
|
253
253
|
Returns True if installation was successful, False otherwise.
|
|
@@ -272,7 +272,7 @@ class LibraryDirectory:
|
|
|
272
272
|
|
|
273
273
|
# Proceed with installation
|
|
274
274
|
if fsm.can_begin_installation():
|
|
275
|
-
fsm.begin_installation()
|
|
275
|
+
await fsm.begin_installation()
|
|
276
276
|
logger.info("Installation completed for library: %s", provenance.get_display_name())
|
|
277
277
|
return True
|
|
278
278
|
logger.error(
|
|
@@ -281,7 +281,7 @@ class LibraryDirectory:
|
|
|
281
281
|
)
|
|
282
282
|
return False
|
|
283
283
|
|
|
284
|
-
def load_library(self, provenance: LibraryProvenance) -> bool:
|
|
284
|
+
async def load_library(self, provenance: LibraryProvenance) -> bool:
|
|
285
285
|
"""Load a library by running its FSM through the loading phase.
|
|
286
286
|
|
|
287
287
|
Returns True if loading was successful, False otherwise.
|
|
@@ -299,7 +299,7 @@ class LibraryDirectory:
|
|
|
299
299
|
return False
|
|
300
300
|
|
|
301
301
|
# Proceed with loading
|
|
302
|
-
fsm.begin_loading()
|
|
302
|
+
await fsm.begin_loading()
|
|
303
303
|
|
|
304
304
|
if not fsm.is_loaded():
|
|
305
305
|
logger.error(
|
|
@@ -87,7 +87,7 @@ class CandidateState(State):
|
|
|
87
87
|
"""Initial state where we have a library candidate ready for processing."""
|
|
88
88
|
|
|
89
89
|
@staticmethod
|
|
90
|
-
def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
90
|
+
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
91
91
|
logger.info("Library %s is now a candidate for processing", context.provenance.get_display_name())
|
|
92
92
|
return None # Wait for explicit transition to InspectingState
|
|
93
93
|
|
|
@@ -106,7 +106,7 @@ class InspectingState(State):
|
|
|
106
106
|
return {InspectedState}
|
|
107
107
|
|
|
108
108
|
@staticmethod
|
|
109
|
-
def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
109
|
+
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
110
110
|
logger.info("Inspecting library %s", context.provenance.get_display_name())
|
|
111
111
|
|
|
112
112
|
# Store inspection result directly
|
|
@@ -145,7 +145,7 @@ class InspectedState(State):
|
|
|
145
145
|
return {EvaluatingState}
|
|
146
146
|
|
|
147
147
|
@staticmethod
|
|
148
|
-
def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
148
|
+
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
149
149
|
if context.inspection_result and context.inspection_result.issues:
|
|
150
150
|
logger.warning(
|
|
151
151
|
"Library %s inspection completed with problems: %s",
|
|
@@ -167,7 +167,7 @@ class EvaluatingState(State):
|
|
|
167
167
|
return {EvaluatedState}
|
|
168
168
|
|
|
169
169
|
@staticmethod
|
|
170
|
-
def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
170
|
+
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
171
171
|
logger.info("Evaluating library %s", context.provenance.get_display_name())
|
|
172
172
|
|
|
173
173
|
context.evaluation_result = context.provenance.evaluate(context)
|
|
@@ -185,7 +185,7 @@ class EvaluatedState(State):
|
|
|
185
185
|
return {InstallingState}
|
|
186
186
|
|
|
187
187
|
@staticmethod
|
|
188
|
-
def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
188
|
+
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
189
189
|
evaluation_issues = context.get_evaluation_issues()
|
|
190
190
|
if evaluation_issues:
|
|
191
191
|
logger.warning(
|
|
@@ -208,7 +208,7 @@ class InstallingState(State):
|
|
|
208
208
|
return {InstalledState}
|
|
209
209
|
|
|
210
210
|
@staticmethod
|
|
211
|
-
def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
211
|
+
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
212
212
|
logger.info("Installing library %s", context.provenance.get_display_name())
|
|
213
213
|
|
|
214
214
|
# Check if user has disabled this library
|
|
@@ -219,7 +219,7 @@ class InstallingState(State):
|
|
|
219
219
|
return InstalledState
|
|
220
220
|
|
|
221
221
|
# Perform installation using delegation
|
|
222
|
-
context.installation_result = context.provenance.install(context)
|
|
222
|
+
context.installation_result = await context.provenance.install(context)
|
|
223
223
|
|
|
224
224
|
# Auto-transition to InstalledState
|
|
225
225
|
return InstalledState
|
|
@@ -234,7 +234,7 @@ class InstalledState(State):
|
|
|
234
234
|
return {LoadingState}
|
|
235
235
|
|
|
236
236
|
@staticmethod
|
|
237
|
-
def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
237
|
+
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
238
238
|
installation_issues = context.get_installation_issues()
|
|
239
239
|
if installation_issues:
|
|
240
240
|
logger.warning(
|
|
@@ -257,7 +257,7 @@ class LoadingState(State):
|
|
|
257
257
|
return {LoadedState}
|
|
258
258
|
|
|
259
259
|
@staticmethod
|
|
260
|
-
def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
260
|
+
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
261
261
|
logger.info("Loading library %s", context.provenance.get_display_name())
|
|
262
262
|
|
|
263
263
|
# Check if user has disabled this library
|
|
@@ -292,7 +292,7 @@ class LoadedState(State):
|
|
|
292
292
|
return set() # Terminal state
|
|
293
293
|
|
|
294
294
|
@staticmethod
|
|
295
|
-
def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
295
|
+
async def on_enter(context: LibraryLifecycleContext) -> StateType | None:
|
|
296
296
|
library_loaded_issues = context.get_library_loaded_issues()
|
|
297
297
|
if library_loaded_issues:
|
|
298
298
|
logger.warning(
|
|
@@ -313,31 +313,31 @@ class LibraryLifecycleFSM(FSM[LibraryLifecycleContext]):
|
|
|
313
313
|
context = LibraryLifecycleContext(provenance=provenance)
|
|
314
314
|
super().__init__(context)
|
|
315
315
|
|
|
316
|
-
def start_lifecycle(self) -> None:
|
|
316
|
+
async def start_lifecycle(self) -> None:
|
|
317
317
|
"""Start the library lifecycle from CandidateState."""
|
|
318
318
|
if self._current_state is not None:
|
|
319
319
|
raise InvalidStateTransitionError(self._current_state, CandidateState, "Lifecycle has already been started")
|
|
320
|
-
self.start(CandidateState)
|
|
320
|
+
await self.start(CandidateState)
|
|
321
321
|
|
|
322
|
-
def begin_inspection(self) -> None:
|
|
322
|
+
async def begin_inspection(self) -> None:
|
|
323
323
|
"""Explicitly transition from Candidate to Inspecting."""
|
|
324
324
|
self._validate_state_transition(InspectingState)
|
|
325
|
-
self.transition_state(InspectingState)
|
|
325
|
+
await self.transition_state(InspectingState)
|
|
326
326
|
|
|
327
|
-
def begin_evaluation(self) -> None:
|
|
327
|
+
async def begin_evaluation(self) -> None:
|
|
328
328
|
"""Explicitly transition from Inspected to Evaluating."""
|
|
329
329
|
self._validate_state_transition(EvaluatingState)
|
|
330
|
-
self.transition_state(EvaluatingState)
|
|
330
|
+
await self.transition_state(EvaluatingState)
|
|
331
331
|
|
|
332
|
-
def begin_installation(self) -> None:
|
|
332
|
+
async def begin_installation(self) -> None:
|
|
333
333
|
"""Explicitly transition from Evaluated to Installing."""
|
|
334
334
|
self._validate_state_transition(InstallingState)
|
|
335
|
-
self.transition_state(InstallingState)
|
|
335
|
+
await self.transition_state(InstallingState)
|
|
336
336
|
|
|
337
|
-
def begin_loading(self) -> None:
|
|
337
|
+
async def begin_loading(self) -> None:
|
|
338
338
|
"""Explicitly transition from Installed to Loading."""
|
|
339
339
|
self._validate_state_transition(LoadingState)
|
|
340
|
-
self.transition_state(LoadingState)
|
|
340
|
+
await self.transition_state(LoadingState)
|
|
341
341
|
|
|
342
342
|
def get_context(self) -> LibraryLifecycleContext:
|
|
343
343
|
"""Get the current context."""
|
|
@@ -60,7 +60,7 @@ class LibraryProvenance(ABC):
|
|
|
60
60
|
"""
|
|
61
61
|
|
|
62
62
|
@abstractmethod
|
|
63
|
-
def install(self, context: LibraryLifecycleContext) -> InstallationResult:
|
|
63
|
+
async def install(self, context: LibraryLifecycleContext) -> InstallationResult:
|
|
64
64
|
"""Install this provenance.
|
|
65
65
|
|
|
66
66
|
Args:
|
|
@@ -72,7 +72,7 @@ class LibraryProvenanceGitHub(LibraryProvenance):
|
|
|
72
72
|
)
|
|
73
73
|
return EvaluationResult(issues=issues)
|
|
74
74
|
|
|
75
|
-
def install(self, context: LibraryLifecycleContext) -> InstallationResult: # noqa: ARG002
|
|
75
|
+
async def install(self, context: LibraryLifecycleContext) -> InstallationResult: # noqa: ARG002
|
|
76
76
|
"""Install this GitHub repository library."""
|
|
77
77
|
issues = []
|
|
78
78
|
issues.append(
|
|
@@ -31,6 +31,7 @@ from griptape_nodes.retained_mode.managers.library_lifecycle.data_models import
|
|
|
31
31
|
from griptape_nodes.retained_mode.managers.library_lifecycle.library_provenance.base import LibraryProvenance
|
|
32
32
|
from griptape_nodes.retained_mode.managers.library_lifecycle.library_status import LibraryStatus
|
|
33
33
|
from griptape_nodes.retained_mode.managers.os_manager import OSManager
|
|
34
|
+
from griptape_nodes.utils.async_utils import subprocess_run
|
|
34
35
|
|
|
35
36
|
if TYPE_CHECKING:
|
|
36
37
|
from griptape_nodes.retained_mode.managers.library_lifecycle.library_fsm import LibraryLifecycleContext
|
|
@@ -114,7 +115,7 @@ class LibraryProvenanceLocalFile(LibraryProvenance):
|
|
|
114
115
|
|
|
115
116
|
return EvaluationResult(issues=issues)
|
|
116
117
|
|
|
117
|
-
def install(self, context: LibraryLifecycleContext) -> InstallationResult:
|
|
118
|
+
async def install(self, context: LibraryLifecycleContext) -> InstallationResult:
|
|
118
119
|
"""Install this local file library."""
|
|
119
120
|
problems = []
|
|
120
121
|
venv_path = ""
|
|
@@ -150,7 +151,7 @@ class LibraryProvenanceLocalFile(LibraryProvenance):
|
|
|
150
151
|
# Only install dependencies if conditions are met
|
|
151
152
|
library_venv_python_path = None
|
|
152
153
|
try:
|
|
153
|
-
library_venv_python_path = library_manager._init_library_venv(venv_path)
|
|
154
|
+
library_venv_python_path = await library_manager._init_library_venv(venv_path)
|
|
154
155
|
except RuntimeError as e:
|
|
155
156
|
problems.append(
|
|
156
157
|
LifecycleIssue(
|
|
@@ -187,7 +188,7 @@ class LibraryProvenanceLocalFile(LibraryProvenance):
|
|
|
187
188
|
# Grab the python executable from the virtual environment so that we can pip install there
|
|
188
189
|
logger.info("Installing dependencies for library '%s' with pip in venv at %s", library_data.name, venv_path)
|
|
189
190
|
try:
|
|
190
|
-
|
|
191
|
+
await subprocess_run(
|
|
191
192
|
[
|
|
192
193
|
sys.executable,
|
|
193
194
|
"-m",
|
|
@@ -61,7 +61,7 @@ class LibraryProvenancePackage(LibraryProvenance):
|
|
|
61
61
|
)
|
|
62
62
|
return EvaluationResult(issues=issues)
|
|
63
63
|
|
|
64
|
-
def install(self, context: LibraryLifecycleContext) -> InstallationResult: # noqa: ARG002
|
|
64
|
+
async def install(self, context: LibraryLifecycleContext) -> InstallationResult: # noqa: ARG002
|
|
65
65
|
"""Install this package library."""
|
|
66
66
|
issues = []
|
|
67
67
|
issues.append(
|
|
@@ -110,7 +110,7 @@ class LibraryProvenanceSandbox(LibraryProvenance):
|
|
|
110
110
|
|
|
111
111
|
return EvaluationResult(issues=issues)
|
|
112
112
|
|
|
113
|
-
def install(self, context: LibraryLifecycleContext) -> InstallationResult: # noqa: ARG002
|
|
113
|
+
async def install(self, context: LibraryLifecycleContext) -> InstallationResult: # noqa: ARG002
|
|
114
114
|
"""Install this sandbox library."""
|
|
115
115
|
issues = []
|
|
116
116
|
|