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,196 @@
1
+ from typing import TYPE_CHECKING, Any, Generic, Iterator, Set, Tuple, Type, TypeVar, Union, cast
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.constants import UNDEF
8
+ from vellum.workflows.references.output import OutputReference
9
+ from vellum.workflows.types.utils import get_class_attr_names, infer_types
10
+
11
+ if TYPE_CHECKING:
12
+ from vellum.workflows.nodes.bases.base import BaseNode
13
+
14
+ _Delta = TypeVar("_Delta")
15
+ _Accumulated = TypeVar("_Accumulated")
16
+
17
+
18
+ class BaseOutput(Generic[_Delta, _Accumulated]):
19
+ _value: Union[_Accumulated, Type[UNDEF]]
20
+ _delta: Union[_Delta, Type[UNDEF]]
21
+ _name: str
22
+
23
+ def __init__(
24
+ self,
25
+ name: str,
26
+ value: Union[_Accumulated, Type[UNDEF]] = UNDEF,
27
+ delta: Union[_Delta, Type[UNDEF]] = UNDEF,
28
+ ) -> None:
29
+ if value is not UNDEF and delta is not UNDEF:
30
+ raise ValueError("Cannot set both value and delta")
31
+
32
+ self._name = name
33
+ self._value = value
34
+ self._delta = delta
35
+
36
+ @property
37
+ def delta(self) -> Union[_Delta, Type[UNDEF]]:
38
+ return self._delta
39
+
40
+ @property
41
+ def value(self) -> Union[_Accumulated, Type[UNDEF]]:
42
+ return self._value
43
+
44
+ @property
45
+ def is_initiated(self) -> bool:
46
+ return self._delta is UNDEF and self._value is UNDEF
47
+
48
+ @property
49
+ def is_streaming(self) -> bool:
50
+ return self._delta is not UNDEF and self._value is UNDEF
51
+
52
+ @property
53
+ def is_fulfilled(self) -> bool:
54
+ return self._delta is UNDEF and self._value is not UNDEF
55
+
56
+ @property
57
+ def name(self) -> str:
58
+ return self._name
59
+
60
+ @classmethod
61
+ def __get_pydantic_core_schema__(
62
+ cls, source_type: Type[Any], handler: GetCoreSchemaHandler
63
+ ) -> core_schema.CoreSchema:
64
+ return core_schema.is_instance_schema(cls)
65
+
66
+ def serialize(self) -> dict:
67
+ data: dict[str, Any] = {
68
+ "name": self.name,
69
+ }
70
+
71
+ if self.value is not UNDEF:
72
+ data["value"] = self.value
73
+
74
+ if self.delta is not UNDEF:
75
+ data["delta"] = self.delta
76
+
77
+ return data
78
+
79
+
80
+ @dataclass_transform(kw_only_default=True)
81
+ class _BaseOutputsMeta(type):
82
+ def __eq__(cls, other: Any) -> bool:
83
+ """
84
+ We need to include custom eq logic to prevent infinite loops during ipython reloading.
85
+ """
86
+
87
+ if not isinstance(other, _BaseOutputsMeta):
88
+ return False
89
+
90
+ if not cls.__qualname__.endswith(".Outputs") or not other.__qualname__.endswith(".Outputs"):
91
+ return super().__eq__(other)
92
+
93
+ self_outputs_class = cast(Type["BaseNode.Outputs"], cls)
94
+ other_outputs_class = cast(Type["BaseNode.Outputs"], other)
95
+
96
+ if not hasattr(self_outputs_class, "_node_class") or not hasattr(other_outputs_class, "_node_class"):
97
+ return super().__eq__(other)
98
+
99
+ if self_outputs_class._node_class is None or other_outputs_class._node_class is None:
100
+ return super().__eq__(other)
101
+
102
+ return getattr(self_outputs_class._node_class, "__qualname__") == getattr(
103
+ other_outputs_class._node_class, "__qualname__"
104
+ )
105
+
106
+ def __setattr__(cls, name: str, value: Any) -> None:
107
+ if isinstance(value, OutputReference):
108
+ # During module reload, tools like ipython will set class attributes from the get attribute
109
+ # from the old class. Our getattribute dynamically returns OutputDescriptors, which means during
110
+ # module reload, it will override the class' original default value with its own OutputReference.
111
+ # We want to avoid this, so we check if the name of the class and the name of the descriptor
112
+ # are the same, and if they are, we don't set the attribute.
113
+ if f"{cls.__qualname__}.{name}" == str(value):
114
+ return super().__setattr__(name, value.instance)
115
+
116
+ return super().__setattr__(name, value)
117
+
118
+ def __getattribute__(cls, name: str) -> Any:
119
+ if name.startswith("_") or not issubclass(cls, BaseOutputs):
120
+ return super().__getattribute__(name)
121
+
122
+ attr_names = get_class_attr_names(cls)
123
+ if name in attr_names:
124
+ # We first try to resolve the instance that this class attribute name is mapped to. If it's not found,
125
+ # we iterate through its inheritance hierarchy to find the first base class that has this attribute
126
+ # and use its mapping.
127
+ instance = vars(cls).get(name, UNDEF)
128
+ if not instance:
129
+ for base in cls.__mro__[1:]:
130
+ if hasattr(base, name):
131
+ instance = getattr(base, name)
132
+ break
133
+
134
+ types = infer_types(cls, name)
135
+ return OutputReference(
136
+ name=name,
137
+ types=types,
138
+ instance=instance,
139
+ outputs_class=cls,
140
+ )
141
+
142
+ return super().__getattribute__(name)
143
+
144
+ def __hash__(self) -> int:
145
+ return hash(self.__qualname__)
146
+
147
+ def __iter__(cls) -> Iterator[OutputReference]:
148
+ # We iterate through the inheritance hierarchy to find all the OutputDescriptors attached to this Outputs class.
149
+ # __mro__ is the method resolution order, which is the order in which base classes are resolved.
150
+ yielded_attr_names: Set[str] = set()
151
+
152
+ for resolved_cls in cls.__mro__:
153
+ attr_names = get_class_attr_names(resolved_cls)
154
+ for attr_name in attr_names:
155
+ if attr_name in yielded_attr_names:
156
+ continue
157
+
158
+ attr_value = getattr(resolved_cls, attr_name)
159
+ if not isinstance(attr_value, OutputReference):
160
+ continue
161
+
162
+ yield attr_value
163
+ yielded_attr_names.add(attr_name)
164
+
165
+
166
+ class BaseOutputs(metaclass=_BaseOutputsMeta):
167
+ def __init__(self, **kwargs: Any) -> None:
168
+ for name, value in kwargs.items():
169
+ setattr(self, name, value)
170
+
171
+ if hasattr(self, "_outputs_post_init") and callable(self._outputs_post_init):
172
+ self._outputs_post_init(**kwargs)
173
+
174
+ def __eq__(self, other: object) -> bool:
175
+ if not isinstance(other, dict):
176
+ return super().__eq__(other)
177
+
178
+ outputs = {name: value for name, value in vars(self).items() if not name.startswith("_") and value is not UNDEF}
179
+ return outputs == other
180
+
181
+ def __repr__(self) -> str:
182
+ values = f"{', '.join(f'{k}={v}' for k, v in vars(self).items() if not k.startswith('_'))}"
183
+ return f"{self.__class__.__name__}({values})"
184
+
185
+ def __iter__(self) -> Iterator[Tuple[OutputReference, Any]]:
186
+ for output_descriptor in self.__class__:
187
+ yield (output_descriptor, getattr(self, output_descriptor.name, output_descriptor.instance))
188
+
189
+ def __getitem__(self, key: str) -> Any:
190
+ return getattr(self, key)
191
+
192
+ @classmethod
193
+ def __get_pydantic_core_schema__(
194
+ cls, source_type: Type[Any], handler: GetCoreSchemaHandler
195
+ ) -> core_schema.CoreSchema:
196
+ return core_schema.is_instance_schema(cls)
@@ -0,0 +1,7 @@
1
+ from .node_ports import NodePorts
2
+ from .port import Port
3
+
4
+ __all__ = [
5
+ "NodePorts",
6
+ "Port",
7
+ ]
@@ -0,0 +1,75 @@
1
+ from typing import Any, Dict, Iterable, Iterator, Optional, Set, Tuple, Type
2
+
3
+ from vellum.workflows.outputs.base import BaseOutput, BaseOutputs
4
+ from vellum.workflows.ports.port import Port
5
+ from vellum.workflows.ports.utils import validate_ports
6
+ from vellum.workflows.state.base import BaseState
7
+ from vellum.workflows.types.core import ConditionType
8
+
9
+
10
+ class _NodePortsMeta(type):
11
+ def __new__(mcs, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]) -> Any:
12
+ for k, v in dct.items():
13
+ if k.startswith("_"):
14
+ continue
15
+ if not isinstance(v, Port):
16
+ raise ValueError(f"All fields in {name} must be of type Port. Received: {v.__class__}")
17
+
18
+ return super().__new__(mcs, name, bases, dct)
19
+
20
+ def __iter__(cls) -> Iterator[Port]:
21
+ for attr_name, attr_value in cls.__dict__.items():
22
+ if not attr_name.startswith("_") and isinstance(attr_value, Port):
23
+ yield attr_value
24
+
25
+ @property
26
+ def _default_port(cls) -> Optional[Port]:
27
+ default_ports = [port for port in cls if port.default]
28
+
29
+ if len(default_ports) > 1:
30
+ raise ValueError(f"Class {cls.__name__} must have only one default port")
31
+
32
+ return default_ports[0] if default_ports else None
33
+
34
+
35
+ class NodePorts(metaclass=_NodePortsMeta):
36
+ def __call__(self, outputs: BaseOutputs, state: BaseState) -> Iterable[Port]:
37
+ """
38
+ Invokes the appropriate ports based on the fulfilled outputs and state.
39
+ """
40
+
41
+ invoked_ports: Set[Port] = set()
42
+ all_ports = [port for port in self.__class__]
43
+ enforce_single_invoked_conditional_port = validate_ports(all_ports)
44
+
45
+ for port in all_ports:
46
+ if port._condition_type == ConditionType.IF:
47
+ resolved_condition = port.resolve_condition(state)
48
+ if resolved_condition:
49
+ invoked_ports.add(port)
50
+ if enforce_single_invoked_conditional_port:
51
+ break
52
+
53
+ elif port._condition_type == ConditionType.ELIF:
54
+ resolved_condition = port.resolve_condition(state)
55
+ if resolved_condition:
56
+ invoked_ports.add(port)
57
+ break
58
+
59
+ elif port._condition_type == ConditionType.ELSE and not invoked_ports:
60
+ invoked_ports.add(port)
61
+ break
62
+
63
+ if not invoked_ports:
64
+ default_port = self.__class__._default_port
65
+ if default_port:
66
+ invoked_ports.add(default_port)
67
+
68
+ return invoked_ports
69
+
70
+ def __lt__(self, output: BaseOutput) -> Iterable[Port]:
71
+ """
72
+ Invokes the appropriate ports based on the streamed output
73
+ """
74
+
75
+ return set()
@@ -0,0 +1,75 @@
1
+ from typing import TYPE_CHECKING, Any, Iterator, List, Optional, Type
2
+
3
+ from vellum.workflows.descriptors.base import BaseDescriptor
4
+ from vellum.workflows.edges.edge import Edge
5
+ from vellum.workflows.graph import Graph, GraphTarget
6
+ from vellum.workflows.state.base import BaseState
7
+ from vellum.workflows.types.core import ConditionType
8
+
9
+ if TYPE_CHECKING:
10
+ from vellum.workflows.nodes.bases import BaseNode
11
+
12
+
13
+ class Port:
14
+ node_class: Type["BaseNode"]
15
+
16
+ _edges: List[Edge]
17
+ _condition: Optional[BaseDescriptor]
18
+ _condition_type: Optional[ConditionType]
19
+
20
+ def __init__(
21
+ self,
22
+ default: bool = False,
23
+ fork_state: bool = False,
24
+ condition: Optional[Any] = None,
25
+ condition_type: Optional[ConditionType] = None,
26
+ ):
27
+ self.default = default
28
+ self.node_class = None # type: ignore[assignment]
29
+ self._fork_state = fork_state
30
+ self._edges = []
31
+ self._condition: Optional[BaseDescriptor] = condition
32
+ self._condition_type: Optional[ConditionType] = condition_type
33
+
34
+ def __set_name__(self, owner: Type, name: str) -> None:
35
+ self.name = name
36
+
37
+ def __repr__(self) -> str:
38
+ return f"{self.node_class}.Ports.{self.name}"
39
+
40
+ @property
41
+ def fork_state(self) -> bool:
42
+ return self._fork_state
43
+
44
+ @property
45
+ def edges(self) -> Iterator[Edge]:
46
+ return iter(self._edges)
47
+
48
+ def __rshift__(self, other: GraphTarget) -> Graph:
49
+ if isinstance(other, set) or isinstance(other, Graph):
50
+ return Graph.from_port(self) >> other
51
+
52
+ edge = Edge(from_port=self, to_node=other)
53
+ if edge not in self._edges:
54
+ self._edges.append(edge)
55
+
56
+ return Graph.from_edge(edge)
57
+
58
+ @staticmethod
59
+ def on_if(condition: BaseDescriptor, fork_state: bool = False) -> "Port":
60
+ return Port(condition=condition, condition_type=ConditionType.IF, fork_state=fork_state)
61
+
62
+ @staticmethod
63
+ def on_elif(condition: BaseDescriptor, fork_state: bool = False) -> "Port":
64
+ return Port(condition=condition, condition_type=ConditionType.ELIF, fork_state=fork_state)
65
+
66
+ @staticmethod
67
+ def on_else(fork_state: bool = False) -> "Port":
68
+ return Port(condition_type=ConditionType.ELSE, fork_state=fork_state)
69
+
70
+ def resolve_condition(self, state: BaseState) -> bool:
71
+ if self._condition is None:
72
+ return False
73
+
74
+ value = self._condition.resolve(state)
75
+ return bool(value)
@@ -0,0 +1,40 @@
1
+ from collections import Counter
2
+ from typing import List
3
+
4
+ from vellum.workflows.ports.port import Port
5
+ from vellum.workflows.types.core import ConditionType
6
+
7
+ PORT_TYPE_PRIORITIES = {
8
+ ConditionType.IF: 1,
9
+ ConditionType.ELIF: 2,
10
+ ConditionType.ELSE: 3,
11
+ }
12
+
13
+
14
+ def validate_ports(ports: List[Port]) -> bool:
15
+ # We don't want to validate ports with no condition (default ports)
16
+ port_types = [port._condition_type for port in ports if port._condition_type is not None]
17
+ sorted_port_types = sorted(port_types, key=lambda port_type: PORT_TYPE_PRIORITIES[port_type])
18
+
19
+ if sorted_port_types != port_types:
20
+ raise ValueError("Port conditions must be in the following order: on_if, on_elif, on_else")
21
+
22
+ counter = Counter(port_types)
23
+ number_of_if_ports = counter[ConditionType.IF]
24
+ number_of_elif_ports = counter[ConditionType.ELIF]
25
+ number_of_else_ports = counter[ConditionType.ELSE]
26
+ ports_class = f"{ports[0].node_class}.Ports"
27
+
28
+ if number_of_if_ports == 0 and (number_of_elif_ports > 0 or number_of_else_ports > 0):
29
+ raise ValueError(
30
+ f"Class {ports_class} containing on_elif or on_else port conditions must have at least one on_if condition"
31
+ )
32
+
33
+ if number_of_elif_ports > 0 and number_of_if_ports != 1:
34
+ raise ValueError(f"Class {ports_class} containing on_elif ports must have exactly one on_if condition")
35
+
36
+ if number_of_else_ports > 1:
37
+ raise ValueError(f"Class {ports_class} must have at most one on_else port condition")
38
+
39
+ enforce_single_invoked_conditional_port = number_of_elif_ports > 0 or number_of_if_ports <= 1
40
+ return enforce_single_invoked_conditional_port
@@ -0,0 +1,17 @@
1
+ from .environment_variable import EnvironmentVariableReference
2
+ from .external_input import ExternalInputReference
3
+ from .lazy import LazyReference
4
+ from .node import NodeReference
5
+ from .output import OutputReference
6
+ from .state_value import StateValueReference
7
+ from .workflow_input import WorkflowInputReference
8
+
9
+ __all__ = [
10
+ "EnvironmentVariableReference",
11
+ "ExternalInputReference",
12
+ "LazyReference",
13
+ "NodeReference",
14
+ "OutputReference",
15
+ "StateValueReference",
16
+ "WorkflowInputReference",
17
+ ]
@@ -0,0 +1,20 @@
1
+ import os
2
+ from typing import TYPE_CHECKING
3
+
4
+ from vellum.workflows.descriptors.base import BaseDescriptor
5
+
6
+ if TYPE_CHECKING:
7
+ from vellum.workflows.state.base import BaseState
8
+
9
+
10
+ class EnvironmentVariableReference(BaseDescriptor[str]):
11
+ def __init__(self, *, name: str) -> None:
12
+ super().__init__(name=name, types=(str,))
13
+
14
+ def resolve(self, state: "BaseState") -> str:
15
+ env_value = os.environ.get(self.name)
16
+ if env_value is not None:
17
+ return env_value
18
+
19
+ # Fetch Vellum Environment Variable named `self.name` once that project is done
20
+ raise ValueError(f"No environment variable named '{self.name}' found")
@@ -0,0 +1,20 @@
1
+ from typing import TYPE_CHECKING, Type
2
+
3
+ from vellum.workflows.descriptors.base import BaseDescriptor
4
+
5
+ if TYPE_CHECKING:
6
+ from vellum.workflows.nodes.bases import BaseNode
7
+ from vellum.workflows.state.base import BaseState
8
+
9
+
10
+ class ExecutionCountReference(BaseDescriptor[int]):
11
+
12
+ def __init__(
13
+ self,
14
+ node_class: Type["BaseNode"],
15
+ ) -> None:
16
+ super().__init__(name=f"Execution Count({node_class.__name__})", types=(int,))
17
+ self._node_class = node_class
18
+
19
+ def resolve(self, state: "BaseState") -> int:
20
+ return state.meta.node_execution_cache.get_execution_count(self._node_class)
@@ -0,0 +1,49 @@
1
+ from typing import TYPE_CHECKING, Any, Generic, Optional, Tuple, Type, TypeVar, cast
2
+
3
+ from pydantic import GetCoreSchemaHandler
4
+ from pydantic_core import core_schema
5
+
6
+ from vellum.workflows.constants import UNDEF
7
+ from vellum.workflows.descriptors.base import BaseDescriptor
8
+ from vellum.workflows.errors.types import VellumErrorCode
9
+ from vellum.workflows.exceptions import NodeException
10
+
11
+ if TYPE_CHECKING:
12
+ from vellum.workflows.inputs.base import BaseInputs
13
+ from vellum.workflows.state.base import BaseState
14
+
15
+ _InputType = TypeVar("_InputType")
16
+
17
+
18
+ class ExternalInputReference(BaseDescriptor[_InputType], Generic[_InputType]):
19
+
20
+ def __init__(
21
+ self,
22
+ *,
23
+ name: str,
24
+ types: Tuple[Type[_InputType], ...],
25
+ instance: Optional[_InputType],
26
+ inputs_class: Type["BaseInputs"],
27
+ ) -> None:
28
+ super().__init__(name=name, types=types, instance=instance)
29
+ self._inputs_class = inputs_class
30
+
31
+ @property
32
+ def inputs_class(self) -> Type["BaseInputs"]:
33
+ return self._inputs_class
34
+
35
+ def resolve(self, state: "BaseState") -> _InputType:
36
+ external_input = state.meta.external_inputs.get(self)
37
+ if external_input is not UNDEF:
38
+ return cast(_InputType, external_input)
39
+
40
+ if state.meta.parent:
41
+ return self.resolve(state.meta.parent)
42
+
43
+ raise NodeException(f"Missing required Node Input: {self._name}", code=VellumErrorCode.INVALID_INPUTS)
44
+
45
+ @classmethod
46
+ def __get_pydantic_core_schema__(
47
+ cls, source_type: Type[Any], handler: GetCoreSchemaHandler
48
+ ) -> core_schema.CoreSchema:
49
+ return core_schema.is_instance_schema(cls)
@@ -0,0 +1,7 @@
1
+ from typing import Union
2
+
3
+ from vellum.workflows.references.external_input import ExternalInputReference
4
+ from vellum.workflows.references.workflow_input import WorkflowInputReference
5
+
6
+ # Note: We may want to consolidate these two into a single descriptor.
7
+ InputReference = Union[ExternalInputReference, WorkflowInputReference]
@@ -0,0 +1,55 @@
1
+ import ast
2
+ import inspect
3
+ from typing import TYPE_CHECKING, Callable, Generic, TypeVar, get_args
4
+
5
+ from vellum.workflows.descriptors.base import BaseDescriptor
6
+
7
+ if TYPE_CHECKING:
8
+ from vellum.workflows.state.base import BaseState
9
+
10
+ _T = TypeVar("_T")
11
+
12
+
13
+ class LazyReference(BaseDescriptor[_T], Generic[_T]):
14
+ def __init__(
15
+ self,
16
+ get: Callable[[], BaseDescriptor[_T]],
17
+ ) -> None:
18
+ self._get = get
19
+ # TODO: figure out this some times returns empty
20
+ # Original example: https://github.com/vellum-ai/workflows-as-code-runner-prototype/pull/128/files#diff-67aaa468aa37b6130756bfaf93f03954d7b518617922efb3350882ea4ae03d60R36 # noqa: E501
21
+ # https://app.shortcut.com/vellum/story/4993
22
+ types = get_args(type(self))
23
+ super().__init__(name=self._get_name(), types=types)
24
+
25
+ def resolve(self, state: "BaseState") -> _T:
26
+ from vellum.workflows.descriptors.utils import resolve_value
27
+
28
+ return resolve_value(self._get(), state)
29
+
30
+ def _get_name(self) -> str:
31
+ """
32
+ We try our best to parse out the source code that generates the descriptor,
33
+ setting that as the descriptor's name. Names are only used for debugging, so
34
+ we could flesh out edge cases over time.
35
+ """
36
+ source = inspect.getsource(self._get).strip()
37
+ try:
38
+ parsed = ast.parse(source)
39
+ assignment = parsed.body[0]
40
+
41
+ if not isinstance(assignment, ast.Assign):
42
+ return source
43
+
44
+ call = assignment.value
45
+ if not isinstance(call, ast.Call) or not call.args:
46
+ return source
47
+
48
+ lambda_expression = call.args[0]
49
+ if not isinstance(lambda_expression, ast.Lambda):
50
+ return source
51
+
52
+ body = lambda_expression.body
53
+ return source[body.col_offset : body.end_col_offset]
54
+ except Exception:
55
+ return source
@@ -0,0 +1,43 @@
1
+ from typing import TYPE_CHECKING, Any, Optional, Tuple, Type, TypeVar
2
+
3
+ from pydantic import GetCoreSchemaHandler
4
+ from pydantic_core import core_schema
5
+
6
+ from vellum.workflows.descriptors.base import BaseDescriptor
7
+ from vellum.workflows.errors.types import VellumErrorCode
8
+ from vellum.workflows.exceptions import NodeException
9
+
10
+ if TYPE_CHECKING:
11
+ from vellum.workflows.nodes.bases.base import BaseNode
12
+ from vellum.workflows.state.base import BaseState
13
+
14
+ _T = TypeVar("_T")
15
+
16
+
17
+ class NodeReference(BaseDescriptor[_T]):
18
+ def __init__(
19
+ self, *, name: str, types: Tuple[Type[_T], ...], instance: Optional[_T] = None, node_class: Type["BaseNode"]
20
+ ) -> None:
21
+ self._name = name
22
+ self._types = types
23
+ self._instance = instance
24
+ self._node_class = node_class
25
+
26
+ @property
27
+ def node_class(self) -> Type["BaseNode"]:
28
+ return self._node_class
29
+
30
+ def resolve(self, state: "BaseState") -> _T:
31
+ raise NodeException(
32
+ f"NodeDescriptors cannot be resolved during runtime. Got: {self._name}",
33
+ code=VellumErrorCode.INTERNAL_ERROR,
34
+ )
35
+
36
+ def __repr__(self) -> str:
37
+ return f"{self._node_class.__qualname__}.{self._name}"
38
+
39
+ @classmethod
40
+ def __get_pydantic_core_schema__(
41
+ cls, source_type: Type[Any], handler: GetCoreSchemaHandler
42
+ ) -> core_schema.CoreSchema:
43
+ return core_schema.is_instance_schema(cls)