vellum-ai 0.12.13__py3-none-any.whl → 0.12.15__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 (52) hide show
  1. vellum/__init__.py +9 -0
  2. vellum/client/__init__.py +2 -6
  3. vellum/client/core/client_wrapper.py +1 -1
  4. vellum/client/environment.py +3 -3
  5. vellum/client/resources/ad_hoc/client.py +2 -6
  6. vellum/client/resources/container_images/client.py +0 -8
  7. vellum/client/resources/metric_definitions/client.py +2 -6
  8. vellum/client/resources/workflows/client.py +8 -8
  9. vellum/client/types/__init__.py +6 -0
  10. vellum/client/types/audio_prompt_block.py +29 -0
  11. vellum/client/types/function_call_prompt_block.py +30 -0
  12. vellum/client/types/image_prompt_block.py +29 -0
  13. vellum/client/types/prompt_block.py +12 -1
  14. vellum/client/types/workflow_push_response.py +1 -0
  15. vellum/plugins/pydantic.py +12 -2
  16. vellum/types/audio_prompt_block.py +3 -0
  17. vellum/types/function_call_prompt_block.py +3 -0
  18. vellum/types/image_prompt_block.py +3 -0
  19. vellum/workflows/descriptors/tests/test_utils.py +3 -0
  20. vellum/workflows/nodes/bases/base.py +4 -1
  21. vellum/workflows/nodes/bases/base_adornment_node.py +75 -0
  22. vellum/workflows/nodes/bases/tests/test_base_node.py +13 -0
  23. vellum/workflows/nodes/core/inline_subworkflow_node/node.py +2 -0
  24. vellum/workflows/nodes/core/map_node/node.py +49 -45
  25. vellum/workflows/nodes/core/retry_node/node.py +10 -45
  26. vellum/workflows/nodes/core/try_node/node.py +12 -84
  27. vellum/workflows/nodes/utils.py +44 -1
  28. vellum/workflows/references/constant.py +21 -0
  29. vellum/workflows/runner/runner.py +4 -3
  30. vellum/workflows/types/cycle_map.py +34 -0
  31. vellum/workflows/workflows/base.py +4 -11
  32. {vellum_ai-0.12.13.dist-info → vellum_ai-0.12.15.dist-info}/METADATA +2 -2
  33. {vellum_ai-0.12.13.dist-info → vellum_ai-0.12.15.dist-info}/RECORD +52 -39
  34. vellum_cli/config.py +4 -0
  35. vellum_cli/pull.py +20 -5
  36. vellum_cli/push.py +7 -0
  37. vellum_cli/tests/test_pull.py +19 -1
  38. vellum_ee/workflows/display/nodes/vellum/__init__.py +2 -0
  39. vellum_ee/workflows/display/nodes/vellum/base_node.py +18 -0
  40. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +10 -41
  41. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py +4 -14
  42. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py +174 -0
  43. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +2 -10
  44. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +2 -10
  45. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +5 -19
  46. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py +2 -8
  47. vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +14 -25
  48. vellum_ee/workflows/server/__init__.py +0 -0
  49. vellum_ee/workflows/server/virtual_file_loader.py +42 -0
  50. {vellum_ai-0.12.13.dist-info → vellum_ai-0.12.15.dist-info}/LICENSE +0 -0
  51. {vellum_ai-0.12.13.dist-info → vellum_ai-0.12.15.dist-info}/WHEEL +0 -0
  52. {vellum_ai-0.12.13.dist-info → vellum_ai-0.12.15.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,174 @@
1
+ from deepdiff import DeepDiff
2
+
3
+ from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
4
+ from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
5
+
6
+ from tests.workflows.basic_generic_node.workflow import BasicGenericNodeWorkflow
7
+
8
+
9
+ def test_serialize_workflow(vellum_client):
10
+ # GIVEN a Workflow that uses a generic node
11
+ # WHEN we serialize it
12
+ workflow_display = get_workflow_display(
13
+ base_display_class=VellumWorkflowDisplay, workflow_class=BasicGenericNodeWorkflow
14
+ )
15
+
16
+ serialized_workflow: dict = workflow_display.serialize()
17
+
18
+ # THEN we should get a serialized representation of the Workflow
19
+ assert serialized_workflow.keys() == {
20
+ "workflow_raw_data",
21
+ "input_variables",
22
+ "output_variables",
23
+ }
24
+
25
+ # AND its input variables should be what we expect
26
+ input_variables = serialized_workflow["input_variables"]
27
+ assert len(input_variables) == 1
28
+ assert not DeepDiff(
29
+ [
30
+ {
31
+ "id": "a07c2273-34a7-42b5-bcad-143b6127cc8a",
32
+ "key": "input",
33
+ "type": "STRING",
34
+ "default": None,
35
+ "required": True,
36
+ "extensions": {"color": None},
37
+ },
38
+ ],
39
+ input_variables,
40
+ ignore_order=True,
41
+ )
42
+
43
+ # AND its output variables should be what we expect
44
+ output_variables = serialized_workflow["output_variables"]
45
+ assert len(output_variables) == 1
46
+ assert not DeepDiff(
47
+ [
48
+ {"id": "2b6389d0-266a-4be4-843e-4e543dd3d727", "key": "output", "type": "STRING"},
49
+ ],
50
+ output_variables,
51
+ ignore_order=True,
52
+ )
53
+
54
+ # AND its raw data should be what we expect
55
+ workflow_raw_data = serialized_workflow["workflow_raw_data"]
56
+ assert workflow_raw_data.keys() == {"edges", "nodes", "display_data", "definition"}
57
+ assert len(workflow_raw_data["edges"]) == 2
58
+ assert len(workflow_raw_data["nodes"]) == 3
59
+
60
+ # AND each node should be serialized correctly
61
+ entrypoint_node = workflow_raw_data["nodes"][0]
62
+ assert entrypoint_node == {
63
+ "id": "f1e4678f-c470-400b-a40e-c8922cc99a86",
64
+ "type": "ENTRYPOINT",
65
+ "inputs": [],
66
+ "data": {"label": "Entrypoint Node", "source_handle_id": "40201804-8beb-43ad-8873-a027759512f1"},
67
+ "display_data": {"position": {"x": 0.0, "y": 0.0}},
68
+ "definition": {
69
+ "name": "BaseNode",
70
+ "module": [
71
+ "vellum",
72
+ "workflows",
73
+ "nodes",
74
+ "bases",
75
+ "base",
76
+ ],
77
+ "bases": [],
78
+ },
79
+ }
80
+
81
+ api_node = workflow_raw_data["nodes"][1]
82
+ assert not DeepDiff(
83
+ {
84
+ "id": "c2ed23f7-f6cb-4a56-a91c-2e5f9d8fda7f",
85
+ "type": "GENERIC",
86
+ },
87
+ api_node,
88
+ )
89
+
90
+ final_output_node = workflow_raw_data["nodes"][2]
91
+ assert not DeepDiff(
92
+ {
93
+ "id": "50e3b446-afcd-4a5d-8c6f-5f05eaf2200e",
94
+ "type": "TERMINAL",
95
+ "data": {
96
+ "label": "Final Output",
97
+ "name": "output",
98
+ "target_handle_id": "8bd9f4f3-9f66-4d95-8e84-529b0002c531",
99
+ "output_id": "2b6389d0-266a-4be4-843e-4e543dd3d727",
100
+ "output_type": "STRING",
101
+ "node_input_id": "7a9f2d3a-0b23-4bd4-b567-e9493135b727",
102
+ },
103
+ "inputs": [
104
+ {
105
+ "id": "7a9f2d3a-0b23-4bd4-b567-e9493135b727",
106
+ "key": "node_input",
107
+ "value": {
108
+ "rules": [
109
+ {
110
+ "type": "NODE_OUTPUT",
111
+ "data": {
112
+ "node_id": "c2ed23f7-f6cb-4a56-a91c-2e5f9d8fda7f",
113
+ "output_id": "0a9c7a80-fc89-4a71-aac0-66489e4ddb85",
114
+ },
115
+ }
116
+ ],
117
+ "combinator": "OR",
118
+ },
119
+ }
120
+ ],
121
+ "display_data": {"position": {"x": 0.0, "y": 0.0}},
122
+ "definition": {
123
+ "name": "FinalOutputNode",
124
+ "module": ["vellum", "workflows", "nodes", "displayable", "final_output_node", "node"],
125
+ "bases": [
126
+ {"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"], "bases": []}
127
+ ],
128
+ },
129
+ },
130
+ final_output_node,
131
+ ignore_order=True,
132
+ )
133
+
134
+ # AND each edge should be serialized correctly
135
+ serialized_edges = workflow_raw_data["edges"]
136
+ assert not DeepDiff(
137
+ [
138
+ {
139
+ "id": "445dd2de-82b2-482b-89f6-5f49d8eb21a9",
140
+ "source_node_id": "f1e4678f-c470-400b-a40e-c8922cc99a86",
141
+ "source_handle_id": "40201804-8beb-43ad-8873-a027759512f1",
142
+ "target_node_id": "c2ed23f7-f6cb-4a56-a91c-2e5f9d8fda7f",
143
+ "target_handle_id": "b7bfb298-959a-4d2b-8b85-bbd0d2522703",
144
+ "type": "DEFAULT",
145
+ },
146
+ {
147
+ "id": "b741c861-cf67-4649-b9ef-b43a4add72b1",
148
+ "source_node_id": "c2ed23f7-f6cb-4a56-a91c-2e5f9d8fda7f",
149
+ "source_handle_id": "89dccfa5-cc1a-4612-bd87-86cb444f6dd4",
150
+ "target_node_id": "50e3b446-afcd-4a5d-8c6f-5f05eaf2200e",
151
+ "target_handle_id": "8bd9f4f3-9f66-4d95-8e84-529b0002c531",
152
+ "type": "DEFAULT",
153
+ },
154
+ ],
155
+ serialized_edges,
156
+ ignore_order=True,
157
+ )
158
+
159
+ # AND the display data should be what we expect
160
+ display_data = workflow_raw_data["display_data"]
161
+ assert display_data == {
162
+ "viewport": {
163
+ "x": 0.0,
164
+ "y": 0.0,
165
+ "zoom": 1.0,
166
+ }
167
+ }
168
+
169
+ # AND the definition should be what we expect
170
+ definition = workflow_raw_data["definition"]
171
+ assert definition == {
172
+ "name": "BasicGenericNodeWorkflow",
173
+ "module": ["tests", "workflows", "basic_generic_node", "workflow"],
174
+ }
@@ -1,8 +1,5 @@
1
- from unittest import mock
2
-
3
1
  from deepdiff import DeepDiff
