vellum-ai 0.9.16rc2__py3-none-any.whl → 0.10.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (245) hide show
  1. vellum/plugins/__init__.py +0 -0
  2. vellum/plugins/pydantic.py +74 -0
  3. vellum/plugins/utils.py +19 -0
  4. vellum/plugins/vellum_mypy.py +639 -3
  5. vellum/workflows/README.md +90 -0
  6. vellum/workflows/__init__.py +5 -0
  7. vellum/workflows/constants.py +43 -0
  8. vellum/workflows/descriptors/__init__.py +0 -0
  9. vellum/workflows/descriptors/base.py +339 -0
  10. vellum/workflows/descriptors/tests/test_utils.py +83 -0
  11. vellum/workflows/descriptors/utils.py +90 -0
  12. vellum/workflows/edges/__init__.py +5 -0
  13. vellum/workflows/edges/edge.py +23 -0
  14. vellum/workflows/emitters/__init__.py +5 -0
  15. vellum/workflows/emitters/base.py +14 -0
  16. vellum/workflows/environment/__init__.py +5 -0
  17. vellum/workflows/environment/environment.py +7 -0
  18. vellum/workflows/errors/__init__.py +6 -0
  19. vellum/workflows/errors/types.py +20 -0
  20. vellum/workflows/events/__init__.py +31 -0
  21. vellum/workflows/events/node.py +125 -0
  22. vellum/workflows/events/tests/__init__.py +0 -0
  23. vellum/workflows/events/tests/test_event.py +216 -0
  24. vellum/workflows/events/types.py +52 -0
  25. vellum/workflows/events/utils.py +5 -0
  26. vellum/workflows/events/workflow.py +139 -0
  27. vellum/workflows/exceptions.py +15 -0
  28. vellum/workflows/expressions/__init__.py +0 -0
  29. vellum/workflows/expressions/accessor.py +52 -0
  30. vellum/workflows/expressions/and_.py +32 -0
  31. vellum/workflows/expressions/begins_with.py +31 -0
  32. vellum/workflows/expressions/between.py +38 -0
  33. vellum/workflows/expressions/coalesce_expression.py +41 -0
  34. vellum/workflows/expressions/contains.py +30 -0
  35. vellum/workflows/expressions/does_not_begin_with.py +31 -0
  36. vellum/workflows/expressions/does_not_contain.py +30 -0
  37. vellum/workflows/expressions/does_not_end_with.py +31 -0
  38. vellum/workflows/expressions/does_not_equal.py +25 -0
  39. vellum/workflows/expressions/ends_with.py +31 -0
  40. vellum/workflows/expressions/equals.py +25 -0
  41. vellum/workflows/expressions/greater_than.py +33 -0
  42. vellum/workflows/expressions/greater_than_or_equal_to.py +33 -0
  43. vellum/workflows/expressions/in_.py +31 -0
  44. vellum/workflows/expressions/is_blank.py +24 -0
  45. vellum/workflows/expressions/is_not_blank.py +24 -0
  46. vellum/workflows/expressions/is_not_null.py +21 -0
  47. vellum/workflows/expressions/is_not_undefined.py +22 -0
  48. vellum/workflows/expressions/is_null.py +21 -0
  49. vellum/workflows/expressions/is_undefined.py +22 -0
  50. vellum/workflows/expressions/less_than.py +33 -0
  51. vellum/workflows/expressions/less_than_or_equal_to.py +33 -0
  52. vellum/workflows/expressions/not_between.py +38 -0
  53. vellum/workflows/expressions/not_in.py +31 -0
  54. vellum/workflows/expressions/or_.py +32 -0
  55. vellum/workflows/graph/__init__.py +3 -0
  56. vellum/workflows/graph/graph.py +131 -0
  57. vellum/workflows/graph/tests/__init__.py +0 -0
  58. vellum/workflows/graph/tests/test_graph.py +437 -0
  59. vellum/workflows/inputs/__init__.py +5 -0
  60. vellum/workflows/inputs/base.py +55 -0
  61. vellum/workflows/logging.py +14 -0
  62. vellum/workflows/nodes/__init__.py +46 -0
  63. vellum/workflows/nodes/bases/__init__.py +7 -0
  64. vellum/workflows/nodes/bases/base.py +332 -0
  65. vellum/workflows/nodes/bases/base_subworkflow_node/__init__.py +5 -0
  66. vellum/workflows/nodes/bases/base_subworkflow_node/node.py +10 -0
  67. vellum/workflows/nodes/bases/tests/__init__.py +0 -0
  68. vellum/workflows/nodes/bases/tests/test_base_node.py +125 -0
  69. vellum/workflows/nodes/core/__init__.py +16 -0
  70. vellum/workflows/nodes/core/error_node/__init__.py +5 -0
  71. vellum/workflows/nodes/core/error_node/node.py +26 -0
  72. vellum/workflows/nodes/core/inline_subworkflow_node/__init__.py +5 -0
  73. vellum/workflows/nodes/core/inline_subworkflow_node/node.py +73 -0
  74. vellum/workflows/nodes/core/map_node/__init__.py +5 -0
  75. vellum/workflows/nodes/core/map_node/node.py +147 -0
  76. vellum/workflows/nodes/core/map_node/tests/__init__.py +0 -0
  77. vellum/workflows/nodes/core/map_node/tests/test_node.py +65 -0
  78. vellum/workflows/nodes/core/retry_node/__init__.py +5 -0
  79. vellum/workflows/nodes/core/retry_node/node.py +106 -0
  80. vellum/workflows/nodes/core/retry_node/tests/__init__.py +0 -0
  81. vellum/workflows/nodes/core/retry_node/tests/test_node.py +93 -0
  82. vellum/workflows/nodes/core/templating_node/__init__.py +5 -0
  83. vellum/workflows/nodes/core/templating_node/custom_filters.py +12 -0
  84. vellum/workflows/nodes/core/templating_node/exceptions.py +2 -0
  85. vellum/workflows/nodes/core/templating_node/node.py +123 -0
  86. vellum/workflows/nodes/core/templating_node/render.py +55 -0
  87. vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py +21 -0
  88. vellum/workflows/nodes/core/try_node/__init__.py +5 -0
  89. vellum/workflows/nodes/core/try_node/node.py +110 -0
  90. vellum/workflows/nodes/core/try_node/tests/__init__.py +0 -0
  91. vellum/workflows/nodes/core/try_node/tests/test_node.py +82 -0
  92. vellum/workflows/nodes/displayable/__init__.py +31 -0
  93. vellum/workflows/nodes/displayable/api_node/__init__.py +5 -0
  94. vellum/workflows/nodes/displayable/api_node/node.py +44 -0
  95. vellum/workflows/nodes/displayable/bases/__init__.py +11 -0
  96. vellum/workflows/nodes/displayable/bases/api_node/__init__.py +5 -0
  97. vellum/workflows/nodes/displayable/bases/api_node/node.py +70 -0
  98. vellum/workflows/nodes/displayable/bases/base_prompt_node/__init__.py +5 -0
  99. vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +60 -0
  100. vellum/workflows/nodes/displayable/bases/inline_prompt_node/__init__.py +5 -0
  101. vellum/workflows/nodes/displayable/bases/inline_prompt_node/constants.py +13 -0
  102. vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +118 -0
  103. vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +98 -0
  104. vellum/workflows/nodes/displayable/bases/search_node.py +90 -0
  105. vellum/workflows/nodes/displayable/code_execution_node/__init__.py +5 -0
  106. vellum/workflows/nodes/displayable/code_execution_node/node.py +197 -0
  107. vellum/workflows/nodes/displayable/code_execution_node/tests/__init__.py +0 -0
  108. vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/__init__.py +0 -0
  109. vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/main.py +3 -0
  110. vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +111 -0
  111. vellum/workflows/nodes/displayable/code_execution_node/utils.py +10 -0
  112. vellum/workflows/nodes/displayable/conditional_node/__init__.py +5 -0
  113. vellum/workflows/nodes/displayable/conditional_node/node.py +25 -0
  114. vellum/workflows/nodes/displayable/final_output_node/__init__.py +5 -0
  115. vellum/workflows/nodes/displayable/final_output_node/node.py +43 -0
  116. vellum/workflows/nodes/displayable/guardrail_node/__init__.py +5 -0
  117. vellum/workflows/nodes/displayable/guardrail_node/node.py +97 -0
  118. vellum/workflows/nodes/displayable/inline_prompt_node/__init__.py +5 -0
  119. vellum/workflows/nodes/displayable/inline_prompt_node/node.py +41 -0
  120. vellum/workflows/nodes/displayable/merge_node/__init__.py +5 -0
  121. vellum/workflows/nodes/displayable/merge_node/node.py +10 -0
  122. vellum/workflows/nodes/displayable/prompt_deployment_node/__init__.py +5 -0
  123. vellum/workflows/nodes/displayable/prompt_deployment_node/node.py +45 -0
  124. vellum/workflows/nodes/displayable/search_node/__init__.py +5 -0
  125. vellum/workflows/nodes/displayable/search_node/node.py +26 -0
  126. vellum/workflows/nodes/displayable/subworkflow_deployment_node/__init__.py +5 -0
  127. vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +156 -0
  128. vellum/workflows/nodes/displayable/tests/__init__.py +0 -0
  129. vellum/workflows/nodes/displayable/tests/test_inline_text_prompt_node.py +148 -0
  130. vellum/workflows/nodes/displayable/tests/test_search_node_wth_text_output.py +134 -0
  131. vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py +80 -0
  132. vellum/workflows/nodes/utils.py +27 -0
  133. vellum/workflows/outputs/__init__.py +6 -0
  134. vellum/workflows/outputs/base.py +196 -0
  135. vellum/workflows/ports/__init__.py +7 -0
  136. vellum/workflows/ports/node_ports.py +75 -0
  137. vellum/workflows/ports/port.py +75 -0
  138. vellum/workflows/ports/utils.py +40 -0
  139. vellum/workflows/references/__init__.py +17 -0
  140. vellum/workflows/references/environment_variable.py +20 -0
  141. vellum/workflows/references/execution_count.py +20 -0
  142. vellum/workflows/references/external_input.py +49 -0
  143. vellum/workflows/references/input.py +7 -0
  144. vellum/workflows/references/lazy.py +55 -0
  145. vellum/workflows/references/node.py +43 -0
  146. vellum/workflows/references/output.py +78 -0
  147. vellum/workflows/references/state_value.py +23 -0
  148. vellum/workflows/references/vellum_secret.py +15 -0
  149. vellum/workflows/references/workflow_input.py +41 -0
  150. vellum/workflows/resolvers/__init__.py +5 -0
  151. vellum/workflows/resolvers/base.py +15 -0
  152. vellum/workflows/runner/__init__.py +5 -0
  153. vellum/workflows/runner/runner.py +588 -0
  154. vellum/workflows/runner/types.py +18 -0
  155. vellum/workflows/state/__init__.py +5 -0
  156. vellum/workflows/state/base.py +327 -0
  157. vellum/workflows/state/context.py +18 -0
  158. vellum/workflows/state/encoder.py +57 -0
  159. vellum/workflows/state/store.py +28 -0
  160. vellum/workflows/state/tests/__init__.py +0 -0
  161. vellum/workflows/state/tests/test_state.py +113 -0
  162. vellum/workflows/types/__init__.py +0 -0
  163. vellum/workflows/types/core.py +91 -0
  164. vellum/workflows/types/generics.py +14 -0
  165. vellum/workflows/types/stack.py +39 -0
  166. vellum/workflows/types/tests/__init__.py +0 -0
  167. vellum/workflows/types/tests/test_utils.py +76 -0
  168. vellum/workflows/types/utils.py +164 -0
  169. vellum/workflows/utils/__init__.py +0 -0
  170. vellum/workflows/utils/names.py +13 -0
  171. vellum/workflows/utils/tests/__init__.py +0 -0
  172. vellum/workflows/utils/tests/test_names.py +15 -0
  173. vellum/workflows/utils/tests/test_vellum_variables.py +25 -0
  174. vellum/workflows/utils/vellum_variables.py +81 -0
  175. vellum/workflows/vellum_client.py +18 -0
  176. vellum/workflows/workflows/__init__.py +5 -0
  177. vellum/workflows/workflows/base.py +365 -0
  178. {vellum_ai-0.9.16rc2.dist-info → vellum_ai-0.10.0.dist-info}/METADATA +2 -1
  179. {vellum_ai-0.9.16rc2.dist-info → vellum_ai-0.10.0.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.10.0.dist-info}/LICENSE +0 -0
  244. {vellum_ai-0.9.16rc2.dist-info → vellum_ai-0.10.0.dist-info}/WHEEL +0 -0
  245. {vellum_ai-0.9.16rc2.dist-info → vellum_ai-0.10.0.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
+ ]