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,76 @@
1
+ import pytest
2
+ from typing import ClassVar, Generic, List, TypeVar, Union
3
+
4
+ from vellum.workflows.outputs.base import BaseOutputs
5
+ from vellum.workflows.references.output import OutputReference
6
+ from vellum.workflows.types.utils import get_class_attr_names, infer_types
7
+
8
+
9
+ class ExampleClass:
10
+ alpha: str
11
+ beta = 3
12
+ gamma: Union[str, int]
13
+ epsilon = OutputReference(
14
+ name="epsilon",
15
+ types=(List[str],),
16
+ instance=None,
17
+ outputs_class=BaseOutputs,
18
+ )
19
+ zeta: ClassVar[str]
20
+ eta: List[str]
21
+
22
+
23
+ T = TypeVar("T")
24
+
25
+
26
+ class ExampleGenericClass(Generic[T]):
27
+ delta: T
28
+
29
+
30
+ class ExampleInheritedClass(ExampleClass):
31
+ theta: int
32
+
33
+
34
+ @pytest.mark.parametrize(
35
+ "cls, attr_name, expected_type",
36
+ [
37
+ (ExampleClass, "alpha", (str,)),
38
+ (ExampleClass, "beta", (int,)),
39
+ (ExampleClass, "gamma", (str, int)),
40
+ (ExampleGenericClass, "delta", ()),
41
+ (ExampleGenericClass[str], "delta", (str,)),
42
+ (ExampleClass, "epsilon", (List[str],)),
43
+ (ExampleClass, "zeta", (str,)),
44
+ (ExampleClass, "eta", (List[str],)),
45
+ (ExampleInheritedClass, "theta", (int,)),
46
+ (ExampleInheritedClass, "alpha", (str,)),
47
+ (ExampleInheritedClass, "beta", (int,)),
48
+ ],
49
+ ids=[
50
+ "str",
51
+ "int",
52
+ "str_or_int",
53
+ "generic_blank",
54
+ "generic_str",
55
+ "descriptor",
56
+ "class_var",
57
+ "list_str",
58
+ "inherited_int",
59
+ "inherited_parent_annotation",
60
+ "inherited_parent_class_var",
61
+ ],
62
+ )
63
+ def test_infer_types(cls, attr_name, expected_type):
64
+ assert infer_types(cls, attr_name) == expected_type
65
+
66
+
67
+ @pytest.mark.parametrize(
68
+ "cls, expected_attr_names",
69
+ [
70
+ (ExampleClass, {"alpha", "beta", "gamma", "epsilon", "zeta", "eta"}),
71
+ (ExampleGenericClass, {"delta"}),
72
+ (ExampleInheritedClass, {"alpha", "beta", "gamma", "epsilon", "zeta", "eta", "theta"}),
73
+ ],
74
+ )
75
+ def test_class_attr_names(cls, expected_attr_names):
76
+ assert get_class_attr_names(cls) == expected_attr_names
@@ -0,0 +1,164 @@
1
+ from copy import deepcopy
2
+ from datetime import datetime
3
+ import importlib
4
+ from typing import (
5
+ Any,
6
+ ClassVar,
7
+ Dict,
8
+ Generic,
9
+ Optional,
10
+ Set,
11
+ Tuple,
12
+ Type,
13
+ TypeVar,
14
+ Union,
15
+ get_args,
16
+ get_origin,
17
+ get_type_hints,
18
+ )
19
+
20
+ from vellum import ArrayVellumValue, ArrayVellumValueRequest, ChatMessagePromptBlock
21
+
22
+ from vellum.workflows.descriptors.base import BaseDescriptor
23
+ from vellum.workflows.types.core import Json, SpecialGenericAlias, UnderGenericAlias, UnionGenericAlias
24
+
25
+ _T = TypeVar("_T")
26
+ _LHS = TypeVar("_LHS")
27
+ _RHS = TypeVar("_RHS")
28
+
29
+ LOCAL_NS = {
30
+ "Json": Json,
31
+ "ArrayVellumValueRequest": ArrayVellumValueRequest,
32
+ "ArrayVellumValue": ArrayVellumValue,
33
+ "ChatMessagePromptBlock": ChatMessagePromptBlock,
34
+ }
35
+
36
+
37
+ def resolve_types(value: Union[BaseDescriptor[_T], _T]) -> Tuple[Type[_T], ...]:
38
+ if isinstance(value, BaseDescriptor):
39
+ return value.types
40
+
41
+ return (value.__class__,)
42
+
43
+
44
+ def resolve_combined_types(
45
+ lhs: Union[BaseDescriptor[_LHS], _LHS], rhs: Union[BaseDescriptor[_RHS], _RHS]
46
+ ) -> Tuple[Union[Type[_LHS], Type[_RHS]], ...]:
47
+ lhs_types = resolve_types(lhs)
48
+ rhs_types = resolve_types(rhs)
49
+
50
+ unique_results = set(lhs_types) | set(rhs_types)
51
+
52
+ return tuple(unique_results)
53
+
54
+
55
+ def infer_types(object_: Type, attr_name: str, localns: Optional[Dict[str, Any]] = None) -> Tuple[Type, ...]:
56
+ try:
57
+ class_ = object_
58
+ type_var_mapping = {}
59
+ if isinstance(object_, UnderGenericAlias):
60
+ origin = get_origin(object_)
61
+ if origin and Generic in origin.__bases__:
62
+ class_ = origin
63
+ args = get_args(object_)
64
+ type_var_mapping = {t: a for t, a in zip(origin.__parameters__, args)}
65
+
66
+ type_hints = get_type_hints(class_, localns=LOCAL_NS if localns is None else {**LOCAL_NS, **localns})
67
+ if attr_name in type_hints:
68
+ type_hint = type_hints[attr_name]
69
+ if get_origin(type_hint) is ClassVar:
70
+ return get_args(type_hint)
71
+ if isinstance(type_hint, type):
72
+ return (type_hint,)
73
+ if isinstance(type_hint, UnionGenericAlias):
74
+ if get_origin(type_hint) is Union:
75
+ return get_args(type_hint)
76
+ if isinstance(type_hint, UnderGenericAlias):
77
+ return (type_hint,)
78
+ if isinstance(type_hint, SpecialGenericAlias):
79
+ return (type_hint,)
80
+ if isinstance(type_hint, TypeVar):
81
+ if type_hint in type_var_mapping:
82
+ return (type_var_mapping[type_hint],)
83
+ return type_hint.__constraints__
84
+
85
+ for base in reversed(class_.__mro__):
86
+ class_attributes = vars(base)
87
+ if attr_name in class_attributes:
88
+ class_attribute = class_attributes[attr_name]
89
+ return resolve_types(class_attribute)
90
+
91
+ raise AttributeError(f"Failed to infer type from attribute {attr_name} on {object_.__name__}")
92
+ except TypeError:
93
+ raise AttributeError(
94
+ f"Found 3.9+ typing syntax for field '{attr_name}' on class '{object_.__name__}' – {object_.__annotations__[attr_name]}. Type annotations must be compatible with python version 3.8. " # noqa: E501
95
+ )
96
+
97
+
98
+ def get_class_attr_names(cls: Type) -> Set[str]:
99
+ # gets type-annotated attributes `foo: int`
100
+ type_annotated_attributes: Set[str] = set()
101
+
102
+ # gets attributes declared `foo = 1`
103
+ class_attributes: Set[str] = set()
104
+
105
+ for base in reversed(cls.__mro__):
106
+ ann = base.__dict__.get("__annotations__", {})
107
+ type_annotated_attributes.update(ann.keys())
108
+
109
+ base_vars = vars(base).keys()
110
+ class_attributes.update(base_vars)
111
+
112
+ # combine and filter out private attributes
113
+ return {a for a in list(set(class_attributes) | set(type_annotated_attributes)) if not a.startswith("_")}
114
+
115
+
116
+ def deepcopy_with_exclusions(
117
+ obj: _T,
118
+ memo: Any,
119
+ exclusions: Optional[Dict[str, Any]] = None,
120
+ ) -> _T:
121
+ cls = obj.__class__
122
+ new_instance = cls.__new__(cls)
123
+ new_instance.__dict__.update(obj.__dict__)
124
+
125
+ exclusions = exclusions or {}
126
+
127
+ for key, value in obj.__dict__.items():
128
+ if key in exclusions:
129
+ continue
130
+ new_instance.__dict__[key] = deepcopy(value, memo)
131
+
132
+ for key, value in exclusions.items():
133
+ new_instance.__dict__[key] = value
134
+
135
+ return new_instance
136
+
137
+
138
+ def get_class_by_qualname(qualname: str) -> Type:
139
+ module_name, class_name = qualname.rsplit(".", 1)
140
+ module = importlib.import_module(module_name)
141
+ imported_class = getattr(module, class_name)
142
+ if not isinstance(imported_class, type):
143
+ raise ValueError(f"Class {qualname} is not a valid type")
144
+
145
+ return imported_class
146
+
147
+
148
+ def datetime_now() -> datetime:
149
+ """
150
+ There's a race condition between freezegun and pydantic that causes `PydanticSchemaGenerationError`
151
+ because freezegun monkey-patches `datetime.now` to return a frozen datetime. This helper provides
152
+ an alternative way to facilitate testing that doesn't rely on freezegun.
153
+ """
154
+
155
+ return datetime.now()
156
+
157
+
158
+ def get_original_base(cls: Type) -> Type:
159
+ if not hasattr(cls, "__orig_bases__"):
160
+ return type(None)
161
+
162
+ # in Python 3.12, there is `from types import get_original_bases`, making this future proof
163
+ # https://docs.python.org/3/library/types.html#types.get_original_bases
164
+ return cls.__orig_bases__[0] # type: ignore[attr-defined]
File without changes
@@ -0,0 +1,13 @@
1
+ import re
2
+
3
+
4
+ def pascal_to_title_case(pascal_str: str) -> str:
5
+ # Insert space before each capital letter (except the first one)
6
+ title_case_str = re.sub(r"(?<!^)(?=[A-Z])", " ", pascal_str)
7
+
8
+ # Return the result in title case
9
+ return title_case_str.title()
10
+
11
+
12
+ def snake_to_title_case(snake_str: str) -> str:
13
+ return pascal_to_title_case(snake_str.replace("_", " "))
File without changes
@@ -0,0 +1,15 @@
1
+ import pytest
2
+
3
+ from vellum.workflows.utils.names import pascal_to_title_case
4
+
5
+
6
+ @pytest.mark.parametrize(
7
+ ["input_str", "expected"],
8
+ [
9
+ ("MyPascalCaseString", "My Pascal Case String"),
10
+ ("AnotherPascalCaseString", "Another Pascal Case String"),
11
+ ],
12
+ )
13
+ def test_pascal_to_title_case(input_str, expected):
14
+ actual = pascal_to_title_case(input_str)
15
+ assert actual == expected
@@ -0,0 +1,25 @@
1
+ import pytest
2
+ from typing import List, Optional
3
+
4
+ from vellum import ChatMessage, SearchResult
5
+
6
+ from vellum.workflows.utils.vellum_variables import primitive_type_to_vellum_variable_type
7
+
8
+
9
+ @pytest.mark.parametrize(
10
+ "type_, expected",
11
+ [
12
+ (str, "STRING"),
13
+ (Optional[str], "STRING"),
14
+ (int, "NUMBER"),
15
+ (Optional[int], "NUMBER"),
16
+ (float, "NUMBER"),
17
+ (Optional[float], "NUMBER"),
18
+ (List[ChatMessage], "CHAT_HISTORY"),
19
+ (Optional[List[ChatMessage]], "CHAT_HISTORY"),
20
+ (List[SearchResult], "SEARCH_RESULTS"),
21
+ (Optional[List[SearchResult]], "SEARCH_RESULTS"),
22
+ ],
23
+ )
24
+ def test_primitive_type_to_vellum_variable_type(type_, expected):
25
+ assert primitive_type_to_vellum_variable_type(type_) == expected
@@ -0,0 +1,81 @@
1
+ from typing import List, Tuple, Type, Union, get_args, get_origin
2
+
3
+ from vellum import (
4
+ ChatMessage,
5
+ ChatMessageRequest,
6
+ FunctionCall,
7
+ FunctionCallRequest,
8
+ SearchResult,
9
+ SearchResultRequest,
10
+ VellumAudio,
11
+ VellumAudioRequest,
12
+ VellumError,
13
+ VellumErrorRequest,
14
+ VellumImage,
15
+ VellumImageRequest,
16
+ VellumValue,
17
+ VellumValueRequest,
18
+ VellumVariableType,
19
+ )
20
+
21
+ from vellum.workflows.descriptors.base import BaseDescriptor
22
+
23
+
24
+ def primitive_type_to_vellum_variable_type(type_: Union[Type, BaseDescriptor]) -> VellumVariableType:
25
+ """Converts a python primitive to a VellumVariableType"""
26
+ if isinstance(type_, BaseDescriptor):
27
+ # Ignore None because those just make types optional
28
+ types = [t for t in type_.types if t is not type(None)]
29
+
30
+ # default to JSON for typevars where the types is empty tuple
31
+ if len(types) == 0:
32
+ return "JSON"
33
+
34
+ if len(types) != 1:
35
+ raise ValueError(f"Expected Descriptor to only have one type, got {types}")
36
+
37
+ type_ = type_.types[0]
38
+
39
+ if _is_type_optionally_equal(type_, str):
40
+ return "STRING"
41
+ elif _is_type_optionally_in(type_, (int, float)):
42
+ return "NUMBER"
43
+ elif _is_type_optionally_in(type_, (FunctionCall, FunctionCallRequest)):
44
+ return "FUNCTION_CALL"
45
+ elif _is_type_optionally_in(type_, (VellumImage, VellumImageRequest)):
46
+ return "IMAGE"
47
+ elif _is_type_optionally_in(type_, (VellumAudio, VellumAudioRequest)):
48
+ return "AUDIO"
49
+ elif _is_type_optionally_in(type_, (VellumError, VellumErrorRequest)):
50
+ return "ERROR"
51
+ elif _is_type_optionally_in(type_, (List[ChatMessage], List[ChatMessageRequest])):
52
+ return "CHAT_HISTORY"
53
+ elif _is_type_optionally_in(type_, (List[SearchResult], List[SearchResultRequest])):
54
+ return "SEARCH_RESULTS"
55
+ elif _is_type_optionally_in(type_, (List[VellumValue], List[VellumValueRequest])):
56
+ return "ARRAY"
57
+
58
+ return "JSON"
59
+
60
+
61
+ def _is_type_optionally_equal(type_: Type, target_type: Type) -> bool:
62
+ if type_ == target_type:
63
+ return True
64
+
65
+ origin = get_origin(type_)
66
+ if origin is not Union:
67
+ return False
68
+
69
+ args = get_args(type_)
70
+ if len(args) != 2:
71
+ return False
72
+
73
+ source_type, none_type = args
74
+ if none_type is not type(None):
75
+ return False
76
+
77
+ return _is_type_optionally_equal(source_type, target_type)
78
+
79
+
80
+ def _is_type_optionally_in(type_: Type, target_types: Tuple[Type, ...]) -> bool:
81
+ return any(_is_type_optionally_equal(type_, target_type) for target_type in target_types)
@@ -0,0 +1,18 @@
1
+ import os
2
+ from typing import Optional
3
+
4
+ from vellum import Vellum, VellumEnvironment
5
+
6
+
7
+ def create_vellum_client(api_key: Optional[str] = None) -> Vellum:
8
+ if api_key is None:
9
+ api_key = os.getenv("VELLUM_API_KEY", default="")
10
+
11
+ return Vellum(
12
+ api_key=api_key,
13
+ environment=VellumEnvironment(
14
+ default=os.getenv("VELLUM_DEFAULT_API_URL", os.getenv("VELLUM_API_URL", "https://api.vellum.ai")),
15
+ documents=os.getenv("VELLUM_DOCUMENTS_API_URL", os.getenv("VELLUM_API_URL", "https://documents.vellum.ai")),
16
+ predict=os.getenv("VELLUM_PREDICT_API_URL", os.getenv("VELLUM_API_URL", "https://predict.vellum.ai")),
17
+ ),
18
+ )
@@ -0,0 +1,5 @@
1
+ from .base import BaseWorkflow
2
+
3
+ __all__ = [
4
+ "BaseWorkflow",
5
+ ]