vellum-ai 0.14.69__py3-none-any.whl → 0.14.71__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 (48) 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 +10 -3
  4. vellum/workflows/nodes/displayable/code_execution_node/node.py +8 -1
  5. vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py +53 -0
  6. vellum/workflows/nodes/experimental/tool_calling_node/tests/test_node.py +77 -1
  7. vellum/workflows/nodes/experimental/tool_calling_node/utils.py +2 -2
  8. vellum/workflows/references/environment_variable.py +11 -9
  9. {vellum_ai-0.14.69.dist-info → vellum_ai-0.14.71.dist-info}/METADATA +1 -1
  10. {vellum_ai-0.14.69.dist-info → vellum_ai-0.14.71.dist-info}/RECORD +48 -42
  11. vellum_cli/__init__.py +5 -2
  12. vellum_cli/image_push.py +24 -1
  13. vellum_cli/tests/test_image_push.py +103 -12
  14. vellum_ee/workflows/display/nodes/base_node_display.py +1 -1
  15. vellum_ee/workflows/display/nodes/utils.py +2 -2
  16. vellum_ee/workflows/display/nodes/vellum/api_node.py +2 -2
  17. vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +1 -1
  18. vellum_ee/workflows/display/nodes/vellum/conditional_node.py +1 -1
  19. vellum_ee/workflows/display/nodes/vellum/error_node.py +1 -1
  20. vellum_ee/workflows/display/nodes/vellum/final_output_node.py +2 -2
  21. vellum_ee/workflows/display/nodes/vellum/guardrail_node.py +1 -1
  22. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +8 -4
  23. vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +9 -1
  24. vellum_ee/workflows/display/nodes/vellum/map_node.py +1 -1
  25. vellum_ee/workflows/display/nodes/vellum/merge_node.py +1 -1
  26. vellum_ee/workflows/display/nodes/vellum/note_node.py +1 -0
  27. vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +1 -1
  28. vellum_ee/workflows/display/nodes/vellum/retry_node.py +1 -1
  29. vellum_ee/workflows/display/nodes/vellum/search_node.py +70 -7
  30. vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +1 -1
  31. vellum_ee/workflows/display/nodes/vellum/templating_node.py +1 -1
  32. vellum_ee/workflows/display/nodes/vellum/tests/test_inline_subworkflow_node.py +88 -0
  33. vellum_ee/workflows/display/nodes/vellum/tests/test_search_node.py +104 -0
  34. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +16 -0
  35. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py +82 -0
  36. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +9 -1
  37. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_serialization.py +4 -4
  38. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +59 -297
  39. vellum_ee/workflows/display/tests/workflow_serialization/test_workflow_input_parameterization_error.py +37 -0
  40. vellum_ee/workflows/display/utils/auto_layout.py +130 -0
  41. vellum_ee/workflows/display/utils/expressions.py +17 -1
  42. vellum_ee/workflows/display/utils/tests/__init__.py +0 -0
  43. vellum_ee/workflows/display/utils/tests/test_auto_layout.py +56 -0
  44. vellum_ee/workflows/display/workflows/base_workflow_display.py +15 -10
  45. vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +41 -0
  46. {vellum_ai-0.14.69.dist-info → vellum_ai-0.14.71.dist-info}/LICENSE +0 -0
  47. {vellum_ai-0.14.69.dist-info → vellum_ai-0.14.71.dist-info}/WHEEL +0 -0
  48. {vellum_ai-0.14.69.dist-info → vellum_ai-0.14.71.dist-info}/entry_points.txt +0 -0
@@ -31,6 +31,7 @@ from vellum.workflows.expressions.or_ import OrExpression
31
31
  from vellum.workflows.expressions.parse_json import ParseJsonExpression
32
32
  from vellum.workflows.nodes.displayable.bases.utils import primitive_to_vellum_value
33
33
  from vellum.workflows.references.constant import ConstantValueReference
34
+ from vellum.workflows.references.environment_variable import EnvironmentVariableReference
34
35
  from vellum.workflows.references.execution_count import ExecutionCountReference