4
2
 
5
- from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
6
3
  from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
7
4
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
8
5
 
@@ -15,12 +12,7 @@ def test_serialize_workflow():
15
12
  workflow_display = get_workflow_display(
16
13
  base_display_class=VellumWorkflowDisplay, workflow_class=BasicInlineSubworkflowWorkflow
17
14
  )
18
-
19
- # TODO: Support serialization of BaseNode
20
- # https://app.shortcut.com/vellum/story/4871/support-serialization-of-base-node
21
- with mock.patch.object(BaseNodeVellumDisplay, "serialize") as mocked_serialize:
22
- mocked_serialize.return_value = {"type": "MOCKED"}
23
- serialized_workflow: dict = workflow_display.serialize()
15
+ serialized_workflow: dict = workflow_display.serialize()
24
16
 
25
17
  # THEN we should get a serialized representation of the Workflow
26
18
  assert serialized_workflow.keys() == {
@@ -156,7 +148,7 @@ def test_serialize_workflow():
156
148
  "bases": [],
157
149
  },
158
150
  },
159
- {"type": "MOCKED"},
151
+ {"id": "1381c078-efa2-4255-89a1-7b4cb742c7fc", "type": "GENERIC"},
160
152
  {
161
153
  "id": "a773c3a5-78cb-4250-8d29-7282e8a579d3",
162
154
  "type": "TERMINAL",
@@ -1,8 +1,5 @@
1
- from unittest import mock
2
-
3
1
  from deepdiff import DeepDiff
4
2
 
5
- from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
6
3
  from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
7
4
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
8
5
 
@@ -13,12 +10,7 @@ def test_serialize_workflow():
13
10
  # GIVEN a Workflow that uses a MapNode
14
11
  # WHEN we serialize it
15
12
  workflow_display = get_workflow_display(base_display_class=VellumWorkflowDisplay, workflow_class=SimpleMapExample)
16
-
17
- # TODO: Support serialization of BaseNode
18
- # https://app.shortcut.com/vellum/story/4871/support-serialization-of-base-node
19
- with mock.patch.object(BaseNodeVellumDisplay, "serialize") as mocked_serialize:
20
- mocked_serialize.return_value = {"type": "MOCKED"}
21
- serialized_workflow: dict = workflow_display.serialize()
13
+ serialized_workflow: dict = workflow_display.serialize()
22
14
 
23
15
  # THEN we should get a serialized representation of the Workflow
24
16
  assert serialized_workflow.keys() == {
@@ -141,7 +133,7 @@ def test_serialize_workflow():
141
133
  "bases": [],
142
134
  },
143
135
  },
