vellum-ai 0.14.68__py3-none-any.whl → 0.14.70__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vellum/__init__.py +4 -0
- vellum/client/core/client_wrapper.py +1 -1
- vellum/client/types/__init__.py +4 -0
- vellum/client/types/fast_embed_vectorizer_baai_bge_small_en_v_15.py +23 -0
- vellum/client/types/fast_embed_vectorizer_baai_bge_small_en_v_15_request.py +23 -0
- vellum/client/types/folder_entity_document_index_data.py +2 -0
- vellum/client/types/indexing_config_vectorizer.py +2 -0
- vellum/client/types/indexing_config_vectorizer_request.py +2 -0
- vellum/types/fast_embed_vectorizer_baai_bge_small_en_v_15.py +3 -0
- vellum/types/fast_embed_vectorizer_baai_bge_small_en_v_15_request.py +3 -0
- vellum/workflows/environment/__init__.py +2 -1
- vellum/workflows/environment/environment.py +5 -1
- vellum/workflows/nodes/displayable/bases/search_node.py +15 -3
- vellum/workflows/nodes/displayable/tests/test_search_node_error_handling.py +215 -0
- vellum/workflows/nodes/experimental/tool_calling_node/tests/test_node.py +77 -1
- vellum/workflows/nodes/experimental/tool_calling_node/utils.py +2 -2
- vellum/workflows/references/environment_variable.py +2 -3
- {vellum_ai-0.14.68.dist-info → vellum_ai-0.14.70.dist-info}/METADATA +1 -1
- {vellum_ai-0.14.68.dist-info → vellum_ai-0.14.70.dist-info}/RECORD +54 -45
- vellum_cli/__init__.py +5 -2
- vellum_cli/image_push.py +24 -1
- vellum_cli/tests/test_image_push.py +103 -12
- vellum_ee/workflows/display/nodes/base_node_display.py +1 -1
- vellum_ee/workflows/display/nodes/utils.py +2 -2
- vellum_ee/workflows/display/nodes/vellum/api_node.py +2 -2
- vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/conditional_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/error_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/final_output_node.py +2 -2
- vellum_ee/workflows/display/nodes/vellum/guardrail_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +4 -4
- vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +9 -1
- vellum_ee/workflows/display/nodes/vellum/map_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/merge_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/note_node.py +1 -0
- vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/retry_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/search_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/templating_node.py +1 -1
- vellum_ee/workflows/display/nodes/vellum/tests/test_inline_subworkflow_node.py +88 -0
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +16 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py +81 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +9 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +59 -297
- vellum_ee/workflows/display/utils/auto_layout.py +130 -0
- vellum_ee/workflows/display/utils/expressions.py +7 -0
- vellum_ee/workflows/display/utils/tests/__init__.py +0 -0
- vellum_ee/workflows/display/utils/tests/test_auto_layout.py +56 -0
- vellum_ee/workflows/display/workflows/base_workflow_display.py +15 -10
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +41 -0
- {vellum_ai-0.14.68.dist-info → vellum_ai-0.14.70.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.68.dist-info → vellum_ai-0.14.70.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.68.dist-info → vellum_ai-0.14.70.dist-info}/entry_points.txt +0 -0
@@ -2,8 +2,10 @@ from uuid import UUID
|
|
2
2
|
from typing import ClassVar, Dict, Generic, List, Optional, Tuple, Type, TypeVar
|
3
3
|
|
4
4
|
from vellum import VellumVariable
|
5
|
+
from vellum.workflows.constants import undefined
|
5
6
|
from vellum.workflows.inputs.base import BaseInputs
|
6
7
|
from vellum.workflows.nodes import InlineSubworkflowNode
|
8
|
+
from vellum.workflows.nodes.displayable.bases.utils import primitive_to_vellum_value
|
7
9
|
from vellum.workflows.types.core import JsonObject
|
8
10
|
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
9
11
|
from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
|
@@ -24,7 +26,7 @@ class BaseInlineSubworkflowNodeDisplay(
|
|
24
26
|
__serializable_inputs__ = {InlineSubworkflowNode.subworkflow_inputs}
|
25
27
|
|
26
28
|
def serialize(
|
27
|
-
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **
|
29
|
+
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **_kwargs
|
28
30
|
) -> JsonObject:
|
29
31
|
node = self._node
|
30
32
|
node_id = self.node_id
|
@@ -100,6 +102,12 @@ class BaseInlineSubworkflowNodeDisplay(
|
|
100
102
|
id=node_inputs_by_key[descriptor.name].id,
|
101
103
|
key=descriptor.name,
|
102
104
|
type=infer_vellum_variable_type(descriptor),
|
105
|
+
required=descriptor.instance is undefined,
|
106
|
+
default=(
|
107
|
+
primitive_to_vellum_value(descriptor.instance).dict()
|
108
|
+
if descriptor.instance is not undefined
|
109
|
+
else None
|
110
|
+
),
|
103
111
|
)
|
104
112
|
for descriptor in subworkflow_inputs_class
|
105
113
|
]
|
@@ -17,7 +17,7 @@ class BaseMapNodeDisplay(BaseAdornmentNodeDisplay[_MapNodeType], Generic[_MapNod
|
|
17
17
|
__serializable_inputs__ = {MapNode.items} # type: ignore[misc]
|
18
18
|
|
19
19
|
def serialize(
|
20
|
-
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **
|
20
|
+
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **_kwargs
|
21
21
|
) -> JsonObject:
|
22
22
|
node = self._node
|
23
23
|
node_id = self.node_id
|
@@ -17,7 +17,7 @@ class BaseMergeNodeDisplay(BaseNodeDisplay[_MergeNodeType], Generic[_MergeNodeTy
|
|
17
17
|
super().__init__()
|
18
18
|
self._target_handle_iterator = 0
|
19
19
|
|
20
|
-
def serialize(self, display_context: WorkflowDisplayContext, **
|
20
|
+
def serialize(self, display_context: WorkflowDisplayContext, **_kwargs: Any) -> JsonObject:
|
21
21
|
node = self._node
|
22
22
|
node_id = self.node_id
|
23
23
|
|
@@ -13,6 +13,7 @@ class BaseNoteNodeDisplay(BaseNodeDisplay[_NoteNodeType], Generic[_NoteNodeType]
|
|
13
13
|
style: ClassVar[Union[Dict[str, Any], None]] = None
|
14
14
|
|
15
15
|
def serialize(self, display_context: WorkflowDisplayContext, **kwargs: Any) -> JsonObject:
|
16
|
+
del display_context, kwargs # Unused parameters
|
16
17
|
node_id = self.node_id
|
17
18
|
|
18
19
|
return {
|
@@ -15,7 +15,7 @@ class BasePromptDeploymentNodeDisplay(BaseNodeDisplay[_PromptDeploymentNodeType]
|
|
15
15
|
__serializable_inputs__ = {PromptDeploymentNode.prompt_inputs}
|
16
16
|
|
17
17
|
def serialize(
|
18
|
-
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **
|
18
|
+
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **_kwargs
|
19
19
|
) -> JsonObject:
|
20
20
|
node = self._node
|
21
21
|
node_id = self.node_id
|
@@ -18,7 +18,7 @@ _RetryNodeType = TypeVar("_RetryNodeType", bound=RetryNode)
|
|
18
18
|
|
19
19
|
|
20
20
|
class BaseRetryNodeDisplay(BaseAdornmentNodeDisplay[_RetryNodeType], Generic[_RetryNodeType]):
|
21
|
-
def serialize(self, display_context: WorkflowDisplayContext, **
|
21
|
+
def serialize(self, display_context: WorkflowDisplayContext, **_kwargs: Any) -> JsonObject:
|
22
22
|
node = self._node
|
23
23
|
node_id = self.node_id
|
24
24
|
|
@@ -42,7 +42,7 @@ class BaseSearchNodeDisplay(BaseNodeDisplay[_SearchNodeType], Generic[_SearchNod
|
|
42
42
|
}
|
43
43
|
|
44
44
|
def serialize(
|
45
|
-
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **
|
45
|
+
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **_kwargs
|
46
46
|
) -> JsonObject:
|
47
47
|
node = self._node
|
48
48
|
node_id = self.node_id
|
@@ -17,7 +17,7 @@ class BaseSubworkflowDeploymentNodeDisplay(
|
|
17
17
|
__serializable_inputs__ = {SubworkflowDeploymentNode.subworkflow_inputs}
|
18
18
|
|
19
19
|
def serialize(
|
20
|
-
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **
|
20
|
+
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **_kwargs
|
21
21
|
) -> JsonObject:
|
22
22
|
node = self._node
|
23
23
|
node_id = self.node_id
|
@@ -18,7 +18,7 @@ class BaseTemplatingNodeDisplay(BaseNodeDisplay[_TemplatingNodeType], Generic[_T
|
|
18
18
|
__serializable_inputs__ = {TemplatingNode.inputs}
|
19
19
|
|
20
20
|
def serialize(
|
21
|
-
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **
|
21
|
+
self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **_kwargs
|
22
22
|
) -> JsonObject:
|
23
23
|
node = self._node
|
24
24
|
node_id = self.node_id
|
@@ -0,0 +1,88 @@
|
|
1
|
+
from typing import Optional
|
2
|
+
|
3
|
+
from vellum.workflows import BaseWorkflow
|
4
|
+
from vellum.workflows.inputs.base import BaseInputs
|
5
|
+
from vellum.workflows.nodes import InlineSubworkflowNode
|
6
|
+
from vellum.workflows.nodes.bases import BaseNode
|
7
|
+
from vellum.workflows.state.base import BaseState
|
8
|
+
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
9
|
+
|
10
|
+
|
11
|
+
def test_serialize_node__inline_subworkflow_inputs():
|
12
|
+
# GIVEN a main workflow with inputs
|
13
|
+
class MainInputs(BaseInputs):
|
14
|
+
pass
|
15
|
+
|
16
|
+
# AND an inline subworkflow with inputs
|
17
|
+
class NestedInputs(BaseInputs):
|
18
|
+
input: str
|
19
|
+
input_with_default: str = "default"
|
20
|
+
optional_input: Optional[str] = None
|
21
|
+
optional_input_with_default: Optional[str] = "optional_default"
|
22
|
+
|
23
|
+
class NestedNode(BaseNode):
|
24
|
+
input = NestedInputs.input
|
25
|
+
input_with_default = NestedInputs.input_with_default
|
26
|
+
|
27
|
+
class Outputs(BaseNode.Outputs):
|
28
|
+
result: str
|
29
|
+
|
30
|
+
def run(self) -> Outputs:
|
31
|
+
return self.Outputs(result=f"{self.input}-{self.input_with_default}")
|
32
|
+
|
33
|
+
class NestedWorkflow(BaseWorkflow[NestedInputs, BaseState]):
|
34
|
+
graph = NestedNode
|
35
|
+
|
36
|
+
class Outputs(BaseWorkflow.Outputs):
|
37
|
+
result = NestedNode.Outputs.result
|
38
|
+
|
39
|
+
class MyInlineSubworkflowNode(InlineSubworkflowNode):
|
40
|
+
subworkflow_inputs = {
|
41
|
+
"input": "input",
|
42
|
+
"input_with_default": "input_with_default",
|
43
|
+
"optional_input": "optional_input",
|
44
|
+
"optional_input_with_default": "optional_input_with_default",
|
45
|
+
}
|
46
|
+
subworkflow = NestedWorkflow
|
47
|
+
|
48
|
+
class MainWorkflow(BaseWorkflow[MainInputs, BaseState]):
|
49
|
+
graph = MyInlineSubworkflowNode
|
50
|
+
|
51
|
+
class Outputs(BaseWorkflow.Outputs):
|
52
|
+
result = MyInlineSubworkflowNode.Outputs.result
|
53
|
+
|
54
|
+
# WHEN the workflow is serialized
|
55
|
+
workflow_display = get_workflow_display(workflow_class=MainWorkflow)
|
56
|
+
serialized_workflow: dict = workflow_display.serialize()
|
57
|
+
|
58
|
+
# THEN the inline subworkflow node should have the correct input variables
|
59
|
+
inline_subworkflow_node = next(
|
60
|
+
node
|
61
|
+
for node in serialized_workflow["workflow_raw_data"]["nodes"]
|
62
|
+
if node["id"] == str(MyInlineSubworkflowNode.__id__)
|
63
|
+
)
|
64
|
+
|
65
|
+
input_variables = inline_subworkflow_node["data"]["input_variables"]
|
66
|
+
assert len(input_variables) == 4
|
67
|
+
|
68
|
+
input_var = next(var for var in input_variables if var["key"] == "input")
|
69
|
+
assert input_var["required"] is True
|
70
|
+
assert input_var["default"] is None
|
71
|
+
assert input_var["type"] == "STRING"
|
72
|
+
|
73
|
+
input_with_default_var = next(var for var in input_variables if var["key"] == "input_with_default")
|
74
|
+
assert input_with_default_var["required"] is False
|
75
|
+
assert input_with_default_var["default"] == {"type": "STRING", "value": "default"}
|
76
|
+
assert input_with_default_var["type"] == "STRING"
|
77
|
+
|
78
|
+
optional_input_var = next(var for var in input_variables if var["key"] == "optional_input")
|
79
|
+
assert optional_input_var["required"] is False
|
80
|
+
assert optional_input_var["default"] == {"type": "JSON", "value": None}
|
81
|
+
assert optional_input_var["type"] == "STRING"
|
82
|
+
|
83
|
+
optional_input_with_default_var = next(
|
84
|
+
var for var in input_variables if var["key"] == "optional_input_with_default"
|
85
|
+
)
|
86
|
+
assert optional_input_with_default_var["required"] is False
|
87
|
+
assert optional_input_with_default_var["default"] == {"type": "STRING", "value": "optional_default"}
|
88
|
+
assert optional_input_with_default_var["type"] == "STRING"
|
@@ -7,6 +7,7 @@ from vellum.client.types.chat_message import ChatMessage
|
|
7
7
|
from vellum.workflows.inputs.base import BaseInputs
|
8
8
|
from vellum.workflows.nodes.bases.base import BaseNode
|
9
9
|
from vellum.workflows.references.constant import ConstantValueReference
|
10
|
+
from vellum.workflows.references.environment_variable import EnvironmentVariableReference
|
10
11
|
from vellum.workflows.references.lazy import LazyReference
|
11
12
|
from vellum.workflows.references.vellum_secret import VellumSecretReference
|
12
13
|
from vellum.workflows.state.base import BaseState
|
@@ -475,6 +476,21 @@ def test_serialize_node__node_execution(serialize_node):
|
|
475
476
|
)
|
476
477
|
|
477
478
|
|
479
|
+
def test_serialize_node__environment_variable(serialize_node):
|
480
|
+
class EnvironmentVariableGenericNode(BaseNode):
|
481
|
+
attr = EnvironmentVariableReference(name="API_KEY")
|
482
|
+
|
483
|
+
serialized_node = serialize_node(EnvironmentVariableGenericNode)
|
484
|
+
|
485
|
+
expected_value = {
|
486
|
+
"type": "ENVIRONMENT_VARIABLE",
|
487
|
+
"environment_variable": "API_KEY",
|
488
|
+
}
|
489
|
+
|
490
|
+
actual_value = serialized_node["attributes"][0]["value"]
|
491
|
+
assert actual_value == expected_value
|
492
|
+
|
493
|
+
|
478
494
|
def test_serialize_node__coalesce(serialize_node):
|
479
495
|
class CoalesceNodeA(BaseNode):
|
480
496
|
class Outputs(BaseNode.Outputs):
|
@@ -1,5 +1,11 @@
|
|
1
1
|
from deepdiff import DeepDiff
|
2
2
|
|
3
|
+
from vellum import ChatMessagePromptBlock, FunctionDefinition, JinjaPromptBlock
|
4
|
+
from vellum.workflows import BaseWorkflow
|
5
|
+
from vellum.workflows.inputs import BaseInputs
|
6
|
+
from vellum.workflows.nodes import InlinePromptNode
|
7
|
+
from vellum.workflows.nodes.bases.base import BaseNode
|
8
|
+
from vellum.workflows.state import BaseState
|
3
9
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
4
10
|
|
5
11
|
from tests.workflows.basic_inline_prompt_node_with_functions.workflow import BasicInlinePromptWithFunctionsWorkflow
|
@@ -185,6 +191,27 @@ def test_serialize_workflow():
|
|
185
191
|
],
|
186
192
|
},
|
187
193
|
},
|
194
|
+
{
|
195
|
+
"id": "8107682b-2ca0-4967-88f9-284455936575",
|
196
|
+
"name": "functions",
|
197
|
+
"value": {
|
198
|
+
"type": "CONSTANT_VALUE",
|
199
|
+
"value": {
|
200
|
+
"type": "JSON",
|
201
|
+
"value": [
|
202
|
+
{
|
203
|
+
"state": None,
|
204
|
+
"cache_config": None,
|
205
|
+
"name": "favorite_noun",
|
206
|
+
"description": "Returns the favorite noun of the user",
|
207
|
+
"parameters": {},
|
208
|
+
"forced": None,
|
209
|
+
"strict": None,
|
210
|
+
}
|
211
|
+
],
|
212
|
+
},
|
213
|
+
},
|
214
|
+
},
|
188
215
|
],
|
189
216
|
},
|
190
217
|
prompt_node,
|
@@ -274,3 +301,57 @@ def test_serialize_workflow():
|
|
274
301
|
"name": "BasicInlinePromptWithFunctionsWorkflow",
|
275
302
|
"module": ["tests", "workflows", "basic_inline_prompt_node_with_functions", "workflow"],
|
276
303
|
}
|
304
|
+
|
305
|
+
|
306
|
+
def test_serialize_workflow_with_descriptor_functions():
|
307
|
+
"""Test that serialization handles BaseDescriptor instances in functions list."""
|
308
|
+
|
309
|
+
class TestInputs(BaseInputs):
|
310
|
+
noun: str
|
311
|
+
|
312
|
+
class MockMCPClientNode(BaseNode):
|
313
|
+
class Outputs(BaseNode.Outputs):
|
314
|
+
tools: list[FunctionDefinition]
|
315
|
+
|
316
|
+
class TestInlinePromptNodeWithDescriptorFunctions(InlinePromptNode):
|
317
|
+
ml_model = "gpt-4o"
|
318
|
+
blocks = [
|
319
|
+
ChatMessagePromptBlock(
|
320
|
+
chat_role="SYSTEM",
|
321
|
+
blocks=[JinjaPromptBlock(template="Test {{noun}}")],
|
322
|
+
),
|
323
|
+
]
|
324
|
+
prompt_inputs = {"noun": TestInputs.noun}
|
325
|
+
functions = MockMCPClientNode.Outputs.tools # type: ignore
|
326
|
+
|
327
|
+
class TestWorkflow(BaseWorkflow[TestInputs, BaseState]):
|
328
|
+
graph = MockMCPClientNode >> TestInlinePromptNodeWithDescriptorFunctions
|
329
|
+
|
330
|
+
workflow_display = get_workflow_display(workflow_class=TestWorkflow)
|
331
|
+
serialized: dict = workflow_display.serialize()
|
332
|
+
|
333
|
+
prompt_nodes = [node for node in serialized["workflow_raw_data"]["nodes"] if node["type"] == "PROMPT"]
|
334
|
+
assert len(prompt_nodes) == 1
|
335
|
+
|
336
|
+
prompt_node = prompt_nodes[0]
|
337
|
+
assert isinstance(prompt_node, dict)
|
338
|
+
blocks = prompt_node["data"]["exec_config"]["prompt_template_block_data"]["blocks"]
|
339
|
+
assert isinstance(blocks, list)
|
340
|
+
|
341
|
+
function_blocks = [
|
342
|
+
block for block in blocks if isinstance(block, dict) and block.get("block_type") == "FUNCTION_DEFINITION"
|
343
|
+
]
|
344
|
+
assert len(function_blocks) == 0 # We don't serialize the legacy function blocks when dynamic
|
345
|
+
|
346
|
+
assert "attributes" in prompt_node
|
347
|
+
assert isinstance(prompt_node["attributes"], list)
|
348
|
+
functions_attr = next(
|
349
|
+
(attr for attr in prompt_node["attributes"] if isinstance(attr, dict) and attr["name"] == "functions"), None
|
350
|
+
)
|
351
|
+
assert isinstance(functions_attr, dict), "functions attribute should be present in serialized attributes"
|
352
|
+
|
353
|
+
assert functions_attr["value"] == {
|
354
|
+
"node_id": "cb1186e0-8ff1-4145-823e-96b3fc05a39a",
|
355
|
+
"node_output_id": "470fadb9-b8b5-477e-a502-5209d398bcf9",
|
356
|
+
"type": "NODE_OUTPUT",
|
357
|
+
}
|
@@ -301,7 +301,15 @@ def test_serialize_workflow():
|
|
301
301
|
},
|
302
302
|
],
|
303
303
|
},
|
304
|
-
"input_variables": [
|
304
|
+
"input_variables": [
|
305
|
+
{
|
306
|
+
"id": "704c4640-bfda-44f0-8da3-e9cfc4f21cf2",
|
307
|
+
"key": "metro",
|
308
|
+
"type": "STRING",
|
309
|
+
"required": True,
|
310
|
+
"default": None,
|
311
|
+
}
|
312
|
+
],
|
305
313
|
"output_variables": [
|
306
314
|
{"id": "2fc57139-7420-49e5-96a6-dcbb3ff5d622", "key": "temperature", "type": "NUMBER"},
|
307
315
|
{"id": "fad5dd9f-3328-4e70-ad55-65a5325a4a82", "key": "reasoning", "type": "STRING"},
|