35
36
  from vellum.workflows.references.lazy import LazyReference
36
37
  from vellum.workflows.references.output import OutputReference
@@ -210,7 +211,16 @@ def serialize_value(display_context: "WorkflowDisplayContext", value: Any) -> Js
210
211
  return serialize_value(display_context, child_descriptor)
211
212
 
212
213
  if isinstance(value, WorkflowInputReference):
213
- workflow_input_display = display_context.global_workflow_input_displays[value]
214
+ try:
215
+ workflow_input_display = display_context.global_workflow_input_displays[value]
216
+ except KeyError:
217
+ inputs_class_name = value.inputs_class.__name__
218
+ workflow_class_name = display_context.workflow_display_class.infer_workflow_class().__name__
219
+ raise UnsupportedSerializationException(
220
+ f"Inputs class '{inputs_class_name}' referenced during serialization of '{workflow_class_name}' "
221
+ f"without parameterizing this Workflow with this Inputs definition. Update your Workflow "
222
+ f"definition to '{workflow_class_name}(BaseWorkflow[{inputs_class_name}, BaseState])'."
223
+ )
214
224
  return {
215
225
  "type": "WORKFLOW_INPUT",
216
226
  "input_variable_id": str(workflow_input_display.id),
@@ -239,6 +249,12 @@ def serialize_value(display_context: "WorkflowDisplayContext", value: Any) -> Js
239
249
  "vellum_secret_name": value.name,
240
250
  }
241
251
 
252
+ if isinstance(value, EnvironmentVariableReference):
253
+ return {
254
+ "type": "ENVIRONMENT_VARIABLE",
255
+ "environment_variable": value.name,
256
+ }
257
+
242
258
  if isinstance(value, ExecutionCountReference):
243
259
  node_class_display = display_context.global_node_displays[value.node_class]
244
260
 
File without changes
@@ -0,0 +1,56 @@
1
+ from uuid import uuid4
2
+
3
+ from vellum_ee.workflows.display.base import EdgeDisplay
4
+ from vellum_ee.workflows.display.editor.types import NodeDisplayData, NodeDisplayPosition
5
+ from vellum_ee.workflows.display.utils.auto_layout import auto_layout_nodes
6
+
7
+
8
+ def test_auto_layout_basic():
9
+ """Test basic auto layout functionality with a simple linear graph."""
10
+ nodes = [
11
+ ("node1", NodeDisplayData(position=NodeDisplayPosition(x=0, y=0), width=100, height=80)),
12
+ ("node2", NodeDisplayData(position=NodeDisplayPosition(x=0, y=0), width=120, height=90)),
13
+ ("node3", NodeDisplayData(position=NodeDisplayPosition(x=0, y=0), width=110, height=85)),
14
+ ]
15
+
16
+ edges = [
17
+ ("node1", "node2", EdgeDisplay(id=uuid4())),
18
+ ("node2", "node3", EdgeDisplay(id=uuid4())),
19
+ ]
20
+
21
+ positioned_nodes = auto_layout_nodes(nodes, edges)
22
+
23
+ node_positions = {node_id: data.position for node_id, data in positioned_nodes}
24
+
25
+ assert node_positions["node1"].x < node_positions["node2"].x
26
+ assert node_positions["node2"].x < node_positions["node3"].x
27
+
28
+
29
+ def test_auto_layout_parallel_branches():
30
+ """Test auto layout with parallel branches."""
31
+ nodes = [
32
+ ("start", NodeDisplayData(position=NodeDisplayPosition(x=0, y=0), width=100, height=80)),
33
+ ("branch1", NodeDisplayData(position=NodeDisplayPosition(x=0, y=0), width=100, height=80)),
34
+ ("branch2", NodeDisplayData(position=NodeDisplayPosition(x=0, y=0), width=100, height=80)),
35
+ ("end", NodeDisplayData(position=NodeDisplayPosition(x=0, y=0), width=100, height=80)),
36
+ ]
37
+
38
+ edges = [
39
+ ("start", "branch1", EdgeDisplay(id=uuid4())),
40
+ ("start", "branch2", EdgeDisplay(id=uuid4())),
41
+ ("branch1", "end", EdgeDisplay(id=uuid4())),
42
+ ("branch2", "end", EdgeDisplay(id=uuid4())),
43
+ ]
44
+
45
+ positioned_nodes = auto_layout_nodes(nodes, edges)
46
+
47
+ node_positions = {node_id: data.position for node_id, data in positioned_nodes}
48
+
49
+ assert node_positions["start"].x < node_positions["branch1"].x
50
+ assert node_positions["start"].x < node_positions["branch2"].x
51
+
52
+ assert node_positions["branch1"].x == node_positions["branch2"].x
53
+ assert node_positions["branch1"].x < node_positions["end"].x
54
+
55
+ assert node_positions["branch1"].x < node_positions["end"].x
56
+ assert node_positions["branch2"].x < node_positions["end"].x
@@ -200,16 +200,21 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
200
200
  raise ValueError(f"Failed to serialize output '{workflow_output.name}': {str(e)}") from e
