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
|
@@ -195,10 +195,56 @@ class ExecuteNodeState(State):
|
|
|
195
195
|
EventBus.publish_event(ExecutionGriptapeNodeEvent(wrapped_event=ExecutionEvent(payload=payload)))
|
|
196
196
|
current_node.parameter_output_values.clear()
|
|
197
197
|
|
|
198
|
+
@staticmethod
|
|
199
|
+
def collect_values_from_upstream_nodes(context: ResolutionContext) -> None:
|
|
200
|
+
"""Collect output values from resolved upstream nodes and pass them to the current node.
|
|
201
|
+
|
|
202
|
+
This method iterates through all input parameters of the current node, finds their
|
|
203
|
+
connected upstream nodes, and if those nodes are resolved, retrieves their output
|
|
204
|
+
values and passes them through using SetParameterValueRequest.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
context (ResolutionContext): The resolution context containing the focus stack
|
|
208
|
+
with the current node being processed.
|
|
209
|
+
"""
|
|
210
|
+
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
211
|
+
|
|
212
|
+
current_node = context.focus_stack[-1].node
|
|
213
|
+
connections = GriptapeNodes.FlowManager().get_connections()
|
|
214
|
+
|
|
215
|
+
for parameter in current_node.parameters:
|
|
216
|
+
# Skip control type parameters
|
|
217
|
+
if ParameterTypeBuiltin.CONTROL_TYPE.value.lower() == parameter.output_type:
|
|
218
|
+
continue
|
|
219
|
+
|
|
220
|
+
# Get the connected upstream node for this parameter
|
|
221
|
+
upstream_connection = connections.get_connected_node(current_node, parameter)
|
|
222
|
+
if upstream_connection:
|
|
223
|
+
upstream_node, upstream_parameter = upstream_connection
|
|
224
|
+
|
|
225
|
+
# If the upstream node is resolved, collect its output value
|
|
226
|
+
if upstream_parameter.name in upstream_node.parameter_output_values:
|
|
227
|
+
output_value = upstream_node.parameter_output_values[upstream_parameter.name]
|
|
228
|
+
|
|
229
|
+
# Pass the value through using the same mechanism as normal resolution
|
|
230
|
+
GriptapeNodes.get_instance().handle_request(
|
|
231
|
+
SetParameterValueRequest(
|
|
232
|
+
parameter_name=parameter.name,
|
|
233
|
+
node_name=current_node.name,
|
|
234
|
+
value=output_value,
|
|
235
|
+
data_type=upstream_parameter.output_type,
|
|
236
|
+
)
|
|
237
|
+
)
|
|
238
|
+
|
|
198
239
|
@staticmethod
|
|
199
240
|
def on_enter(context: ResolutionContext) -> type[State] | None:
|
|
200
241
|
current_node = context.focus_stack[-1].node
|
|
242
|
+
|
|
201
243
|
# Clear all of the current output values
|
|
244
|
+
# if node is locked, don't clear anything. skip all of this.
|
|
245
|
+
if current_node.lock:
|
|
246
|
+
return ExecuteNodeState
|
|
247
|
+
ExecuteNodeState.collect_values_from_upstream_nodes(context)
|
|
202
248
|
ExecuteNodeState.clear_parameter_output_values(context)
|
|
203
249
|
for parameter in current_node.parameters:
|
|
204
250
|
if ParameterTypeBuiltin.CONTROL_TYPE.value.lower() == parameter.output_type:
|
|
@@ -244,95 +290,85 @@ class ExecuteNodeState(State):
|
|
|
244
290
|
# Once everything has been set
|
|
245
291
|
current_focus = context.focus_stack[-1]
|
|
246
292
|
current_node = current_focus.node
|
|
247
|
-
#
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
wrapped_event=ExecutionEvent(payload=NodeStartProcessEvent(node_name=current_node.name))
|
|
251
|
-
)
|
|
252
|
-
)
|
|
253
|
-
logger.info("Node '%s' is processing.", current_node.name)
|
|
254
|
-
|
|
255
|
-
try:
|
|
256
|
-
work_is_scheduled = ExecuteNodeState._process_node(current_focus)
|
|
257
|
-
if work_is_scheduled:
|
|
258
|
-
logger.debug("Pausing Node '%s' to run background work", current_node.name)
|
|
259
|
-
return None
|
|
260
|
-
except Exception as e:
|
|
261
|
-
logger.exception("Error processing node '%s", current_node.name)
|
|
262
|
-
msg = f"Canceling flow run. Node '{current_node.name}' encountered a problem: {e}"
|
|
263
|
-
# Mark the node as unresolved, broadcasting to everyone.
|
|
264
|
-
current_node.make_node_unresolved(
|
|
265
|
-
current_states_to_trigger_change_event=set(
|
|
266
|
-
{NodeResolutionState.UNRESOLVED, NodeResolutionState.RESOLVED, NodeResolutionState.RESOLVING}
|
|
267
|
-
)
|
|
268
|
-
)
|
|
269
|
-
current_focus.process_generator = None
|
|
270
|
-
current_focus.scheduled_value = None
|
|
271
|
-
|
|
272
|
-
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
273
|
-
|
|
274
|
-
GriptapeNodes.FlowManager().cancel_flow_run()
|
|
275
|
-
|
|
293
|
+
# If the node is not locked, execute all of this.
|
|
294
|
+
if not current_node.lock:
|
|
295
|
+
# To set the event manager without circular import errors
|
|
276
296
|
EventBus.publish_event(
|
|
277
297
|
ExecutionGriptapeNodeEvent(
|
|
278
|
-
wrapped_event=ExecutionEvent(payload=
|
|
298
|
+
wrapped_event=ExecutionEvent(payload=NodeStartProcessEvent(node_name=current_node.name))
|
|
279
299
|
)
|
|
280
300
|
)
|
|
281
|
-
|
|
301
|
+
logger.info("Node '%s' is processing.", current_node.name)
|
|
282
302
|
|
|
283
|
-
|
|
303
|
+
try:
|
|
304
|
+
work_is_scheduled = ExecuteNodeState._process_node(current_focus)
|
|
305
|
+
if work_is_scheduled:
|
|
306
|
+
logger.debug("Pausing Node '%s' to run background work", current_node.name)
|
|
307
|
+
return None
|
|
308
|
+
except Exception as e:
|
|
309
|
+
logger.exception("Error processing node '%s", current_node.name)
|
|
310
|
+
msg = f"Canceling flow run. Node '{current_node.name}' encountered a problem: {e}"
|
|
311
|
+
# Mark the node as unresolved, broadcasting to everyone.
|
|
312
|
+
current_node.make_node_unresolved(
|
|
313
|
+
current_states_to_trigger_change_event=set(
|
|
314
|
+
{NodeResolutionState.UNRESOLVED, NodeResolutionState.RESOLVED, NodeResolutionState.RESOLVING}
|
|
315
|
+
)
|
|
316
|
+
)
|
|
317
|
+
current_focus.process_generator = None
|
|
318
|
+
current_focus.scheduled_value = None
|
|
284
319
|
|
|
285
|
-
|
|
286
|
-
ExecutionGriptapeNodeEvent(
|
|
287
|
-
wrapped_event=ExecutionEvent(payload=NodeFinishProcessEvent(node_name=current_node.name))
|
|
288
|
-
)
|
|
289
|
-
)
|
|
290
|
-
current_node.state = NodeResolutionState.RESOLVED
|
|
291
|
-
details = f"'{current_node.name}' resolved."
|
|
320
|
+
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
292
321
|
|
|
293
|
-
|
|
322
|
+
GriptapeNodes.FlowManager().cancel_flow_run()
|
|
294
323
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
324
|
+
EventBus.publish_event(
|
|
325
|
+
ExecutionGriptapeNodeEvent(
|
|
326
|
+
wrapped_event=ExecutionEvent(payload=NodeFinishProcessEvent(node_name=current_node.name))
|
|
327
|
+
)
|
|
328
|
+
)
|
|
329
|
+
raise RuntimeError(msg) from e
|
|
330
|
+
|
|
331
|
+
logger.info("Node '%s' finished processing.", current_node.name)
|
|
302
332
|
|
|
303
|
-
for parameter_name, value in current_node.parameter_output_values.items():
|
|
304
|
-
parameter = current_node.get_parameter_by_name(parameter_name)
|
|
305
|
-
if parameter is None:
|
|
306
|
-
err = f"Canceling flow run. Node '{current_node.name}' specified a Parameter '{parameter_name}', but no such Parameter could be found on that Node."
|
|
307
|
-
raise KeyError(err)
|
|
308
|
-
data_type = parameter.type
|
|
309
|
-
if data_type is None:
|
|
310
|
-
data_type = ParameterTypeBuiltin.NONE.value
|
|
311
333
|
EventBus.publish_event(
|
|
312
334
|
ExecutionGriptapeNodeEvent(
|
|
313
|
-
wrapped_event=ExecutionEvent(
|
|
314
|
-
payload=ParameterValueUpdateEvent(
|
|
315
|
-
node_name=current_node.name,
|
|
316
|
-
parameter_name=parameter_name,
|
|
317
|
-
data_type=data_type,
|
|
318
|
-
value=TypeValidator.safe_serialize(value),
|
|
319
|
-
)
|
|
320
|
-
),
|
|
335
|
+
wrapped_event=ExecutionEvent(payload=NodeFinishProcessEvent(node_name=current_node.name))
|
|
321
336
|
)
|
|
322
337
|
)
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
)
|
|
338
|
+
current_node.state = NodeResolutionState.RESOLVED
|
|
339
|
+
details = f"'{current_node.name}' resolved."
|
|
340
|
+
|
|
341
|
+
logger.info(details)
|
|
342
|
+
|
|
343
|
+
# Serialization can be slow so only do it if the user wants debug details.
|
|
344
|
+
if logger.level <= logging.DEBUG:
|
|
345
|
+
logger.debug(
|
|
346
|
+
"INPUTS: %s\nOUTPUTS: %s",
|
|
347
|
+
TypeValidator.safe_serialize(current_node.parameter_values),
|
|
348
|
+
TypeValidator.safe_serialize(current_node.parameter_output_values),
|
|
333
349
|
)
|
|
334
350
|
|
|
335
|
-
|
|
351
|
+
for parameter_name, value in current_node.parameter_output_values.items():
|
|
352
|
+
parameter = current_node.get_parameter_by_name(parameter_name)
|
|
353
|
+
if parameter is None:
|
|
354
|
+
err = f"Canceling flow run. Node '{current_node.name}' specified a Parameter '{parameter_name}', but no such Parameter could be found on that Node."
|
|
355
|
+
raise KeyError(err)
|
|
356
|
+
data_type = parameter.type
|
|
357
|
+
if data_type is None:
|
|
358
|
+
data_type = ParameterTypeBuiltin.NONE.value
|
|
359
|
+
EventBus.publish_event(
|
|
360
|
+
ExecutionGriptapeNodeEvent(
|
|
361
|
+
wrapped_event=ExecutionEvent(
|
|
362
|
+
payload=ParameterValueUpdateEvent(
|
|
363
|
+
node_name=current_node.name,
|
|
364
|
+
parameter_name=parameter_name,
|
|
365
|
+
data_type=data_type,
|
|
366
|
+
value=TypeValidator.safe_serialize(value),
|
|
367
|
+
)
|
|
368
|
+
),
|
|
369
|
+
)
|
|
370
|
+
)
|
|
371
|
+
# Output values should already be saved!
|
|
336
372
|
library = LibraryRegistry.get_libraries_with_node_type(current_node.__class__.__name__)
|
|
337
373
|
if len(library) == 1:
|
|
338
374
|
library_name = library[0]
|
|
File without changes
|
|
@@ -57,19 +57,21 @@ SUPPORTED_REQUEST_EVENTS: dict[str, type[RequestPayload]] = {
|
|
|
57
57
|
"SetParameterValueRequest": SetParameterValueRequest,
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
GTN_MCP_SERVER_HOST = os.getenv("GTN_MCP_SERVER_HOST", "localhost")
|
|
60
61
|
GTN_MCP_SERVER_PORT = int(os.getenv("GTN_MCP_SERVER_PORT", "9927"))
|
|
62
|
+
GTN_MCP_SERVER_LOG_LEVEL = os.getenv("GTN_MCP_SERVER_LOG_LEVEL", "ERROR").lower()
|
|
61
63
|
|
|
62
64
|
config_manager = ConfigManager()
|
|
63
65
|
secrets_manager = SecretsManager(config_manager)
|
|
64
66
|
|
|
67
|
+
mcp_server_logger = logging.getLogger("griptape_nodes_mcp_server")
|
|
68
|
+
mcp_server_logger.addHandler(RichHandler(show_time=True, show_path=False, markup=True, rich_tracebacks=True))
|
|
69
|
+
mcp_server_logger.setLevel(logging.INFO)
|
|
70
|
+
|
|
65
71
|
|
|
66
72
|
def main(api_key: str) -> None:
|
|
67
73
|
"""Main entry point for the Griptape Nodes MCP server."""
|
|
68
|
-
mcp_server_logger
|
|
69
|
-
mcp_server_logger.addHandler(RichHandler(show_time=True, show_path=False, markup=True, rich_tracebacks=True))
|
|
70
|
-
mcp_server_logger.setLevel(logging.INFO)
|
|
71
|
-
mcp_server_logger.info("Starting MCP GTN server...")
|
|
72
|
-
|
|
74
|
+
mcp_server_logger.debug("Starting MCP GTN server...")
|
|
73
75
|
# Give these a session ID
|
|
74
76
|
connection_manager = WebSocketConnectionManager()
|
|
75
77
|
request_manager = AsyncRequestManager(connection_manager, api_key)
|
|
@@ -108,11 +110,11 @@ def main(api_key: str) -> None:
|
|
|
108
110
|
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
|
|
109
111
|
"""Context manager for managing session manager lifecycle."""
|
|
110
112
|
async with session_manager.run():
|
|
111
|
-
mcp_server_logger.
|
|
113
|
+
mcp_server_logger.debug("GTN MCP server started with StreamableHTTP session manager!")
|
|
112
114
|
try:
|
|
113
115
|
yield
|
|
114
116
|
finally:
|
|
115
|
-
mcp_server_logger.
|
|
117
|
+
mcp_server_logger.debug("GTN MCP server shutting down...")
|
|
116
118
|
|
|
117
119
|
# Create an ASGI application using the transport
|
|
118
120
|
mcp_server_app = FastAPI(lifespan=lifespan)
|
|
@@ -123,4 +125,10 @@ def main(api_key: str) -> None:
|
|
|
123
125
|
|
|
124
126
|
mcp_server_app.mount("/mcp", app=handle_streamable_http)
|
|
125
127
|
|
|
126
|
-
uvicorn.run(
|
|
128
|
+
uvicorn.run(
|
|
129
|
+
mcp_server_app,
|
|
130
|
+
host=GTN_MCP_SERVER_HOST,
|
|
131
|
+
port=GTN_MCP_SERVER_PORT,
|
|
132
|
+
log_config=None,
|
|
133
|
+
log_level=GTN_MCP_SERVER_LOG_LEVEL,
|
|
134
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -26,6 +26,7 @@ class WorkflowMetadata(BaseModel):
|
|
|
26
26
|
is_template: bool | None = False
|
|
27
27
|
creation_date: datetime | None = Field(default=None)
|
|
28
28
|
last_modified_date: datetime | None = Field(default=None)
|
|
29
|
+
branched_from: str | None = Field(default=None)
|
|
29
30
|
|
|
30
31
|
|
|
31
32
|
class WorkflowRegistry(metaclass=SingletonMeta):
|
|
@@ -81,6 +82,16 @@ class WorkflowRegistry(metaclass=SingletonMeta):
|
|
|
81
82
|
raise KeyError(msg)
|
|
82
83
|
return instance._workflows.pop(name)
|
|
83
84
|
|
|
85
|
+
@classmethod
|
|
86
|
+
def get_branches_of_workflow(cls, workflow_name: str) -> list[str]:
|
|
87
|
+
"""Get all workflows that are branches of the specified workflow."""
|
|
88
|
+
instance = cls()
|
|
89
|
+
branches = []
|
|
90
|
+
for name, workflow in instance._workflows.items():
|
|
91
|
+
if workflow.metadata.branched_from == workflow_name:
|
|
92
|
+
branches.append(name)
|
|
93
|
+
return branches
|
|
94
|
+
|
|
84
95
|
|
|
85
96
|
class Workflow:
|
|
86
97
|
"""A workflow card to be ran."""
|
|
@@ -102,6 +113,23 @@ class Workflow:
|
|
|
102
113
|
msg = f"File path '{complete_path}' does not exist."
|
|
103
114
|
raise ValueError(msg)
|
|
104
115
|
|
|
116
|
+
@property
|
|
117
|
+
def is_synced(self) -> bool:
|
|
118
|
+
"""Check if this workflow is in the synced workflows directory."""
|
|
119
|
+
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
120
|
+
|
|
121
|
+
config_mgr = GriptapeNodes.ConfigManager()
|
|
122
|
+
synced_directory = config_mgr.get_config_value("synced_workflows_directory")
|
|
123
|
+
|
|
124
|
+
# Get the full path to the synced workflows directory
|
|
125
|
+
synced_path = config_mgr.get_full_path(synced_directory)
|
|
126
|
+
|
|
127
|
+
# Get the complete file path for this workflow
|
|
128
|
+
complete_file_path = WorkflowRegistry.get_complete_file_path(self.file_path)
|
|
129
|
+
|
|
130
|
+
# Check if the workflow file is within the synced directory
|
|
131
|
+
return Path(complete_file_path).is_relative_to(synced_path)
|
|
132
|
+
|
|
105
133
|
def get_workflow_metadata(self) -> dict:
|
|
106
134
|
# Convert from the Pydantic schema.
|
|
107
135
|
ret_val = {**self.metadata.model_dump()}
|
|
@@ -109,4 +137,5 @@ class Workflow:
|
|
|
109
137
|
# The schema doesn't have the file path in it, because it is baked into the file itself.
|
|
110
138
|
# Customers of this function need that, so let's stuff it in.
|
|
111
139
|
ret_val["file_path"] = self.file_path
|
|
140
|
+
ret_val["is_synced"] = self.is_synced
|
|
112
141
|
return ret_val
|
griptape_nodes/py.typed
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -5,6 +5,7 @@ from griptape_nodes.retained_mode.events.base_events import (
|
|
|
5
5
|
RequestPayload,
|
|
6
6
|
ResultPayloadFailure,
|
|
7
7
|
ResultPayloadSuccess,
|
|
8
|
+
SkipTheLineMixin,
|
|
8
9
|
WorkflowNotAlteredMixin,
|
|
9
10
|
)
|
|
10
11
|
from griptape_nodes.retained_mode.events.payload_registry import PayloadRegistry
|
|
@@ -18,15 +19,9 @@ class AppStartSessionRequest(RequestPayload):
|
|
|
18
19
|
Use when: Initializing client connections, beginning new workflow sessions,
|
|
19
20
|
setting up isolated execution environments, managing session state.
|
|
20
21
|
|
|
21
|
-
Args:
|
|
22
|
-
session_id: Specific session ID to use (None for auto-generated)
|
|
23
|
-
|
|
24
22
|
Results: AppStartSessionResultSuccess (with session ID) | AppStartSessionResultFailure (session creation error)
|
|
25
23
|
"""
|
|
26
24
|
|
|
27
|
-
# TODO: https://github.com/griptape-ai/griptape-nodes/issues/1600
|
|
28
|
-
session_id: str | None = None
|
|
29
|
-
|
|
30
25
|
|
|
31
26
|
@dataclass
|
|
32
27
|
@PayloadRegistry.register
|
|
@@ -154,7 +149,7 @@ class AppEndSessionResultFailure(ResultPayloadFailure):
|
|
|
154
149
|
|
|
155
150
|
@dataclass
|
|
156
151
|
@PayloadRegistry.register
|
|
157
|
-
class SessionHeartbeatRequest(RequestPayload):
|
|
152
|
+
class SessionHeartbeatRequest(RequestPayload, SkipTheLineMixin):
|
|
158
153
|
"""Request clients can use ensure the engine session is still active."""
|
|
159
154
|
|
|
160
155
|
|
|
@@ -172,7 +167,7 @@ class SessionHeartbeatResultFailure(ResultPayloadFailure):
|
|
|
172
167
|
|
|
173
168
|
@dataclass
|
|
174
169
|
@PayloadRegistry.register
|
|
175
|
-
class EngineHeartbeatRequest(RequestPayload):
|
|
170
|
+
class EngineHeartbeatRequest(RequestPayload, SkipTheLineMixin):
|
|
176
171
|
"""Request clients can use to discover active engines and their status.
|
|
177
172
|
|
|
178
173
|
Attributes:
|
|
File without changes
|
|
@@ -10,7 +10,7 @@ from griptape.events import BaseEvent as GtBaseEvent
|
|
|
10
10
|
from griptape.mixins.serializable_mixin import SerializableMixin
|
|
11
11
|
from griptape.structures import Structure
|
|
12
12
|
from griptape.tools import BaseTool
|
|
13
|
-
from pydantic import BaseModel, Field
|
|
13
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
16
16
|
import builtins
|
|
@@ -62,6 +62,15 @@ class WorkflowNotAlteredMixin:
|
|
|
62
62
|
altered_workflow_state: bool = field(default=False, init=False)
|
|
63
63
|
|
|
64
64
|
|
|
65
|
+
class SkipTheLineMixin:
|
|
66
|
+
"""Mixin for events that should skip the event queue and be processed immediately.
|
|
67
|
+
|
|
68
|
+
Events that implement this mixin will be handled directly without being added
|
|
69
|
+
to the event queue, allowing for priority processing of critical events like
|
|
70
|
+
heartbeats or other time-sensitive operations.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
|
|
65
74
|
# Success result payload abstract base class
|
|
66
75
|
@dataclass(kw_only=True)
|
|
67
76
|
class ResultPayloadSuccess(ResultPayload, ABC):
|
|
@@ -118,16 +127,15 @@ class BaseEvent(BaseModel, ABC):
|
|
|
118
127
|
session_id: str | None = Field(default_factory=lambda: BaseEvent._session_id)
|
|
119
128
|
|
|
120
129
|
# Custom JSON encoder for the payload
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
arbitrary_types_allowed = True
|
|
125
|
-
json_encoders: ClassVar[dict] = {
|
|
130
|
+
model_config = ConfigDict(
|
|
131
|
+
arbitrary_types_allowed=True,
|
|
132
|
+
json_encoders={
|
|
126
133
|
# Use to_dict() methods for Griptape objects
|
|
127
134
|
BaseArtifact: lambda obj: obj.to_dict(),
|
|
128
135
|
BaseTool: lambda obj: obj.to_dict(),
|
|
129
136
|
Structure: lambda obj: obj.to_dict(),
|
|
130
|
-
}
|
|
137
|
+
},
|
|
138
|
+
)
|
|
131
139
|
|
|
132
140
|
def dict(self, *args, **kwargs) -> dict[str, Any]:
|
|
133
141
|
"""Override dict to handle payload serialization and add event_type."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -9,7 +9,7 @@ from griptape_nodes.retained_mode.events.base_events import (
|
|
|
9
9
|
WorkflowAlteredMixin,
|
|
10
10
|
WorkflowNotAlteredMixin,
|
|
11
11
|
)
|
|
12
|
-
from griptape_nodes.retained_mode.events.node_events import SerializedNodeCommands
|
|
12
|
+
from griptape_nodes.retained_mode.events.node_events import SerializedNodeCommands, SetLockNodeStateRequest
|
|
13
13
|
from griptape_nodes.retained_mode.events.payload_registry import PayloadRegistry
|
|
14
14
|
from griptape_nodes.retained_mode.events.workflow_events import ImportWorkflowAsReferencedSubFlowRequest
|
|
15
15
|
|
|
@@ -229,6 +229,7 @@ class SerializedFlowCommands:
|
|
|
229
229
|
set_parameter_value_commands: dict[
|
|
230
230
|
SerializedNodeCommands.NodeUUID, list[SerializedNodeCommands.IndirectSetParameterValueCommand]
|
|
231
231
|
]
|
|
232
|
+
set_lock_commands_per_node: dict[SerializedNodeCommands.NodeUUID, SetLockNodeStateRequest]
|
|
232
233
|
sub_flows_commands: list["SerializedFlowCommands"]
|
|
233
234
|
referenced_workflows: set[str]
|
|
234
235
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -303,6 +303,7 @@ class GetAllNodeInfoResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess)
|
|
|
303
303
|
|
|
304
304
|
metadata: dict
|
|
305
305
|
node_resolution_state: str
|
|
306
|
+
locked: bool
|
|
306
307
|
connections: ListConnectionsForNodeResultSuccess
|
|
307
308
|
element_id_to_value: dict[str, ParameterInfoValue]
|
|
308
309
|
root_node_element: dict[str, Any]
|
|
@@ -317,6 +318,39 @@ class GetAllNodeInfoResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure)
|
|
|
317
318
|
"""
|
|
318
319
|
|
|
319
320
|
|
|
321
|
+
@dataclass
|
|
322
|
+
@PayloadRegistry.register
|
|
323
|
+
class SetLockNodeStateRequest(WorkflowNotAlteredMixin, RequestPayload):
|
|
324
|
+
"""Lock a node.
|
|
325
|
+
|
|
326
|
+
Use when: Implementing locking functionality, preventing changes to nodes.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
node_name: Name of the node to lock
|
|
330
|
+
lock: Whether to lock or unlock the node. If true, the node will be locked, otherwise it will be unlocked.
|
|
331
|
+
|
|
332
|
+
Results: SetLockNodeStateResultSuccess (node locked) | SetLockNodeStateResultFailure (node not found)
|
|
333
|
+
"""
|
|
334
|
+
|
|
335
|
+
node_name: str | None
|
|
336
|
+
lock: bool
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
@dataclass
|
|
340
|
+
@PayloadRegistry.register
|
|
341
|
+
class SetLockNodeStateResultSuccess(WorkflowNotAlteredMixin, ResultPayloadSuccess):
|
|
342
|
+
"""Node locked successfully."""
|
|
343
|
+
|
|
344
|
+
node_name: str
|
|
345
|
+
locked: bool
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
@dataclass
|
|
349
|
+
@PayloadRegistry.register
|
|
350
|
+
class SetLockNodeStateResultFailure(WorkflowNotAlteredMixin, ResultPayloadFailure):
|
|
351
|
+
"""Node failed to lock."""
|
|
352
|
+
|
|
353
|
+
|
|
320
354
|
# A Node's state can be serialized to a sequence of commands that the engine runs.
|
|
321
355
|
@dataclass
|
|
322
356
|
class SerializedNodeCommands:
|
|
@@ -354,6 +388,7 @@ class SerializedNodeCommands:
|
|
|
354
388
|
create_node_command: CreateNodeRequest
|
|
355
389
|
element_modification_commands: list[RequestPayload]
|
|
356
390
|
node_library_details: LibraryNameAndVersion
|
|
391
|
+
lock_node_command: SetLockNodeStateRequest | None = None
|
|
357
392
|
node_uuid: NodeUUID = field(default_factory=lambda: SerializedNodeCommands.NodeUUID(str(uuid4())))
|
|
358
393
|
|
|
359
394
|
|
|
@@ -477,6 +512,7 @@ class SerializedSelectedNodesCommands:
|
|
|
477
512
|
set_parameter_value_commands: dict[
|
|
478
513
|
SerializedNodeCommands.NodeUUID, list[SerializedNodeCommands.IndirectSetParameterValueCommand]
|
|
479
514
|
]
|
|
515
|
+
set_lock_commands_per_node: dict[SerializedNodeCommands.NodeUUID, SetLockNodeStateRequest]
|
|
480
516
|
serialized_connection_commands: list[IndirectConnectionSerialization]
|
|
481
517
|
|
|
482
518
|
|
|
File without changes
|