vellum-ai 0.14.9__py3-none-any.whl → 0.14.11__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/client/core/client_wrapper.py +1 -1
- vellum/utils/templating/render.py +4 -1
- vellum/workflows/events/types.py +6 -0
- vellum/workflows/events/workflow.py +3 -2
- vellum/workflows/nodes/core/templating_node/node.py +0 -1
- vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py +49 -0
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +44 -10
- vellum/workflows/nodes/displayable/code_execution_node/utils.py +10 -49
- vellum/workflows/nodes/displayable/final_output_node/node.py +13 -1
- vellum/workflows/nodes/displayable/final_output_node/tests/__init__.py +0 -0
- vellum/workflows/nodes/displayable/final_output_node/tests/test_node.py +20 -0
- vellum/workflows/nodes/utils.py +70 -5
- vellum/workflows/outputs/base.py +2 -4
- vellum/workflows/runner/runner.py +1 -5
- vellum/workflows/workflows/base.py +1 -2
- {vellum_ai-0.14.9.dist-info → vellum_ai-0.14.11.dist-info}/METADATA +1 -1
- {vellum_ai-0.14.9.dist-info → vellum_ai-0.14.11.dist-info}/RECORD +27 -24
- vellum_cli/image_push.py +28 -5
- vellum_ee/workflows/display/nodes/vellum/retry_node.py +92 -2
- vellum_ee/workflows/display/nodes/vellum/tests/test_retry_node.py +47 -0
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +2 -6
- vellum_ee/workflows/display/workflows/base_workflow_display.py +37 -13
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +27 -0
- vellum_ee/workflows/tests/test_display_meta.py +2 -0
- {vellum_ai-0.14.9.dist-info → vellum_ai-0.14.11.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.9.dist-info → vellum_ai-0.14.11.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.9.dist-info → vellum_ai-0.14.11.dist-info}/entry_points.txt +0 -0
@@ -18,7 +18,7 @@ class BaseClientWrapper:
|
|
18
18
|
headers: typing.Dict[str, str] = {
|
19
19
|
"X-Fern-Language": "Python",
|
20
20
|
"X-Fern-SDK-Name": "vellum-ai",
|
21
|
-
"X-Fern-SDK-Version": "0.14.
|
21
|
+
"X-Fern-SDK-Version": "0.14.11",
|
22
22
|
}
|
23
23
|
headers["X_API_KEY"] = self.api_key
|
24
24
|
return headers
|
@@ -2,6 +2,7 @@ import json
|
|
2
2
|
from typing import Any, Dict, Optional
|
3
3
|
|
4
4
|
from jinja2.sandbox import SandboxedEnvironment
|
5
|
+
from pydantic import BaseModel
|
5
6
|
|
6
7
|
from vellum.utils.templating.constants import FilterFunc
|
7
8
|
from vellum.utils.templating.exceptions import JinjaTemplateError
|
@@ -12,6 +13,9 @@ def finalize(obj: Any) -> str:
|
|
12
13
|
if isinstance(obj, (dict, list)):
|
13
14
|
return json.dumps(obj, cls=DefaultStateEncoder)
|
14
15
|
|
16
|
+
if isinstance(obj, BaseModel):
|
17
|
+
return json.dumps(obj.model_dump(), cls=DefaultStateEncoder)
|
18
|
+
|
15
19
|
return str(obj)
|
16
20
|
|
17
21
|
|
@@ -23,7 +27,6 @@ def render_sandboxed_jinja_template(
|
|
23
27
|
jinja_globals: Optional[Dict[str, Any]] = None,
|
24
28
|
) -> str:
|
25
29
|
"""Render a Jinja template within a sandboxed environment."""
|
26
|
-
|
27
30
|
try:
|
28
31
|
environment = SandboxedEnvironment(
|
29
32
|
keep_trailing_newline=True,
|
vellum/workflows/events/types.py
CHANGED
@@ -75,6 +75,7 @@ class BaseDeploymentParentContext(BaseParentContext):
|
|
75
75
|
release_tag_id: UUID
|
76
76
|
release_tag_name: str
|
77
77
|
external_id: Optional[str]
|
78
|
+
metadata: Optional[dict]
|
78
79
|
|
79
80
|
|
80
81
|
class WorkflowDeploymentParentContext(BaseDeploymentParentContext):
|
@@ -104,6 +105,10 @@ class WorkflowSandboxParentContext(BaseParentContext):
|
|
104
105
|
scenario_id: UUID
|
105
106
|
|
106
107
|
|
108
|
+
class APIRequestParentContext(BaseParentContext):
|
109
|
+
type: Literal["API_REQUEST"] = "API_REQUEST"
|
110
|
+
|
111
|
+
|
107
112
|
# Define the discriminated union
|
108
113
|
ParentContext = Annotated[
|
109
114
|
Union[
|
@@ -112,6 +117,7 @@ ParentContext = Annotated[
|
|
112
117
|
WorkflowDeploymentParentContext,
|
113
118
|
PromptDeploymentParentContext,
|
114
119
|
WorkflowSandboxParentContext,
|
120
|
+
APIRequestParentContext,
|
115
121
|
],
|
116
122
|
Field(discriminator="type"),
|
117
123
|
]
|
@@ -39,14 +39,15 @@ class _BaseWorkflowEvent(BaseEvent):
|
|
39
39
|
return self.body.workflow_definition
|
40
40
|
|
41
41
|
|
42
|
-
class
|
42
|
+
class NodeEventDisplayContext(UniversalBaseModel):
|
43
43
|
input_display: Dict[str, UUID]
|
44
44
|
output_display: Dict[str, UUID]
|
45
45
|
port_display: Dict[str, UUID]
|
46
|
+
subworkflow_display: Optional["WorkflowEventDisplayContext"] = None
|
46
47
|
|
47
48
|
|
48
49
|
class WorkflowEventDisplayContext(UniversalBaseModel):
|
49
|
-
node_displays: Dict[str,
|
50
|
+
node_displays: Dict[str, NodeEventDisplayContext]
|
50
51
|
workflow_inputs: Dict[str, UUID]
|
51
52
|
workflow_outputs: Dict[str, UUID]
|
52
53
|
|
@@ -82,7 +82,6 @@ class TemplatingNode(BaseNode[StateType], Generic[StateType, _OutputType], metac
|
|
82
82
|
def run(self) -> Outputs:
|
83
83
|
rendered_template = self._render_template()
|
84
84
|
result = self._cast_rendered_template(rendered_template)
|
85
|
-
|
86
85
|
return self.Outputs(result=result)
|
87
86
|
|
88
87
|
def _render_template(self) -> str:
|
@@ -234,3 +234,52 @@ def test_templating_node__last_chat_message():
|
|
234
234
|
|
235
235
|
# THEN the output is the expected JSON
|
236
236
|
assert outputs.result == [ChatMessage(role="USER", text="Hello")]
|
237
|
+
|
238
|
+
|
239
|
+
def test_templating_node__function_call_value_input():
|
240
|
+
# GIVEN a templating node that receives a FunctionCallVellumValue
|
241
|
+
class FunctionCallTemplateNode(TemplatingNode[BaseState, FunctionCall]):
|
242
|
+
template = """{{ function_call }}"""
|
243
|
+
inputs = {
|
244
|
+
"function_call": FunctionCallVellumValue(
|
245
|
+
type="FUNCTION_CALL",
|
246
|
+
value=FunctionCall(name="test_function", arguments={"key": "value"}, id="test_id", state="FULFILLED"),
|
247
|
+
)
|
248
|
+
}
|
249
|
+
|
250
|
+
# WHEN the node is run
|
251
|
+
node = FunctionCallTemplateNode()
|
252
|
+
outputs = node.run()
|
253
|
+
|
254
|
+
# THEN the output is the expected function call
|
255
|
+
assert outputs.result == FunctionCall(
|
256
|
+
name="test_function", arguments={"key": "value"}, id="test_id", state="FULFILLED"
|
257
|
+
)
|
258
|
+
|
259
|
+
|
260
|
+
def test_templating_node__function_call_as_json():
|
261
|
+
# GIVEN a node that receives a FunctionCallVellumValue but outputs as Json
|
262
|
+
class JsonOutputNode(TemplatingNode[BaseState, Json]):
|
263
|
+
template = """{{ function_call }}"""
|
264
|
+
inputs = {
|
265
|
+
"function_call": FunctionCallVellumValue(
|
266
|
+
type="FUNCTION_CALL",
|
267
|
+
value=FunctionCall(name="test_function", arguments={"key": "value"}, id="test_id", state="FULFILLED"),
|
268
|
+
)
|
269
|
+
}
|
270
|
+
|
271
|
+
# WHEN the node is run
|
272
|
+
node = JsonOutputNode()
|
273
|
+
outputs = node.run()
|
274
|
+
|
275
|
+
# THEN we get just the FunctionCall data as JSON
|
276
|
+
assert outputs.result == {
|
277
|
+
"name": "test_function",
|
278
|
+
"arguments": {"key": "value"},
|
279
|
+
"id": "test_id",
|
280
|
+
"state": "FULFILLED",
|
281
|
+
}
|
282
|
+
|
283
|
+
# AND we can access fields directly
|
284
|
+
assert outputs.result["arguments"] == {"key": "value"}
|
285
|
+
assert outputs.result["name"] == "test_function"
|
@@ -458,16 +458,7 @@ def main(word: str) -> int:
|
|
458
458
|
node.run()
|
459
459
|
|
460
460
|
# THEN the node should have produced the exception we expected
|
461
|
-
assert
|
462
|
-
exc_info.value.message
|
463
|
-
== """\
|
464
|
-
2 validation errors for FunctionCall
|
465
|
-
arguments
|
466
|
-
Field required [type=missing, input_value={'n': 'hello', 'a': {}}, input_type=dict]
|
467
|
-
name
|
468
|
-
Field required [type=missing, input_value={'n': 'hello', 'a': {}}, input_type=dict]\
|
469
|
-
"""
|
470
|
-
)
|
461
|
+
assert exc_info.value.message == "Expected an output of type 'FunctionCall', but received 'dict'"
|
471
462
|
|
472
463
|
|
473
464
|
def test_run_node__run_inline__valid_dict_to_pydantic_any_type():
|
@@ -743,3 +734,46 @@ Node.js v21.7.3
|
|
743
734
|
|
744
735
|
# AND the error should contain the execution error details
|
745
736
|
assert exc_info.value.message == message
|
737
|
+
|
738
|
+
|
739
|
+
def test_run_node__execute_code__list_extends():
|
740
|
+
# GIVEN a node that will return a list with output type Json
|
741
|
+
class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, Json]):
|
742
|
+
code = """\
|
743
|
+
def main(left, right):
|
744
|
+
all = []
|
745
|
+
all.extend(left)
|
746
|
+
all.extend(right)
|
747
|
+
return all
|
748
|
+
"""
|
749
|
+
code_inputs = {
|
750
|
+
"left": [1, 2, 3],
|
751
|
+
"right": [4, 5, 6],
|
752
|
+
}
|
753
|
+
runtime = "PYTHON_3_11_6"
|
754
|
+
|
755
|
+
# WHEN we run the node
|
756
|
+
node = ExampleCodeExecutionNode()
|
757
|
+
outputs = node.run()
|
758
|
+
|
759
|
+
# AND the result should be the correct output
|
760
|
+
assert outputs == {"result": [1, 2, 3, 4, 5, 6], "log": ""}
|
761
|
+
|
762
|
+
|
763
|
+
def test_run_node__execute_code__non_str_print():
|
764
|
+
# GIVEN a node that will print a non-string value
|
765
|
+
class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, str]):
|
766
|
+
code = """\
|
767
|
+
def main():
|
768
|
+
print(type(1))
|
769
|
+
return "hello"
|
770
|
+
"""
|
771
|
+
code_inputs = {}
|
772
|
+
runtime = "PYTHON_3_11_6"
|
773
|
+
|
774
|
+
# WHEN we run the node
|
775
|
+
node = ExampleCodeExecutionNode()
|
776
|
+
outputs = node.run()
|
777
|
+
|
778
|
+
# AND the result should be the correct output
|
779
|
+
assert outputs == {"result": "hello", "log": "<class 'int'>\n"}
|
@@ -1,12 +1,12 @@
|
|
1
1
|
import io
|
2
2
|
import os
|
3
|
-
import
|
4
|
-
from typing import Any, Tuple, Union, get_args, get_origin
|
3
|
+
from typing import Any, Tuple, Union
|
5
4
|
|
6
|
-
from pydantic import BaseModel
|
5
|
+
from pydantic import BaseModel
|
7
6
|
|
8
7
|
from vellum.workflows.errors.types import WorkflowErrorCode
|
9
8
|
from vellum.workflows.exceptions import NodeException
|
9
|
+
from vellum.workflows.nodes.utils import cast_to_output_type
|
10
10
|
from vellum.workflows.types.core import EntityInputsInterface
|
11
11
|
|
12
12
|
|
@@ -67,49 +67,6 @@ def _clean_for_dict_wrapper(obj):
|
|
67
67
|
return obj
|
68
68
|
|
69
69
|
|
70
|
-
def _get_type_name(obj: Any) -> str:
|
71
|
-
if isinstance(obj, type):
|
72
|
-
return obj.__name__
|
73
|
-
|
74
|
-
if get_origin(obj) is Union:
|
75
|
-
children = [_get_type_name(child) for child in get_args(obj)]
|
76
|
-
return " | ".join(children)
|
77
|
-
|
78
|
-
return str(obj)
|
79
|
-
|
80
|
-
|
81
|
-
def _cast_to_output_type(result: Any, output_type: Any) -> Any:
|
82
|
-
is_valid_output_type = isinstance(output_type, type)
|
83
|
-
if get_origin(output_type) is Union:
|
84
|
-
allowed_types = get_args(output_type)
|
85
|
-
for allowed_type in allowed_types:
|
86
|
-
try:
|
87
|
-
return _cast_to_output_type(result, allowed_type)
|
88
|
-
except NodeException:
|
89
|
-
continue
|
90
|
-
elif get_origin(output_type) is list:
|
91
|
-
allowed_item_type = get_args(output_type)[0]
|
92
|
-
if isinstance(result, list):
|
93
|
-
return [_cast_to_output_type(item, allowed_item_type) for item in result]
|
94
|
-
elif is_valid_output_type and issubclass(output_type, BaseModel) and not isinstance(result, output_type):
|
95
|
-
try:
|
96
|
-
return output_type.model_validate(result)
|
97
|
-
except ValidationError as e:
|
98
|
-
raise NodeException(
|
99
|
-
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
100
|
-
message=re.sub(r"\s+For further information visit [^\s]+", "", str(e)),
|
101
|
-
) from e
|
102
|
-
elif is_valid_output_type and isinstance(result, output_type):
|
103
|
-
return result
|
104
|
-
|
105
|
-
output_type_name = _get_type_name(output_type)
|
106
|
-
result_type_name = _get_type_name(type(result))
|
107
|
-
raise NodeException(
|
108
|
-
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
109
|
-
message=f"Expected an output of type '{output_type_name}', but received '{result_type_name}'",
|
110
|
-
)
|
111
|
-
|
112
|
-
|
113
70
|
def run_code_inline(
|
114
71
|
code: str,
|
115
72
|
inputs: EntityInputsInterface,
|
@@ -117,6 +74,11 @@ def run_code_inline(
|
|
117
74
|
) -> Tuple[str, Any]:
|
118
75
|
log_buffer = io.StringIO()
|
119
76
|
|
77
|
+
def _inline_print(*args: Any, **kwargs: Any) -> None:
|
78
|
+
str_args = [str(arg) for arg in args]
|
79
|
+
print_line = f"{' '.join(str_args)}\n"
|
80
|
+
log_buffer.write(print_line)
|
81
|
+
|
120
82
|
def wrap_value(value):
|
121
83
|
if isinstance(value, list):
|
122
84
|
return ListWrapper(
|
@@ -135,7 +97,7 @@ def run_code_inline(
|
|
135
97
|
exec_globals = {
|
136
98
|
"__arg__inputs": {name: wrap_value(value) for name, value in inputs.items()},
|
137
99
|
"__arg__out": None,
|
138
|
-
"print":
|
100
|
+
"print": _inline_print,
|
139
101
|
}
|
140
102
|
run_args = [f"{name}=__arg__inputs['{name}']" for name in inputs.keys()]
|
141
103
|
execution_code = f"""\
|
@@ -154,7 +116,6 @@ __arg__out = main({", ".join(run_args)})
|
|
154
116
|
logs = log_buffer.getvalue()
|
155
117
|
result = exec_globals["__arg__out"]
|
156
118
|
|
157
|
-
|
158
|
-
result = _cast_to_output_type(result, output_type)
|
119
|
+
result = cast_to_output_type(result, output_type)
|
159
120
|
|
160
121
|
return logs, result
|
@@ -1,7 +1,9 @@
|
|
1
1
|
from typing import Any, Dict, Generic, Tuple, Type, TypeVar, get_args
|
2
2
|
|
3
|
+
from vellum.workflows.constants import undefined
|
3
4
|
from vellum.workflows.nodes.bases import BaseNode
|
4
5
|
from vellum.workflows.nodes.bases.base import BaseNodeMeta
|
6
|
+
from vellum.workflows.nodes.utils import cast_to_output_type
|
5
7
|
from vellum.workflows.types import MergeBehavior
|
6
8
|
from vellum.workflows.types.generics import StateType
|
7
9
|
from vellum.workflows.types.utils import get_original_base
|
@@ -48,4 +50,14 @@ class FinalOutputNode(BaseNode[StateType], Generic[StateType, _OutputType], meta
|
|
48
50
|
class Outputs(BaseNode.Outputs):
|
49
51
|
# We use our mypy plugin to override the _OutputType with the actual output type
|
50
52
|
# for downstream references to this output.
|
51
|
-
value: _OutputType # type: ignore[valid-type]
|
53
|
+
value: _OutputType = undefined # type: ignore[valid-type]
|
54
|
+
|
55
|
+
def run(self) -> Outputs:
|
56
|
+
original_outputs = self.Outputs()
|
57
|
+
|
58
|
+
return self.Outputs(
|
59
|
+
value=cast_to_output_type(
|
60
|
+
original_outputs.value,
|
61
|
+
self.__class__.get_output_type(),
|
62
|
+
)
|
63
|
+
)
|
File without changes
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import pytest
|
2
|
+
|
3
|
+
from vellum.workflows.exceptions import NodeException
|
4
|
+
from vellum.workflows.nodes.displayable.final_output_node import FinalOutputNode
|
5
|
+
from vellum.workflows.state.base import BaseState
|
6
|
+
|
7
|
+
|
8
|
+
def test_final_output_node__mismatched_output_type():
|
9
|
+
# GIVEN a FinalOutputNode with a mismatched output type
|
10
|
+
class StringOutputNode(FinalOutputNode[BaseState, str]):
|
11
|
+
class Outputs(FinalOutputNode.Outputs):
|
12
|
+
value = {"foo": "bar"}
|
13
|
+
|
14
|
+
# WHEN the node is run
|
15
|
+
node = StringOutputNode()
|
16
|
+
with pytest.raises(NodeException) as exc_info:
|
17
|
+
node.run()
|
18
|
+
|
19
|
+
# THEN an error is raised
|
20
|
+
assert str(exc_info.value) == "Expected an output of type 'str', but received 'dict'"
|
vellum/workflows/nodes/utils.py
CHANGED
@@ -2,10 +2,12 @@ from functools import cache
|
|
2
2
|
import json
|
3
3
|
import sys
|
4
4
|
from types import ModuleType
|
5
|
-
from typing import Any, Callable, Optional, Type, TypeVar, Union, get_args, get_origin
|
5
|
+
from typing import Any, Callable, Dict, ForwardRef, List, Optional, Type, TypeVar, Union, get_args, get_origin
|
6
6
|
|
7
|
-
from pydantic import BaseModel
|
7
|
+
from pydantic import BaseModel, create_model
|
8
8
|
|
9
|
+
from vellum.workflows.errors.types import WorkflowErrorCode
|
10
|
+
from vellum.workflows.exceptions import NodeException
|
9
11
|
from vellum.workflows.nodes import BaseNode
|
10
12
|
from vellum.workflows.nodes.bases.base_adornment_node import BaseAdornmentNode
|
11
13
|
from vellum.workflows.ports.port import Port
|
@@ -109,7 +111,11 @@ def parse_type_from_str(result_as_str: str, output_type: Any) -> Any:
|
|
109
111
|
|
110
112
|
if output_type is Json:
|
111
113
|
try:
|
112
|
-
|
114
|
+
data = json.loads(result_as_str)
|
115
|
+
# If we got a FunctionCallVellumValue, return just the value
|
116
|
+
if isinstance(data, dict) and data.get("type") == "FUNCTION_CALL" and "value" in data:
|
117
|
+
return data["value"]
|
118
|
+
return data
|
113
119
|
except json.JSONDecodeError:
|
114
120
|
raise ValueError("Invalid JSON format for result_as_str")
|
115
121
|
|
@@ -124,9 +130,68 @@ def parse_type_from_str(result_as_str: str, output_type: Any) -> Any:
|
|
124
130
|
if issubclass(output_type, BaseModel):
|
125
131
|
try:
|
126
132
|
data = json.loads(result_as_str)
|
133
|
+
# If we got a FunctionCallVellumValue extract FunctionCall,
|
134
|
+
if (
|
135
|
+
hasattr(output_type, "__name__")
|
136
|
+
and output_type.__name__ == "FunctionCall"
|
137
|
+
and isinstance(data, dict)
|
138
|
+
and "value" in data
|
139
|
+
):
|
140
|
+
data = data["value"]
|
141
|
+
return output_type.model_validate(data)
|
127
142
|
except json.JSONDecodeError:
|
128
143
|
raise ValueError("Invalid JSON format for result_as_str")
|
129
144
|
|
130
|
-
return output_type.model_validate(data)
|
131
|
-
|
132
145
|
raise ValueError(f"Unsupported output type: {output_type}")
|
146
|
+
|
147
|
+
|
148
|
+
def _get_type_name(obj: Any) -> str:
|
149
|
+
if isinstance(obj, type):
|
150
|
+
return obj.__name__
|
151
|
+
|
152
|
+
if get_origin(obj) is Union:
|
153
|
+
children = [_get_type_name(child) for child in get_args(obj)]
|
154
|
+
return " | ".join(children)
|
155
|
+
|
156
|
+
return str(obj)
|
157
|
+
|
158
|
+
|
159
|
+
def _clean_output_type(output_type: Any) -> Any:
|
160
|
+
"""
|
161
|
+
pydantic currently has a bug where it doesn't support forward references in the create_model function. It will
|
162
|
+
fail due to a max recursion depth error. This negatively impacts our `Json` type, but could also apply to other
|
163
|
+
user defined ForwardRef types.
|
164
|
+
"""
|
165
|
+
if get_origin(output_type) is Union:
|
166
|
+
clean_args = [_clean_output_type(child) for child in get_args(output_type)]
|
167
|
+
return Union[tuple(clean_args)]
|
168
|
+
|
169
|
+
if isinstance(output_type, ForwardRef):
|
170
|
+
# Here is where we prevent the max recursion depth error
|
171
|
+
return Any
|
172
|
+
|
173
|
+
if get_origin(output_type) is list:
|
174
|
+
clean_args = [_clean_output_type(child) for child in get_args(output_type)]
|
175
|
+
return List[clean_args[0]] # type: ignore[valid-type]
|
176
|
+
|
177
|
+
if get_origin(output_type) is dict:
|
178
|
+
clean_args = [_clean_output_type(child) for child in get_args(output_type)]
|
179
|
+
return Dict[clean_args[0], clean_args[1]] # type: ignore[valid-type]
|
180
|
+
|
181
|
+
return output_type
|
182
|
+
|
183
|
+
|
184
|
+
def cast_to_output_type(result: Any, output_type: Any) -> Any:
|
185
|
+
clean_output_type = _clean_output_type(output_type)
|
186
|
+
DynamicModel = create_model("Output", output_type=(clean_output_type, ...))
|
187
|
+
|
188
|
+
try:
|
189
|
+
# mypy doesn't realize that this dynamic model has the output_type field defined above
|
190
|
+
return DynamicModel.model_validate({"output_type": result}).output_type # type: ignore[attr-defined]
|
191
|
+
except Exception:
|
192
|
+
output_type_name = _get_type_name(output_type)
|
193
|
+
result_type_name = _get_type_name(type(result))
|
194
|
+
raise NodeException(
|
195
|
+
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
196
|
+
message=f"Expected an output of type '{output_type_name}', but received '{result_type_name}'",
|
197
|
+
)
|
vellum/workflows/outputs/base.py
CHANGED
@@ -204,13 +204,11 @@ class BaseOutputs(metaclass=_BaseOutputsMeta):
|
|
204
204
|
if not isinstance(other, dict):
|
205
205
|
return super().__eq__(other)
|
206
206
|
|
207
|
-
outputs = {
|
208
|
-
name: value for name, value in vars(self).items() if not name.startswith("_") and value is not undefined
|
209
|
-
}
|
207
|
+
outputs = {ref.name: value for ref, value in self if value is not undefined}
|
210
208
|
return outputs == other
|
211
209
|
|
212
210
|
def __repr__(self) -> str:
|
213
|
-
values = f"{', '.join(f'{
|
211
|
+
values = f"{', '.join(f'{ref.name}={value}' for ref, value in self if value is not undefined)}"
|
214
212
|
return f"{self.__class__.__name__}({values})"
|
215
213
|
|
216
214
|
def __iter__(self) -> Iterator[Tuple[OutputReference, Any]]:
|
@@ -328,11 +328,7 @@ class WorkflowRunner(Generic[StateType]):
|
|
328
328
|
node_definition=node.__class__,
|
329
329
|
error=e.error,
|
330
330
|
),
|
331
|
-
parent=
|
332
|
-
span_id=span_id,
|
333
|
-
workflow_definition=self.workflow.__class__,
|
334
|
-
parent=self._parent_context,
|
335
|
-
),
|
331
|
+
parent=parent_context,
|
336
332
|
)
|
337
333
|
)
|
338
334
|
except Exception as e:
|
@@ -24,7 +24,6 @@ from typing import (
|
|
24
24
|
get_args,
|
25
25
|
)
|
26
26
|
|
27
|
-
from vellum.workflows.context import get_execution_context
|
28
27
|
from vellum.workflows.edges import Edge
|
29
28
|
from vellum.workflows.emitters.base import BaseWorkflowEmitter
|
30
29
|
from vellum.workflows.errors import WorkflowError, WorkflowErrorCode
|
@@ -481,7 +480,7 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
|
481
480
|
return self.get_inputs_class()()
|
482
481
|
|
483
482
|
def get_default_state(self, workflow_inputs: Optional[InputsType] = None) -> StateType:
|
484
|
-
execution_context =
|
483
|
+
execution_context = self._execution_context
|
485
484
|
return self.get_state_class()(
|
486
485
|
meta=(
|
487
486
|
StateMeta(
|
@@ -3,7 +3,7 @@ vellum_cli/README.md,sha256=2NudRoLzWxNKqnuVy1JuQ7DerIaxWGYkrH8kMd-asIE,90
|
|
3
3
|
vellum_cli/__init__.py,sha256=7aO9XFnaEVRiVshn86cFudebFUccT-gV8xIARJWqKYo,12257
|
4
4
|
vellum_cli/aliased_group.py,sha256=ugW498j0yv4ALJ8vS9MsO7ctDW7Jlir9j6nE_uHAP8c,3363
|
5
5
|
vellum_cli/config.py,sha256=aKnhvM5B8QdPA4cQC5Sqg7ImP-vNcVdSkZmk_OBpQTw,9309
|
6
|
-
vellum_cli/image_push.py,sha256=
|
6
|
+
vellum_cli/image_push.py,sha256=4auU15Pb6c8DTGvT-AQ5HHXXrvIvEDs6L02d4OvJYI8,5199
|
7
7
|
vellum_cli/init.py,sha256=WpnMXPItPmh0f0bBGIer3p-e5gu8DUGwSArT_FuoMEw,5093
|
8
8
|
vellum_cli/logger.py,sha256=PuRFa0WCh4sAGFS5aqWB0QIYpS6nBWwPJrIXpWxugV4,1022
|
9
9
|
vellum_cli/ping.py,sha256=lWyJw6sziXjyTopTYRdFF5hV-sYPVDdX0yVbG5fzcY4,585
|
@@ -44,13 +44,14 @@ vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=8CPnn06HIBxBOiECevUf
|
|
44
44
|
vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=HkNMgdQELiON42jdO-xDLmqrEKdGx1RVqrz2DXNTLS8,3239
|
45
45
|
vellum_ee/workflows/display/nodes/vellum/note_node.py,sha256=TMb8txILu2uWjzoxaghjgjlzeBAgzn4vkP_8zSh2qoE,1151
|
46
46
|
vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=LFjLUrH6sJ4czPnExdRqFr0PB_yKBMLXLvK5GAzIAgc,3273
|
47
|
-
vellum_ee/workflows/display/nodes/vellum/retry_node.py,sha256
|
47
|
+
vellum_ee/workflows/display/nodes/vellum/retry_node.py,sha256=-eH41LUcpUeZ8Jt5f40-e7bn2nymeKnsYsmuprVSo7g,5805
|
48
48
|
vellum_ee/workflows/display/nodes/vellum/search_node.py,sha256=TxcAGZDl_hvJ7Y1hUi9YVEVrj9Ie0hKkASdpfRL4_cs,9227
|
49
49
|
vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py,sha256=62baAElKoRKIoba0lLhnrXGWWx96B73VxKGxh7BaIxc,2612
|
50
50
|
vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=JVIMPR3WpveOCWZubHKZkE04mavnTdb_9QY_r3XliRg,3424
|
51
51
|
vellum_ee/workflows/display/nodes/vellum/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
52
52
|
vellum_ee/workflows/display/nodes/vellum/tests/test_error_node.py,sha256=ulrpoYUW-5kIxfG4Lf5F2p0k_EoYKhmahEbF3P_eruM,1648
|
53
53
|
vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py,sha256=bg9INsXiWfyK047u8TD1oEOFYrqDq8GC7Hvgz69n7BE,1988
|
54
|
+
vellum_ee/workflows/display/nodes/vellum/tests/test_retry_node.py,sha256=NuIw8Yb42KUdoGi3Ur8_7VPg50IC4hNrwAkCociwqNk,2091
|
54
55
|
vellum_ee/workflows/display/nodes/vellum/tests/test_try_node.py,sha256=mtzB8LJlFCHVFM4H5AanLp29gQfaVmnN4A4iaRGJHoI,2427
|
55
56
|
vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py,sha256=4YUaTeD_OWF-UaPMyOTBTu9skGC1jgSHlAYrzbH7Z04,5039
|
56
57
|
vellum_ee/workflows/display/nodes/vellum/try_node.py,sha256=HBfGz4yt9GlmMW9JxzaCacPnHBDNIeXE8Jhqr9DqLLw,6191
|
@@ -60,7 +61,7 @@ vellum_ee/workflows/display/tests/test_vellum_workflow_display.py,sha256=cdpUoDN
|
|
60
61
|
vellum_ee/workflows/display/tests/workflow_serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
61
62
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
62
63
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py,sha256=A1-tIpC5KIKG9JA_rkd1nLS8zUG3Kb4QiVdvb3boFxE,2509
|
63
|
-
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py,sha256=
|
64
|
+
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py,sha256=jw8keqLNwqtaRSAqStsMqSH_OuuaTQm2MvIx8NTWAa4,14905
|
64
65
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py,sha256=1cszL6N6FNGVm61MOa7AEiHnF0QjZWqDQuPOp4yiG94,18277
|
65
66
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py,sha256=-12ZkZb3f5gyoNASV2yeQtMo5HmNsVEo8nXwL6IC-I8,6261
|
66
67
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py,sha256=6th6kCwzql6lddjkTQx4Jbvvs4ChqtJwctW-B4QuBhI,37352
|
@@ -89,9 +90,9 @@ vellum_ee/workflows/display/utils/expressions.py,sha256=9FpOslDI-RCR5m4TgAu9KCHh
|
|
89
90
|
vellum_ee/workflows/display/utils/vellum.py,sha256=UjK_RxnSEmlIu9klGCPWU5RAQBmgZ7cRbRdgxaTbubE,8081
|
90
91
|
vellum_ee/workflows/display/vellum.py,sha256=7mqQaKZPPrLMcXSAQkPIxCy5x8HkKs5PbCu3GRaC2o8,8507
|
91
92
|
vellum_ee/workflows/display/workflows/__init__.py,sha256=kapXsC67VJcgSuiBMa86FdePG5A9kMB5Pi4Uy1O2ob4,207
|
92
|
-
vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=
|
93
|
+
vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=Z9s_rtqAH5IfBtXOw40gbiT0GZeN6JRdFwGgPT-DIYM,20143
|
93
94
|
vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=kp0u8LN_2IwshLrhMImhpZx1hRyAcD5gXY-kDuuaGMQ,1269
|
94
|
-
vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=
|
95
|
+
vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=STVSG0eL97mdnwBA5nOOgW8AuK8k-b8kWDyHKatNXIA,4259
|
95
96
|
vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=mbAzCpswOek34ITeTkesbVreCXpulj4NFjIg3RcdVZ8,18243
|
96
97
|
vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
97
98
|
vellum_ee/workflows/server/virtual_file_loader.py,sha256=X_DdNK7MfyOjKWekk6YQpOSCT6klKcdjT6nVJcBH1sM,1481
|
@@ -114,7 +115,7 @@ vellum_ee/workflows/tests/local_workflow/nodes/__init__.py,sha256=1F6jxUpSKfPXPj
|
|
114
115
|
vellum_ee/workflows/tests/local_workflow/nodes/final_output.py,sha256=ZX7zBv87zirg0w9VKUW3QVDSdBLDqcqAMZjCL_oWbpU,297
|
115
116
|
vellum_ee/workflows/tests/local_workflow/nodes/templating_node.py,sha256=NQwFN61QkHfI3Vssz-B0NKGfupK8PU0FDSAIAhYBLi0,325
|
116
117
|
vellum_ee/workflows/tests/local_workflow/workflow.py,sha256=A4qOzOPNwePYxWbcAgIPLsmrVS_aVEZEc-wULSv787Q,393
|
117
|
-
vellum_ee/workflows/tests/test_display_meta.py,sha256=
|
118
|
+
vellum_ee/workflows/tests/test_display_meta.py,sha256=xLQ7QtIXIiIm61pm2lyl588Ohzc3pjyq1nDp-qVu2Fs,2295
|
118
119
|
vellum_ee/workflows/tests/test_server.py,sha256=M6vvQ2hjIpDWtQdDM9EPbMvUrZ93niAuYnxMNJWOjPA,511
|
119
120
|
vellum_ee/workflows/tests/test_virtual_files.py,sha256=TJEcMR0v2S8CkloXNmCHA0QW0K6pYNGaIjraJz7sFvY,2762
|
120
121
|
vellum/__init__.py,sha256=a_aM1_A04XGma4MAIDNeBF9BKzWbiQaVVMRzImHuxjA,36438
|
@@ -122,7 +123,7 @@ vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
|
|
122
123
|
vellum/client/__init__.py,sha256=tKtdM1_GqmGq1gpi9ydWD_T-MM7fPn8QdHh8ww19cNI,117564
|
123
124
|
vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
|
124
125
|
vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
|
125
|
-
vellum/client/core/client_wrapper.py,sha256=
|
126
|
+
vellum/client/core/client_wrapper.py,sha256=l2ugjFyqT9acEpOr6jNbb_P6uA6RL_Y5IP-FTsOd_uQ,1869
|
126
127
|
vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
|
127
128
|
vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
|
128
129
|
vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
|
@@ -1287,7 +1288,7 @@ vellum/utils/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
|
|
1287
1288
|
vellum/utils/templating/constants.py,sha256=8OHMO6WFAEimbIaiHc5gy6s91D7_KvW-vTlEMWwvl_M,711
|
1288
1289
|
vellum/utils/templating/custom_filters.py,sha256=XVHriwazejRZmxB_eg4xHgCxl7AiQQ2sx-hRLMmylfU,885
|
1289
1290
|
vellum/utils/templating/exceptions.py,sha256=cDp140PP4OnInW4qAvg3KqiSiF70C71UyEAKRBR1Abo,46
|
1290
|
-
vellum/utils/templating/render.py,sha256=
|
1291
|
+
vellum/utils/templating/render.py,sha256=P2t9qU4w_WdXAVLM5Nj3bc1-XlIKOkwK-czQ80pHBag,2172
|
1291
1292
|
vellum/utils/templating/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1292
1293
|
vellum/utils/templating/tests/test_custom_filters.py,sha256=mkJwc7t1gE13SKgPxhF-lN_m2XGCkphCB9Te81dGekI,532
|
1293
1294
|
vellum/utils/typing.py,sha256=wx_daFqD69cYkuJTVnvNrpjhqC3uuhbnyJ9_bIwC9OU,327
|
@@ -1314,8 +1315,8 @@ vellum/workflows/events/__init__.py,sha256=6pxxceJo2dcaRkWtkDAYlUQZ-PHBQSZytIoyu
|
|
1314
1315
|
vellum/workflows/events/node.py,sha256=uHT6If0esgZ3nLjrjmUPTKf3qbjGhoV_x5YKpjDBDcU,5280
|
1315
1316
|
vellum/workflows/events/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1316
1317
|
vellum/workflows/events/tests/test_event.py,sha256=uRfMwSOqU-ROeZKCEngGvvJYlOZuxBhnC3qH5AGi3fM,15399
|
1317
|
-
vellum/workflows/events/types.py,sha256=
|
1318
|
-
vellum/workflows/events/workflow.py,sha256=
|
1318
|
+
vellum/workflows/events/types.py,sha256=AeTJaQt_fNHDLI4nyBzo7XrW9QQybRC09AKzu3kEYEE,3575
|
1319
|
+
vellum/workflows/events/workflow.py,sha256=sLO29djAeHGVd4hLhaNtOQ48uwUjfl-DotZQt06PxQA,6033
|
1319
1320
|
vellum/workflows/exceptions.py,sha256=NiBiR3ggfmPxBVqD-H1SqmjI-7mIn0EStSN1BqApvCM,1213
|
1320
1321
|
vellum/workflows/expressions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1321
1322
|
vellum/workflows/expressions/accessor.py,sha256=ItZF7fMLzVTqsdAiaXb5SiDupXmX0X9xbIus1W6hRds,1870
|
@@ -1381,8 +1382,8 @@ vellum/workflows/nodes/core/retry_node/node.py,sha256=Vt3fx4G-DRIb9a-IHIUfaAclgf
|
|
1381
1382
|
vellum/workflows/nodes/core/retry_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1382
1383
|
vellum/workflows/nodes/core/retry_node/tests/test_node.py,sha256=RM_OHwxrHwyxvlQQBJPqVBxpedFuWQ9h2-Xa3kP75sc,4399
|
1383
1384
|
vellum/workflows/nodes/core/templating_node/__init__.py,sha256=GmyuYo81_A1_Bz6id69ozVFS6FKiuDsZTiA3I6MaL2U,70
|
1384
|
-
vellum/workflows/nodes/core/templating_node/node.py,sha256
|
1385
|
-
vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=
|
1385
|
+
vellum/workflows/nodes/core/templating_node/node.py,sha256=-JIqLUv6Xpx_LTVZt7whQ2X2VatgHDdTxjMrz64luEs,3721
|
1386
|
+
vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=ldnmSASx0TfAnT3ZvU0AXtN0diZGrfySiXipuJIIzWU,9055
|
1386
1387
|
vellum/workflows/nodes/core/try_node/__init__.py,sha256=JVD4DrldTIqFQQFrubs9KtWCCc0YCAc7Fzol5ZWIWeM,56
|
1387
1388
|
vellum/workflows/nodes/core/try_node/node.py,sha256=_0df2_6kim8pW4hB7IXUJOYS4fLduaeGDH4rRhYYvcg,4212
|
1388
1389
|
vellum/workflows/nodes/core/try_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -1411,12 +1412,14 @@ vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=pdDrjI8wdq
|
|
1411
1412
|
vellum/workflows/nodes/displayable/code_execution_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1412
1413
|
vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1413
1414
|
vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/main.py,sha256=5QsbmkzSlSbcbWTG_JmIqcP-JNJzOPTKxGzdHos19W4,79
|
1414
|
-
vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py,sha256=
|
1415
|
-
vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256
|
1415
|
+
vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py,sha256=6_RMWedAWU8Zrl0HpQorQgDfjV9khnhSouZeKadClyI,23441
|
1416
|
+
vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=-7YdoF-NO6sYRFLrdkd0n1FhQFiZbJBK5jFcLNghgVo,3635
|
1416
1417
|
vellum/workflows/nodes/displayable/conditional_node/__init__.py,sha256=AS_EIqFdU1F9t8aLmbZU-rLh9ry6LCJ0uj0D8F0L5Uw,72
|
1417
1418
|
vellum/workflows/nodes/displayable/conditional_node/node.py,sha256=Qjfl33gZ3JEgxBA1EgzSUebboGvsARthIxxcQyvx5Gg,1152
|
1418
1419
|
vellum/workflows/nodes/displayable/final_output_node/__init__.py,sha256=G7VXM4OWpubvSJtVkGmMNeqgb9GkM7qZT838eL18XU4,72
|
1419
|
-
vellum/workflows/nodes/displayable/final_output_node/node.py,sha256=
|
1420
|
+
vellum/workflows/nodes/displayable/final_output_node/node.py,sha256=PuQ0RvtAmoSIZ5En_92tym7gpSMEoiHgwu20-UDbC7o,2368
|
1421
|
+
vellum/workflows/nodes/displayable/final_output_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1422
|
+
vellum/workflows/nodes/displayable/final_output_node/tests/test_node.py,sha256=E6LQ74qZjY4Xi4avx2qdOCgGhF8pEcNLBh8cqYRkzMI,709
|
1420
1423
|
vellum/workflows/nodes/displayable/guardrail_node/__init__.py,sha256=Ab5eXmOoBhyV4dMWdzh32HLUmnPIBEK_zFCT38C4Fng,68
|
1421
1424
|
vellum/workflows/nodes/displayable/guardrail_node/node.py,sha256=h5nIBzQxbXTrdTq1wjDcekk1RV4-rKUNCshqdBAiJJY,4025
|
1422
1425
|
vellum/workflows/nodes/displayable/inline_prompt_node/__init__.py,sha256=gSUOoEZLlrx35-tQhSAd3An8WDwBqyiQh-sIebLU9wU,74
|
@@ -1448,9 +1451,9 @@ vellum/workflows/nodes/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQ
|
|
1448
1451
|
vellum/workflows/nodes/experimental/openai_chat_completion_node/__init__.py,sha256=lsyD9laR9p7kx5-BXGH2gUTM242UhKy8SMV0SR6S2iE,90
|
1449
1452
|
vellum/workflows/nodes/experimental/openai_chat_completion_node/node.py,sha256=1EGeiaT-Zoo6pttQFKKBcdf3dmhAbjKGaErYD5FFwlc,10185
|
1450
1453
|
vellum/workflows/nodes/mocks.py,sha256=gvM2tyoe-V84jFbFdhQsyGAPyQBzmjn_CkhT_yxccgY,499
|
1451
|
-
vellum/workflows/nodes/utils.py,sha256=
|
1454
|
+
vellum/workflows/nodes/utils.py,sha256=uaTPGYp4utenz_QDghqQ23Q1iCsGHQ40nNZh1g9H9WI,7117
|
1452
1455
|
vellum/workflows/outputs/__init__.py,sha256=AyZ4pRh_ACQIGvkf0byJO46EDnSix1ZCAXfvh-ms1QE,94
|
1453
|
-
vellum/workflows/outputs/base.py,sha256=
|
1456
|
+
vellum/workflows/outputs/base.py,sha256=W5KL9FaWfSbZuF7lOQ677giHO839Do-MXmuzkDuzPqk,8607
|
1454
1457
|
vellum/workflows/ports/__init__.py,sha256=bZuMt-R7z5bKwpu4uPW7LlJeePOQWmCcDSXe5frUY5g,101
|
1455
1458
|
vellum/workflows/ports/node_ports.py,sha256=g4A-8iUAvEJSkaWppbvzAR8XU02R9U-qLN4rP2Kq4Aw,2743
|
1456
1459
|
vellum/workflows/ports/port.py,sha256=eI2SOZPZ5rsC3jMsxW6Rbn0NpaYQsrR7AapiIbbiy8Q,3635
|
@@ -1470,7 +1473,7 @@ vellum/workflows/references/workflow_input.py,sha256=86IuhlBz-9cGxeUzizyjdp482aj
|
|
1470
1473
|
vellum/workflows/resolvers/__init__.py,sha256=eH6hTvZO4IciDaf_cf7aM2vs-DkBDyJPycOQevJxQnI,82
|
1471
1474
|
vellum/workflows/resolvers/base.py,sha256=WHra9LRtlTuB1jmuNqkfVE2JUgB61Cyntn8f0b0WZg4,411
|
1472
1475
|
vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
|
1473
|
-
vellum/workflows/runner/runner.py,sha256=
|
1476
|
+
vellum/workflows/runner/runner.py,sha256=_p19T1woplSxZGabZuSUFBKSYBrXADrM7b_1YnWVN3g,31013
|
1474
1477
|
vellum/workflows/sandbox.py,sha256=GVJzVjMuYzOBnSrboB0_6MMRZWBluAyQ2o7syeaeBd0,2235
|
1475
1478
|
vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
|
1476
1479
|
vellum/workflows/state/base.py,sha256=Vkhneko3VlQrPsMLU1PYSzXU_W1u7_AraJsghiv5O-4,15512
|
@@ -1501,13 +1504,13 @@ vellum/workflows/utils/uuids.py,sha256=DFzPv9RCvsKhvdTEIQyfSek2A31D6S_QcmeLPbgrg
|
|
1501
1504
|
vellum/workflows/utils/vellum_variables.py,sha256=fC2aSLvlS31D15dOWu43LBRR0QsgUKNXBiCUvvaLXSs,3231
|
1502
1505
|
vellum/workflows/vellum_client.py,sha256=ODrq_TSl-drX2aezXegf7pizpWDVJuTXH-j6528t75s,683
|
1503
1506
|
vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
|
1504
|
-
vellum/workflows/workflows/base.py,sha256=
|
1507
|
+
vellum/workflows/workflows/base.py,sha256=TSS2BHC8LAi-N5GdEa75BeChwzwTzL7yldFnTlLINro,22665
|
1505
1508
|
vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
|
1506
1509
|
vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1507
1510
|
vellum/workflows/workflows/tests/test_base_workflow.py,sha256=NRteiICyJvDM5zrtUfq2fZoXcGQVaWC9xmNlLLVW0cU,7979
|
1508
1511
|
vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
|
1509
|
-
vellum_ai-0.14.
|
1510
|
-
vellum_ai-0.14.
|
1511
|
-
vellum_ai-0.14.
|
1512
|
-
vellum_ai-0.14.
|
1513
|
-
vellum_ai-0.14.
|
1512
|
+
vellum_ai-0.14.11.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
|
1513
|
+
vellum_ai-0.14.11.dist-info/METADATA,sha256=584bKGEOfx24lR-N0_BdsuLUo5G-nDYN2-hXxKWGCNg,5408
|
1514
|
+
vellum_ai-0.14.11.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
1515
|
+
vellum_ai-0.14.11.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
|
1516
|
+
vellum_ai-0.14.11.dist-info/RECORD,,
|
vellum_cli/image_push.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import json
|
2
2
|
import logging
|
3
|
+
import re
|
3
4
|
import subprocess
|
4
5
|
from typing import List, Optional
|
5
6
|
|
@@ -24,9 +25,11 @@ def image_push_command(image: str, tags: Optional[List[str]] = None) -> None:
|
|
24
25
|
# listing all of the architectures of the image instead of just the one that matches the machine. We can fall back
|
25
26
|
# to using normal inspect which returns the machine image for this case though. And in the future we could figure
|
26
27
|
# out how to call the docker host directly to do this.
|
28
|
+
logger.info("Pre-validating image...")
|
27
29
|
docker_client = docker.from_env()
|
28
30
|
check_architecture(docker_client, image, logger)
|
29
31
|
|
32
|
+
logger.info("Authenticating...")
|
30
33
|
auth = vellum_client.container_images.docker_service_token()
|
31
34
|
|
32
35
|
docker_client.login(
|
@@ -70,9 +73,29 @@ def image_push_command(image: str, tags: Optional[List[str]] = None) -> None:
|
|
70
73
|
except Exception:
|
71
74
|
continue
|
72
75
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
+
result = subprocess.run(
|
77
|
+
["docker", "inspect", "--format='{{index .RepoDigests 0}}'", image],
|
78
|
+
stdout=subprocess.PIPE,
|
79
|
+
stderr=subprocess.PIPE,
|
80
|
+
)
|
81
|
+
|
82
|
+
sha = ""
|
83
|
+
if result.returncode == 0:
|
84
|
+
match = re.search(r"sha256[^']*", result.stdout.decode("utf-8"))
|
85
|
+
if match and match.group(0):
|
86
|
+
sha = match.group(0)
|
87
|
+
|
88
|
+
if not sha:
|
89
|
+
# Fallback to using docker client if command line fails, at least on some systems
|
90
|
+
# this appears to give a bad sha.
|
91
|
+
logger.warning(
|
92
|
+
"Could not determine image hash with command line docker falling back to python docker client..."
|
93
|
+
)
|
94
|
+
|
95
|
+
image_details = docker_client.api.inspect_image(image)
|
96
|
+
sha = image_details["Id"]
|
97
|
+
|
98
|
+
logger.info(f"Updating Vellum metadata and validating image works in our system with image digest: {sha}...")
|
76
99
|
|
77
100
|
vellum_client.container_images.push_container_image(
|
78
101
|
name=image_name,
|
@@ -95,12 +118,12 @@ def check_architecture(docker_client: DockerClient, image: str, logger: logging.
|
|
95
118
|
manifest = json.loads(result.stdout)
|
96
119
|
architectures = [manifest_item["platform"]["architecture"] for manifest_item in manifest["manifests"]]
|
97
120
|
except Exception:
|
98
|
-
logger.
|
121
|
+
logger.debug("Error parsing manifest response")
|
99
122
|
manifest_parse_failed = True
|
100
123
|
|
101
124
|
# Fall back to inspect image if we errored out using docker command line
|
102
125
|
if result.returncode != 0 or manifest_parse_failed:
|
103
|
-
logger.
|
126
|
+
logger.debug(f"Error inspecting manifest: {result.stderr.decode('utf-8').strip()}")
|
104
127
|
image_details = docker_client.api.inspect_image(image)
|
105
128
|
|
106
129
|
if image_details["Architecture"] != _SUPPORTED_ARCHITECTURE:
|
@@ -1,11 +1,19 @@
|
|
1
1
|
import inspect
|
2
|
-
from typing import Any, Generic, TypeVar, cast
|
2
|
+
from typing import Any, Callable, Generic, Optional, Tuple, Type, TypeVar, cast
|
3
3
|
|
4
4
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
5
|
+
from vellum.workflows.errors.types import WorkflowErrorCode
|
6
|
+
from vellum.workflows.nodes.bases.base import BaseNode
|
5
7
|
from vellum.workflows.nodes.core.retry_node.node import RetryNode
|
8
|
+
from vellum.workflows.nodes.utils import ADORNMENT_MODULE_NAME
|
9
|
+
from vellum.workflows.references.output import OutputReference
|
6
10
|
from vellum.workflows.types.core import JsonArray, JsonObject
|
11
|
+
from vellum.workflows.types.utils import get_original_base
|
7
12
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
8
13
|
from vellum.workflows.workflows.base import BaseWorkflow
|
14
|
+
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
15
|
+
from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
|
16
|
+
from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay
|
9
17
|
from vellum_ee.workflows.display.nodes.vellum.base_adornment_node import BaseAdornmentNodeDisplay
|
10
18
|
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
11
19
|
|
@@ -39,4 +47,86 @@ class BaseRetryNodeDisplay(BaseAdornmentNodeDisplay[_RetryNodeType], Generic[_Re
|
|
39
47
|
"attributes": attributes,
|
40
48
|
}
|
41
49
|
|
42
|
-
|
50
|
+
serialized_node = super().serialize(
|
51
|
+
display_context,
|
52
|
+
adornment=adornment,
|
53
|
+
)
|
54
|
+
|
55
|
+
if serialized_node["type"] == "GENERIC":
|
56
|
+
return serialized_node
|
57
|
+
|
58
|
+
serialized_node_definition = serialized_node.get("definition")
|
59
|
+
if isinstance(serialized_node_definition, dict):
|
60
|
+
serialized_node_definition_module = serialized_node_definition.get("module")
|
61
|
+
if isinstance(serialized_node_definition_module, list):
|
62
|
+
serialized_node_definition_module.extend(
|
63
|
+
[
|
64
|
+
serialized_node_definition["name"],
|
65
|
+
ADORNMENT_MODULE_NAME,
|
66
|
+
]
|
67
|
+
)
|
68
|
+
serialized_node_definition["name"] = node.__name__
|
69
|
+
|
70
|
+
return serialized_node
|
71
|
+
|
72
|
+
def get_node_output_display(self, output: OutputReference) -> Tuple[Type[BaseNode], NodeOutputDisplay]:
|
73
|
+
inner_node = self._node.__wrapped_node__
|
74
|
+
if not inner_node:
|
75
|
+
return super().get_node_output_display(output)
|
76
|
+
|
77
|
+
node_display_class = get_node_display_class(BaseNodeDisplay, inner_node)
|
78
|
+
node_display = node_display_class()
|
79
|
+
|
80
|
+
inner_output = getattr(inner_node.Outputs, output.name)
|
81
|
+
return node_display.get_node_output_display(inner_output)
|
82
|
+
|
83
|
+
@classmethod
|
84
|
+
def wrap(
|
85
|
+
cls,
|
86
|
+
max_attempts: int,
|
87
|
+
delay: Optional[float] = None,
|
88
|
+
retry_on_error_code: Optional[WorkflowErrorCode] = None,
|
89
|
+
retry_on_condition: Optional[BaseDescriptor] = None,
|
90
|
+
) -> Callable[..., Type["BaseRetryNodeDisplay"]]:
|
91
|
+
_max_attempts = max_attempts
|
92
|
+
_delay = delay
|
93
|
+
_retry_on_error_code = retry_on_error_code
|
94
|
+
_retry_on_condition = retry_on_condition
|
95
|
+
|
96
|
+
NodeDisplayType = TypeVar("NodeDisplayType", bound=BaseNodeDisplay)
|
97
|
+
|
98
|
+
def decorator(inner_cls: Type[NodeDisplayType]) -> Type[NodeDisplayType]:
|
99
|
+
node_class = inner_cls.infer_node_class()
|
100
|
+
wrapped_node_class = cast(Type[BaseNode], node_class.__wrapped_node__)
|
101
|
+
|
102
|
+
class RetryNodeDisplay(BaseRetryNodeDisplay[node_class]): # type: ignore[valid-type]
|
103
|
+
max_attempts = _max_attempts
|
104
|
+
delay = _delay
|
105
|
+
retry_on_error_code = _retry_on_error_code
|
106
|
+
retry_on_condition = _retry_on_condition
|
107
|
+
|
108
|
+
setattr(inner_cls, "__adorned_by__", RetryNodeDisplay)
|
109
|
+
|
110
|
+
# We must edit the node display class to use __wrapped_node__ everywhere it
|
111
|
+
# references the adorned node class, which is three places:
|
112
|
+
|
113
|
+
# 1. The node display class' parameterized type
|
114
|
+
original_base_node_display = get_original_base(inner_cls)
|
115
|
+
original_base_node_display.__args__ = (wrapped_node_class,)
|
116
|
+
inner_cls._node_display_registry[wrapped_node_class] = inner_cls
|
117
|
+
|
118
|
+
# 2. The node display class' output displays
|
119
|
+
old_outputs = list(inner_cls.output_display.keys())
|
120
|
+
for old_output in old_outputs:
|
121
|
+
new_output = getattr(wrapped_node_class.Outputs, old_output.name)
|
122
|
+
inner_cls.output_display[new_output] = inner_cls.output_display.pop(old_output)
|
123
|
+
|
124
|
+
# 3. The node display class' port displays
|
125
|
+
old_ports = list(inner_cls.port_displays.keys())
|
126
|
+
for old_port in old_ports:
|
127
|
+
new_port = getattr(wrapped_node_class.Ports, old_port.name)
|
128
|
+
inner_cls.port_displays[new_port] = inner_cls.port_displays.pop(old_port)
|
129
|
+
|
130
|
+
return inner_cls
|
131
|
+
|
132
|
+
return decorator
|
@@ -0,0 +1,47 @@
|
|
1
|
+
from typing import Any, Dict, cast
|
2
|
+
|
3
|
+
from vellum.workflows import BaseWorkflow
|
4
|
+
from vellum.workflows.errors.types import WorkflowErrorCode
|
5
|
+
from vellum.workflows.nodes.bases.base import BaseNode
|
6
|
+
from vellum.workflows.nodes.core.retry_node.node import RetryNode
|
7
|
+
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
8
|
+
from vellum_ee.workflows.display.workflows.vellum_workflow_display import VellumWorkflowDisplay
|
9
|
+
|
10
|
+
|
11
|
+
def test_retry_node_parameters():
|
12
|
+
"""Test that RetryNode parameters are correctly serialized."""
|
13
|
+
|
14
|
+
# GIVEN a RetryNode with specific parameters
|
15
|
+
@RetryNode.wrap(max_attempts=5, delay=2.5, retry_on_error_code=WorkflowErrorCode.INVALID_INPUTS)
|
16
|
+
class MyRetryNode(BaseNode):
|
17
|
+
pass
|
18
|
+
|
19
|
+
# AND a workflow using the node
|
20
|
+
class MyWorkflow(BaseWorkflow):
|
21
|
+
graph = MyRetryNode
|
22
|
+
|
23
|
+
# WHEN we serialize the workflow
|
24
|
+
workflow_display = get_workflow_display(base_display_class=VellumWorkflowDisplay, workflow_class=MyWorkflow)
|
25
|
+
serialized_workflow = cast(Dict[str, Any], workflow_display.serialize())
|
26
|
+
|
27
|
+
# THEN the correct inputs should be serialized on the node
|
28
|
+
serialized_node = next(
|
29
|
+
node for node in serialized_workflow["workflow_raw_data"]["nodes"] if node["type"] != "ENTRYPOINT"
|
30
|
+
)
|
31
|
+
|
32
|
+
retry_adornment = next(
|
33
|
+
adornment for adornment in serialized_node["adornments"] if adornment["label"] == "RetryNode"
|
34
|
+
)
|
35
|
+
|
36
|
+
max_attempts_attribute = next(attr for attr in retry_adornment["attributes"] if attr["name"] == "max_attempts")
|
37
|
+
assert max_attempts_attribute["value"]["value"]["value"] == 5
|
38
|
+
|
39
|
+
delay_attribute = next(attr for attr in retry_adornment["attributes"] if attr["name"] == "delay")
|
40
|
+
assert delay_attribute["value"]["value"]["value"] == 2.5
|
41
|
+
|
42
|
+
retry_on_error_code_attribute = next(
|
43
|
+
attr for attr in retry_adornment["attributes"] if attr["name"] == "retry_on_error_code"
|
44
|
+
)
|
45
|
+
|
46
|
+
assert retry_on_error_code_attribute["value"]["value"]["type"] == "STRING"
|
47
|
+
assert retry_on_error_code_attribute["value"]["value"]["value"] == "INVALID_INPUTS"
|
@@ -29,11 +29,8 @@ class InnerRetryGenericNode(BaseNode):
|
|
29
29
|
output: str
|
30
30
|
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
class OuterRetryNodeDisplay(BaseRetryNodeDisplay[InnerRetryGenericNode]): # type: ignore
|
32
|
+
@BaseRetryNodeDisplay.wrap(max_attempts=3)
|
33
|
+
class InnerRetryGenericNodeDisplay(BaseNodeDisplay[InnerRetryGenericNode]):
|
37
34
|
pass
|
38
35
|
|
39
36
|
|
@@ -44,7 +41,6 @@ def test_serialize_node__retry(serialize_node):
|
|
44
41
|
global_workflow_input_displays={Inputs.input: WorkflowInputsDisplay(id=input_id)},
|
45
42
|
global_node_displays={
|
46
43
|
InnerRetryGenericNode.__wrapped_node__: InnerRetryGenericNodeDisplay,
|
47
|
-
InnerRetryGenericNode: OuterRetryNodeDisplay,
|
48
44
|
},
|
49
45
|
)
|
50
46
|
|
@@ -9,7 +9,7 @@ from typing import Any, Dict, Generic, Iterator, List, Optional, Tuple, Type, Un
|
|
9
9
|
from vellum.workflows import BaseWorkflow
|
10
10
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
11
11
|
from vellum.workflows.edges import Edge
|
12
|
-
from vellum.workflows.events.workflow import
|
12
|
+
from vellum.workflows.events.workflow import NodeEventDisplayContext, WorkflowEventDisplayContext
|
13
13
|
from vellum.workflows.expressions.coalesce_expression import CoalesceExpression
|
14
14
|
from vellum.workflows.nodes.bases import BaseNode
|
15
15
|
from vellum.workflows.nodes.utils import get_wrapped_node
|
@@ -34,7 +34,9 @@ from vellum_ee.workflows.display.base import (
|
|
34
34
|
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
35
35
|
from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
|
36
36
|
from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay, PortDisplay, PortDisplayOverrides
|
37
|
+
from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
|
37
38
|
from vellum_ee.workflows.display.types import NodeDisplayType, WorkflowDisplayContext
|
39
|
+
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
38
40
|
|
39
41
|
logger = logging.getLogger(__name__)
|
40
42
|
|
@@ -383,10 +385,15 @@ class BaseWorkflowDisplay(
|
|
383
385
|
except ModuleNotFoundError:
|
384
386
|
return None
|
385
387
|
|
386
|
-
|
387
|
-
if not isinstance(
|
388
|
+
workflow_display = display_class.WorkflowDisplay(workflow_class)
|
389
|
+
if not isinstance(workflow_display, BaseWorkflowDisplay):
|
388
390
|
return None
|
389
391
|
|
392
|
+
return workflow_display.get_event_display_context()
|
393
|
+
|
394
|
+
def get_event_display_context(self):
|
395
|
+
display_context = self.display_context
|
396
|
+
|
390
397
|
workflow_outputs = {
|
391
398
|
output.name: display_context.workflow_output_displays[output].id
|
392
399
|
for output in display_context.workflow_output_displays
|
@@ -398,27 +405,44 @@ class BaseWorkflowDisplay(
|
|
398
405
|
node_displays = {
|
399
406
|
str(node.__id__): display_context.node_displays[node] for node in display_context.node_displays
|
400
407
|
}
|
401
|
-
|
402
|
-
for
|
403
|
-
|
408
|
+
node_event_displays = {}
|
409
|
+
for node_id in node_displays:
|
410
|
+
current_node_display = node_displays[node_id]
|
404
411
|
input_display = {}
|
405
|
-
if isinstance(
|
406
|
-
input_display =
|
412
|
+
if isinstance(current_node_display, BaseNodeVellumDisplay):
|
413
|
+
input_display = current_node_display.node_input_ids_by_name
|
407
414
|
node_display_meta = {
|
408
|
-
output.name:
|
415
|
+
output.name: current_node_display.output_display[output].id
|
416
|
+
for output in current_node_display.output_display
|
409
417
|
}
|
410
|
-
port_display_meta = {
|
411
|
-
|
412
|
-
|
418
|
+
port_display_meta = {
|
419
|
+
port.name: current_node_display.port_displays[port].id for port in current_node_display.port_displays
|
420
|
+
}
|
421
|
+
node = current_node_display._node
|
422
|
+
subworkflow_display_context: Optional[WorkflowEventDisplayContext] = None
|
423
|
+
if hasattr(node, "subworkflow"):
|
424
|
+
# All nodes that have a subworkflow attribute are currently expected to call them `subworkflow`
|
425
|
+
# This will change if in the future we decide to support multiple subworkflows on a single node
|
426
|
+
subworkflow_attribute = raise_if_descriptor(getattr(node, "subworkflow"))
|
427
|
+
if issubclass(subworkflow_attribute, BaseWorkflow):
|
428
|
+
subworkflow_display = get_workflow_display(
|
429
|
+
base_display_class=display_context.workflow_display_class,
|
430
|
+
workflow_class=subworkflow_attribute,
|
431
|
+
parent_display_context=display_context,
|
432
|
+
)
|
433
|
+
subworkflow_display_context = subworkflow_display.get_event_display_context()
|
434
|
+
|
435
|
+
node_event_displays[node_id] = NodeEventDisplayContext(
|
413
436
|
input_display=input_display,
|
414
437
|
output_display=node_display_meta,
|
415
438
|
port_display=port_display_meta,
|
439
|
+
subworkflow_display=subworkflow_display_context,
|
416
440
|
)
|
417
441
|
|
418
442
|
display_meta = WorkflowEventDisplayContext(
|
419
443
|
workflow_outputs=workflow_outputs,
|
420
444
|
workflow_inputs=workflow_inputs,
|
421
|
-
node_displays=
|
445
|
+
node_displays=node_event_displays,
|
422
446
|
)
|
423
447
|
return display_meta
|
424
448
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
import pytest
|
2
2
|
|
3
3
|
from vellum.workflows.nodes.bases.base import BaseNode
|
4
|
+
from vellum.workflows.nodes.core.inline_subworkflow_node.node import InlineSubworkflowNode
|
4
5
|
from vellum.workflows.workflows.base import BaseWorkflow
|
5
6
|
from vellum_ee.workflows.display.nodes import BaseNodeDisplay
|
6
7
|
from vellum_ee.workflows.display.vellum import NodeDisplayData, NodeDisplayPosition
|
@@ -92,3 +93,29 @@ def test_serialize_workflow__node_display_class_not_registered():
|
|
92
93
|
|
93
94
|
# THEN it should should succeed
|
94
95
|
assert data is not None
|
96
|
+
|
97
|
+
|
98
|
+
def test_get_event_display_context__node_display_to_include_subworkflow_display():
|
99
|
+
# GIVEN a simple workflow
|
100
|
+
class InnerNode(BaseNode):
|
101
|
+
pass
|
102
|
+
|
103
|
+
class Subworkflow(BaseWorkflow):
|
104
|
+
graph = InnerNode
|
105
|
+
|
106
|
+
# AND a workflow that includes the subworkflow
|
107
|
+
class SubworkflowNode(InlineSubworkflowNode):
|
108
|
+
subworkflow = Subworkflow
|
109
|
+
|
110
|
+
class MyWorkflow(BaseWorkflow):
|
111
|
+
graph = SubworkflowNode
|
112
|
+
|
113
|
+
# WHEN we gather the event display context
|
114
|
+
display_context = VellumWorkflowDisplay(MyWorkflow).get_event_display_context()
|
115
|
+
|
116
|
+
# THEN the subworkflow display should be included
|
117
|
+
assert str(SubworkflowNode.__id__) in display_context.node_displays
|
118
|
+
node_event_display = display_context.node_displays[str(SubworkflowNode.__id__)]
|
119
|
+
|
120
|
+
assert node_event_display.subworkflow_display is not None
|
121
|
+
assert str(InnerNode.__id__) in node_event_display.subworkflow_display.node_displays
|
@@ -40,11 +40,13 @@ def test_base_class_dynamic_import(files):
|
|
40
40
|
},
|
41
41
|
"output_display": {"result": UUID("423bc529-1a1a-4f72-af4d-cbdb5f0a5929")},
|
42
42
|
"port_display": {"default": UUID("afda9a19-0618-42e1-9b63-5d0db2a88f62")},
|
43
|
+
"subworkflow_display": None,
|
43
44
|
},
|
44
45
|
"f3ef4b2b-fec9-4026-9cc6-e5eac295307f": {
|
45
46
|
"input_display": {"node_input": UUID("fe6cba85-2423-4b5e-8f85-06311a8be5fb")},
|
46
47
|
"output_display": {"value": UUID("5469b810-6ea6-4362-9e79-e360d44a1405")},
|
47
48
|
"port_display": {},
|
49
|
+
"subworkflow_display": None,
|
48
50
|
},
|
49
51
|
},
|
50
52
|
"workflow_inputs": {"input_value": UUID("2268a996-bd17-4832-b3ff-f5662d54b306")},
|
File without changes
|
File without changes
|
File without changes
|