vellum-ai 0.14.18__py3-none-any.whl → 0.14.20__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vellum/__init__.py +2 -0
- vellum/client/core/client_wrapper.py +1 -1
- vellum/client/types/__init__.py +2 -0
- vellum/client/types/node_input_compiled_secret_value.py +23 -0
- vellum/client/types/node_input_variable_compiled_value.py +2 -0
- vellum/types/node_input_compiled_secret_value.py +3 -0
- vellum/workflows/nodes/core/inline_subworkflow_node/node.py +16 -14
- vellum/workflows/nodes/core/inline_subworkflow_node/tests/test_node.py +29 -0
- vellum/workflows/nodes/displayable/code_execution_node/node.py +1 -1
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +41 -0
- vellum/workflows/nodes/displayable/code_execution_node/utils.py +6 -1
- vellum/workflows/references/lazy.py +9 -1
- vellum/workflows/references/tests/test_lazy.py +30 -0
- {vellum_ai-0.14.18.dist-info → vellum_ai-0.14.20.dist-info}/METADATA +1 -1
- {vellum_ai-0.14.18.dist-info → vellum_ai-0.14.20.dist-info}/RECORD +44 -40
- vellum_ee/workflows/display/base.py +6 -2
- vellum_ee/workflows/display/nodes/base_node_display.py +41 -10
- vellum_ee/workflows/display/nodes/base_node_vellum_display.py +0 -20
- vellum_ee/workflows/display/nodes/get_node_display_class.py +3 -3
- vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +14 -10
- vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +2 -6
- vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +2 -6
- vellum_ee/workflows/display/nodes/vellum/tests/test_code_execution_node.py +113 -0
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py +2 -2
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +4 -4
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +39 -8
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py +3 -3
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py +14 -14
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py +3 -3
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +5 -5
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_default_state_serialization.py +1 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py +1 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +2 -2
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +2 -2
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +3 -3
- vellum_ee/workflows/display/types.py +4 -7
- vellum_ee/workflows/display/vellum.py +10 -2
- vellum_ee/workflows/display/workflows/base_workflow_display.py +60 -32
- vellum_ee/workflows/display/workflows/vellum_workflow_display.py +33 -78
- vellum_ee/workflows/server/virtual_file_loader.py +52 -22
- vellum_ee/workflows/tests/test_server.py +61 -0
- {vellum_ai-0.14.18.dist-info → vellum_ai-0.14.20.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.18.dist-info → vellum_ai-0.14.20.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.18.dist-info → vellum_ai-0.14.20.dist-info}/entry_points.txt +0 -0
@@ -115,6 +115,10 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
|
115
115
|
port_displays: Dict[Port, PortDisplayOverrides] = {}
|
116
116
|
node_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
|
117
117
|
|
118
|
+
# Used to explicitly set the target handle id for a node
|
119
|
+
# Once all nodes are Generic Nodes, we may replace this with a trigger_id or trigger attribute
|
120
|
+
target_handle_id: ClassVar[Optional[UUID]] = None
|
121
|
+
|
118
122
|
# Used to store the mapping between node types and their display classes
|
119
123
|
_node_display_registry: Dict[Type[NodeType], Type["BaseNodeDisplay"]] = {}
|
120
124
|
|
@@ -129,13 +133,16 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
|
129
133
|
continue
|
130
134
|
|
131
135
|
id = str(uuid4_from_hash(f"{node_id}|{attribute.name}"))
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
136
|
+
try:
|
137
|
+
attributes.append(
|
138
|
+
{
|
139
|
+
"id": id,
|
140
|
+
"name": attribute.name,
|
141
|
+
"value": self.serialize_value(display_context, attribute.instance),
|
142
|
+
}
|
143
|
+
)
|
144
|
+
except ValueError as e:
|
145
|
+
raise ValueError(f"Failed to serialize attribute '{attribute.name}': {e}")
|
139
146
|
|
140
147
|
adornments = kwargs.get("adornments", None)
|
141
148
|
wrapped_node = get_wrapped_node(node)
|
@@ -202,7 +209,7 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
|
202
209
|
"base": self.get_base().dict(),
|
203
210
|
"definition": self.get_definition().dict(),
|
204
211
|
"trigger": {
|
205
|
-
"id": str(self.
|
212
|
+
"id": str(self.get_target_handle_id()),
|
206
213
|
"merge_behavior": node.Trigger.merge_behavior.value,
|
207
214
|
},
|
208
215
|
"ports": ports,
|
@@ -252,7 +259,24 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
|
252
259
|
return PortDisplay(id=port_id, node_id=self.node_id)
|
253
260
|
|
254
261
|
def get_trigger_id(self) -> UUID:
|
255
|
-
return
|
262
|
+
return self.get_target_handle_id()
|
263
|
+
|
264
|
+
def get_target_handle_id(self) -> UUID:
|
265
|
+
"""
|
266
|
+
Is the same as `get_trigger_id()` but kept for legacy workflows. Once all workflows have been updated to
|
267
|
+
become Generic Nodes, we should be able to drive off of only `get_trigger_id()` going forward
|
268
|
+
"""
|
269
|
+
|
270
|
+
return self._get_node_display_uuid("target_handle_id")
|
271
|
+
|
272
|
+
def get_target_handle_id_by_source_node_id(self, source_node_id: UUID) -> UUID:
|
273
|
+
"""
|
274
|
+
In the vast majority of cases, nodes will only have a single target handle and can be retrieved independently
|
275
|
+
of the source node. However, in rare cases (such as legacy Merge nodes), this method can be overridden to
|
276
|
+
account for the case of retrieving one amongst multiple target handles on a node.
|
277
|
+
"""
|
278
|
+
|
279
|
+
return self.get_target_handle_id()
|
256
280
|
|
257
281
|
@classmethod
|
258
282
|
def get_from_node_display_registry(cls, node_class: Type[NodeType]) -> Optional[Type["BaseNodeDisplay"]]:
|
@@ -312,6 +336,10 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
|
312
336
|
|
313
337
|
raise ValueError(f"Node {cls.__name__} must define an explicit {attribute} of type {attribute_type.__name__}.")
|
314
338
|
|
339
|
+
def _get_node_display_uuid(self, attribute: str) -> UUID:
|
340
|
+
explicit_value = self._get_explicit_node_display_attr(attribute, UUID)
|
341
|
+
return explicit_value if explicit_value else uuid4_from_hash(f"{self.node_id}|{attribute}")
|
342
|
+
|
315
343
|
def __init_subclass__(cls, **kwargs: Any) -> None:
|
316
344
|
super().__init_subclass__(**kwargs)
|
317
345
|
if not cls._node_display_registry:
|
@@ -369,7 +397,7 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
|
369
397
|
"rhs": rhs,
|
370
398
|
}
|
371
399
|
|
372
|
-
def serialize_value(self, display_context: "WorkflowDisplayContext", value:
|
400
|
+
def serialize_value(self, display_context: "WorkflowDisplayContext", value: Any) -> JsonObject:
|
373
401
|
if isinstance(value, ConstantValueReference):
|
374
402
|
return self.serialize_value(display_context, value._value)
|
375
403
|
|
@@ -415,6 +443,9 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
|
415
443
|
"node_id": str(node_class_display.node_id),
|
416
444
|
}
|
417
445
|
|
446
|
+
if isinstance(value, dict) and any(isinstance(v, BaseDescriptor) for v in value.values()):
|
447
|
+
raise ValueError("Nested references are not supported.")
|
448
|
+
|
418
449
|
if not isinstance(value, BaseDescriptor):
|
419
450
|
vellum_value = primitive_to_vellum_value(value)
|
420
451
|
return {
|
@@ -4,7 +4,6 @@ from typing import ClassVar, Dict, Optional
|
|
4
4
|
from vellum.workflows.nodes.utils import get_unadorned_node
|
5
5
|
from vellum.workflows.ports import Port
|
6
6
|
from vellum.workflows.types.generics import NodeType
|
7
|
-
from vellum.workflows.utils.uuids import uuid4_from_hash
|
8
7
|
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
9
8
|
from vellum_ee.workflows.display.nodes.types import PortDisplay
|
10
9
|
from vellum_ee.workflows.display.vellum import NodeDisplayComment, NodeDisplayData
|
@@ -14,13 +13,6 @@ class BaseNodeVellumDisplay(BaseNodeDisplay[NodeType]):
|
|
14
13
|
# Used to explicitly set display data for a node
|
15
14
|
display_data: ClassVar[Optional[NodeDisplayData]] = None
|
16
15
|
|
17
|
-
# Used to explicitly set the target handle id for a node
|
18
|
-
target_handle_id: ClassVar[Optional[UUID]] = None
|
19
|
-
|
20
|
-
def _get_node_display_uuid(self, attribute: str) -> UUID:
|
21
|
-
explicit_value = self._get_explicit_node_display_attr(attribute, UUID)
|
22
|
-
return explicit_value if explicit_value else uuid4_from_hash(f"{self.node_id}|{attribute}")
|
23
|
-
|
24
16
|
def get_display_data(self) -> NodeDisplayData:
|
25
17
|
explicit_value = self._get_explicit_node_display_attr("display_data", NodeDisplayData)
|
26
18
|
docstring = self._node.__doc__
|
@@ -40,18 +32,6 @@ class BaseNodeVellumDisplay(BaseNodeDisplay[NodeType]):
|
|
40
32
|
|
41
33
|
return explicit_value if explicit_value else NodeDisplayData()
|
42
34
|
|
43
|
-
def get_target_handle_id(self) -> UUID:
|
44
|
-
return self._get_node_display_uuid("target_handle_id")
|
45
|
-
|
46
|
-
def get_target_handle_id_by_source_node_id(self, source_node_id: UUID) -> UUID:
|
47
|
-
"""
|
48
|
-
In the vast majority of cases, nodes will only have a single target handle and can be retrieved independently
|
49
|
-
of the source node. However, in rare cases (such as legacy Merge nodes), this method can be overridden to
|
50
|
-
account for the case of retrieving one amongst multiple target handles on a node.
|
51
|
-
"""
|
52
|
-
|
53
|
-
return self.get_target_handle_id()
|
54
|
-
|
55
35
|
def get_source_handle_id(self, port_displays: Dict[Port, PortDisplay]) -> UUID:
|
56
36
|
unadorned_node = get_unadorned_node(self._node)
|
57
37
|
default_port = unadorned_node.Ports.default
|
@@ -7,12 +7,12 @@ from vellum.workflows.types.generics import NodeType
|
|
7
7
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
8
8
|
|
9
9
|
if TYPE_CHECKING:
|
10
|
-
from vellum_ee.workflows.display.
|
10
|
+
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
11
11
|
|
12
12
|
|
13
13
|
def get_node_display_class(
|
14
|
-
base_class: Type["
|
15
|
-
) -> Type["
|
14
|
+
base_class: Type["BaseNodeDisplay"], node_class: Type[NodeType], root_node_class: Optional[Type[NodeType]] = None
|
15
|
+
) -> Type["BaseNodeDisplay"]:
|
16
16
|
node_display_class = base_class.get_from_node_display_registry(node_class)
|
17
17
|
if node_display_class:
|
18
18
|
if not issubclass(node_display_class, base_class):
|
@@ -15,9 +15,6 @@ _CodeExecutionNodeType = TypeVar("_CodeExecutionNodeType", bound=CodeExecutionNo
|
|
15
15
|
|
16
16
|
|
17
17
|
class BaseCodeExecutionNodeDisplay(BaseNodeVellumDisplay[_CodeExecutionNodeType], Generic[_CodeExecutionNodeType]):
|
18
|
-
code_input_id: ClassVar[Optional[UUID]] = None
|
19
|
-
runtime_input_id: ClassVar[Optional[UUID]] = None
|
20
|
-
|
21
18
|
output_id: ClassVar[Optional[UUID]] = None
|
22
19
|
log_output_id: ClassVar[Optional[UUID]] = None
|
23
20
|
|
@@ -27,17 +24,20 @@ class BaseCodeExecutionNodeDisplay(BaseNodeVellumDisplay[_CodeExecutionNodeType]
|
|
27
24
|
node = self._node
|
28
25
|
node_id = self.node_id
|
29
26
|
raw_code = raise_if_descriptor(node.code)
|
27
|
+
filepath = raise_if_descriptor(node.filepath)
|
30
28
|
|
31
29
|
code_value: Optional[str]
|
32
30
|
if raw_code:
|
33
31
|
code_value = raw_code
|
34
|
-
|
32
|
+
elif filepath:
|
35
33
|
node_file_path = inspect.getfile(node)
|
36
34
|
file_code = read_file_from_path(
|
37
35
|
node_filepath=node_file_path,
|
38
|
-
script_filepath=
|
36
|
+
script_filepath=filepath,
|
39
37
|
)
|
40
38
|
code_value = file_code
|
39
|
+
else:
|
40
|
+
code_value = ""
|
41
41
|
|
42
42
|
code_inputs = raise_if_descriptor(node.code_inputs)
|
43
43
|
|
@@ -47,24 +47,28 @@ class BaseCodeExecutionNodeDisplay(BaseNodeVellumDisplay[_CodeExecutionNodeType]
|
|
47
47
|
input_name=variable_name,
|
48
48
|
value=variable_value,
|
49
49
|
display_context=display_context,
|
50
|
-
input_id=self.node_input_ids_by_name.get(variable_name)
|
50
|
+
input_id=self.node_input_ids_by_name.get(f"{CodeExecutionNode.code_inputs.name}.{variable_name}")
|
51
|
+
or self.node_input_ids_by_name.get(variable_name),
|
51
52
|
)
|
52
53
|
for variable_name, variable_value in code_inputs.items()
|
53
54
|
]
|
54
55
|
|
56
|
+
code_input_id = self.node_input_ids_by_name.get(CodeExecutionNode.code.name)
|
55
57
|
code_node_input = create_node_input(
|
56
58
|
node_id=node_id,
|
57
59
|
input_name="code",
|
58
60
|
value=code_value,
|
59
61
|
display_context=display_context,
|
60
|
-
input_id=
|
62
|
+
input_id=code_input_id,
|
61
63
|
)
|
64
|
+
|
65
|
+
runtime_input_id = self.node_input_ids_by_name.get(CodeExecutionNode.runtime.name)
|
62
66
|
runtime_node_input = create_node_input(
|
63
67
|
node_id=node_id,
|
64
68
|
input_name="runtime",
|
65
69
|
value=node.runtime,
|
66
70
|
display_context=display_context,
|
67
|
-
input_id=
|
71
|
+
input_id=runtime_input_id,
|
68
72
|
)
|
69
73
|
inputs.extend([code_node_input, runtime_node_input])
|
70
74
|
|
@@ -84,8 +88,8 @@ class BaseCodeExecutionNodeDisplay(BaseNodeVellumDisplay[_CodeExecutionNodeType]
|
|
84
88
|
"error_output_id": str(error_output_id) if error_output_id else None,
|
85
89
|
"source_handle_id": str(self.get_source_handle_id(display_context.port_displays)),
|
86
90
|
"target_handle_id": str(self.get_target_handle_id()),
|
87
|
-
"code_input_id":
|
88
|
-
"runtime_input_id":
|
91
|
+
"code_input_id": code_node_input.id,
|
92
|
+
"runtime_input_id": runtime_node_input.id,
|
89
93
|
"output_type": output_type,
|
90
94
|
"packages": [package.dict() for package in packages] if packages is not None else [],
|
91
95
|
"output_id": str(self.output_id) if self.output_id else str(output_display.id),
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from uuid import UUID
|
2
|
-
from typing import Callable,
|
2
|
+
from typing import Callable, Dict, Generic, List, Optional, Tuple, Type, TypeVar, Union
|
3
3
|
|
4
4
|
from vellum import FunctionDefinition, PromptBlock, RichTextChildBlock, VellumVariable
|
5
5
|
from vellum.workflows.nodes import InlinePromptNode
|
@@ -17,10 +17,6 @@ _InlinePromptNodeType = TypeVar("_InlinePromptNodeType", bound=InlinePromptNode)
|
|
17
17
|
|
18
18
|
|
19
19
|
class BaseInlinePromptNodeDisplay(BaseNodeVellumDisplay[_InlinePromptNodeType], Generic[_InlinePromptNodeType]):
|
20
|
-
output_id: ClassVar[Optional[UUID]] = None
|
21
|
-
array_output_id: ClassVar[Optional[UUID]] = None
|
22
|
-
prompt_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
|
23
|
-
|
24
20
|
def serialize(
|
25
21
|
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
|
26
22
|
) -> JsonObject:
|
@@ -98,7 +94,7 @@ class BaseInlinePromptNodeDisplay(BaseNodeVellumDisplay[_InlinePromptNodeType],
|
|
98
94
|
input_name=variable_name,
|
99
95
|
value=variable_value,
|
100
96
|
display_context=display_context,
|
101
|
-
input_id=self.
|
97
|
+
input_id=self.node_input_ids_by_name.get(variable_name),
|
102
98
|
)
|
103
99
|
vellum_variable_type = infer_vellum_variable_type(variable_value)
|
104
100
|
node_inputs.append(node_input)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from uuid import UUID
|
2
|
-
from typing import
|
2
|
+
from typing import Generic, Optional, TypeVar, cast
|
3
3
|
|
4
4
|
from vellum.workflows.nodes.displayable.prompt_deployment_node import PromptDeploymentNode
|
5
5
|
from vellum.workflows.references import OutputReference
|
@@ -16,10 +16,6 @@ _PromptDeploymentNodeType = TypeVar("_PromptDeploymentNodeType", bound=PromptDep
|
|
16
16
|
class BasePromptDeploymentNodeDisplay(
|
17
17
|
BaseNodeVellumDisplay[_PromptDeploymentNodeType], Generic[_PromptDeploymentNodeType]
|
18
18
|
):
|
19
|
-
output_id: ClassVar[Optional[UUID]] = None
|
20
|
-
array_output_id: ClassVar[Optional[UUID]] = None
|
21
|
-
prompt_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
|
22
|
-
|
23
19
|
def serialize(
|
24
20
|
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
|
25
21
|
) -> JsonObject:
|
@@ -34,7 +30,7 @@ class BasePromptDeploymentNodeDisplay(
|
|
34
30
|
input_name=variable_name,
|
35
31
|
value=variable_value,
|
36
32
|
display_context=display_context,
|
37
|
-
input_id=self.
|
33
|
+
input_id=self.node_input_ids_by_name.get(variable_name),
|
38
34
|
)
|
39
35
|
for variable_name, variable_value in prompt_inputs.items()
|
40
36
|
]
|
@@ -0,0 +1,113 @@
|
|
1
|
+
import pytest
|
2
|
+
from uuid import UUID
|
3
|
+
from typing import Type
|
4
|
+
|
5
|
+
from vellum.workflows.nodes.displayable.code_execution_node.node import CodeExecutionNode
|
6
|
+
from vellum.workflows.workflows.base import BaseWorkflow
|
7
|
+
from vellum_ee.workflows.display.nodes.vellum.code_execution_node import BaseCodeExecutionNodeDisplay
|
8
|
+
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
9
|
+
from vellum_ee.workflows.display.workflows.vellum_workflow_display import VellumWorkflowDisplay
|
10
|
+
|
11
|
+
|
12
|
+
def _no_display_class(Node: Type[CodeExecutionNode]):
|
13
|
+
return None
|
14
|
+
|
15
|
+
|
16
|
+
def _display_class_with_node_input_ids_by_name(Node: Type[CodeExecutionNode]):
|
17
|
+
class CodeExecutionNodeDisplay(BaseCodeExecutionNodeDisplay[Node]): # type: ignore[valid-type]
|
18
|
+
node_input_ids_by_name = {"foo": UUID("fba6a4d5-835a-4e99-afb7-f6a4aed15110")}
|
19
|
+
|
20
|
+
return CodeExecutionNodeDisplay
|
21
|
+
|
22
|
+
|
23
|
+
def _display_class_with_node_input_ids_by_name_with_inputs_prefix(Node: Type[CodeExecutionNode]):
|
24
|
+
class CodeExecutionNodeDisplay(BaseCodeExecutionNodeDisplay[Node]): # type: ignore[valid-type]
|
25
|
+
node_input_ids_by_name = {"code_inputs.foo": UUID("fba6a4d5-835a-4e99-afb7-f6a4aed15110")}
|
26
|
+
|
27
|
+
return CodeExecutionNodeDisplay
|
28
|
+
|
29
|
+
|
30
|
+
@pytest.mark.parametrize(
|
31
|
+
["GetDisplayClass", "expected_input_id"],
|
32
|
+
[
|
33
|
+
(_no_display_class, "e3cdb222-324e-4ad1-abb2-bdd7881b3a0e"),
|
34
|
+
(_display_class_with_node_input_ids_by_name, "fba6a4d5-835a-4e99-afb7-f6a4aed15110"),
|
35
|
+
(_display_class_with_node_input_ids_by_name_with_inputs_prefix, "fba6a4d5-835a-4e99-afb7-f6a4aed15110"),
|
36
|
+
],
|
37
|
+
ids=[
|
38
|
+
"no_display_class",
|
39
|
+
"display_class_with_node_input_ids_by_name",
|
40
|
+
"display_class_with_node_input_ids_by_name_with_inputs_prefix",
|
41
|
+
],
|
42
|
+
)
|
43
|
+
def test_serialize_node__code_node_inputs(GetDisplayClass, expected_input_id):
|
44
|
+
# GIVEN a code node with inputs
|
45
|
+
class MyCodeExecutionNode(CodeExecutionNode):
|
46
|
+
code_inputs = {"foo": "bar"}
|
47
|
+
|
48
|
+
# AND a workflow with the code node
|
49
|
+
class Workflow(BaseWorkflow):
|
50
|
+
graph = MyCodeExecutionNode
|
51
|
+
|
52
|
+
# AND a display class
|
53
|
+
GetDisplayClass(MyCodeExecutionNode)
|
54
|
+
|
55
|
+
# WHEN the workflow is serialized
|
56
|
+
workflow_display = get_workflow_display(base_display_class=VellumWorkflowDisplay, workflow_class=Workflow)
|
57
|
+
serialized_workflow: dict = workflow_display.serialize()
|
58
|
+
|
59
|
+
# THEN the node should properly serialize the inputs
|
60
|
+
my_code_execution_node = next(
|
61
|
+
node for node in serialized_workflow["workflow_raw_data"]["nodes"] if node["type"] == "CODE_EXECUTION"
|
62
|
+
)
|
63
|
+
|
64
|
+
assert my_code_execution_node["inputs"] == [
|
65
|
+
{
|
66
|
+
"id": expected_input_id,
|
67
|
+
"key": "foo",
|
68
|
+
"value": {
|
69
|
+
"combinator": "OR",
|
70
|
+
"rules": [
|
71
|
+
{
|
72
|
+
"type": "CONSTANT_VALUE",
|
73
|
+
"data": {
|
74
|
+
"type": "STRING",
|
75
|
+
"value": "bar",
|
76
|
+
},
|
77
|
+
}
|
78
|
+
],
|
79
|
+
},
|
80
|
+
},
|
81
|
+
{
|
82
|
+
"id": "9774d864-c76d-4a1a-8181-b632ed3ab87c",
|
83
|
+
"key": "code",
|
84
|
+
"value": {
|
85
|
+
"combinator": "OR",
|
86
|
+
"rules": [
|
87
|
+
{
|
88
|
+
"type": "CONSTANT_VALUE",
|
89
|
+
"data": {
|
90
|
+
"type": "STRING",
|
91
|
+
"value": "",
|
92
|
+
},
|
93
|
+
}
|
94
|
+
],
|
95
|
+
},
|
96
|
+
},
|
97
|
+
{
|
98
|
+
"id": "34742235-5699-45cd-9d34-bce3745e743d",
|
99
|
+
"key": "runtime",
|
100
|
+
"value": {
|
101
|
+
"combinator": "OR",
|
102
|
+
"rules": [
|
103
|
+
{
|
104
|
+
"type": "CONSTANT_VALUE",
|
105
|
+
"data": {
|
106
|
+
"type": "STRING",
|
107
|
+
"value": "PYTHON_3_11_6",
|
108
|
+
},
|
109
|
+
}
|
110
|
+
],
|
111
|
+
},
|
112
|
+
},
|
113
|
+
]
|
@@ -12,7 +12,7 @@ from vellum_ee.workflows.display.base import StateValueDisplayType, WorkflowInpu
|
|
12
12
|
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
13
13
|
from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
|
14
14
|
from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay
|
15
|
-
from vellum_ee.workflows.display.types import
|
15
|
+
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
16
16
|
from vellum_ee.workflows.display.vellum import NodeDisplayData, WorkflowMetaVellumDisplay
|
17
17
|
from vellum_ee.workflows.display.workflows.vellum_workflow_display import VellumWorkflowDisplay
|
18
18
|
|
@@ -24,7 +24,7 @@ def serialize_node():
|
|
24
24
|
base_class: type[BaseNodeDisplay[Any]] = BaseNodeDisplay,
|
25
25
|
global_workflow_input_displays: Dict[WorkflowInputReference, WorkflowInputsDisplayType] = {},
|
26
26
|
global_state_value_displays: Dict[StateValueReference, StateValueDisplayType] = {},
|
27
|
-
global_node_displays: Dict[Type[BaseNode],
|
27
|
+
global_node_displays: Dict[Type[BaseNode], BaseNodeDisplay] = {},
|
28
28
|
global_node_output_displays: Dict[OutputReference, Tuple[Type[BaseNode], NodeOutputDisplay]] = {},
|
29
29
|
) -> JsonObject:
|
30
30
|
node_display_class = get_node_display_class(base_class, node_class)
|
@@ -64,7 +64,7 @@ def test_serialize_node__retry(serialize_node):
|
|
64
64
|
"test_adornments_serialization",
|
65
65
|
],
|
66
66
|
},
|
67
|
-
"trigger": {"id": "
|
67
|
+
"trigger": {"id": "75fbe874-c00b-4fc2-9ade-52f4fe9209fa", "merge_behavior": "AWAIT_ATTRIBUTES"},
|
68
68
|
"ports": [{"id": "078650c9-f775-4cd0-a08c-23af9983a361", "name": "default", "type": "DEFAULT"}],
|
69
69
|
"adornments": [
|
70
70
|
{
|
@@ -175,7 +175,7 @@ def test_serialize_node__try(serialize_node):
|
|
175
175
|
"test_adornments_serialization",
|
176
176
|
],
|
177
177
|
},
|
178
|
-
"trigger": {"id": "
|
178
|
+
"trigger": {"id": "bbb343ff-2b7a-4793-a8cf-fb05132ca46a", "merge_behavior": "AWAIT_ATTRIBUTES"},
|
179
179
|
"ports": [{"id": "8d25f244-4b12-4f8b-b202-8948698679a0", "name": "default", "type": "DEFAULT"}],
|
180
180
|
"adornments": [
|
181
181
|
{
|
@@ -283,7 +283,7 @@ def test_serialize_node__stacked():
|
|
283
283
|
"test_adornments_serialization",
|
284
284
|
],
|
285
285
|
},
|
286
|
-
"trigger": {"id": "
|
286
|
+
"trigger": {"id": "6e4af17f-bbee-4777-b10d-af042cd6e16a", "merge_behavior": "AWAIT_ATTRIBUTES"},
|
287
287
|
"ports": [{"id": "408cd5fb-3a3e-4eb2-9889-61111bd6a129", "name": "default", "type": "DEFAULT"}],
|
288
288
|
"adornments": [
|
289
289
|
{
|
@@ -342,7 +342,7 @@ def test_serialize_node__stacked():
|
|
342
342
|
"source_node_id": "c14c1c9b-a7a4-4d2c-84fb-c940cfb09525",
|
343
343
|
"source_handle_id": "51a5eb25-af14-4bee-9ced-d2aa534ea8e9",
|
344
344
|
"target_node_id": "074833b0-e142-4bbc-8dec-209a35e178a3",
|
345
|
-
"target_handle_id": "
|
345
|
+
"target_handle_id": "6e4af17f-bbee-4777-b10d-af042cd6e16a",
|
346
346
|
"type": "DEFAULT",
|
347
347
|
}
|
348
348
|
],
|
@@ -1,12 +1,16 @@
|
|
1
|
+
import pytest
|
1
2
|
from uuid import uuid4
|
3
|
+
from typing import List
|
2
4
|
|
3
5
|
from deepdiff import DeepDiff
|
4
6
|
|
7
|
+
from vellum.client.types.chat_message import ChatMessage
|
5
8
|
from vellum.workflows.inputs.base import BaseInputs
|
6
9
|
from vellum.workflows.nodes.bases.base import BaseNode
|
7
10
|
from vellum.workflows.references.constant import ConstantValueReference
|
8
11
|
from vellum.workflows.references.lazy import LazyReference
|
9
12
|
from vellum.workflows.references.vellum_secret import VellumSecretReference
|
13
|
+
from vellum.workflows.state.base import BaseState
|
10
14
|
from vellum.workflows.workflows.base import BaseWorkflow
|
11
15
|
from vellum_ee.workflows.display.base import WorkflowInputsDisplay
|
12
16
|
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
@@ -44,7 +48,7 @@ def test_serialize_node__constant_value(serialize_node):
|
|
44
48
|
"test_attributes_serialization",
|
45
49
|
],
|
46
50
|
},
|
47
|
-
"trigger": {"id": "
|
51
|
+
"trigger": {"id": "e2cde904-de60-4755-87cf-55052ea23a51", "merge_behavior": "AWAIT_ATTRIBUTES"},
|
48
52
|
"ports": [{"id": "96ac6512-0128-4cf7-ba51-2725b4807c8f", "type": "DEFAULT", "name": "default"}],
|
49
53
|
"adornments": None,
|
50
54
|
"attributes": [
|
@@ -92,7 +96,7 @@ def test_serialize_node__constant_value_reference(serialize_node):
|
|
92
96
|
"test_attributes_serialization",
|
93
97
|
],
|
94
98
|
},
|
95
|
-
"trigger": {"id": "
|
99
|
+
"trigger": {"id": "dc2f90b9-14a1-457a-a9f9-dec7a04f74eb", "merge_behavior": "AWAIT_ATTRIBUTES"},
|
96
100
|
"ports": [{"id": "61adfacf-c3a9-4aea-a3da-bcdbc03273c6", "name": "default", "type": "DEFAULT"}],
|
97
101
|
"adornments": None,
|
98
102
|
"attributes": [
|
@@ -134,7 +138,7 @@ def test_serialize_node__lazy_reference(serialize_node):
|
|
134
138
|
"test_attributes_serialization",
|
135
139
|
],
|
136
140
|
},
|
137
|
-
"trigger": {"id": "
|
141
|
+
"trigger": {"id": "14ec4d19-13e5-4db3-94fa-4e15274bffc7", "merge_behavior": "AWAIT_ATTRIBUTES"},
|
138
142
|
"ports": [{"id": "2dba7224-a376-4780-8414-2b50601f9283", "name": "default", "type": "DEFAULT"}],
|
139
143
|
"adornments": None,
|
140
144
|
"attributes": [
|
@@ -217,7 +221,7 @@ def test_serialize_node__workflow_input(serialize_node):
|
|
217
221
|
"test_attributes_serialization",
|
218
222
|
],
|
219
223
|
},
|
220
|
-
"trigger": {"id": "
|
224
|
+
"trigger": {"id": "debf37b9-720d-48dd-9699-69283966f927", "merge_behavior": "AWAIT_ATTRIBUTES"},
|
221
225
|
"ports": [{"id": "20d91130-ca86-4420-b2e7-a962c0f1a509", "type": "DEFAULT", "name": "default"}],
|
222
226
|
"adornments": None,
|
223
227
|
"attributes": [
|
@@ -237,6 +241,33 @@ def test_serialize_node__workflow_input(serialize_node):
|
|
237
241
|
)
|
238
242
|
|
239
243
|
|
244
|
+
def test_serialize_node__workflow_input_as_nested_chat_history():
|
245
|
+
# GIVEN workflow inputs as chat history
|
246
|
+
class Inputs(BaseInputs):
|
247
|
+
chat_history: List[ChatMessage]
|
248
|
+
|
249
|
+
# AND a node referencing the workflow input
|
250
|
+
class GenericNode(BaseNode):
|
251
|
+
attr = {
|
252
|
+
"hello": Inputs.chat_history,
|
253
|
+
}
|
254
|
+
|
255
|
+
# AND a workflow with the node
|
256
|
+
class Workflow(BaseWorkflow[Inputs, BaseState]):
|
257
|
+
graph = GenericNode
|
258
|
+
|
259
|
+
# WHEN the workflow is serialized
|
260
|
+
workflow_display = get_workflow_display(
|
261
|
+
base_display_class=VellumWorkflowDisplay,
|
262
|
+
workflow_class=Workflow,
|
263
|
+
)
|
264
|
+
with pytest.raises(Exception) as exc_info:
|
265
|
+
workflow_display.serialize()
|
266
|
+
|
267
|
+
# THEN we should raise a user facing error
|
268
|
+
assert str(exc_info.value) == "Failed to serialize attribute 'attr': Nested references are not supported."
|
269
|
+
|
270
|
+
|
240
271
|
def test_serialize_node__node_output(serialize_node):
|
241
272
|
class NodeWithOutput(BaseNode):
|
242
273
|
class Outputs(BaseNode.Outputs):
|
@@ -278,7 +309,7 @@ def test_serialize_node__node_output(serialize_node):
|
|
278
309
|
"test_attributes_serialization",
|
279
310
|
],
|
280
311
|
},
|
281
|
-
"trigger": {"id": "
|
312
|
+
"trigger": {"id": "d4b08664-bb78-4fdd-83a2-877c4ca4175a", "merge_behavior": "AWAIT_ATTRIBUTES"},
|
282
313
|
"ports": [{"id": "a345665a-decd-4f6b-af38-387bd41c2643", "type": "DEFAULT", "name": "default"}],
|
283
314
|
"adornments": None,
|
284
315
|
"attributes": [
|
@@ -328,7 +359,7 @@ def test_serialize_node__vellum_secret(serialize_node):
|
|
328
359
|
"test_attributes_serialization",
|
329
360
|
],
|
330
361
|
},
|
331
|
-
"trigger": {"id": "
|
362
|
+
"trigger": {"id": "70a3d4c0-83e3-428d-ac84-bf9e5644a84d", "merge_behavior": "AWAIT_ATTRIBUTES"},
|
332
363
|
"ports": [{"id": "6d1c2139-64bd-4433-84d7-3fe08850134b", "type": "DEFAULT", "name": "default"}],
|
333
364
|
"adornments": None,
|
334
365
|
"attributes": [
|
@@ -381,7 +412,7 @@ def test_serialize_node__node_execution(serialize_node):
|
|
381
412
|
"test_attributes_serialization",
|
382
413
|
],
|
383
414
|
},
|
384
|
-
"trigger": {"id": "
|
415
|
+
"trigger": {"id": "0c06baa5-55b6-494a-a89d-9535dfa5f24b", "merge_behavior": "AWAIT_ATTRIBUTES"},
|
385
416
|
"ports": [{"id": "59844b72-ac5e-43c5-b3a7-9c57ba73ec8c", "type": "DEFAULT", "name": "default"}],
|
386
417
|
"adornments": None,
|
387
418
|
"attributes": [
|
@@ -462,7 +493,7 @@ def test_serialize_node__coalesce(serialize_node):
|
|
462
493
|
"test_attributes_serialization",
|
463
494
|
],
|
464
495
|
},
|
465
|
-
"trigger": {"id": "
|
496
|
+
"trigger": {"id": "b9894d9a-1887-416d-895d-a4129aac37b8", "merge_behavior": "AWAIT_ATTRIBUTES"},
|
466
497
|
"ports": [{"id": "9d97a0c9-6a79-433a-bcdf-e07aa10c0f3c", "name": "default", "type": "DEFAULT"}],
|
467
498
|
"adornments": None,
|
468
499
|
"attributes": [
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py
CHANGED
@@ -39,7 +39,7 @@ def test_serialize_node__annotated_output(serialize_node):
|
|
39
39
|
"test_outputs_serialization",
|
40
40
|
],
|
41
41
|
},
|
42
|
-
"trigger": {"id": "
|
42
|
+
"trigger": {"id": "e66c7dde-02c9-4f6d-84a6-16117b54cd88", "merge_behavior": "AWAIT_ATTRIBUTES"},
|
43
43
|
"ports": [{"id": "d83b7a5d-bbac-47ee-9277-1fbed71e83e8", "type": "DEFAULT", "name": "default"}],
|
44
44
|
"adornments": None,
|
45
45
|
"attributes": [],
|
@@ -87,7 +87,7 @@ def test_serialize_node__workflow_input(serialize_node):
|
|
87
87
|
"test_outputs_serialization",
|
88
88
|
],
|
89
89
|
},
|
90
|
-
"trigger": {"id": "
|
90
|
+
"trigger": {"id": "debf37b9-720d-48dd-9699-69283966f927", "merge_behavior": "AWAIT_ATTRIBUTES"},
|
91
91
|
"ports": [{"id": "20d91130-ca86-4420-b2e7-a962c0f1a509", "type": "DEFAULT", "name": "default"}],
|
92
92
|
"adornments": None,
|
93
93
|
"attributes": [],
|
@@ -150,7 +150,7 @@ def test_serialize_node__node_output_reference(serialize_node):
|
|
150
150
|
"test_outputs_serialization",
|
151
151
|
],
|
152
152
|
},
|
153
|
-
"trigger": {"id": "
|
153
|
+
"trigger": {"id": "c8804b97-9f84-41b6-ade8-aa74544d6846", "merge_behavior": "AWAIT_ATTRIBUTES"},
|
154
154
|
"ports": [{"id": "383dc10a-d8f3-4bac-b995-8b95bc6deb21", "type": "DEFAULT", "name": "default"}],
|
155
155
|
"adornments": None,
|
156
156
|
"attributes": [],
|