vellum-ai 0.12.15__py3-none-any.whl → 0.12.17__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- vellum/client/core/client_wrapper.py +1 -1
- vellum/plugins/vellum_mypy.py +80 -11
- vellum/prompts/blocks/compilation.py +43 -0
- vellum/utils/templating/render.py +3 -0
- vellum/workflows/nodes/bases/base.py +4 -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/workflows/nodes/core/retry_node/node.py +24 -3
- vellum/workflows/nodes/core/retry_node/tests/test_node.py +40 -0
- vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py +20 -1
- vellum/workflows/nodes/displayable/api_node/node.py +3 -3
- {vellum_ai-0.12.15.dist-info → vellum_ai-0.12.17.dist-info}/METADATA +10 -8
- {vellum_ai-0.12.15.dist-info → vellum_ai-0.12.17.dist-info}/RECORD +48 -45
- vellum_cli/__init__.py +14 -0
- vellum_cli/pull.py +7 -4
- vellum_cli/push.py +26 -4
- vellum_cli/tests/conftest.py +4 -2
- vellum_cli/tests/test_push.py +75 -4
- vellum_ee/workflows/display/nodes/vellum/api_node.py +3 -3
- vellum_ee/workflows/display/nodes/vellum/base_node.py +17 -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 +2 -11
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py +2 -14
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py +1 -7
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +17 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +17 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +1 -9
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py +1 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +1 -4
- 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.15.dist-info → vellum_ai-0.12.17.dist-info}/LICENSE +0 -0
- {vellum_ai-0.12.15.dist-info → vellum_ai-0.12.17.dist-info}/WHEEL +0 -0
- {vellum_ai-0.12.15.dist-info → vellum_ai-0.12.17.dist-info}/entry_points.txt +0 -0
@@ -91,18 +91,18 @@ def test_create_node_input_value_pointer_rules(
|
|
91
91
|
entrypoint_node_source_handle_id=uuid4(),
|
92
92
|
entrypoint_node_display=NodeDisplayData(),
|
93
93
|
),
|
94
|
-
|
94
|
+
global_workflow_input_displays={
|
95
95
|
cast(WorkflowInputReference, Inputs.example_workflow_input): WorkflowInputsVellumDisplayOverrides(
|
96
96
|
id=UUID("a154c29d-fac0-4cd0-ba88-bc52034f5470"),
|
97
97
|
),
|
98
98
|
},
|
99
|
-
|
99
|
+
global_node_output_displays={
|
100
100
|
cast(OutputReference, MyNodeA.Outputs.output): (
|
101
101
|
MyNodeA,
|
102
102
|
NodeOutputDisplay(id=UUID("4b16a629-11a1-4b3f-a965-a57b872d13b8"), name="output"),
|
103
103
|
),
|
104
104
|
},
|
105
|
-
|
105
|
+
global_node_displays={
|
106
106
|
MyNodeA: MyNodeADisplay(),
|
107
107
|
},
|
108
108
|
),
|
File without changes
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import pytest
|
2
|
+
from uuid import uuid4
|
3
|
+
|
4
|
+
from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
|
5
|
+
from vellum_ee.workflows.display.nodes.vellum.base_node import BaseNodeDisplay
|
6
|
+
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
7
|
+
from vellum_ee.workflows.display.vellum import NodeDisplayData, WorkflowMetaVellumDisplay
|
8
|
+
from vellum_ee.workflows.display.workflows.vellum_workflow_display import VellumWorkflowDisplay
|
9
|
+
|
10
|
+
|
11
|
+
@pytest.fixture()
|
12
|
+
def serialize_node():
|
13
|
+
def _serialize_node(node_class) -> dict:
|
14
|
+
node_display_class = get_node_display_class(BaseNodeDisplay, node_class)
|
15
|
+
node_display = node_display_class()
|
16
|
+
|
17
|
+
context: WorkflowDisplayContext = WorkflowDisplayContext(
|
18
|
+
workflow_display_class=VellumWorkflowDisplay,
|
19
|
+
workflow_display=WorkflowMetaVellumDisplay(
|
20
|
+
entrypoint_node_id=uuid4(),
|
21
|
+
entrypoint_node_source_handle_id=uuid4(),
|
22
|
+
entrypoint_node_display=NodeDisplayData(),
|
23
|
+
),
|
24
|
+
node_displays={node_class: node_display},
|
25
|
+
)
|
26
|
+
return node_display.serialize(context)
|
27
|
+
|
28
|
+
return _serialize_node
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
from deepdiff import DeepDiff
|
2
|
+
|
3
|
+
from vellum.workflows.inputs.base import BaseInputs
|
4
|
+
from vellum.workflows.nodes.bases.base import BaseNode
|
5
|
+
from vellum.workflows.types.core import MergeBehavior
|
6
|
+
|
7
|
+
|
8
|
+
class Inputs(BaseInputs):
|
9
|
+
input: str
|
10
|
+
|
11
|
+
|
12
|
+
class BasicGenericNode(BaseNode):
|
13
|
+
class Outputs(BaseNode.Outputs):
|
14
|
+
output = Inputs.input
|
15
|
+
|
16
|
+
|
17
|
+
class AwaitAnyGenericNode(BaseNode):
|
18
|
+
class Outputs(BaseNode.Outputs):
|
19
|
+
output = Inputs.input
|
20
|
+
|
21
|
+
class Trigger(BaseNode.Trigger):
|
22
|
+
merge_behavior = MergeBehavior.AWAIT_ANY
|
23
|
+
|
24
|
+
|
25
|
+
class AwaitAllGenericNode(BaseNode):
|
26
|
+
class Outputs(BaseNode.Outputs):
|
27
|
+
output = Inputs.input
|
28
|
+
|
29
|
+
class Trigger(BaseNode.Trigger):
|
30
|
+
merge_behavior = MergeBehavior.AWAIT_ALL
|
31
|
+
|
32
|
+
|
33
|
+
def test_serialize_node__basic(serialize_node):
|
34
|
+
serialized_node = serialize_node(BasicGenericNode)
|
35
|
+
assert not DeepDiff(
|
36
|
+
{
|
37
|
+
"id": "c2ed23f7-f6cb-4a56-a91c-2e5f9d8fda7f",
|
38
|
+
"label": "BasicGenericNode",
|
39
|
+
"type": "GENERIC",
|
40
|
+
"display_data": {"position": {"x": 0.0, "y": 0.0}},
|
41
|
+
"definition": {
|
42
|
+
"name": "BasicGenericNode",
|
43
|
+
"module": [
|
44
|
+
"vellum_ee",
|
45
|
+
"workflows",
|
46
|
+
"display",
|
47
|
+
"tests",
|
48
|
+
"workflow_serialization",
|
49
|
+
"generic_nodes",
|
50
|
+
"test_trigger_serialization",
|
51
|
+
],
|
52
|
+
"bases": [{"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"]}],
|
53
|
+
},
|
54
|
+
"trigger": {"id": "9d3a1b3d-4a38-4f2e-bbf1-dd8be152bce8", "merge_behavior": "AWAIT_ANY"},
|
55
|
+
"ports": [],
|
56
|
+
"adornments": None,
|
57
|
+
"attributes": [],
|
58
|
+
},
|
59
|
+
serialized_node,
|
60
|
+
ignore_order=True,
|
61
|
+
)
|
62
|
+
|
63
|
+
|
64
|
+
def test_serialize_node__await_any(serialize_node):
|
65
|
+
serialized_node = serialize_node(AwaitAnyGenericNode)
|
66
|
+
assert not DeepDiff(
|
67
|
+
{
|
68
|
+
"id": "0ba67f76-aaff-4bd4-a20f-73a32ef5810d",
|
69
|
+
"label": "AwaitAnyGenericNode",
|
70
|
+
"type": "GENERIC",
|
71
|
+
"display_data": {"position": {"x": 0.0, "y": 0.0}},
|
72
|
+
"definition": {
|
73
|
+
"name": "AwaitAnyGenericNode",
|
74
|
+
"module": [
|
75
|
+
"vellum_ee",
|
76
|
+
"workflows",
|
77
|
+
"display",
|
78
|
+
"tests",
|
79
|
+
"workflow_serialization",
|
80
|
+
"generic_nodes",
|
81
|
+
"test_trigger_serialization",
|
82
|
+
],
|
83
|
+
"bases": [{"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"]}],
|
84
|
+
},
|
85
|
+
"trigger": {"id": "ffa72187-9a18-453f-ae55-b77aad332630", "merge_behavior": "AWAIT_ANY"},
|
86
|
+
"ports": [],
|
87
|
+
"adornments": None,
|
88
|
+
"attributes": [],
|
89
|
+
},
|
90
|
+
serialized_node,
|
91
|
+
ignore_order=True,
|
92
|
+
)
|
93
|
+
|
94
|
+
|
95
|
+
def test_serialize_node__await_all(serialize_node):
|
96
|
+
serialized_node = serialize_node(AwaitAllGenericNode)
|
97
|
+
assert not DeepDiff(
|
98
|
+
{
|
99
|
+
"id": "09d06cd3-06ea-40cc-afd8-17ad88542271",
|
100
|
+
"label": "AwaitAllGenericNode",
|
101
|
+
"type": "GENERIC",
|
102
|
+
"display_data": {"position": {"x": 0.0, "y": 0.0}},
|
103
|
+
"definition": {
|
104
|
+
"name": "AwaitAllGenericNode",
|
105
|
+
"module": [
|
106
|
+
"vellum_ee",
|
107
|
+
"workflows",
|
108
|
+
"display",
|
109
|
+
"tests",
|
110
|
+
"workflow_serialization",
|
111
|
+
"generic_nodes",
|
112
|
+
"test_trigger_serialization",
|
113
|
+
],
|
114
|
+
"bases": [{"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"]}],
|
115
|
+
},
|
116
|
+
"trigger": {"id": "62074276-c817-476d-b59d-da523ae3f218", "merge_behavior": "AWAIT_ALL"},
|
117
|
+
"ports": [],
|
118
|
+
"adornments": None,
|
119
|
+
"attributes": [],
|
120
|
+
},
|
121
|
+
serialized_node,
|
122
|
+
ignore_order=True,
|
123
|
+
)
|
@@ -448,17 +448,8 @@ def test_serialize_workflow():
|
|
448
448
|
ignore_order=True,
|
449
449
|
)
|
450
450
|
|
451
|
-
|
452
|
-
|
453
|
-
{"id": "148c61bd-e8b0-4d4b-8734-b043a72b90ed", "type": "GENERIC"},
|
454
|
-
{"id": "ed7caf01-9ae7-47a3-b15a-16697abaf486", "type": "GENERIC"},
|
455
|
-
{"id": "0d959311-c836-4641-a867-58f63df9dfea", "type": "GENERIC"},
|
456
|
-
{"id": "8df781b1-ff28-48a5-98a2-d7d796b932b0", "type": "GENERIC"},
|
457
|
-
{"id": "68c02b7c-5077-4087-803d-841474a8081f", "type": "GENERIC"},
|
458
|
-
],
|
459
|
-
workflow_raw_data["nodes"][2:7],
|
460
|
-
ignore_order=True,
|
461
|
-
)
|
451
|
+
passthrough_nodes = [node for node in workflow_raw_data["nodes"] if node["type"] == "GENERIC"]
|
452
|
+
assert len(passthrough_nodes) == 5
|
462
453
|
|
463
454
|
assert not DeepDiff(
|
464
455
|
[
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py
CHANGED
@@ -122,20 +122,8 @@ def test_serialize_workflow():
|
|
122
122
|
ignore_order=True,
|
123
123
|
)
|
124
124
|
|
125
|
-
|
126
|
-
|
127
|
-
for i, node in enumerate(workflow_raw_data["nodes"])
|
128
|
-
if i != error_index and i != 0 and i != len(workflow_raw_data["nodes"]) - 1
|
129
|
-
]
|
130
|
-
|
131
|
-
assert not DeepDiff(
|
132
|
-
[
|
133
|
-
{"id": "1381c078-efa2-4255-89a1-7b4cb742c7fc", "type": "GENERIC"},
|
134
|
-
{"id": "1eee9b4e-531f-45f2-a4b9-42207fac2c33", "type": "GENERIC"},
|
135
|
-
],
|
136
|
-
mocked_base_nodes,
|
137
|
-
ignore_order=True,
|
138
|
-
)
|
125
|
+
passthrough_nodes = [node for node in workflow_raw_data["nodes"] if node["type"] == "GENERIC"]
|
126
|
+
assert len(passthrough_nodes) == 2
|
139
127
|
|
140
128
|
terminal_node = workflow_raw_data["nodes"][-1]
|
141
129
|
assert not DeepDiff(
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py
CHANGED
@@ -79,13 +79,7 @@ def test_serialize_workflow(vellum_client):
|
|
79
79
|
}
|
80
80
|
|
81
81
|
api_node = workflow_raw_data["nodes"][1]
|
82
|
-
assert
|
83
|
-
{
|
84
|
-
"id": "c2ed23f7-f6cb-4a56-a91c-2e5f9d8fda7f",
|
85
|
-
"type": "GENERIC",
|
86
|
-
},
|
87
|
-
api_node,
|
88
|
-
)
|
82
|
+
assert api_node["id"] == "c2ed23f7-f6cb-4a56-a91c-2e5f9d8fda7f"
|
89
83
|
|
90
84
|
final_output_node = workflow_raw_data["nodes"][2]
|
91
85
|
assert not DeepDiff(
|
@@ -148,7 +148,23 @@ def test_serialize_workflow():
|
|
148
148
|
"bases": [],
|
149
149
|
},
|
150
150
|
},
|
151
|
-
{
|
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
|
+
},
|
152
168
|
{
|
153
169
|
"id": "a773c3a5-78cb-4250-8d29-7282e8a579d3",
|
154
170
|
"type": "TERMINAL",
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py
CHANGED
@@ -133,7 +133,23 @@ def test_serialize_workflow():
|
|
133
133
|
"bases": [],
|
134
134
|
},
|
135
135
|
},
|
136
|
-
{
|
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
|
+
},
|
137
153
|
{
|
138
154
|
"id": "6f4883b2-70b1-4e1c-ae15-7d0f5aec810b",
|
139
155
|
"type": "TERMINAL",
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py
CHANGED
@@ -69,15 +69,7 @@ def test_serialize_workflow__await_all():
|
|
69
69
|
}
|
70
70
|
|
71
71
|
passthrough_nodes = [node for node in workflow_raw_data["nodes"] if node["type"] == "GENERIC"]
|
72
|
-
assert
|
73
|
-
[
|
74
|
-
{"id": "127ef456-91bc-43c6-bd8b-1772db5e3cb5", "type": "GENERIC"},
|
75
|
-
{"id": "59243c65-053f-4ea6-9157-3f3edb1477bf", "type": "GENERIC"},
|
76
|
-
{"id": "634f0202-9ea9-4c62-b152-1a58c595cffb", "type": "GENERIC"},
|
77
|
-
],
|
78
|
-
passthrough_nodes,
|
79
|
-
ignore_order=True,
|
80
|
-
)
|
72
|
+
assert len(passthrough_nodes) == 3
|
81
73
|
|
82
74
|
merge_node = next(node for node in workflow_raw_data["nodes"] if node["type"] == "MERGE")
|
83
75
|
assert not DeepDiff(
|
vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py
CHANGED
@@ -91,10 +91,7 @@ def test_serialize_workflow__missing_final_output_node():
|
|
91
91
|
}
|
92
92
|
|
93
93
|
passthrough_node = next(node for node in workflow_raw_data["nodes"] if node["type"] == "GENERIC")
|
94
|
-
assert passthrough_node ==
|
95
|
-
"id": "32d88cab-e9fa-4a56-9bc2-fb6e1fd0897f",
|
96
|
-
"type": "GENERIC",
|
97
|
-
}
|
94
|
+
assert passthrough_node["id"] == "32d88cab-e9fa-4a56-9bc2-fb6e1fd0897f"
|
98
95
|
|
99
96
|
final_output_nodes = [node for node in workflow_raw_data["nodes"] if node["type"] == "TERMINAL"]
|
100
97
|
assert not DeepDiff(
|
@@ -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
|