144
- {"type": "MOCKED"},
136
+ {"id": "baf6d316-dc75-41e8-96c0-015aede96309", "type": "GENERIC"},
145
137
  {
146
138
  "id": "6f4883b2-70b1-4e1c-ae15-7d0f5aec810b",
147
139
  "type": "TERMINAL",
@@ -1,8 +1,5 @@
1
- from unittest import mock
2
-
3
1
  from deepdiff import DeepDiff
4
2
 
5
- from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
6
3
  from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
7
4
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
8
5
 
@@ -15,12 +12,7 @@ def test_serialize_workflow__await_all():
15
12
  workflow_display = get_workflow_display(
16
13
  base_display_class=VellumWorkflowDisplay, workflow_class=AwaitAllPassingWorkflow
17
14
  )
18
-
19
- # TODO: Support serialization of BaseNode
20
- # https://app.shortcut.com/vellum/story/4871/support-serialization-of-base-node
21
- with mock.patch.object(BaseNodeVellumDisplay, "serialize") as mocked_serialize:
22
- mocked_serialize.return_value = {"type": "MOCKED"}
23
- serialized_workflow: dict = workflow_display.serialize()
15
+ serialized_workflow: dict = workflow_display.serialize()
24
16
 
25
17
  # THEN we should get a serialized representation of the Workflow
26
18
  assert serialized_workflow.keys() == {
@@ -76,18 +68,12 @@ def test_serialize_workflow__await_all():
76
68
  },
77
69
  }
