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,437 @@
1
+ from vellum.workflows.edges.edge import Edge
2
+ from vellum.workflows.graph.graph import Graph
3
+ from vellum.workflows.nodes.bases.base import BaseNode
4
+ from vellum.workflows.ports.port import Port
5
+
6
+
7
+ def test_graph__from_node():
8
+ # GIVEN a node
9
+ class MyNode(BaseNode):
10
+ pass
11
+
12
+ # WHEN we create a graph from the node
13
+ graph = Graph.from_node(MyNode)
14
+
15
+ # THEN the graph has the node as the entrypoint
16
+ assert set(graph.entrypoints) == {MyNode}
17
+
18
+ # AND one node
19
+ assert len(list(graph.nodes)) == 1
20
+
21
+ # AND no edges
22
+ assert len(list(graph.edges)) == 0
23
+
24
+
25
+ def test_graph__from_edge():
26
+ # GIVEN an edge
27
+ class SourceNode(BaseNode):
28
+ pass
29
+
30
+ class TargetNode(BaseNode):
31
+ pass
32
+
33
+ edge = Edge(SourceNode.Ports.default, TargetNode)
34
+
35
+ # WHEN we create a graph from the edge
36
+ graph = Graph.from_edge(edge)
37
+
38
+ # THEN the graph has the source node as the entrypoint
39
+ assert set(graph.entrypoints) == {SourceNode}
40
+
41
+ # AND two nodes
42
+ assert len(list(graph.nodes)) == 2
43
+
44
+ # AND one edge
45
+ assert len(list(graph.edges)) == 1
46
+
47
+
48
+ def test_graph__node_to_node():
49
+ # GIVEN two nodes
50
+ class SourceNode(BaseNode):
51
+ pass
52
+
53
+ class TargetNode(BaseNode):
54
+ pass
55
+
56
+ # WHEN we create a graph from the source node to the target node
57
+ graph = SourceNode >> TargetNode
58
+
59
+ # THEN the graph has the source node as the entrypoint
60
+ assert set(graph.entrypoints) == {SourceNode}
61
+
62
+ # AND two nodes
63
+ assert len(list(graph.nodes)) == 2
64
+
65
+ # AND one edge
66
+ assert len(list(graph.edges)) == 1
67
+
68
+
69
+ def test_graph__port_to_node():
70
+ # GIVEN a two nodes, where the source node has a port
71
+ class SourceNode(BaseNode):
72
+ class Ports(BaseNode.Ports):
73
+ output = Port.on_else()
74
+
75
+ class TargetNode(BaseNode):
76
+ pass
77
+
78
+ # WHEN we create a graph from the source port to the target node
79
+ graph = SourceNode.Ports.output >> TargetNode
80
+
81
+ # THEN the graph has the source node as the entrypoint
82
+ assert set(graph.entrypoints) == {SourceNode}
83
+
84
+ # AND two nodes
85
+ assert len(list(graph.nodes)) == 2
86
+
87
+ # AND one edge
88
+ assert len(list(graph.edges)) == 1
89
+
90
+
91
+ def test_graph__graph_to_node():
92
+ # GIVEN three nodes
93
+ class SourceNode(BaseNode):
94
+ pass
95
+
96
+ class MiddleNode(BaseNode):
97
+ pass
98
+
99
+ class TargetNode(BaseNode):
100
+ pass
101
+
102
+ # WHEN we create a graph from the source node to the target node via the middle node
103
+ graph = SourceNode >> MiddleNode >> TargetNode
104
+
105
+ # THEN the graph has the source node as the entrypoint
106
+ assert set(graph.entrypoints) == {SourceNode}
107
+
108
+ # AND three nodes
109
+ assert len(list(graph.nodes)) == 3
110
+
111
+ # AND two edges
112
+ assert len(list(graph.edges)) == 2
113
+
114
+
115
+ def test_graph__edgeless_graph_to_node():
116
+ # GIVEN two nodes
117
+ class SourceNode(BaseNode):
118
+ pass
119
+
120
+ class TargetNode(BaseNode):
121
+ pass
122
+
123
+ # AND a graph of the first node
124
+ subgraph = Graph.from_node(SourceNode)
125
+
126
+ # WHEN we create a graph from the source graph to the target node
127
+ graph = subgraph >> TargetNode
128
+
129
+ # THEN the graph has the source node as the entrypoint
130
+ assert set(graph.entrypoints) == {SourceNode}
131
+
132
+ # AND two nodes
133
+ assert len(list(graph.nodes)) == 2
134
+
135
+ # AND one edge
136
+ assert len(list(graph.edges)) == 1
137
+
138
+ # AND the first node's port has reference to the edge
139
+ assert set(SourceNode.Ports.default.edges) == set(graph.edges)
140
+
141
+
142
+ def test_graph__edgeless_graph_to_graph():
143
+ # GIVEN two nodes
144
+ class SourceNode(BaseNode):
145
+ pass
146
+
147
+ class TargetNode(BaseNode):
148
+ pass
149
+
150
+ # AND a graph of the first node
151
+ subgraph = Graph.from_node(SourceNode)
152
+
153
+ # AND a graph of the second node
154
+ target_subgraph = Graph.from_node(TargetNode)
155
+
156
+ # WHEN we create a graph from the source graph to the target node
157
+ graph = subgraph >> target_subgraph
158
+
159
+ # THEN the graph has the source node as the entrypoint
160
+ assert set(graph.entrypoints) == {SourceNode}
161
+
162
+ # AND two nodes
163
+ assert len(list(graph.nodes)) == 2
164
+
165
+ # AND one edge
166
+ assert len(list(graph.edges)) == 1
167
+
168
+ # AND the first node's port has reference to the edges
169
+ assert set(SourceNode.Ports.default.edges) == set(graph.edges)
170
+
171
+
172
+ def test_graph__graph_to_edgeless_graph():
173
+ # GIVEN three nodes
174
+ class SourceNode(BaseNode):
175
+ pass
176
+
177
+ class MiddleNode(BaseNode):
178
+ pass
179
+
180
+ class TargetNode(BaseNode):
181
+ pass
182
+
183
+ # AND a graph of the first two nodes
184
+ subgraph = SourceNode >> MiddleNode
185
+
186
+ # AND a graph of the third node
187
+ target_subgraph = Graph.from_node(TargetNode)
188
+
189
+ # WHEN we create a graph from the source graph to the target node
190
+ graph = subgraph >> target_subgraph
191
+
192
+ # THEN the graph has the source node as the entrypoint
193
+ assert set(graph.entrypoints) == {SourceNode}
194
+
195
+ # AND three nodes
196
+ assert len(list(graph.nodes)) == 3
197
+
198
+ # AND two edges
199
+ assert len(list(graph.edges)) == 2
200
+
201
+
202
+ def test_graph__node_to_graph():
203
+ # GIVEN three nodes
204
+ class SourceNode(BaseNode):
205
+ pass
206
+
207
+ class MiddleNode(BaseNode):
208
+ pass
209
+
210
+ class TargetNode(BaseNode):
211
+ pass
212
+
213
+ # AND a graph of the last two nodes
214
+ target_subgraph = MiddleNode >> TargetNode
215
+
216
+ # WHEN we create a graph from the source node to the target graph
217
+ graph = SourceNode >> target_subgraph
218
+
219
+ # THEN the graph has the source node as the entrypoint
220
+ assert set(graph.entrypoints) == {SourceNode}
221
+
222
+ # AND three nodes
223
+ assert len(list(graph.nodes)) == 3
224
+
225
+ # AND two edges
226
+ assert len(list(graph.edges)) == 2
227
+
228
+
229
+ def test_graph__repeated_edge():
230
+ # GIVEN two nodes
231
+ class SourceNode(BaseNode):
232
+ pass
233
+
234
+ class TargetNode(BaseNode):
235
+ pass
236
+
237
+ # WHEN we create a graph from the source node to the target node twice
238
+ graph = SourceNode >> TargetNode >> SourceNode >> TargetNode
239
+
240
+ # THEN the graph has the source node as the entrypoint
241
+ assert set(graph.entrypoints) == {SourceNode}
242
+
243
+ # AND two nodes
244
+ assert len(list(graph.nodes)) == 2
245
+
246
+ # AND two edges
247
+ assert len(list(graph.edges)) == 2
248
+
249
+ # AND the first node's port has reference to just one edge
250
+ assert len(list(SourceNode.Ports.default.edges)) == 1
251
+
252
+
253
+ def test_graph__node_to_set():
254
+ # GIVEN three nodes, one with ports
255
+ class SourceNode(BaseNode):
256
+ pass
257
+
258
+ class MiddleNode(BaseNode):
259
+ class Ports(BaseNode.Ports):
260
+ top = Port.on_if(SourceNode.Execution.count.less_than(1))
261
+ bottom = Port.on_else()
262
+
263
+ class TargetNode(BaseNode):
264
+ pass
265
+
266
+ # WHEN we create a graph with a set
267
+ graph = SourceNode >> {
268
+ MiddleNode.Ports.top >> SourceNode,
269
+ MiddleNode.Ports.bottom >> TargetNode,
270
+ }
271
+
272
+ # THEN the graph has the source node as the entrypoint
273
+ assert set(graph.entrypoints) == {SourceNode}
274
+
275
+ # AND three nodes
276
+ assert len(list(graph.nodes)) == 3
277
+
278
+ # AND two edges
279
+ assert len(list(graph.edges)) == 3
280
+
281
+
282
+ def test_graph__graph_to_set():
283
+ # GIVEN four nodes, one with ports
284
+ class SourceNode(BaseNode):
285
+ pass
286
+
287
+ class SecondNode(BaseNode):
288
+ pass
289
+
290
+ class MiddleNode(BaseNode):
291
+ class Ports(BaseNode.Ports):
292
+ top = Port.on_if(SourceNode.Execution.count.less_than(1))
293
+ bottom = Port.on_else()
294
+
295
+ class TargetNode(BaseNode):
296
+ pass
297
+
298
+ # WHEN we create a graph between a graph and a set
299
+ graph = (
300
+ SourceNode
301
+ >> SecondNode
302
+ >> {
303
+ MiddleNode.Ports.top >> SourceNode,
304
+ MiddleNode.Ports.bottom >> TargetNode,
305
+ }
306
+ )
307
+
308
+ # THEN the graph has the source node as the entrypoint
309
+ assert set(graph.entrypoints) == {SourceNode}
310
+
311
+ # AND three nodes
312
+ assert len(list(graph.nodes)) == 4
313
+
314
+ # AND two edges
315
+ assert len(list(graph.edges)) == 4
316
+
317
+
318
+ def test_graph__graph_set_to_set():
319
+ # GIVEN five nodes
320
+ class SourceNode(BaseNode):
321
+ pass
322
+
323
+ class TopNode(BaseNode):
324
+ pass
325
+
326
+ class BottomNode(BaseNode):
327
+ pass
328
+
329
+ class ConditionalNode(BaseNode):
330
+ class Ports(BaseNode.Ports):
331
+ top = Port.on_if(SourceNode.Execution.count.less_than(1))
332
+ bottom = Port.on_else()
333
+
334
+ class EndNode(BaseNode):
335
+ pass
336
+
337
+ # WHEN we create a graph that draws an edge between sets
338
+ graph = (
339
+ SourceNode
340
+ >> {
341
+ TopNode,
342
+ BottomNode,
343
+ }
344
+ >> {
345
+ ConditionalNode.Ports.top >> SourceNode,
346
+ ConditionalNode.Ports.bottom >> EndNode,
347
+ }
348
+ )
349
+
350
+ # THEN the graph has the source node as the entrypoint
351
+ assert set(graph.entrypoints) == {SourceNode}
352
+
353
+ # AND three nodes
354
+ assert len(list(graph.nodes)) == 5
355
+
356
+ # AND two edges
357
+ assert len(list(graph.edges)) == 6
358
+
359
+
360
+ def test_graph__port_to_graph():
361
+ # GIVEN three nodes where the first node has a port
362
+ class SourceNode(BaseNode):
363
+ class Ports(BaseNode.Ports):
364
+ custom = Port.on_else()
365
+
366
+ class MiddleNode(BaseNode):
367
+ pass
368
+
369
+ class EndNode(BaseNode):
370
+ pass
371
+
372
+ # WHEN we create a graph from the port to a subgraph
373
+ graph = SourceNode.Ports.custom >> (MiddleNode >> EndNode)
374
+
375
+ # THEN the graph has the source node as the entrypoint
376
+ assert set(graph.entrypoints) == {SourceNode}
377
+
378
+ # AND three nodes
379
+ assert len(list(graph.nodes)) == 3
380
+
381
+ # AND two edges
382
+ assert len(list(graph.edges)) == 2
383
+
384
+
385
+ def test_graph__port_to_set():
386
+ # GIVEN three nodes where the first node has a port
387
+ class SourceNode(BaseNode):
388
+ class Ports(BaseNode.Ports):
389
+ custom = Port.on_else()
390
+
391
+ class TopNode(BaseNode):
392
+ pass
393
+
394
+ class BottomNode(BaseNode):
395
+ pass
396
+
397
+ # WHEN we create a graph from the port to the set
398
+ graph = SourceNode.Ports.custom >> {
399
+ TopNode,
400
+ BottomNode,
401
+ }
402
+
403
+ # THEN the graph has the source node as the entrypoint
404
+ assert set(graph.entrypoints) == {SourceNode}
405
+
406
+ # AND three nodes
407
+ assert len(list(graph.nodes)) == 3
408
+
409
+ # AND two edges
410
+ assert len(list(graph.edges)) == 2
411
+
412
+
413
+ def test_graph__set_to_node():
414
+ # GIVEN three nodes
415
+ class TopNode(BaseNode):
416
+ pass
417
+
418
+ class BottomNode(BaseNode):
419
+ pass
420
+
421
+ class TargetNode(BaseNode):
422
+ pass
423
+
424
+ # WHEN we create a graph from a set to a node
425
+ graph = {
426
+ TopNode,
427
+ BottomNode,
428
+ } >> TargetNode
429
+
430
+ # THEN the graph has both the top node and the bottom node as the entrypoints
431
+ assert set(graph.entrypoints) == {TopNode, BottomNode}
432
+
433
+ # AND three nodes
434
+ assert len(list(graph.nodes)) == 3
435
+
436
+ # AND two edges
437
+ assert len(list(graph.edges)) == 2
@@ -0,0 +1,5 @@
1
+ from .base import BaseInputs
2
+
3
+ __all__ = [
4
+ "BaseInputs",
5
+ ]
@@ -0,0 +1,55 @@
1
+ from typing import Any, Iterator, Tuple, Type
2
+ from typing_extensions import dataclass_transform
3
+
4
+ from pydantic import GetCoreSchemaHandler
5
+ from pydantic_core import core_schema
6
+
7
+ from vellum.workflows.references import ExternalInputReference, WorkflowInputReference
8
+ from vellum.workflows.references.input import InputReference
9
+ from vellum.workflows.types.utils import get_class_attr_names, infer_types
10
+
11
+
12
+ @dataclass_transform(kw_only_default=True)
13
+ class _BaseInputsMeta(type):
14
+ def __getattribute__(cls, name: str) -> Any:
15
+ if not name.startswith("_") and name in cls.__annotations__ and issubclass(cls, BaseInputs):
16
+ instance = vars(cls).get(name)
17
+ types = infer_types(cls, name)
18
+
19
+ if getattr(cls, "__descriptor_class__", None) is ExternalInputReference:
20
+ return ExternalInputReference(name=name, types=types, instance=instance, inputs_class=cls)
21
+ else:
22
+ return WorkflowInputReference(name=name, types=types, instance=instance, inputs_class=cls)
23
+
24
+ return super().__getattribute__(name)
25
+
26
+ def __iter__(cls) -> Iterator[InputReference]:
27
+ # We iterate through the inheritance hierarchy to find all the WorkflowInputReference attached to this
28
+ # Inputs class. __mro__ is the method resolution order, which is the order in which base classes are resolved.
29
+ for resolved_cls in cls.__mro__:
30
+ attr_names = get_class_attr_names(resolved_cls)
31
+ for attr_name in attr_names:
32
+ attr_value = getattr(resolved_cls, attr_name)
33
+ if not isinstance(attr_value, (WorkflowInputReference, ExternalInputReference)):
34
+ continue
35
+
36
+ yield attr_value
37
+
38
+
39
+ class BaseInputs(metaclass=_BaseInputsMeta):
40
+ __parent_class__: Type = type(None)
41
+
42
+ def __init__(self, **kwargs: Any) -> None:
43
+ for name, value in kwargs.items():
44
+ setattr(self, name, value)
45
+
46
+ def __iter__(self) -> Iterator[Tuple[InputReference, Any]]:
47
+ for input_descriptor in self.__class__:
48
+ if hasattr(self, input_descriptor.name):
49
+ yield (input_descriptor, getattr(self, input_descriptor.name))
50
+
51
+ @classmethod
52
+ def __get_pydantic_core_schema__(
53
+ cls, source_type: Type[Any], handler: GetCoreSchemaHandler
54
+ ) -> core_schema.CoreSchema:
55
+ return core_schema.is_instance_schema(cls)
@@ -0,0 +1,14 @@
1
+ import logging
2
+ import os
3
+
4
+
5
+ def load_logger() -> logging.Logger:
6
+ logger = logging.getLogger(__package__)
7
+ logger.setLevel(os.getenv("LOG_LEVEL", logging.DEBUG))
8
+
9
+ # Add a stream handler so that we see logs in the console when running tests
10
+ handler = logging.StreamHandler()
11
+ handler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
12
+ logger.addHandler(handler)
13
+
14
+ return logger
@@ -0,0 +1,46 @@
1
+ from vellum.workflows.nodes.bases import BaseNode
2
+ from vellum.workflows.nodes.core import ErrorNode, InlineSubworkflowNode, MapNode, RetryNode, TemplatingNode
3
+ from vellum.workflows.nodes.displayable import (
4
+ APINode,
5
+ CodeExecutionNode,
6
+ ConditionalNode,
7
+ FinalOutputNode,
8
+ GuardrailNode,
9
+ InlinePromptNode,
10
+ PromptDeploymentNode,
11
+ SearchNode,
12
+ SubworkflowDeploymentNode,
13
+ )
14
+ from vellum.workflows.nodes.displayable.bases import (
15
+ BaseInlinePromptNode as BaseInlinePromptNode,
16
+ BasePromptDeploymentNode as BasePromptDeploymentNode,
17
+ BaseSearchNode as BaseSearchNode,
18
+ )
19
+
20
+ __all__ = [
21
+ # Base
22
+ "BaseNode",
23
+ # Core
24
+ "ErrorNode",
25
+ "InlineSubworkflowNode",
26
+ "MapNode",
27
+ "RetryNode",
28
+ "TemplatingNode",
29
+ # Vellum Base Nodes
30
+ "BaseSearchNode",
31
+ "BaseInlinePromptNode",
32
+ "BasePromptDeploymentNode",
33
+ # Vellum Nodes
34
+ "APINode",
35
+ "CodeExecutionNode",
36
+ "GuardrailNode",
37
+ "InlinePromptNode",
38
+ "PromptDeploymentNode",
39
+ "SearchNode",
40
+ "ConditionalNode",
41
+ "GuardrailNode",
42
+ "SubworkflowDeploymentNode",
43
+ "FinalOutputNode",
44
+ "PromptDeploymentNode",
45
+ "SearchNode",
46
+ ]
@@ -0,0 +1,7 @@
1
+ from .base import BaseNode
2
+ from .base_subworkflow_node import BaseSubworkflowNode
3
+
4
+ __all__ = [
5
+ "BaseNode",
6
+ "BaseSubworkflowNode",
7
+ ]