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,386 @@
1
+ import logging
2
+ from uuid import UUID
3
+ from typing import Dict, List, Optional, Type, cast
4
+
5
+ from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
6
+ from vellum_ee.workflows.display.nodes.types import PortDisplay
7
+ from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input
8
+ from vellum_ee.workflows.display.utils.uuids import uuid4_from_hash
9
+ from vellum_ee.workflows.display.utils.vellum import infer_vellum_variable_type
10
+ from vellum_ee.workflows.display.vellum import (
11
+ EdgeVellumDisplay,
12
+ EdgeVellumDisplayOverrides,
13
+ EntrypointVellumDisplay,
14
+ EntrypointVellumDisplayOverrides,
15
+ NodeDisplayData,
16
+ WorkflowInputsVellumDisplay,
17
+ WorkflowInputsVellumDisplayOverrides,
18
+ WorkflowMetaVellumDisplay,
19
+ WorkflowMetaVellumDisplayOverrides,
20
+ WorkflowOutputVellumDisplay,
21
+ WorkflowOutputVellumDisplayOverrides,
22
+ )
23
+ from vellum_ee.workflows.display.workflows.base_workflow_display import BaseWorkflowDisplay
24
+ from vellum.workflows.descriptors.base import BaseDescriptor
25
+ from vellum.workflows.edges import Edge
26
+ from vellum.workflows.nodes.bases import BaseNode
27
+ from vellum.workflows.nodes.displayable.final_output_node import FinalOutputNode
28
+ from vellum.workflows.nodes.utils import get_wrapped_node, has_wrapped_node
29
+ from vellum.workflows.ports import Port
30
+ from vellum.workflows.references import WorkflowInputReference
31
+ from vellum.workflows.references.output import OutputReference
32
+ from vellum.workflows.types.core import JsonArray, JsonObject
33
+ from vellum.workflows.types.generics import WorkflowType
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+
38
+ class VellumWorkflowDisplay(
39
+ BaseWorkflowDisplay[
40
+ WorkflowType,
41
+ WorkflowMetaVellumDisplay,
42
+ WorkflowMetaVellumDisplayOverrides,
43
+ WorkflowInputsVellumDisplay,
44
+ WorkflowInputsVellumDisplayOverrides,
45
+ BaseNodeVellumDisplay,
46
+ EntrypointVellumDisplay,
47
+ EntrypointVellumDisplayOverrides,
48
+ EdgeVellumDisplay,
49
+ EdgeVellumDisplayOverrides,
50
+ WorkflowOutputVellumDisplay,
51
+ WorkflowOutputVellumDisplayOverrides,
52
+ ]
53
+ ):
54
+ node_display_base_class = BaseNodeVellumDisplay
55
+
56
+ def serialize(self, raise_errors: bool = True) -> JsonObject:
57
+ input_variables: JsonArray = []
58
+ for workflow_input, workflow_input_display in self.display_context.workflow_input_displays.items():
59
+ input_variables.append(
60
+ {
61
+ "id": str(workflow_input_display.id),
62
+ "key": workflow_input.name,
63
+ "type": infer_vellum_variable_type(workflow_input),
64
+ # TODO: Add support for serializing default, required, and extensions
65
+ # https://app.shortcut.com/vellum/story/5429
66
+ "default": None,
67
+ "required": None,
68
+ "extensions": None,
69
+ }
70
+ )
71
+
72
+ nodes: JsonArray = []
73
+ edges: JsonArray = []
74
+
75
+ # Add a single synthetic node for the workflow entrypoint
76
+ base_node_definition: JsonObject = {
77
+ "name": BaseNode.__name__,
78
+ "module": cast(JsonArray, BaseNode.__module__.split(".")),
79
+ "bases": [],
80
+ }
81
+ nodes.append(
82
+ {
83
+ "id": str(self.display_context.workflow_display.entrypoint_node_id),
84
+ "type": "ENTRYPOINT",
85
+ "inputs": [],
86
+ "data": {
87
+ "label": "Entrypoint Node",
88
+ "source_handle_id": str(self.display_context.workflow_display.entrypoint_node_source_handle_id),
89
+ },
90
+ "display_data": self.display_context.workflow_display.entrypoint_node_display.dict(),
91
+ "definition": base_node_definition,
92
+ },
93
+ )
94
+
95
+ # Add all the nodes in the workflow
96
+ for node, node_display in self.display_context.node_displays.items():
97
+ if getattr(node, "_is_wrapped_node") is True:
98
+ # Nodes that are wrapped or decorated by other nodes are not serialized here
99
+ # They are instead serialized by the wrapper node
100
+ continue
101
+
102
+ try:
103
+ serialized_node = node_display.serialize(self.display_context)
104
+ except NotImplementedError:
105
+ logger.warning("Unable to serialize node", extra={"node": node.__name__})
106
+ if raise_errors:
107
+ raise
108
+ else:
109
+ continue
110
+
111
+ nodes.append(serialized_node)
112
+
113
+ synthetic_output_edges: JsonArray = []
114
+ output_variables: JsonArray = []
115
+ final_output_nodes = [
116
+ node for node in self.display_context.node_displays.keys() if issubclass(node, FinalOutputNode)
117
+ ]
118
+ final_output_node_outputs = {node.Outputs.value for node in final_output_nodes}
119
+ unreferenced_final_output_node_outputs = final_output_node_outputs.copy()
120
+ final_output_node_definition: JsonObject = {
121
+ "name": FinalOutputNode.__name__,
122
+ "module": cast(JsonArray, FinalOutputNode.__module__.split(".")),
123
+ "bases": [base_node_definition],
124
+ }
125
+
126
+ # Add a synthetic Terminal Node and track the Workflow's output variables for each Workflow output
127
+ for workflow_output, workflow_output_display in self.display_context.workflow_output_displays.items():
128
+ final_output_node_id = workflow_output_display.node_id
129
+ inferred_type = infer_vellum_variable_type(workflow_output)
130
+
131
+ # Remove the terminal node output from the unreferenced set
132
+ unreferenced_final_output_node_outputs.discard(cast(OutputReference, workflow_output.instance))
133
+
134
+ if workflow_output.instance not in final_output_node_outputs:
135
+ # Create a synthetic terminal node only if there is no terminal node for this output
136
+ node_input = create_node_input(
137
+ final_output_node_id,
138
+ "node_input",
139
+ # This is currently the wrapper node's output, but we want the wrapped node
140
+ workflow_output.instance,
141
+ self.display_context,
142
+ workflow_output_display.node_input_id,
143
+ )
144
+
145
+ source_node_display: Optional[BaseNodeVellumDisplay]
146
+ first_rule = node_input.value.rules[0]
147
+ if first_rule.type == "NODE_OUTPUT":
148
+ source_node_id = UUID(first_rule.data.node_id)
149
+ try:
150
+ source_node_display = [
151
+ node_display
152
+ for node_display in self.display_context.node_displays.values()
153
+ if node_display.node_id == source_node_id
154
+ ][0]
155
+ except IndexError:
156
+ source_node_display = None
157
+
158
+ nodes.append(
159
+ {
160
+ "id": str(final_output_node_id),
161
+ "type": "TERMINAL",
162
+ "data": {
163
+ "label": workflow_output_display.label,
164
+ "name": workflow_output_display.name,
165
+ "target_handle_id": str(workflow_output_display.target_handle_id),
166
+ "output_id": str(workflow_output_display.id),
167
+ "output_type": inferred_type,
168
+ "node_input_id": str(node_input.id),
169
+ },
170
+ "inputs": [node_input.dict()],
171
+ "display_data": workflow_output_display.display_data.dict(),
172
+ "definition": final_output_node_definition,
173
+ }
174
+ )
175
+
176
+ if source_node_display:
177
+ synthetic_output_edges.append(
178
+ {
179
+ "id": str(workflow_output_display.edge_id),
180
+ "source_node_id": str(source_node_display.node_id),
181
+ "source_handle_id": str(
182
+ source_node_display.get_source_handle_id(
183
+ port_displays=self.display_context.port_displays
184
+ )
185
+ ),
186
+ "target_node_id": str(workflow_output_display.node_id),
187
+ "target_handle_id": str(workflow_output_display.target_handle_id),
188
+ "type": "DEFAULT",
189
+ }
190
+ )
191
+
192
+ output_variables.append(
193
+ {
194
+ "id": str(workflow_output_display.id),
195
+ "key": workflow_output_display.name,
196
+ "type": inferred_type,
197
+ }
198
+ )
199
+
200
+ # If there are terminal nodes with no workflow output reference,
201
+ # raise a serialization error
202
+ if len(unreferenced_final_output_node_outputs) > 0:
203
+ raise ValueError("Unable to serialize terminal nodes that are not referenced by workflow outputs.")
204
+
205
+ # Add an edge for each edge in the workflow
206
+ all_edge_displays: List[EdgeVellumDisplay] = [
207
+ # Create a synthetic edge from the synthetic entrypoint node to each actual entrypoint
208
+ *[
209
+ entrypoint_display.edge_display
210
+ for entrypoint_display in self.display_context.entrypoint_displays.values()
211
+ ],
212
+ # Include the concrete edges in the workflow
213
+ *self.display_context.edge_displays.values(),
214
+ ]
215
+
216
+ for edge_display in all_edge_displays:
217
+ edges.append(
218
+ {
219
+ "id": str(edge_display.id),
220
+ "source_node_id": str(edge_display.source_node_id),
221
+ "source_handle_id": str(edge_display.source_handle_id),
222
+ "target_node_id": str(edge_display.target_node_id),
223
+ "target_handle_id": str(edge_display.target_handle_id),
224
+ "type": edge_display.type,
225
+ }
226
+ )
227
+
228
+ edges.extend(synthetic_output_edges)
229
+
230
+ return {
231
+ "workflow_raw_data": {
232
+ "nodes": nodes,
233
+ "edges": edges,
234
+ "display_data": self.display_context.workflow_display.display_data.dict(),
235
+ "definition": {
236
+ "name": self._workflow.__name__,
237
+ "module": cast(JsonArray, self._workflow.__module__.split(".")),
238
+ },
239
+ },
240
+ "input_variables": input_variables,
241
+ "output_variables": output_variables,
242
+ }
243
+
244
+ def _generate_workflow_meta_display(self) -> WorkflowMetaVellumDisplay:
245
+ overrides = self.workflow_display
246
+ if overrides:
247
+ return WorkflowMetaVellumDisplay(
248
+ entrypoint_node_id=overrides.entrypoint_node_id,
249
+ entrypoint_node_source_handle_id=overrides.entrypoint_node_source_handle_id,
250
+ entrypoint_node_display=overrides.entrypoint_node_display,
251
+ display_data=overrides.display_data,
252
+ )
253
+
254
+ entrypoint_node_id = uuid4_from_hash(f"{self.workflow_id}|entrypoint_node_id")
255
+ entrypoint_node_source_handle_id = uuid4_from_hash(f"{self.workflow_id}|entrypoint_node_source_handle_id")
256
+
257
+ return WorkflowMetaVellumDisplay(
258
+ entrypoint_node_id=entrypoint_node_id,
259
+ entrypoint_node_source_handle_id=entrypoint_node_source_handle_id,
260
+ entrypoint_node_display=NodeDisplayData(),
261
+ )
262
+
263
+ def _generate_workflow_input_display(
264
+ self, workflow_input: WorkflowInputReference, overrides: Optional[WorkflowInputsVellumDisplayOverrides] = None
265
+ ) -> WorkflowInputsVellumDisplay:
266
+ workflow_input_id: UUID
267
+ if overrides:
268
+ workflow_input_id = overrides.id
269
+ else:
270
+ workflow_input_id = uuid4_from_hash(f"{self.workflow_id}|inputs|id|{workflow_input.name}")
271
+
272
+ return WorkflowInputsVellumDisplay(id=workflow_input_id)
273
+
274
+ def _generate_entrypoint_display(
275
+ self,
276
+ entrypoint: Type[BaseNode],
277
+ workflow_display: WorkflowMetaVellumDisplay,
278
+ node_displays: Dict[Type[BaseNode], BaseNodeVellumDisplay],
279
+ overrides: Optional[EntrypointVellumDisplayOverrides] = None,
280
+ ) -> EntrypointVellumDisplay:
281
+ entrypoint_node_id = workflow_display.entrypoint_node_id
282
+ source_handle_id = workflow_display.entrypoint_node_source_handle_id
283
+
284
+ edge_display_overrides = overrides.edge_display if overrides else None
285
+ entrypoint_id = (
286
+ edge_display_overrides.id
287
+ if edge_display_overrides
288
+ else uuid4_from_hash(f"{self.workflow_id}|id|{entrypoint_node_id}")
289
+ )
290
+
291
+ if has_wrapped_node(entrypoint):
292
+ wrapped_node = get_wrapped_node(entrypoint)
293
+ if wrapped_node._is_wrapped_node:
294
+ entrypoint = wrapped_node
295
+
296
+ target_node_id = node_displays[entrypoint].node_id
297
+ target_handle_id = node_displays[entrypoint].get_target_handle_id()
298
+
299
+ edge_display = self._generate_edge_display_from_source(
300
+ entrypoint_node_id, source_handle_id, target_node_id, target_handle_id, overrides=edge_display_overrides
301
+ )
302
+
303
+ return EntrypointVellumDisplay(id=entrypoint_id, edge_display=edge_display)
304
+
305
+ def _generate_workflow_output_display(
306
+ self,
307
+ output: BaseDescriptor,
308
+ overrides: Optional[WorkflowOutputVellumDisplayOverrides] = None,
309
+ ) -> WorkflowOutputVellumDisplay:
310
+ if overrides:
311
+ return WorkflowOutputVellumDisplay(
312
+ id=overrides.id,
313
+ name=overrides.name,
314
+ label=overrides.label,
315
+ node_id=overrides.node_id,
316
+ node_input_id=overrides.node_input_id,
317
+ target_handle_id=overrides.target_handle_id,
318
+ edge_id=overrides.edge_id,
319
+ display_data=overrides.display_data,
320
+ )
321
+
322
+ output_id = uuid4_from_hash(f"{self.workflow_id}|id|{output.name}")
323
+ edge_id = uuid4_from_hash(f"{self.workflow_id}|edge_id|{output.name}")
324
+ node_id = uuid4_from_hash(f"{self.workflow_id}|node_id|{output.name}")
325
+ node_input_id = uuid4_from_hash(f"{self.workflow_id}|node_input_id|{output.name}")
326
+ target_handle_id = uuid4_from_hash(f"{self.workflow_id}|target_handle_id|{output.name}")
327
+
328
+ return WorkflowOutputVellumDisplay(
329
+ id=output_id,
330
+ node_id=node_id,
331
+ node_input_id=node_input_id,
332
+ name=output.name,
333
+ label="Final Output",
334
+ target_handle_id=target_handle_id,
335
+ edge_id=edge_id,
336
+ display_data=NodeDisplayData(),
337
+ )
338
+
339
+ def _generate_edge_display(
340
+ self,
341
+ edge: Edge,
342
+ node_displays: Dict[Type[BaseNode], BaseNodeVellumDisplay],
343
+ port_displays: Dict[Port, PortDisplay],
344
+ overrides: Optional[EdgeVellumDisplayOverrides] = None,
345
+ ) -> EdgeVellumDisplay:
346
+ source_node = edge.from_port.node_class
347
+ target_node = edge.to_node
348
+
349
+ if has_wrapped_node(source_node):
350
+ source_node = get_wrapped_node(source_node)
351
+
352
+ if has_wrapped_node(target_node):
353
+ target_node = get_wrapped_node(target_node)
354
+
355
+ source_node_id = node_displays[source_node].node_id
356
+ source_handle_id = port_displays[edge.from_port].id
357
+
358
+ target_node_display = node_displays[target_node]
359
+ target_node_id = target_node_display.node_id
360
+ target_handle_id = target_node_display.get_target_handle_id()
361
+
362
+ return self._generate_edge_display_from_source(
363
+ source_node_id, source_handle_id, target_node_id, target_handle_id, overrides
364
+ )
365
+
366
+ def _generate_edge_display_from_source(
367
+ self,
368
+ source_node_id: UUID,
369
+ source_handle_id: UUID,
370
+ target_node_id: UUID,
371
+ target_handle_id: UUID,
372
+ overrides: Optional[EdgeVellumDisplayOverrides] = None,
373
+ ) -> EdgeVellumDisplay:
374
+ edge_id: UUID
375
+ if overrides:
376
+ edge_id = overrides.id
377
+ else:
378
+ edge_id = uuid4_from_hash(f"{self.workflow_id}|id|{source_node_id}|{target_node_id}")
379
+
380
+ return EdgeVellumDisplay(
381
+ id=edge_id,
382
+ source_node_id=source_node_id,
383
+ target_node_id=target_node_id,
384
+ source_handle_id=source_handle_id,
385
+ target_handle_id=target_handle_id,
386
+ )