vellum-ai 1.11.2__py3-none-any.whl → 1.13.5__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.

Potentially problematic release.


This version of vellum-ai might be problematic. Click here for more details.

Files changed (275) hide show
  1. vellum/__init__.py +18 -0
  2. vellum/client/README.md +1 -1
  3. vellum/client/core/client_wrapper.py +2 -2
  4. vellum/client/core/force_multipart.py +4 -2
  5. vellum/client/core/http_response.py +1 -1
  6. vellum/client/core/pydantic_utilities.py +7 -4
  7. vellum/client/errors/too_many_requests_error.py +1 -2
  8. vellum/client/reference.md +677 -76
  9. vellum/client/resources/container_images/client.py +299 -0
  10. vellum/client/resources/container_images/raw_client.py +286 -0
  11. vellum/client/resources/documents/client.py +20 -10
  12. vellum/client/resources/documents/raw_client.py +20 -10
  13. vellum/client/resources/events/raw_client.py +4 -4
  14. vellum/client/resources/integration_auth_configs/client.py +2 -0
  15. vellum/client/resources/integration_auth_configs/raw_client.py +2 -0
  16. vellum/client/resources/integration_providers/client.py +28 -2
  17. vellum/client/resources/integration_providers/raw_client.py +24 -0
  18. vellum/client/resources/integrations/client.py +52 -4
  19. vellum/client/resources/integrations/raw_client.py +61 -0
  20. vellum/client/resources/workflow_deployments/client.py +156 -0
  21. vellum/client/resources/workflow_deployments/raw_client.py +334 -0
  22. vellum/client/resources/workflows/client.py +212 -8
  23. vellum/client/resources/workflows/raw_client.py +343 -6
  24. vellum/client/types/__init__.py +18 -0
  25. vellum/client/types/api_actor_type_enum.py +1 -1
  26. vellum/client/types/check_workflow_execution_status_error.py +21 -0
  27. vellum/client/types/check_workflow_execution_status_response.py +29 -0
  28. vellum/client/types/code_execution_package_request.py +21 -0
  29. vellum/client/types/composio_execute_tool_request.py +5 -0
  30. vellum/client/types/composio_tool_definition.py +1 -0
  31. vellum/client/types/container_image_build_config.py +1 -0
  32. vellum/client/types/container_image_container_image_tag.py +1 -0
  33. vellum/client/types/dataset_row_push_request.py +3 -0
  34. vellum/client/types/document_document_to_document_index.py +1 -0
  35. vellum/client/types/integration_name.py +24 -0
  36. vellum/client/types/node_execution_fulfilled_body.py +1 -0
  37. vellum/client/types/node_execution_log_body.py +24 -0
  38. vellum/client/types/node_execution_log_event.py +47 -0
  39. vellum/client/types/prompt_deployment_release_prompt_deployment.py +1 -0
  40. vellum/client/types/runner_config_request.py +24 -0
  41. vellum/client/types/severity_enum.py +5 -0
  42. vellum/client/types/slim_composio_tool_definition.py +1 -0
  43. vellum/client/types/slim_document_document_to_document_index.py +2 -0
  44. vellum/client/types/type_checker_enum.py +5 -0
  45. vellum/client/types/vellum_audio.py +5 -1
  46. vellum/client/types/vellum_audio_request.py +5 -1
  47. vellum/client/types/vellum_document.py +5 -1
  48. vellum/client/types/vellum_document_request.py +5 -1
  49. vellum/client/types/vellum_image.py +5 -1
  50. vellum/client/types/vellum_image_request.py +5 -1
  51. vellum/client/types/vellum_node_execution_event.py +2 -0
  52. vellum/client/types/vellum_variable.py +5 -0
  53. vellum/client/types/vellum_variable_extensions.py +1 -0
  54. vellum/client/types/vellum_variable_type.py +1 -0
  55. vellum/client/types/vellum_video.py +5 -1
  56. vellum/client/types/vellum_video_request.py +5 -1
  57. vellum/client/types/workflow_deployment_release_workflow_deployment.py +1 -0
  58. vellum/client/types/workflow_event.py +2 -0
  59. vellum/client/types/workflow_execution_fulfilled_body.py +1 -0
  60. vellum/client/types/workflow_result_event_output_data_array.py +1 -1
  61. vellum/client/types/workflow_result_event_output_data_chat_history.py +1 -1
  62. vellum/client/types/workflow_result_event_output_data_error.py +1 -1
  63. vellum/client/types/workflow_result_event_output_data_function_call.py +1 -1
  64. vellum/client/types/workflow_result_event_output_data_json.py +1 -1
  65. vellum/client/types/workflow_result_event_output_data_number.py +1 -1
  66. vellum/client/types/workflow_result_event_output_data_search_results.py +1 -1
  67. vellum/client/types/workflow_result_event_output_data_string.py +1 -1
  68. vellum/client/types/workflow_sandbox_execute_node_response.py +8 -0
  69. vellum/plugins/vellum_mypy.py +37 -2
  70. vellum/types/check_workflow_execution_status_error.py +3 -0
  71. vellum/types/check_workflow_execution_status_response.py +3 -0
  72. vellum/types/code_execution_package_request.py +3 -0
  73. vellum/types/node_execution_log_body.py +3 -0
  74. vellum/types/node_execution_log_event.py +3 -0
  75. vellum/types/runner_config_request.py +3 -0
  76. vellum/types/severity_enum.py +3 -0
  77. vellum/types/type_checker_enum.py +3 -0
  78. vellum/types/workflow_sandbox_execute_node_response.py +3 -0
  79. vellum/utils/files/mixin.py +26 -0
  80. vellum/utils/files/tests/test_mixin.py +62 -0
  81. vellum/utils/tests/test_vellum_client.py +95 -0
  82. vellum/utils/uuid.py +19 -2
  83. vellum/utils/vellum_client.py +10 -3
  84. vellum/workflows/__init__.py +7 -1
  85. vellum/workflows/descriptors/base.py +86 -0
  86. vellum/workflows/descriptors/tests/test_utils.py +9 -0
  87. vellum/workflows/errors/tests/__init__.py +0 -0
  88. vellum/workflows/errors/tests/test_types.py +52 -0
  89. vellum/workflows/errors/types.py +1 -0
  90. vellum/workflows/events/node.py +24 -0
  91. vellum/workflows/events/tests/test_event.py +123 -0
  92. vellum/workflows/events/types.py +2 -1
  93. vellum/workflows/events/workflow.py +28 -2
  94. vellum/workflows/expressions/add.py +3 -0
  95. vellum/workflows/expressions/tests/test_add.py +24 -0
  96. vellum/workflows/graph/graph.py +26 -5
  97. vellum/workflows/graph/tests/test_graph.py +228 -1
  98. vellum/workflows/inputs/base.py +22 -6
  99. vellum/workflows/inputs/dataset_row.py +121 -16
  100. vellum/workflows/inputs/tests/test_inputs.py +3 -3
  101. vellum/workflows/integrations/tests/test_vellum_integration_service.py +84 -0
  102. vellum/workflows/integrations/vellum_integration_service.py +12 -1
  103. vellum/workflows/loaders/base.py +2 -0
  104. vellum/workflows/nodes/bases/base.py +37 -16
  105. vellum/workflows/nodes/bases/tests/test_base_node.py +104 -1
  106. vellum/workflows/nodes/core/inline_subworkflow_node/node.py +1 -0
  107. vellum/workflows/nodes/core/inline_subworkflow_node/tests/test_node.py +1 -1
  108. vellum/workflows/nodes/core/map_node/node.py +7 -5
  109. vellum/workflows/nodes/core/map_node/tests/test_node.py +33 -0
  110. vellum/workflows/nodes/core/retry_node/node.py +1 -0
  111. vellum/workflows/nodes/core/try_node/node.py +1 -0
  112. vellum/workflows/nodes/displayable/api_node/node.py +3 -2
  113. vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py +38 -0
  114. vellum/workflows/nodes/displayable/bases/api_node/node.py +1 -1
  115. vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py +18 -1
  116. vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +109 -2
  117. vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py +13 -2
  118. vellum/workflows/nodes/displayable/code_execution_node/node.py +9 -15
  119. vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py +65 -24
  120. vellum/workflows/nodes/displayable/code_execution_node/utils.py +3 -0
  121. vellum/workflows/nodes/displayable/final_output_node/node.py +24 -69
  122. vellum/workflows/nodes/displayable/final_output_node/tests/test_node.py +53 -3
  123. vellum/workflows/nodes/displayable/note_node/node.py +4 -1
  124. vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +16 -5
  125. vellum/workflows/nodes/displayable/tests/test_text_prompt_deployment_node.py +47 -0
  126. vellum/workflows/nodes/displayable/tool_calling_node/node.py +74 -34
  127. vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py +204 -8
  128. vellum/workflows/nodes/displayable/tool_calling_node/utils.py +92 -71
  129. vellum/workflows/nodes/mocks.py +47 -213
  130. vellum/workflows/nodes/tests/test_mocks.py +0 -177
  131. vellum/workflows/nodes/utils.py +23 -8
  132. vellum/workflows/outputs/base.py +36 -3
  133. vellum/workflows/references/environment_variable.py +1 -11
  134. vellum/workflows/references/lazy.py +8 -0
  135. vellum/workflows/references/state_value.py +24 -1
  136. vellum/workflows/references/tests/test_lazy.py +58 -0
  137. vellum/workflows/references/trigger.py +8 -3
  138. vellum/workflows/references/workflow_input.py +8 -0
  139. vellum/workflows/resolvers/resolver.py +13 -3
  140. vellum/workflows/resolvers/tests/test_resolver.py +31 -0
  141. vellum/workflows/runner/runner.py +159 -14
  142. vellum/workflows/runner/tests/__init__.py +0 -0
  143. vellum/workflows/runner/tests/test_runner.py +170 -0
  144. vellum/workflows/sandbox.py +7 -8
  145. vellum/workflows/state/base.py +89 -30
  146. vellum/workflows/state/context.py +74 -3
  147. vellum/workflows/state/tests/test_state.py +269 -1
  148. vellum/workflows/tests/test_dataset_row.py +8 -7
  149. vellum/workflows/tests/test_sandbox.py +97 -8
  150. vellum/workflows/triggers/__init__.py +2 -1
  151. vellum/workflows/triggers/base.py +160 -28
  152. vellum/workflows/triggers/chat_message.py +141 -0
  153. vellum/workflows/triggers/integration.py +12 -0
  154. vellum/workflows/triggers/manual.py +3 -1
  155. vellum/workflows/triggers/schedule.py +3 -1
  156. vellum/workflows/triggers/tests/test_chat_message.py +257 -0
  157. vellum/workflows/types/core.py +18 -0
  158. vellum/workflows/types/definition.py +6 -13
  159. vellum/workflows/types/generics.py +12 -0
  160. vellum/workflows/types/tests/test_utils.py +12 -0
  161. vellum/workflows/types/utils.py +32 -2
  162. vellum/workflows/types/workflow_metadata.py +124 -0
  163. vellum/workflows/utils/functions.py +152 -16
  164. vellum/workflows/utils/pydantic_schema.py +19 -1
  165. vellum/workflows/utils/tests/test_functions.py +123 -8
  166. vellum/workflows/utils/tests/test_validate.py +79 -0
  167. vellum/workflows/utils/tests/test_vellum_variables.py +62 -2
  168. vellum/workflows/utils/uuids.py +90 -0
  169. vellum/workflows/utils/validate.py +108 -0
  170. vellum/workflows/utils/vellum_variables.py +96 -16
  171. vellum/workflows/workflows/base.py +177 -35
  172. vellum/workflows/workflows/tests/test_base_workflow.py +51 -0
  173. {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/METADATA +6 -1
  174. {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/RECORD +274 -227
  175. vellum_cli/__init__.py +21 -0
  176. vellum_cli/config.py +16 -2
  177. vellum_cli/pull.py +2 -0
  178. vellum_cli/push.py +23 -10
  179. vellum_cli/tests/conftest.py +8 -13
  180. vellum_cli/tests/test_image_push.py +4 -11
  181. vellum_cli/tests/test_pull.py +83 -68
  182. vellum_cli/tests/test_push.py +251 -2
  183. vellum_ee/assets/node-definitions.json +225 -12
  184. vellum_ee/scripts/generate_node_definitions.py +15 -3
  185. vellum_ee/workflows/display/base.py +4 -3
  186. vellum_ee/workflows/display/nodes/base_node_display.py +44 -11
  187. vellum_ee/workflows/display/nodes/tests/test_base_node_display.py +93 -0
  188. vellum_ee/workflows/display/nodes/types.py +1 -0
  189. vellum_ee/workflows/display/nodes/vellum/__init__.py +0 -2
  190. vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py +5 -2
  191. vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +1 -1
  192. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +10 -2
  193. vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +17 -14
  194. vellum_ee/workflows/display/nodes/vellum/map_node.py +2 -0
  195. vellum_ee/workflows/display/nodes/vellum/note_node.py +18 -3
  196. vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +37 -14
  197. vellum_ee/workflows/display/nodes/vellum/tests/test_code_execution_node.py +62 -2
  198. vellum_ee/workflows/display/nodes/vellum/tests/test_final_output_node.py +136 -0
  199. vellum_ee/workflows/display/nodes/vellum/tests/test_note_node.py +44 -7
  200. vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py +5 -13
  201. vellum_ee/workflows/display/nodes/vellum/tests/test_subworkflow_deployment_node.py +27 -17
  202. vellum_ee/workflows/display/nodes/vellum/tests/test_tool_calling_node.py +145 -22
  203. vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py +107 -2
  204. vellum_ee/workflows/display/nodes/vellum/utils.py +54 -12
  205. vellum_ee/workflows/display/tests/test_base_workflow_display.py +13 -16
  206. vellum_ee/workflows/display/tests/test_json_schema_validation.py +190 -0
  207. vellum_ee/workflows/display/tests/test_mocks.py +912 -0
  208. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +14 -2
  209. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +109 -0
  210. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py +3 -0
  211. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py +187 -1
  212. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py +34 -325
  213. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py +42 -393
  214. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +13 -315
  215. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_default_state_serialization.py +2 -122
  216. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py +24 -115
  217. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py +4 -93
  218. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py +7 -80
  219. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py +9 -101
  220. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +77 -308
  221. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +62 -324
  222. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +3 -82
  223. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_prompt_deployment_serialization.py +4 -142
  224. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_serialization.py +1 -61
  225. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_set_state_node_serialization.py +4 -4
  226. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py +205 -134
  227. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_node_serialization.py +34 -146
  228. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py +2 -0
  229. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_composio_serialization.py +8 -6
  230. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +137 -266
  231. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_tool_wrapper_serialization.py +84 -0
  232. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_mcp_serialization.py +55 -16
  233. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py +15 -1
  234. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_tool_wrapper_serialization.py +71 -0
  235. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_vellum_integration_serialization.py +119 -0
  236. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_workflow_deployment_serialization.py +1 -1
  237. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py +0 -2
  238. vellum_ee/workflows/display/tests/workflow_serialization/test_chat_message_dict_reference_serialization.py +22 -1
  239. vellum_ee/workflows/display/tests/workflow_serialization/test_chat_message_trigger_serialization.py +412 -0
  240. vellum_ee/workflows/display/tests/workflow_serialization/test_code_tool_node_reference_error.py +106 -0
  241. vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +9 -41
  242. vellum_ee/workflows/display/tests/workflow_serialization/test_duplicate_trigger_name_validation.py +208 -0
  243. vellum_ee/workflows/display/tests/workflow_serialization/test_final_output_node_not_referenced_by_workflow_outputs.py +45 -0
  244. vellum_ee/workflows/display/tests/workflow_serialization/test_infinite_loop_validation.py +66 -0
  245. vellum_ee/workflows/display/tests/workflow_serialization/test_int_input_serialization.py +40 -0
  246. vellum_ee/workflows/display/tests/workflow_serialization/test_integration_trigger_serialization.py +8 -14
  247. vellum_ee/workflows/display/tests/workflow_serialization/test_integration_trigger_validation.py +173 -0
  248. vellum_ee/workflows/display/tests/workflow_serialization/test_integration_trigger_with_entrypoint_node_id.py +16 -13
  249. vellum_ee/workflows/display/tests/workflow_serialization/test_list_vellum_document_serialization.py +5 -1
  250. vellum_ee/workflows/display/tests/workflow_serialization/test_manual_trigger_serialization.py +12 -2
  251. vellum_ee/workflows/display/tests/workflow_serialization/test_multi_trigger_same_node_serialization.py +111 -0
  252. vellum_ee/workflows/display/tests/workflow_serialization/test_no_triggers_no_entrypoint_validation.py +64 -0
  253. vellum_ee/workflows/display/tests/workflow_serialization/test_partial_workflow_meta_display_override.py +55 -0
  254. vellum_ee/workflows/display/tests/workflow_serialization/test_sandbox_dataset_mocks_serialization.py +268 -0
  255. vellum_ee/workflows/display/tests/workflow_serialization/test_sandbox_invalid_pdf_data_url.py +49 -0
  256. vellum_ee/workflows/display/tests/workflow_serialization/test_sandbox_validation_errors.py +112 -0
  257. vellum_ee/workflows/display/tests/workflow_serialization/test_scheduled_trigger_serialization.py +25 -16
  258. vellum_ee/workflows/display/tests/workflow_serialization/test_terminal_node_in_unused_graphs_serialization.py +53 -0
  259. vellum_ee/workflows/display/utils/exceptions.py +34 -0
  260. vellum_ee/workflows/display/utils/expressions.py +463 -52
  261. vellum_ee/workflows/display/utils/metadata.py +98 -33
  262. vellum_ee/workflows/display/utils/tests/test_metadata.py +31 -0
  263. vellum_ee/workflows/display/utils/triggers.py +153 -0
  264. vellum_ee/workflows/display/utils/vellum.py +59 -5
  265. vellum_ee/workflows/display/workflows/base_workflow_display.py +656 -254
  266. vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py +26 -0
  267. vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +77 -29
  268. vellum_ee/workflows/server/namespaces.py +18 -0
  269. vellum_ee/workflows/tests/test_display_meta.py +2 -0
  270. vellum_ee/workflows/tests/test_serialize_module.py +174 -7
  271. vellum_ee/workflows/tests/test_server.py +0 -3
  272. vellum_ee/workflows/display/nodes/vellum/function_node.py +0 -14
  273. {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/LICENSE +0 -0
  274. {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/WHEEL +0 -0
  275. {vellum_ai-1.11.2.dist-info → vellum_ai-1.13.5.dist-info}/entry_points.txt +0 -0
@@ -1,16 +1,34 @@
1
1
  import dataclasses
2
+ from datetime import datetime
2
3
  import inspect
3
- from typing import TYPE_CHECKING, Annotated, Any, Callable, List, Literal, Optional, Type, Union, get_args, get_origin
4
+ from typing import (
5
+ TYPE_CHECKING,
6
+ Annotated,
7
+ Any,
8
+ Callable,
9
+ ForwardRef,
10
+ List,
11
+ Literal,
12
+ Optional,
13
+ Type,
14
+ TypeVar,
15
+ Union,
16
+ get_args,
17
+ get_origin,
18
+ )
4
19
 
5
20
  from pydantic import BaseModel
6
21
  from pydantic_core import PydanticUndefined
7
22
  from pydash import snake_case
8
23
 
9
24
  from vellum import Vellum
25
+ from vellum.client.types.array_chat_message_content_item import ArrayChatMessageContentItem
10
26
  from vellum.client.types.function_definition import FunctionDefinition
27
+ from vellum.workflows.constants import undefined
11
28
  from vellum.workflows.integrations.composio_service import ComposioService
12
29
  from vellum.workflows.integrations.mcp_service import MCPService
13
30
  from vellum.workflows.integrations.vellum_integration_service import VellumIntegrationService
31
+ from vellum.workflows.types.core import is_json_type
14
32
  from vellum.workflows.types.definition import (
15
33
  ComposioToolDefinition,
16
34
  DeploymentDefinition,
@@ -41,12 +59,51 @@ for k, v in list(type_map.items()):
41
59
  type_map[k.__name__] = v
42
60
 
43
61
 
62
+ def _get_def_name(annotation: Type) -> str:
63
+ return f"{annotation.__module__}.{annotation.__qualname__}"
64
+
65
+
66
+ recorded_unions = {
67
+ ArrayChatMessageContentItem: "vellum.client.types.array_chat_message_content_item.ArrayChatMessageContentItem",
68
+ }
69
+
70
+
44
71
  def compile_annotation(annotation: Optional[Any], defs: dict[str, Any]) -> dict:
45
72
  if annotation is None:
46
73
  return {"type": "null"}
47
74
 
75
+ if annotation is Any:
76
+ return {}
77
+
78
+ # Handle type variables (e.g., MapNodeItemType) - return empty schema since we can't determine the type
79
+ if isinstance(annotation, TypeVar):
80
+ return {}
81
+
82
+ if annotation is datetime:
83
+ return {"type": "string", "format": "date-time"}
84
+
48
85
  if get_origin(annotation) is Union:
49
- return {"anyOf": [compile_annotation(a, defs) for a in get_args(annotation)]}
86
+ if is_json_type(get_args(annotation)):
87
+ return {"$ref": "#/$defs/vellum.workflows.types.core.Json"}
88
+
89
+ if annotation in recorded_unions:
90
+ return {"$ref": f"#/$defs/{recorded_unions[annotation]}"}
91
+
92
+ # Filter out Type[undefined] from union args - it just makes the property optional
93
+ filtered_args = [
94
+ a
95
+ for a in get_args(annotation)
96
+ if not (get_origin(a) is type and get_args(a) and get_args(a)[0] is undefined)
97
+ ]
98
+
99
+ if len(filtered_args) == 0:
100
+ # Edge case: all union members were Type[undefined], return empty schema
101
+ return {}
102
+
103
+ if len(filtered_args) == 1:
104
+ return compile_annotation(filtered_args[0], defs)
105
+
106
+ return {"anyOf": [compile_annotation(a, defs) for a in filtered_args]}
50
107
 
51
108
  if get_origin(annotation) is Literal:
52
109
  values = list(get_args(annotation))
@@ -84,7 +141,8 @@ def compile_annotation(annotation: Optional[Any], defs: dict[str, Any]) -> dict:
84
141
  return result
85
142
 
86
143
  if dataclasses.is_dataclass(annotation) and isinstance(annotation, type):
87
- if annotation.__name__ not in defs:
144
+ def_name = _get_def_name(annotation)
145
+ if def_name not in defs:
88
146
  properties = {}
89
147
  required = []
90
148
  for field in dataclasses.fields(annotation):
@@ -93,11 +151,12 @@ def compile_annotation(annotation: Optional[Any], defs: dict[str, Any]) -> dict:
93
151
  required.append(field.name)
94
152
  else:
95
153
  properties[field.name]["default"] = _compile_default_value(field.default)
96
- defs[annotation.__name__] = {"type": "object", "properties": properties, "required": required}
97
- return {"$ref": f"#/$defs/{annotation.__name__}"}
154
+ defs[def_name] = {"type": "object", "properties": properties, "required": required}
155
+ return {"$ref": f"#/$defs/{def_name}"}
98
156
 
99
157
  if inspect.isclass(annotation) and issubclass(annotation, BaseModel):
100
- if annotation.__name__ not in defs:
158
+ def_name = _get_def_name(annotation)
159
+ if def_name not in defs:
101
160
  properties = {}
102
161
  required = []
103
162
  for field_name, field_info in annotation.model_fields.items():
@@ -111,9 +170,22 @@ def compile_annotation(annotation: Optional[Any], defs: dict[str, Any]) -> dict:
111
170
  required.append(field_name)
112
171
  else:
113
172
  properties[field_name]["default"] = _compile_default_value(field_info.default)
114
- defs[annotation.__name__] = {"type": "object", "properties": properties, "required": required}
173
+ defs[def_name] = {"type": "object", "properties": properties, "required": required}
174
+
175
+ return {"$ref": f"#/$defs/{def_name}"}
176
+
177
+ if type(annotation) is ForwardRef:
178
+ # Ignore forward references for now
179
+ return {}
180
+
181
+ # Handle Type[undefined] - skip it as it just makes the property optional
182
+ if get_origin(annotation) is type:
183
+ args = get_args(annotation)
184
+ if args and args[0] is undefined:
185
+ return {}
115
186
 
116
- return {"$ref": f"#/$defs/{annotation.__name__}"}
187
+ if annotation not in type_map:
188
+ raise ValueError(f"Failed to compile type: {annotation}")
117
189
 
118
190
  return {"type": type_map[annotation]}
119
191
 
@@ -161,6 +233,7 @@ def compile_function_definition(function: Callable) -> FunctionDefinition:
161
233
 
162
234
  # Get inputs from the decorator if present
163
235
  inputs = getattr(function, "__vellum_inputs__", {})
236
+ examples = getattr(function, "__vellum_examples__", None)
164
237
  exclude_params = set(inputs.keys())
165
238
 
166
239
  properties = {}
@@ -192,6 +265,8 @@ def compile_function_definition(function: Callable) -> FunctionDefinition:
192
265
  parameters = {"type": "object", "properties": properties, "required": required}
193
266
  if defs:
194
267
  parameters["$defs"] = defs
268
+ if examples is not None:
269
+ parameters["examples"] = examples
195
270
 
196
271
  return FunctionDefinition(
197
272
  name=function.__name__,
@@ -208,6 +283,11 @@ def compile_inline_workflow_function_definition(workflow_class: Type["BaseWorkfl
208
283
  inputs_class = workflow_class.get_inputs_class()
209
284
  vars_inputs_class = vars(inputs_class)
210
285
 
286
+ # Get inputs from the decorator if present (to exclude from schema)
287
+ inputs = getattr(workflow_class, "__vellum_inputs__", {})
288
+ examples = getattr(workflow_class, "__vellum_examples__", None)
289
+ exclude_params = set(inputs.keys())
290
+
211
291
  properties = {}
212
292
  required = []
213
293
  defs: dict[str, Any] = {}
@@ -216,6 +296,10 @@ def compile_inline_workflow_function_definition(workflow_class: Type["BaseWorkfl
216
296
  if name.startswith("__"):
217
297
  continue
218
298
 
299
+ # Skip parameters that are in the exclude_params set
300
+ if exclude_params and name in exclude_params:
301
+ continue
302
+
219
303
  properties[name] = compile_annotation(field_type, defs)
220
304
 
221
305
  # Check if the field has a default value
@@ -228,6 +312,8 @@ def compile_inline_workflow_function_definition(workflow_class: Type["BaseWorkfl
228
312
  parameters = {"type": "object", "properties": properties, "required": required}
229
313
  if defs:
230
314
  parameters["$defs"] = defs
315
+ if examples is not None:
316
+ parameters["examples"] = examples
231
317
 
232
318
  return FunctionDefinition(
233
319
  name=snake_case(workflow_class.__name__),
@@ -342,6 +428,7 @@ def compile_vellum_integration_tool_definition(
342
428
  integration=tool_def.integration_name,
343
429
  provider=tool_def.provider.value,
344
430
  tool_name=tool_def.name,
431
+ toolkit_version=tool_def.toolkit_version,
345
432
  )
346
433
 
347
434
  return FunctionDefinition(
@@ -354,11 +441,66 @@ def compile_vellum_integration_tool_definition(
354
441
  return FunctionDefinition(name=tool_def.name, description=tool_def.description, parameters={})
355
442
 
356
443
 
357
- def use_tool_inputs(**inputs):
444
+ ToolType = Union[Callable[..., Any], Type["BaseWorkflow"]]
445
+
446
+
447
+ def tool(
448
+ *,
449
+ inputs: Optional[dict[str, Any]] = None,
450
+ examples: Optional[List[dict[str, Any]]] = None,
451
+ ) -> Callable[[ToolType], ToolType]:
452
+ """
453
+ Decorator to configure a tool function or inline workflow.
454
+
455
+ Currently supports specifying which parameters should come from parent workflow inputs
456
+ via the `inputs` mapping. Also supports providing `examples` which will be hoisted
457
+ into the JSON Schema `examples` keyword for this tool's parameters.
458
+
459
+ Args:
460
+ inputs: Mapping of parameter names to parent input references
461
+ examples: List of example argument objects for the tool
462
+
463
+ Example with function:
464
+ @tool(inputs={
465
+ "parent_input": ParentInputs.parent_input,
466
+ }, examples=[{"location": "San Francisco"}])
467
+ def get_string(parent_input: str, user_query: str) -> str:
468
+ return f"Parent: {parent_input}, Query: {user_query}"
469
+
470
+ Example with inline workflow:
471
+ @tool(inputs={
472
+ "context": ParentInputs.context,
473
+ })
474
+ class MyInlineWorkflow(BaseWorkflow):
475
+ graph = MyNode
476
+
477
+ class Outputs(BaseWorkflow.Outputs):
478
+ result = MyNode.Outputs.result
479
+ """
480
+
481
+ def decorator(func: ToolType) -> ToolType:
482
+ # Store the inputs mapping on the function/workflow for later use
483
+ if inputs is not None:
484
+ setattr(func, "__vellum_inputs__", inputs)
485
+ # Store the examples on the function/workflow for later use
486
+ if examples is not None:
487
+ setattr(func, "__vellum_examples__", examples)
488
+ return func
489
+
490
+ return decorator
491
+
492
+
493
+ def use_tool_inputs(**inputs: Any) -> Callable[[Callable], Callable]:
358
494
  """
359
495
  Decorator to specify which parameters of a tool function should be provided
360
496
  from the parent workflow inputs rather than from the LLM.
361
497
 
498
+ .. deprecated:: 2.0.0
499
+ This function is deprecated and will be removed in version 2.0.0.
500
+ Use :func:`tool` with the ``inputs`` parameter instead.
501
+
502
+ This is a backward-compatible helper equivalent to @tool(inputs={...}).
503
+
362
504
  Args:
363
505
  **inputs: Mapping of parameter names to parent input references
364
506
 
@@ -369,10 +511,4 @@ def use_tool_inputs(**inputs):
369
511
  def get_string(parent_input: str, user_query: str) -> str:
370
512
  return f"Parent: {parent_input}, Query: {user_query}"
371
513
  """
372
-
373
- def decorator(func: Callable) -> Callable:
374
- # Store the inputs mapping on the function for later use
375
- setattr(func, "__vellum_inputs__", inputs)
376
- return func
377
-
378
- return decorator
514
+ return tool(inputs=inputs)
@@ -1,10 +1,28 @@
1
1
  import inspect
2
- from typing import Any, Dict
2
+ from typing import Any, Dict, Type, TypeVar
3
3
 
4
+ import pydantic
4
5
  from pydantic import BaseModel
5
6
 
6
7
  from vellum.workflows.utils.functions import compile_annotation
7
8
 
9
+ T = TypeVar("T")
10
+ IS_PYDANTIC_V2 = pydantic.VERSION.startswith("2.")
11
+ _type_adapter_cache: Dict[Type, "pydantic.TypeAdapter"] = {} # type: ignore[type-arg]
12
+
13
+
14
+ def validate_obj_as(type_: Type[T], object_: Any) -> T:
15
+ """Validate an object as a given type using pydantic's TypeAdapter (v2) or parse_obj_as (v1).
16
+
17
+ This is similar to parse_obj_as but without the convert_and_respect_annotation_metadata step,
18
+ making it suitable for simple type validation without annotation metadata handling.
19
+ """
20
+ if IS_PYDANTIC_V2:
21
+ if type_ not in _type_adapter_cache:
22
+ _type_adapter_cache[type_] = pydantic.TypeAdapter(type_) # type: ignore[attr-defined]
23
+ return _type_adapter_cache[type_].validate_python(object_)
24
+ return pydantic.parse_obj_as(type_, object_) # type: ignore[attr-defined]
25
+
8
26
 
9
27
  def normalize_json(schema_input: Any) -> Any:
10
28
  """
@@ -18,6 +18,8 @@ from vellum.workflows.utils.functions import (
18
18
  compile_function_definition,
19
19
  compile_inline_workflow_function_definition,
20
20
  compile_workflow_deployment_function_definition,
21
+ tool,
22
+ use_tool_inputs,
21
23
  )
22
24
 
23
25
 
@@ -187,14 +189,15 @@ def test_compile_function_definition__dataclasses():
187
189
  compiled_function = compile_function_definition(my_function)
188
190
 
189
191
  # THEN it should return the compiled function definition
192
+ ref_name = f"{__name__}.test_compile_function_definition__dataclasses.<locals>.MyDataClass"
190
193
  assert compiled_function == FunctionDefinition(
191
194
  name="my_function",
192
195
  parameters={
193
196
  "type": "object",
194
- "properties": {"c": {"$ref": "#/$defs/MyDataClass"}},
197
+ "properties": {"c": {"$ref": f"#/$defs/{ref_name}"}},
195
198
  "required": ["c"],
196
199
  "$defs": {
197
- "MyDataClass": {
200
+ ref_name: {
198
201
  "type": "object",
199
202
  "properties": {"a": {"type": "integer"}, "b": {"type": "string"}},
200
203
  "required": ["a", "b"],
@@ -217,14 +220,15 @@ def test_compile_function_definition__pydantic():
217
220
  compiled_function = compile_function_definition(my_function)
218
221
 
219
222
  # THEN it should return the compiled function definition
223
+ ref_name = f"{__name__}.test_compile_function_definition__pydantic.<locals>.MyPydanticModel"
220
224
  assert compiled_function == FunctionDefinition(
221
225
  name="my_function",
222
226
  parameters={
223
227
  "type": "object",
224
- "properties": {"c": {"$ref": "#/$defs/MyPydanticModel"}},
228
+ "properties": {"c": {"$ref": f"#/$defs/{ref_name}"}},
225
229
  "required": ["c"],
226
230
  "$defs": {
227
- "MyPydanticModel": {
231
+ ref_name: {
228
232
  "type": "object",
229
233
  "properties": {
230
234
  "a": {"type": "integer", "description": "The first number"},
@@ -251,14 +255,15 @@ def test_compile_function_definition__default_dataclass():
251
255
  compiled_function = compile_function_definition(my_function)
252
256
 
253
257
  # THEN it should return the compiled function definition
258
+ ref_name = f"{__name__}.test_compile_function_definition__default_dataclass.<locals>.MyDataClass"
254
259
  assert compiled_function == FunctionDefinition(
255
260
  name="my_function",
256
261
  parameters={
257
262
  "type": "object",
258
- "properties": {"c": {"$ref": "#/$defs/MyDataClass", "default": {"a": 1, "b": "hello"}}},
263
+ "properties": {"c": {"$ref": f"#/$defs/{ref_name}", "default": {"a": 1, "b": "hello"}}},
259
264
  "required": [],
260
265
  "$defs": {
261
- "MyDataClass": {
266
+ ref_name: {
262
267
  "type": "object",
263
268
  "properties": {"a": {"type": "integer"}, "b": {"type": "string"}},
264
269
  "required": ["a", "b"],
@@ -281,14 +286,15 @@ def test_compile_function_definition__default_pydantic():
281
286
  compiled_function = compile_function_definition(my_function)
282
287
 
283
288
  # THEN it should return the compiled function definition
289
+ ref_name = f"{__name__}.test_compile_function_definition__default_pydantic.<locals>.MyPydanticModel"
284
290
  assert compiled_function == FunctionDefinition(
285
291
  name="my_function",
286
292
  parameters={
287
293
  "type": "object",
288
- "properties": {"c": {"$ref": "#/$defs/MyPydanticModel", "default": {"a": 1, "b": "hello"}}},
294
+ "properties": {"c": {"$ref": f"#/$defs/{ref_name}", "default": {"a": 1, "b": "hello"}}},
289
295
  "required": [],
290
296
  "$defs": {
291
- "MyPydanticModel": {
297
+ ref_name: {
292
298
  "type": "object",
293
299
  "properties": {"a": {"type": "integer"}, "b": {"type": "string"}},
294
300
  "required": ["a", "b"],
@@ -773,3 +779,112 @@ def test_compile_function_definition__string_annotations_with_future_imports():
773
779
  "required": ["a", "b", "c", "d", "e", "f", "g"],
774
780
  },
775
781
  )
782
+
783
+
784
+ def test_use_tool_inputs__inline_vs_decorator():
785
+ """
786
+ Tests that inline use_tool_inputs(...)(func) behaves the same as @use_tool_inputs(...) decorator.
787
+ """
788
+
789
+ # GIVEN a function with some parameters
790
+ def my_function(a: str, b: int, c: float) -> str:
791
+ """A test function."""
792
+ return f"{a}-{b}-{c}"
793
+
794
+ # WHEN using use_tool_inputs as a decorator
795
+ @use_tool_inputs(a="fixed_a", b=42)
796
+ def decorated_function(a: str, b: int, c: float) -> str:
797
+ """A test function."""
798
+ return f"{a}-{b}-{c}"
799
+
800
+ # AND using use_tool_inputs inline
801
+ inline_function = use_tool_inputs(a="fixed_a", b=42)(my_function)
802
+
803
+ # THEN both should have the same __vellum_inputs__ attribute
804
+ assert hasattr(decorated_function, "__vellum_inputs__")
805
+ assert hasattr(inline_function, "__vellum_inputs__")
806
+
807
+ # AND the inputs should be identical
808
+ assert decorated_function.__vellum_inputs__ == inline_function.__vellum_inputs__
809
+ assert decorated_function.__vellum_inputs__ == {"a": "fixed_a", "b": 42}
810
+
811
+
812
+ def test_tool__inline_vs_decorator():
813
+ """
814
+ Tests that inline tool(inputs={...})(func) behaves the same as @tool(inputs={...}) decorator.
815
+ """
816
+
817
+ # GIVEN a function with some parameters
818
+ def my_function(a: str, b: int, c: float) -> str:
819
+ """A test function."""
820
+ return f"{a}-{b}-{c}"
821
+
822
+ # WHEN using tool as a decorator
823
+ @tool(inputs={"a": "fixed_a", "b": 42})
824
+ def decorated_function(a: str, b: int, c: float) -> str:
825
+ """A test function."""
826
+ return f"{a}-{b}-{c}"
827
+
828
+ # AND using tool inline
829
+ inline_function = tool(inputs={"a": "fixed_a", "b": 42})(my_function)
830
+
831
+ # THEN both should have the same __vellum_inputs__ attribute
832
+ assert hasattr(decorated_function, "__vellum_inputs__")
833
+ assert hasattr(inline_function, "__vellum_inputs__")
834
+
835
+ # AND the inputs should be identical
836
+ assert decorated_function.__vellum_inputs__ == inline_function.__vellum_inputs__
837
+ assert decorated_function.__vellum_inputs__ == {"a": "fixed_a", "b": 42}
838
+
839
+
840
+ def test_tool__backward_compatibility_with_use_tool_inputs():
841
+ """
842
+ Tests that tool(inputs={...}) and use_tool_inputs(**inputs) produce the same __vellum_inputs__ attribute.
843
+ """
844
+
845
+ # GIVEN a function with some parameters
846
+ def my_function(a: str, b: int) -> str:
847
+ """A test function."""
848
+ return f"{a}-{b}"
849
+
850
+ # WHEN using tool as a decorator with inputs dict
851
+ @tool(inputs={"a": "value_a"})
852
+ def tool_decorated(a: str, b: int) -> str:
853
+ """A test function."""
854
+ return f"{a}-{b}"
855
+
856
+ # AND using use_tool_inputs as a decorator with kwargs
857
+ @use_tool_inputs(a="value_a")
858
+ def use_tool_inputs_decorated(a: str, b: int) -> str:
859
+ """A test function."""
860
+ return f"{a}-{b}"
861
+
862
+ # THEN both should have identical __vellum_inputs__ attributes
863
+ assert getattr(tool_decorated, "__vellum_inputs__") == getattr(use_tool_inputs_decorated, "__vellum_inputs__")
864
+ assert getattr(tool_decorated, "__vellum_inputs__") == {"a": "value_a"}
865
+
866
+
867
+ def test_tool_examples_included_in_schema():
868
+ @tool(
869
+ examples=[
870
+ {"location": "San Francisco"},
871
+ {"location": "New York", "units": "celsius"},
872
+ ]
873
+ )
874
+ def get_current_weather(location: str, units: str = "fahrenheit") -> str:
875
+ return "sunny"
876
+
877
+ compiled = compile_function_definition(get_current_weather)
878
+ assert isinstance(compiled.parameters, dict)
879
+ assert compiled.parameters == {
880
+ "type": "object",
881
+ "properties": {
882
+ "location": {"type": "string"},
883
+ "units": {"type": "string", "default": "fahrenheit"},
884
+ },
885
+ "required": ["location"],
886
+ "examples": [
887
+ {"location": "San Francisco"},
888
+ {"location": "New York", "units": "celsius"},
889
+ ],
890
+ }
@@ -0,0 +1,79 @@
1
+ import pytest
2
+ from typing import Any, Union
3
+
4
+ from vellum.workflows.utils.validate import validate_target_type
5
+
6
+
7
+ @pytest.mark.parametrize(
8
+ ["declared_type", "target_type"],
9
+ [
10
+ (str, int),
11
+ (list[str], list[int]),
12
+ (str, Union[str, int]),
13
+ (int, Union[str, int]),
14
+ (list[str], Union[list[str], list[int]]),
15
+ (Union[str, int], Union[bool, float]),
16
+ ],
17
+ ids=[
18
+ "str_int",
19
+ "list_str_int",
20
+ "str_union_str_int",
21
+ "int_union_str_int",
22
+ "list_str_union",
23
+ "union_str_int_union_bool_float",
24
+ ],
25
+ )
26
+ def test_validate__should_raise_exception(
27
+ declared_type,
28
+ target_type,
29
+ ):
30
+ """
31
+ Tests that validate_target_type raises an exception for mismatched types.
32
+ """
33
+
34
+ # WHEN validating the target type
35
+ with pytest.raises(ValueError) as exc_info:
36
+ validate_target_type(declared_type, target_type)
37
+
38
+ # THEN an exception should be raised
39
+ assert "Output type mismatch" in str(exc_info.value)
40
+
41
+
42
+ @pytest.mark.parametrize(
43
+ ["declared_type", "target_type"],
44
+ [
45
+ (str, str),
46
+ (list[str], list[str]),
47
+ (dict, dict[str, str]),
48
+ (str, Any),
49
+ (Any, str),
50
+ (Union[str, int], str),
51
+ (Union[str, int], int),
52
+ (Union[str, int], Union[str, int]),
53
+ (Union[str, int], Union[int, str]),
54
+ (Union[str, int], Union[str, bool]),
55
+ ],
56
+ ids=[
57
+ "str",
58
+ "list_str",
59
+ "bare_dict_params_dict",
60
+ "str_any",
61
+ "any_str",
62
+ "union_str_int_str",
63
+ "union_str_int_int",
64
+ "union_str_int_union_str_int",
65
+ "union_str_int_union_int_str",
66
+ "union_str_int_union_str_bool",
67
+ ],
68
+ )
69
+ def test_validate__should_validate(
70
+ declared_type,
71
+ target_type,
72
+ ):
73
+ """
74
+ Tests that validate_target_type accepts matching types.
75
+ """
76
+
77
+ # WHEN validating the target type
78
+ # THEN no exception should be raised
79
+ validate_target_type(declared_type, target_type)
@@ -1,9 +1,20 @@
1
1
  import pytest
2
- from typing import List, Optional
2
+ from datetime import datetime
3
+ from typing import List, Optional, Union
3
4
 
4
- from vellum import ChatMessage, SearchResult, VellumAudio, VellumDocument, VellumImage, VellumValue
5
+ from vellum import (
6
+ ArrayChatMessageContentItem,
7
+ ChatMessage,
8
+ SearchResult,
9
+ VellumAudio,
10
+ VellumDocument,
11
+ VellumImage,
12
+ VellumValue,
13
+ )
14
+ import vellum.client.types as vellum_types
5
15
  from vellum.workflows.types.core import Json
6
16
  from vellum.workflows.utils.vellum_variables import (
17
+ _is_vellum_value_subtype,
7
18
  primitive_type_to_vellum_variable_type,
8
19
  vellum_variable_type_to_openapi_type,
9
20
  )
@@ -14,6 +25,8 @@ from vellum.workflows.utils.vellum_variables import (
14
25
  [
15
26
  (str, "STRING"),
16
27
  (Optional[str], "STRING"),
28
+ (datetime, "STRING"),
29
+ (Optional[datetime], "STRING"),
17
30
  (int, "NUMBER"),
18
31
  (Optional[int], "NUMBER"),
19
32
  (float, "NUMBER"),
@@ -36,6 +49,8 @@ from vellum.workflows.utils.vellum_variables import (
36
49
  (Optional[list[SearchResult]], "SEARCH_RESULTS"),
37
50
  (list[VellumValue], "ARRAY"),
38
51
  (Optional[list[VellumValue]], "ARRAY"),
52
+ (List[ArrayChatMessageContentItem], "ARRAY"),
53
+ (Optional[List[ArrayChatMessageContentItem]], "ARRAY"),
39
54
  ],
40
55
  )
41
56
  def test_primitive_type_to_vellum_variable_type(type_, expected):
@@ -61,3 +76,48 @@ def test_primitive_type_to_vellum_variable_type(type_, expected):
61
76
  )
62
77
  def test_vellum_variable_type_to_openapi_type(vellum_type, expected):
63
78
  assert vellum_variable_type_to_openapi_type(vellum_type) == expected
79
+
80
+
81
+ class _SomeType:
82
+ pass
83
+
84
+
85
+ _STRING_OR_NUMBER_VELLUM_VALUE = Union[vellum_types.StringVellumValue, vellum_types.NumberVellumValue]
86
+
87
+
88
+ @pytest.mark.parametrize(
89
+ ["type_", "expected_is_vellum_value_subtype"],
90
+ [
91
+ # Individual union members
92
+ (vellum_types.StringVellumValue, True),
93
+ (vellum_types.NumberVellumValue, True),
94
+ (vellum_types.JsonVellumValue, True),
95
+ (vellum_types.AudioVellumValue, True),
96
+ (vellum_types.VideoVellumValue, True),
97
+ (vellum_types.ImageVellumValue, True),
98
+ (vellum_types.DocumentVellumValue, True),
99
+ (vellum_types.FunctionCallVellumValue, True),
100
+ (vellum_types.ErrorVellumValue, True),
101
+ (vellum_types.ArrayVellumValue, True),
102
+ (vellum_types.ChatHistoryVellumValue, True),
103
+ (vellum_types.SearchResultsVellumValue, True),
104
+ (vellum_types.ThinkingVellumValue, True),
105
+ # Misc union types
106
+ (Union[vellum_types.StringVellumValue, vellum_types.NumberVellumValue], True),
107
+ (_STRING_OR_NUMBER_VELLUM_VALUE, True),
108
+ (Union[vellum_types.StringVellumValue], True),
109
+ (Union["vellum_types.StringVellumValue"], False),
110
+ # Random types
111
+ (int, False),
112
+ (_SomeType, False),
113
+ (List[int], False),
114
+ (Union[int, str], False),
115
+ (Optional[int], False),
116
+ (List["_SomeType"], False),
117
+ # Handle non-types gracefully
118
+ (1, False),
119
+ (_SomeType(), False),
120
+ ],
121
+ )
122
+ def test_is_vellum_value_subtype(type_, expected_is_vellum_value_subtype):
123
+ assert _is_vellum_value_subtype(type_) == expected_is_vellum_value_subtype