vellum-ai 0.9.16rc2__py3-none-any.whl → 0.9.16rc4__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (245) hide show
  1. vellum/plugins/__init__.py +0 -0
  2. vellum/plugins/pydantic.py +74 -0
  3. vellum/plugins/utils.py +19 -0
  4. vellum/plugins/vellum_mypy.py +639 -3
  5. vellum/workflows/README.md +90 -0
  6. vellum/workflows/__init__.py +5 -0
  7. vellum/workflows/constants.py +43 -0
  8. vellum/workflows/descriptors/__init__.py +0 -0
  9. vellum/workflows/descriptors/base.py +339 -0
  10. vellum/workflows/descriptors/tests/test_utils.py +83 -0
  11. vellum/workflows/descriptors/utils.py +90 -0
  12. vellum/workflows/edges/__init__.py +5 -0
  13. vellum/workflows/edges/edge.py +23 -0
  14. vellum/workflows/emitters/__init__.py +5 -0
  15. vellum/workflows/emitters/base.py +14 -0
  16. vellum/workflows/environment/__init__.py +5 -0
  17. vellum/workflows/environment/environment.py +7 -0
  18. vellum/workflows/errors/__init__.py +6 -0
  19. vellum/workflows/errors/types.py +20 -0
  20. vellum/workflows/events/__init__.py +31 -0
  21. vellum/workflows/events/node.py +125 -0
  22. vellum/workflows/events/tests/__init__.py +0 -0
  23. vellum/workflows/events/tests/test_event.py +216 -0
  24. vellum/workflows/events/types.py +52 -0
  25. vellum/workflows/events/utils.py +5 -0
  26. vellum/workflows/events/workflow.py +139 -0
  27. vellum/workflows/exceptions.py +15 -0
  28. vellum/workflows/expressions/__init__.py +0 -0
  29. vellum/workflows/expressions/accessor.py +52 -0
  30. vellum/workflows/expressions/and_.py +32 -0
  31. vellum/workflows/expressions/begins_with.py +31 -0
  32. vellum/workflows/expressions/between.py +38 -0
  33. vellum/workflows/expressions/coalesce_expression.py +41 -0
  34. vellum/workflows/expressions/contains.py +30 -0
  35. vellum/workflows/expressions/does_not_begin_with.py +31 -0
  36. vellum/workflows/expressions/does_not_contain.py +30 -0
  37. vellum/workflows/expressions/does_not_end_with.py +31 -0
  38. vellum/workflows/expressions/does_not_equal.py +25 -0
  39. vellum/workflows/expressions/ends_with.py +31 -0
  40. vellum/workflows/expressions/equals.py +25 -0
  41. vellum/workflows/expressions/greater_than.py +33 -0
  42. vellum/workflows/expressions/greater_than_or_equal_to.py +33 -0
  43. vellum/workflows/expressions/in_.py +31 -0
  44. vellum/workflows/expressions/is_blank.py +24 -0
  45. vellum/workflows/expressions/is_not_blank.py +24 -0
  46. vellum/workflows/expressions/is_not_null.py +21 -0
  47. vellum/workflows/expressions/is_not_undefined.py +22 -0
  48. vellum/workflows/expressions/is_null.py +21 -0
  49. vellum/workflows/expressions/is_undefined.py +22 -0
  50. vellum/workflows/expressions/less_than.py +33 -0
  51. vellum/workflows/expressions/less_than_or_equal_to.py +33 -0
  52. vellum/workflows/expressions/not_between.py +38 -0
  53. vellum/workflows/expressions/not_in.py +31 -0
  54. vellum/workflows/expressions/or_.py +32 -0
  55. vellum/workflows/graph/__init__.py +3 -0
  56. vellum/workflows/graph/graph.py +131 -0
  57. vellum/workflows/graph/tests/__init__.py +0 -0
  58. vellum/workflows/graph/tests/test_graph.py +437 -0
  59. vellum/workflows/inputs/__init__.py +5 -0
  60. vellum/workflows/inputs/base.py +55 -0
  61. vellum/workflows/logging.py +14 -0
  62. vellum/workflows/nodes/__init__.py +46 -0
  63. vellum/workflows/nodes/bases/__init__.py +7 -0
  64. vellum/workflows/nodes/bases/base.py +332 -0
  65. vellum/workflows/nodes/bases/base_subworkflow_node/__init__.py +5 -0
  66. vellum/workflows/nodes/bases/base_subworkflow_node/node.py +10 -0
  67. vellum/workflows/nodes/bases/tests/__init__.py +0 -0
  68. vellum/workflows/nodes/bases/tests/test_base_node.py +125 -0
  69. vellum/workflows/nodes/core/__init__.py +16 -0
  70. vellum/workflows/nodes/core/error_node/__init__.py +5 -0
  71. vellum/workflows/nodes/core/error_node/node.py +26 -0
  72. vellum/workflows/nodes/core/inline_subworkflow_node/__init__.py +5 -0
  73. vellum/workflows/nodes/core/inline_subworkflow_node/node.py +73 -0
  74. vellum/workflows/nodes/core/map_node/__init__.py +5 -0
  75. vellum/workflows/nodes/core/map_node/node.py +147 -0
  76. vellum/workflows/nodes/core/map_node/tests/__init__.py +0 -0
  77. vellum/workflows/nodes/core/map_node/tests/test_node.py +65 -0
  78. vellum/workflows/nodes/core/retry_node/__init__.py +5 -0
  79. vellum/workflows/nodes/core/retry_node/node.py +106 -0
  80. vellum/workflows/nodes/core/retry_node/tests/__init__.py +0 -0
  81. vellum/workflows/nodes/core/retry_node/tests/test_node.py +93 -0
  82. vellum/workflows/nodes/core/templating_node/__init__.py +5 -0
  83. vellum/workflows/nodes/core/templating_node/custom_filters.py +12 -0
  84. vellum/workflows/nodes/core/templating_node/exceptions.py +2 -0
  85. vellum/workflows/nodes/core/templating_node/node.py +123 -0
  86. vellum/workflows/nodes/core/templating_node/render.py +55 -0
  87. vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py +21 -0
  88. vellum/workflows/nodes/core/try_node/__init__.py +5 -0
  89. vellum/workflows/nodes/core/try_node/node.py +110 -0
  90. vellum/workflows/nodes/core/try_node/tests/__init__.py +0 -0
  91. vellum/workflows/nodes/core/try_node/tests/test_node.py +82 -0
  92. vellum/workflows/nodes/displayable/__init__.py +31 -0
  93. vellum/workflows/nodes/displayable/api_node/__init__.py +5 -0
  94. vellum/workflows/nodes/displayable/api_node/node.py +44 -0
  95. vellum/workflows/nodes/displayable/bases/__init__.py +11 -0
  96. vellum/workflows/nodes/displayable/bases/api_node/__init__.py +5 -0
  97. vellum/workflows/nodes/displayable/bases/api_node/node.py +70 -0
  98. vellum/workflows/nodes/displayable/bases/base_prompt_node/__init__.py +5 -0
  99. vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +60 -0
  100. vellum/workflows/nodes/displayable/bases/inline_prompt_node/__init__.py +5 -0
  101. vellum/workflows/nodes/displayable/bases/inline_prompt_node/constants.py +13 -0
  102. vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +118 -0
  103. vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +98 -0
  104. vellum/workflows/nodes/displayable/bases/search_node.py +90 -0
  105. vellum/workflows/nodes/displayable/code_execution_node/__init__.py +5 -0
  106. vellum/workflows/nodes/displayable/code_execution_node/node.py +197 -0
  107. vellum/workflows/nodes/displayable/code_execution_node/tests/__init__.py +0 -0
  108. vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/__init__.py +0 -0
  109. vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/main.py +3 -0
  110. vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +111 -0
  111. vellum/workflows/nodes/displayable/code_execution_node/utils.py +10 -0
  112. vellum/workflows/nodes/displayable/conditional_node/__init__.py +5 -0
  113. vellum/workflows/nodes/displayable/conditional_node/node.py +25 -0
  114. vellum/workflows/nodes/displayable/final_output_node/__init__.py +5 -0
  115. vellum/workflows/nodes/displayable/final_output_node/node.py +43 -0
  116. vellum/workflows/nodes/displayable/guardrail_node/__init__.py +5 -0
  117. vellum/workflows/nodes/displayable/guardrail_node/node.py +97 -0
  118. vellum/workflows/nodes/displayable/inline_prompt_node/__init__.py +5 -0
  119. vellum/workflows/nodes/displayable/inline_prompt_node/node.py +41 -0
  120. vellum/workflows/nodes/displayable/merge_node/__init__.py +5 -0
  121. vellum/workflows/nodes/displayable/merge_node/node.py +10 -0
  122. vellum/workflows/nodes/displayable/prompt_deployment_node/__init__.py +5 -0
  123. vellum/workflows/nodes/displayable/prompt_deployment_node/node.py +45 -0
  124. vellum/workflows/nodes/displayable/search_node/__init__.py +5 -0
  125. vellum/workflows/nodes/displayable/search_node/node.py +26 -0
  126. vellum/workflows/nodes/displayable/subworkflow_deployment_node/__init__.py +5 -0
  127. vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +156 -0
  128. vellum/workflows/nodes/displayable/tests/__init__.py +0 -0
  129. vellum/workflows/nodes/displayable/tests/test_inline_text_prompt_node.py +148 -0
  130. vellum/workflows/nodes/displayable/tests/test_search_node_wth_text_output.py +134 -0
  131. vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py +80 -0
  132. vellum/workflows/nodes/utils.py +27 -0
  133. vellum/workflows/outputs/__init__.py +6 -0
  134. vellum/workflows/outputs/base.py +196 -0
  135. vellum/workflows/ports/__init__.py +7 -0
  136. vellum/workflows/ports/node_ports.py +75 -0
  137. vellum/workflows/ports/port.py +75 -0
  138. vellum/workflows/ports/utils.py +40 -0
  139. vellum/workflows/references/__init__.py +17 -0
  140. vellum/workflows/references/environment_variable.py +20 -0
  141. vellum/workflows/references/execution_count.py +20 -0
  142. vellum/workflows/references/external_input.py +49 -0
  143. vellum/workflows/references/input.py +7 -0
  144. vellum/workflows/references/lazy.py +55 -0
  145. vellum/workflows/references/node.py +43 -0
  146. vellum/workflows/references/output.py +78 -0
  147. vellum/workflows/references/state_value.py +23 -0
  148. vellum/workflows/references/vellum_secret.py +15 -0
  149. vellum/workflows/references/workflow_input.py +41 -0
  150. vellum/workflows/resolvers/__init__.py +5 -0
  151. vellum/workflows/resolvers/base.py +15 -0
  152. vellum/workflows/runner/__init__.py +5 -0
  153. vellum/workflows/runner/runner.py +588 -0
  154. vellum/workflows/runner/types.py +18 -0
  155. vellum/workflows/state/__init__.py +5 -0
  156. vellum/workflows/state/base.py +327 -0
  157. vellum/workflows/state/context.py +18 -0
  158. vellum/workflows/state/encoder.py +57 -0
  159. vellum/workflows/state/store.py +28 -0
  160. vellum/workflows/state/tests/__init__.py +0 -0
  161. vellum/workflows/state/tests/test_state.py +113 -0
  162. vellum/workflows/types/__init__.py +0 -0
  163. vellum/workflows/types/core.py +91 -0
  164. vellum/workflows/types/generics.py +14 -0
  165. vellum/workflows/types/stack.py +39 -0
  166. vellum/workflows/types/tests/__init__.py +0 -0
  167. vellum/workflows/types/tests/test_utils.py +76 -0
  168. vellum/workflows/types/utils.py +164 -0
  169. vellum/workflows/utils/__init__.py +0 -0
  170. vellum/workflows/utils/names.py +13 -0
  171. vellum/workflows/utils/tests/__init__.py +0 -0
  172. vellum/workflows/utils/tests/test_names.py +15 -0
  173. vellum/workflows/utils/tests/test_vellum_variables.py +25 -0
  174. vellum/workflows/utils/vellum_variables.py +81 -0
  175. vellum/workflows/vellum_client.py +18 -0
  176. vellum/workflows/workflows/__init__.py +5 -0
  177. vellum/workflows/workflows/base.py +365 -0
  178. {vellum_ai-0.9.16rc2.dist-info → vellum_ai-0.9.16rc4.dist-info}/METADATA +2 -1
  179. {vellum_ai-0.9.16rc2.dist-info → vellum_ai-0.9.16rc4.dist-info}/RECORD +245 -7
  180. vellum_cli/__init__.py +72 -0
  181. vellum_cli/aliased_group.py +103 -0
  182. vellum_cli/config.py +96 -0
  183. vellum_cli/image_push.py +112 -0
  184. vellum_cli/logger.py +36 -0
  185. vellum_cli/pull.py +73 -0
  186. vellum_cli/push.py +121 -0
  187. vellum_cli/tests/test_config.py +100 -0
  188. vellum_cli/tests/test_pull.py +152 -0
  189. vellum_ee/workflows/__init__.py +0 -0
  190. vellum_ee/workflows/display/__init__.py +0 -0
  191. vellum_ee/workflows/display/base.py +73 -0
  192. vellum_ee/workflows/display/nodes/__init__.py +4 -0
  193. vellum_ee/workflows/display/nodes/base_node_display.py +116 -0
  194. vellum_ee/workflows/display/nodes/base_node_vellum_display.py +36 -0
  195. vellum_ee/workflows/display/nodes/get_node_display_class.py +25 -0
  196. vellum_ee/workflows/display/nodes/tests/__init__.py +0 -0
  197. vellum_ee/workflows/display/nodes/tests/test_base_node_display.py +47 -0
  198. vellum_ee/workflows/display/nodes/types.py +18 -0
  199. vellum_ee/workflows/display/nodes/utils.py +33 -0
  200. vellum_ee/workflows/display/nodes/vellum/__init__.py +32 -0
  201. vellum_ee/workflows/display/nodes/vellum/api_node.py +205 -0
  202. vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +71 -0
  203. vellum_ee/workflows/display/nodes/vellum/conditional_node.py +217 -0
  204. vellum_ee/workflows/display/nodes/vellum/final_output_node.py +61 -0
  205. vellum_ee/workflows/display/nodes/vellum/guardrail_node.py +49 -0
  206. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +170 -0
  207. vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +99 -0
  208. vellum_ee/workflows/display/nodes/vellum/map_node.py +100 -0
  209. vellum_ee/workflows/display/nodes/vellum/merge_node.py +48 -0
  210. vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +68 -0
  211. vellum_ee/workflows/display/nodes/vellum/search_node.py +193 -0
  212. vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +58 -0
  213. vellum_ee/workflows/display/nodes/vellum/templating_node.py +67 -0
  214. vellum_ee/workflows/display/nodes/vellum/tests/__init__.py +0 -0
  215. vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py +106 -0
  216. vellum_ee/workflows/display/nodes/vellum/try_node.py +38 -0
  217. vellum_ee/workflows/display/nodes/vellum/utils.py +76 -0
  218. vellum_ee/workflows/display/tests/__init__.py +0 -0
  219. vellum_ee/workflows/display/tests/workflow_serialization/__init__.py +0 -0
  220. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py +426 -0
  221. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py +607 -0
  222. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +1175 -0
  223. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py +235 -0
  224. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +511 -0
  225. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +372 -0
  226. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +272 -0
  227. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_prompt_deployment_serialization.py +289 -0
  228. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py +354 -0
  229. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py +123 -0
  230. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py +84 -0
  231. vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +233 -0
  232. vellum_ee/workflows/display/types.py +46 -0
  233. vellum_ee/workflows/display/utils/__init__.py +0 -0
  234. vellum_ee/workflows/display/utils/tests/__init__.py +0 -0
  235. vellum_ee/workflows/display/utils/tests/test_uuids.py +16 -0
  236. vellum_ee/workflows/display/utils/uuids.py +24 -0
  237. vellum_ee/workflows/display/utils/vellum.py +121 -0
  238. vellum_ee/workflows/display/vellum.py +357 -0
  239. vellum_ee/workflows/display/workflows/__init__.py +5 -0
  240. vellum_ee/workflows/display/workflows/base_workflow_display.py +302 -0
  241. vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py +32 -0
  242. vellum_ee/workflows/display/workflows/vellum_workflow_display.py +386 -0
  243. {vellum_ai-0.9.16rc2.dist-info → vellum_ai-0.9.16rc4.dist-info}/LICENSE +0 -0
  244. {vellum_ai-0.9.16rc2.dist-info → vellum_ai-0.9.16rc4.dist-info}/WHEEL +0 -0
  245. {vellum_ai-0.9.16rc2.dist-info → vellum_ai-0.9.16rc4.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,123 @@
1
+ from tests.workflows.basic_final_output_node.workflow import BasicFinalOutputNodeWorkflow
2
+ from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
3
+ from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
4
+
5
+
6
+ def test_serialize_workflow():
7
+ # GIVEN a Workflow that uses a Final Output Node
8
+ # WHEN we serialize it
9
+ workflow_display = get_workflow_display(
10
+ base_display_class=VellumWorkflowDisplay, workflow_class=BasicFinalOutputNodeWorkflow
11
+ )
12
+ serialized_workflow: dict = workflow_display.serialize()
13
+
14
+ # THEN we should get a serialized representation of the Workflow
15
+ assert serialized_workflow.keys() == {
16
+ "workflow_raw_data",
17
+ "input_variables",
18
+ "output_variables",
19
+ }
20
+
21
+ # AND its input variables should be what we expect
22
+ input_variables = serialized_workflow["input_variables"]
23
+ assert len(input_variables) == 1
24
+ assert input_variables == [
25
+ {
26
+ "id": "e39a7b63-de15-490a-ae9b-8112c767aea0",
27
+ "key": "input",
28
+ "type": "STRING",
29
+ "required": None,
30
+ "default": None,
31
+ "extensions": None,
32
+ }
33
+ ]
34
+
35
+ # AND its output variables should be what we expect
36
+ output_variables = serialized_workflow["output_variables"]
37
+ assert len(output_variables) == 1
38
+ assert output_variables == [
39
+ {
40
+ "id": "a34cd21e-40e5-47f4-8fdb-910593f3e9e2",
41
+ "key": "value",
42
+ "type": "STRING",
43
+ }
44
+ ]
45
+
46
+ # AND its raw data should be what we expect
47
+ workflow_raw_data = serialized_workflow["workflow_raw_data"]
48
+ assert workflow_raw_data.keys() == {"edges", "nodes", "display_data", "definition"}
49
+ assert len(workflow_raw_data["edges"]) == 1
50
+ assert len(workflow_raw_data["nodes"]) == 2
51
+
52
+ # AND each node should be serialized correctly
53
+ entrypoint_node = workflow_raw_data["nodes"][0]
54
+ assert entrypoint_node == {
55
+ "id": "631e2789-d60d-4088-9e3a-0ea93517075b",
56
+ "type": "ENTRYPOINT",
57
+ "inputs": [],
58
+ "data": {"label": "Entrypoint Node", "source_handle_id": "8b8d52a2-844f-44fe-a6c4-142fa70d391b"},
59
+ "display_data": {"position": {"x": 0.0, "y": 0.0}},
60
+ "definition": {
61
+ "name": "BaseNode",
62
+ "module": [
63
+ "vellum",
64
+ "workflows",
65
+ "nodes",
66
+ "bases",
67
+ "base",
68
+ ],
69
+ "bases": [],
70
+ },
71
+ }
72
+
73
+ final_output_node = workflow_raw_data["nodes"][1]
74
+ assert final_output_node == {
75
+ "id": "620ec17b-e330-4212-b619-3c39dc63fb22",
76
+ "type": "TERMINAL",
77
+ "data": {
78
+ "label": "Basic Final Output Node",
79
+ "name": "basic-final-output-node",
80
+ "target_handle_id": "0173d3c6-11d1-44b7-b070-ca9ff5119046",
81
+ "output_id": "97349956-d228-4b51-a64b-1331f788373f",
82
+ "output_type": "STRING",
83
+ "node_input_id": "5322567a-f40c-400a-96b3-c3b054db543e",
84
+ },
85
+ "inputs": [
86
+ {
87
+ "id": "5322567a-f40c-400a-96b3-c3b054db543e",
88
+ "key": "node_input",
89
+ "value": {
90
+ "rules": [
91
+ {
92
+ "type": "INPUT_VARIABLE",
93
+ "data": {"input_variable_id": "e39a7b63-de15-490a-ae9b-8112c767aea0"},
94
+ }
95
+ ],
96
+ "combinator": "OR",
97
+ },
98
+ }
99
+ ],
100
+ "display_data": {"position": {"x": 0.0, "y": 0.0}},
101
+ "definition": {
102
+ "name": "BasicFinalOutputNode",
103
+ "module": [
104
+ "tests",
105
+ "workflows",
106
+ "basic_final_output_node",
107
+ "workflow",
108
+ ],
109
+ "bases": [
110
+ {
111
+ "name": "FinalOutputNode",
112
+ "module": [
113
+ "vellum",
114
+ "workflows",
115
+ "nodes",
116
+ "displayable",
117
+ "final_output_node",
118
+ "node",
119
+ ],
120
+ }
121
+ ],
122
+ },
123
+ }
@@ -0,0 +1,84 @@
1
+ from unittest import mock
2
+
3
+ from deepdiff import DeepDiff
4
+
5
+ from tests.workflows.basic_try_node.workflow import SimpleTryExample
6
+ from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
7
+ from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
8
+ from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
9
+
10
+
11
+ def test_serialize_workflow():
12
+ # GIVEN a Workflow with a TryNode
13
+ # WHEN we serialize it
14
+ workflow_display = get_workflow_display(base_display_class=VellumWorkflowDisplay, workflow_class=SimpleTryExample)
15
+
16
+ with mock.patch.object(BaseNodeVellumDisplay, "serialize") as mocked_serialize:
17
+ mocked_serialize.return_value = {"type": "MOCKED"}
18
+ serialized_workflow: dict = workflow_display.serialize()
19
+
20
+ # THEN we should get a serialized representation of the Workflow
21
+ assert serialized_workflow.keys() == {
22
+ "workflow_raw_data",
23
+ "input_variables",
24
+ "output_variables",
25
+ }
26
+
27
+ # AND its input variables should be what we expect
28
+ input_variables = serialized_workflow["input_variables"]
29
+ assert len(input_variables) == 0
30
+
31
+ # AND its output variables should be what we expect
32
+ output_variables = serialized_workflow["output_variables"]
33
+ assert len(output_variables) == 2
34
+ assert not DeepDiff(
35
+ [
36
+ {
37
+ "id": "d4a3117e-8ec3-4579-8eeb-5e4247bb086d",
38
+ "key": "error",
39
+ "type": "JSON",
40
+ },
41
+ {
42
+ "id": "a8b99024-cd32-42f6-bb2f-827189bf3a3c",
43
+ "key": "final_value",
44
+ "type": "NUMBER",
45
+ },
46
+ ],
47
+ output_variables,
48
+ ignore_order=True,
49
+ )
50
+
51
+ # AND its raw data should be what we expect
52
+ workflow_raw_data = serialized_workflow["workflow_raw_data"]
53
+ assert workflow_raw_data.keys() == {"edges", "nodes", "display_data", "definition"}
54
+ assert len(workflow_raw_data["edges"]) == 3
55
+ assert len(workflow_raw_data["nodes"]) == 4
56
+
57
+ # AND each node should be serialized correctly
58
+ entrypoint_node = workflow_raw_data["nodes"][0]
59
+ assert entrypoint_node == {
60
+ "id": "c238508d-85ab-4644-8cbb-88eae457fe12",
61
+ "type": "ENTRYPOINT",
62
+ "inputs": [],
63
+ "definition": {
64
+ "bases": [],
65
+ "module": [
66
+ "vellum",
67
+ "workflows",
68
+ "nodes",
69
+ "bases",
70
+ "base",
71
+ ],
72
+ "name": "BaseNode",
73
+ },
74
+ "data": {
75
+ "label": "Entrypoint Node",
76
+ "source_handle_id": "04da0bb6-5b42-4dd1-a4e4-08f3ab03e1a3",
77
+ },
78
+ "display_data": {
79
+ "position": {"x": 0.0, "y": 0.0},
80
+ },
81
+ }
82
+
83
+ try_node = workflow_raw_data["nodes"][1]
84
+ assert try_node == {"type": "MOCKED"}
@@ -0,0 +1,233 @@
1
+ import pytest
2
+ from unittest import mock
3
+
4
+ from deepdiff import DeepDiff
5
+
6
+ from tests.workflows.complex_final_output_node.missing_final_output_node import MissingFinalOutputNodeWorkflow
7
+ from tests.workflows.complex_final_output_node.missing_workflow_output import MissingWorkflowOutputWorkflow
8
+ from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
9
+ from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
10
+ from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
11
+
12
+
13
+ def test_serialize_workflow__missing_final_output_node():
14
+ # GIVEN a Workflow that is missing a Terminal Node
15
+
16
+ # TODO: Support serialization of BaseNode
17
+ # https://app.shortcut.com/vellum/story/4871/support-serialization-of-base-node
18
+ with mock.patch.object(BaseNodeVellumDisplay, "serialize") as mocked_serialize:
19
+ mocked_serialize.return_value = {"type": "MOCKED"}
20
+ workflow_display = get_workflow_display(
21
+ base_display_class=VellumWorkflowDisplay, workflow_class=MissingFinalOutputNodeWorkflow
22
+ )
23
+
24
+ # WHEN we serialize it
25
+ serialized_workflow: dict = workflow_display.serialize()
26
+
27
+ # THEN we should get a serialized representation of the Workflow
28
+ assert serialized_workflow.keys() == {
29
+ "workflow_raw_data",
30
+ "input_variables",
31
+ "output_variables",
32
+ }
33
+
34
+ # AND its input variables should be what we expect
35
+ input_variables = serialized_workflow["input_variables"]
36
+ assert len(input_variables) == 2
37
+ assert not DeepDiff(
38
+ [
39
+ {
40
+ "id": "da086239-d743-4246-b666-5c91e22fb88c",
41
+ "key": "alpha",
42
+ "type": "STRING",
43
+ "default": None,
44
+ "required": None,
45
+ "extensions": None,
46
+ },
47
+ {
48
+ "id": "a8b6c5d4-a0e9-4457-834b-46b633c466a6",
49
+ "key": "beta",
50
+ "type": "STRING",
51
+ "default": None,
52
+ "required": None,
53
+ "extensions": None,
54
+ },
55
+ ],
56
+ input_variables,
57
+ ignore_order=True,
58
+ )
59
+
60
+ # AND its output variables should be what we expect
61
+ output_variables = serialized_workflow["output_variables"]
62
+ assert len(output_variables) == 2
63
+ assert not DeepDiff(
64
+ [
65
+ {"id": "a360aef6-3bb4-4c56-b407-478042ef224d", "key": "alpha", "type": "STRING"},
66
+ {"id": "5e6d3ea6-ef91-4937-8fff-f33e07446e6a", "key": "beta", "type": "STRING"},
67
+ ],
68
+ output_variables,
69
+ ignore_order=True,
70
+ )
71
+
72
+ # AND its raw data should be what we expect
73
+ workflow_raw_data = serialized_workflow["workflow_raw_data"]
74
+ assert workflow_raw_data.keys() == {"edges", "nodes", "display_data", "definition"}
75
+ assert len(workflow_raw_data["edges"]) == 3
76
+ assert len(workflow_raw_data["nodes"]) == 4
77
+
78
+ # AND each node should be serialized correctly
79
+ entrypoint_node = workflow_raw_data["nodes"][0]
80
+ assert entrypoint_node == {
81
+ "id": "b109349f-ca1b-4a5a-a66e-a1321cf297f7",
82
+ "type": "ENTRYPOINT",
83
+ "inputs": [],
84
+ "data": {"label": "Entrypoint Node", "source_handle_id": "943ac183-d107-4604-aed1-619bd7fef09c"},
85
+ "display_data": {"position": {"x": 0.0, "y": 0.0}},
86
+ "definition": {
87
+ "name": "BaseNode",
88
+ "module": [
89
+ "vellum",
90
+ "workflows",
91
+ "nodes",
92
+ "bases",
93
+ "base",
94
+ ],
95
+ "bases": [],
96
+ },
97
+ }
98
+
99
+ passthrough_node = next(node for node in workflow_raw_data["nodes"] if node["type"] == "MOCKED")
100
+ assert passthrough_node == {
101
+ "type": "MOCKED",
102
+ }
103
+
104
+ final_output_nodes = [node for node in workflow_raw_data["nodes"] if node["type"] == "TERMINAL"]
105
+ assert not DeepDiff(
106
+ [
107
+ {
108
+ "id": "acc91103-f761-47a5-bdd4-0e5e7650bb30",
109
+ "type": "TERMINAL",
110
+ "data": {
111
+ "label": "First Final Output Node",
112
+ "name": "first-final-output-node",
113
+ "target_handle_id": "a0c2eb7a-398e-4f28-b63d-f3bae9b563ee",
114
+ "output_id": "5517e50d-7f40-4f7c-acb2-e329d79a25bf",
115
+ "output_type": "STRING",
116
+ "node_input_id": "16363762-c14a-4162-8fab-525079d3cffe",
117
+ },
118
+ "inputs": [
119
+ {
120
+ "id": "16363762-c14a-4162-8fab-525079d3cffe",
121
+ "key": "node_input",
122
+ "value": {
123
+ "rules": [
124
+ {
125
+ "type": "INPUT_VARIABLE",
126
+ "data": {"input_variable_id": "da086239-d743-4246-b666-5c91e22fb88c"},
127
+ }
128
+ ],
129
+ "combinator": "OR",
130
+ },
131
+ }
132
+ ],
133
+ "display_data": {"position": {"x": 0.0, "y": 0.0}},
134
+ "definition": {
135
+ "name": "FirstFinalOutputNode",
136
+ "module": [
137
+ "tests",
138
+ "workflows",
139
+ "complex_final_output_node",
140
+ "missing_final_output_node",
141
+ ],
142
+ "bases": [
143
+ {
144
+ "name": "FinalOutputNode",
145
+ "module": [
146
+ "vellum",
147
+ "workflows",
148
+ "nodes",
149
+ "displayable",
150
+ "final_output_node",
151
+ "node",
152
+ ],
153
+ }
154
+ ],
155
+ },
156
+ },
157
+ {
158
+ "id": "bb88768d-472e-4997-b7ea-de09163d1b4c",
159
+ "type": "TERMINAL",
160
+ "data": {
161
+ "label": "Final Output",
162
+ "name": "beta",
163
+ "target_handle_id": "5e337b19-cef6-45af-802b-46da4ad7e794",
164
+ "output_id": "5e6d3ea6-ef91-4937-8fff-f33e07446e6a",
165
+ "output_type": "STRING",
166
+ "node_input_id": "47ba0ee9-4725-4065-a178-c929ac556be9",
167
+ },
168
+ "inputs": [
169
+ {
170
+ "id": "47ba0ee9-4725-4065-a178-c929ac556be9",
171
+ "key": "node_input",
172
+ "value": {
173
+ "rules": [
174
+ {
175
+ "type": "NODE_OUTPUT",
176
+ "data": {
177
+ "node_id": "32d88cab-e9fa-4a56-9bc2-fb6e1fd0897f",
178
+ "output_id": "04df0e76-690a-4ae1-ab52-fe825a334dcc",
179
+ },
180
+ }
181
+ ],
182
+ "combinator": "OR",
183
+ },
184
+ }
185
+ ],
186
+ "display_data": {"position": {"x": 0.0, "y": 0.0}},
187
+ "definition": {
188
+ "name": "FinalOutputNode",
189
+ "module": [
190
+ "vellum",
191
+ "workflows",
192
+ "nodes",
193
+ "displayable",
194
+ "final_output_node",
195
+ "node",
196
+ ],
197
+ "bases": [
198
+ {
199
+ "name": "BaseNode",
200
+ "module": [
201
+ "vellum",
202
+ "workflows",
203
+ "nodes",
204
+ "bases",
205
+ "base",
206
+ ],
207
+ "bases": [],
208
+ }
209
+ ],
210
+ },
211
+ },
212
+ ],
213
+ final_output_nodes,
214
+ ignore_order=True,
215
+ )
216
+
217
+
218
+ def test_serialize_workflow__missing_workflow_output():
219
+ # GIVEN a Workflow that contains a terminal node that is unreferenced by the Workflow's Outputs
220
+
221
+ # TODO: Support serialization of BaseNode
222
+ # https://app.shortcut.com/vellum/story/4871/support-serialization-of-base-node
223
+ with mock.patch.object(BaseNodeVellumDisplay, "serialize") as mocked_serialize:
224
+ mocked_serialize.return_value = {"type": "MOCKED"}
225
+ workflow_display = get_workflow_display(
226
+ base_display_class=VellumWorkflowDisplay, workflow_class=MissingWorkflowOutputWorkflow
227
+ )
228
+
229
+ # WHEN we serialize it, it should throw an error
230
+ with pytest.raises(ValueError) as exc_info:
231
+ workflow_display.serialize()
232
+
233
+ assert exc_info.value.args[0] == "Unable to serialize terminal nodes that are not referenced by workflow outputs."
@@ -0,0 +1,46 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import TYPE_CHECKING, Dict, Generic, Tuple, Type, TypeVar
3
+
4
+ from vellum_ee.workflows.display.base import (
5
+ EdgeDisplayType,
6
+ EntrypointDisplayType,
7
+ WorkflowInputsDisplayType,
8
+ WorkflowMetaDisplayType,
9
+ WorkflowOutputDisplayType,
10
+ )
11
+ from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay, PortDisplay
12
+ from vellum.workflows.descriptors.base import BaseDescriptor
13
+ from vellum.workflows.nodes import BaseNode
14
+ from vellum.workflows.ports import Port
15
+ from vellum.workflows.references import OutputReference, WorkflowInputReference
16
+
17
+ if TYPE_CHECKING:
18
+ from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
19
+ from vellum_ee.workflows.display.workflows import BaseWorkflowDisplay
20
+
21
+ NodeDisplayType = TypeVar("NodeDisplayType", bound="BaseNodeDisplay")
22
+ WorkflowDisplayType = TypeVar("WorkflowDisplayType", bound="BaseWorkflowDisplay")
23
+
24
+
25
+ @dataclass
26
+ class WorkflowDisplayContext(
27
+ Generic[
28
+ WorkflowMetaDisplayType,
29
+ WorkflowInputsDisplayType,
30
+ NodeDisplayType,
31
+ EntrypointDisplayType,
32
+ WorkflowOutputDisplayType,
33
+ EdgeDisplayType,
34
+ ]
35
+ ):
36
+ workflow_display_class: Type["BaseWorkflowDisplay"]
37
+ workflow_display: WorkflowMetaDisplayType
38
+ workflow_input_displays: Dict[WorkflowInputReference, WorkflowInputsDisplayType] = field(default_factory=dict)
39
+ node_displays: Dict[Type[BaseNode], "NodeDisplayType"] = field(default_factory=dict)
40
+ node_output_displays: Dict[OutputReference, Tuple[Type[BaseNode], "NodeOutputDisplay"]] = field(
41
+ default_factory=dict
42
+ )
43
+ entrypoint_displays: Dict[Type[BaseNode], EntrypointDisplayType] = field(default_factory=dict)
44
+ workflow_output_displays: Dict[BaseDescriptor, WorkflowOutputDisplayType] = field(default_factory=dict)
45
+ edge_displays: Dict[Tuple[Port, Type[BaseNode]], EdgeDisplayType] = field(default_factory=dict)
46
+ port_displays: Dict[Port, "PortDisplay"] = field(default_factory=dict)
File without changes
File without changes
@@ -0,0 +1,16 @@
1
+ import pytest
2
+ from uuid import UUID
3
+
4
+ from vellum_ee.workflows.display.utils.uuids import uuid4_from_hash
5
+
6
+
7
+ @pytest.mark.parametrize(
8
+ ["input_str", "expected"],
9
+ [
10
+ ("MyExampleString", UUID("b2dadec1-bff4-4e26-8d65-e99e62628cd2")),
11
+ ("My Example String", UUID("a1e68bde-3263-4526-88bd-70f4bf800224")),
12
+ ],
13
+ )
14
+ def test_uuid4_from_hash(input_str, expected):
15
+ actual = uuid4_from_hash(input_str)
16
+ assert actual == expected
@@ -0,0 +1,24 @@
1
+ import hashlib
2
+ from uuid import UUID
3
+
4
+
5
+ def uuid4_from_hash(input_str: str) -> UUID:
6
+ # Create a SHA-256 hash of the input string
7
+ hash_bytes = hashlib.sha256(input_str.encode()).digest()
8
+
9
+ # Modify the hash to follow UUID4 structure
10
+ # UUID4 has 8-4-4-4-12 hexadecimal characters
11
+ # Version bits (4 bits) should be set to 4 (for UUID4),
12
+ # and variant bits (2 bits) should start with 0b10
13
+
14
+ # Convert first 16 bytes into UUID
15
+ hash_list = list(hash_bytes[:16])
16
+
17
+ # Set the version to 4 (UUID4)
18
+ hash_list[6] = (hash_list[6] & 0x0F) | 0x40
19
+
20
+ # Set the variant to 0b10xxxxxx
21
+ hash_list[8] = (hash_list[8] & 0x3F) | 0x80
22
+
23
+ # Create a UUID from the modified bytes
24
+ return UUID(bytes=bytes(hash_list))
@@ -0,0 +1,121 @@
1
+ import enum
2
+ import json
3
+ import typing
4
+ from typing import Any, List, Union, cast
5
+
6
+ from vellum import ChatMessage, SearchResult, SearchResultRequest, VellumVariableType
7
+
8
+ from vellum_ee.workflows.display.types import WorkflowDisplayContext
9
+ from vellum_ee.workflows.display.vellum import (
10
+ ChatHistoryVellumValue,
11
+ ConstantValuePointer,
12
+ InputVariableData,
13
+ InputVariablePointer,
14
+ JsonVellumValue,
15
+ NodeInputValuePointerRule,
16
+ NodeOutputData,
17
+ NodeOutputPointer,
18
+ NumberVellumValue,
19
+ SearchResultsVellumValue,
20
+ StringVellumValue,
21
+ VellumValue,
22
+ WorkspaceSecretData,
23
+ WorkspaceSecretPointer,
24
+ )
25
+ from vellum.workflows.descriptors.base import BaseDescriptor
26
+ from vellum.workflows.references import OutputReference, WorkflowInputReference
27
+ from vellum.workflows.references.node import NodeReference
28
+ from vellum.workflows.references.vellum_secret import VellumSecretReference
29
+ from vellum.workflows.types.core import VellumValuePrimitive
30
+ from vellum.workflows.utils.vellum_variables import primitive_type_to_vellum_variable_type
31
+ from vellum.workflows.vellum_client import create_vellum_client
32
+
33
+ _T = typing.TypeVar("_T")
34
+
35
+
36
+ def infer_vellum_variable_type(value: Any) -> VellumVariableType:
37
+ inferred_type: VellumVariableType
38
+
39
+ if isinstance(value, BaseDescriptor):
40
+ descriptor: BaseDescriptor = value
41
+ if isinstance(descriptor, NodeReference):
42
+ if not isinstance(descriptor.instance, BaseDescriptor):
43
+ raise ValueError(
44
+ f"Expected NodeReference {descriptor.name} to have an instance pointing to a descriptor"
45
+ )
46
+
47
+ descriptor = descriptor.instance
48
+
49
+ inferred_type = primitive_type_to_vellum_variable_type(descriptor)
50
+ else:
51
+ vellum_variable_value = primitive_to_vellum_value(value)
52
+ inferred_type = vellum_variable_value.type
53
+
54
+ return inferred_type
55
+
56
+
57
+ def create_node_input_value_pointer_rule(
58
+ value: Any, display_context: WorkflowDisplayContext
59
+ ) -> NodeInputValuePointerRule:
60
+ if isinstance(value, OutputReference):
61
+ upstream_node, output_display = display_context.node_output_displays[value]
62
+ upstream_node_display = display_context.node_displays[upstream_node]
63
+ return NodeOutputPointer(
64
+ type="NODE_OUTPUT",
65
+ data=NodeOutputData(node_id=str(upstream_node_display.node_id), output_id=str(output_display.id)),
66
+ )
67
+ if isinstance(value, WorkflowInputReference):
68
+ workflow_input_display = display_context.workflow_input_displays[value]
69
+ return InputVariablePointer(
70
+ type="INPUT_VARIABLE", data=InputVariableData(input_variable_id=str(workflow_input_display.id))
71
+ )
72
+ if isinstance(value, VellumSecretReference):
73
+ # TODO: Pass through the name instead of retrieving the ID
74
+ # https://app.shortcut.com/vellum/story/5072
75
+ vellum_client = create_vellum_client()
76
+ workspace_secret = vellum_client.workspace_secrets.retrieve(
77
+ id=value.name,
78
+ )
79
+ return WorkspaceSecretPointer(
80
+ type="WORKSPACE_SECRET",
81
+ data=WorkspaceSecretData(
82
+ type="STRING",
83
+ workspace_secret_id=str(workspace_secret.id),
84
+ ),
85
+ )
86
+
87
+ if not isinstance(value, BaseDescriptor):
88
+ vellum_value = primitive_to_vellum_value(value)
89
+ return ConstantValuePointer(type="CONSTANT_VALUE", data=vellum_value)
90
+
91
+ raise ValueError(f"Unsupported descriptor type: {value.__class__.__name__}")
92
+
93
+
94
+ def primitive_to_vellum_value(value: VellumValuePrimitive) -> VellumValue:
95
+ """Converts a python primitive to a VellumVariableValue"""
96
+
97
+ if isinstance(value, str):
98
+ return StringVellumValue(value=value)
99
+ elif isinstance(value, enum.Enum):
100
+ return StringVellumValue(value=value.value)
101
+ elif isinstance(value, (int, float)):
102
+ return NumberVellumValue(value=value)
103
+ elif isinstance(value, list) and (
104
+ all(isinstance(message, ChatMessage) for message in value)
105
+ or all(isinstance(message, ChatMessage) for message in value)
106
+ ):
107
+ chat_messages = cast(Union[List[ChatMessage], List[ChatMessage]], value)
108
+ return ChatHistoryVellumValue(value=chat_messages)
109
+ elif isinstance(value, list) and (
110
+ all(isinstance(search_result, SearchResultRequest) for search_result in value)
111
+ or all(isinstance(search_result, SearchResult) for search_result in value)
112
+ ):
113
+ search_results = cast(Union[List[SearchResultRequest], List[SearchResult]], value)
114
+ return SearchResultsVellumValue(value=search_results)
115
+
116
+ try:
117
+ json_value = json.dumps(value)
118
+ except json.JSONDecodeError:
119
+ raise ValueError(f"Unsupported variable type: {value.__class__.__name__}")
120
+
121
+ return JsonVellumValue(value=json.loads(json_value))