vellum-ai 0.12.14__py3-none-any.whl → 0.12.16__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- vellum/__init__.py +6 -0
- vellum/client/__init__.py +2 -6
- vellum/client/core/client_wrapper.py +1 -1
- vellum/client/environment.py +3 -3
- vellum/client/resources/ad_hoc/client.py +2 -6
- vellum/client/resources/container_images/client.py +0 -8
- vellum/client/resources/metric_definitions/client.py +2 -6
- vellum/client/resources/workflows/client.py +8 -8
- vellum/client/types/__init__.py +6 -0
- vellum/client/types/audio_prompt_block.py +29 -0
- vellum/client/types/function_call_prompt_block.py +30 -0
- vellum/client/types/image_prompt_block.py +29 -0
- vellum/client/types/prompt_block.py +12 -1
- vellum/client/types/workflow_push_response.py +1 -0
- vellum/prompts/blocks/compilation.py +43 -0
- vellum/types/audio_prompt_block.py +3 -0
- vellum/types/function_call_prompt_block.py +3 -0
- vellum/types/image_prompt_block.py +3 -0
- vellum/workflows/nodes/core/inline_subworkflow_node/node.py +10 -2
- vellum/workflows/nodes/core/inline_subworkflow_node/tests/test_node.py +16 -0
- {vellum_ai-0.12.14.dist-info → vellum_ai-0.12.16.dist-info}/METADATA +11 -9
- {vellum_ai-0.12.14.dist-info → vellum_ai-0.12.16.dist-info}/RECORD +59 -48
- vellum_cli/__init__.py +14 -0
- vellum_cli/config.py +4 -0
- vellum_cli/pull.py +20 -5
- vellum_cli/push.py +33 -4
- vellum_cli/tests/test_pull.py +19 -1
- vellum_cli/tests/test_push.py +63 -0
- vellum_ee/workflows/display/nodes/vellum/__init__.py +2 -0
- vellum_ee/workflows/display/nodes/vellum/api_node.py +3 -3
- vellum_ee/workflows/display/nodes/vellum/base_node.py +35 -0
- vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +2 -2
- vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +2 -2
- vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +20 -6
- vellum_ee/workflows/display/nodes/vellum/map_node.py +1 -0
- vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +2 -2
- vellum_ee/workflows/display/nodes/vellum/search_node.py +4 -2
- vellum_ee/workflows/display/nodes/vellum/templating_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py +3 -3
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/__init__.py +0 -0
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py +28 -0
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py +123 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +6 -46
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py +3 -25
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py +168 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +18 -10
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +18 -10
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +3 -25
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py +2 -8
- vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +13 -27
- vellum_ee/workflows/display/types.py +5 -1
- vellum_ee/workflows/display/utils/vellum.py +3 -3
- vellum_ee/workflows/display/vellum.py +4 -0
- vellum_ee/workflows/display/workflows/base_workflow_display.py +44 -16
- vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py +3 -0
- vellum_ee/workflows/display/workflows/vellum_workflow_display.py +7 -8
- {vellum_ai-0.12.14.dist-info → vellum_ai-0.12.16.dist-info}/LICENSE +0 -0
- {vellum_ai-0.12.14.dist-info → vellum_ai-0.12.16.dist-info}/WHEEL +0 -0
- {vellum_ai-0.12.14.dist-info → vellum_ai-0.12.16.dist-info}/entry_points.txt +0 -0
@@ -1,8 +1,5 @@
|
|
1
|
-
from unittest import mock
|
2
|
-
|
3
1
|
from deepdiff import DeepDiff
|
4
2
|
|
5
|
-
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
6
3
|
from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
|
7
4
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
8
5
|
|
@@ -15,12 +12,7 @@ def test_serialize_workflow():
|
|
15
12
|
workflow_display = get_workflow_display(
|
16
13
|
base_display_class=VellumWorkflowDisplay, workflow_class=BasicInlineSubworkflowWorkflow
|
17
14
|
)
|
18
|
-
|
19
|
-
# TODO: Support serialization of BaseNode
|
20
|
-
# https://app.shortcut.com/vellum/story/4871/support-serialization-of-base-node
|
21
|
-
with mock.patch.object(BaseNodeVellumDisplay, "serialize") as mocked_serialize:
|
22
|
-
mocked_serialize.return_value = {"type": "MOCKED"}
|
23
|
-
serialized_workflow: dict = workflow_display.serialize()
|
15
|
+
serialized_workflow: dict = workflow_display.serialize()
|
24
16
|
|
25
17
|
# THEN we should get a serialized representation of the Workflow
|
26
18
|
assert serialized_workflow.keys() == {
|
@@ -156,7 +148,23 @@ def test_serialize_workflow():
|
|
156
148
|
"bases": [],
|
157
149
|
},
|
158
150
|
},
|
159
|
-
{
|
151
|
+
{
|
152
|
+
"id": "1381c078-efa2-4255-89a1-7b4cb742c7fc",
|
153
|
+
"label": "StartNode",
|
154
|
+
"type": "GENERIC",
|
155
|
+
"display_data": {"position": {"x": 0.0, "y": 0.0}},
|
156
|
+
"definition": {
|
157
|
+
"name": "StartNode",
|
158
|
+
"module": ["tests", "workflows", "basic_inline_subworkflow", "workflow"],
|
159
|
+
"bases": [
|
160
|
+
{"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"]}
|
161
|
+
],
|
162
|
+
},
|
163
|
+
"trigger": {"id": "a95a34f2-e894-4fb6-a2c9-15d12c1e3135", "merge_behavior": "AWAIT_ANY"},
|
164
|
+
"ports": [],
|
165
|
+
"adornments": None,
|
166
|
+
"attributes": [],
|
167
|
+
},
|
160
168
|
{
|
161
169
|
"id": "a773c3a5-78cb-4250-8d29-7282e8a579d3",
|
162
170
|
"type": "TERMINAL",
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py
CHANGED
@@ -1,8 +1,5 @@
|
|
1
|
-
from unittest import mock
|
2
|
-
|
3
1
|
from deepdiff import DeepDiff
|
4
2
|
|
5
|
-
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
6
3
|
from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
|
7
4
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
8
5
|
|
@@ -13,12 +10,7 @@ def test_serialize_workflow():
|
|
13
10
|
# GIVEN a Workflow that uses a MapNode
|
14
11
|
# WHEN we serialize it
|
15
12
|
workflow_display = get_workflow_display(base_display_class=VellumWorkflowDisplay, workflow_class=SimpleMapExample)
|
16
|
-
|
17
|
-
# TODO: Support serialization of BaseNode
|
18
|
-
# https://app.shortcut.com/vellum/story/4871/support-serialization-of-base-node
|
19
|
-
with mock.patch.object(BaseNodeVellumDisplay, "serialize") as mocked_serialize:
|
20
|
-
mocked_serialize.return_value = {"type": "MOCKED"}
|
21
|
-
serialized_workflow: dict = workflow_display.serialize()
|
13
|
+
serialized_workflow: dict = workflow_display.serialize()
|
22
14
|
|
23
15
|
# THEN we should get a serialized representation of the Workflow
|
24
16
|
assert serialized_workflow.keys() == {
|
@@ -141,7 +133,23 @@ def test_serialize_workflow():
|
|
141
133
|
"bases": [],
|
142
134
|
},
|
143
135
|
},
|
144
|
-
{
|
136
|
+
{
|
137
|
+
"id": "baf6d316-dc75-41e8-96c0-015aede96309",
|
138
|
+
"label": "Iteration",
|
139
|
+
"type": "GENERIC",
|
140
|
+
"display_data": {"position": {"x": 0.0, "y": 0.0}},
|
141
|
+
"definition": {
|
142
|
+
"name": "Iteration",
|
143
|
+
"module": ["tests", "workflows", "basic_map_node", "workflow"],
|
144
|
+
"bases": [
|
145
|
+
{"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"]}
|
146
|
+
],
|
147
|
+
},
|
148
|
+
"trigger": {"id": "01324747-9bc0-4ecd-a8ab-40dca5a94e2e", "merge_behavior": "AWAIT_ANY"},
|
149
|
+
"ports": [],
|
150
|
+
"adornments": None,
|
151
|
+
"attributes": [],
|
152
|
+
},
|
145
153
|
{
|
146
154
|
"id": "6f4883b2-70b1-4e1c-ae15-7d0f5aec810b",
|
147
155
|
"type": "TERMINAL",
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py
CHANGED
@@ -1,8 +1,5 @@
|
|
1
|
-
from unittest import mock
|
2
|
-
|
3
1
|
from deepdiff import DeepDiff
|
4
2
|
|
5
|
-
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
6
3
|
from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
|
7
4
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
8
5
|
|
@@ -15,12 +12,7 @@ def test_serialize_workflow__await_all():
|
|
15
12
|
workflow_display = get_workflow_display(
|
16
13
|
base_display_class=VellumWorkflowDisplay, workflow_class=AwaitAllPassingWorkflow
|
17
14
|
)
|
18
|
-
|
19
|
-
# TODO: Support serialization of BaseNode
|
20
|
-
# https://app.shortcut.com/vellum/story/4871/support-serialization-of-base-node
|
21
|
-
with mock.patch.object(BaseNodeVellumDisplay, "serialize") as mocked_serialize:
|
22
|
-
mocked_serialize.return_value = {"type": "MOCKED"}
|
23
|
-
serialized_workflow: dict = workflow_display.serialize()
|
15
|
+
serialized_workflow: dict = workflow_display.serialize()
|
24
16
|
|
25
17
|
# THEN we should get a serialized representation of the Workflow
|
26
18
|
assert serialized_workflow.keys() == {
|
@@ -76,22 +68,8 @@ def test_serialize_workflow__await_all():
|
|
76
68
|
},
|
77
69
|
}
|
78
70
|
|
79
|
-
passthrough_nodes = [node for node in workflow_raw_data["nodes"] if node["type"] == "
|
80
|
-
assert
|
81
|
-
[
|
82
|
-
{
|
83
|
-
"type": "MOCKED",
|
84
|
-
},
|
85
|
-
{
|
86
|
-
"type": "MOCKED",
|
87
|
-
},
|
88
|
-
{
|
89
|
-
"type": "MOCKED",
|
90
|
-
},
|
91
|
-
],
|
92
|
-
passthrough_nodes,
|
93
|
-
ignore_order=True,
|
94
|
-
)
|
71
|
+
passthrough_nodes = [node for node in workflow_raw_data["nodes"] if node["type"] == "GENERIC"]
|
72
|
+
assert len(passthrough_nodes) == 3
|
95
73
|
|
96
74
|
merge_node = next(node for node in workflow_raw_data["nodes"] if node["type"] == "MERGE")
|
97
75
|
assert not DeepDiff(
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py
CHANGED
@@ -1,8 +1,5 @@
|
|
1
|
-
from unittest import mock
|
2
|
-
|
3
1
|
from deepdiff import DeepDiff
|
4
2
|
|
5
|
-
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
6
3
|
from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
|
7
4
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
8
5
|
|
@@ -13,10 +10,7 @@ def test_serialize_workflow():
|
|
13
10
|
# GIVEN a Workflow with a TryNode
|
14
11
|
# WHEN we serialize it
|
15
12
|
workflow_display = get_workflow_display(base_display_class=VellumWorkflowDisplay, workflow_class=SimpleTryExample)
|
16
|
-
|
17
|
-
with mock.patch.object(BaseNodeVellumDisplay, "serialize") as mocked_serialize:
|
18
|
-
mocked_serialize.return_value = {"type": "MOCKED"}
|
19
|
-
serialized_workflow: dict = workflow_display.serialize()
|
13
|
+
serialized_workflow: dict = workflow_display.serialize()
|
20
14
|
|
21
15
|
# THEN we should get a serialized representation of the Workflow
|
22
16
|
assert serialized_workflow.keys() == {
|
@@ -82,4 +76,4 @@ def test_serialize_workflow():
|
|
82
76
|
}
|
83
77
|
|
84
78
|
try_node = workflow_raw_data["nodes"][1]
|
85
|
-
assert try_node ==
|
79
|
+
assert try_node["id"] == "1381c078-efa2-4255-89a1-7b4cb742c7fc"
|
vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py
CHANGED
@@ -1,9 +1,7 @@
|
|
1
1
|
import pytest
|
2
|
-
from unittest import mock
|
3
2
|
|
4
3
|
from deepdiff import DeepDiff
|
5
4
|
|
6
|
-
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
7
5
|
from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
|
8
6
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
9
7
|
|
@@ -13,17 +11,12 @@ from tests.workflows.complex_final_output_node.missing_workflow_output import Mi
|
|
13
11
|
|
14
12
|
def test_serialize_workflow__missing_final_output_node():
|
15
13
|
# GIVEN a Workflow that is missing a Terminal Node
|
14
|
+
workflow_display = get_workflow_display(
|
15
|
+
base_display_class=VellumWorkflowDisplay, workflow_class=MissingFinalOutputNodeWorkflow
|
16
|
+
)
|
16
17
|
|
17
|
-
#
|
18
|
-
|
19
|
-
with mock.patch.object(BaseNodeVellumDisplay, "serialize") as mocked_serialize:
|
20
|
-
mocked_serialize.return_value = {"type": "MOCKED"}
|
21
|
-
workflow_display = get_workflow_display(
|
22
|
-
base_display_class=VellumWorkflowDisplay, workflow_class=MissingFinalOutputNodeWorkflow
|
23
|
-
)
|
24
|
-
|
25
|
-
# WHEN we serialize it
|
26
|
-
serialized_workflow: dict = workflow_display.serialize()
|
18
|
+
# WHEN we serialize it
|
19
|
+
serialized_workflow: dict = workflow_display.serialize()
|
27
20
|
|
28
21
|
# THEN we should get a serialized representation of the Workflow
|
29
22
|
assert serialized_workflow.keys() == {
|
@@ -97,10 +90,8 @@ def test_serialize_workflow__missing_final_output_node():
|
|
97
90
|
},
|
98
91
|
}
|
99
92
|
|
100
|
-
passthrough_node = next(node for node in workflow_raw_data["nodes"] if node["type"] == "
|
101
|
-
assert passthrough_node ==
|
102
|
-
"type": "MOCKED",
|
103
|
-
}
|
93
|
+
passthrough_node = next(node for node in workflow_raw_data["nodes"] if node["type"] == "GENERIC")
|
94
|
+
assert passthrough_node["id"] == "32d88cab-e9fa-4a56-9bc2-fb6e1fd0897f"
|
104
95
|
|
105
96
|
final_output_nodes = [node for node in workflow_raw_data["nodes"] if node["type"] == "TERMINAL"]
|
106
97
|
assert not DeepDiff(
|
@@ -218,17 +209,12 @@ def test_serialize_workflow__missing_final_output_node():
|
|
218
209
|
|
219
210
|
def test_serialize_workflow__missing_workflow_output():
|
220
211
|
# GIVEN a Workflow that contains a terminal node that is unreferenced by the Workflow's Outputs
|
212
|
+
workflow_display = get_workflow_display(
|
213
|
+
base_display_class=VellumWorkflowDisplay, workflow_class=MissingWorkflowOutputWorkflow
|
214
|
+
)
|
221
215
|
|
222
|
-
#
|
223
|
-
|
224
|
-
|
225
|
-
mocked_serialize.return_value = {"type": "MOCKED"}
|
226
|
-
workflow_display = get_workflow_display(
|
227
|
-
base_display_class=VellumWorkflowDisplay, workflow_class=MissingWorkflowOutputWorkflow
|
228
|
-
)
|
229
|
-
|
230
|
-
# WHEN we serialize it, it should throw an error
|
231
|
-
with pytest.raises(ValueError) as exc_info:
|
232
|
-
workflow_display.serialize()
|
216
|
+
# WHEN we serialize it, it should throw an error
|
217
|
+
with pytest.raises(ValueError) as exc_info:
|
218
|
+
workflow_display.serialize()
|
233
219
|
|
234
220
|
assert exc_info.value.args[0] == "Unable to serialize terminal nodes that are not referenced by workflow outputs."
|
@@ -36,8 +36,12 @@ class WorkflowDisplayContext(
|
|
36
36
|
workflow_display_class: Type["BaseWorkflowDisplay"]
|
37
37
|
workflow_display: WorkflowMetaDisplayType
|
38
38
|
workflow_input_displays: Dict[WorkflowInputReference, WorkflowInputsDisplayType] = field(default_factory=dict)
|
39
|
+
global_workflow_input_displays: Dict[WorkflowInputReference, WorkflowInputsDisplayType] = field(
|
40
|
+
default_factory=dict
|
41
|
+
)
|
39
42
|
node_displays: Dict[Type[BaseNode], "NodeDisplayType"] = field(default_factory=dict)
|
40
|
-
|
43
|
+
global_node_displays: Dict[Type[BaseNode], NodeDisplayType] = field(default_factory=dict)
|
44
|
+
global_node_output_displays: Dict[OutputReference, Tuple[Type[BaseNode], "NodeOutputDisplay"]] = field(
|
41
45
|
default_factory=dict
|
42
46
|
)
|
43
47
|
entrypoint_displays: Dict[Type[BaseNode], EntrypointDisplayType] = field(default_factory=dict)
|
@@ -58,13 +58,13 @@ def create_node_input_value_pointer_rule(
|
|
58
58
|
value: Any, display_context: WorkflowDisplayContext
|
59
59
|
) -> NodeInputValuePointerRule:
|
60
60
|
if isinstance(value, OutputReference):
|
61
|
-
upstream_node, output_display = display_context.
|
62
|
-
upstream_node_display = display_context.
|
61
|
+
upstream_node, output_display = display_context.global_node_output_displays[value]
|
62
|
+
upstream_node_display = display_context.global_node_displays[upstream_node]
|
63
63
|
return NodeOutputPointer(
|
64
64
|
data=NodeOutputData(node_id=str(upstream_node_display.node_id), output_id=str(output_display.id)),
|
65
65
|
)
|
66
66
|
if isinstance(value, WorkflowInputReference):
|
67
|
-
workflow_input_display = display_context.
|
67
|
+
workflow_input_display = display_context.global_workflow_input_displays[value]
|
68
68
|
return InputVariablePointer(data=InputVariableData(input_variable_id=str(workflow_input_display.id)))
|
69
69
|
if isinstance(value, VellumSecretReference):
|
70
70
|
# TODO: Pass through the name instead of retrieving the ID
|
@@ -38,6 +38,10 @@ class NodeDisplayData(UniversalBaseModel):
|
|
38
38
|
comment: Optional[NodeDisplayComment] = None
|
39
39
|
|
40
40
|
|
41
|
+
class GenericNodeDisplayData(UniversalBaseModel):
|
42
|
+
position: NodeDisplayPosition = Field(default_factory=NodeDisplayPosition)
|
43
|
+
|
44
|
+
|
41
45
|
class CodeResourceDefinition(UniversalBaseModel):
|
42
46
|
name: str
|
43
47
|
module: List[str]
|
@@ -1,9 +1,9 @@
|
|
1
1
|
from abc import abstractmethod
|
2
|
-
from copy import
|
2
|
+
from copy import copy
|
3
3
|
from functools import cached_property
|
4
4
|
import logging
|
5
5
|
from uuid import UUID
|
6
|
-
from typing import Any, Dict, Generic, Optional, Tuple, Type, get_args
|
6
|
+
from typing import Any, Dict, Generic, Iterator, List, Optional, Tuple, Type, get_args
|
7
7
|
|
8
8
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
9
9
|
from vellum.workflows.edges import Edge
|
@@ -71,6 +71,10 @@ class BaseWorkflowDisplay(
|
|
71
71
|
# Used to store the mapping between workflows and their display classes
|
72
72
|
_workflow_display_registry: Dict[Type[WorkflowType], Type["BaseWorkflowDisplay"]] = {}
|
73
73
|
|
74
|
+
_errors: List[Exception]
|
75
|
+
|
76
|
+
_dry_run: bool
|
77
|
+
|
74
78
|
def __init__(
|
75
79
|
self,
|
76
80
|
workflow: Type[WorkflowType],
|
@@ -85,12 +89,15 @@ class BaseWorkflowDisplay(
|
|
85
89
|
EdgeDisplayType,
|
86
90
|
]
|
87
91
|
] = None,
|
92
|
+
dry_run: bool = False,
|
88
93
|
):
|
89
94
|
self._workflow = workflow
|
90
95
|
self._parent_display_context = parent_display_context
|
96
|
+
self._errors: List[Exception] = []
|
97
|
+
self._dry_run = dry_run
|
91
98
|
|
92
99
|
@abstractmethod
|
93
|
-
def serialize(self
|
100
|
+
def serialize(self) -> JsonObject:
|
94
101
|
pass
|
95
102
|
|
96
103
|
@classmethod
|
@@ -110,7 +117,18 @@ class BaseWorkflowDisplay(
|
|
110
117
|
def node_display_base_class(self) -> Type[NodeDisplayType]:
|
111
118
|
pass
|
112
119
|
|
113
|
-
def
|
120
|
+
def add_error(self, error: Exception) -> None:
|
121
|
+
if self._dry_run:
|
122
|
+
self._errors.append(error)
|
123
|
+
return
|
124
|
+
|
125
|
+
raise error
|
126
|
+
|
127
|
+
@property
|
128
|
+
def errors(self) -> Iterator[Exception]:
|
129
|
+
return iter(self._errors)
|
130
|
+
|
131
|
+
def _enrich_global_node_output_displays(
|
114
132
|
self,
|
115
133
|
node: Type[BaseNode],
|
116
134
|
node_display: NodeDisplayType,
|
@@ -126,7 +144,7 @@ class BaseWorkflowDisplay(
|
|
126
144
|
inner_node = get_wrapped_node(node)
|
127
145
|
if inner_node._is_wrapped_node:
|
128
146
|
inner_node_display = self._get_node_display(inner_node)
|
129
|
-
self.
|
147
|
+
self._enrich_global_node_output_displays(inner_node, inner_node_display, node_output_displays)
|
130
148
|
|
131
149
|
# TODO: Make sure this output ID matches the workflow output ID of the subworkflow node's workflow
|
132
150
|
# https://app.shortcut.com/vellum/story/5660/fix-output-id-in-subworkflow-nodes
|
@@ -174,19 +192,26 @@ class BaseWorkflowDisplay(
|
|
174
192
|
]:
|
175
193
|
workflow_display = self._generate_workflow_meta_display()
|
176
194
|
|
177
|
-
|
178
|
-
|
179
|
-
deepcopy(self._parent_display_context.node_output_displays) if self._parent_display_context else {}
|
195
|
+
global_node_output_displays: Dict[OutputReference, Tuple[Type[BaseNode], NodeOutputDisplay]] = (
|
196
|
+
copy(self._parent_display_context.global_node_output_displays) if self._parent_display_context else {}
|
180
197
|
)
|
181
198
|
|
182
199
|
node_displays: Dict[Type[BaseNode], NodeDisplayType] = {}
|
200
|
+
|
201
|
+
global_node_displays: Dict[Type[BaseNode], NodeDisplayType] = (
|
202
|
+
copy(self._parent_display_context.global_node_displays) if self._parent_display_context else {}
|
203
|
+
)
|
204
|
+
|
183
205
|
port_displays: Dict[Port, PortDisplay] = {}
|
184
206
|
|
185
207
|
# TODO: We should still serialize nodes that are in the workflow's directory but aren't used in the graph.
|
186
208
|
# https://app.shortcut.com/vellum/story/5394
|
187
209
|
for node in self._workflow.get_nodes():
|
210
|
+
if node in global_node_displays:
|
211
|
+
continue
|
188
212
|
node_display = self._get_node_display(node)
|
189
213
|
node_displays[node] = node_display
|
214
|
+
global_node_displays[node] = node_display
|
190
215
|
|
191
216
|
# Nodes wrapped in a decorator need to be in our node display dictionary for later retrieval
|
192
217
|
if has_wrapped_node(node):
|
@@ -195,22 +220,23 @@ class BaseWorkflowDisplay(
|
|
195
220
|
|
196
221
|
if inner_node._is_wrapped_node:
|
197
222
|
node_displays[inner_node] = inner_node_display
|
223
|
+
global_node_displays[inner_node] = inner_node_display
|
198
224
|
|
199
|
-
self.
|
225
|
+
self._enrich_global_node_output_displays(node, node_display, global_node_output_displays)
|
200
226
|
self._enrich_node_port_displays(node, node_display, port_displays)
|
201
227
|
|
228
|
+
workflow_input_displays: Dict[WorkflowInputReference, WorkflowInputsDisplayType] = {}
|
202
229
|
# If we're dealing with a nested workflow, then it should have access to the inputs of its parents.
|
203
|
-
|
204
|
-
|
230
|
+
global_workflow_input_displays = (
|
231
|
+
copy(self._parent_display_context.workflow_input_displays) if self._parent_display_context else {}
|
205
232
|
)
|
206
233
|
for workflow_input in self._workflow.get_inputs_class():
|
207
|
-
if workflow_input in workflow_input_displays:
|
208
|
-
continue
|
209
|
-
|
210
234
|
workflow_input_display_overrides = self.inputs_display.get(workflow_input)
|
211
|
-
|
235
|
+
input_display = self._generate_workflow_input_display(
|
212
236
|
workflow_input, overrides=workflow_input_display_overrides
|
213
237
|
)
|
238
|
+
workflow_input_displays[workflow_input] = input_display
|
239
|
+
global_workflow_input_displays[workflow_input] = input_display
|
214
240
|
|
215
241
|
entrypoint_displays: Dict[Type[BaseNode], EntrypointDisplayType] = {}
|
216
242
|
for entrypoint in self._workflow.get_entrypoints():
|
@@ -253,8 +279,10 @@ class BaseWorkflowDisplay(
|
|
253
279
|
return WorkflowDisplayContext(
|
254
280
|
workflow_display=workflow_display,
|
255
281
|
workflow_input_displays=workflow_input_displays,
|
282
|
+
global_workflow_input_displays=global_workflow_input_displays,
|
256
283
|
node_displays=node_displays,
|
257
|
-
|
284
|
+
global_node_output_displays=global_node_output_displays,
|
285
|
+
global_node_displays=global_node_displays,
|
258
286
|
entrypoint_displays=entrypoint_displays,
|
259
287
|
workflow_output_displays=workflow_output_displays,
|
260
288
|
edge_displays=edge_displays,
|
@@ -10,6 +10,7 @@ def get_workflow_display(
|
|
10
10
|
workflow_class: Type[WorkflowType],
|
11
11
|
root_workflow_class: Optional[Type[WorkflowType]] = None,
|
12
12
|
parent_display_context: Optional[WorkflowDisplayContext] = None,
|
13
|
+
dry_run: bool = False,
|
13
14
|
) -> WorkflowDisplayType:
|
14
15
|
try:
|
15
16
|
workflow_display_class = base_display_class.get_from_workflow_display_registry(workflow_class)
|
@@ -20,6 +21,7 @@ def get_workflow_display(
|
|
20
21
|
workflow_class=workflow_class.__bases__[0],
|
21
22
|
root_workflow_class=workflow_class if root_workflow_class is None else root_workflow_class,
|
22
23
|
parent_display_context=parent_display_context,
|
24
|
+
dry_run=dry_run,
|
23
25
|
)
|
24
26
|
except IndexError:
|
25
27
|
return base_display_class(workflow_class)
|
@@ -27,4 +29,5 @@ def get_workflow_display(
|
|
27
29
|
return workflow_display_class( # type: ignore[return-value]
|
28
30
|
workflow_class,
|
29
31
|
parent_display_context=parent_display_context,
|
32
|
+
dry_run=dry_run,
|
30
33
|
)
|
@@ -53,7 +53,7 @@ class VellumWorkflowDisplay(
|
|
53
53
|
):
|
54
54
|
node_display_base_class = BaseNodeVellumDisplay
|
55
55
|
|
56
|
-
def serialize(self
|
56
|
+
def serialize(self) -> JsonObject:
|
57
57
|
input_variables: JsonArray = []
|
58
58
|
for workflow_input, workflow_input_display in self.display_context.workflow_input_displays.items():
|
59
59
|
default = primitive_to_vellum_value(workflow_input.instance) if workflow_input.instance else None
|
@@ -106,12 +106,9 @@ class VellumWorkflowDisplay(
|
|
106
106
|
|
107
107
|
try:
|
108
108
|
serialized_node = node_display.serialize(self.display_context)
|
109
|
-
except NotImplementedError:
|
110
|
-
|
111
|
-
|
112
|
-
raise
|
113
|
-
else:
|
114
|
-
continue
|
109
|
+
except NotImplementedError as e:
|
110
|
+
self.add_error(e)
|
111
|
+
continue
|
115
112
|
|
116
113
|
nodes.append(serialized_node)
|
117
114
|
|
@@ -205,7 +202,9 @@ class VellumWorkflowDisplay(
|
|
205
202
|
# If there are terminal nodes with no workflow output reference,
|
206
203
|
# raise a serialization error
|
207
204
|
if len(unreferenced_final_output_node_outputs) > 0:
|
208
|
-
|
205
|
+
self.add_error(
|
206
|
+
ValueError("Unable to serialize terminal nodes that are not referenced by workflow outputs.")
|
207
|
+
)
|
209
208
|
|
210
209
|
# Add an edge for each edge in the workflow
|
211
210
|
all_edge_displays: List[EdgeVellumDisplay] = [
|
File without changes
|
File without changes
|
File without changes
|