vellum-ai 0.12.14__py3-none-any.whl → 0.12.16__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.
- 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
|