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.
Files changed (54) hide show
  1. vellum/__init__.py +4 -0
  2. vellum/client/core/client_wrapper.py +1 -1
  3. vellum/client/types/__init__.py +4 -0
  4. vellum/client/types/fast_embed_vectorizer_baai_bge_small_en_v_15.py +23 -0
  5. vellum/client/types/fast_embed_vectorizer_baai_bge_small_en_v_15_request.py +23 -0
  6. vellum/client/types/folder_entity_document_index_data.py +2 -0
  7. vellum/client/types/indexing_config_vectorizer.py +2 -0
  8. vellum/client/types/indexing_config_vectorizer_request.py +2 -0
  9. vellum/types/fast_embed_vectorizer_baai_bge_small_en_v_15.py +3 -0
  10. vellum/types/fast_embed_vectorizer_baai_bge_small_en_v_15_request.py +3 -0
  11. vellum/workflows/environment/__init__.py +2 -1
  12. vellum/workflows/environment/environment.py +5 -1
  13. vellum/workflows/nodes/displayable/bases/search_node.py +15 -3
  14. vellum/workflows/nodes/displayable/tests/test_search_node_error_handling.py +215 -0
  15. vellum/workflows/nodes/experimental/tool_calling_node/tests/test_node.py +77 -1
  16. vellum/workflows/nodes/experimental/tool_calling_node/utils.py +2 -2
  17. vellum/workflows/references/environment_variable.py +2 -3
  18. {vellum_ai-0.14.68.dist-info → vellum_ai-0.14.70.dist-info}/METADATA +1 -1
  19. {vellum_ai-0.14.68.dist-info → vellum_ai-0.14.70.dist-info}/RECORD +54 -45
  20. vellum_cli/__init__.py +5 -2
  21. vellum_cli/image_push.py +24 -1
  22. vellum_cli/tests/test_image_push.py +103 -12
  23. vellum_ee/workflows/display/nodes/base_node_display.py +1 -1
  24. vellum_ee/workflows/display/nodes/utils.py +2 -2
  25. vellum_ee/workflows/display/nodes/vellum/api_node.py +2 -2
  26. vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +1 -1
  27. vellum_ee/workflows/display/nodes/vellum/conditional_node.py +1 -1
  28. vellum_ee/workflows/display/nodes/vellum/error_node.py +1 -1
  29. vellum_ee/workflows/display/nodes/vellum/final_output_node.py +2 -2
  30. vellum_ee/workflows/display/nodes/vellum/guardrail_node.py +1 -1
  31. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +4 -4
  32. vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +9 -1
  33. vellum_ee/workflows/display/nodes/vellum/map_node.py +1 -1
  34. vellum_ee/workflows/display/nodes/vellum/merge_node.py +1 -1
  35. vellum_ee/workflows/display/nodes/vellum/note_node.py +1 -0
  36. vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +1 -1
  37. vellum_ee/workflows/display/nodes/vellum/retry_node.py +1 -1
  38. vellum_ee/workflows/display/nodes/vellum/search_node.py +1 -1
  39. vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +1 -1
  40. vellum_ee/workflows/display/nodes/vellum/templating_node.py +1 -1
  41. vellum_ee/workflows/display/nodes/vellum/tests/test_inline_subworkflow_node.py +88 -0
  42. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +16 -0
  43. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py +81 -0
  44. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +9 -1
  45. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +59 -297
  46. vellum_ee/workflows/display/utils/auto_layout.py +130 -0
  47. vellum_ee/workflows/display/utils/expressions.py +7 -0
  48. vellum_ee/workflows/display/utils/tests/__init__.py +0 -0
  49. vellum_ee/workflows/display/utils/tests/test_auto_layout.py +56 -0
  50. vellum_ee/workflows/display/workflows/base_workflow_display.py +15 -10
  51. vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +41 -0
  52. {vellum_ai-0.14.68.dist-info → vellum_ai-0.14.70.dist-info}/LICENSE +0 -0
  53. {vellum_ai-0.14.68.dist-info → vellum_ai-0.14.70.dist-info}/WHEEL +0 -0
  54. {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, **kwargs
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, **kwargs
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, **kwargs: Any) -> JsonObject:
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, **kwargs
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, **kwargs: Any) -> JsonObject:
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, **kwargs
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, **kwargs
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, **kwargs
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": [{"id": "704c4640-bfda-44f0-8da3-e9cfc4f21cf2", "key": "metro", "type": "STRING"}],
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"},