vellum-ai 0.13.8__py3-none-any.whl → 0.13.10__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.
Files changed (49) hide show
  1. vellum/client/core/client_wrapper.py +1 -1
  2. vellum/plugins/vellum_mypy.py +6 -2
  3. vellum/workflows/descriptors/utils.py +1 -1
  4. vellum/workflows/nodes/bases/base.py +1 -2
  5. vellum/workflows/nodes/displayable/api_node/node.py +4 -1
  6. vellum/workflows/nodes/displayable/bases/api_node/node.py +4 -1
  7. vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +4 -1
  8. vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +4 -0
  9. vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +4 -0
  10. vellum/workflows/nodes/displayable/bases/search_node.py +4 -0
  11. vellum/workflows/nodes/displayable/code_execution_node/node.py +4 -1
  12. vellum/workflows/nodes/displayable/conditional_node/node.py +4 -0
  13. vellum/workflows/nodes/displayable/final_output_node/node.py +4 -0
  14. vellum/workflows/nodes/displayable/guardrail_node/node.py +4 -1
  15. vellum/workflows/nodes/displayable/inline_prompt_node/node.py +4 -0
  16. vellum/workflows/nodes/displayable/merge_node/node.py +3 -1
  17. vellum/workflows/nodes/displayable/note_node/node.py +4 -0
  18. vellum/workflows/nodes/displayable/prompt_deployment_node/node.py +4 -0
  19. vellum/workflows/nodes/displayable/search_node/node.py +4 -0
  20. vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +4 -1
  21. vellum/workflows/nodes/utils.py +6 -18
  22. {vellum_ai-0.13.8.dist-info → vellum_ai-0.13.10.dist-info}/METADATA +1 -1
  23. {vellum_ai-0.13.8.dist-info → vellum_ai-0.13.10.dist-info}/RECORD +49 -48
  24. vellum_ee/workflows/display/nodes/base_node_display.py +103 -3
  25. vellum_ee/workflows/display/nodes/vellum/base_node.py +34 -105
  26. vellum_ee/workflows/display/nodes/vellum/conditional_node.py +2 -1
  27. vellum_ee/workflows/display/nodes/vellum/tests/test_try_node.py +63 -0
  28. vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py +3 -4
  29. vellum_ee/workflows/display/nodes/vellum/try_node.py +28 -4
  30. vellum_ee/workflows/display/nodes/vellum/utils.py +1 -80
  31. vellum_ee/workflows/display/tests/test_vellum_workflow_display.py +56 -0
  32. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py +3 -2
  33. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +143 -26
  34. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +10 -10
  35. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py +6 -6
  36. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py +32 -34
  37. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py +4 -4
  38. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py +1 -2
  39. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +5 -5
  40. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py +1 -1
  41. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +6 -3
  42. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +6 -3
  43. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +3 -3
  44. vellum_ee/workflows/display/utils/vellum.py +74 -4
  45. vellum_ee/workflows/display/workflows/base_workflow_display.py +22 -24
  46. vellum_ee/workflows/display/workflows/vellum_workflow_display.py +6 -15
  47. {vellum_ai-0.13.8.dist-info → vellum_ai-0.13.10.dist-info}/LICENSE +0 -0
  48. {vellum_ai-0.13.8.dist-info → vellum_ai-0.13.10.dist-info}/WHEEL +0 -0
  49. {vellum_ai-0.13.8.dist-info → vellum_ai-0.13.10.dist-info}/entry_points.txt +0 -0
@@ -1,13 +1,12 @@
1
1
  import pytest
2
2
  from uuid import UUID, uuid4
3
- from typing import List, cast
3
+ from typing import List
4
4
 
5
5
  from vellum.client.types.string_vellum_value import StringVellumValue
6
6
  from vellum.workflows.descriptors.base import BaseDescriptor
7
7
  from vellum.workflows.inputs import BaseInputs
8
8
  from vellum.workflows.nodes.bases import BaseNode
9
9
  from vellum.workflows.outputs import BaseOutputs
10
- from vellum.workflows.references import OutputReference, WorkflowInputReference
11
10
  from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
12
11
  from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay
13
12
  from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input_value_pointer_rules
@@ -92,12 +91,12 @@ def test_create_node_input_value_pointer_rules(
92
91
  entrypoint_node_display=NodeDisplayData(),
93
92
  ),