78
70
 
79
- passthrough_nodes = [node for node in workflow_raw_data["nodes"] if node["type"] == "MOCKED"]
71
+ passthrough_nodes = [node for node in workflow_raw_data["nodes"] if node["type"] == "GENERIC"]
80
72
  assert not DeepDiff(
81
73
  [
82
- {
83
- "type": "MOCKED",
84
- },
85
- {
86
- "type": "MOCKED",
87
- },
88
- {
89
- "type": "MOCKED",
90
- },
74
+ {"id": "127ef456-91bc-43c6-bd8b-1772db5e3cb5", "type": "GENERIC"},
75
+ {"id": "59243c65-053f-4ea6-9157-3f3edb1477bf", "type": "GENERIC"},
76
+ {"id": "634f0202-9ea9-4c62-b152-1a58c595cffb", "type": "GENERIC"},
91
77
  ],
92
78
  passthrough_nodes,
93
79
  ignore_order=True,
@@ -1,8 +1,5 @@
1
- from unittest import mock
2
-
3
1
  from deepdiff import DeepDiff
4
2
 
5
- from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
6
3
  from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
7
4
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
8
5
 
@@ -13,10 +10,7 @@ def test_serialize_workflow():
13
10
  # GIVEN a Workflow with a TryNode
14
11
  # WHEN we serialize it
15
12
  workflow_display = get_workflow_display(base_display_class=VellumWorkflowDisplay, workflow_class=SimpleTryExample)
16
-
17
- with mock.patch.object(BaseNodeVellumDisplay, "serialize") as mocked_serialize:
18
- mocked_serialize.return_value = {"type": "MOCKED"}
19
- serialized_workflow: dict = workflow_display.serialize()
13
+ serialized_workflow: dict = workflow_display.serialize()
20
14
 
21
15
  # THEN we should get a serialized representation of the Workflow
22
16
  assert serialized_workflow.keys() == {
@@ -82,4 +76,4 @@ def test_serialize_workflow():
82
76
  }
83
77
 
84
78
  try_node = workflow_raw_data["nodes"][1]
85
- assert try_node == {"type": "MOCKED"}
79
+ assert try_node == {"id": "1381c078-efa2-4255-89a1-7b4cb742c7fc", "type": "GENERIC"}
@@ -1,9 +1,7 @@
1
1
  import pytest
2
- from unittest import mock
3
2
 
4
3
  from deepdiff import DeepDiff
5
4
 
6
- from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
7
5
  from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
8
6
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
9
7
 
@@ -13,17 +11,12 @@ from tests.workflows.complex_final_output_node.missing_workflow_output import Mi
13
11
 
14
12
  def test_serialize_workflow__missing_final_output_node():
15
13
  # GIVEN a Workflow that is missing a Terminal Node
14
+ workflow_display = get_workflow_display(
15
+ base_display_class=VellumWorkflowDisplay, workflow_class=MissingFinalOutputNodeWorkflow
16
+ )
16
17
 
17
- # TODO: Support serialization of BaseNode
18
- # https://app.shortcut.com/vellum/story/4871/support-serialization-of-base-node
19
- with mock.patch.object(BaseNodeVellumDisplay, "serialize") as mocked_serialize:
20
- mocked_serialize.return_value = {"type": "MOCKED"}
21
- workflow_display = get_workflow_display(
22
- base_display_class=VellumWorkflowDisplay, workflow_class=MissingFinalOutputNodeWorkflow
23
- )
24
-
25
- # WHEN we serialize it
26
- serialized_workflow: dict = workflow_display.serialize()
18
+ # WHEN we serialize it
19
+ serialized_workflow: dict = workflow_display.serialize()
27
20
 
28
21
  # THEN we should get a serialized representation of the Workflow
29
22
  assert serialized_workflow.keys() == {
@@ -97,9 +90,10 @@ def test_serialize_workflow__missing_final_output_node():
97
90
  },
98
91
  }
