vellum-ai 0.14.69__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 (42) hide show
  1. vellum/client/core/client_wrapper.py +1 -1
  2. vellum/workflows/environment/__init__.py +2 -1
  3. vellum/workflows/environment/environment.py +5 -1
  4. vellum/workflows/nodes/experimental/tool_calling_node/tests/test_node.py +77 -1
  5. vellum/workflows/nodes/experimental/tool_calling_node/utils.py +2 -2
  6. vellum/workflows/references/environment_variable.py +2 -3
  7. {vellum_ai-0.14.69.dist-info → vellum_ai-0.14.70.dist-info}/METADATA +1 -1
  8. {vellum_ai-0.14.69.dist-info → vellum_ai-0.14.70.dist-info}/RECORD +42 -38
  9. vellum_cli/__init__.py +5 -2
  10. vellum_cli/image_push.py +24 -1
  11. vellum_cli/tests/test_image_push.py +103 -12
  12. vellum_ee/workflows/display/nodes/base_node_display.py +1 -1
  13. vellum_ee/workflows/display/nodes/utils.py +2 -2
  14. vellum_ee/workflows/display/nodes/vellum/api_node.py +2 -2
  15. vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +1 -1
  16. vellum_ee/workflows/display/nodes/vellum/conditional_node.py +1 -1
  17. vellum_ee/workflows/display/nodes/vellum/error_node.py +1 -1
  18. vellum_ee/workflows/display/nodes/vellum/final_output_node.py +2 -2
  19. vellum_ee/workflows/display/nodes/vellum/guardrail_node.py +1 -1
  20. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +1 -1
  21. vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +9 -1
  22. vellum_ee/workflows/display/nodes/vellum/map_node.py +1 -1
  23. vellum_ee/workflows/display/nodes/vellum/merge_node.py +1 -1
  24. vellum_ee/workflows/display/nodes/vellum/note_node.py +1 -0
  25. vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +1 -1
  26. vellum_ee/workflows/display/nodes/vellum/retry_node.py +1 -1
  27. vellum_ee/workflows/display/nodes/vellum/search_node.py +1 -1
  28. vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +1 -1
  29. vellum_ee/workflows/display/nodes/vellum/templating_node.py +1 -1
  30. vellum_ee/workflows/display/nodes/vellum/tests/test_inline_subworkflow_node.py +88 -0
  31. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +16 -0
  32. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +9 -1
  33. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +59 -297
  34. vellum_ee/workflows/display/utils/auto_layout.py +130 -0
  35. vellum_ee/workflows/display/utils/expressions.py +7 -0
  36. vellum_ee/workflows/display/utils/tests/__init__.py +0 -0
  37. vellum_ee/workflows/display/utils/tests/test_auto_layout.py +56 -0
  38. vellum_ee/workflows/display/workflows/base_workflow_display.py +15 -10
  39. vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +41 -0
  40. {vellum_ai-0.14.69.dist-info → vellum_ai-0.14.70.dist-info}/LICENSE +0 -0
  41. {vellum_ai-0.14.69.dist-info → vellum_ai-0.14.70.dist-info}/WHEEL +0 -0
  42. {vellum_ai-0.14.69.dist-info → vellum_ai-0.14.70.dist-info}/entry_points.txt +0 -0
@@ -20,7 +20,7 @@ class BaseErrorNodeDisplay(BaseNodeDisplay[_ErrorNodeType], Generic[_ErrorNodeTy
20
20
 
21
21
  __serializable_inputs__ = {ErrorNode.error}
22
22
 
23
- def serialize(self, display_context: WorkflowDisplayContext, **kwargs) -> JsonObject:
23
+ def serialize(self, display_context: WorkflowDisplayContext, **_kwargs) -> JsonObject:
24
24
  node_id = self.node_id
25
25
  error_source_input_id = self.node_input_ids_by_name.get(
26
26
  ErrorNode.error.name,
@@ -1,5 +1,5 @@
1
1
  from uuid import UUID
2
- from typing import Any, ClassVar, Generic, Optional, TypeVar
2
+ from typing import ClassVar, Generic, Optional, TypeVar
3
3
 
4
4
  from vellum.workflows.nodes.displayable.final_output_node import FinalOutputNode
5
5
  from vellum.workflows.types.core import JsonObject
@@ -19,7 +19,7 @@ NODE_INPUT_KEY = "node_input"
19
19
  class BaseFinalOutputNodeDisplay(BaseNodeDisplay[_FinalOutputNodeType], Generic[_FinalOutputNodeType]):
20
20
  output_name: ClassVar[Optional[str]] = None
21
21
 
22
- def serialize(self, display_context: WorkflowDisplayContext, **kwargs: Any) -> JsonObject:
22
+ def serialize(self, display_context: WorkflowDisplayContext, **_kwargs) -> JsonObject:
23
23
  node = self._node
24
24
  node_id = self.node_id
25
25
 
@@ -15,7 +15,7 @@ class BaseGuardrailNodeDisplay(BaseNodeDisplay[_GuardrailNodeType], Generic[_Gua
15
15
  __serializable_inputs__ = {GuardrailNode.metric_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
@@ -28,7 +28,7 @@ class BaseInlinePromptNodeDisplay(BaseNodeDisplay[_InlinePromptNodeType], Generi
28
28
  }
29
29
 
30
30
  def serialize(
31
- self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs
31
+ self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **_kwargs
32
32
  ) -> JsonObject:
33
33
  node = self._node
34
34
  node_id = self.node_id
@@ -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):
@@ -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"},