vellum-ai 0.14.4__py3-none-any.whl → 0.14.5__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/context.py +8 -3
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +81 -1
- vellum/workflows/nodes/displayable/code_execution_node/utils.py +44 -20
- vellum/workflows/nodes/displayable/prompt_deployment_node/node.py +17 -10
- vellum/workflows/workflows/base.py +41 -24
- vellum/workflows/workflows/tests/test_base_workflow.py +47 -0
- vellum/workflows/workflows/tests/test_context.py +60 -0
- {vellum_ai-0.14.4.dist-info → vellum_ai-0.14.5.dist-info}/METADATA +1 -1
- {vellum_ai-0.14.4.dist-info → vellum_ai-0.14.5.dist-info}/RECORD +21 -19
- vellum_ee/workflows/display/nodes/vellum/__init__.py +2 -0
- vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py +39 -0
- vellum_ee/workflows/display/nodes/vellum/map_node.py +2 -2
- vellum_ee/workflows/display/nodes/vellum/retry_node.py +36 -4
- vellum_ee/workflows/display/nodes/vellum/try_node.py +43 -29
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +25 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py +14 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py +19 -1
- {vellum_ai-0.14.4.dist-info → vellum_ai-0.14.5.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.4.dist-info → vellum_ai-0.14.5.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.4.dist-info → vellum_ai-0.14.5.dist-info}/entry_points.txt +0 -0
@@ -18,7 +18,7 @@ class BaseClientWrapper:
|
|
18
18
|
headers: typing.Dict[str, str] = {
|
19
19
|
"X-Fern-Language": "Python",
|
20
20
|
"X-Fern-SDK-Name": "vellum-ai",
|
21
|
-
"X-Fern-SDK-Version": "0.14.
|
21
|
+
"X-Fern-SDK-Version": "0.14.5",
|
22
22
|
}
|
23
23
|
headers["X_API_KEY"] = self.api_key
|
24
24
|
return headers
|
vellum/workflows/context.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from contextlib import contextmanager
|
2
2
|
import threading
|
3
|
+
from uuid import UUID
|
3
4
|
from typing import Iterator, Optional, cast
|
4
5
|
|
5
6
|
from vellum.client.core import UniversalBaseModel
|
@@ -8,6 +9,7 @@ from vellum.workflows.events.types import ParentContext
|
|
8
9
|
|
9
10
|
class ExecutionContext(UniversalBaseModel):
|
10
11
|
parent_context: Optional[ParentContext] = None
|
12
|
+
trace_id: Optional[UUID] = None
|
11
13
|
|
12
14
|
|
13
15
|
_CONTEXT_KEY = "_execution_context"
|
@@ -30,11 +32,14 @@ def get_parent_context() -> ParentContext:
|
|
30
32
|
|
31
33
|
|
32
34
|
@contextmanager
|
33
|
-
def execution_context(
|
35
|
+
def execution_context(
|
36
|
+
parent_context: Optional[ParentContext] = None, trace_id: Optional[UUID] = None
|
37
|
+
) -> Iterator[None]:
|
34
38
|
"""Context manager for handling execution context."""
|
35
39
|
prev_context = get_execution_context()
|
36
|
-
|
37
|
-
|
40
|
+
set_trace_id = prev_context.trace_id or trace_id
|
41
|
+
set_parent_context = parent_context or prev_context.parent_context
|
42
|
+
set_context = ExecutionContext(parent_context=set_parent_context, trace_id=set_trace_id)
|
38
43
|
try:
|
39
44
|
set_execution_context(set_context)
|
40
45
|
yield
|
@@ -1,12 +1,16 @@
|
|
1
1
|
import pytest
|
2
2
|
import os
|
3
|
-
from typing import Any, Union
|
3
|
+
from typing import Any, List, Union
|
4
|
+
|
5
|
+
from pydantic import BaseModel
|
4
6
|
|
5
7
|
from vellum import CodeExecutorResponse, NumberVellumValue, StringInput, StringVellumValue
|
8
|
+
from vellum.client.types.chat_message import ChatMessage
|
6
9
|
from vellum.client.types.code_execution_package import CodeExecutionPackage
|
7
10
|
from vellum.client.types.code_executor_secret_input import CodeExecutorSecretInput
|
8
11
|
from vellum.client.types.function_call import FunctionCall
|
9
12
|
from vellum.client.types.number_input import NumberInput
|
13
|
+
from vellum.client.types.string_chat_message_content import StringChatMessageContent
|
10
14
|
from vellum.workflows.errors import WorkflowErrorCode
|
11
15
|
from vellum.workflows.exceptions import NodeException
|
12
16
|
from vellum.workflows.inputs.base import BaseInputs
|
@@ -610,3 +614,79 @@ def main(arg1: list[bool]) -> int:
|
|
610
614
|
|
611
615
|
# AND the error should contain the execution error details
|
612
616
|
assert outputs == {"result": 3, "log": ""}
|
617
|
+
|
618
|
+
|
619
|
+
def test_run_node__union_output_type__pydantic_children():
|
620
|
+
# GIVEN a node that is a union type with a pydantic child
|
621
|
+
class OptionOne(BaseModel):
|
622
|
+
foo: str
|
623
|
+
|
624
|
+
class OptionTwo(BaseModel):
|
625
|
+
bar: int
|
626
|
+
|
627
|
+
class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, Union[OptionOne, OptionTwo]]):
|
628
|
+
code = """\
|
629
|
+
def main():
|
630
|
+
return { "foo": "hello" }
|
631
|
+
"""
|
632
|
+
runtime = "PYTHON_3_11_6"
|
633
|
+
code_inputs = {}
|
634
|
+
|
635
|
+
# WHEN we run the node
|
636
|
+
node = ExampleCodeExecutionNode()
|
637
|
+
|
638
|
+
# THEN it should run successfully
|
639
|
+
outputs = node.run()
|
640
|
+
|
641
|
+
# AND the result should be the correct type
|
642
|
+
assert outputs == {"result": OptionOne(foo="hello"), "log": ""}
|
643
|
+
|
644
|
+
|
645
|
+
def test_run_node__union_output_type__miss():
|
646
|
+
# GIVEN a node that is a union type
|
647
|
+
class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, Union[int, float]]):
|
648
|
+
code = """\
|
649
|
+
def main():
|
650
|
+
return "hello"
|
651
|
+
"""
|
652
|
+
runtime = "PYTHON_3_11_6"
|
653
|
+
code_inputs = {}
|
654
|
+
|
655
|
+
# WHEN we run the node
|
656
|
+
node = ExampleCodeExecutionNode()
|
657
|
+
|
658
|
+
# THEN it should raise a NodeException with the execution error
|
659
|
+
with pytest.raises(NodeException) as exc_info:
|
660
|
+
node.run()
|
661
|
+
|
662
|
+
# AND the error should contain the execution error details
|
663
|
+
assert exc_info.value.message == "Expected an output of type 'int | float', but received 'str'"
|
664
|
+
|
665
|
+
|
666
|
+
def test_run_node__chat_history_output_type():
|
667
|
+
# GIVEN a node that that has a chat history return type
|
668
|
+
class ExampleCodeExecutionNode(CodeExecutionNode[BaseState, List[ChatMessage]]):
|
669
|
+
code = """\
|
670
|
+
def main():
|
671
|
+
return [
|
672
|
+
{
|
673
|
+
"role": "USER",
|
674
|
+
"content": {
|
675
|
+
"type": "STRING",
|
676
|
+
"value": "Hello, world!",
|
677
|
+
}
|
678
|
+
}
|
679
|
+
]
|
680
|
+
"""
|
681
|
+
code_inputs = {}
|
682
|
+
runtime = "PYTHON_3_11_6"
|
683
|
+
|
684
|
+
# WHEN we run the node
|
685
|
+
node = ExampleCodeExecutionNode()
|
686
|
+
outputs = node.run()
|
687
|
+
|
688
|
+
# AND the error should contain the execution error details
|
689
|
+
assert outputs == {
|
690
|
+
"result": [ChatMessage(role="USER", content=StringChatMessageContent(value="Hello, world!"))],
|
691
|
+
"log": "",
|
692
|
+
}
|
@@ -67,6 +67,49 @@ def _clean_for_dict_wrapper(obj):
|
|
67
67
|
return obj
|
68
68
|
|
69
69
|
|
70
|
+
def _get_type_name(obj: Any) -> str:
|
71
|
+
if isinstance(obj, type):
|
72
|
+
return obj.__name__
|
73
|
+
|
74
|
+
if get_origin(obj) is Union:
|
75
|
+
children = [_get_type_name(child) for child in get_args(obj)]
|
76
|
+
return " | ".join(children)
|
77
|
+
|
78
|
+
return str(obj)
|
79
|
+
|
80
|
+
|
81
|
+
def _cast_to_output_type(result: Any, output_type: Any) -> Any:
|
82
|
+
is_valid_output_type = isinstance(output_type, type)
|
83
|
+
if get_origin(output_type) is Union:
|
84
|
+
allowed_types = get_args(output_type)
|
85
|
+
for allowed_type in allowed_types:
|
86
|
+
try:
|
87
|
+
return _cast_to_output_type(result, allowed_type)
|
88
|
+
except NodeException:
|
89
|
+
continue
|
90
|
+
elif get_origin(output_type) is list:
|
91
|
+
allowed_item_type = get_args(output_type)[0]
|
92
|
+
if isinstance(result, list):
|
93
|
+
return [_cast_to_output_type(item, allowed_item_type) for item in result]
|
94
|
+
elif is_valid_output_type and issubclass(output_type, BaseModel) and not isinstance(result, output_type):
|
95
|
+
try:
|
96
|
+
return output_type.model_validate(result)
|
97
|
+
except ValidationError as e:
|
98
|
+
raise NodeException(
|
99
|
+
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
100
|
+
message=re.sub(r"\s+For further information visit [^\s]+", "", str(e)),
|
101
|
+
) from e
|
102
|
+
elif is_valid_output_type and isinstance(result, output_type):
|
103
|
+
return result
|
104
|
+
|
105
|
+
output_type_name = _get_type_name(output_type)
|
106
|
+
result_type_name = _get_type_name(type(result))
|
107
|
+
raise NodeException(
|
108
|
+
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
109
|
+
message=f"Expected an output of type '{output_type_name}', but received '{result_type_name}'",
|
110
|
+
)
|
111
|
+
|
112
|
+
|
70
113
|
def run_code_inline(
|
71
114
|
code: str,
|
72
115
|
inputs: EntityInputsInterface,
|
@@ -112,25 +155,6 @@ __arg__out = main({", ".join(run_args)})
|
|
112
155
|
result = exec_globals["__arg__out"]
|
113
156
|
|
114
157
|
if output_type != Any:
|
115
|
-
|
116
|
-
allowed_types = get_args(output_type)
|
117
|
-
if not isinstance(result, allowed_types):
|
118
|
-
raise NodeException(
|
119
|
-
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
120
|
-
message=f"Expected output to be in types {allowed_types}, but received '{type(result).__name__}'",
|
121
|
-
)
|
122
|
-
elif issubclass(output_type, BaseModel) and not isinstance(result, output_type):
|
123
|
-
try:
|
124
|
-
result = output_type.model_validate(result)
|
125
|
-
except ValidationError as e:
|
126
|
-
raise NodeException(
|
127
|
-
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
128
|
-
message=re.sub(r"\s+For further information visit [^\s]+", "", str(e)),
|
129
|
-
) from e
|
130
|
-
elif not isinstance(result, output_type):
|
131
|
-
raise NodeException(
|
132
|
-
code=WorkflowErrorCode.INVALID_OUTPUTS,
|
133
|
-
message=f"Expected an output of type '{output_type.__name__}', but received '{type(result).__name__}'",
|
134
|
-
)
|
158
|
+
result = _cast_to_output_type(result, output_type)
|
135
159
|
|
136
160
|
return logs, result
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import json
|
1
2
|
from typing import Iterator
|
2
3
|
|
3
4
|
from vellum.workflows.errors import WorkflowErrorCode
|
@@ -44,13 +45,19 @@ class PromptDeploymentNode(BasePromptDeploymentNode[StateType]):
|
|
44
45
|
code=WorkflowErrorCode.INTERNAL_ERROR,
|
45
46
|
)
|
46
47
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
48
|
+
string_outputs = []
|
49
|
+
for output in outputs:
|
50
|
+
if output.value is None:
|
51
|
+
continue
|
52
|
+
|
53
|
+
if output.type == "STRING":
|
54
|
+
string_outputs.append(output.value)
|
55
|
+
elif output.type == "JSON":
|
56
|
+
string_outputs.append(json.dumps(output.value, indent=4))
|
57
|
+
elif output.type == "FUNCTION_CALL":
|
58
|
+
string_outputs.append(output.value.model_dump_json(indent=4))
|
59
|
+
else:
|
60
|
+
string_outputs.append(output.value.message)
|
61
|
+
|
62
|
+
value = "\n".join(string_outputs)
|
63
|
+
yield BaseOutput(name="text", value=value)
|
@@ -221,6 +221,28 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
|
221
221
|
nodes.add(node)
|
222
222
|
yield node
|
223
223
|
|
224
|
+
@classmethod
|
225
|
+
def get_unused_subgraphs(cls) -> List[Graph]:
|
226
|
+
"""
|
227
|
+
Returns a list of subgraphs that are defined but not used in the graph
|
228
|
+
"""
|
229
|
+
if not hasattr(cls, "unused_graphs"):
|
230
|
+
return []
|
231
|
+
|
232
|
+
graphs = []
|
233
|
+
for item in cls.unused_graphs:
|
234
|
+
if isinstance(item, Graph):
|
235
|
+
graphs.append(item)
|
236
|
+
elif isinstance(item, set):
|
237
|
+
for subitem in item:
|
238
|
+
if isinstance(subitem, Graph):
|
239
|
+
graphs.append(subitem)
|
240
|
+
elif issubclass(subitem, BaseNode):
|
241
|
+
graphs.append(Graph.from_node(subitem))
|
242
|
+
elif issubclass(item, BaseNode):
|
243
|
+
graphs.append(Graph.from_node(item))
|
244
|
+
return graphs
|
245
|
+
|
224
246
|
@classmethod
|
225
247
|
def get_unused_nodes(cls) -> Iterator[Type[BaseNode]]:
|
226
248
|
"""
|
@@ -230,30 +252,25 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
|
230
252
|
yield from ()
|
231
253
|
else:
|
232
254
|
nodes = set()
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
elif issubclass(item, BaseNode):
|
253
|
-
# Item is a node
|
254
|
-
if item not in nodes:
|
255
|
-
nodes.add(item)
|
256
|
-
yield item
|
255
|
+
subgraphs = cls.get_unused_subgraphs()
|
256
|
+
for subgraph in subgraphs:
|
257
|
+
for node in subgraph.nodes:
|
258
|
+
if node not in nodes:
|
259
|
+
nodes.add(node)
|
260
|
+
yield node
|
261
|
+
|
262
|
+
@classmethod
|
263
|
+
def get_unused_edges(cls) -> Iterator[Edge]:
|
264
|
+
"""
|
265
|
+
Returns an iterator over edges that are defined but not used in the graph.
|
266
|
+
"""
|
267
|
+
edges = set()
|
268
|
+
subgraphs = cls.get_unused_subgraphs()
|
269
|
+
for subgraph in subgraphs:
|
270
|
+
for edge in subgraph.edges:
|
271
|
+
if edge not in edges:
|
272
|
+
edges.add(edge)
|
273
|
+
yield edge
|
257
274
|
|
258
275
|
@classmethod
|
259
276
|
def get_entrypoints(cls) -> Iterable[Type[BaseNode]]:
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import pytest
|
2
2
|
|
3
|
+
from vellum.workflows.edges.edge import Edge
|
3
4
|
from vellum.workflows.inputs.base import BaseInputs
|
4
5
|
from vellum.workflows.nodes.bases.base import BaseNode
|
5
6
|
from vellum.workflows.nodes.core.inline_subworkflow_node.node import InlineSubworkflowNode
|
@@ -166,3 +167,49 @@ def test_workflow__node_in_both_graph_and_unused():
|
|
166
167
|
|
167
168
|
# THEN it should raise an error
|
168
169
|
assert "Node(s) NodeA cannot appear in both graph and unused_graphs" in str(exc_info.value)
|
170
|
+
|
171
|
+
|
172
|
+
def test_workflow__get_unused_edges():
|
173
|
+
"""
|
174
|
+
Test that get_unused_edges correctly identifies edges that are defined but not used in the workflow graph.
|
175
|
+
"""
|
176
|
+
|
177
|
+
class NodeA(BaseNode):
|
178
|
+
pass
|
179
|
+
|
180
|
+
class NodeB(BaseNode):
|
181
|
+
pass
|
182
|
+
|
183
|
+
class NodeC(BaseNode):
|
184
|
+
pass
|
185
|
+
|
186
|
+
class NodeD(BaseNode):
|
187
|
+
pass
|
188
|
+
|
189
|
+
class NodeE(BaseNode):
|
190
|
+
pass
|
191
|
+
|
192
|
+
class NodeF(BaseNode):
|
193
|
+
pass
|
194
|
+
|
195
|
+
class NodeG(BaseNode):
|
196
|
+
pass
|
197
|
+
|
198
|
+
class TestWorkflow(BaseWorkflow[BaseInputs, BaseState]):
|
199
|
+
graph = NodeA >> NodeB
|
200
|
+
unused_graphs = {NodeC >> {NodeD >> NodeE, NodeF} >> NodeG}
|
201
|
+
|
202
|
+
edge_c_to_d = Edge(from_port=NodeC.Ports.default, to_node=NodeD)
|
203
|
+
edge_c_to_f = Edge(from_port=NodeC.Ports.default, to_node=NodeF)
|
204
|
+
edge_d_to_e = Edge(from_port=NodeD.Ports.default, to_node=NodeE)
|
205
|
+
edge_e_to_g = Edge(from_port=NodeE.Ports.default, to_node=NodeG)
|
206
|
+
edge_f_to_g = Edge(from_port=NodeF.Ports.default, to_node=NodeG)
|
207
|
+
|
208
|
+
# Collect unused edges
|
209
|
+
unused_edges = set(TestWorkflow.get_unused_edges())
|
210
|
+
|
211
|
+
# Expected unused edges
|
212
|
+
expected_unused_edges = {edge_c_to_d, edge_c_to_f, edge_d_to_e, edge_e_to_g, edge_f_to_g}
|
213
|
+
|
214
|
+
# TEST that unused edges are correctly identified
|
215
|
+
assert unused_edges == expected_unused_edges, f"Expected {expected_unused_edges}, but got {unused_edges}"
|
@@ -0,0 +1,60 @@
|
|
1
|
+
from uuid import UUID, uuid4
|
2
|
+
|
3
|
+
from vellum.workflows import BaseWorkflow
|
4
|
+
from vellum.workflows.context import execution_context, get_execution_context
|
5
|
+
from vellum.workflows.events.types import NodeParentContext, WorkflowParentContext
|
6
|
+
from vellum.workflows.inputs import BaseInputs
|
7
|
+
from vellum.workflows.nodes import BaseNode
|
8
|
+
from vellum.workflows.references import VellumSecretReference
|
9
|
+
from vellum.workflows.state import BaseState
|
10
|
+
|
11
|
+
|
12
|
+
class MockInputs(BaseInputs):
|
13
|
+
foo: str
|
14
|
+
|
15
|
+
|
16
|
+
class MockNode(BaseNode):
|
17
|
+
node_foo = MockInputs.foo
|
18
|
+
node_secret = VellumSecretReference("secret")
|
19
|
+
|
20
|
+
class Outputs(BaseNode.Outputs):
|
21
|
+
example: str
|
22
|
+
|
23
|
+
|
24
|
+
class MockWorkflow(BaseWorkflow[MockInputs, BaseState]):
|
25
|
+
graph = MockNode
|
26
|
+
|
27
|
+
|
28
|
+
def test_context_trace_and_parent():
|
29
|
+
trace_id = uuid4()
|
30
|
+
parent_context = NodeParentContext(
|
31
|
+
node_definition=MockNode,
|
32
|
+
span_id=UUID("123e4567-e89b-12d3-a456-426614174000"),
|
33
|
+
parent=WorkflowParentContext(
|
34
|
+
workflow_definition=MockWorkflow,
|
35
|
+
span_id=UUID("123e4567-e89b-12d3-a456-426614174000"),
|
36
|
+
),
|
37
|
+
)
|
38
|
+
second_parent_context = WorkflowParentContext(
|
39
|
+
workflow_definition=MockWorkflow, span_id=uuid4(), parent=parent_context
|
40
|
+
)
|
41
|
+
# When using execution context , if we set trace id within
|
42
|
+
with execution_context(parent_context=parent_context, trace_id=trace_id):
|
43
|
+
test = get_execution_context()
|
44
|
+
assert test.trace_id == trace_id
|
45
|
+
assert test.parent_context == parent_context
|
46
|
+
with execution_context(parent_context=second_parent_context):
|
47
|
+
test1 = get_execution_context()
|
48
|
+
assert test1.trace_id == trace_id
|
49
|
+
assert test1.parent_context == second_parent_context
|
50
|
+
# then we can assume trace id will not change
|
51
|
+
with execution_context(trace_id=uuid4()):
|
52
|
+
test3 = get_execution_context()
|
53
|
+
assert test3.trace_id == trace_id
|
54
|
+
with execution_context(parent_context=parent_context, trace_id=uuid4()):
|
55
|
+
test3 = get_execution_context()
|
56
|
+
assert test3.trace_id == trace_id
|
57
|
+
# and if we have a new context, the trace will differ
|
58
|
+
with execution_context(parent_context=parent_context, trace_id=uuid4()):
|
59
|
+
test = get_execution_context()
|
60
|
+
assert test.trace_id != trace_id
|
@@ -28,8 +28,9 @@ vellum_ee/workflows/display/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5J
|
|
28
28
|
vellum_ee/workflows/display/nodes/tests/test_base_node_display.py,sha256=QqR3Ly0RNrXwOeLdW5nERDFt0gRPf76n1bPES6o5UN4,1093
|
29
29
|
vellum_ee/workflows/display/nodes/types.py,sha256=St1BB6no528OyELGiyRabWao0GGw6mLhstQAvEACbGk,247
|
30
30
|
vellum_ee/workflows/display/nodes/utils.py,sha256=sloya5TpXsnot1HURc9L51INwflRqUzHxRVnCS9Cd-4,973
|
31
|
-
vellum_ee/workflows/display/nodes/vellum/__init__.py,sha256=
|
31
|
+
vellum_ee/workflows/display/nodes/vellum/__init__.py,sha256=nUIgH2s0-7IbQRNrBhLPyRNe8YIrx3Yo9HeeW-aXXFk,1668
|
32
32
|
vellum_ee/workflows/display/nodes/vellum/api_node.py,sha256=hoV-cUtS6H9kmRQXHd2py95GRWI_dAnnaPwvlNBkDOQ,8571
|
33
|
+
vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py,sha256=UlfX8ifleaAaRCmGlj6XNubEMJnHOgCXSBRk6LAZw38,1995
|
33
34
|
vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=z00Z3L0d4PsUQo4S8FRDTtOFLtjdi17TJbatNVF4nM8,4288
|
34
35
|
vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=ybLIa4uclqVIy3VAQvI1ivg2tnK5Ug_1R5a69DFqL7E,11104
|
35
36
|
vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=I1Jkp2htRINJATtv1e-zs9BrReFX842djpiVgBPHDYg,2186
|
@@ -37,11 +38,11 @@ vellum_ee/workflows/display/nodes/vellum/final_output_node.py,sha256=p-PvlnxpBQ7
|
|
37
38
|
vellum_ee/workflows/display/nodes/vellum/guardrail_node.py,sha256=aYZSJTxknU4LMiQdWk9LcK6CkhdozeDEMiRxfAyUNEc,2202
|
38
39
|
vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=aNZhjw5CwpUO8IcLJ2nhYrzn96RJ3FWeJXdfDizuPzw,8491
|
39
40
|
vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py,sha256=MU9I8CB1X1TgL1aa1eT6DHWwNJ-2v79t74xl0oy-fBo,5510
|
40
|
-
vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=
|
41
|
+
vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=8CPnn06HIBxBOiECevUffeVmQmCpec6WtPQnNl9gj9Y,3748
|
41
42
|
vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=HkNMgdQELiON42jdO-xDLmqrEKdGx1RVqrz2DXNTLS8,3239
|
42
43
|
vellum_ee/workflows/display/nodes/vellum/note_node.py,sha256=TMb8txILu2uWjzoxaghjgjlzeBAgzn4vkP_8zSh2qoE,1151
|
43
44
|
vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=LFjLUrH6sJ4czPnExdRqFr0PB_yKBMLXLvK5GAzIAgc,3273
|
44
|
-
vellum_ee/workflows/display/nodes/vellum/retry_node.py,sha256=
|
45
|
+
vellum_ee/workflows/display/nodes/vellum/retry_node.py,sha256=hVqTtuHjlw_cYJ3ydNAvUHfGEoQi5YocVONZUo4i_Gs,1717
|
45
46
|
vellum_ee/workflows/display/nodes/vellum/search_node.py,sha256=TxcAGZDl_hvJ7Y1hUi9YVEVrj9Ie0hKkASdpfRL4_cs,9227
|
46
47
|
vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py,sha256=62baAElKoRKIoba0lLhnrXGWWx96B73VxKGxh7BaIxc,2612
|
47
48
|
vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=JVIMPR3WpveOCWZubHKZkE04mavnTdb_9QY_r3XliRg,3424
|
@@ -50,20 +51,20 @@ vellum_ee/workflows/display/nodes/vellum/tests/test_error_node.py,sha256=ulrpoYU
|
|
50
51
|
vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py,sha256=bg9INsXiWfyK047u8TD1oEOFYrqDq8GC7Hvgz69n7BE,1988
|
51
52
|
vellum_ee/workflows/display/nodes/vellum/tests/test_try_node.py,sha256=mtzB8LJlFCHVFM4H5AanLp29gQfaVmnN4A4iaRGJHoI,2427
|
52
53
|
vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py,sha256=3uT7Gbc0f_mQ3u8uZuCWd0mJ4GtWbz2gbUMySYaVlNE,3774
|
53
|
-
vellum_ee/workflows/display/nodes/vellum/try_node.py,sha256=
|
54
|
+
vellum_ee/workflows/display/nodes/vellum/try_node.py,sha256=HBfGz4yt9GlmMW9JxzaCacPnHBDNIeXE8Jhqr9DqLLw,6191
|
54
55
|
vellum_ee/workflows/display/nodes/vellum/utils.py,sha256=OEGHjQSbuUgJexXI1aubYW33z2F_YdkhQ8REahfz864,4320
|
55
56
|
vellum_ee/workflows/display/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
56
57
|
vellum_ee/workflows/display/tests/test_vellum_workflow_display.py,sha256=1EEvkKQRfOKlnpLxE9-hKSsVLLaelM39LY7007LM5dg,4983
|
57
58
|
vellum_ee/workflows/display/tests/workflow_serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
58
59
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
59
60
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py,sha256=A1-tIpC5KIKG9JA_rkd1nLS8zUG3Kb4QiVdvb3boFxE,2509
|
60
|
-
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py,sha256=
|
61
|
+
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py,sha256=7Fc-TtPw7hz_pvQ-TWz3G8Vy9h2AztukpyDK0p7REGU,9071
|
61
62
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py,sha256=1cszL6N6FNGVm61MOa7AEiHnF0QjZWqDQuPOp4yiG94,18277
|
62
63
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py,sha256=-12ZkZb3f5gyoNASV2yeQtMo5HmNsVEo8nXwL6IC-I8,6261
|
63
64
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py,sha256=6th6kCwzql6lddjkTQx4Jbvvs4ChqtJwctW-B4QuBhI,37352
|
64
65
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py,sha256=EbVgg_3_ipTt3MOop4RARX0fmNjwqZtkhIXzx9nGw7Y,4487
|
65
66
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py,sha256=IRazH2QR6F8RGqNemEnHueyj5DtEa6rFTYhT16S4jI8,15917
|
66
|
-
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py,sha256=
|
67
|
+
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py,sha256=V__y7uu-dy6TJjPeu4UDvaoO2yYwBRbPiW9uJdzWRx4,29828
|
67
68
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py,sha256=R8DW1DUb0DOSLtnF2E1HaCTmtpG-ski0LfcM2WeLVNo,47672
|
68
69
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_default_state_serialization.py,sha256=z9FSufJeV-003R87wi_Lx4mZewdeeOeTPCGNc9vg8vY,8819
|
69
70
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py,sha256=wgedEa2IVP2ssH_FLghoEmLgpJR41AY-iNIw1SESeqA,6106
|
@@ -78,7 +79,7 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_
|
|
78
79
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py,sha256=BzFNl9ECeGh0krm-CUjbBQQq0g7krANsp0Sh-j5dAkc,11322
|
79
80
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_node_serialization.py,sha256=xXtW915v9yxxKlyu5ObzKHyJYMvobvev3ermX61SGx4,7818
|
80
81
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py,sha256=NdhE3lm7RMQ8DqkraPSq24IbOxNla9unbs4tsMWRzm4,3781
|
81
|
-
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py,sha256=
|
82
|
+
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py,sha256=eD5686C9nWC5s6t08vbAnm9qf9t53gYQM-E1FwAa75c,3035
|
82
83
|
vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py,sha256=huKAOeMJ2MKmp6XtbvMJTUadqynoV40Ypoz9jsBEBEQ,7431
|
83
84
|
vellum_ee/workflows/display/types.py,sha256=xDC1zy4rWKNqDtSr-h6MQfWnJ6scZ_Sadxp4t8Q3PY4,2897
|
84
85
|
vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -119,7 +120,7 @@ vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
|
|
119
120
|
vellum/client/__init__.py,sha256=j6zi0NZ4BMC6JrwckvzMWuG5x8KoOvO4KqsLhvVCa68,117624
|
120
121
|
vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
|
121
122
|
vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
|
122
|
-
vellum/client/core/client_wrapper.py,sha256=
|
123
|
+
vellum/client/core/client_wrapper.py,sha256=XW9zEBd9XlCTI0wEOgZ6LI4dCsGwvpW4NO7KogWDgao,1868
|
123
124
|
vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
|
124
125
|
vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
|
125
126
|
vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
|
@@ -1271,7 +1272,7 @@ vellum/version.py,sha256=jq-1PlAYxN9AXuaZqbYk9ak27SgE2lw9Ia5gx1b1gVI,76
|
|
1271
1272
|
vellum/workflows/README.md,sha256=MLNm-ihc0ao6I8gwwOhXQQBf0jOf-EsA9C519ALYI1o,3610
|
1272
1273
|
vellum/workflows/__init__.py,sha256=CssPsbNvN6rDhoLuqpEv7MMKGa51vE6dvAh6U31Pcio,71
|
1273
1274
|
vellum/workflows/constants.py,sha256=2yg4_uo5gpqViy3ZLSwfC8qTybleYCtOnhA4Rj6bacM,1310
|
1274
|
-
vellum/workflows/context.py,sha256=
|
1275
|
+
vellum/workflows/context.py,sha256=DwSf8lO9NHABiqOoD3exgrjUoRuNsKtutaL5TgRbD-A,1441
|
1275
1276
|
vellum/workflows/descriptors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1276
1277
|
vellum/workflows/descriptors/base.py,sha256=gSib3vJpcI_UC8y8jhdp-hOK3_TGTF-SuwdhxF6x5iQ,14332
|
1277
1278
|
vellum/workflows/descriptors/exceptions.py,sha256=gUy4UD9JFUKSeQnQpeuDSLiRqWjWiIsxLahB7p_q3JY,54
|
@@ -1380,8 +1381,8 @@ vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=_yrQn_uLwy
|
|
1380
1381
|
vellum/workflows/nodes/displayable/code_execution_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1381
1382
|
vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1382
1383
|
vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/main.py,sha256=5QsbmkzSlSbcbWTG_JmIqcP-JNJzOPTKxGzdHos19W4,79
|
1383
|
-
vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py,sha256=
|
1384
|
-
vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=
|
1384
|
+
vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py,sha256=4ADDKsObtUs0PhcWyAjWyQcAF7PGUYE0CxjYp8d-1NM,20637
|
1385
|
+
vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=BQraIN4I3DCzXLEuBlRYCyp7ote7hQmnnKHu4jFHCCA,5174
|
1385
1386
|
vellum/workflows/nodes/displayable/conditional_node/__init__.py,sha256=AS_EIqFdU1F9t8aLmbZU-rLh9ry6LCJ0uj0D8F0L5Uw,72
|
1386
1387
|
vellum/workflows/nodes/displayable/conditional_node/node.py,sha256=Qjfl33gZ3JEgxBA1EgzSUebboGvsARthIxxcQyvx5Gg,1152
|
1387
1388
|
vellum/workflows/nodes/displayable/final_output_node/__init__.py,sha256=G7VXM4OWpubvSJtVkGmMNeqgb9GkM7qZT838eL18XU4,72
|
@@ -1397,7 +1398,7 @@ vellum/workflows/nodes/displayable/merge_node/node.py,sha256=nZtGGVAvY4fvGg8vwV6
|
|
1397
1398
|
vellum/workflows/nodes/displayable/note_node/__init__.py,sha256=KWA3P4fyYJ-fOTky8qNGlcOotQ-HeHJ9AjZt6mRQmCE,58
|
1398
1399
|
vellum/workflows/nodes/displayable/note_node/node.py,sha256=sIN1VBQ7zeT3GhN0kupXbFfdpvgedWV79k4woJNp5IQ,394
|
1399
1400
|
vellum/workflows/nodes/displayable/prompt_deployment_node/__init__.py,sha256=krX1Hds-TSVYZsx0wJFX4wsAKkEFYOX1ifwRGiIM-EA,82
|
1400
|
-
vellum/workflows/nodes/displayable/prompt_deployment_node/node.py,sha256=
|
1401
|
+
vellum/workflows/nodes/displayable/prompt_deployment_node/node.py,sha256=pb-KbrnfTRL7mmNtVAMmiCiys8raXkl5Od7sIu682xU,2707
|
1401
1402
|
vellum/workflows/nodes/displayable/prompt_deployment_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1402
1403
|
vellum/workflows/nodes/displayable/prompt_deployment_node/tests/test_node.py,sha256=ymEwMwrwRuQGyvkTnqeRZvfK7dhnf-kmRJTuwlycNjI,3939
|
1403
1404
|
vellum/workflows/nodes/displayable/search_node/__init__.py,sha256=hpBpvbrDYf43DElRZFLzieSn8weXiwNiiNOJurERQbs,62
|
@@ -1470,12 +1471,13 @@ vellum/workflows/utils/uuids.py,sha256=DFzPv9RCvsKhvdTEIQyfSek2A31D6S_QcmeLPbgrg
|
|
1470
1471
|
vellum/workflows/utils/vellum_variables.py,sha256=fC2aSLvlS31D15dOWu43LBRR0QsgUKNXBiCUvvaLXSs,3231
|
1471
1472
|
vellum/workflows/vellum_client.py,sha256=ODrq_TSl-drX2aezXegf7pizpWDVJuTXH-j6528t75s,683
|
1472
1473
|
vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
|
1473
|
-
vellum/workflows/workflows/base.py,sha256=
|
1474
|
+
vellum/workflows/workflows/base.py,sha256=MNwWXt0xosrGAjbkQ_lhmBPlHzsJVX1Cr2qy7guFRK8,22446
|
1474
1475
|
vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
|
1475
1476
|
vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1476
|
-
vellum/workflows/workflows/tests/test_base_workflow.py,sha256=
|
1477
|
-
|
1478
|
-
vellum_ai-0.14.
|
1479
|
-
vellum_ai-0.14.
|
1480
|
-
vellum_ai-0.14.
|
1481
|
-
vellum_ai-0.14.
|
1477
|
+
vellum/workflows/workflows/tests/test_base_workflow.py,sha256=DTfXqHUF0SJELTVSA9PCOWHVoAylqpDNFZTkQM44CdU,6215
|
1478
|
+
vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
|
1479
|
+
vellum_ai-0.14.5.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
|
1480
|
+
vellum_ai-0.14.5.dist-info/METADATA,sha256=_53R_GRMep--gP15bdsMgW6SYTC9AD_W9yvPkQp94w0,5407
|
1481
|
+
vellum_ai-0.14.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
1482
|
+
vellum_ai-0.14.5.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
|
1483
|
+
vellum_ai-0.14.5.dist-info/RECORD,,
|
@@ -1,4 +1,5 @@
|
|
1
1
|
from .api_node import BaseAPINodeDisplay
|
2
|
+
from .base_adornment_node import BaseAdornmentNodeDisplay
|
2
3
|
from .code_execution_node import BaseCodeExecutionNodeDisplay
|
3
4
|
from .conditional_node import BaseConditionalNodeDisplay
|
4
5
|
from .error_node import BaseErrorNodeDisplay
|
@@ -18,6 +19,7 @@ from .try_node import BaseTryNodeDisplay
|
|
18
19
|
|
19
20
|
# All node display classes must be imported here to be registered in BaseNodeDisplay's node display registry
|
20
21
|
__all__ = [
|
22
|
+
"BaseAdornmentNodeDisplay",
|
21
23
|
"BaseAPINodeDisplay",
|
22
24
|
"BaseCodeExecutionNodeDisplay",
|
23
25
|
"BaseConditionalNodeDisplay",
|
@@ -0,0 +1,39 @@
|
|
1
|
+
from uuid import UUID
|
2
|
+
from typing import Any, Callable, Generic, Optional, TypeVar, cast
|
3
|
+
|
4
|
+
from vellum.workflows.nodes.bases.base_adornment_node import BaseAdornmentNode
|
5
|
+
from vellum.workflows.nodes.utils import get_wrapped_node
|
6
|
+
from vellum.workflows.types.core import JsonArray, JsonObject
|
7
|
+
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
8
|
+
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
9
|
+
from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
|
10
|
+
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
11
|
+
|
12
|
+
_BaseAdornmentNodeType = TypeVar("_BaseAdornmentNodeType", bound=BaseAdornmentNode)
|
13
|
+
|
14
|
+
|
15
|
+
class BaseAdornmentNodeDisplay(BaseNodeVellumDisplay[_BaseAdornmentNodeType], Generic[_BaseAdornmentNodeType]):
|
16
|
+
def serialize(
|
17
|
+
self,
|
18
|
+
display_context: "WorkflowDisplayContext",
|
19
|
+
**kwargs: Any,
|
20
|
+
) -> dict:
|
21
|
+
node = self._node
|
22
|
+
adornment = cast(Optional[JsonObject], kwargs.get("adornment"))
|
23
|
+
get_additional_kwargs = cast(Optional[Callable[[UUID], dict]], kwargs.get("get_additional_kwargs"))
|
24
|
+
|
25
|
+
wrapped_node = get_wrapped_node(node)
|
26
|
+
if not wrapped_node:
|
27
|
+
raise NotImplementedError(
|
28
|
+
"Unable to serialize standalone adornment nodes. Please use adornment nodes as a decorator."
|
29
|
+
)
|
30
|
+
|
31
|
+
wrapped_node_display_class = get_node_display_class(BaseNodeDisplay, wrapped_node)
|
32
|
+
wrapped_node_display = wrapped_node_display_class()
|
33
|
+
additional_kwargs = get_additional_kwargs(wrapped_node_display.node_id) if get_additional_kwargs else {}
|
34
|
+
serialized_wrapped_node = wrapped_node_display.serialize(display_context, **kwargs, **additional_kwargs)
|
35
|
+
|
36
|
+
adornments = cast(JsonArray, serialized_wrapped_node.get("adornments")) or []
|
37
|
+
serialized_wrapped_node["adornments"] = adornments + [adornment] if adornment else adornments
|
38
|
+
|
39
|
+
return serialized_wrapped_node
|
@@ -3,8 +3,8 @@ from typing import Dict, Generic, List, Optional, TypeVar, cast
|
|
3
3
|
|
4
4
|
from vellum.workflows.nodes import MapNode
|
5
5
|
from vellum.workflows.types.core import JsonObject
|
6
|
-
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
7
6
|
from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
|
7
|
+
from vellum_ee.workflows.display.nodes.vellum.base_adornment_node import BaseAdornmentNodeDisplay
|
8
8
|
from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
|
9
9
|
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
10
10
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
@@ -12,7 +12,7 @@ from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class imp
|
|
12
12
|
_MapNodeType = TypeVar("_MapNodeType", bound=MapNode)
|
13
13
|
|
14
14
|
|
15
|
-
class BaseMapNodeDisplay(
|
15
|
+
class BaseMapNodeDisplay(BaseAdornmentNodeDisplay[_MapNodeType], Generic[_MapNodeType]):
|
16
16
|
def serialize(
|
17
17
|
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
|
18
18
|
) -> JsonObject:
|
@@ -1,10 +1,42 @@
|
|
1
|
-
|
1
|
+
import inspect
|
2
|
+
from typing import Any, Generic, TypeVar, cast
|
2
3
|
|
4
|
+
from vellum.workflows.descriptors.base import BaseDescriptor
|
3
5
|
from vellum.workflows.nodes.core.retry_node.node import RetryNode
|
4
|
-
from
|
6
|
+
from vellum.workflows.types.core import JsonArray, JsonObject
|
7
|
+
from vellum.workflows.utils.uuids import uuid4_from_hash
|
8
|
+
from vellum.workflows.workflows.base import BaseWorkflow
|
9
|
+
from vellum_ee.workflows.display.nodes.vellum.base_adornment_node import BaseAdornmentNodeDisplay
|
10
|
+
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
5
11
|
|
6
12
|
_RetryNodeType = TypeVar("_RetryNodeType", bound=RetryNode)
|
7
13
|
|
8
14
|
|
9
|
-
class BaseRetryNodeDisplay(
|
10
|
-
|
15
|
+
class BaseRetryNodeDisplay(BaseAdornmentNodeDisplay[_RetryNodeType], Generic[_RetryNodeType]):
|
16
|
+
def serialize(self, display_context: WorkflowDisplayContext, **kwargs: Any) -> JsonObject:
|
17
|
+
node = self._node
|
18
|
+
node_id = self.node_id
|
19
|
+
|
20
|
+
attributes: JsonArray = []
|
21
|
+
for attribute in node:
|
22
|
+
if inspect.isclass(attribute.instance) and issubclass(attribute.instance, BaseWorkflow):
|
23
|
+
# We don't need to serialize attributes that are workflows
|
24
|
+
continue
|
25
|
+
|
26
|
+
id = str(uuid4_from_hash(f"{node_id}|{attribute.name}"))
|
27
|
+
attributes.append(
|
28
|
+
{
|
29
|
+
"id": id,
|
30
|
+
"name": attribute.name,
|
31
|
+
"value": self.serialize_value(display_context, cast(BaseDescriptor, attribute.instance)),
|
32
|
+
}
|
33
|
+
)
|
34
|
+
|
35
|
+
adornment: JsonObject = {
|
36
|
+
"id": str(node_id),
|
37
|
+
"label": node.__qualname__,
|
38
|
+
"base": self.get_base().dict(),
|
39
|
+
"attributes": attributes,
|
40
|
+
}
|
41
|
+
|
42
|
+
return super().serialize(display_context, adornment=adornment)
|
@@ -1,55 +1,69 @@
|
|
1
|
+
import inspect
|
1
2
|
from uuid import UUID
|
2
3
|
from typing import Any, Callable, ClassVar, Generic, Optional, Tuple, Type, TypeVar, cast
|
3
4
|
|
5
|
+
from vellum.workflows.descriptors.base import BaseDescriptor
|
4
6
|
from vellum.workflows.nodes.bases.base import BaseNode
|
5
7
|
from vellum.workflows.nodes.core.try_node.node import TryNode
|
6
|
-
from vellum.workflows.nodes.utils import ADORNMENT_MODULE_NAME
|
8
|
+
from vellum.workflows.nodes.utils import ADORNMENT_MODULE_NAME
|
7
9
|
from vellum.workflows.references.output import OutputReference
|
8
|
-
from vellum.workflows.types.core import JsonObject
|
10
|
+
from vellum.workflows.types.core import JsonArray, JsonObject
|
9
11
|
from vellum.workflows.types.utils import get_original_base
|
10
12
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
13
|
+
from vellum.workflows.workflows.base import BaseWorkflow
|
11
14
|
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
12
|
-
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
13
15
|
from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
|
14
16
|
from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay
|
15
|
-
from vellum_ee.workflows.display.nodes.
|
17
|
+
from vellum_ee.workflows.display.nodes.vellum.base_adornment_node import BaseAdornmentNodeDisplay
|
16
18
|
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
17
19
|
|
18
20
|
_TryNodeType = TypeVar("_TryNodeType", bound=TryNode)
|
19
21
|
|
20
22
|
|
21
|
-
class BaseTryNodeDisplay(
|
23
|
+
class BaseTryNodeDisplay(BaseAdornmentNodeDisplay[_TryNodeType], Generic[_TryNodeType]):
|
22
24
|
error_output_id: ClassVar[Optional[UUID]] = None
|
23
25
|
|
24
|
-
def serialize(self, display_context: WorkflowDisplayContext, **kwargs: Any) -> JsonObject:
|
26
|
+
def serialize(self, display_context: "WorkflowDisplayContext", **kwargs: Any) -> JsonObject:
|
25
27
|
node = self._node
|
28
|
+
node_id = self.node_id
|
29
|
+
|
30
|
+
# We let the inner node serialize first and then append to it
|
31
|
+
attributes: JsonArray = []
|
32
|
+
for attribute in node:
|
33
|
+
if inspect.isclass(attribute.instance) and issubclass(attribute.instance, BaseWorkflow):
|
34
|
+
# We don't need to serialize attributes that are workflows
|
35
|
+
continue
|
36
|
+
|
37
|
+
id = str(uuid4_from_hash(f"{node_id}|{attribute.name}"))
|
38
|
+
attributes.append(
|
39
|
+
{
|
40
|
+
"id": id,
|
41
|
+
"name": attribute.name,
|
42
|
+
"value": self.serialize_value(display_context, cast(BaseDescriptor, attribute.instance)),
|
43
|
+
}
|
44
|
+
)
|
26
45
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
class TryBaseNodeDisplay(BaseNodeDisplay[node]): # type: ignore[valid-type]
|
39
|
-
pass
|
40
|
-
|
41
|
-
return TryBaseNodeDisplay().serialize(display_context)
|
42
|
-
|
43
|
-
# We need the node display class of the underlying node because
|
44
|
-
# it contains the logic for serializing the node and potential display overrides
|
45
|
-
node_display_class = get_node_display_class(BaseNodeDisplay, inner_node)
|
46
|
-
node_display = node_display_class()
|
47
|
-
|
48
|
-
serialized_node = node_display.serialize(
|
46
|
+
adornment: JsonObject = {
|
47
|
+
"id": str(node_id),
|
48
|
+
"label": node.__qualname__,
|
49
|
+
"base": self.get_base().dict(),
|
50
|
+
"attributes": attributes,
|
51
|
+
}
|
52
|
+
|
53
|
+
# We need the inner node's ID to generate the error output ID
|
54
|
+
# Long term we want to hoist error_output_id append from inner node displays to this display,
|
55
|
+
# But that's a lot of work and this allows us to punt a little longer
|
56
|
+
serialized_node = super().serialize(
|
49
57
|
display_context,
|
50
|
-
|
58
|
+
adornment=adornment,
|
59
|
+
get_additional_kwargs=lambda node_id: {
|
60
|
+
"error_output_id": self.error_output_id or uuid4_from_hash(f"{node_id}|error_output_id")
|
61
|
+
},
|
51
62
|
)
|
52
63
|
|
64
|
+
if serialized_node["type"] == "GENERIC":
|
65
|
+
return serialized_node
|
66
|
+
|
53
67
|
serialized_node_definition = serialized_node.get("definition")
|
54
68
|
if isinstance(serialized_node_definition, dict):
|
55
69
|
serialized_node_definition_module = serialized_node_definition.get("module")
|
@@ -1,3 +1,4 @@
|
|
1
|
+
import pytest
|
1
2
|
from uuid import uuid4
|
2
3
|
|
3
4
|
from deepdiff import DeepDiff
|
@@ -11,6 +12,7 @@ from vellum.workflows.workflows.base import BaseWorkflow
|
|
11
12
|
from vellum_ee.workflows.display.base import WorkflowInputsDisplay
|
12
13
|
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
13
14
|
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
15
|
+
from vellum_ee.workflows.display.nodes.vellum.retry_node import BaseRetryNodeDisplay
|
14
16
|
from vellum_ee.workflows.display.nodes.vellum.try_node import BaseTryNodeDisplay
|
15
17
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
16
18
|
from vellum_ee.workflows.display.workflows.vellum_workflow_display import VellumWorkflowDisplay
|
@@ -32,7 +34,7 @@ class InnerRetryGenericNodeDisplay(BaseNodeDisplay[InnerRetryGenericNode.__wrapp
|
|
32
34
|
pass
|
33
35
|
|
34
36
|
|
35
|
-
class OuterRetryNodeDisplay(
|
37
|
+
class OuterRetryNodeDisplay(BaseRetryNodeDisplay[InnerRetryGenericNode]): # type: ignore
|
36
38
|
pass
|
37
39
|
|
38
40
|
|
@@ -213,3 +215,25 @@ def test_serialize_node__try(serialize_node):
|
|
213
215
|
},
|
214
216
|
serialized_node,
|
215
217
|
)
|
218
|
+
|
219
|
+
|
220
|
+
@pytest.mark.skip(reason="Not implemented")
|
221
|
+
def test_serialize_node__stacked():
|
222
|
+
@TryNode.wrap()
|
223
|
+
@RetryNode.wrap(max_attempts=5)
|
224
|
+
class InnerStackedGenericNode(BaseNode):
|
225
|
+
pass
|
226
|
+
|
227
|
+
# AND a workflow that uses the adornment node
|
228
|
+
class StackedWorkflow(BaseWorkflow):
|
229
|
+
graph = InnerStackedGenericNode
|
230
|
+
|
231
|
+
# WHEN we serialize the workflow
|
232
|
+
workflow_display = get_workflow_display(
|
233
|
+
base_display_class=VellumWorkflowDisplay,
|
234
|
+
workflow_class=StackedWorkflow,
|
235
|
+
)
|
236
|
+
exec_config = workflow_display.serialize()
|
237
|
+
|
238
|
+
# THEN the workflow display is created successfully
|
239
|
+
assert exec_config is not None
|
@@ -596,6 +596,20 @@ def test_serialize_workflow__try_wrapped():
|
|
596
596
|
],
|
597
597
|
"name": "TryNode",
|
598
598
|
},
|
599
|
+
"adornments": [
|
600
|
+
{
|
601
|
+
"id": "3344083c-a32c-4a32-920b-0fb5093448fa",
|
602
|
+
"label": "TryNode",
|
603
|
+
"base": {"name": "TryNode", "module": ["vellum", "workflows", "nodes", "core", "try_node", "node"]},
|
604
|
+
"attributes": [
|
605
|
+
{
|
606
|
+
"id": "ab2fbab0-e2a0-419b-b1ef-ce11ecf11e90",
|
607
|
+
"name": "on_error_code",
|
608
|
+
"value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
|
609
|
+
}
|
610
|
+
],
|
611
|
+
}
|
612
|
+
],
|
599
613
|
}
|
600
614
|
|
601
615
|
final_output_nodes = workflow_raw_data["nodes"][2:]
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
+
import pytest
|
2
|
+
|
1
3
|
from deepdiff import DeepDiff
|
2
4
|
|
3
5
|
from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
|
4
6
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
5
7
|
|
6
|
-
from tests.workflows.basic_try_node.workflow import SimpleTryExample
|
8
|
+
from tests.workflows.basic_try_node.workflow import SimpleTryExample, StandaloneTryExample
|
7
9
|
|
8
10
|
|
9
11
|
def test_serialize_workflow():
|
@@ -69,3 +71,19 @@ def test_serialize_workflow():
|
|
69
71
|
|
70
72
|
try_node = workflow_raw_data["nodes"][1]
|
71
73
|
assert try_node["id"] == "1381c078-efa2-4255-89a1-7b4cb742c7fc"
|
74
|
+
|
75
|
+
|
76
|
+
def test_serialize_workflow__standalone():
|
77
|
+
# GIVEN a Workflow with a standalone TryNode
|
78
|
+
# WHEN we serialize it
|
79
|
+
with pytest.raises(NotImplementedError) as exc:
|
80
|
+
workflow_display = get_workflow_display(
|
81
|
+
base_display_class=VellumWorkflowDisplay, workflow_class=StandaloneTryExample
|
82
|
+
)
|
83
|
+
workflow_display.serialize()
|
84
|
+
|
85
|
+
# THEN we should get an error
|
86
|
+
assert (
|
87
|
+
exc.value.args[0]
|
88
|
+
== "Unable to serialize standalone adornment nodes. Please use adornment nodes as a decorator."
|
89
|
+
)
|
File without changes
|
File without changes
|
File without changes
|