94
93
  global_workflow_input_displays={
95
- cast(WorkflowInputReference, Inputs.example_workflow_input): WorkflowInputsVellumDisplayOverrides(
94
+ Inputs.example_workflow_input: WorkflowInputsVellumDisplayOverrides(
96
95
  id=UUID("a154c29d-fac0-4cd0-ba88-bc52034f5470"),
97
96
  ),
98
97
  },
99
98
  global_node_output_displays={
100
- cast(OutputReference, MyNodeA.Outputs.output): (
99
+ MyNodeA.Outputs.output: (
101
100
  MyNodeA,
102
101
  NodeOutputDisplay(id=UUID("4b16a629-11a1-4b3f-a965-a57b872d13b8"), name="output"),
103
102
  ),
@@ -1,16 +1,19 @@
1
1
  from uuid import UUID
2
- from typing import Any, Callable, ClassVar, Generic, Optional, Type, TypeVar, cast
2
+ from typing import Any, Callable, ClassVar, Generic, Optional, Tuple, Type, TypeVar, cast
3
3
 
4
4
  from vellum.workflows.nodes.bases.base import BaseNode
5
5
  from vellum.workflows.nodes.core.try_node.node import TryNode
6
6
  from vellum.workflows.nodes.utils import ADORNMENT_MODULE_NAME, get_wrapped_node
7
+ from vellum.workflows.references.output import OutputReference
7
8
  from vellum.workflows.types.core import JsonObject
8
9
  from vellum.workflows.types.utils import get_original_base
9
10
  from vellum.workflows.utils.uuids import uuid4_from_hash
10
11
  from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
11
12
  from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
12
13
  from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
14
+ from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay
13
15
  from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
16
+ from vellum_ee.workflows.display.nodes.vellum.base_node import BaseNodeDisplay as GenericBaseNodeDisplay
14
17
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
15
18
 
16
19
  _TryNodeType = TypeVar("_TryNodeType", bound=TryNode)
@@ -22,9 +25,8 @@ class BaseTryNodeDisplay(BaseNodeVellumDisplay[_TryNodeType], Generic[_TryNodeTy
22
25
  def serialize(self, display_context: WorkflowDisplayContext, **kwargs: Any) -> JsonObject:
23
26
  node = self._node
24
27
 
25
- try:
26
- inner_node = get_wrapped_node(node)
27
- except AttributeError:
28
+ inner_node = get_wrapped_node(node)
29
+ if not inner_node:
28
30
  subworkflow = raise_if_descriptor(node.subworkflow)
29
31
  if not isinstance(subworkflow.graph, type) or not issubclass(subworkflow.graph, BaseNode):
30
32
  raise NotImplementedError(
@@ -32,6 +34,12 @@ class BaseTryNodeDisplay(BaseNodeVellumDisplay[_TryNodeType], Generic[_TryNodeTy
32
34
  )
33
35
 
34
36
  inner_node = subworkflow.graph
37
+ elif inner_node.__bases__[0] is BaseNode:
38
+ # If the wrapped node is a generic node, we let generic node do adornment handling
39
+ class TryBaseNodeDisplay(GenericBaseNodeDisplay[node]): # type: ignore[valid-type]
40
+ pass
41
+
42
+ return TryBaseNodeDisplay().serialize(display_context)
35
43
 
36
44
  # We need the node display class of the underlying node because
37
45
  # it contains the logic for serializing the node and potential display overrides
@@ -57,6 +65,22 @@ class BaseTryNodeDisplay(BaseNodeVellumDisplay[_TryNodeType], Generic[_TryNodeTy
57
65
 
58
66
  return serialized_node
59
67
 
68
+ def get_node_output_display(self, output: OutputReference) -> Tuple[Type[BaseNode], NodeOutputDisplay]:
69
+ inner_node = self._node.__wrapped_node__
70
+ if not inner_node:
71
+ return super().get_node_output_display(output)
72
+
73
+ node_display_class = get_node_display_class(BaseNodeVellumDisplay, inner_node)
74
+ node_display = node_display_class()
75
+ if output.name == "error":
76
+ return inner_node, NodeOutputDisplay(
77
+ id=self.error_output_id or uuid4_from_hash(f"{node_display.node_id}|error_output_id"),
78
+ name="error",
79
+ )
80
+
81
+ inner_output = getattr(inner_node.Outputs, output.name)
82
+ return node_display.get_node_output_display(inner_output)
83
+
60
84
  @classmethod
61
85
  def wrap(cls, error_output_id: Optional[UUID] = None) -> Callable[..., Type["BaseTryNodeDisplay"]]:
62
86
  _error_output_id = error_output_id
@@ -2,34 +2,9 @@ from uuid import UUID
2
2
  from typing import Any, List, Optional, Type, Union, cast
3
3
 
4
4
  from vellum.workflows.descriptors.base import BaseDescriptor
5
- from vellum.workflows.expressions.and_ import AndExpression
6
- from vellum.workflows.expressions.begins_with import BeginsWithExpression
7
- from vellum.workflows.expressions.between import BetweenExpression
8
5
  from vellum.workflows.expressions.coalesce_expression import CoalesceExpression
9
- from vellum.workflows.expressions.contains import ContainsExpression
10
- from vellum.workflows.expressions.does_not_begin_with import DoesNotBeginWithExpression
11
- from vellum.workflows.expressions.does_not_contain import DoesNotContainExpression
12
- from vellum.workflows.expressions.does_not_end_with import DoesNotEndWithExpression
13
- from vellum.workflows.expressions.does_not_equal import DoesNotEqualExpression
14
- from vellum.workflows.expressions.ends_with import EndsWithExpression
15
- from vellum.workflows.expressions.equals import EqualsExpression
16
- from vellum.workflows.expressions.greater_than import GreaterThanExpression
17
- from vellum.workflows.expressions.greater_than_or_equal_to import GreaterThanOrEqualToExpression
18
- from vellum.workflows.expressions.in_ import InExpression
19
- from vellum.workflows.expressions.is_nil import IsNilExpression
20
- from vellum.workflows.expressions.is_not_nil import IsNotNilExpression
21
- from vellum.workflows.expressions.is_not_null import IsNotNullExpression
22
- from vellum.workflows.expressions.is_not_undefined import IsNotUndefinedExpression
23
- from vellum.workflows.expressions.is_null import IsNullExpression
24
- from vellum.workflows.expressions.is_undefined import IsUndefinedExpression
25
- from vellum.workflows.expressions.less_than import LessThanExpression
26
- from vellum.workflows.expressions.less_than_or_equal_to import LessThanOrEqualToExpression
27
- from vellum.workflows.expressions.not_between import NotBetweenExpression
28
- from vellum.workflows.expressions.not_in import NotInExpression
29
- from vellum.workflows.expressions.or_ import OrExpression
30
6
  from vellum.workflows.nodes.displayable.bases.utils import primitive_to_vellum_value
31
- from vellum.workflows.nodes.utils import get_wrapped_node, has_wrapped_node
32
- from vellum.workflows.references import NodeReference, OutputReference
7
+ from vellum.workflows.references import NodeReference
33
8
  from vellum.workflows.utils.uuids import uuid4_from_hash
34
9
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
35
10
  from vellum_ee.workflows.display.utils.vellum import create_node_input_value_pointer_rule
@@ -56,15 +31,6 @@ def create_node_input(
56
31
  pointer_type: Optional[Type[NodeInputValuePointerRule]] = ConstantValuePointer,
57
32
  ) -> NodeInput:
58
33
  input_id = str(input_id) if input_id else str(uuid4_from_hash(f"{node_id}|{input_name}"))
59
- if (
60
- isinstance(value, OutputReference)
61
- and value.outputs_class._node_class
62
- and has_wrapped_node(value.outputs_class._node_class)
63
- ):
64
- wrapped_node = get_wrapped_node(value.outputs_class._node_class)
65
- if wrapped_node._is_wrapped_node:
66
- value = getattr(wrapped_node.Outputs, value.name)
67
-
68
34
  rules = create_node_input_value_pointer_rules(value, display_context, pointer_type=pointer_type)
69
35
  return NodeInput(
70
36
  id=input_id,
@@ -134,48 +100,3 @@ def create_pointer(
134
100
  return ConstantValuePointer(type="CONSTANT_VALUE", data=vellum_variable_value)
135
101
  else:
136
102
  raise ValueError(f"Pointer type {pointer_type} not supported")
137
-
138
-
139
- def convert_descriptor_to_operator(descriptor: BaseDescriptor) -> str:
140
- if isinstance(descriptor, EqualsExpression):
141
- return "="
142
- elif isinstance(descriptor, DoesNotEqualExpression):
143
- return "!="
144
- elif isinstance(descriptor, LessThanExpression):
145
- return "<"
146
- elif isinstance(descriptor, GreaterThanExpression):
147
- return ">"
148
- elif isinstance(descriptor, LessThanOrEqualToExpression):
149
- return "<="
150
- elif isinstance(descriptor, GreaterThanOrEqualToExpression):
151
- return ">="
152
- elif isinstance(descriptor, ContainsExpression):
153
- return "contains"
154
- elif isinstance(descriptor, BeginsWithExpression):
155
- return "beginsWith"
156
- elif isinstance(descriptor, EndsWithExpression):
157
- return "endsWith"
158
- elif isinstance(descriptor, DoesNotContainExpression):
159
- return "doesNotContain"
160
- elif isinstance(descriptor, DoesNotBeginWithExpression):
161
- return "doesNotBeginWith"
162
- elif isinstance(descriptor, DoesNotEndWithExpression):
163
- return "doesNotEndWith"
164
- elif isinstance(descriptor, (IsNullExpression, IsNilExpression, IsUndefinedExpression)):
165
- return "null"
166
- elif isinstance(descriptor, (IsNotNullExpression, IsNotNilExpression, IsNotUndefinedExpression)):
167
- return "notNull"
168
- elif isinstance(descriptor, InExpression):
169
- return "in"
170
- elif isinstance(descriptor, NotInExpression):
171
- return "notIn"
172
- elif isinstance(descriptor, BetweenExpression):
173
- return "between"
174
- elif isinstance(descriptor, NotBetweenExpression):
175
- return "notBetween"
176
- elif isinstance(descriptor, AndExpression):
177
- return "and"
178
- elif isinstance(descriptor, OrExpression):
179
- return "or"
180
- else:
181
- raise ValueError(f"Unsupported descriptor type: {descriptor}")
@@ -88,3 +88,59 @@ def test_vellum_workflow_display__serialize_input_variables_with_capitalized_var
88
88
  "extensions": {"color": None},
89
89
  }
90
90
  ]
91
+
92
+
93
+ def test_vellum_workflow_display_serialize_valid_handle_ids_for_base_nodes():
94
+ # GIVEN a workflow between two base nodes
95
+ class StartNode(BaseNode):
96
+ pass
97
+
98
+ class EndNode(BaseNode):
99
+ class Outputs(BaseNode.Outputs):
100
+ hello = "world"
101
+
102
+ class Workflow(BaseWorkflow):
103
+ graph = StartNode >> EndNode
104
+
105
+ class Outputs(BaseWorkflow.Outputs):
106
+ final_value = EndNode.Outputs.hello
107
+
108
+ # AND a display class for this workflow
109
+ workflow_display = get_workflow_display(
110
+ base_display_class=VellumWorkflowDisplay,
111
+ workflow_class=Workflow,
112
+ )
113
+
114
+ # WHEN we serialize the workflow
115
+ exec_config = workflow_display.serialize()
116
+
117
+ # THEN the serialized workflow handle ids are valid
118
+ raw_data = exec_config.get("workflow_raw_data")
119
+ assert isinstance(raw_data, dict)
120
+ nodes = raw_data.get("nodes")
121
+ edges = raw_data.get("edges")
122
+
123
+ assert isinstance(nodes, list)
124
+ assert isinstance(edges, list)
125
+
126
+ edge_source_handle_ids = {edge.get("source_handle_id") for edge in edges if isinstance(edge, dict)}
127
+ edge_target_handle_ids = {edge.get("target_handle_id") for edge in edges if isinstance(edge, dict)}
128
+
129
+ for node in nodes:
130
+ assert isinstance(node, dict)
131
+
132
+ if node["type"] in {"ENTRYPOINT", "TERMINAL"}:
133
+ continue
134
+
135
+ ports = node.get("ports")
136
+ assert isinstance(ports, list)
137
+ for port in ports:
138
+ assert isinstance(port, dict)
139
+ assert (
140
+ port["id"] in edge_source_handle_ids
141
+ ), f"Port {port['id']} from node {node['label']} not found in edge source handle ids"
142
+
143
+ assert isinstance(node["trigger"], dict)
144
+ assert (
145
+ node["trigger"]["id"] in edge_target_handle_ids
146
+ ), f"Trigger {node['trigger']['id']} from node {node['label']} not found in edge target handle ids"
@@ -1,6 +1,6 @@
1
1
  import pytest
2
2
  from uuid import uuid4
3
- from typing import Dict, Tuple, Type
3
+ from typing import Any, Dict, Tuple, Type
4
4
 
5
5
  from vellum.workflows.nodes.bases.base import BaseNode
6
6
  from vellum.workflows.references.output import OutputReference
@@ -20,11 +20,12 @@ from vellum_ee.workflows.display.workflows.vellum_workflow_display import Vellum
20
20
  def serialize_node():
21
21
  def _serialize_node(
22
22
  node_class: Type[NodeType],
23
+ base_class: type[BaseNodeDisplay[Any]] = BaseNodeDisplay,
23
24
  global_workflow_input_displays: Dict[WorkflowInputReference, WorkflowInputsDisplayType] = {},
24
25
  global_node_displays: Dict[Type[BaseNode], NodeDisplayType] = {},
25
26
  global_node_output_displays: Dict[OutputReference, Tuple[Type[BaseNode], NodeOutputDisplay]] = {},
26
27
  ) -> JsonObject:
27
- node_display_class = get_node_display_class(BaseNodeDisplay, node_class)
28
+ node_display_class = get_node_display_class(base_class, node_class)
28
29
  node_display = node_display_class()
29
30
 
30
31
  context: WorkflowDisplayContext = WorkflowDisplayContext(
@@ -1,47 +1,148 @@
1
- import pytest
1
+ from uuid import uuid4
2
2
 
3
3
  from deepdiff import DeepDiff
4
4
 
5
5
  from vellum.workflows.inputs.base import BaseInputs
6
6
  from vellum.workflows.nodes.bases.base import BaseNode
7
- from vellum.workflows.nodes.core.map_node.node import MapNode
7
+ from vellum.workflows.nodes.core.retry_node.node import RetryNode
8
+ from vellum.workflows.nodes.core.try_node.node import TryNode
8
9
  from vellum.workflows.outputs.base import BaseOutputs
9
- from vellum.workflows.state.base import BaseState
10
+ from vellum_ee.workflows.display.base import WorkflowInputsDisplay
11
+ from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
12
+ from vellum_ee.workflows.display.nodes.vellum.base_node import BaseNodeDisplay
13
+ from vellum_ee.workflows.display.nodes.vellum.try_node import BaseTryNodeDisplay
10
14
 
11
15
 
12
16
  class Inputs(BaseInputs):
13
- foo: str
17
+ input: str
14
18
 
15
19
 
16
- class State(BaseState):
17
- bar: str
20
+ @RetryNode.wrap(max_attempts=3)
21
+ class InnerRetryGenericNode(BaseNode):
22
+ input = Inputs.input
23
+
24
+ class Outputs(BaseOutputs):
25
+ output: str
26
+
27
+
28
+ class InnerRetryGenericNodeDisplay(BaseNodeDisplay[InnerRetryGenericNode.__wrapped_node__]): # type: ignore
29
+ pass
30
+
31
+
32
+ class OuterRetryNodeDisplay(BaseNodeDisplay[InnerRetryGenericNode]):
33
+ pass
34
+
35
+
36
+ def test_serialize_node__retry(serialize_node):
37
+ input_id = uuid4()
38
+ serialized_node = serialize_node(
39
+ node_class=InnerRetryGenericNode,
40
+ global_workflow_input_displays={Inputs.input: WorkflowInputsDisplay(id=input_id)},
41
+ global_node_displays={
42
+ InnerRetryGenericNode.__wrapped_node__: InnerRetryGenericNodeDisplay,
43
+ InnerRetryGenericNode: OuterRetryNodeDisplay,
44
+ },
45
+ )
46
+
47
+ serialized_node["adornments"][0]["attributes"] = sorted(
48
+ serialized_node["adornments"][0]["attributes"], key=lambda x: x["name"]
49
+ )
50
+ assert not DeepDiff(
51
+ {
52
+ "id": "f2a95e79-7d4b-47ad-b986-4f648297ec65",
53
+ "label": "InnerRetryGenericNode",
54
+ "type": "GENERIC",
55
+ "display_data": {"position": {"x": 0.0, "y": 0.0}},
56
+ "base": {"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"]},
57
+ "definition": {
58
+ "name": "InnerRetryGenericNode",
59
+ "module": [
60
+ "vellum_ee",
61
+ "workflows",
62
+ "display",
63
+ "tests",
64
+ "workflow_serialization",
65
+ "generic_nodes",
66
+ "test_adornments_serialization",
67
+ ],
68
+ },
69
+ "trigger": {"id": "af9ba01c-4cde-4632-9aa1-7673b42e7bd8", "merge_behavior": "AWAIT_ATTRIBUTES"},
70
+ "ports": [{"id": "c2ecc6c0-f353-4495-9b93-a61a47248556", "name": "default", "type": "DEFAULT"}],
71
+ "adornments": [
72
+ {
73
+ "id": "5be7d260-74f7-4734-b31b-a46a94539586",
74
+ "label": "RetryNode",
75
+ "base": {
76
+ "name": "RetryNode",
77
+ "module": ["vellum", "workflows", "nodes", "core", "retry_node", "node"],
78
+ },
79
+ "attributes": [
80
+ {
81
+ "id": "f388e93b-8c68-4f54-8577-bbd0c9091557",
82
+ "name": "max_attempts",
83
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "NUMBER", "value": 3.0}},
84
+ },
85
+ {
86
+ "id": "73a02e62-4535-4e1f-97b5-1264ca8b1d71",
87
+ "name": "retry_on_condition",
88
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
89
+ },
90
+ {
91
+ "id": "c91782e3-140f-4938-9c23-d2a7b85dcdd8",
92
+ "name": "retry_on_error_code",
93
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
94
+ },
95
+ ],
96
+ }
97
+ ],
98
+ "attributes": [
99
+ {
100
+ "id": "c363daa7-9482-4c0e-aee8-faa080602ee3",
101
+ "name": "input",
102
+ "value": {"type": "WORKFLOW_INPUT", "input_variable_id": str(input_id)},
103
+ }
104
+ ],
105
+ "outputs": [
106
+ {"id": "8aaf6cd8-3fa5-4f17-a60f-ec7da5ec6498", "name": "output", "type": "STRING", "value": None}
107
+ ],
108
+ },
109
+ serialized_node,
110
+ )
18
111
 
19
112
 
20
- @MapNode.wrap(items=[1, 2, 3])
21
- class MapGenericNode(BaseNode):
22
- item = MapNode.SubworkflowInputs.item
23
- foo = Inputs.foo
24
- bar = State.bar
113
+ @TryNode.wrap()
114
+ class InnerTryGenericNode(BaseNode):
115
+ input = Inputs.input
25
116
 
26
117
  class Outputs(BaseOutputs):
27
- value: str
118
+ output: str
28
119
 
29
- def run(self) -> Outputs:
30
- return self.Outputs(value=f"{self.foo} {self.bar} {self.item}")
120
+
121
+ @BaseTryNodeDisplay.wrap()
122
+ class InnerTryGenericNodeDisplay(BaseNodeDisplay[InnerTryGenericNode]):
123
+ pass
31
124
 
32
125
 
33
- @pytest.mark.skip(reason="not implemented")
34
126
  def test_serialize_node__try(serialize_node):
35
- serialized_node = serialize_node(MapGenericNode)
127
+ input_id = uuid4()
128
+ serialized_node = serialize_node(
129
+ base_class=BaseNodeVellumDisplay,
130
+ node_class=InnerTryGenericNode,
131
+ global_workflow_input_displays={Inputs.input: WorkflowInputsDisplay(id=input_id)},
132
+ global_node_displays={
133
+ InnerTryGenericNode.__wrapped_node__: InnerTryGenericNodeDisplay,
134
+ },
135
+ )
136
+
36
137
  assert not DeepDiff(
37
138
  {
38
- "id": "c2ed23f7-f6cb-4a56-a91c-2e5f9d8fda7f",
39
- "label": "BasicGenericNode",
139
+ "id": str(InnerTryGenericNode.__wrapped_node__.__id__),
140
+ "label": "InnerTryGenericNode",
40
141
  "type": "GENERIC",
41
142
  "display_data": {"position": {"x": 0.0, "y": 0.0}},
42
143
  "base": {"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"]},
43
144
  "definition": {
44
- "name": "BasicGenericNode",
145
+ "name": "InnerTryGenericNode",
45
146
  "module": [
46
147
  "vellum_ee",
47
148
  "workflows",
@@ -52,16 +153,32 @@ def test_serialize_node__try(serialize_node):
52
153
  "test_adornments_serialization",
53
154
  ],
54
155
  },
55
- "trigger": {"id": "9d3a1b3d-4a38-4f2e-bbf1-dd8be152bce8", "merge_behavior": "AWAIT_ANY"},
56
- "ports": [
156
+ "trigger": {"id": "741f7f75-e921-47a9-8c05-9e66640d0866", "merge_behavior": "AWAIT_ATTRIBUTES"},
157
+ "ports": [{"id": "1b8f8ab5-a656-4015-926c-80655bbd9cb8", "name": "default", "type": "DEFAULT"}],
158
+ "adornments": [
57
159
  {
58
- "id": "4fbf0fff-a42e-4410-852a-238b5059198e",
59
- "type": "DEFAULT",
160
+ "id": "3344083c-a32c-4a32-920b-0fb5093448fa",
161
+ "label": "TryNode",
162
+ "base": {"name": "TryNode", "module": ["vellum", "workflows", "nodes", "core", "try_node", "node"]},
163
+ "attributes": [
164
+ {
165
+ "id": "ab2fbab0-e2a0-419b-b1ef-ce11ecf11e90",
166
+ "name": "on_error_code",
167
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
168
+ }
169
+ ],
60
170
  }
61
171
  ],
62
- "adornments": None,
63
- "attributes": [],
172
+ "attributes": [
173
+ {
174
+ "id": "4d8b4c2c-4f92-4c7a-abf0-b9c88a15a790",
175
+ "name": "input",
176
+ "value": {"type": "WORKFLOW_INPUT", "input_variable_id": str(input_id)},
177
+ }
178
+ ],
179
+ "outputs": [
180
+ {"id": "63ba929b-bf79-44ee-bd1f-d259dbe8d48e", "name": "output", "type": "STRING", "value": None}
181
+ ],
64
182
  },
65
183
  serialized_node,
66
- ignore_order=True,
67
184
  )
@@ -44,8 +44,8 @@ def test_serialize_node__constant_value(serialize_node):
44
44
  "test_attributes_serialization",
45
45
  ],
46
46
  },
47
- "trigger": {"id": "279e8228-9b82-43a3-8c31-affc036e3a0b", "merge_behavior": "AWAIT_ANY"},
48
- "ports": [{"id": "e6cf13b0-5590-421c-84f2-5a0b169677e1", "type": "DEFAULT", "name": "default"}],
47
+ "trigger": {"id": "279e8228-9b82-43a3-8c31-affc036e3a0b", "merge_behavior": "AWAIT_ATTRIBUTES"},
48
+ "ports": [{"id": "7cd373aa-34d1-402d-bcb4-1c8f329b63e9", "type": "DEFAULT", "name": "default"}],
49
49
  "adornments": None,
50
50
  "attributes": [
51
51
  {
@@ -96,8 +96,8 @@ def test_serialize_node__workflow_input(serialize_node):
96
96
  "test_attributes_serialization",
97
97
  ],
98
98
  },
99
- "trigger": {"id": "b1a5d749-bac0-4f11-8427-191febb2198e", "merge_behavior": "AWAIT_ANY"},
100
- "ports": [{"id": "f013bf3f-49f6-41cd-ac13-7439b71a304a", "type": "DEFAULT", "name": "default"}],
99
+ "trigger": {"id": "b1a5d749-bac0-4f11-8427-191febb2198e", "merge_behavior": "AWAIT_ATTRIBUTES"},
100
+ "ports": [{"id": "d15c7175-139c-4885-8ef8-3e4081db121b", "type": "DEFAULT", "name": "default"}],
101
101
  "adornments": None,
102
102
  "attributes": [
103
103
  {
@@ -160,8 +160,8 @@ def test_serialize_node__node_output(serialize_node):
160
160
  "test_attributes_serialization",
161
161
  ],
162
162
  },
163
- "trigger": {"id": "449072ba-f7b6-4314-ac96-682123f225e5", "merge_behavior": "AWAIT_ANY"},
164
- "ports": [{"id": "ec9a79b8-65c3-4de8-bd29-42c914d72d4f", "type": "DEFAULT", "name": "default"}],
163
+ "trigger": {"id": "449072ba-f7b6-4314-ac96-682123f225e5", "merge_behavior": "AWAIT_ATTRIBUTES"},
164
+ "ports": [{"id": "1879f33e-6efa-46a0-9281-e02bbbc1d413", "type": "DEFAULT", "name": "default"}],
165
165
  "adornments": None,
166
166
  "attributes": [
167
167
  {
@@ -210,8 +210,8 @@ def test_serialize_node__vellum_secret(serialize_node):
210
210
  "test_attributes_serialization",
211
211
  ],
212
212
  },
213
- "trigger": {"id": "3ea0305d-d8ea-45fe-8cf1-f6c1c85e6979", "merge_behavior": "AWAIT_ANY"},
214
- "ports": [{"id": "69e49322-8f8a-42d2-b36f-3fbe53dad616", "type": "DEFAULT", "name": "default"}],
213
+ "trigger": {"id": "3ea0305d-d8ea-45fe-8cf1-f6c1c85e6979", "merge_behavior": "AWAIT_ATTRIBUTES"},
214
+ "ports": [{"id": "881abc1c-ada3-4405-8faf-e167fa2f851b", "type": "DEFAULT", "name": "default"}],
215
215
  "adornments": None,
216
216
  "attributes": [
217
217
  {
@@ -266,8 +266,8 @@ def test_serialize_node__node_execution(serialize_node):
266
266
  "test_attributes_serialization",
267
267
  ],
268
268
  },
269
- "trigger": {"id": "68a91426-4c30-4194-a4c0-cff224d3c0f3", "merge_behavior": "AWAIT_ANY"},
270
- "ports": [{"id": "1794c2eb-5cab-49fe-9354-dfc29f11b374", "type": "DEFAULT", "name": "default"}],
269
+ "trigger": {"id": "68a91426-4c30-4194-a4c0-cff224d3c0f3", "merge_behavior": "AWAIT_ATTRIBUTES"},
270
+ "ports": [{"id": "0e1f78d5-8be1-4533-b2b4-a52777a8d43d", "type": "DEFAULT", "name": "default"}],
271
271
  "adornments": None,
272
272
  "attributes": [
273
273
  {
@@ -40,8 +40,8 @@ def test_serialize_node__annotated_output(serialize_node):
40
40
  "test_outputs_serialization",
41
41
  ],
42
42
  },
43
- "trigger": {"id": "256ef76c-39a6-4a8f-8bda-922f5972a1d4", "merge_behavior": "AWAIT_ANY"},
44
- "ports": [{"id": "378d8939-7a0f-4c87-a849-7fd00067cd2d", "type": "DEFAULT", "name": "default"}],
43
+ "trigger": {"id": "256ef76c-39a6-4a8f-8bda-922f5972a1d4", "merge_behavior": "AWAIT_ATTRIBUTES"},
44
+ "ports": [{"id": "9f391128-5d83-4c46-a62e-2b8bd075f569", "type": "DEFAULT", "name": "default"}],
45
45
  "adornments": None,
46
46
  "attributes": [],
47
47
  "outputs": [
@@ -89,8 +89,8 @@ def test_serialize_node__workflow_input(serialize_node):
89
89
  "test_outputs_serialization",
90
90
  ],
91
91
  },
92
- "trigger": {"id": "b1a5d749-bac0-4f11-8427-191febb2198e", "merge_behavior": "AWAIT_ANY"},
93
- "ports": [{"id": "f013bf3f-49f6-41cd-ac13-7439b71a304a", "type": "DEFAULT", "name": "default"}],
92
+ "trigger": {"id": "b1a5d749-bac0-4f11-8427-191febb2198e", "merge_behavior": "AWAIT_ATTRIBUTES"},
93
+ "ports": [{"id": "d15c7175-139c-4885-8ef8-3e4081db121b", "type": "DEFAULT", "name": "default"}],
94
94
  "adornments": None,
95
95
  "attributes": [],
96
96
  "outputs": [
@@ -155,8 +155,8 @@ def test_serialize_node__node_output_reference(serialize_node):
155
155
  "test_outputs_serialization",
156
156
  ],
157
157
  },
158
- "trigger": {"id": "449072ba-f7b6-4314-ac96-682123f225e5", "merge_behavior": "AWAIT_ANY"},
159
- "ports": [{"id": "ec9a79b8-65c3-4de8-bd29-42c914d72d4f", "type": "DEFAULT", "name": "default"}],
158
+ "trigger": {"id": "449072ba-f7b6-4314-ac96-682123f225e5", "merge_behavior": "AWAIT_ATTRIBUTES"},
159
+ "ports": [{"id": "1879f33e-6efa-46a0-9281-e02bbbc1d413", "type": "DEFAULT", "name": "default"}],
160
160
  "adornments": None,
161
161
  "attributes": [],
162
162
  "outputs": [