griptape-nodes 0.56.0__py3-none-any.whl → 0.57.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- griptape_nodes/app/app.py +10 -15
- griptape_nodes/app/watch.py +35 -67
- griptape_nodes/bootstrap/utils/__init__.py +1 -0
- griptape_nodes/bootstrap/utils/python_subprocess_executor.py +122 -0
- griptape_nodes/bootstrap/workflow_executors/local_session_workflow_executor.py +418 -0
- griptape_nodes/bootstrap/workflow_executors/local_workflow_executor.py +37 -8
- griptape_nodes/bootstrap/workflow_executors/subprocess_workflow_executor.py +326 -0
- griptape_nodes/bootstrap/workflow_executors/utils/__init__.py +1 -0
- griptape_nodes/bootstrap/workflow_executors/utils/subprocess_script.py +51 -0
- griptape_nodes/bootstrap/workflow_publishers/__init__.py +1 -0
- griptape_nodes/bootstrap/workflow_publishers/local_workflow_publisher.py +43 -0
- griptape_nodes/bootstrap/workflow_publishers/subprocess_workflow_publisher.py +84 -0
- griptape_nodes/bootstrap/workflow_publishers/utils/__init__.py +1 -0
- griptape_nodes/bootstrap/workflow_publishers/utils/subprocess_script.py +54 -0
- griptape_nodes/cli/commands/engine.py +4 -15
- griptape_nodes/cli/main.py +6 -1
- griptape_nodes/exe_types/core_types.py +26 -0
- griptape_nodes/exe_types/node_types.py +116 -1
- griptape_nodes/retained_mode/events/agent_events.py +2 -0
- griptape_nodes/retained_mode/events/base_events.py +18 -17
- griptape_nodes/retained_mode/events/execution_events.py +3 -1
- griptape_nodes/retained_mode/events/flow_events.py +5 -7
- griptape_nodes/retained_mode/events/mcp_events.py +363 -0
- griptape_nodes/retained_mode/events/node_events.py +3 -4
- griptape_nodes/retained_mode/griptape_nodes.py +8 -0
- griptape_nodes/retained_mode/managers/agent_manager.py +67 -4
- griptape_nodes/retained_mode/managers/event_manager.py +31 -13
- griptape_nodes/retained_mode/managers/flow_manager.py +76 -44
- griptape_nodes/retained_mode/managers/library_manager.py +7 -9
- griptape_nodes/retained_mode/managers/mcp_manager.py +364 -0
- griptape_nodes/retained_mode/managers/node_manager.py +12 -1
- griptape_nodes/retained_mode/managers/settings.py +40 -0
- griptape_nodes/retained_mode/managers/workflow_manager.py +94 -8
- griptape_nodes/traits/multi_options.py +5 -1
- griptape_nodes/traits/options.py +10 -2
- {griptape_nodes-0.56.0.dist-info → griptape_nodes-0.57.0.dist-info}/METADATA +2 -2
- {griptape_nodes-0.56.0.dist-info → griptape_nodes-0.57.0.dist-info}/RECORD +39 -26
- {griptape_nodes-0.56.0.dist-info → griptape_nodes-0.57.0.dist-info}/WHEEL +0 -0
- {griptape_nodes-0.56.0.dist-info → griptape_nodes-0.57.0.dist-info}/entry_points.txt +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import logging
|
|
3
4
|
import uuid
|
|
4
5
|
from abc import ABC, abstractmethod
|
|
5
6
|
from copy import deepcopy
|
|
@@ -9,6 +10,8 @@ from typing import TYPE_CHECKING, Any, ClassVar, Literal, NamedTuple, Self, Type
|
|
|
9
10
|
|
|
10
11
|
from pydantic import BaseModel
|
|
11
12
|
|
|
13
|
+
logger = logging.getLogger("griptape_nodes")
|
|
14
|
+
|
|
12
15
|
|
|
13
16
|
class NodeMessagePayload(BaseModel):
|
|
14
17
|
"""Structured payload for node messages.
|
|
@@ -554,6 +557,7 @@ class ParameterMessage(BaseNodeElement, UIOptionsMixin):
|
|
|
554
557
|
button_align: ButtonAlignType = "full-width",
|
|
555
558
|
full_width: bool = False,
|
|
556
559
|
ui_options: dict | None = None,
|
|
560
|
+
traits: set[Trait.__class__ | Trait] | None = None,
|
|
557
561
|
**kwargs,
|
|
558
562
|
):
|
|
559
563
|
super().__init__(element_type=ParameterMessage.__name__, **kwargs)
|
|
@@ -569,6 +573,17 @@ class ParameterMessage(BaseNodeElement, UIOptionsMixin):
|
|
|
569
573
|
self._full_width = full_width
|
|
570
574
|
self._ui_options = ui_options or {}
|
|
571
575
|
|
|
576
|
+
# Handle traits if provided
|
|
577
|
+
if traits:
|
|
578
|
+
for trait in traits:
|
|
579
|
+
if isinstance(trait, type):
|
|
580
|
+
# It's a trait class, instantiate it
|
|
581
|
+
trait_instance = trait()
|
|
582
|
+
else:
|
|
583
|
+
# It's already a trait instance
|
|
584
|
+
trait_instance = trait
|
|
585
|
+
self.add_child(trait_instance)
|
|
586
|
+
|
|
572
587
|
@property
|
|
573
588
|
def variant(self) -> VariantType:
|
|
574
589
|
return self._variant
|
|
@@ -694,6 +709,16 @@ class ParameterMessage(BaseNodeElement, UIOptionsMixin):
|
|
|
694
709
|
else:
|
|
695
710
|
button_icon = self.button_icon
|
|
696
711
|
|
|
712
|
+
# Check if there are any Button traits with on_click callbacks
|
|
713
|
+
has_button_callback = False
|
|
714
|
+
for child in self.children:
|
|
715
|
+
# Import here to avoid circular imports
|
|
716
|
+
from griptape_nodes.traits.button import Button
|
|
717
|
+
|
|
718
|
+
if isinstance(child, Button) and child.on_click_callback is not None:
|
|
719
|
+
has_button_callback = True
|
|
720
|
+
break
|
|
721
|
+
|
|
697
722
|
# Merge the UI options with the message-specific options
|
|
698
723
|
# Always include these fields, even if they're None or empty
|
|
699
724
|
message_ui_options = {
|
|
@@ -705,6 +730,7 @@ class ParameterMessage(BaseNodeElement, UIOptionsMixin):
|
|
|
705
730
|
"button_icon": button_icon,
|
|
706
731
|
"button_variant": self.button_variant,
|
|
707
732
|
"button_align": self.button_align,
|
|
733
|
+
"button_on_click": has_button_callback,
|
|
708
734
|
"full_width": self.full_width,
|
|
709
735
|
}
|
|
710
736
|
|
|
@@ -5,8 +5,9 @@ import logging
|
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
6
|
from collections.abc import Callable, Generator, Iterable
|
|
7
7
|
from concurrent.futures import ThreadPoolExecutor
|
|
8
|
+
from dataclasses import dataclass, field
|
|
8
9
|
from enum import StrEnum, auto
|
|
9
|
-
from typing import TYPE_CHECKING, Any, TypeVar
|
|
10
|
+
from typing import TYPE_CHECKING, Any, NamedTuple, TypeVar
|
|
10
11
|
|
|
11
12
|
from griptape_nodes.exe_types.core_types import (
|
|
12
13
|
BaseNodeElement,
|
|
@@ -43,6 +44,7 @@ from griptape_nodes.traits.options import Options
|
|
|
43
44
|
|
|
44
45
|
if TYPE_CHECKING:
|
|
45
46
|
from griptape_nodes.exe_types.core_types import NodeMessagePayload
|
|
47
|
+
from griptape_nodes.node_library.library_registry import LibraryNameAndVersion
|
|
46
48
|
|
|
47
49
|
logger = logging.getLogger("griptape_nodes")
|
|
48
50
|
|
|
@@ -50,6 +52,53 @@ T = TypeVar("T")
|
|
|
50
52
|
|
|
51
53
|
AsyncResult = Generator[Callable[[], T], T]
|
|
52
54
|
|
|
55
|
+
LOCAL_EXECUTION = "Local Execution"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class ImportDependency(NamedTuple):
|
|
59
|
+
"""Import dependency specification for a node.
|
|
60
|
+
|
|
61
|
+
Attributes:
|
|
62
|
+
module: The module name to import
|
|
63
|
+
class_name: Optional class name to import from the module. If None, imports the entire module.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
module: str
|
|
67
|
+
class_name: str | None = None
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass
|
|
71
|
+
class NodeDependencies:
|
|
72
|
+
"""Dependencies that a node has on external resources.
|
|
73
|
+
|
|
74
|
+
This class provides a way for nodes to declare their dependencies on workflows,
|
|
75
|
+
static files, Python imports, and libraries. This information can be used by the system
|
|
76
|
+
for workflow packaging, dependency resolution, and deployment planning.
|
|
77
|
+
|
|
78
|
+
Attributes:
|
|
79
|
+
referenced_workflows: Set of workflow names that this node references
|
|
80
|
+
static_files: Set of static file names that this node depends on
|
|
81
|
+
imports: Set of Python imports that this node requires
|
|
82
|
+
libraries: Set of library names and versions that this node uses
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
referenced_workflows: set[str] = field(default_factory=set)
|
|
86
|
+
static_files: set[str] = field(default_factory=set)
|
|
87
|
+
imports: set[ImportDependency] = field(default_factory=set)
|
|
88
|
+
libraries: set[LibraryNameAndVersion] = field(default_factory=set)
|
|
89
|
+
|
|
90
|
+
def aggregate_from(self, other: NodeDependencies) -> None:
|
|
91
|
+
"""Aggregate dependencies from another NodeDependencies object into this one.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
other: The NodeDependencies object to aggregate from
|
|
95
|
+
"""
|
|
96
|
+
# Aggregate all dependency types - no None checks needed since we use default_factory=set
|
|
97
|
+
self.referenced_workflows.update(other.referenced_workflows)
|
|
98
|
+
self.static_files.update(other.static_files)
|
|
99
|
+
self.imports.update(other.imports)
|
|
100
|
+
self.libraries.update(other.libraries)
|
|
101
|
+
|
|
53
102
|
|
|
54
103
|
class NodeResolutionState(StrEnum):
|
|
55
104
|
"""Possible states for a node during resolution."""
|
|
@@ -59,6 +108,23 @@ class NodeResolutionState(StrEnum):
|
|
|
59
108
|
RESOLVED = auto()
|
|
60
109
|
|
|
61
110
|
|
|
111
|
+
def get_library_names_with_publish_handlers() -> list[str]:
|
|
112
|
+
"""Get names of all registered libraries that have PublishWorkflowRequest handlers."""
|
|
113
|
+
from griptape_nodes.retained_mode.events.workflow_events import PublishWorkflowRequest
|
|
114
|
+
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
115
|
+
|
|
116
|
+
library_manager = GriptapeNodes.LibraryManager()
|
|
117
|
+
event_handlers = library_manager.get_registered_event_handlers(PublishWorkflowRequest)
|
|
118
|
+
|
|
119
|
+
# Always include "local" as the first option
|
|
120
|
+
library_names = [LOCAL_EXECUTION]
|
|
121
|
+
|
|
122
|
+
# Add all registered library names that can handle PublishWorkflowRequest
|
|
123
|
+
library_names.extend(sorted(event_handlers.keys()))
|
|
124
|
+
|
|
125
|
+
return library_names
|
|
126
|
+
|
|
127
|
+
|
|
62
128
|
class BaseNode(ABC):
|
|
63
129
|
# Owned by a flow
|
|
64
130
|
name: str
|
|
@@ -104,6 +170,16 @@ class BaseNode(ABC):
|
|
|
104
170
|
self.process_generator = None
|
|
105
171
|
self._tracked_parameters = []
|
|
106
172
|
self.set_entry_control_parameter(None)
|
|
173
|
+
self.execution_environment = Parameter(
|
|
174
|
+
name="execution_environment",
|
|
175
|
+
tooltip="Environment that the node should execute in",
|
|
176
|
+
type=ParameterTypeBuiltin.STR,
|
|
177
|
+
allowed_modes={ParameterMode.PROPERTY},
|
|
178
|
+
default_value=LOCAL_EXECUTION,
|
|
179
|
+
traits={Options(choices=get_library_names_with_publish_handlers())},
|
|
180
|
+
ui_options={"hide": True},
|
|
181
|
+
)
|
|
182
|
+
self.add_parameter(self.execution_environment)
|
|
107
183
|
|
|
108
184
|
# This is gross and we need to have a universal pass on resolution state changes and emission of events. That's what this ticket does!
|
|
109
185
|
# https://github.com/griptape-ai/griptape-nodes/issues/994
|
|
@@ -819,6 +895,35 @@ class BaseNode(ABC):
|
|
|
819
895
|
# Then clear the reference to the first spotlight parameter
|
|
820
896
|
self.current_spotlight_parameter = None
|
|
821
897
|
|
|
898
|
+
def get_node_dependencies(self) -> NodeDependencies | None:
|
|
899
|
+
"""Return the dependencies that this node has on external resources.
|
|
900
|
+
|
|
901
|
+
This method should be overridden by nodes that have dependencies on:
|
|
902
|
+
- Referenced workflows: Other workflows that this node calls or references
|
|
903
|
+
- Static files: Files that this node reads from or requires for operation
|
|
904
|
+
- Python imports: Modules or classes that this node imports beyond standard dependencies
|
|
905
|
+
|
|
906
|
+
This information can be used by the system for workflow packaging, dependency
|
|
907
|
+
resolution, deployment planning, and ensuring all required resources are available.
|
|
908
|
+
|
|
909
|
+
Returns:
|
|
910
|
+
NodeDependencies object containing the node's dependencies, or None if the node
|
|
911
|
+
has no external dependencies beyond the standard framework dependencies.
|
|
912
|
+
|
|
913
|
+
Example:
|
|
914
|
+
def get_node_dependencies(self) -> NodeDependencies | None:
|
|
915
|
+
return NodeDependencies(
|
|
916
|
+
referenced_workflows={"image_processing_workflow", "validation_workflow"},
|
|
917
|
+
static_files={"config.json", "model_weights.pkl"},
|
|
918
|
+
imports={
|
|
919
|
+
ImportDependency("numpy"),
|
|
920
|
+
ImportDependency("sklearn.linear_model", "LinearRegression"),
|
|
921
|
+
ImportDependency("custom_module", "SpecialProcessor")
|
|
922
|
+
}
|
|
923
|
+
)
|
|
924
|
+
"""
|
|
925
|
+
return None
|
|
926
|
+
|
|
822
927
|
def append_value_to_parameter(self, parameter_name: str, value: Any) -> None:
|
|
823
928
|
from griptape_nodes.retained_mode.griptape_nodes import GriptapeNodes
|
|
824
929
|
|
|
@@ -1390,6 +1495,16 @@ class EndNode(BaseNode):
|
|
|
1390
1495
|
|
|
1391
1496
|
self.status_component.set_execution_result(was_successful=was_successful, result_details=details)
|
|
1392
1497
|
|
|
1498
|
+
# Update all values to use the output value
|
|
1499
|
+
for param in self.parameters:
|
|
1500
|
+
if param.type != ParameterTypeBuiltin.CONTROL_TYPE:
|
|
1501
|
+
value = self.get_parameter_value(param.name)
|
|
1502
|
+
self.parameter_output_values[param.name] = value
|
|
1503
|
+
next_control_output = self.get_next_control_output()
|
|
1504
|
+
# Update which control parameter to flag as the output value.
|
|
1505
|
+
if next_control_output is not None:
|
|
1506
|
+
self.parameter_output_values[next_control_output.name] = 1
|
|
1507
|
+
|
|
1393
1508
|
|
|
1394
1509
|
class StartLoopNode(BaseNode):
|
|
1395
1510
|
end_node: EndLoopNode | None = None
|
|
@@ -29,12 +29,14 @@ class RunAgentRequest(RequestPayload):
|
|
|
29
29
|
Args:
|
|
30
30
|
input: Text input to send to the agent
|
|
31
31
|
url_artifacts: List of URL artifacts to include with the request
|
|
32
|
+
additional_mcp_servers: List of additional MCP server names to include
|
|
32
33
|
|
|
33
34
|
Results: RunAgentResultStarted -> RunAgentResultSuccess (with output) | RunAgentResultFailure (execution error)
|
|
34
35
|
"""
|
|
35
36
|
|
|
36
37
|
input: str
|
|
37
38
|
url_artifacts: list[RunAgentRequestArtifact]
|
|
39
|
+
additional_mcp_servers: list[str] = field(default_factory=list)
|
|
38
40
|
|
|
39
41
|
|
|
40
42
|
@dataclass
|
|
@@ -3,7 +3,7 @@ from __future__ import annotations
|
|
|
3
3
|
import json
|
|
4
4
|
import logging
|
|
5
5
|
from abc import ABC, abstractmethod
|
|
6
|
-
from dataclasses import asdict, dataclass, field, is_dataclass
|
|
6
|
+
from dataclasses import asdict, dataclass, field, fields, is_dataclass
|
|
7
7
|
from typing import TYPE_CHECKING, Any, ClassVar, TypeVar
|
|
8
8
|
|
|
9
9
|
from griptape.artifacts import BaseArtifact
|
|
@@ -389,6 +389,21 @@ class EventResult[P: RequestPayload, R: ResultPayload](BaseEvent, ABC):
|
|
|
389
389
|
bool: True if success, False if failure
|
|
390
390
|
"""
|
|
391
391
|
|
|
392
|
+
@classmethod
|
|
393
|
+
def _create_payload_instance(cls, payload_type: type, payload_data: dict[str, Any]) -> Any:
|
|
394
|
+
"""Create a payload instance from data, handling dataclass init=False fields."""
|
|
395
|
+
if is_dataclass(payload_type):
|
|
396
|
+
# Filter out fields that have init=False to avoid TypeError
|
|
397
|
+
init_fields = {f.name for f in fields(payload_type) if f.init}
|
|
398
|
+
filtered_data = {k: v for k, v in payload_data.items() if k in init_fields}
|
|
399
|
+
return payload_type(**filtered_data)
|
|
400
|
+
if issubclass(payload_type, BaseModel):
|
|
401
|
+
return payload_type.model_validate(payload_data)
|
|
402
|
+
instance = payload_type()
|
|
403
|
+
for key, value in payload_data.items():
|
|
404
|
+
setattr(instance, key, value)
|
|
405
|
+
return instance
|
|
406
|
+
|
|
392
407
|
@classmethod
|
|
393
408
|
def from_dict( # pyright: ignore[reportIncompatibleMethodOverride]
|
|
394
409
|
cls, data: builtins.dict[str, Any], req_payload_type: type[P], res_payload_type: type[R]
|
|
@@ -403,28 +418,14 @@ class EventResult[P: RequestPayload, R: ResultPayload](BaseEvent, ABC):
|
|
|
403
418
|
|
|
404
419
|
# Process request payload
|
|
405
420
|
if req_payload_type:
|
|
406
|
-
|
|
407
|
-
request_payload = req_payload_type(**request_data)
|
|
408
|
-
elif issubclass(req_payload_type, BaseModel):
|
|
409
|
-
request_payload = req_payload_type.model_validate(request_data)
|
|
410
|
-
else:
|
|
411
|
-
request_payload = req_payload_type()
|
|
412
|
-
for key, value in request_data.items():
|
|
413
|
-
setattr(request_payload, key, value)
|
|
421
|
+
request_payload = cls._create_payload_instance(req_payload_type, request_data)
|
|
414
422
|
else:
|
|
415
423
|
msg = f"Cannot create {cls.__name__} without a request payload type"
|
|
416
424
|
raise ValueError(msg)
|
|
417
425
|
|
|
418
426
|
# Process result payload
|
|
419
427
|
if res_payload_type:
|
|
420
|
-
|
|
421
|
-
result_payload = res_payload_type(**result_data)
|
|
422
|
-
elif issubclass(res_payload_type, BaseModel):
|
|
423
|
-
result_payload = res_payload_type.model_validate(result_data)
|
|
424
|
-
else:
|
|
425
|
-
result_payload = res_payload_type()
|
|
426
|
-
for key, value in result_data.items():
|
|
427
|
-
setattr(result_payload, key, value)
|
|
428
|
+
result_payload = cls._create_payload_instance(res_payload_type, result_data)
|
|
428
429
|
else:
|
|
429
430
|
msg = f"Cannot create {cls.__name__} without a result payload type"
|
|
430
431
|
raise ValueError(msg)
|
|
@@ -4,6 +4,7 @@ from typing import Any
|
|
|
4
4
|
from griptape_nodes.retained_mode.events.base_events import (
|
|
5
5
|
ExecutionPayload,
|
|
6
6
|
RequestPayload,
|
|
7
|
+
ResultDetails,
|
|
7
8
|
ResultPayloadFailure,
|
|
8
9
|
ResultPayloadSuccess,
|
|
9
10
|
WorkflowAlteredMixin,
|
|
@@ -307,7 +308,8 @@ class ControlFlowResolvedEvent(ExecutionPayload):
|
|
|
307
308
|
@dataclass
|
|
308
309
|
@PayloadRegistry.register
|
|
309
310
|
class ControlFlowCancelledEvent(ExecutionPayload):
|
|
310
|
-
|
|
311
|
+
result_details: ResultDetails | str | None = None
|
|
312
|
+
exception: Exception | None = None
|
|
311
313
|
|
|
312
314
|
|
|
313
315
|
@dataclass
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
|
-
from griptape_nodes.
|
|
4
|
+
from griptape_nodes.exe_types.node_types import NodeDependencies
|
|
5
5
|
from griptape_nodes.node_library.workflow_registry import WorkflowShape
|
|
6
6
|
from griptape_nodes.retained_mode.events.base_events import (
|
|
7
7
|
RequestPayload,
|
|
@@ -186,8 +186,6 @@ class SerializedFlowCommands:
|
|
|
186
186
|
Useful for save/load, copy/paste, etc.
|
|
187
187
|
|
|
188
188
|
Attributes:
|
|
189
|
-
node_libraries_used (set[LibraryNameAndVersion]): Set of libraries and versions used by the nodes,
|
|
190
|
-
including those in child flows.
|
|
191
189
|
flow_initialization_command (CreateFlowRequest | ImportWorkflowAsReferencedSubFlowRequest | None): Command to initialize the flow that contains all of this.
|
|
192
190
|
Can be CreateFlowRequest for standalone flows, ImportWorkflowAsReferencedSubFlowRequest for referenced workflows,
|
|
193
191
|
or None to deserialize into whatever Flow is in the Current Context.
|
|
@@ -200,8 +198,9 @@ class SerializedFlowCommands:
|
|
|
200
198
|
set_parameter_value_commands (dict[SerializedNodeCommands.NodeUUID, list[SerializedNodeCommands.IndirectSetParameterValueCommand]]): List of commands
|
|
201
199
|
to set parameter values, keyed by node UUID, during deserialization.
|
|
202
200
|
sub_flows_commands (list["SerializedFlowCommands"]): List of sub-flow commands. Cascades into sub-flows within this serialization.
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
node_dependencies (NodeDependencies): Aggregated dependencies from all nodes in this flow and its sub-flows.
|
|
202
|
+
Includes referenced workflows, static files, Python imports, and libraries. Used for workflow packaging,
|
|
203
|
+
dependency resolution, and deployment planning.
|
|
205
204
|
"""
|
|
206
205
|
|
|
207
206
|
@dataclass
|
|
@@ -222,7 +221,6 @@ class SerializedFlowCommands:
|
|
|
222
221
|
target_node_uuid: SerializedNodeCommands.NodeUUID
|
|
223
222
|
target_parameter_name: str
|
|
224
223
|
|
|
225
|
-
node_libraries_used: set[LibraryNameAndVersion]
|
|
226
224
|
flow_initialization_command: CreateFlowRequest | ImportWorkflowAsReferencedSubFlowRequest | None
|
|
227
225
|
serialized_node_commands: list[SerializedNodeCommands]
|
|
228
226
|
serialized_connections: list[IndirectConnectionSerialization]
|
|
@@ -232,7 +230,7 @@ class SerializedFlowCommands:
|
|
|
232
230
|
]
|
|
233
231
|
set_lock_commands_per_node: dict[SerializedNodeCommands.NodeUUID, SetLockNodeStateRequest]
|
|
234
232
|
sub_flows_commands: list["SerializedFlowCommands"]
|
|
235
|
-
|
|
233
|
+
node_dependencies: NodeDependencies
|
|
236
234
|
|
|
237
235
|
|
|
238
236
|
@dataclass
|