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,11 +1,13 @@
1
1
  from dataclasses import asdict, is_dataclass
2
2
  import inspect
3
+ import logging
3
4
  from uuid import UUID
4
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, cast
5
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Type, Union, cast, get_args, get_origin
5
6
 
6
7
  from pydantic import BaseModel
7
8
 
8
9
  from vellum.client.types.logical_operator import LogicalOperator
10
+ from vellum.utils.uuid import is_valid_uuid
9
11
  from vellum.workflows.constants import undefined
10
12
  from vellum.workflows.descriptors.base import BaseDescriptor
11
13
  from vellum.workflows.expressions.accessor import AccessorExpression
@@ -25,8 +27,10 @@ from vellum.workflows.expressions.equals import EqualsExpression
25
27
  from vellum.workflows.expressions.greater_than import GreaterThanExpression
26
28
  from vellum.workflows.expressions.greater_than_or_equal_to import GreaterThanOrEqualToExpression
27
29
  from vellum.workflows.expressions.in_ import InExpression
30
+ from vellum.workflows.expressions.is_blank import IsBlankExpression
28
31
  from vellum.workflows.expressions.is_error import IsErrorExpression
29
32
  from vellum.workflows.expressions.is_nil import IsNilExpression
33
+ from vellum.workflows.expressions.is_not_blank import IsNotBlankExpression
30
34
  from vellum.workflows.expressions.is_not_nil import IsNotNilExpression
31
35
  from vellum.workflows.expressions.is_not_null import IsNotNullExpression
32
36
  from vellum.workflows.expressions.is_not_undefined import IsNotUndefinedExpression
@@ -40,6 +44,7 @@ from vellum.workflows.expressions.not_between import NotBetweenExpression
40
44
  from vellum.workflows.expressions.not_in import NotInExpression
41
45
  from vellum.workflows.expressions.or_ import OrExpression
42
46
  from vellum.workflows.expressions.parse_json import ParseJsonExpression
47
+ from vellum.workflows.inputs.base import BaseInputs
43
48
  from vellum.workflows.nodes.bases.base import BaseNode
44
49
  from vellum.workflows.nodes.displayable.bases.utils import primitive_to_vellum_value
45
50
  from vellum.workflows.nodes.utils import get_unadorned_node
@@ -52,16 +57,24 @@ from vellum.workflows.references.state_value import StateValueReference
52
57
  from vellum.workflows.references.trigger import TriggerAttributeReference
53
58
  from vellum.workflows.references.vellum_secret import VellumSecretReference
54
59
  from vellum.workflows.references.workflow_input import WorkflowInputReference
60
+ from vellum.workflows.state.base import BaseState
55
61
  from vellum.workflows.types.core import JsonArray, JsonObject
56
62
  from vellum.workflows.types.generics import is_workflow_class
57
63
  from vellum.workflows.utils.files import virtual_open
58
- from vellum.workflows.utils.functions import compile_function_definition
64
+ from vellum.workflows.utils.functions import compile_function_definition, compile_inline_workflow_function_definition
59
65
  from vellum.workflows.utils.uuids import uuid4_from_hash
60
- from vellum_ee.workflows.display.utils.exceptions import InvalidInputReferenceError, UnsupportedSerializationException
66
+ from vellum.workflows.workflows.base import BaseWorkflow
67
+ from vellum_ee.workflows.display.utils.exceptions import (
68
+ InvalidInputReferenceError,
69
+ InvalidOutputReferenceError,
70
+ UnsupportedSerializationException,
71
+ )
61
72
 
62
73
  if TYPE_CHECKING:
63
74
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
64
75
 
76
+ logger = logging.getLogger(__name__)
77
+
65
78
 
66
79
  def convert_descriptor_to_operator(descriptor: BaseDescriptor) -> LogicalOperator:
67
80
  if isinstance(descriptor, EqualsExpression):
@@ -94,6 +107,10 @@ def convert_descriptor_to_operator(descriptor: BaseDescriptor) -> LogicalOperato
94
107
  return "null"