201
201
 
202
202
  source_node_display: Optional[BaseNodeDisplay]
203
- first_rule = node_input.value.rules[0]
204
- if first_rule.type == "NODE_OUTPUT":
205
- source_node_id = UUID(first_rule.data.node_id)
206
- try:
207
- source_node_display = [
208
- node_display
209
- for node_display in self.display_context.node_displays.values()
210
- if node_display.node_id == source_node_id
211
- ][0]
212
- except IndexError:
203
+ if not node_input.value.rules:
204
+ source_node_display = None
205
+ else:
206
+ first_rule = node_input.value.rules[0]
207
+ if first_rule.type == "NODE_OUTPUT":
208
+ source_node_id = UUID(first_rule.data.node_id)
209
+ try:
210
+ source_node_display = [
211
+ node_display
212
+ for node_display in self.display_context.node_displays.values()
213
+ if node_display.node_id == source_node_id
214
+ ][0]
215
+ except IndexError:
216
+ source_node_display = None
217
+ else:
213
218
  source_node_display = None
214
219
 
215
220
  synthetic_target_handle_id = str(
@@ -787,3 +787,44 @@ def test_serialize_workflow__dict_reference():
787
787
  },
788
788
  ],
789
789
  }
790
+
791
+
792
+ def test_serialize_workflow__empty_rules_indexerror():
793
+ """Test that workflow serialization handles dictionary key access correctly."""
794
+
795
+ # GIVEN a node with dictionary output
796
+ class StartNode(BaseNode):
797
+ class Outputs(BaseNode.Outputs):
798
+ data: dict = {"key": "value"}
799
+
800
+ # AND a workflow that references dictionary key access in its outputs
801
+ class MyWorkflow(BaseWorkflow):
802
+ graph = StartNode
803
+
804
+ class Outputs(BaseWorkflow.Outputs):
805
+ # This dictionary key access should be handled gracefully
806
+ problematic_output = StartNode.Outputs.data["bar"]
807
+
808
+ # WHEN we serialize the workflow
809
+ workflow_display = get_workflow_display(workflow_class=MyWorkflow)
810
+ result: dict = workflow_display.serialize()
811
+
812
+ assert result is not None
813
+ assert "output_variables" in result
814
+ assert "workflow_raw_data" in result
815
+
816
+ # AND the workflow output should contain the dictionary key access
817
+ output_variables = result["output_variables"]
818
+ assert len(output_variables) == 1
819
+ assert output_variables[0]["key"] == "problematic_output"
820
+
821
+ # AND the workflow raw data should contain nodes including terminal node
822
+ workflow_raw_data = result["workflow_raw_data"]
823
+ assert "nodes" in workflow_raw_data
824
+ nodes = workflow_raw_data["nodes"]
825
+
826
+ assert len(nodes) >= 3
827
+
828
+ terminal_nodes = [node for node in nodes if node.get("type") == "TERMINAL"]
829
+ assert len(terminal_nodes) == 1
830
+ assert terminal_nodes[0]["data"]["name"] == "problematic_output"