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.
- vellum/client/core/client_wrapper.py +1 -1
- vellum/workflows/environment/__init__.py +2 -1
- vellum/workflows/environment/environment.py +10 -3
- vellum/workflows/nodes/displayable/code_execution_node/node.py +8 -1
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py +53 -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 +11 -9
- {vellum_ai-0.14.69.dist-info → vellum_ai-0.14.71.dist-info}/METADATA +1 -1
- {vellum_ai-0.14.69.dist-info → vellum_ai-0.14.71.dist-info}/RECORD +48 -42
- 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 +8 -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 +70 -7
- 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/nodes/vellum/tests/test_search_node.py +104 -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 +82 -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_search_node_serialization.py +4 -4
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +59 -297
- vellum_ee/workflows/display/tests/workflow_serialization/test_workflow_input_parameterization_error.py +37 -0
- vellum_ee/workflows/display/utils/auto_layout.py +130 -0
- vellum_ee/workflows/display/utils/expressions.py +17 -1
- 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.69.dist-info → vellum_ai-0.14.71.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.69.dist-info → vellum_ai-0.14.71.dist-info}/WHEEL +0 -0
- {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
|
-
|
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
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
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"
|
File without changes
|
File without changes
|
File without changes
|