vellum-ai 0.9.16rc2__py3-none-any.whl → 0.9.16rc4__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 (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))