vellum-ai 0.13.9__py3-none-any.whl → 0.13.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/workflows/descriptors/utils.py +1 -1
- vellum/workflows/errors/types.py +21 -0
- vellum/workflows/nodes/bases/base.py +1 -1
- vellum/workflows/nodes/displayable/api_node/node.py +4 -1
- vellum/workflows/nodes/displayable/bases/api_node/node.py +4 -1
- vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +18 -2
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +4 -0
- vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +4 -0
- vellum/workflows/nodes/displayable/bases/search_node.py +4 -0
- vellum/workflows/nodes/displayable/bases/tests/test_utils.py +18 -0
- vellum/workflows/nodes/displayable/bases/utils.py +8 -1
- vellum/workflows/nodes/displayable/code_execution_node/node.py +4 -1
- vellum/workflows/nodes/displayable/conditional_node/node.py +4 -0
- vellum/workflows/nodes/displayable/final_output_node/node.py +4 -0
- vellum/workflows/nodes/displayable/guardrail_node/node.py +4 -1
- vellum/workflows/nodes/displayable/inline_prompt_node/node.py +4 -0
- vellum/workflows/nodes/displayable/inline_prompt_node/tests/test_node.py +55 -0
- vellum/workflows/nodes/displayable/merge_node/node.py +3 -1
- vellum/workflows/nodes/displayable/note_node/node.py +4 -0
- vellum/workflows/nodes/displayable/prompt_deployment_node/node.py +4 -0
- vellum/workflows/nodes/displayable/search_node/node.py +4 -0
- vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +4 -1
- {vellum_ai-0.13.9.dist-info → vellum_ai-0.13.11.dist-info}/METADATA +1 -1
- {vellum_ai-0.13.9.dist-info → vellum_ai-0.13.11.dist-info}/RECORD +59 -59
- vellum_cli/__init__.py +9 -1
- vellum_cli/config.py +29 -1
- vellum_cli/push.py +24 -3
- vellum_cli/tests/conftest.py +3 -0
- vellum_cli/tests/test_pull.py +6 -0
- vellum_cli/tests/test_push.py +88 -1
- vellum_ee/workflows/display/nodes/base_node_display.py +207 -3
- vellum_ee/workflows/display/nodes/base_node_vellum_display.py +16 -1
- vellum_ee/workflows/display/nodes/get_node_display_class.py +6 -4
- vellum_ee/workflows/display/nodes/vellum/__init__.py +0 -2
- vellum_ee/workflows/display/nodes/vellum/conditional_node.py +2 -1
- vellum_ee/workflows/display/nodes/vellum/error_node.py +9 -3
- vellum_ee/workflows/display/nodes/vellum/tests/test_error_node.py +44 -0
- vellum_ee/workflows/display/nodes/vellum/try_node.py +8 -2
- vellum_ee/workflows/display/nodes/vellum/utils.py +0 -69
- vellum_ee/workflows/display/tests/test_vellum_workflow_display.py +56 -0
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py +4 -3
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +146 -26
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +11 -11
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py +7 -7
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py +33 -35
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py +4 -4
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +5 -5
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py +15 -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 +6 -3
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +6 -3
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +3 -3
- vellum_ee/workflows/display/utils/vellum.py +74 -4
- vellum_ee/workflows/display/workflows/base_workflow_display.py +6 -4
- vellum_ee/workflows/display/workflows/vellum_workflow_display.py +26 -14
- vellum_ee/workflows/display/nodes/vellum/base_node.py +0 -192
- {vellum_ai-0.13.9.dist-info → vellum_ai-0.13.11.dist-info}/LICENSE +0 -0
- {vellum_ai-0.13.9.dist-info → vellum_ai-0.13.11.dist-info}/WHEEL +0 -0
- {vellum_ai-0.13.9.dist-info → vellum_ai-0.13.11.dist-info}/entry_points.txt +0 -0
@@ -16,16 +16,34 @@ from typing import (
|
|
16
16
|
get_origin,
|
17
17
|
)
|
18
18
|
|
19
|
+
from vellum.workflows import BaseWorkflow
|
20
|
+
from vellum.workflows.constants import UNDEF
|
21
|
+
from vellum.workflows.descriptors.base import BaseDescriptor
|
22
|
+
from vellum.workflows.expressions.between import BetweenExpression
|
23
|
+
from vellum.workflows.expressions.is_nil import IsNilExpression
|
24
|
+
from vellum.workflows.expressions.is_not_nil import IsNotNilExpression
|
25
|
+
from vellum.workflows.expressions.is_not_null import IsNotNullExpression
|
26
|
+
from vellum.workflows.expressions.is_not_undefined import IsNotUndefinedExpression
|
27
|
+
from vellum.workflows.expressions.is_null import IsNullExpression
|
28
|
+
from vellum.workflows.expressions.is_undefined import IsUndefinedExpression
|
29
|
+
from vellum.workflows.expressions.not_between import NotBetweenExpression
|
19
30
|
from vellum.workflows.nodes.bases.base import BaseNode
|
31
|
+
from vellum.workflows.nodes.utils import get_wrapped_node
|
20
32
|
from vellum.workflows.ports import Port
|
21
33
|
from vellum.workflows.references import OutputReference
|
22
|
-
from vellum.workflows.
|
34
|
+
from vellum.workflows.references.execution_count import ExecutionCountReference
|
35
|
+
from vellum.workflows.references.vellum_secret import VellumSecretReference
|
36
|
+
from vellum.workflows.references.workflow_input import WorkflowInputReference
|
37
|
+
from vellum.workflows.types.core import JsonArray, JsonObject
|
23
38
|
from vellum.workflows.types.generics import NodeType
|
24
39
|
from vellum.workflows.types.utils import get_original_base
|
25
40
|
from vellum.workflows.utils.names import pascal_to_title_case
|
26
41
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
42
|
+
from vellum.workflows.utils.vellum_variables import primitive_type_to_vellum_variable_type
|
43
|
+
from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
|
27
44
|
from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay, PortDisplay, PortDisplayOverrides
|
28
|
-
from vellum_ee.workflows.display.vellum import
|
45
|
+
from vellum_ee.workflows.display.utils.vellum import convert_descriptor_to_operator, primitive_to_vellum_value
|
46
|
+
from vellum_ee.workflows.display.vellum import CodeResourceDefinition, GenericNodeDisplayData
|
29
47
|
|
30
48
|
if TYPE_CHECKING:
|
31
49
|
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
@@ -52,7 +70,97 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
|
52
70
|
_node_display_registry: Dict[Type[NodeType], Type["BaseNodeDisplay"]] = {}
|
53
71
|
|
54
72
|
def serialize(self, display_context: "WorkflowDisplayContext", **kwargs: Any) -> JsonObject:
|
55
|
-
|
73
|
+
node = self._node
|
74
|
+
node_id = self.node_id
|
75
|
+
|
76
|
+
attributes: JsonArray = []
|
77
|
+
for attribute in node:
|
78
|
+
if inspect.isclass(attribute.instance) and issubclass(attribute.instance, BaseWorkflow):
|
79
|
+
# We don't need to serialize generic node attributes containing a subworkflow
|
80
|
+
continue
|
81
|
+
|
82
|
+
id = str(uuid4_from_hash(f"{node_id}|{attribute.name}"))
|
83
|
+
attributes.append(
|
84
|
+
{
|
85
|
+
"id": id,
|
86
|
+
"name": attribute.name,
|
87
|
+
"value": self.serialize_value(display_context, cast(BaseDescriptor, attribute.instance)),
|
88
|
+
}
|
89
|
+
)
|
90
|
+
|
91
|
+
adornments = kwargs.get("adornments", None)
|
92
|
+
wrapped_node = get_wrapped_node(node)
|
93
|
+
if wrapped_node is not None:
|
94
|
+
display_class = get_node_display_class(BaseNodeDisplay, wrapped_node)
|
95
|
+
|
96
|
+
adornment: JsonObject = {
|
97
|
+
"id": str(node_id),
|
98
|
+
"label": node.__qualname__,
|
99
|
+
"base": self.get_base().dict(),
|
100
|
+
"attributes": attributes,
|
101
|
+
}
|
102
|
+
|
103
|
+
existing_adornments = adornments if adornments is not None else []
|
104
|
+
return display_class().serialize(display_context, adornments=existing_adornments + [adornment])
|
105
|
+
|
106
|
+
ports: JsonArray = []
|
107
|
+
for port in node.Ports:
|
108
|
+
id = str(self.get_node_port_display(port).id)
|
109
|
+
|
110
|
+
if port._condition_type:
|
111
|
+
ports.append(
|
112
|
+
{
|
113
|
+
"id": id,
|
114
|
+
"name": port.name,
|
115
|
+
"type": port._condition_type.value,
|
116
|
+
"expression": (
|
117
|
+
self.serialize_condition(display_context, port._condition) if port._condition else None
|
118
|
+
),
|
119
|
+
}
|
120
|
+
)
|
121
|
+
else:
|
122
|
+
ports.append(
|
123
|
+
{
|
124
|
+
"id": id,
|
125
|
+
"name": port.name,
|
126
|
+
"type": "DEFAULT",
|
127
|
+
}
|
128
|
+
)
|
129
|
+
|
130
|
+
outputs: JsonArray = []
|
131
|
+
for output in node.Outputs:
|
132
|
+
type = primitive_type_to_vellum_variable_type(output)
|
133
|
+
value = (
|
134
|
+
self.serialize_value(display_context, output.instance)
|
135
|
+
if output.instance is not None and output.instance != UNDEF
|
136
|
+
else None
|
137
|
+
)
|
138
|
+
|
139
|
+
outputs.append(
|
140
|
+
{
|
141
|
+
"id": str(uuid4_from_hash(f"{node_id}|{output.name}")),
|
142
|
+
"name": output.name,
|
143
|
+
"type": type,
|
144
|
+
"value": value,
|
145
|
+
}
|
146
|
+
)
|
147
|
+
|
148
|
+
return {
|
149
|
+
"id": str(node_id),
|
150
|
+
"label": node.__qualname__,
|
151
|
+
"type": "GENERIC",
|
152
|
+
"display_data": self._get_generic_node_display_data().dict(),
|
153
|
+
"base": self.get_base().dict(),
|
154
|
+
"definition": self.get_definition().dict(),
|
155
|
+
"trigger": {
|
156
|
+
"id": str(self.get_trigger_id()),
|
157
|
+
"merge_behavior": node.Trigger.merge_behavior.value,
|
158
|
+
},
|
159
|
+
"ports": ports,
|
160
|
+
"adornments": adornments,
|
161
|
+
"attributes": attributes,
|
162
|
+
"outputs": outputs,
|
163
|
+
}
|
56
164
|
|
57
165
|
def get_base(self) -> CodeResourceDefinition:
|
58
166
|
node = self._node
|
@@ -94,6 +202,9 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
|
94
202
|
|
95
203
|
return PortDisplay(id=port_id, node_id=self.node_id)
|
96
204
|
|
205
|
+
def get_trigger_id(self) -> UUID:
|
206
|
+
return uuid4_from_hash(f"{self.node_id}|trigger")
|
207
|
+
|
97
208
|
@classmethod
|
98
209
|
def get_from_node_display_registry(cls, node_class: Type[NodeType]) -> Type["BaseNodeDisplay"]:
|
99
210
|
return cls._node_display_registry[node_class]
|
@@ -171,6 +282,99 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
|
171
282
|
|
172
283
|
def __init_subclass__(cls, **kwargs: Any) -> None:
|
173
284
|
super().__init_subclass__(**kwargs)
|
285
|
+
if not cls._node_display_registry:
|
286
|
+
cls._node_display_registry[BaseNode] = BaseNodeDisplay
|
174
287
|
|
175
288
|
node_class = cls.infer_node_class()
|
289
|
+
if node_class is BaseNode:
|
290
|
+
return
|
291
|
+
|
176
292
|
cls._node_display_registry[node_class] = cls
|
293
|
+
|
294
|
+
def _get_generic_node_display_data(self) -> GenericNodeDisplayData:
|
295
|
+
explicit_value = self._get_explicit_node_display_attr("display_data", GenericNodeDisplayData)
|
296
|
+
return explicit_value if explicit_value else GenericNodeDisplayData()
|
297
|
+
|
298
|
+
def serialize_condition(self, display_context: "WorkflowDisplayContext", condition: BaseDescriptor) -> JsonObject:
|
299
|
+
if isinstance(
|
300
|
+
condition,
|
301
|
+
(
|
302
|
+
IsNullExpression,
|
303
|
+
IsNotNullExpression,
|
304
|
+
IsNilExpression,
|
305
|
+
IsNotNilExpression,
|
306
|
+
IsUndefinedExpression,
|
307
|
+
IsNotUndefinedExpression,
|
308
|
+
),
|
309
|
+
):
|
310
|
+
lhs = self.serialize_value(display_context, condition._expression)
|
311
|
+
return {
|
312
|
+
"type": "UNARY_EXPRESSION",
|
313
|
+
"lhs": lhs,
|
314
|
+
"operator": convert_descriptor_to_operator(condition),
|
315
|
+
}
|
316
|
+
elif isinstance(condition, (BetweenExpression, NotBetweenExpression)):
|
317
|
+
base = self.serialize_value(display_context, condition._value)
|
318
|
+
lhs = self.serialize_value(display_context, condition._start)
|
319
|
+
rhs = self.serialize_value(display_context, condition._end)
|
320
|
+
|
321
|
+
return {
|
322
|
+
"type": "TERNARY_EXPRESSION",
|
323
|
+
"base": base,
|
324
|
+
"operator": convert_descriptor_to_operator(condition),
|
325
|
+
"lhs": lhs,
|
326
|
+
"rhs": rhs,
|
327
|
+
}
|
328
|
+
else:
|
329
|
+
lhs = self.serialize_value(display_context, condition._lhs) # type: ignore[attr-defined]
|
330
|
+
rhs = self.serialize_value(display_context, condition._rhs) # type: ignore[attr-defined]
|
331
|
+
|
332
|
+
return {
|
333
|
+
"type": "BINARY_EXPRESSION",
|
334
|
+
"lhs": lhs,
|
335
|
+
"operator": convert_descriptor_to_operator(condition),
|
336
|
+
"rhs": rhs,
|
337
|
+
}
|
338
|
+
|
339
|
+
def serialize_value(self, display_context: "WorkflowDisplayContext", value: BaseDescriptor) -> JsonObject:
|
340
|
+
if isinstance(value, WorkflowInputReference):
|
341
|
+
workflow_input_display = display_context.global_workflow_input_displays[value]
|
342
|
+
return {
|
343
|
+
"type": "WORKFLOW_INPUT",
|
344
|
+
"input_variable_id": str(workflow_input_display.id),
|
345
|
+
}
|
346
|
+
|
347
|
+
if isinstance(value, OutputReference):
|
348
|
+
upstream_node, output_display = display_context.global_node_output_displays[value]
|
349
|
+
upstream_node_display = display_context.global_node_displays[upstream_node]
|
350
|
+
|
351
|
+
return {
|
352
|
+
"type": "NODE_OUTPUT",
|
353
|
+
"node_id": str(upstream_node_display.node_id),
|
354
|
+
"node_output_id": str(output_display.id),
|
355
|
+
}
|
356
|
+
|
357
|
+
if isinstance(value, VellumSecretReference):
|
358
|
+
return {
|
359
|
+
"type": "VELLUM_SECRET",
|
360
|
+
"vellum_secret_name": value.name,
|
361
|
+
}
|
362
|
+
|
363
|
+
if isinstance(value, ExecutionCountReference):
|
364
|
+
node_class_display = display_context.global_node_displays[value.node_class]
|
365
|
+
|
366
|
+
return {
|
367
|
+
"type": "EXECUTION_COUNTER",
|
368
|
+
"node_id": str(node_class_display.node_id),
|
369
|
+
}
|
370
|
+
|
371
|
+
if not isinstance(value, BaseDescriptor):
|
372
|
+
vellum_value = primitive_to_vellum_value(value)
|
373
|
+
return {
|
374
|
+
"type": "CONSTANT_VALUE",
|
375
|
+
"value": vellum_value.dict(),
|
376
|
+
}
|
377
|
+
|
378
|
+
# If it's not any of the references we know about,
|
379
|
+
# then try to serialize it as a nested value
|
380
|
+
return self.serialize_condition(display_context, value)
|
@@ -7,7 +7,7 @@ from vellum.workflows.types.generics import NodeType
|
|
7
7
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
8
8
|
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
9
9
|
from vellum_ee.workflows.display.nodes.types import PortDisplay
|
10
|
-
from vellum_ee.workflows.display.vellum import NodeDisplayData
|
10
|
+
from vellum_ee.workflows.display.vellum import NodeDisplayComment, NodeDisplayData
|
11
11
|
|
12
12
|
|
13
13
|
class BaseNodeVellumDisplay(BaseNodeDisplay[NodeType]):
|
@@ -26,6 +26,21 @@ class BaseNodeVellumDisplay(BaseNodeDisplay[NodeType]):
|
|
26
26
|
|
27
27
|
def get_display_data(self) -> NodeDisplayData:
|
28
28
|
explicit_value = self._get_explicit_node_display_attr("display_data", NodeDisplayData)
|
29
|
+
docstring = self._node.__doc__
|
30
|
+
|
31
|
+
if explicit_value and explicit_value.comment and docstring:
|
32
|
+
comment = (
|
33
|
+
NodeDisplayComment(value=docstring, expanded=explicit_value.comment.expanded)
|
34
|
+
if explicit_value.comment.expanded
|
35
|
+
else NodeDisplayComment(value=docstring)
|
36
|
+
)
|
37
|
+
return NodeDisplayData(
|
38
|
+
position=explicit_value.position,
|
39
|
+
width=explicit_value.width,
|
40
|
+
height=explicit_value.height,
|
41
|
+
comment=comment,
|
42
|
+
)
|
43
|
+
|
29
44
|
return explicit_value if explicit_value else NodeDisplayData()
|
30
45
|
|
31
46
|
def get_target_handle_id(self) -> UUID:
|
@@ -1,13 +1,15 @@
|
|
1
1
|
import types
|
2
|
-
from typing import Optional, Type
|
2
|
+
from typing import TYPE_CHECKING, Optional, Type
|
3
3
|
|
4
4
|
from vellum.workflows.types.generics import NodeType
|
5
|
-
|
5
|
+
|
6
|
+
if TYPE_CHECKING:
|
7
|
+
from vellum_ee.workflows.display.types import NodeDisplayType
|
6
8
|
|
7
9
|
|
8
10
|
def get_node_display_class(
|
9
|
-
base_class: Type[NodeDisplayType], node_class: Type[NodeType], root_node_class: Optional[Type[NodeType]] = None
|
10
|
-
) -> Type[NodeDisplayType]:
|
11
|
+
base_class: Type["NodeDisplayType"], node_class: Type[NodeType], root_node_class: Optional[Type[NodeType]] = None
|
12
|
+
) -> Type["NodeDisplayType"]:
|
11
13
|
try:
|
12
14
|
node_display_class = base_class.get_from_node_display_registry(node_class)
|
13
15
|
except KeyError:
|
@@ -1,5 +1,4 @@
|
|
1
1
|
from .api_node import BaseAPINodeDisplay
|
2
|
-
from .base_node import BaseNodeDisplay
|
3
2
|
from .code_execution_node import BaseCodeExecutionNodeDisplay
|
4
3
|
from .conditional_node import BaseConditionalNodeDisplay
|
5
4
|
from .error_node import BaseErrorNodeDisplay
|
@@ -28,7 +27,6 @@ __all__ = [
|
|
28
27
|
"BaseInlineSubworkflowNodeDisplay",
|
29
28
|
"BaseMapNodeDisplay",
|
30
29
|
"BaseMergeNodeDisplay",
|
31
|
-
"BaseNodeDisplay",
|
32
30
|
"BaseNoteNodeDisplay",
|
33
31
|
"BasePromptDeploymentNodeDisplay",
|
34
32
|
"BaseSearchNodeDisplay",
|
@@ -17,8 +17,9 @@ from vellum.workflows.nodes.displayable import ConditionalNode
|
|
17
17
|
from vellum.workflows.types.core import ConditionType, JsonObject
|
18
18
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
19
19
|
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
20
|
-
from vellum_ee.workflows.display.nodes.vellum.utils import
|
20
|
+
from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
|
21
21
|
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
22
|
+
from vellum_ee.workflows.display.utils.vellum import convert_descriptor_to_operator
|
22
23
|
from vellum_ee.workflows.display.vellum import NodeInput
|
23
24
|
|
24
25
|
_ConditionalNodeType = TypeVar("_ConditionalNodeType", bound=ConditionalNode)
|
@@ -1,9 +1,10 @@
|
|
1
1
|
from uuid import UUID
|
2
|
-
from typing import
|
2
|
+
from typing import ClassVar, Generic, Optional, TypeVar
|
3
3
|
|
4
4
|
from vellum.workflows.nodes import ErrorNode
|
5
5
|
from vellum.workflows.types.core import JsonObject
|
6
6
|
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
7
|
+
from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
|
7
8
|
from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
|
8
9
|
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
9
10
|
|
@@ -12,7 +13,7 @@ _ErrorNodeType = TypeVar("_ErrorNodeType", bound=ErrorNode)
|
|
12
13
|
|
13
14
|
class BaseErrorNodeDisplay(BaseNodeVellumDisplay[_ErrorNodeType], Generic[_ErrorNodeType]):
|
14
15
|
error_output_id: ClassVar[Optional[UUID]] = None
|
15
|
-
|
16
|
+
|
16
17
|
name: ClassVar[str] = "error-node"
|
17
18
|
|
18
19
|
def serialize(
|
@@ -21,6 +22,11 @@ class BaseErrorNodeDisplay(BaseNodeVellumDisplay[_ErrorNodeType], Generic[_Error
|
|
21
22
|
node_id = self.node_id
|
22
23
|
error_source_input_id = self.node_input_ids_by_name.get("error_source_input_id")
|
23
24
|
|
25
|
+
error_attribute = raise_if_descriptor(self._node.error)
|
26
|
+
input_values_by_name = {
|
27
|
+
"error_source_input_id": error_attribute,
|
28
|
+
}
|
29
|
+
|
24
30
|
node_inputs = [
|
25
31
|
create_node_input(
|
26
32
|
node_id=node_id,
|
@@ -29,7 +35,7 @@ class BaseErrorNodeDisplay(BaseNodeVellumDisplay[_ErrorNodeType], Generic[_Error
|
|
29
35
|
display_context=display_context,
|
30
36
|
input_id=self.node_input_ids_by_name.get(variable_name),
|
31
37
|
)
|
32
|
-
for variable_name, variable_value in
|
38
|
+
for variable_name, variable_value in input_values_by_name.items()
|
33
39
|
]
|
34
40
|
|
35
41
|
return {
|
@@ -0,0 +1,44 @@
|
|
1
|
+
from typing import Any, Dict, cast
|
2
|
+
|
3
|
+
from vellum.client.types.vellum_error import VellumError
|
4
|
+
from vellum.workflows import BaseWorkflow
|
5
|
+
from vellum.workflows.nodes.core.error_node.node import ErrorNode
|
6
|
+
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
7
|
+
from vellum_ee.workflows.display.workflows.vellum_workflow_display import VellumWorkflowDisplay
|
8
|
+
|
9
|
+
|
10
|
+
def test_error_node_display__serialize_with_vellum_error() -> None:
|
11
|
+
# GIVEN an Error Node with a VellumError
|
12
|
+
class MyNode(ErrorNode):
|
13
|
+
error = VellumError(
|
14
|
+
message="A bad thing happened",
|
15
|
+
code="USER_DEFINED_ERROR",
|
16
|
+
)
|
17
|
+
|
18
|
+
# AND a workflow referencing the two node
|
19
|
+
class MyWorkflow(BaseWorkflow):
|
20
|
+
graph = MyNode
|
21
|
+
|
22
|
+
# WHEN we serialize the workflow
|
23
|
+
workflow_display = get_workflow_display(base_display_class=VellumWorkflowDisplay, workflow_class=MyWorkflow)
|
24
|
+
serialized_workflow = cast(Dict[str, Any], workflow_display.serialize())
|
25
|
+
|
26
|
+
# THEN the correct inputs should be serialized on the node
|
27
|
+
serialized_node = next(
|
28
|
+
node for node in serialized_workflow["workflow_raw_data"]["nodes"] if node["id"] == str(MyNode.__id__)
|
29
|
+
)
|
30
|
+
assert serialized_node["inputs"][0]["value"] == {
|
31
|
+
"combinator": "OR",
|
32
|
+
"rules": [
|
33
|
+
{
|
34
|
+
"data": {
|
35
|
+
"type": "ERROR",
|
36
|
+
"value": {
|
37
|
+
"message": "A bad thing happened",
|
38
|
+
"code": "USER_DEFINED_ERROR",
|
39
|
+
},
|
40
|
+
},
|
41
|
+
"type": "CONSTANT_VALUE",
|
42
|
+
}
|
43
|
+
],
|
44
|
+
}
|
@@ -33,10 +33,16 @@ class BaseTryNodeDisplay(BaseNodeVellumDisplay[_TryNodeType], Generic[_TryNodeTy
|
|
33
33
|
)
|
34
34
|
|
35
35
|
inner_node = subworkflow.graph
|
36
|
+
elif inner_node.__bases__[0] is BaseNode:
|
37
|
+
# If the wrapped node is a generic node, we let generic node do adornment handling
|
38
|
+
class TryBaseNodeDisplay(BaseNodeDisplay[node]): # type: ignore[valid-type]
|
39
|
+
pass
|
40
|
+
|
41
|
+
return TryBaseNodeDisplay().serialize(display_context)
|
36
42
|
|
37
43
|
# We need the node display class of the underlying node because
|
38
44
|
# it contains the logic for serializing the node and potential display overrides
|
39
|
-
node_display_class = get_node_display_class(
|
45
|
+
node_display_class = get_node_display_class(BaseNodeDisplay, inner_node)
|
40
46
|
node_display = node_display_class()
|
41
47
|
|
42
48
|
serialized_node = node_display.serialize(
|
@@ -63,7 +69,7 @@ class BaseTryNodeDisplay(BaseNodeVellumDisplay[_TryNodeType], Generic[_TryNodeTy
|
|
63
69
|
if not inner_node:
|
64
70
|
return super().get_node_output_display(output)
|
65
71
|
|
66
|
-
node_display_class = get_node_display_class(
|
72
|
+
node_display_class = get_node_display_class(BaseNodeDisplay, inner_node)
|
67
73
|
node_display = node_display_class()
|
68
74
|
if output.name == "error":
|
69
75
|
return inner_node, NodeOutputDisplay(
|
@@ -2,31 +2,7 @@ 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
7
|
from vellum.workflows.references import NodeReference
|
32
8
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
@@ -124,48 +100,3 @@ def create_pointer(
|
|
124
100
|
return ConstantValuePointer(type="CONSTANT_VALUE", data=vellum_variable_value)
|
125
101
|
else:
|
126
102
|
raise ValueError(f"Pointer type {pointer_type} not supported")
|
127
|
-
|
128
|
-
|
129
|
-
def convert_descriptor_to_operator(descriptor: BaseDescriptor) -> str:
|
130
|
-
if isinstance(descriptor, EqualsExpression):
|
131
|
-
return "="
|
132
|
-
elif isinstance(descriptor, DoesNotEqualExpression):
|
133
|
-
return "!="
|
134
|
-
elif isinstance(descriptor, LessThanExpression):
|
135
|
-
return "<"
|
136
|
-
elif isinstance(descriptor, GreaterThanExpression):
|
137
|
-
return ">"
|
138
|
-
elif isinstance(descriptor, LessThanOrEqualToExpression):
|
139
|
-
return "<="
|
140
|
-
elif isinstance(descriptor, GreaterThanOrEqualToExpression):
|
141
|
-
return ">="
|
142
|
-
elif isinstance(descriptor, ContainsExpression):
|
143
|
-
return "contains"
|
144
|
-
elif isinstance(descriptor, BeginsWithExpression):
|
145
|
-
return "beginsWith"
|
146
|
-
elif isinstance(descriptor, EndsWithExpression):
|
147
|
-
return "endsWith"
|
148
|
-
elif isinstance(descriptor, DoesNotContainExpression):
|
149
|
-
return "doesNotContain"
|
150
|
-
elif isinstance(descriptor, DoesNotBeginWithExpression):
|
151
|
-
return "doesNotBeginWith"
|
152
|
-
elif isinstance(descriptor, DoesNotEndWithExpression):
|
153
|
-
return "doesNotEndWith"
|
154
|
-
elif isinstance(descriptor, (IsNullExpression, IsNilExpression, IsUndefinedExpression)):
|
155
|
-
return "null"
|
156
|
-
elif isinstance(descriptor, (IsNotNullExpression, IsNotNilExpression, IsNotUndefinedExpression)):
|
157
|
-
return "notNull"
|
158
|
-
elif isinstance(descriptor, InExpression):
|
159
|
-
return "in"
|
160
|
-
elif isinstance(descriptor, NotInExpression):
|
161
|
-
return "notIn"
|
162
|
-
elif isinstance(descriptor, BetweenExpression):
|
163
|
-
return "between"
|
164
|
-
elif isinstance(descriptor, NotBetweenExpression):
|
165
|
-
return "notBetween"
|
166
|
-
elif isinstance(descriptor, AndExpression):
|
167
|
-
return "and"
|
168
|
-
elif isinstance(descriptor, OrExpression):
|
169
|
-
return "or"
|
170
|
-
else:
|
171
|
-
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
|
@@ -8,9 +8,9 @@ from vellum.workflows.references.workflow_input import WorkflowInputReference
|
|
8
8
|
from vellum.workflows.types.core import JsonObject
|
9
9
|
from vellum.workflows.types.generics import NodeType
|
10
10
|
from vellum_ee.workflows.display.base import WorkflowInputsDisplayType
|
11
|
+
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
11
12
|
from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
|
12
13
|
from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay
|
13
|
-
from vellum_ee.workflows.display.nodes.vellum.base_node import BaseNodeDisplay
|
14
14
|
from vellum_ee.workflows.display.types import NodeDisplayType, WorkflowDisplayContext
|
15
15
|
from vellum_ee.workflows.display.vellum import NodeDisplayData, WorkflowMetaVellumDisplay
|
16
16
|
from vellum_ee.workflows.display.workflows.vellum_workflow_display import VellumWorkflowDisplay
|
@@ -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(
|
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(
|