95
108
  elif isinstance(descriptor, (IsNotNullExpression, IsNotNilExpression, IsNotUndefinedExpression)):
96
109
  return "notNull"
110
+ elif isinstance(descriptor, IsBlankExpression):
111
+ return "blank"
112
+ elif isinstance(descriptor, IsNotBlankExpression):
113
+ return "notBlank"
97
114
  elif isinstance(descriptor, InExpression):
98
115
  return "in"
99
116
  elif isinstance(descriptor, NotInExpression):
@@ -119,28 +136,40 @@ def convert_descriptor_to_operator(descriptor: BaseDescriptor) -> LogicalOperato
119
136
  elif isinstance(descriptor, ConcatExpression):
120
137
  return "concat"
121
138
  else:
122
- raise ValueError(f"Unsupported descriptor type: {descriptor}")
139
+ raise UnsupportedSerializationException(f"Unsupported descriptor type: {descriptor}")
123
140
 
124
141
 
125
142
  def get_child_descriptor(value: LazyReference, display_context: "WorkflowDisplayContext") -> BaseDescriptor:
126
143
  if isinstance(value._get, str):
127
144
  reference_parts = value._get.split(".")
128
145
  if len(reference_parts) < 3:
129
- raise Exception(f"Failed to parse lazy reference: {value._get}. Only Node Output references are supported.")
146
+ raise InvalidOutputReferenceError(
147
+ f"Failed to parse lazy reference: {value._get}. Only Node and Workflow Output references are supported."
148
+ )
130
149
 
131
150
  output_name = reference_parts[-1]
132
151
  nested_class_name = reference_parts[-2]
133
152
  if nested_class_name != "Outputs":
134
- raise Exception(
135
- f"Failed to parse lazy reference: {value._get}. Outputs are the only node reference supported."
153
+ raise InvalidOutputReferenceError(
154
+ f"Failed to parse lazy reference: {value._get}. Outputs are the only supported references."
136
155
  )
137
156
 
138
- node_class_name = ".".join(reference_parts[:-2])
157
+ reference_class_name = ".".join(reference_parts[:-2])
158
+
159
+ # First try to find a node with the matching name
139
160
  for node in display_context.global_node_displays.keys():
140
- if node.__name__ == node_class_name:
161
+ if node.__name__ == reference_class_name:
141
162
  return getattr(node.Outputs, output_name)
142
163
 
143
- raise Exception(f"Failed to parse lazy reference: {value._get}")
164
+ # If no node found, check if it's a reference to the current workflow's outputs
165
+ workflow_class = display_context.workflow_display_class.infer_workflow_class()
166
+ if workflow_class.__name__ == reference_class_name:
167
+ return getattr(workflow_class.Outputs, output_name)
168
+
169
+ raise InvalidOutputReferenceError(
170
+ f"Failed to parse lazy reference: {value._get}. "
171
+ f"Could not find node or workflow class '{reference_class_name}'."
172
+ )
144
173
 
145
174
  return value._get()
146
175
 
@@ -161,13 +190,39 @@ def _get_pydantic_model_definition(model_class: type) -> Optional[JsonObject]:
161
190
  }
162
191
 
163
192
 
193
+ BinaryExpression = Union[
194
+ AddExpression,
195
+ AndExpression,
196
+ BeginsWithExpression,
197
+ CoalesceExpression,
198
+ ConcatExpression,
199
+ ContainsExpression,
200
+ DoesNotBeginWithExpression,
201
+ DoesNotContainExpression,
202
+ DoesNotEndWithExpression,
203
+ DoesNotEqualExpression,
204
+ EndsWithExpression,
205
+ EqualsExpression,
206
+ GreaterThanExpression,
207
+ GreaterThanOrEqualToExpression,
208
+ InExpression,
209
+ LessThanExpression,
210
+ LessThanOrEqualToExpression,
211
+ MinusExpression,
212
+ NotInExpression,
213
+ OrExpression,
214
+ ]
215
+
216
+
164
217
  def _serialize_condition(
165
218
  executable_id: UUID, display_context: "WorkflowDisplayContext", condition: BaseDescriptor
166
219
  ) -> JsonObject:
167
220
  if isinstance(
168
221
  condition,
169
222
  (
223
+ IsBlankExpression,
170
224
  IsErrorExpression,
225
+ IsNotBlankExpression,
171
226
  IsNullExpression,
172
227
  IsNotNullExpression,
173
228
  IsNilExpression,
@@ -198,28 +253,7 @@ def _serialize_condition(
198
253
  }
199
254
  elif isinstance(
200
255
  condition,
201
- (
202
- AddExpression,
203
- AndExpression,
204
- BeginsWithExpression,
205
- CoalesceExpression,
206
- ConcatExpression,
207
- ContainsExpression,
208
- DoesNotBeginWithExpression,
209
- DoesNotContainExpression,
210
- DoesNotEndWithExpression,
211
- DoesNotEqualExpression,
212
- EndsWithExpression,
213
- EqualsExpression,
214
- GreaterThanExpression,
215
- GreaterThanOrEqualToExpression,
216
- InExpression,
217
- LessThanExpression,
218
- LessThanOrEqualToExpression,
219
- MinusExpression,
220
- NotInExpression,
221
- OrExpression,
222
- ),
256
+ get_args(BinaryExpression),
223
257
  ):
224
258
  lhs = serialize_value(executable_id, display_context, condition._lhs)
225
259
  rhs = serialize_value(executable_id, display_context, condition._rhs)
@@ -251,6 +285,55 @@ def serialize_key(key: Any) -> str:
251
285
  return str(key)
252
286
 
253
287
 
288
+ def _extract_node_classes_from_object(obj: Any, seen: Set[Type[BaseNode]], node_classes: List[Type[BaseNode]]) -> None:
289
+ """
290
+ Extract BaseNode subclasses from an object, handling classes, instances, and typing constructs.
291
+
292
+ Modifies seen and node_classes in place.
293
+ """
294
+ if isinstance(obj, str):
295
+ return
296
+
297
+ if inspect.isclass(obj) and issubclass(obj, BaseNode) and obj is not BaseNode:
298
+ if obj not in seen:
299
+ seen.add(obj)
300
+ node_classes.append(obj)
301
+ return
302
+
303
+ if isinstance(obj, BaseNode):
304
+ obj_class = obj.__class__
305
+ if obj_class not in seen:
306
+ seen.add(obj_class)
307
+ node_classes.append(obj_class)
308
+ return
309
+
310
+ origin = get_origin(obj)
311
+ if origin is not None:
312
+ for arg in get_args(obj):
313
+ _extract_node_classes_from_object(arg, seen, node_classes)
314
+
315
+
316
+ def _get_node_references_in_callable(func: Any) -> List[Type[BaseNode]]:
317
+ """
318
+ Check if a callable actually references any BaseNode subclasses in its body.
319
+
320
+ Uses inspect.getclosurevars to detect globals/nonlocals referenced by the function.
321
+
322
+ Returns a list of node classes found in the callable's referenced scope.
323
+ """
324
+ node_classes: List[Type[BaseNode]] = []
325
+ seen: Set[Type[BaseNode]] = set()
326
+
327
+ try:
328
+ closure_vars = inspect.getclosurevars(func)
329
+ for obj in list(closure_vars.globals.values()) + list(closure_vars.nonlocals.values()):
330
+ _extract_node_classes_from_object(obj, seen, node_classes)
331
+ except (TypeError, ValueError):
332
+ pass
333
+
334
+ return node_classes
335
+
336
+
254
337
  def serialize_value(executable_id: UUID, display_context: "WorkflowDisplayContext", value: Any) -> Optional[JsonObject]:
255
338
  """
256
339
  Serialize a value to a JSON object. Returns `None` if the value resolves to `undefined`.
@@ -272,7 +355,12 @@ def serialize_value(executable_id: UUID, display_context: "WorkflowDisplayContex
272
355
  return serialize_value(executable_id, display_context, value._value)
273
356
 
274
357
  if isinstance(value, LazyReference):
275
- child_descriptor = get_child_descriptor(value, display_context)
358
+ try:
359
+ child_descriptor = get_child_descriptor(value, display_context)
360
+ except InvalidOutputReferenceError as e:
361
+ logger.warning("Failed to parse lazy reference '%s', skipping serialization", value.name)
362
+ display_context.add_validation_error(e)
363
+ return None
276
364
  return serialize_value(executable_id, display_context, child_descriptor)
277
365
 
278
366
  if isinstance(value, WorkflowInputReference):
@@ -307,6 +395,22 @@ def serialize_value(executable_id: UUID, display_context: "WorkflowDisplayContex
307
395
  }
308
396
 
309
397
  if isinstance(value, OutputReference):
398
+ if issubclass(value.outputs_class, BaseWorkflow.Outputs):
399
+ if value in display_context.workflow_output_displays:
400
+ workflow_output_display = display_context.workflow_output_displays[value]
401
+ return {
402
+ "type": "WORKFLOW_OUTPUT",
403
+ "output_variable_id": str(workflow_output_display.id),
404
+ }
405
+ return serialize_value(executable_id, display_context, value.instance)
406
+
407
+ if value not in display_context.global_node_output_displays:
408
+ if issubclass(value.outputs_class, BaseNode.Outputs):
409
+ raise InvalidOutputReferenceError(
410
+ f"Reference to node '{value.outputs_class.__parent_class__.__name__}' not found in graph."
411
+ )
412
+ raise InvalidOutputReferenceError(f"Reference to outputs '{value.outputs_class.__qualname__}' is invalid.")
413
+
310
414
  output_display = display_context.global_node_output_displays[value]
311
415
 
312
416
  upstream_node_class = value.outputs_class.__parent_class__
@@ -328,19 +432,10 @@ def serialize_value(executable_id: UUID, display_context: "WorkflowDisplayContex
328
432
  }
329
433
 
330
434
  if isinstance(value, EnvironmentVariableReference):
331
- if value.serialize_as_constant:
332
- return {
333
- "type": "CONSTANT_VALUE",
334
- "value": {
335
- "type": "STRING",
336
- "value": value.name,
337
- },
338
- }
339
- else:
340
- return {
341
- "type": "ENVIRONMENT_VARIABLE",
342
- "environment_variable": value.name,
343
- }
435
+ return {
436
+ "type": "ENVIRONMENT_VARIABLE",
437
+ "environment_variable": value.name,
438
+ }
344
439
 
345
440
  if isinstance(value, ExecutionCountReference):
346
441
  node_class_display = display_context.global_node_displays[value.node_class]
@@ -430,6 +525,7 @@ def serialize_value(executable_id: UUID, display_context: "WorkflowDisplayContex
430
525
  }
431
526
 
432
527
  if is_workflow_class(value):
528
+ # Import here to avoid circular imports
433
529
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
434
530
 
435
531
  # Pass the parent display context so the subworkflow can resolve parent workflow inputs
@@ -440,16 +536,38 @@ def serialize_value(executable_id: UUID, display_context: "WorkflowDisplayContex
440
536
  serialized_value: dict = workflow_display.serialize()
441
537
  name = serialized_value["workflow_raw_data"]["definition"]["name"]
442
538
  description = value.__doc__ or ""
539
+
540
+ # Compile the function definition (which now handles __vellum_inputs__ and __vellum_examples__)
541
+ function_definition = compile_inline_workflow_function_definition(value)
542
+
543
+ # Handle __vellum_inputs__ for inline workflows (similar to function tools)
544
+ inputs = getattr(value, "__vellum_inputs__", {})
545
+ if inputs:
546
+ serialized_inputs = {}
547
+ for param_name, input_ref in inputs.items():
548
+ serialized_input = serialize_value(executable_id, display_context, input_ref)
549
+ if serialized_input is not None:
550
+ serialized_inputs[param_name] = serialized_input
551
+
552
+ model_data = function_definition.model_dump()
553
+ model_data["inputs"] = serialized_inputs
554
+ function_definition_data = model_data
555
+ else:
556
+ function_definition_data = function_definition.model_dump()
557
+
558
+ workflow_data: Dict[str, Any] = {
559
+ "type": "INLINE_WORKFLOW",
560
+ "name": name,
561
+ "description": description,
562
+ "definition": function_definition_data,
563
+ "exec_config": serialized_value,
564
+ }
565
+
443
566
  return {
444
567
  "type": "CONSTANT_VALUE",
445
568
  "value": {
446
569
  "type": "JSON",
447
- "value": {
448
- "type": "INLINE_WORKFLOW",
449
- "name": name,
450
- "description": description,
451
- "exec_config": serialized_value,
452
- },
570
+ "value": workflow_data,
453
571
  },
454
572
  }
455
573
 
@@ -462,6 +580,15 @@ def serialize_value(executable_id: UUID, display_context: "WorkflowDisplayContex
462
580
  return dict_ref
463
581
 
464
582
  if callable(value):
583
+ node_references = _get_node_references_in_callable(value)
584
+ if node_references:
585
+ node_names = ", ".join(node.__name__ for node in node_references)
586
+ raise UnsupportedSerializationException(
587
+ f"Code tools cannot reference workflow nodes ({node_names}). "
588
+ "Consider inlining the node's run method logic directly in your function, "
589
+ "or use an Inline Subworkflow tool instead."
590
+ )
591
+
465
592
  function_definition = compile_function_definition(value)
466
593
 
467
594
  name = function_definition.name
@@ -513,3 +640,287 @@ def serialize_value(executable_id: UUID, display_context: "WorkflowDisplayContex
513
640
  # If it's not any of the references we know about,
514
641
  # then try to serialize it as a nested value
515
642
  return _serialize_condition(executable_id, display_context, value)
643
+
644
+
645
+ def base_descriptor_validator(value: dict, workflow: Type[BaseWorkflow]) -> BaseDescriptor:
646
+ descriptor_type = value.get("type")
647
+
648
+ if descriptor_type == "CONSTANT_VALUE":
649
+ return _constant_value_reference_validator(value.get("value"))
650
+
651
+ if descriptor_type == "WORKFLOW_INPUT":
652
+ return _workflow_input_reference_validator(value.get("input_variable_id"), workflow)
653
+
654
+ if descriptor_type == "WORKFLOW_STATE":
655
+ return _workflow_state_reference_validator(value.get("state_variable_id"), workflow)
656
+
657
+ if descriptor_type == "NODE_OUTPUT":
658
+ return _node_output_reference_validator(value.get("node_id"), value.get("node_output_id"), workflow)
659
+
660
+ if descriptor_type == "EXECUTION_COUNTER":
661
+ return _execution_counter_reference_validator(value.get("node_id"), workflow)
662
+
663
+ if descriptor_type == "VELLUM_SECRET":
664
+ return _vellum_secret_reference_validator(value.get("vellum_secret_name"))
665
+
666
+ if descriptor_type == "ENVIRONMENT_VARIABLE":
667
+ return _environment_variable_reference_validator(value.get("environment_variable"))
668
+
669
+ if descriptor_type == "ARRAY_REFERENCE":
670
+ return _array_reference_validator(value.get("items"), workflow)
671
+
672
+ if descriptor_type == "DICTIONARY_REFERENCE":
673
+ return _dictionary_reference_validator(value.get("entries"), workflow)
674
+
675
+ if descriptor_type == "BINARY_EXPRESSION":
676
+ return _binary_expression_validator(value, workflow)
677
+
678
+ if descriptor_type == "UNARY_EXPRESSION":
679
+ return _unary_expression_validator(value, workflow)
680
+
681
+ if descriptor_type == "TERNARY_EXPRESSION":
682
+ return _ternary_expression_validator(value, workflow)
683
+
684
+ raise ValueError(f"Unsupported descriptor type: {descriptor_type}")
685
+
686
+
687
+ def _constant_value_reference_validator(constant: Any) -> ConstantValueReference:
688
+ if not isinstance(constant, dict):
689
+ raise ValueError(f"Unexpected type for constant reference: {type(constant)}")
690
+
691
+ return ConstantValueReference(constant.get("value"))
692
+
693
+
694
+ def _workflow_input_reference_validator(workflow_input_id: Any, workflow: Type[BaseWorkflow]) -> WorkflowInputReference:
695
+ inputs_class = workflow.get_inputs_class()
696
+ if not issubclass(inputs_class, BaseInputs):
697
+ raise ValueError(f"Unexpected type for inputs class: {type(inputs_class)}")
698
+
699
+ input_reference = next(
700
+ (
701
+ input
702
+ for input in inputs_class
703
+ if isinstance(input, WorkflowInputReference) and str(input.id) == str(workflow_input_id)
704
+ ),
705
+ None,
706
+ )
707
+ if input_reference is None:
708
+ raise ValueError(f"Input reference not found: {workflow_input_id}")
709
+
710
+ return input_reference
711
+
712
+
713
+ def _workflow_state_reference_validator(workflow_state_id: Any, workflow: Type[BaseWorkflow]) -> StateValueReference:
714
+ state_class = workflow.get_state_class()
715
+ if not issubclass(state_class, BaseState):
716
+ raise ValueError(f"Unexpected type for state class: {type(state_class)}")
717
+
718
+ state_reference = next((state for state in state_class if str(state.id) == str(workflow_state_id)), None)
719
+ if state_reference is None:
720
+ raise ValueError(f"State reference not found: {workflow_state_id}")
721
+
722
+ return state_reference
723
+
724
+
725
+ def _node_output_reference_validator(
726
+ node_id: Any, node_output_id: Any, workflow: Type[BaseWorkflow]
727
+ ) -> OutputReference:
728
+ if not is_valid_uuid(node_id):
729
+ raise ValueError(f"Unexpected type for node id: {type(node_id)}")
730
+
731
+ if not is_valid_uuid(node_output_id):
732
+ raise ValueError(f"Unexpected type for node output id: {type(node_output_id)}")
733
+
734
+ node_class = workflow.resolve_node_ref(node_id)
735
+ if not issubclass(node_class, BaseNode):
736
+ raise ValueError(f"Unexpected type for node class: {type(node_class)}")
737
+
738
+ output_reference = next((output for output in node_class.Outputs if str(output.id) == str(node_output_id)), None)
739
+ if output_reference is None:
740
+ raise ValueError(f"Output reference not found: {node_output_id}")
741
+
742
+ return output_reference
743
+
744
+
745
+ def _execution_counter_reference_validator(node_id: Any, workflow: Type[BaseWorkflow]) -> ExecutionCountReference:
746
+ node_class = workflow.resolve_node_ref(node_id)
747
+ return ExecutionCountReference(node_class)
748
+
749
+
750
+ def _vellum_secret_reference_validator(secret_name: Any) -> VellumSecretReference:
751
+ if not isinstance(secret_name, str):
752
+ raise ValueError(f"Unexpected type for vellum secret name: {type(secret_name)}")
753
+ return VellumSecretReference(secret_name)
754
+
755
+
756
+ def _environment_variable_reference_validator(env_var_name: Any) -> EnvironmentVariableReference:
757
+ if not isinstance(env_var_name, str):
758
+ raise ValueError(f"Unexpected type for environment variable name: {type(env_var_name)}")
759
+ return EnvironmentVariableReference(name=env_var_name)
760
+
761
+
762
+ def _array_reference_validator(items: Any, workflow: Type[BaseWorkflow]) -> ConstantValueReference:
763
+ if not isinstance(items, list):
764
+ raise ValueError(f"Unexpected type for array items: {type(items)}")
765
+
766
+ validated_items = []
767
+ for item in items:
768
+ if isinstance(item, dict):
769
+ validated_items.append(base_descriptor_validator(item, workflow))
770
+ else:
771
+ validated_items.append(item)
772
+
773
+ return ConstantValueReference(validated_items)
774
+
775
+
776
+ def _dictionary_reference_validator(entries: Any, workflow: Type[BaseWorkflow]) -> ConstantValueReference:
777
+ if not isinstance(entries, list):
778
+ raise ValueError(f"Unexpected type for dictionary entries: {type(entries)}")
779
+
780
+ validated_dict: Dict[str, Any] = {}
781
+ for entry in entries:
782
+ if not isinstance(entry, dict):
783
+ raise ValueError(f"Unexpected type for dictionary entry: {type(entry)}")
784
+
785
+ key = entry.get("key")
786
+ if not isinstance(key, str):
787
+ raise ValueError(f"Unexpected type for dictionary key: {type(key)}")
788
+
789
+ raw_value = entry.get("value")
790
+ if isinstance(raw_value, dict):
791
+ validated_dict[key] = base_descriptor_validator(raw_value, workflow)
792
+ else:
793
+ validated_dict[key] = raw_value
794
+
795
+ return ConstantValueReference(validated_dict)
796
+
797
+
798
+ UnaryExpression = Union[
799
+ IsBlankExpression,
800
+ IsErrorExpression,
801
+ IsNilExpression,
802
+ IsNotBlankExpression,
803
+ IsNotNilExpression,
804
+ IsNotNullExpression,
805
+ IsNotUndefinedExpression,
806
+ IsNullExpression,
807
+ IsUndefinedExpression,
808
+ LengthExpression,
809
+ ParseJsonExpression,
810
+ ]
811
+
812
+
813
+ def _unary_expression_validator(unary_expression: dict, workflow: Type[BaseWorkflow]) -> UnaryExpression:
814
+ operator = unary_expression.get("operator")
815
+ raw_lhs = unary_expression.get("lhs")
816
+ if not isinstance(raw_lhs, dict):
817
+ raise ValueError(f"Unexpected type for lhs: {type(raw_lhs)}")
818
+
819
+ expression = base_descriptor_validator(raw_lhs, workflow)
820
+
821
+ if operator == "blank":
822
+ return IsBlankExpression(expression=expression)
823
+ elif operator == "notBlank":
824
+ return IsNotBlankExpression(expression=expression)
825
+ elif operator == "null":
826
+ return IsNullExpression(expression=expression)
827
+ elif operator == "notNull":
828
+ return IsNotNullExpression(expression=expression)
829
+ elif operator == "isError":
830
+ return IsErrorExpression(expression=expression)
831
+ elif operator == "length":
832
+ return LengthExpression(expression=expression)
833
+ elif operator == "parseJson":
834
+ return ParseJsonExpression(expression=expression)
835
+
836
+ raise ValueError(f"Unsupported unary expression operator: {operator}")
837
+
838
+
839
+ TernaryExpression = Union[BetweenExpression, NotBetweenExpression]
840
+
841
+
842
+ def _ternary_expression_validator(ternary_expression: dict, workflow: Type[BaseWorkflow]) -> TernaryExpression:
843
+ operator = ternary_expression.get("operator")
844
+ raw_base = ternary_expression.get("base")
845
+ if not isinstance(raw_base, dict):
846
+ raise ValueError(f"Unexpected type for base: {type(raw_base)}")
847
+
848
+ raw_lhs = ternary_expression.get("lhs")
849
+ if not isinstance(raw_lhs, dict):
850
+ raise ValueError(f"Unexpected type for lhs: {type(raw_lhs)}")
851
+
852
+ raw_rhs = ternary_expression.get("rhs")
853
+ if not isinstance(raw_rhs, dict):
854
+ raise ValueError(f"Unexpected type for rhs: {type(raw_rhs)}")
855
+
856
+ base = base_descriptor_validator(raw_base, workflow)
857
+ start = base_descriptor_validator(raw_lhs, workflow)
858
+ end = base_descriptor_validator(raw_rhs, workflow)
859
+
860
+ if operator == "between":
861
+ return BetweenExpression(value=base, start=start, end=end)
862
+ elif operator == "notBetween":
863
+ return NotBetweenExpression(value=base, start=start, end=end)
864
+
865
+ raise ValueError(f"Unsupported ternary expression operator: {operator}")
866
+
867
+
868
+ def _binary_expression_validator(
869
+ binary_expression: dict, workflow: Type[BaseWorkflow]
870
+ ) -> Union[BinaryExpression, AccessorExpression]:
871
+ operator = binary_expression.get("operator")
872
+ raw_lhs = binary_expression.get("lhs")
873
+ if not isinstance(raw_lhs, dict):
874
+ raise ValueError(f"Unexpected type for lhs: {type(raw_lhs)}")
875
+
876
+ raw_rhs = binary_expression.get("rhs")
877
+ if not isinstance(raw_rhs, dict):
878
+ raise ValueError(f"Unexpected type for rhs: {type(raw_rhs)}")
879
+
880
+ lhs = base_descriptor_validator(raw_lhs, workflow)
881
+ rhs = base_descriptor_validator(raw_rhs, workflow)
882
+
883
+ if operator in ("=", "=="):
884
+ return EqualsExpression(lhs=lhs, rhs=rhs)
885
+ elif operator == "!=":
886
+ return DoesNotEqualExpression(lhs=lhs, rhs=rhs)
887
+ elif operator == "<":
888
+ return LessThanExpression(lhs=lhs, rhs=rhs)
889
+ elif operator == ">":
890
+ return GreaterThanExpression(lhs=lhs, rhs=rhs)
891
+ elif operator == "<=":
892
+ return LessThanOrEqualToExpression(lhs=lhs, rhs=rhs)
893
+ elif operator == ">=":
894
+ return GreaterThanOrEqualToExpression(lhs=lhs, rhs=rhs)
895
+ elif operator == "contains":
896
+ return ContainsExpression(lhs=lhs, rhs=rhs)
897
+ elif operator == "beginsWith":
898
+ return BeginsWithExpression(lhs=lhs, rhs=rhs)
899
+ elif operator == "endsWith":
900
+ return EndsWithExpression(lhs=lhs, rhs=rhs)
901
+ elif operator == "doesNotContain":
902
+ return DoesNotContainExpression(lhs=lhs, rhs=rhs)
903
+ elif operator == "doesNotBeginWith":
904
+ return DoesNotBeginWithExpression(lhs=lhs, rhs=rhs)
905
+ elif operator == "doesNotEndWith":
906
+ return DoesNotEndWithExpression(lhs=lhs, rhs=rhs)
907
+ elif operator == "in":
908
+ return InExpression(lhs=lhs, rhs=rhs)
909
+ elif operator == "notIn":
910
+ return NotInExpression(lhs=lhs, rhs=rhs)
911
+ elif operator == "and":
912
+ return AndExpression(lhs=lhs, rhs=rhs)
913
+ elif operator == "or":
914
+ return OrExpression(lhs=lhs, rhs=rhs)
915
+ elif operator == "coalesce":
916
+ return CoalesceExpression(lhs=lhs, rhs=rhs)
917
+ elif operator == "+":
918
+ return AddExpression(lhs=lhs, rhs=rhs)
919
+ elif operator == "-":
920
+ return MinusExpression(lhs=lhs, rhs=rhs)
921
+ elif operator == "concat":
922
+ return ConcatExpression(lhs=lhs, rhs=rhs)
923
+ elif operator == "accessField":
924
+ return AccessorExpression(base=lhs, field=rhs)
925
+
926
+ raise ValueError(f"Unsupported binary expression operator: {operator}")