99
92
 
100
- passthrough_node = next(node for node in workflow_raw_data["nodes"] if node["type"] == "MOCKED")
93
+ passthrough_node = next(node for node in workflow_raw_data["nodes"] if node["type"] == "GENERIC")
101
94
  assert passthrough_node == {
102
- "type": "MOCKED",
95
+ "id": "32d88cab-e9fa-4a56-9bc2-fb6e1fd0897f",
96
+ "type": "GENERIC",
103
97
  }
104
98
 
105
99
  final_output_nodes = [node for node in workflow_raw_data["nodes"] if node["type"] == "TERMINAL"]
@@ -218,17 +212,12 @@ def test_serialize_workflow__missing_final_output_node():
218
212
 
219
213
  def test_serialize_workflow__missing_workflow_output():
220
214
  # GIVEN a Workflow that contains a terminal node that is unreferenced by the Workflow's Outputs
215
+ workflow_display = get_workflow_display(
216
+ base_display_class=VellumWorkflowDisplay, workflow_class=MissingWorkflowOutputWorkflow
217
+ )
221
218
 
222
- # TODO: Support serialization of BaseNode
223
- # https://app.shortcut.com/vellum/story/4871/support-serialization-of-base-node
224
- with mock.patch.object(BaseNodeVellumDisplay, "serialize") as mocked_serialize:
225
- mocked_serialize.return_value = {"type": "MOCKED"}
226
- workflow_display = get_workflow_display(
227
- base_display_class=VellumWorkflowDisplay, workflow_class=MissingWorkflowOutputWorkflow
228
- )
229
-
230
- # WHEN we serialize it, it should throw an error
231
- with pytest.raises(ValueError) as exc_info:
232
- workflow_display.serialize()
219
+ # WHEN we serialize it, it should throw an error
220
+ with pytest.raises(ValueError) as exc_info:
221
+ workflow_display.serialize()
233
222
 
234
223
  assert exc_info.value.args[0] == "Unable to serialize terminal nodes that are not referenced by workflow outputs."
File without changes
@@ -0,0 +1,42 @@
1
+ import importlib
2
+
3
+
4
+ class VirtualFileLoader(importlib.abc.Loader):
5
+ def __init__(self, code: str, is_package: bool):
6
+ self.code = code
7
+ self.is_package = is_package
8
+
9
+ def create_module(self, spec):
10
+ return None # use default module creation
11
+
12
+ def exec_module(self, module):
13
+ if not self.is_package:
14
+ exec(self.code, module.__dict__)
15
+
16
+
17
+ class VirtualFileFinder(importlib.abc.MetaPathFinder, importlib.abc.Loader):
18
+ def __init__(self, files: dict[str, str], namespace: str):
19
+ self.files = files
20
+ self.namespace = namespace
21
+
22
+ def find_spec(self, fullname, path, target=None):
23
+ # Do the namespacing on the fly to avoid having to copy the file dict
24
+ prefixed_name = fullname if fullname.startswith(self.namespace) else f"{self.namespace}.{fullname}"
25
+
26
+ key_name = "__init__" if fullname == self.namespace else fullname.replace(f"{self.namespace}.", "")
27
+
28
+ files_key = f"{key_name.replace('.', '/')}.py"
29
+ if not self.files.get(files_key):
30
+ files_key = f"{key_name.replace('.', '/')}/__init__.py"
31
+
32
+ file = self.files.get(files_key)
33
+ is_package = "__init__" in files_key
34
+
35
+ if file:
36
+ return importlib.machinery.ModuleSpec(
37
+ prefixed_name,
38
+ VirtualFileLoader(file, is_package),
39
+ origin=prefixed_name,
40
+ is_package=is_package,
41
+ )
42
+ return None