griptape-nodes 0.38.1__py3-none-any.whl → 0.40.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 +13 -9
- griptape_nodes/app/__init__.py +10 -1
- griptape_nodes/app/app.py +2 -3
- griptape_nodes/app/app_sessions.py +458 -0
- griptape_nodes/bootstrap/workflow_executors/__init__.py +1 -0
- griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +213 -0
- griptape_nodes/bootstrap/workflow_executors/workflow_executor.py +13 -0
- griptape_nodes/bootstrap/workflow_runners/local_workflow_runner.py +1 -1
- griptape_nodes/drivers/storage/__init__.py +4 -0
- griptape_nodes/drivers/storage/storage_backend.py +10 -0
- griptape_nodes/exe_types/core_types.py +5 -1
- griptape_nodes/exe_types/node_types.py +20 -24
- griptape_nodes/machines/node_resolution.py +5 -1
- griptape_nodes/node_library/advanced_node_library.py +51 -0
- griptape_nodes/node_library/library_registry.py +28 -2
- griptape_nodes/node_library/workflow_registry.py +1 -1
- griptape_nodes/retained_mode/events/agent_events.py +15 -2
- griptape_nodes/retained_mode/events/app_events.py +113 -2
- griptape_nodes/retained_mode/events/base_events.py +28 -1
- griptape_nodes/retained_mode/events/library_events.py +111 -1
- griptape_nodes/retained_mode/events/workflow_events.py +1 -0
- griptape_nodes/retained_mode/griptape_nodes.py +240 -18
- griptape_nodes/retained_mode/managers/agent_manager.py +123 -17
- griptape_nodes/retained_mode/managers/flow_manager.py +16 -48
- griptape_nodes/retained_mode/managers/library_manager.py +642 -121
- griptape_nodes/retained_mode/managers/node_manager.py +1 -1
- griptape_nodes/retained_mode/managers/static_files_manager.py +4 -3
- griptape_nodes/retained_mode/managers/workflow_manager.py +666 -37
- griptape_nodes/retained_mode/utils/__init__.py +1 -0
- griptape_nodes/retained_mode/utils/engine_identity.py +131 -0
- griptape_nodes/retained_mode/utils/name_generator.py +162 -0
- griptape_nodes/retained_mode/utils/session_persistence.py +105 -0
- {griptape_nodes-0.38.1.dist-info → griptape_nodes-0.40.0.dist-info}/METADATA +1 -1
- {griptape_nodes-0.38.1.dist-info → griptape_nodes-0.40.0.dist-info}/RECORD +37 -27
- {griptape_nodes-0.38.1.dist-info → griptape_nodes-0.40.0.dist-info}/WHEEL +0 -0
- {griptape_nodes-0.38.1.dist-info → griptape_nodes-0.40.0.dist-info}/entry_points.txt +0 -0
- {griptape_nodes-0.38.1.dist-info → griptape_nodes-0.40.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from queue import Queue
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from griptape.events import BaseEvent, EventBus, EventListener
|
|
6
|
+
|
|
7
|
+
from griptape_nodes.bootstrap.workflow_executors.workflow_executor import WorkflowExecutor
|
|
8
|
+
from griptape_nodes.drivers.storage import StorageBackend
|
|
9
|
+
from griptape_nodes.exe_types.node_types import EndNode, StartNode
|
|
10
|
+
from griptape_nodes.retained_mode.events.base_events import (
|
|
11
|
+
AppEvent,
|
|
12
|
+
EventRequest,
|
|
13
|
+
ExecutionGriptapeNodeEvent,
|
|
14
|
+
GriptapeNodeEvent,
|
|
15
|
+
ProgressEvent,
|
|
16
|
+
)
|
|
17
|
+
from griptape_nodes.retained_mode.events.execution_events import SingleExecutionStepRequest, StartFlowRequest
|
|
18
|
+
from griptape_nodes.retained_mode.events.parameter_events import SetParameterValueRequest
|
|
19
|
+
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class LocalExecutorError(Exception):
|
|
25
|
+
"""Exception raised during local workflow execution."""
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class LocalWorkflowExecutor(WorkflowExecutor):
|
|
29
|
+
def __init__(self) -> None:
|
|
30
|
+
self.queue = Queue()
|
|
31
|
+
self.output: dict | None = None
|
|
32
|
+
|
|
33
|
+
def _load_flow_for_workflow(self) -> str:
|
|
34
|
+
try:
|
|
35
|
+
context_manager = GriptapeNodes.ContextManager()
|
|
36
|
+
return context_manager.get_current_flow().name
|
|
37
|
+
except Exception as e:
|
|
38
|
+
msg = f"Failed to get current flow from context manager: {e}"
|
|
39
|
+
logger.exception(msg)
|
|
40
|
+
raise LocalExecutorError(msg) from e
|
|
41
|
+
|
|
42
|
+
def _set_storage_backend(self, storage_backend: StorageBackend) -> None:
|
|
43
|
+
from griptape_nodes.retained_mode.managers.config_manager import ConfigManager
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
config_manager = ConfigManager()
|
|
47
|
+
config_manager.set_config_value(
|
|
48
|
+
key="storage_backend",
|
|
49
|
+
value=storage_backend,
|
|
50
|
+
)
|
|
51
|
+
except Exception as e:
|
|
52
|
+
msg = f"Failed to set storage backend: {e}"
|
|
53
|
+
logger.exception(msg)
|
|
54
|
+
raise LocalExecutorError(msg) from e
|
|
55
|
+
|
|
56
|
+
def _handle_event(self, event: BaseEvent) -> None:
|
|
57
|
+
try:
|
|
58
|
+
match event:
|
|
59
|
+
case GriptapeNodeEvent():
|
|
60
|
+
self.__handle_node_event(event)
|
|
61
|
+
case ExecutionGriptapeNodeEvent():
|
|
62
|
+
self.__handle_execution_node_event(event)
|
|
63
|
+
case ProgressEvent():
|
|
64
|
+
self.__handle_progress_event(event)
|
|
65
|
+
case AppEvent():
|
|
66
|
+
self.__handle_app_event(event)
|
|
67
|
+
case _:
|
|
68
|
+
msg = f"Unknown event type: {type(event)}"
|
|
69
|
+
logger.info(msg)
|
|
70
|
+
self.queue.put(event)
|
|
71
|
+
except Exception as e:
|
|
72
|
+
logger.info(e)
|
|
73
|
+
|
|
74
|
+
def __handle_node_event(self, event: GriptapeNodeEvent) -> None:
|
|
75
|
+
result_event = event.wrapped_event
|
|
76
|
+
event_json = result_event.json()
|
|
77
|
+
event_log = f"GriptapeNodeEvent: {event_json}"
|
|
78
|
+
logger.info(event_log)
|
|
79
|
+
|
|
80
|
+
def __handle_execution_node_event(self, event: ExecutionGriptapeNodeEvent) -> None:
|
|
81
|
+
result_event = event.wrapped_event
|
|
82
|
+
if type(result_event.payload).__name__ == "NodeStartProcessEvent":
|
|
83
|
+
event_log = f"NodeStartProcessEvent: {result_event.payload}"
|
|
84
|
+
logger.info(event_log)
|
|
85
|
+
|
|
86
|
+
elif type(result_event.payload).__name__ == "ResumeNodeProcessingEvent":
|
|
87
|
+
event_log = f"ResumeNodeProcessingEvent: {result_event.payload}"
|
|
88
|
+
logger.info(event_log)
|
|
89
|
+
|
|
90
|
+
# Here we need to handle the resume event since this is the callback mechanism
|
|
91
|
+
# for the flow to be resumed for any Node that yields a generator in its process method.
|
|
92
|
+
node_name = result_event.payload.node_name
|
|
93
|
+
flow_name = GriptapeNodes.NodeManager().get_node_parent_flow_by_name(node_name)
|
|
94
|
+
event_request = EventRequest(request=SingleExecutionStepRequest(flow_name=flow_name))
|
|
95
|
+
GriptapeNodes.handle_request(event_request.request)
|
|
96
|
+
|
|
97
|
+
elif type(result_event.payload).__name__ == "NodeFinishProcessEvent":
|
|
98
|
+
event_log = f"NodeFinishProcessEvent: {result_event.payload}"
|
|
99
|
+
logger.info(event_log)
|
|
100
|
+
|
|
101
|
+
else:
|
|
102
|
+
event_log = f"ExecutionGriptapeNodeEvent: {result_event.payload}"
|
|
103
|
+
logger.info(event_log)
|
|
104
|
+
|
|
105
|
+
self.queue.put(event)
|
|
106
|
+
|
|
107
|
+
def __handle_progress_event(self, gt_event: ProgressEvent) -> None:
|
|
108
|
+
event_log = f"ProgressEvent: {gt_event}"
|
|
109
|
+
logger.info(event_log)
|
|
110
|
+
|
|
111
|
+
def __handle_app_event(self, event: AppEvent) -> None:
|
|
112
|
+
event_log = f"AppEvent: {event.payload}"
|
|
113
|
+
logger.info(event_log)
|
|
114
|
+
|
|
115
|
+
def _submit_output(self, output: dict) -> None:
|
|
116
|
+
self.output = output
|
|
117
|
+
|
|
118
|
+
def _set_input_for_flow(self, flow_name: str, flow_input: dict[str, dict]) -> None:
|
|
119
|
+
control_flow = GriptapeNodes.FlowManager().get_flow_by_name(flow_name)
|
|
120
|
+
nodes = control_flow.nodes
|
|
121
|
+
for node_name, node in nodes.items():
|
|
122
|
+
if isinstance(node, StartNode):
|
|
123
|
+
param_map: dict | None = flow_input.get(node_name)
|
|
124
|
+
if param_map is not None:
|
|
125
|
+
for parameter_name, parameter_value in param_map.items():
|
|
126
|
+
set_parameter_value_request = SetParameterValueRequest(
|
|
127
|
+
parameter_name=parameter_name,
|
|
128
|
+
value=parameter_value,
|
|
129
|
+
node_name=node_name,
|
|
130
|
+
)
|
|
131
|
+
set_parameter_value_result = GriptapeNodes.handle_request(set_parameter_value_request)
|
|
132
|
+
|
|
133
|
+
if set_parameter_value_result.failed():
|
|
134
|
+
msg = f"Failed to set parameter {parameter_name} for node {node_name}."
|
|
135
|
+
raise LocalExecutorError(msg)
|
|
136
|
+
|
|
137
|
+
def _get_output_for_flow(self, flow_name: str) -> dict:
|
|
138
|
+
control_flow = GriptapeNodes.FlowManager().get_flow_by_name(flow_name)
|
|
139
|
+
nodes = control_flow.nodes
|
|
140
|
+
output = {}
|
|
141
|
+
for node_name, node in nodes.items():
|
|
142
|
+
if isinstance(node, EndNode):
|
|
143
|
+
output[node_name] = node.parameter_values
|
|
144
|
+
|
|
145
|
+
return output
|
|
146
|
+
|
|
147
|
+
def run(self, workflow_name: str, flow_input: Any, storage_backend: StorageBackend = StorageBackend.LOCAL) -> None:
|
|
148
|
+
"""Executes a local workflow.
|
|
149
|
+
|
|
150
|
+
Executes a workflow by setting up event listeners, registering libraries,
|
|
151
|
+
loading the user-defined workflow, and running the specified workflow.
|
|
152
|
+
|
|
153
|
+
Parameters:
|
|
154
|
+
workflow_name: The name of the workflow to execute.
|
|
155
|
+
flow_input: Input data for the flow, typically a dictionary.
|
|
156
|
+
storage_backend: The storage backend to use for the workflow execution.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
None
|
|
160
|
+
"""
|
|
161
|
+
logger.info("Executing workflow: %s", workflow_name)
|
|
162
|
+
|
|
163
|
+
EventBus.add_event_listener(
|
|
164
|
+
event_listener=EventListener(
|
|
165
|
+
on_event=self._handle_event,
|
|
166
|
+
)
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
# Set the storage backend
|
|
170
|
+
self._set_storage_backend(storage_backend=storage_backend)
|
|
171
|
+
# Load the flow
|
|
172
|
+
flow_name = self._load_flow_for_workflow()
|
|
173
|
+
# Now let's set the input to the flow
|
|
174
|
+
self._set_input_for_flow(flow_name=flow_name, flow_input=flow_input)
|
|
175
|
+
|
|
176
|
+
# Now send the run command to actually execute it
|
|
177
|
+
start_flow_request = StartFlowRequest(flow_name=flow_name)
|
|
178
|
+
start_flow_result = GriptapeNodes.handle_request(start_flow_request)
|
|
179
|
+
|
|
180
|
+
if start_flow_result.failed():
|
|
181
|
+
msg = f"Failed to start flow {flow_name}"
|
|
182
|
+
raise LocalExecutorError(msg)
|
|
183
|
+
|
|
184
|
+
logger.info("Workflow started!")
|
|
185
|
+
|
|
186
|
+
# Wait for the control flow to finish
|
|
187
|
+
is_flow_finished = False
|
|
188
|
+
error: Exception | None = None
|
|
189
|
+
while not is_flow_finished:
|
|
190
|
+
try:
|
|
191
|
+
event = self.queue.get(block=True)
|
|
192
|
+
|
|
193
|
+
if isinstance(event, ExecutionGriptapeNodeEvent):
|
|
194
|
+
result_event = event.wrapped_event
|
|
195
|
+
|
|
196
|
+
if type(result_event.payload).__name__ == "ControlFlowResolvedEvent":
|
|
197
|
+
self._submit_output(self._get_output_for_flow(flow_name=flow_name))
|
|
198
|
+
is_flow_finished = True
|
|
199
|
+
logger.info("Workflow finished!")
|
|
200
|
+
elif type(result_event.payload).__name__ == "ControlFlowCancelledEvent":
|
|
201
|
+
msg = "Control flow cancelled"
|
|
202
|
+
is_flow_finished = True
|
|
203
|
+
logger.error(msg)
|
|
204
|
+
error = LocalExecutorError(msg)
|
|
205
|
+
|
|
206
|
+
self.queue.task_done()
|
|
207
|
+
|
|
208
|
+
except Exception as e:
|
|
209
|
+
msg = f"Error handling queue event: {e}"
|
|
210
|
+
logger.info(msg)
|
|
211
|
+
|
|
212
|
+
if error is not None:
|
|
213
|
+
raise error
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from griptape_nodes.drivers.storage import StorageBackend
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class WorkflowExecutor(ABC):
|
|
11
|
+
@abstractmethod
|
|
12
|
+
def run(self, workflow_name: str, flow_input: Any, storage_backend: StorageBackend = StorageBackend.LOCAL) -> None:
|
|
13
|
+
pass
|
|
@@ -35,7 +35,7 @@ class LocalWorkflowRunner(WorkflowRunner):
|
|
|
35
35
|
file_path = Path(path_to_workflow)
|
|
36
36
|
|
|
37
37
|
# Generate a unique module name
|
|
38
|
-
module_name = f"
|
|
38
|
+
module_name = f"gtn_dynamic_module_{file_path.name.replace('.', '_')}_{hash(str(file_path))}"
|
|
39
39
|
|
|
40
40
|
# Load the module specification
|
|
41
41
|
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
@@ -216,7 +216,6 @@ class BaseNodeElement:
|
|
|
216
216
|
new_value = getattr(self, f"{func.__name__}", None) if hasattr(self, f"{func.__name__}") else None
|
|
217
217
|
# Track change if different
|
|
218
218
|
if old_value != new_value:
|
|
219
|
-
# it needs to be static so we can call these methods.
|
|
220
219
|
self._changes[func.__name__] = new_value
|
|
221
220
|
if self._node_context is not None and self not in self._node_context._tracked_parameters:
|
|
222
221
|
self._node_context._tracked_parameters.append(self)
|
|
@@ -735,6 +734,11 @@ class Parameter(BaseNodeElement):
|
|
|
735
734
|
@BaseNodeElement.emits_update_on_write
|
|
736
735
|
def allowed_modes(self, value: Any) -> None:
|
|
737
736
|
self._allowed_modes = value
|
|
737
|
+
# Handle mode flag decomposition
|
|
738
|
+
if isinstance(value, set):
|
|
739
|
+
self._changes["mode_allowed_input"] = ParameterMode.INPUT in value
|
|
740
|
+
self._changes["mode_allowed_output"] = ParameterMode.OUTPUT in value
|
|
741
|
+
self._changes["mode_allowed_property"] = ParameterMode.PROPERTY in value
|
|
738
742
|
|
|
739
743
|
@property
|
|
740
744
|
def ui_options(self) -> dict:
|
|
@@ -136,7 +136,6 @@ class BaseNode(ABC):
|
|
|
136
136
|
source_node: BaseNode, # noqa: ARG002
|
|
137
137
|
source_parameter: Parameter, # noqa: ARG002
|
|
138
138
|
target_parameter: Parameter, # noqa: ARG002
|
|
139
|
-
modified_parameters_set: set[str] | None = None, # noqa: ARG002
|
|
140
139
|
) -> None:
|
|
141
140
|
"""Callback after a Connection has been established TO this Node."""
|
|
142
141
|
return
|
|
@@ -146,7 +145,6 @@ class BaseNode(ABC):
|
|
|
146
145
|
source_parameter: Parameter, # noqa: ARG002
|
|
147
146
|
target_node: BaseNode, # noqa: ARG002
|
|
148
147
|
target_parameter: Parameter, # noqa: ARG002
|
|
149
|
-
modified_parameters_set: set[str] | None = None, # noqa: ARG002
|
|
150
148
|
) -> None:
|
|
151
149
|
"""Callback after a Connection has been established OUT of this Node."""
|
|
152
150
|
return
|
|
@@ -156,7 +154,6 @@ class BaseNode(ABC):
|
|
|
156
154
|
source_node: BaseNode, # noqa: ARG002
|
|
157
155
|
source_parameter: Parameter, # noqa: ARG002
|
|
158
156
|
target_parameter: Parameter, # noqa: ARG002
|
|
159
|
-
modified_parameters_set: set[str] | None = None, # noqa: ARG002
|
|
160
157
|
) -> None:
|
|
161
158
|
"""Callback after a Connection TO this Node was REMOVED."""
|
|
162
159
|
return
|
|
@@ -166,7 +163,6 @@ class BaseNode(ABC):
|
|
|
166
163
|
source_parameter: Parameter, # noqa: ARG002
|
|
167
164
|
target_node: BaseNode, # noqa: ARG002
|
|
168
165
|
target_parameter: Parameter, # noqa: ARG002
|
|
169
|
-
modified_parameters_set: set[str] | None = None, # noqa: ARG002
|
|
170
166
|
) -> None:
|
|
171
167
|
"""Callback after a Connection OUT of this Node was REMOVED."""
|
|
172
168
|
return
|
|
@@ -175,7 +171,6 @@ class BaseNode(ABC):
|
|
|
175
171
|
self,
|
|
176
172
|
parameter: Parameter, # noqa: ARG002
|
|
177
173
|
value: Any,
|
|
178
|
-
modified_parameters_set: set[str] | None = None, # noqa: ARG002
|
|
179
174
|
) -> Any:
|
|
180
175
|
"""Callback when a Parameter's value is ABOUT to be set.
|
|
181
176
|
|
|
@@ -191,7 +186,6 @@ class BaseNode(ABC):
|
|
|
191
186
|
Args:
|
|
192
187
|
parameter: the Parameter on this node that is about to be changed
|
|
193
188
|
value: the value intended to be set (this has already gone through any converters and validators on the Parameter)
|
|
194
|
-
modified_parameters_set: A set of parameter names within this node that were modified as a result of this call.
|
|
195
189
|
|
|
196
190
|
Returns:
|
|
197
191
|
The final value to set for the Parameter. This gives the Node logic one last opportunity to mutate the value
|
|
@@ -204,7 +198,6 @@ class BaseNode(ABC):
|
|
|
204
198
|
self,
|
|
205
199
|
parameter: Parameter, # noqa: ARG002
|
|
206
200
|
value: Any, # noqa: ARG002
|
|
207
|
-
modified_parameters_set: set[str] | None = None, # noqa: ARG002
|
|
208
201
|
) -> None:
|
|
209
202
|
"""Callback AFTER a Parameter's value was set.
|
|
210
203
|
|
|
@@ -225,8 +218,6 @@ class BaseNode(ABC):
|
|
|
225
218
|
Args:
|
|
226
219
|
parameter: the Parameter on this node that was just changed
|
|
227
220
|
value: the value that was set (already converted, validated, and possibly mutated by the node code)
|
|
228
|
-
modified_parameters_set: Optional set of parameter names within this node
|
|
229
|
-
that were modified as a result of this call. The Parameter this was called on does NOT need to be part of the return.
|
|
230
221
|
|
|
231
222
|
Returns:
|
|
232
223
|
Nothing
|
|
@@ -480,7 +471,9 @@ class BaseNode(ABC):
|
|
|
480
471
|
return element_item
|
|
481
472
|
return None
|
|
482
473
|
|
|
483
|
-
def set_parameter_value(
|
|
474
|
+
def set_parameter_value(
|
|
475
|
+
self, param_name: str, value: Any, *, initial_setup: bool = False, emit_change: bool = True
|
|
476
|
+
) -> None:
|
|
484
477
|
"""Attempt to set a Parameter's value.
|
|
485
478
|
|
|
486
479
|
The Node may choose to store a different value (or type) than what was passed in.
|
|
@@ -498,6 +491,8 @@ class BaseNode(ABC):
|
|
|
498
491
|
Args:
|
|
499
492
|
param_name: the name of the Parameter on this node that is about to be changed
|
|
500
493
|
value: the value intended to be set
|
|
494
|
+
emit_change: whether to emit a parameter lifecycle event, defaults to True
|
|
495
|
+
initial_setup: Whether this value is being set as the initial setup on the node, defaults to False. When True, the value is not given to any before/after hooks.
|
|
501
496
|
|
|
502
497
|
Returns:
|
|
503
498
|
A set of parameter names within this node that were modified as a result
|
|
@@ -525,22 +520,18 @@ class BaseNode(ABC):
|
|
|
525
520
|
|
|
526
521
|
# Allow custom node logic to prepare and possibly mutate the value before it is actually set.
|
|
527
522
|
# Record any parameters modified for cascading.
|
|
528
|
-
|
|
523
|
+
if not initial_setup:
|
|
529
524
|
final_value = self.before_value_set(parameter=parameter, value=candidate_value)
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
parameter=parameter, value=candidate_value, modified_parameters_set=set()
|
|
533
|
-
)
|
|
534
|
-
# ACTUALLY SET THE NEW VALUE
|
|
535
|
-
self.parameter_values[param_name] = final_value
|
|
525
|
+
# ACTUALLY SET THE NEW VALUE
|
|
526
|
+
self.parameter_values[param_name] = final_value
|
|
536
527
|
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
try:
|
|
528
|
+
# If a parameter value has been set at the top level of a container, wipe all children.
|
|
529
|
+
# Allow custom node logic to respond after it's been set. Record any modified parameters for cascading.
|
|
540
530
|
self.after_value_set(parameter=parameter, value=final_value)
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
531
|
+
if emit_change:
|
|
532
|
+
self._emit_parameter_lifecycle_event(parameter)
|
|
533
|
+
else:
|
|
534
|
+
self.parameter_values[param_name] = candidate_value
|
|
544
535
|
# handle with container parameters
|
|
545
536
|
if parameter.parent_container_name is not None:
|
|
546
537
|
# Does it have a parent container
|
|
@@ -551,7 +542,12 @@ class BaseNode(ABC):
|
|
|
551
542
|
new_parent_value = handle_container_parameter(self, parent_parameter)
|
|
552
543
|
if new_parent_value is not None:
|
|
553
544
|
# set that new value if it exists.
|
|
554
|
-
self.set_parameter_value(
|
|
545
|
+
self.set_parameter_value(
|
|
546
|
+
parameter.parent_container_name,
|
|
547
|
+
new_parent_value,
|
|
548
|
+
initial_setup=initial_setup,
|
|
549
|
+
emit_change=False,
|
|
550
|
+
)
|
|
555
551
|
|
|
556
552
|
def kill_parameter_children(self, parameter: Parameter) -> None:
|
|
557
553
|
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
@@ -230,7 +230,11 @@ class ExecuteNodeState(State):
|
|
|
230
230
|
)
|
|
231
231
|
)
|
|
232
232
|
|
|
233
|
-
current_node.validate_before_node_run()
|
|
233
|
+
exceptions = current_node.validate_before_node_run()
|
|
234
|
+
if exceptions:
|
|
235
|
+
msg = f"Canceling flow run. Node '{current_node.name}' encountered problems: {exceptions}"
|
|
236
|
+
# Mark the node as unresolved, broadcasting to everyone.
|
|
237
|
+
raise RuntimeError(msg)
|
|
234
238
|
if not context.paused:
|
|
235
239
|
return ExecuteNodeState
|
|
236
240
|
return None
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from griptape_nodes.node_library.library_registry import Library, LibrarySchema
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AdvancedNodeLibrary:
|
|
10
|
+
"""Base class for advanced node libraries with callback support.
|
|
11
|
+
|
|
12
|
+
Library modules can inherit from this class to provide custom initialization
|
|
13
|
+
and cleanup logic that runs before and after node loading.
|
|
14
|
+
|
|
15
|
+
Example usage:
|
|
16
|
+
```python
|
|
17
|
+
# In your library's advanced library module file:
|
|
18
|
+
from griptape_nodes.node_library.advanced_node_library import AdvancedNodeLibrary
|
|
19
|
+
|
|
20
|
+
class MyLibrary(AdvancedNodeLibrary):
|
|
21
|
+
def before_library_nodes_loaded(self, library_data, library):
|
|
22
|
+
# Set up any prerequisites before nodes are loaded
|
|
23
|
+
print(f"About to load nodes for {library_data.name}")
|
|
24
|
+
|
|
25
|
+
def after_library_nodes_loaded(self, library_data, library):
|
|
26
|
+
# Perform any cleanup or additional setup after nodes are loaded
|
|
27
|
+
print(f"Finished loading {len(library.get_registered_nodes())} nodes")
|
|
28
|
+
```
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def before_library_nodes_loaded(self, library_data: LibrarySchema, library: Library) -> None:
|
|
32
|
+
"""Called before any nodes are loaded from the library.
|
|
33
|
+
|
|
34
|
+
This method is called after the library instance is created but before
|
|
35
|
+
any individual node classes are dynamically loaded and registered.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
library_data: The library schema containing metadata and node definitions
|
|
39
|
+
library: The library instance that will contain the loaded nodes
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def after_library_nodes_loaded(self, library_data: LibrarySchema, library: Library) -> None:
|
|
43
|
+
"""Called after all nodes have been loaded from the library.
|
|
44
|
+
|
|
45
|
+
This method is called after all node classes have been successfully
|
|
46
|
+
loaded and registered with the library.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
library_data: The library schema containing metadata and node definitions
|
|
50
|
+
library: The library instance containing the loaded nodes
|
|
51
|
+
"""
|
|
@@ -9,6 +9,7 @@ from griptape_nodes.utils.metaclasses import SingletonMeta
|
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
11
|
from griptape_nodes.exe_types.node_types import BaseNode
|
|
12
|
+
from griptape_nodes.node_library.advanced_node_library import AdvancedNodeLibrary
|
|
12
13
|
|
|
13
14
|
logger = logging.getLogger("griptape_nodes")
|
|
14
15
|
|
|
@@ -42,6 +43,13 @@ class LibraryMetadata(BaseModel):
|
|
|
42
43
|
is_griptape_nodes_searchable: bool = True
|
|
43
44
|
|
|
44
45
|
|
|
46
|
+
class IconVariant(BaseModel):
|
|
47
|
+
"""Icon variant for light and dark themes."""
|
|
48
|
+
|
|
49
|
+
light: str
|
|
50
|
+
dark: str
|
|
51
|
+
|
|
52
|
+
|
|
45
53
|
class NodeMetadata(BaseModel):
|
|
46
54
|
"""Metadata about each node within the library, which informs where in the hierarchy it sits, details on usage, and tags to assist search."""
|
|
47
55
|
|
|
@@ -49,6 +57,9 @@ class NodeMetadata(BaseModel):
|
|
|
49
57
|
description: str
|
|
50
58
|
display_name: str
|
|
51
59
|
tags: list[str] | None = None
|
|
60
|
+
icon: str | IconVariant | None = None
|
|
61
|
+
color: str | None = None
|
|
62
|
+
group: str | None = None
|
|
52
63
|
|
|
53
64
|
|
|
54
65
|
class CategoryDefinition(BaseModel):
|
|
@@ -84,7 +95,7 @@ class LibrarySchema(BaseModel):
|
|
|
84
95
|
library itself.
|
|
85
96
|
"""
|
|
86
97
|
|
|
87
|
-
LATEST_SCHEMA_VERSION: ClassVar[str] = "0.
|
|
98
|
+
LATEST_SCHEMA_VERSION: ClassVar[str] = "0.2.0"
|
|
88
99
|
|
|
89
100
|
name: str
|
|
90
101
|
library_schema_version: str
|
|
@@ -95,6 +106,7 @@ class LibrarySchema(BaseModel):
|
|
|
95
106
|
scripts: list[str] | None = None
|
|
96
107
|
settings: list[Setting] | None = None
|
|
97
108
|
is_default_library: bool | None = None
|
|
109
|
+
advanced_library_path: str | None = None
|
|
98
110
|
|
|
99
111
|
|
|
100
112
|
class LibraryRegistry(metaclass=SingletonMeta):
|
|
@@ -110,13 +122,16 @@ class LibraryRegistry(metaclass=SingletonMeta):
|
|
|
110
122
|
library_data: LibrarySchema,
|
|
111
123
|
*,
|
|
112
124
|
mark_as_default_library: bool = False,
|
|
125
|
+
advanced_library: AdvancedNodeLibrary | None = None,
|
|
113
126
|
) -> Library:
|
|
114
127
|
instance = cls()
|
|
115
128
|
|
|
116
129
|
if library_data.name in instance._libraries:
|
|
117
130
|
msg = f"Library '{library_data.name}' already registered."
|
|
118
131
|
raise KeyError(msg)
|
|
119
|
-
library = Library(
|
|
132
|
+
library = Library(
|
|
133
|
+
library_data=library_data, is_default_library=mark_as_default_library, advanced_library=advanced_library
|
|
134
|
+
)
|
|
120
135
|
instance._libraries[library_data.name] = library
|
|
121
136
|
return library
|
|
122
137
|
|
|
@@ -227,12 +242,14 @@ class Library:
|
|
|
227
242
|
# Maintain fast lookups for node class name to class and to its metadata.
|
|
228
243
|
_node_types: dict[str, type[BaseNode]]
|
|
229
244
|
_node_metadata: dict[str, NodeMetadata]
|
|
245
|
+
_advanced_library: AdvancedNodeLibrary | None
|
|
230
246
|
|
|
231
247
|
def __init__(
|
|
232
248
|
self,
|
|
233
249
|
library_data: LibrarySchema,
|
|
234
250
|
*,
|
|
235
251
|
is_default_library: bool = False,
|
|
252
|
+
advanced_library: AdvancedNodeLibrary | None = None,
|
|
236
253
|
) -> None:
|
|
237
254
|
self._library_data = library_data
|
|
238
255
|
|
|
@@ -244,6 +261,7 @@ class Library:
|
|
|
244
261
|
|
|
245
262
|
self._node_types = {}
|
|
246
263
|
self._node_metadata = {}
|
|
264
|
+
self._advanced_library = advanced_library
|
|
247
265
|
|
|
248
266
|
def register_new_node_type(self, node_class: type[BaseNode], metadata: NodeMetadata) -> str | None:
|
|
249
267
|
"""Register a new node type in this library. Returns an error string for forensics, or None if all clear."""
|
|
@@ -303,3 +321,11 @@ class Library:
|
|
|
303
321
|
|
|
304
322
|
def get_metadata(self) -> LibraryMetadata:
|
|
305
323
|
return self._library_data.metadata
|
|
324
|
+
|
|
325
|
+
def get_advanced_library(self) -> AdvancedNodeLibrary | None:
|
|
326
|
+
"""Get the advanced library instance for this library.
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
The AdvancedNodeLibrary instance, or None if not set
|
|
330
|
+
"""
|
|
331
|
+
return self._advanced_library
|
|
@@ -12,22 +12,35 @@ from griptape_nodes.retained_mode.events.base_events import (
|
|
|
12
12
|
from griptape_nodes.retained_mode.events.payload_registry import PayloadRegistry
|
|
13
13
|
|
|
14
14
|
|
|
15
|
+
@dataclass
|
|
16
|
+
class RunAgentRequestArtifact(dict):
|
|
17
|
+
type: str
|
|
18
|
+
value: str
|
|
19
|
+
|
|
20
|
+
|
|
15
21
|
@dataclass
|
|
16
22
|
@PayloadRegistry.register
|
|
17
23
|
class RunAgentRequest(RequestPayload):
|
|
18
24
|
input: str
|
|
25
|
+
url_artifacts: list[RunAgentRequestArtifact]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
@PayloadRegistry.register
|
|
30
|
+
class RunAgentResultStarted(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
31
|
+
pass
|
|
19
32
|
|
|
20
33
|
|
|
21
34
|
@dataclass
|
|
22
35
|
@PayloadRegistry.register
|
|
23
36
|
class RunAgentResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
24
|
-
output:
|
|
37
|
+
output: dict
|
|
25
38
|
|
|
26
39
|
|
|
27
40
|
@dataclass
|
|
28
41
|
@PayloadRegistry.register
|
|
29
42
|
class RunAgentResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
30
|
-
error:
|
|
43
|
+
error: dict
|
|
31
44
|
|
|
32
45
|
|
|
33
46
|
@dataclass
|