vellum-ai 1.3.4__py3-none-any.whl → 1.3.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 +2 -2
- vellum/workflows/nodes/bases/base.py +2 -5
- vellum/workflows/nodes/core/map_node/node.py +8 -1
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +2 -2
- vellum/workflows/nodes/displayable/guardrail_node/node.py +8 -3
- vellum/workflows/nodes/displayable/tool_calling_node/node.py +4 -0
- vellum/workflows/nodes/displayable/tool_calling_node/utils.py +2 -0
- vellum/workflows/outputs/base.py +11 -11
- vellum/workflows/references/output.py +3 -5
- vellum/workflows/workflows/base.py +9 -1
- {vellum_ai-1.3.4.dist-info → vellum_ai-1.3.5.dist-info}/METADATA +1 -1
- {vellum_ai-1.3.4.dist-info → vellum_ai-1.3.5.dist-info}/RECORD +22 -20
- vellum_ee/workflows/display/tests/test_base_workflow_display.py +47 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_parent_input.py +85 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py +1 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_final_output_node_map_reference_serialization.py +88 -0
- vellum_ee/workflows/display/utils/expressions.py +12 -0
- vellum_ee/workflows/display/utils/vellum.py +3 -1
- vellum_ee/workflows/display/workflows/base_workflow_display.py +9 -5
- {vellum_ai-1.3.4.dist-info → vellum_ai-1.3.5.dist-info}/LICENSE +0 -0
- {vellum_ai-1.3.4.dist-info → vellum_ai-1.3.5.dist-info}/WHEEL +0 -0
- {vellum_ai-1.3.4.dist-info → vellum_ai-1.3.5.dist-info}/entry_points.txt +0 -0
@@ -27,10 +27,10 @@ class BaseClientWrapper:
|
|
27
27
|
|
28
28
|
def get_headers(self) -> typing.Dict[str, str]:
|
29
29
|
headers: typing.Dict[str, str] = {
|
30
|
-
"User-Agent": "vellum-ai/1.3.
|
30
|
+
"User-Agent": "vellum-ai/1.3.5",
|
31
31
|
"X-Fern-Language": "Python",
|
32
32
|
"X-Fern-SDK-Name": "vellum-ai",
|
33
|
-
"X-Fern-SDK-Version": "1.3.
|
33
|
+
"X-Fern-SDK-Version": "1.3.5",
|
34
34
|
**(self.get_custom_headers() or {}),
|
35
35
|
}
|
36
36
|
if self._api_version is not None:
|
@@ -120,7 +120,7 @@ class BaseNodeMeta(ABCMeta):
|
|
120
120
|
cls = super().__new__(mcs, name, bases, dct)
|
121
121
|
node_class = cast(Type["BaseNode"], cls)
|
122
122
|
|
123
|
-
node_class.Outputs.
|
123
|
+
node_class.Outputs.__parent_class__ = node_class
|
124
124
|
|
125
125
|
# Add cls to relevant nested classes, since python should've been doing this by default
|
126
126
|
for port in node_class.Ports:
|
@@ -270,11 +270,8 @@ class BaseNode(Generic[StateType], ABC, metaclass=BaseNodeMeta):
|
|
270
270
|
class ExternalInputs(BaseInputs):
|
271
271
|
__descriptor_class__ = ExternalInputReference
|
272
272
|
|
273
|
-
# TODO: Consider using metaclasses to prevent the need for users to specify that the
|
274
|
-
# "Outputs" class inherits from "BaseOutputs" and do so automatically.
|
275
|
-
# https://app.shortcut.com/vellum/story/4008/auto-inherit-basenodeoutputs-in-outputs-classes
|
276
273
|
class Outputs(BaseOutputs):
|
277
|
-
|
274
|
+
__parent_class__: Type["BaseNode"] = field(init=False)
|
278
275
|
|
279
276
|
class Ports(NodePorts):
|
280
277
|
default = Port(default=True)
|
@@ -27,6 +27,7 @@ from vellum.workflows.nodes.bases.base_adornment_node import BaseAdornmentNode
|
|
27
27
|
from vellum.workflows.nodes.utils import create_adornment
|
28
28
|
from vellum.workflows.outputs import BaseOutputs
|
29
29
|
from vellum.workflows.outputs.base import BaseOutput
|
30
|
+
from vellum.workflows.references.node import NodeReference
|
30
31
|
from vellum.workflows.references.output import OutputReference
|
31
32
|
from vellum.workflows.state.context import WorkflowContext
|
32
33
|
from vellum.workflows.types.generics import StateType
|
@@ -217,5 +218,11 @@ class MapNode(BaseAdornmentNode[StateType], Generic[StateType, MapNodeItemType])
|
|
217
218
|
# value: List[str]
|
218
219
|
outputs_class.__annotations__ = {**previous_annotations, reference.name: annotation}
|
219
220
|
|
220
|
-
|
221
|
+
subworkflow_class = cls.subworkflow.instance if isinstance(cls.subworkflow, NodeReference) else None
|
222
|
+
if subworkflow_class:
|
223
|
+
output_id = subworkflow_class.__output_ids__.get(reference.name) or uuid4_from_hash(
|
224
|
+
f"{cls.__id__}|{reference.name}"
|
225
|
+
)
|
226
|
+
else:
|
227
|
+
output_id = uuid4_from_hash(f"{cls.__id__}|{reference.name}")
|
221
228
|
cls.__output_ids__[reference.name] = output_id
|
@@ -100,7 +100,7 @@ class BaseInlinePromptNode(BasePromptNode[StateType], Generic[StateType]):
|
|
100
100
|
execution_context = get_execution_context()
|
101
101
|
request_options = self.request_options or RequestOptions()
|
102
102
|
|
103
|
-
processed_parameters = self.
|
103
|
+
processed_parameters = self.process_parameters(self.parameters)
|
104
104
|
|
105
105
|
request_options["additional_body_parameters"] = {
|
106
106
|
"execution_context": execution_context.model_dump(mode="json"),
|
@@ -300,7 +300,7 @@ class BaseInlinePromptNode(BasePromptNode[StateType], Generic[StateType]):
|
|
300
300
|
|
301
301
|
return input_variables, input_values
|
302
302
|
|
303
|
-
def
|
303
|
+
def process_parameters(self, parameters: PromptParameters) -> PromptParameters:
|
304
304
|
"""
|
305
305
|
Process parameters to recursively convert any Pydantic models to JSON schema dictionaries.
|
306
306
|
"""
|
@@ -8,7 +8,6 @@ from vellum.workflows.constants import LATEST_RELEASE_TAG
|
|
8
8
|
from vellum.workflows.errors.types import WorkflowErrorCode
|
9
9
|
from vellum.workflows.exceptions import NodeException
|
10
10
|
from vellum.workflows.nodes.bases import BaseNode
|
11
|
-
from vellum.workflows.outputs.base import BaseOutputs
|
12
11
|
from vellum.workflows.types.core import EntityInputsInterface, MergeBehavior
|
13
12
|
from vellum.workflows.types.generics import StateType
|
14
13
|
|
@@ -33,7 +32,7 @@ class GuardrailNode(BaseNode[StateType], Generic[StateType]):
|
|
33
32
|
class Trigger(BaseNode.Trigger):
|
34
33
|
merge_behavior = MergeBehavior.AWAIT_ANY
|
35
34
|
|
36
|
-
class Outputs(
|
35
|
+
class Outputs(BaseNode.Outputs):
|
37
36
|
score: float
|
38
37
|
normalized_score: Optional[float]
|
39
38
|
log: Optional[str]
|
@@ -98,7 +97,13 @@ class GuardrailNode(BaseNode[StateType], Generic[StateType]):
|
|
98
97
|
else:
|
99
98
|
reason = None
|
100
99
|
|
101
|
-
return self.Outputs(
|
100
|
+
return self.Outputs(
|
101
|
+
score=score,
|
102
|
+
normalized_score=normalized_score,
|
103
|
+
log=log,
|
104
|
+
reason=reason,
|
105
|
+
**metric_outputs, # type: ignore [arg-type]
|
106
|
+
)
|
102
107
|
|
103
108
|
def _compile_metric_inputs(self) -> List[MetricDefinitionInput]:
|
104
109
|
# TODO: We may want to consolidate with prompt deployment input compilation
|
@@ -138,6 +138,9 @@ class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
|
|
138
138
|
)
|
139
139
|
|
140
140
|
def _build_graph(self) -> None:
|
141
|
+
# Get the process_parameters method if it exists on this class
|
142
|
+
process_parameters_method = getattr(self.__class__, "process_parameters", None)
|
143
|
+
|
141
144
|
self.tool_prompt_node = create_tool_prompt_node(
|
142
145
|
ml_model=self.ml_model,
|
143
146
|
blocks=self.blocks,
|
@@ -145,6 +148,7 @@ class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
|
|
145
148
|
prompt_inputs=self.prompt_inputs,
|
146
149
|
parameters=self.parameters,
|
147
150
|
max_prompt_iterations=self.max_prompt_iterations,
|
151
|
+
process_parameters_method=process_parameters_method,
|
148
152
|
)
|
149
153
|
|
150
154
|
# Create the router node (handles routing logic only)
|
@@ -329,6 +329,7 @@ def create_tool_prompt_node(
|
|
329
329
|
prompt_inputs: Optional[EntityInputsInterface],
|
330
330
|
parameters: PromptParameters,
|
331
331
|
max_prompt_iterations: Optional[int] = None,
|
332
|
+
process_parameters_method: Optional[Callable] = None,
|
332
333
|
) -> Type[ToolPromptNode]:
|
333
334
|
if functions and len(functions) > 0:
|
334
335
|
prompt_functions: List[Union[Tool, FunctionDefinition]] = []
|
@@ -398,6 +399,7 @@ def create_tool_prompt_node(
|
|
398
399
|
"prompt_inputs": node_prompt_inputs,
|
399
400
|
"parameters": parameters,
|
400
401
|
"max_prompt_iterations": max_prompt_iterations,
|
402
|
+
**({"process_parameters": process_parameters_method} if process_parameters_method is not None else {}),
|
401
403
|
"__module__": __name__,
|
402
404
|
},
|
403
405
|
),
|
vellum/workflows/outputs/base.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
+
from dataclasses import field
|
1
2
|
import inspect
|
2
|
-
from typing import
|
3
|
+
from typing import Any, Generic, Iterator, Set, Tuple, Type, TypeVar, Union, cast
|
3
4
|
from typing_extensions import dataclass_transform
|
4
5
|
|
5
6
|
from pydantic import GetCoreSchemaHandler
|
@@ -13,9 +14,6 @@ from vellum.workflows.references.output import OutputReference
|
|
13
14
|
from vellum.workflows.types.generics import is_node_instance
|
14
15
|
from vellum.workflows.types.utils import get_class_attr_names, infer_types
|
15
16
|
|
16
|
-
if TYPE_CHECKING:
|
17
|
-
from vellum.workflows.nodes.bases.base import BaseNode
|
18
|
-
|
19
17
|
_Delta = TypeVar("_Delta")
|
20
18
|
_Accumulated = TypeVar("_Accumulated")
|
21
19
|
|
@@ -112,18 +110,16 @@ class _BaseOutputsMeta(type):
|
|
112
110
|
if not cls.__qualname__.endswith(".Outputs") or not other.__qualname__.endswith(".Outputs"):
|
113
111
|
return super().__eq__(other)
|
114
112
|
|
115
|
-
self_outputs_class = cast(Type["
|
116
|
-
other_outputs_class = cast(Type["
|
113
|
+
self_outputs_class = cast(Type["BaseOutputs"], cls)
|
114
|
+
other_outputs_class = cast(Type["BaseOutputs"], other)
|
117
115
|
|
118
|
-
if not hasattr(self_outputs_class, "
|
116
|
+
if not hasattr(self_outputs_class, "__parent_class__") or not hasattr(other_outputs_class, "__parent_class__"):
|
119
117
|
return super().__eq__(other)
|
120
118
|
|
121
|
-
if self_outputs_class.
|
119
|
+
if self_outputs_class.__parent_class__ is None or other_outputs_class.__parent_class__ is None:
|
122
120
|
return super().__eq__(other)
|
123
121
|
|
124
|
-
return
|
125
|
-
other_outputs_class._node_class, "__qualname__"
|
126
|
-
)
|
122
|
+
return self_outputs_class.__parent_class__.__qualname__ == other_outputs_class.__parent_class__.__qualname__
|
127
123
|
|
128
124
|
def __setattr__(cls, name: str, value: Any) -> None:
|
129
125
|
if isinstance(value, OutputReference):
|
@@ -187,6 +183,10 @@ class _BaseOutputsMeta(type):
|
|
187
183
|
|
188
184
|
|
189
185
|
class BaseOutputs(metaclass=_BaseOutputsMeta):
|
186
|
+
# TODO: Uncomment once we figure out why this causes a failure in `infer_types`
|
187
|
+
# __parent_class__: Type[Union["BaseNode", "BaseWorkflow"]] = field(init=False)
|
188
|
+
__parent_class__: Type = field(init=False)
|
189
|
+
|
190
190
|
def __init__(self, **kwargs: Any) -> None:
|
191
191
|
declared_fields = {descriptor.name for descriptor in self.__class__}
|
192
192
|
provided_fields = set(kwargs.keys())
|
@@ -35,13 +35,11 @@ class OutputReference(BaseDescriptor[_OutputType], Generic[_OutputType]):
|
|
35
35
|
|
36
36
|
@cached_property
|
37
37
|
def id(self) -> UUID:
|
38
|
-
|
39
|
-
|
40
|
-
node_class = getattr(self._outputs_class, "_node_class", None)
|
41
|
-
if not node_class:
|
38
|
+
parent_class = self._outputs_class.__parent_class__
|
39
|
+
if not parent_class:
|
42
40
|
return uuid4()
|
43
41
|
|
44
|
-
output_ids = getattr(
|
42
|
+
output_ids = getattr(parent_class, "__output_ids__", {})
|
45
43
|
if not isinstance(output_ids, dict):
|
46
44
|
return uuid4()
|
47
45
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
from dataclasses import field
|
1
2
|
from datetime import datetime
|
2
3
|
from functools import lru_cache
|
3
4
|
import importlib
|
@@ -199,6 +200,12 @@ class _BaseWorkflowMeta(type):
|
|
199
200
|
if inputs_class is not BaseInputs and inputs_class.__parent_class__ is type(None):
|
200
201
|
inputs_class.__parent_class__ = workflow_class
|
201
202
|
|
203
|
+
# TODO: Uncomment this once we figure out why it's causing the ipython reload test to fail
|
204
|
+
# workflow_class.Outputs.__parent_class__ = workflow_class
|
205
|
+
workflow_class.__output_ids__ = {
|
206
|
+
ref.name: uuid4_from_hash(f"{workflow_class.__id__}|id|{ref.name}") for ref in workflow_class.Outputs
|
207
|
+
}
|
208
|
+
|
202
209
|
return workflow_class
|
203
210
|
|
204
211
|
|
@@ -207,6 +214,7 @@ GraphAttribute = Union[Type[BaseNode], Graph, Set[Type[BaseNode]], Set[Graph]]
|
|
207
214
|
|
208
215
|
class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
209
216
|
__id__: UUID = uuid4_from_hash(__qualname__)
|
217
|
+
__output_ids__: Dict[str, UUID] = {}
|
210
218
|
graph: ClassVar[GraphAttribute]
|
211
219
|
unused_graphs: ClassVar[Set[GraphAttribute]] # nodes or graphs that are defined but not used in the graph
|
212
220
|
emitters: List[BaseWorkflowEmitter]
|
@@ -214,7 +222,7 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
|
|
214
222
|
is_dynamic: ClassVar[bool] = False
|
215
223
|
|
216
224
|
class Outputs(BaseOutputs):
|
217
|
-
|
225
|
+
__parent_class__: Type["BaseWorkflow"] = field(init=False)
|
218
226
|
|
219
227
|
WorkflowEvent = Union[ # type: ignore
|
220
228
|
GenericWorkflowEvent,
|
@@ -72,7 +72,7 @@ vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py,sha256=rHybfUAWwa0L
|
|
72
72
|
vellum_ee/workflows/display/nodes/vellum/try_node.py,sha256=47fOnSCEFnY8th9m2yTYlgnoUuzgyRZdjg-SXwn--lk,4079
|
73
73
|
vellum_ee/workflows/display/nodes/vellum/utils.py,sha256=oICunzyaXPs0tYnW5zH1r93Bx35MSH7mcD-n0DEWRok,4978
|
74
74
|
vellum_ee/workflows/display/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
75
|
-
vellum_ee/workflows/display/tests/test_base_workflow_display.py,sha256=
|
75
|
+
vellum_ee/workflows/display/tests/test_base_workflow_display.py,sha256=fh-F7TmzOSiWEBwhtd3Nm_Ix9v5W4LhMNhiIxT7G100,15990
|
76
76
|
vellum_ee/workflows/display/tests/workflow_serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
77
77
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
78
78
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py,sha256=Y-ajeT65b5varmrZCw6L3hir4hJCFq-eO0jZfRcrs7g,1886
|
@@ -100,10 +100,12 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_nod
|
|
100
100
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_composio_serialization.py,sha256=oVXCjkU0G56QJmqnd_xIwF3D9bhJwALFibM2wmRhwUk,3739
|
101
101
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py,sha256=Sg82qV5NCzQDy-RD90hA6QaHgFHOq6ESNkbWXygsnNw,26367
|
102
102
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_mcp_serialization.py,sha256=QhQbijeCnFeX1i3SMjHJg2WVAEt5JEO3dhFRv-mofdA,2458
|
103
|
-
vellum_ee/workflows/display/tests/workflow_serialization/
|
103
|
+
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_parent_input.py,sha256=HMMa4liBACtL7vU2b-9Ui8Oltyxxb5dbAf-CziLGCes,4284
|
104
|
+
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=cKaxme5vUIvKa8aBU7xdeFxXF9wVZ5fW3T5Ie5vToU0,10152
|
104
105
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_workflow_deployment_serialization.py,sha256=XIZZr5POo2NLn2uEWm9EC3rejeBMoO4X-JtzTH6mvp4,4074
|
105
106
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py,sha256=pLCyMScV88DTBXRH7jXaXOEA1GBq8NIipCUFwIAWnwI,2771
|
106
107
|
vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py,sha256=exT7U-axwtYgFylagScflSQLJEND51qIAx2UATju6JM,6023
|
108
|
+
vellum_ee/workflows/display/tests/workflow_serialization/test_final_output_node_map_reference_serialization.py,sha256=vl3pxUJlrYRA8zzFJ-gRm7fe-5fviLNSIsUC7imnMqk,3502
|
107
109
|
vellum_ee/workflows/display/tests/workflow_serialization/test_web_search_node_serialization.py,sha256=vbDFBrWUPeeW7cxjNA6SXrsHlYcbOAhlQ4C45Vdnr1c,3428
|
108
110
|
vellum_ee/workflows/display/tests/workflow_serialization/test_workflow_input_parameterization_error.py,sha256=vAdmn3YTBDpo55znbydQxsgg9ASqHcvsUPwiBR_7wfo,1461
|
109
111
|
vellum_ee/workflows/display/types.py,sha256=cyZruu4sXAdHjwuFc7dydM4DcFNf-pp_CmulXItxac4,3679
|
@@ -111,15 +113,15 @@ vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeR
|
|
111
113
|
vellum_ee/workflows/display/utils/auto_layout.py,sha256=f4GiLn_LazweupfqTpubcdtdfE_vrOcmZudSsnYIY9E,3906
|
112
114
|
vellum_ee/workflows/display/utils/events.py,sha256=MEG2BT6GgAzkbv1dMaFpov5OShtaAZeAb1-g3xDFsAM,1826
|
113
115
|
vellum_ee/workflows/display/utils/exceptions.py,sha256=LSwwxCYNxFkf5XMUcFkaZKpQ13OSrI7y_bpEUwbKVk0,169
|
114
|
-
vellum_ee/workflows/display/utils/expressions.py,sha256=
|
116
|
+
vellum_ee/workflows/display/utils/expressions.py,sha256=ci6vOzfCwqVrSRI9A-HR7t4bz_reuS2kzr71SHYIloI,19118
|
115
117
|
vellum_ee/workflows/display/utils/registry.py,sha256=1qXiBTdsnro6FeCX0FGBEK7CIf6wa--Jt50iZ_nEp_M,3460
|
116
118
|
vellum_ee/workflows/display/utils/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
117
119
|
vellum_ee/workflows/display/utils/tests/test_auto_layout.py,sha256=vfXI769418s9vda5Gb5NFBH747WMOwSgHRXeLCTLVm8,2356
|
118
120
|
vellum_ee/workflows/display/utils/tests/test_events.py,sha256=Qze6wEmFJx23_sKQhX-i329apWgMeS9zTptWlRca6Ko,4528
|
119
|
-
vellum_ee/workflows/display/utils/vellum.py,sha256=
|
121
|
+
vellum_ee/workflows/display/utils/vellum.py,sha256=sZwU0KdmZZTKWW62SyxJTl2tC8tN6p_BpZ-lDoinV-U,5670
|
120
122
|
vellum_ee/workflows/display/vellum.py,sha256=J2mdJZ1sdLW535DDUkq_Vm8Z572vhuxHxVZF9deKSdk,391
|
121
123
|
vellum_ee/workflows/display/workflows/__init__.py,sha256=JTB9ObEV3l4gGGdtfBHwVJtTTKC22uj-a-XjTVwXCyA,148
|
122
|
-
vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=
|
124
|
+
vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=5PCZY37qMA3z1Qpu35Ot0koF5-iH4XVctlLzEMJ26OQ,43991
|
123
125
|
vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=gxz76AeCqgAZ9D2lZeTiZzxY9eMgn3qOSfVgiqYcOh8,2028
|
124
126
|
vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=pb7BTH-ivRnya1LQU3j-MApWk_m8POpPNOdD0oEK82A,37847
|
125
127
|
vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -153,7 +155,7 @@ vellum/client/README.md,sha256=b6XKeYBBbhQx0v1sHWfM0gIJeJhUFF-aqL2ig7ADa08,5564
|
|
153
155
|
vellum/client/__init__.py,sha256=T5Ht_w-Mk_9nzGqdadhQB8V20M0vYj7am06ut0A3P1o,73401
|
154
156
|
vellum/client/core/__init__.py,sha256=lTcqUPXcx4112yLDd70RAPeqq6tu3eFMe1pKOqkW9JQ,1562
|
155
157
|
vellum/client/core/api_error.py,sha256=44vPoTyWN59gonCIZMdzw7M1uspygiLnr3GNFOoVL2Q,614
|
156
|
-
vellum/client/core/client_wrapper.py,sha256=
|
158
|
+
vellum/client/core/client_wrapper.py,sha256=Mh9pKr9Xxrby4VuKOk1M3W1PdfGhP8jsj32r0WIAdN8,2840
|
157
159
|
vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
|
158
160
|
vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
|
159
161
|
vellum/client/core/force_multipart.py,sha256=awxh5MtcRYe74ehY8U76jzv6fYM_w_D3Rur7KQQzSDk,429
|
@@ -1797,7 +1799,7 @@ vellum/workflows/integrations/tests/test_mcp_service.py,sha256=q_DYrDkIqI4sQBNgI
|
|
1797
1799
|
vellum/workflows/logging.py,sha256=_a217XogktV4Ncz6xKFz7WfYmZAzkfVRVuC0rWob8ls,437
|
1798
1800
|
vellum/workflows/nodes/__init__.py,sha256=zymtc3_iW2rFmMR-sayTLuN6ZsAw8VnJweWPsjQk2-Q,1197
|
1799
1801
|
vellum/workflows/nodes/bases/__init__.py,sha256=cniHuz_RXdJ4TQgD8CBzoiKDiPxg62ErdVpCbWICX64,58
|
1800
|
-
vellum/workflows/nodes/bases/base.py,sha256=
|
1802
|
+
vellum/workflows/nodes/bases/base.py,sha256=424pCSFZtXqMpAYX01nQCP3Ej2s16_ynWY3VtxUwTdo,20497
|
1801
1803
|
vellum/workflows/nodes/bases/base_adornment_node.py,sha256=hrgzuTetM4ynPd9YGHoK8Vwwn4XITi3aZZ_OCnQrq4Y,3433
|
1802
1804
|
vellum/workflows/nodes/bases/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1803
1805
|
vellum/workflows/nodes/bases/tests/test_base_adornment_node.py,sha256=fXZI9KqpS4XMBrBnIEkK3foHaBVvyHwYcQWWDKay7ic,1148
|
@@ -1810,7 +1812,7 @@ vellum/workflows/nodes/core/inline_subworkflow_node/node.py,sha256=TCmO0wPbt7kc8
|
|
1810
1812
|
vellum/workflows/nodes/core/inline_subworkflow_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1811
1813
|
vellum/workflows/nodes/core/inline_subworkflow_node/tests/test_node.py,sha256=RK2g1h2ib-ruQZ9A2_2L-B9WBdHV44WZj75rkDNL0cE,5766
|
1812
1814
|
vellum/workflows/nodes/core/map_node/__init__.py,sha256=MXpZYmGfhsMJHqqlpd64WiJRtbAtAMQz-_3fCU_cLV0,56
|
1813
|
-
vellum/workflows/nodes/core/map_node/node.py,sha256=
|
1815
|
+
vellum/workflows/nodes/core/map_node/node.py,sha256=kSyQmIWk4v-KSt4WBf3d-0_QueKYkjtrEmQPGTbMryw,10054
|
1814
1816
|
vellum/workflows/nodes/core/map_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1815
1817
|
vellum/workflows/nodes/core/map_node/tests/test_node.py,sha256=Xc2xZY5ShSy-bsIQe41JbvIjq3TE95duS-ygaELRVkk,9320
|
1816
1818
|
vellum/workflows/nodes/core/retry_node/__init__.py,sha256=lN2bIy5a3Uzhs_FYCrooADyYU6ZGShtvLKFWpelwPvo,60
|
@@ -1837,7 +1839,7 @@ vellum/workflows/nodes/displayable/bases/api_node/tests/test_node.py,sha256=5C59
|
|
1837
1839
|
vellum/workflows/nodes/displayable/bases/base_prompt_node/__init__.py,sha256=Org3xTvgp1pA0uUXFfnJr29D3HzCey2lEdYF4zbIUgo,70
|
1838
1840
|
vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py,sha256=ea20icDM1HB942wkH-XtXNSNCBDcjeOiN3vowkHL4fs,4477
|
1839
1841
|
vellum/workflows/nodes/displayable/bases/inline_prompt_node/__init__.py,sha256=Hl35IAoepRpE-j4cALaXVJIYTYOF3qszyVbxTj4kS1s,82
|
1840
|
-
vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=
|
1842
|
+
vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=wtJjaV0tJ5svO0shxFA-lInDQB25wfv24VUvAOYLOZ8,13793
|
1841
1843
|
vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1842
1844
|
vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/test_inline_prompt_node.py,sha256=Hk_u2IxLIeeqL_s0RTgoyL5QGYwY9VllKT8z5_JHiDU,24956
|
1843
1845
|
vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py,sha256=0a40fkkZkFMmZN0CsWf6EP_y1H6x36EGa3WcfVNyOsM,9797
|
@@ -1861,7 +1863,7 @@ vellum/workflows/nodes/displayable/final_output_node/node.py,sha256=6SMaGeBlHQ5r
|
|
1861
1863
|
vellum/workflows/nodes/displayable/final_output_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1862
1864
|
vellum/workflows/nodes/displayable/final_output_node/tests/test_node.py,sha256=E6LQ74qZjY4Xi4avx2qdOCgGhF8pEcNLBh8cqYRkzMI,709
|
1863
1865
|
vellum/workflows/nodes/displayable/guardrail_node/__init__.py,sha256=Ab5eXmOoBhyV4dMWdzh32HLUmnPIBEK_zFCT38C4Fng,68
|
1864
|
-
vellum/workflows/nodes/displayable/guardrail_node/node.py,sha256=
|
1866
|
+
vellum/workflows/nodes/displayable/guardrail_node/node.py,sha256=axYUojar_kdB3gi4LG3g9euJ8VkOxNtiFxJNI46v-SQ,5869
|
1865
1867
|
vellum/workflows/nodes/displayable/guardrail_node/test_node.py,sha256=SAGv6hSFcBwQkudn1VxtaKNsXSXWWELl3eK05zM6tS0,5410
|
1866
1868
|
vellum/workflows/nodes/displayable/guardrail_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1867
1869
|
vellum/workflows/nodes/displayable/guardrail_node/tests/test_node.py,sha256=X2pd6TI8miYxIa7rgvs1pHTEreyWcf77EyR0_Jsa700,2055
|
@@ -1891,13 +1893,13 @@ vellum/workflows/nodes/displayable/tests/test_search_node_error_handling.py,sha2
|
|
1891
1893
|
vellum/workflows/nodes/displayable/tests/test_search_node_wth_text_output.py,sha256=VepO5z1277c1y5N6LLIC31nnWD1aak2m5oPFplfJHHs,6935
|
1892
1894
|
vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py,sha256=Bjv-wZyFgNaVZb9KEMMZd9lFoLzbPEPjEMpANizMZw4,2413
|
1893
1895
|
vellum/workflows/nodes/displayable/tool_calling_node/__init__.py,sha256=3n0-ysmFKsr40CVxPthc0rfJgqVJeZuUEsCmYudLVRg,117
|
1894
|
-
vellum/workflows/nodes/displayable/tool_calling_node/node.py,sha256=
|
1896
|
+
vellum/workflows/nodes/displayable/tool_calling_node/node.py,sha256=KxKU8JWFr-XUdwYelEtUTfAh3UaN3FTm2wK-nioXu9U,8065
|
1895
1897
|
vellum/workflows/nodes/displayable/tool_calling_node/state.py,sha256=CcBVb_YtwfSSka4ze678k6-qwmzMSfjfVP8_Y95feSo,302
|
1896
1898
|
vellum/workflows/nodes/displayable/tool_calling_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1897
1899
|
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_composio_service.py,sha256=in1fbEz5x1tx3uKv9YXdvOncsHucNL8Ro6Go7lBuuOQ,8962
|
1898
1900
|
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py,sha256=GZoeybB9uM7ai8sBLAtUMHrMVgh-WrJDWrKZci6feDs,11892
|
1899
1901
|
vellum/workflows/nodes/displayable/tool_calling_node/tests/test_utils.py,sha256=SIu5GCj4tIE4fz-cAcdULtQfqZIhrcc3Doo6TWLXBws,8804
|
1900
|
-
vellum/workflows/nodes/displayable/tool_calling_node/utils.py,sha256=
|
1902
|
+
vellum/workflows/nodes/displayable/tool_calling_node/utils.py,sha256=VC0TenXfktfWHd4ZYQFqiXaZvkhizLJFIK3nQvKoVG0,23780
|
1901
1903
|
vellum/workflows/nodes/displayable/web_search_node/__init__.py,sha256=8FOnEP-n-U68cvxTlJW9wphIAGHq5aqjzLM-DoSSXnU,61
|
1902
1904
|
vellum/workflows/nodes/displayable/web_search_node/node.py,sha256=NQYux2bOtuBF5E4tn-fXi5y3btURPRrNqMSM9MAZYI4,5091
|
1903
1905
|
vellum/workflows/nodes/displayable/web_search_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -1912,7 +1914,7 @@ vellum/workflows/nodes/tests/test_mocks.py,sha256=mfPvrs75PKcsNsbJLQAN6PDFoVqs9T
|
|
1912
1914
|
vellum/workflows/nodes/tests/test_utils.py,sha256=BUugAHx2C9YuCwTlsTXV1Glxca0kW3St6T9o_QFatSU,5649
|
1913
1915
|
vellum/workflows/nodes/utils.py,sha256=wCvf8K5qruT5GwtvnHcQ-LMllktTD8aaFmAGpKQy--c,10720
|
1914
1916
|
vellum/workflows/outputs/__init__.py,sha256=AyZ4pRh_ACQIGvkf0byJO46EDnSix1ZCAXfvh-ms1QE,94
|
1915
|
-
vellum/workflows/outputs/base.py,sha256=
|
1917
|
+
vellum/workflows/outputs/base.py,sha256=XLt2WnjOhHBC0tT4-1FIUHLVdI1J5NXME6UZxKCKwOI,9741
|
1916
1918
|
vellum/workflows/ports/__init__.py,sha256=bZuMt-R7z5bKwpu4uPW7LlJeePOQWmCcDSXe5frUY5g,101
|
1917
1919
|
vellum/workflows/ports/node_ports.py,sha256=SM9uLAaoaE1HwR-Uqwf2v5zZK5iFnphKs6mE5Ls7ldE,2877
|
1918
1920
|
vellum/workflows/ports/port.py,sha256=PYhmzEHgJyjUjSFkPIQ38cNIKpcXrhYiZlj7nZC5yCk,3989
|
@@ -1925,7 +1927,7 @@ vellum/workflows/references/external_input.py,sha256=c_4SojTpykCSbGS1Pjmx9FfquyY
|
|
1925
1927
|
vellum/workflows/references/input.py,sha256=3INu-TLTi4dziWmva6LO3WvgDlPzsjayUx61cVvqLJA,325
|
1926
1928
|
vellum/workflows/references/lazy.py,sha256=jgUYmgt-yAybzPf_R-74MzdU8VuNwMYI8EQqrj9lVR0,2948
|
1927
1929
|
vellum/workflows/references/node.py,sha256=LP854wDVs-9I_aZ7-nkbwXqL2H7W2_3LED2e9FixNS8,1418
|
1928
|
-
vellum/workflows/references/output.py,sha256=
|
1930
|
+
vellum/workflows/references/output.py,sha256=utYoYPVfsPCTFPXcAc0zTVn2aZcFwL_-y1_WLiKMhHA,3329
|
1929
1931
|
vellum/workflows/references/state_value.py,sha256=bInUF0A3Pt4-zhA0f6LdSuyv8tz7n5QRkHAEn4gsmqI,711
|
1930
1932
|
vellum/workflows/references/tests/test_lazy.py,sha256=0s50-LizMTlSTBQahpK0fg_xqCucA8YTp6QmIMqPvMk,919
|
1931
1933
|
vellum/workflows/references/vellum_secret.py,sha256=Od4d19a5yletWMqNfJR5d_mZQUkVcFzj29mE-T9J7yE,480
|
@@ -1974,13 +1976,13 @@ vellum/workflows/utils/vellum_variables.py,sha256=YHLNiQGWDNssGH1FQoG9Z1jUFZ-zYe
|
|
1974
1976
|
vellum/workflows/utils/zip.py,sha256=HVg_YZLmBOTXKaDV3Xhaf3V6sYnfqqZXQ8CpuafkbPY,1181
|
1975
1977
|
vellum/workflows/vellum_client.py,sha256=xkfoucodxNK5JR2-lbRqZx3xzDgExWkP6kySrpi_Ubc,1079
|
1976
1978
|
vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
|
1977
|
-
vellum/workflows/workflows/base.py,sha256=
|
1979
|
+
vellum/workflows/workflows/base.py,sha256=nEFaKuLQahpEAxi-3VmlIbxEcTaKJ3_1G3WNVATVvzA,28466
|
1978
1980
|
vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
|
1979
1981
|
vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1980
1982
|
vellum/workflows/workflows/tests/test_base_workflow.py,sha256=ptMntHzVyy8ZuzNgeTuk7hREgKQ5UBdgq8VJFSGaW4Y,20832
|
1981
1983
|
vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
|
1982
|
-
vellum_ai-1.3.
|
1983
|
-
vellum_ai-1.3.
|
1984
|
-
vellum_ai-1.3.
|
1985
|
-
vellum_ai-1.3.
|
1986
|
-
vellum_ai-1.3.
|
1984
|
+
vellum_ai-1.3.5.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
|
1985
|
+
vellum_ai-1.3.5.dist-info/METADATA,sha256=cGN1uqwggZ4itH8f0IlQACy6h-uM2e-IspLFVz3DwKU,5547
|
1986
|
+
vellum_ai-1.3.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
1987
|
+
vellum_ai-1.3.5.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
|
1988
|
+
vellum_ai-1.3.5.dist-info/RECORD,,
|
@@ -9,6 +9,8 @@ from vellum.workflows.references.lazy import LazyReference
|
|
9
9
|
from vellum.workflows.state import BaseState
|
10
10
|
from vellum.workflows.workflows.base import BaseWorkflow
|
11
11
|
from vellum_ee.workflows.display.base import EdgeDisplay, WorkflowInputsDisplay
|
12
|
+
from vellum_ee.workflows.display.editor.types import NodeDisplayData, NodeDisplayPosition
|
13
|
+
from vellum_ee.workflows.display.nodes import BaseNodeDisplay
|
12
14
|
from vellum_ee.workflows.display.workflows.base_workflow_display import BaseWorkflowDisplay
|
13
15
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
14
16
|
|
@@ -429,3 +431,48 @@ def test_serialize_workflow_with_edge_display_data():
|
|
429
431
|
assert edge_with_display_data["type"] == "DEFAULT"
|
430
432
|
assert "source_node_id" in edge_with_display_data
|
431
433
|
assert "target_node_id" in edge_with_display_data
|
434
|
+
|
435
|
+
|
436
|
+
def test_serialize_workflow_with_node_display_data():
|
437
|
+
"""
|
438
|
+
Tests that nodes with z_index values serialize display_data correctly.
|
439
|
+
"""
|
440
|
+
|
441
|
+
# GIVEN a workflow with a node that has custom display data
|
442
|
+
class TestNode(BaseNode):
|
443
|
+
class Outputs(BaseNode.Outputs):
|
444
|
+
result: str
|
445
|
+
|
446
|
+
class TestWorkflow(BaseWorkflow):
|
447
|
+
graph = TestNode
|
448
|
+
|
449
|
+
class Outputs(BaseWorkflow.Outputs):
|
450
|
+
final_result = TestNode.Outputs.result
|
451
|
+
|
452
|
+
class TestNodeDisplay(BaseNodeDisplay[TestNode]):
|
453
|
+
display_data = NodeDisplayData(position=NodeDisplayPosition(x=100, y=200), z_index=10, width=300, height=150)
|
454
|
+
|
455
|
+
class TestWorkflowDisplay(BaseWorkflowDisplay[TestWorkflow]):
|
456
|
+
pass
|
457
|
+
|
458
|
+
# WHEN we serialize the workflow with the custom node display
|
459
|
+
display = get_workflow_display(
|
460
|
+
base_display_class=TestWorkflowDisplay,
|
461
|
+
workflow_class=TestWorkflow,
|
462
|
+
)
|
463
|
+
serialized_workflow = display.serialize()
|
464
|
+
|
465
|
+
# THEN the node should include display_data with z_index
|
466
|
+
workflow_raw_data = cast(Dict[str, Any], serialized_workflow["workflow_raw_data"])
|
467
|
+
nodes = cast(List[Dict[str, Any]], workflow_raw_data["nodes"])
|
468
|
+
|
469
|
+
test_node = None
|
470
|
+
for node in nodes:
|
471
|
+
if node.get("type") == "GENERIC":
|
472
|
+
definition = node.get("definition")
|
473
|
+
if isinstance(definition, dict) and definition.get("name") == "TestNode":
|
474
|
+
test_node = node
|
475
|
+
break
|
476
|
+
|
477
|
+
assert test_node is not None, "TestNode not found in serialized nodes"
|
478
|
+
assert test_node["display_data"] == {"position": {"x": 100, "y": 200}, "z_index": 10, "width": 300, "height": 150}
|
@@ -0,0 +1,85 @@
|
|
1
|
+
from deepdiff import DeepDiff
|
2
|
+
|
3
|
+
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
4
|
+
|
5
|
+
from tests.workflows.basic_tool_calling_node_parent_input.workflow import BasicToolCallingNodeParentInputWorkflow
|
6
|
+
|
7
|
+
|
8
|
+
def test_serialize_workflow():
|
9
|
+
# GIVEN a Workflow that uses a generic node
|
10
|
+
# WHEN we serialize it
|
11
|
+
workflow_display = get_workflow_display(workflow_class=BasicToolCallingNodeParentInputWorkflow)
|
12
|
+
|
13
|
+
serialized_workflow: dict = workflow_display.serialize()
|
14
|
+
# THEN we should get a serialized representation of the Workflow
|
15
|
+
assert serialized_workflow.keys() == {
|
16
|
+
"workflow_raw_data",
|
17
|
+
"input_variables",
|
18
|
+
"state_variables",
|
19
|
+
"output_variables",
|
20
|
+
}
|
21
|
+
|
22
|
+
# AND its input variables should be what we expect
|
23
|
+
input_variables = serialized_workflow["input_variables"]
|
24
|
+
assert len(input_variables) == 1
|
25
|
+
|
26
|
+
# AND its output variables should be what we expect
|
27
|
+
output_variables = serialized_workflow["output_variables"]
|
28
|
+
assert len(output_variables) == 2
|
29
|
+
assert not DeepDiff(
|
30
|
+
[
|
31
|
+
{"id": "e2e36cfc-cf24-42fd-ba8f-cce39c53d47b", "key": "text", "type": "STRING"},
|
32
|
+
{"id": "08ca9519-e421-47e8-a42d-44f49f6aab16", "key": "chat_history", "type": "CHAT_HISTORY"},
|
33
|
+
],
|
34
|
+
output_variables,
|
35
|
+
ignore_order=True,
|
36
|
+
)
|
37
|
+
|
38
|
+
# AND its raw data should be what we expect
|
39
|
+
workflow_raw_data = serialized_workflow["workflow_raw_data"]
|
40
|
+
tool_calling_node = workflow_raw_data["nodes"][2]
|
41
|
+
|
42
|
+
attributes = tool_calling_node["attributes"]
|
43
|
+
function_attributes = next(attribute for attribute in attributes if attribute["name"] == "functions")
|
44
|
+
assert function_attributes == {
|
45
|
+
"id": "cec9f5f2-7bb0-42e4-9c56-f215f07c5569",
|
46
|
+
"name": "functions",
|
47
|
+
"value": {
|
48
|
+
"type": "CONSTANT_VALUE",
|
49
|
+
"value": {
|
50
|
+
"type": "JSON",
|
51
|
+
"value": [
|
52
|
+
{
|
53
|
+
"type": "CODE_EXECUTION",
|
54
|
+
"name": "get_string",
|
55
|
+
"description": "\n Get a string with the parent input, dummy input, and the populated input.\n ", # noqa: E501
|
56
|
+
"definition": {
|
57
|
+
"state": None,
|
58
|
+
"cache_config": None,
|
59
|
+
"name": "get_string",
|
60
|
+
"description": "\n Get a string with the parent input, dummy input, and the populated input.\n ", # noqa: E501
|
61
|
+
"parameters": {
|
62
|
+
"type": "object",
|
63
|
+
"properties": {"populated_input": {"type": "string"}},
|
64
|
+
"required": ["populated_input"],
|
65
|
+
},
|
66
|
+
"inputs": {
|
67
|
+
"parent_input": {
|
68
|
+
"type": "WORKFLOW_INPUT",
|
69
|
+
"input_variable_id": "4bf1f0e7-76c6-4204-9f8c-bd9c3b73a8db",
|
70
|
+
},
|
71
|
+
"dummy_input": {
|
72
|
+
"type": "NODE_OUTPUT",
|
73
|
+
"node_id": "8e89ae10-a709-45ec-89f8-242f92e4a83f",
|
74
|
+
"node_output_id": "0cd09f0a-c142-4d5d-acc7-b93cd30ca58d",
|
75
|
+
},
|
76
|
+
},
|
77
|
+
"forced": None,
|
78
|
+
"strict": None,
|
79
|
+
},
|
80
|
+
"src": 'from vellum.workflows.utils.functions import use_tool_inputs\n\nfrom .inputs import ParentInputs\nfrom .nodes.dummy_node import DummyNode\n\n\n@use_tool_inputs(\n parent_input=ParentInputs.parent_input,\n dummy_input=DummyNode.Outputs.text,\n)\ndef get_string(parent_input: str, dummy_input: str, populated_input: str) -> str:\n """\n Get a string with the parent input, dummy input, and the populated input.\n """\n return f"This is the parent input: {parent_input}, this is the dummy input: {dummy_input}, and this is the populated input: {populated_input}" # noqa: E501\n', # noqa: E501
|
81
|
+
}
|
82
|
+
],
|
83
|
+
},
|
84
|
+
},
|
85
|
+
}
|
@@ -0,0 +1,88 @@
|
|
1
|
+
from typing import List
|
2
|
+
|
3
|
+
from vellum.workflows import BaseWorkflow
|
4
|
+
from vellum.workflows.inputs.base import BaseInputs
|
5
|
+
from vellum.workflows.nodes import MapNode
|
6
|
+
from vellum.workflows.nodes.bases import BaseNode
|
7
|
+
from vellum.workflows.nodes.displayable import FinalOutputNode
|
8
|
+
from vellum.workflows.outputs import BaseOutputs
|
9
|
+
from vellum.workflows.state import BaseState
|
10
|
+
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
11
|
+
|
12
|
+
|
13
|
+
class TestInputs(BaseInputs):
|
14
|
+
items: List[str]
|
15
|
+
|
16
|
+
|
17
|
+
class TestIteration(BaseNode):
|
18
|
+
item = MapNode.SubworkflowInputs.item
|
19
|
+
index = MapNode.SubworkflowInputs.index
|
20
|
+
|
21
|
+
class Outputs(BaseOutputs):
|
22
|
+
processed: str
|
23
|
+
|
24
|
+
def run(self) -> Outputs:
|
25
|
+
return self.Outputs(processed=f"processed_{self.item}_{self.index}")
|
26
|
+
|
27
|
+
|
28
|
+
class TestIterationSubworkflow(BaseWorkflow[MapNode.SubworkflowInputs, BaseState]):
|
29
|
+
graph = TestIteration
|
30
|
+
|
31
|
+
class Outputs(BaseOutputs):
|
32
|
+
processed = TestIteration.Outputs.processed
|
33
|
+
|
34
|
+
|
35
|
+
class TestMapNode(MapNode):
|
36
|
+
items = TestInputs.items
|
37
|
+
subworkflow = TestIterationSubworkflow
|
38
|
+
|
39
|
+
|
40
|
+
class TestFinalOutputNode(FinalOutputNode[BaseState, List[str]]):
|
41
|
+
class Outputs(FinalOutputNode.Outputs):
|
42
|
+
value = TestMapNode.Outputs.processed
|
43
|
+
|
44
|
+
|
45
|
+
class TestWorkflowWithFinalOutputReferencingMap(BaseWorkflow[TestInputs, BaseState]):
|
46
|
+
graph = TestMapNode >> TestFinalOutputNode
|
47
|
+
|
48
|
+
class Outputs(BaseOutputs):
|
49
|
+
final_result = TestFinalOutputNode.Outputs.value
|
50
|
+
|
51
|
+
|
52
|
+
def test_serialize_workflow__final_output_node_referencing_map_node():
|
53
|
+
"""
|
54
|
+
Test that final output nodes referencing map node outputs have correct outputs structure.
|
55
|
+
|
56
|
+
This test verifies that when a FinalOutputNode references a MapNode output,
|
57
|
+
the serialized output contains proper NODE_OUTPUT references instead of None values.
|
58
|
+
This addresses the Agent Builder issue where final outputs showed value=None in the UI.
|
59
|
+
"""
|
60
|
+
workflow_display = get_workflow_display(workflow_class=TestWorkflowWithFinalOutputReferencingMap)
|
61
|
+
|
62
|
+
# WHEN we serialize it
|
63
|
+
serialized_workflow: dict = workflow_display.serialize()
|
64
|
+
|
65
|
+
# THEN the final output node should have the correct outputs structure
|
66
|
+
workflow_raw_data = serialized_workflow["workflow_raw_data"]
|
67
|
+
map_node = next(node for node in workflow_raw_data["nodes"] if node["type"] == "MAP")
|
68
|
+
final_output_node = next(node for node in workflow_raw_data["nodes"] if node["type"] == "TERMINAL")
|
69
|
+
|
70
|
+
# AND the map node's subworkflow should have the one output variable
|
71
|
+
output_variable = next(iter(map_node["data"]["output_variables"]))
|
72
|
+
map_node_output_id = output_variable["id"]
|
73
|
+
|
74
|
+
# AND the final output node should have an outputs array with proper structure
|
75
|
+
assert "outputs" in final_output_node
|
76
|
+
outputs = final_output_node["outputs"]
|
77
|
+
assert len(outputs) == 1
|
78
|
+
|
79
|
+
output = outputs[0]
|
80
|
+
# AND the output should have the correct structure with NODE_OUTPUT reference instead of None
|
81
|
+
assert output["name"] == "value"
|
82
|
+
assert output["type"] == "JSON"
|
83
|
+
|
84
|
+
# AND the value should be a NODE_OUTPUT reference, not None
|
85
|
+
assert output["value"] is not None, f"Expected NODE_OUTPUT reference but got None. Full output: {output}"
|
86
|
+
assert output["value"]["type"] == "NODE_OUTPUT", f"Expected NODE_OUTPUT type but got {output['value']['type']}"
|
87
|
+
assert "node_id" in output["value"], f"Missing node_id in output value: {output['value']}"
|
88
|
+
assert output["value"]["node_output_id"] == map_node_output_id
|
@@ -6,6 +6,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, cast
|
|
6
6
|
|
7
7
|
from pydantic import BaseModel
|
8
8
|
|
9
|
+
from vellum.client.types.function_definition import FunctionDefinition
|
9
10
|
from vellum.client.types.logical_operator import LogicalOperator
|
10
11
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
11
12
|
from vellum.workflows.expressions.accessor import AccessorExpression
|
@@ -422,6 +423,17 @@ def serialize_value(display_context: "WorkflowDisplayContext", value: Any) -> Js
|
|
422
423
|
|
423
424
|
if callable(value):
|
424
425
|
function_definition = compile_function_definition(value)
|
426
|
+
inputs = getattr(value, "__vellum_inputs__", {})
|
427
|
+
|
428
|
+
if inputs:
|
429
|
+
serialized_inputs = {}
|
430
|
+
for param_name, input_ref in inputs.items():
|
431
|
+
serialized_inputs[param_name] = serialize_value(display_context, input_ref)
|
432
|
+
|
433
|
+
model_data = function_definition.model_dump()
|
434
|
+
model_data["inputs"] = serialized_inputs
|
435
|
+
function_definition = FunctionDefinition.model_validate(model_data)
|
436
|
+
|
425
437
|
source_path = inspect.getsourcefile(value)
|
426
438
|
if source_path is not None:
|
427
439
|
with virtual_open(source_path) as f:
|
@@ -102,7 +102,9 @@ def create_node_input_value_pointer_rule(
|
|
102
102
|
if isinstance(value, OutputReference):
|
103
103
|
if value not in display_context.global_node_output_displays:
|
104
104
|
if issubclass(value.outputs_class, BaseNode.Outputs):
|
105
|
-
raise ValueError(
|
105
|
+
raise ValueError(
|
106
|
+
f"Reference to node '{value.outputs_class.__parent_class__.__name__}' not found in graph."
|
107
|
+
)
|
106
108
|
|
107
109
|
raise ValueError(f"Reference to outputs '{value.outputs_class.__qualname__}' is invalid.")
|
108
110
|
|
@@ -294,7 +294,7 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
|
|
294
294
|
)
|
295
295
|
|
296
296
|
elif isinstance(workflow_output.instance, OutputReference):
|
297
|
-
terminal_node_id = workflow_output.instance.outputs_class.
|
297
|
+
terminal_node_id = workflow_output.instance.outputs_class.__parent_class__.__id__
|
298
298
|
serialized_terminal_node = serialized_nodes.get(terminal_node_id)
|
299
299
|
if serialized_terminal_node and isinstance(serialized_terminal_node["data"], dict):
|
300
300
|
serialized_terminal_node["data"]["name"] = workflow_output_display.name
|
@@ -599,7 +599,7 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
|
|
599
599
|
|
600
600
|
workflow_output_display = self.output_displays.get(workflow_output)
|
601
601
|
workflow_output_displays[workflow_output] = (
|
602
|
-
workflow_output_display or self._generate_workflow_output_display(workflow_output)
|
602
|
+
workflow_output_display or self._generate_workflow_output_display(workflow_output, self._workflow)
|
603
603
|
)
|
604
604
|
|
605
605
|
return WorkflowDisplayContext(
|
@@ -688,9 +688,13 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
|
|
688
688
|
|
689
689
|
return EntrypointDisplay(id=entrypoint_id, edge_display=edge_display)
|
690
690
|
|
691
|
-
def _generate_workflow_output_display(
|
692
|
-
|
693
|
-
|
691
|
+
def _generate_workflow_output_display(
|
692
|
+
self, output: OutputReference, workflow_class: Type[BaseWorkflow]
|
693
|
+
) -> WorkflowOutputDisplay:
|
694
|
+
# TODO: use the output.id field instead once we add `__parent_class__` to BaseWorkflow.Outputs
|
695
|
+
output_id = workflow_class.__output_ids__.get(output.name) or uuid4_from_hash(
|
696
|
+
f"{self.workflow_id}|id|{output.name}"
|
697
|
+
)
|
694
698
|
return WorkflowOutputDisplay(id=output_id, name=output.name)
|
695
699
|
|
696
700
|
def __init_subclass__(cls, **kwargs: Any) -> None:
|
File without changes
|
File without changes
|
File without changes
|