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
@@ -29,6 +29,7 @@ from typing import (
29
29
 
30
30
  from pydantic import ValidationError
31
31
 
32
+ from vellum.utils.uuid import is_valid_uuid
32
33
  from vellum.workflows.edges import Edge
33
34
  from vellum.workflows.emitters.base import BaseWorkflowEmitter
34
35
  from vellum.workflows.errors import WorkflowError, WorkflowErrorCode
@@ -38,6 +39,8 @@ from vellum.workflows.events.node import (
38
39
  NodeExecutionFulfilledEvent,
39
40
  NodeExecutionInitiatedBody,
40
41
  NodeExecutionInitiatedEvent,
42
+ NodeExecutionLogBody,
43
+ NodeExecutionLogEvent,
41
44
  NodeExecutionPausedBody,
42
45
  NodeExecutionPausedEvent,
43
46
  NodeExecutionRejectedBody,
@@ -74,6 +77,7 @@ from vellum.workflows.nodes.bases import BaseNode
74
77
  from vellum.workflows.nodes.mocks import MockNodeExecutionArg
75
78
  from vellum.workflows.nodes.utils import get_unadorned_node
76
79
  from vellum.workflows.outputs import BaseOutputs
80
+ from vellum.workflows.references.trigger import TriggerAttributeReference
77
81
  from vellum.workflows.resolvers.base import BaseWorkflowResolver
78
82
  from vellum.workflows.runner import WorkflowRunner
79
83
  from vellum.workflows.runner.runner import ExternalInputsArg, RunFromNodeArg
@@ -271,8 +275,6 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
271
275
  for resolver in self.resolvers:
272
276
  resolver.register_workflow_instance(self)
273
277
 
274
- self.validate()
275
-
276
278
  @property
277
279
  def context(self) -> WorkflowContext:
278
280
  return self._context
@@ -289,12 +291,12 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
289
291
  for item in graph:
290
292
  if isinstance(item, Graph):
291
293
  graphs.append(item)
292
- elif issubclass(item, BaseNode):
294
+ elif inspect.isclass(item) and issubclass(item, BaseNode):
293
295
  graphs.append(Graph.from_node(item))
294
296
  else:
295
297
  raise ValueError(f"Unexpected graph type: {type(item)}")
296
298
  return graphs
297
- if issubclass(graph, BaseNode):
299
+ if inspect.isclass(graph) and issubclass(graph, BaseNode):
298
300
  return [Graph.from_node(graph)]
299
301
  raise ValueError(f"Unexpected graph type: {type(graph)}")
300
302
 
@@ -526,6 +528,7 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
526
528
  max_concurrency: Optional[int] = None,
527
529
  timeout: Optional[float] = None,
528
530
  trigger: Optional[BaseTrigger] = None,
531
+ event_max_size: Optional[int] = None,
529
532
  ) -> WorkflowEventStream:
530
533
  """
531
534
  Invoke a Workflow, yielding events as they are emitted.
@@ -576,6 +579,10 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
576
579
  The trigger instance is bound to the workflow state, making its attributes accessible to downstream nodes.
577
580
  Required for workflows that only have IntegrationTrigger; optional for workflows with both ManualTrigger
578
581
  and IntegrationTrigger.
582
+
583
+ event_max_size: Optional[int] = None
584
+ The maximum size in bytes for serialized events. If an event's serialized size exceeds this value,
585
+ the outputs will be set to an empty dict.
579
586
  """
580
587
 
581
588
  should_yield = event_filter or workflow_event_filter
@@ -593,6 +600,7 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
593
600
  init_execution_context=self._execution_context,
594
601
  trigger=trigger,
595
602
  execution_id=execution_id,
603
+ event_max_size=event_max_size,
596
604
  )
597
605
  self._current_runner = runner
598
606
  runner_stream = runner.stream()
@@ -604,15 +612,28 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
604
612
 
605
613
  return WorkflowEventGenerator(_generate_filtered_events(), runner_stream.span_id)
606
614
 
607
- def validate(self) -> None:
615
+ @classmethod
616
+ def validate(cls) -> None:
608
617
  """
609
618
  Validates the Workflow, by running through our list of linter rules.
610
619
  """
611
- # TODO: Implement rule that all entrypoints are non empty
612
- # https://app.shortcut.com/vellum/story/4327
613
- pass
614
620
 
615
- def _resolve_node_ref(self, node_ref: Union[Type[BaseNode], UUID, str]) -> Type[BaseNode]:
621
+ cls._validate_no_self_edges()
622
+
623
+ @classmethod
624
+ def get_all_nodes_recursive(cls) -> Iterator[Type[BaseNode]]:
625
+ """
626
+ Returns an iterator over all nodes in the Workflow, including nodes nested in subworkflows.
627
+ """
628
+ for node in cls.get_all_nodes():
629
+ yield node
630
+ for node_ref in node:
631
+ attr_value = node_ref.instance
632
+ if inspect.isclass(attr_value) and issubclass(attr_value, BaseWorkflow):
633
+ yield from attr_value.get_all_nodes_recursive()
634
+
635
+ @classmethod
636
+ def resolve_node_ref(cls, node_ref: Union[Type[BaseNode], UUID, str]) -> Type[BaseNode]:
616
637
  """
617
638
  Resolve a node reference (class, UUID, or string) to a node class.
618
639
 
@@ -630,18 +651,27 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
630
651
  return node_ref
631
652
 
632
653
  candidate_nodes: List[Type[BaseNode]] = []
633
- for node in self.get_all_nodes():
654
+ for node in cls.get_all_nodes_recursive():
634
655
  candidate_nodes.append(node)
635
656
  wrapped_node = get_unadorned_node(node)
636
657
  if wrapped_node != node:
637
658
  candidate_nodes.append(wrapped_node)
638
659
 
639
660
  if isinstance(node_ref, UUID):
661
+ node_uuid = node_ref
662
+ elif is_valid_uuid(node_ref):
663
+ node_uuid = UUID(node_ref)
664
+ else:
665
+ node_uuid = None
666
+
667
+ if node_uuid:
640
668
  for node in candidate_nodes:
641
- if node.__id__ == node_ref:
669
+ if node.__id__ == node_uuid:
642
670
  return node
643
- identifier = str(node_ref)
644
- raise WorkflowInitializationException(message=f"Node '{identifier}' not found in workflow")
671
+ raise WorkflowInitializationException(
672
+ message=f"Node '{node_uuid}' not found in workflow",
673
+ raw_data={"node_ref": str(node_uuid)},
674
+ )
645
675
 
646
676
  if isinstance(node_ref, str):
647
677
  try:
@@ -650,14 +680,23 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
650
680
  node_class = getattr(module, class_name)
651
681
  if inspect.isclass(node_class) and issubclass(node_class, BaseNode):
652
682
  return node_class
653
- raise WorkflowInitializationException(message=f"Node '{node_ref}' not found in workflow")
683
+ raise WorkflowInitializationException(
684
+ message=f"Node '{node_ref}' not found in workflow",
685
+ raw_data={"node_ref": node_ref},
686
+ )
654
687
  except (ValueError, ModuleNotFoundError, AttributeError):
655
688
  for node in candidate_nodes:
656
689
  if f"{node.__module__}.{node.__name__}" == node_ref:
657
690
  return node
658
- raise WorkflowInitializationException(message=f"Node '{node_ref}' not found in workflow")
691
+ raise WorkflowInitializationException(
692
+ message=f"Node '{node_ref}' not found in workflow",
693
+ raw_data={"node_ref": node_ref},
694
+ )
659
695
 
660
- raise WorkflowInitializationException(message=f"Node '{node_ref}' not found in workflow")
696
+ raise WorkflowInitializationException(
697
+ message=f"Node '{node_ref}' not found in workflow",
698
+ raw_data={"node_ref": str(node_ref)},
699
+ )
661
700
 
662
701
  def run_node(
663
702
  self, node: Union[Type[BaseNode], UUID, str], *, inputs: Optional[Dict[str, Any]] = None
@@ -676,7 +715,7 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
676
715
  Raises:
677
716
  ValueError: If the node reference cannot be resolved
678
717
  """
679
- resolved_node = self._resolve_node_ref(node)
718
+ resolved_node = self.resolve_node_ref(node)
680
719
  runner = WorkflowRunner(self)
681
720
  span_id = uuid4()
682
721
  node_instance = resolved_node(state=self.get_default_state(), context=self._context, inputs=inputs)
@@ -717,12 +756,27 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
717
756
  return self.get_inputs_class()()
718
757
 
719
758
  def get_default_state(
720
- self, workflow_inputs: Optional[InputsType] = None, execution_id: Optional[UUID] = None
759
+ self,
760
+ workflow_inputs: Optional[InputsType] = None,
761
+ execution_id: Optional[UUID] = None,
762
+ *,
763
+ trigger_attributes: Optional[Dict[TriggerAttributeReference, Any]] = None,
721
764
  ) -> StateType:
722
- meta = StateMeta(
723
- parent=self._parent_state,
724
- workflow_inputs=workflow_inputs or self.get_default_inputs(),
725
- workflow_definition=self.__class__,
765
+ resolved_inputs: Optional[InputsType] = workflow_inputs
766
+
767
+ meta_payload: Dict[str, Any] = {
768
+ "parent": self._parent_state,
769
+ "workflow_definition": self.__class__,
770
+ "workflow_inputs": resolved_inputs,
771
+ }
772
+ if trigger_attributes is not None:
773
+ meta_payload["trigger_attributes"] = trigger_attributes
774
+
775
+ meta = StateMeta.model_validate(
776
+ meta_payload,
777
+ context={
778
+ "workflow_definition": self.__class__,
779
+ },
726
780
  )
727
781
 
728
782
  # Makes the uuid factory mocker work this way instead of setting in cosntructor
@@ -781,11 +835,13 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
781
835
 
782
836
  state_class = cls.get_state_class()
783
837
  if "meta" in state:
838
+ meta_payload = dict(state["meta"])
839
+ if workflow_inputs is not None:
840
+ meta_payload["workflow_inputs"] = workflow_inputs
841
+ meta_payload.setdefault("workflow_definition", cls)
842
+
784
843
  state["meta"] = StateMeta.model_validate(
785
- {
786
- **state["meta"],
787
- "workflow_inputs": workflow_inputs,
788
- },
844
+ meta_payload,
789
845
  context={
790
846
  "workflow_definition": cls,
791
847
  },
@@ -794,7 +850,9 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
794
850
  return state_class(**state)
795
851
 
796
852
  @classmethod
797
- def deserialize_trigger(cls, trigger_id: Optional[UUID], inputs: dict) -> Union[InputsType, BaseTrigger]:
853
+ def deserialize_trigger(
854
+ cls, trigger_id: Optional[Union[str, UUID]], inputs: dict
855
+ ) -> Union[InputsType, BaseTrigger]:
798
856
  """
799
857
  Deserialize a trigger from a trigger_id and inputs dict.
800
858
 
@@ -803,8 +861,12 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
803
861
 
804
862
  Parameters
805
863
  ----------
806
- trigger_id: Optional[UUID]
807
- The UUID of the trigger class to instantiate. If None, returns workflow Inputs.
864
+ trigger_id: Optional[Union[str, UUID]]
865
+ The identifier of the trigger class to instantiate. Can be:
866
+ - None: Returns workflow Inputs
867
+ - UUID: Matches by trigger class __id__
868
+ - str (valid UUID): Matches by trigger class __id__
869
+ - str (non-UUID): Matches by trigger name (from __trigger_name__)
808
870
 
809
871
  inputs: dict
810
872
  The inputs to pass to the trigger or Inputs constructor.
@@ -824,18 +886,54 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
824
886
  inputs_class = cls.get_inputs_class()
825
887
  return inputs_class(**inputs)
826
888
 
889
+ # Determine if trigger_id is a UUID or a name string
890
+ resolved_trigger_id: Optional[UUID] = None
891
+ trigger_name: Optional[str] = None
892
+
893
+ if isinstance(trigger_id, UUID):
894
+ resolved_trigger_id = trigger_id
895
+ elif is_valid_uuid(trigger_id):
896
+ resolved_trigger_id = UUID(trigger_id)
897
+ else:
898
+ trigger_name = trigger_id
899
+
827
900
  trigger_classes = []
828
901
  for subgraph in cls.get_subgraphs():
829
902
  for trigger_class in subgraph.triggers:
830
- if trigger_class.__id__ == trigger_id:
831
- return trigger_class(**inputs)
903
+ # Match by UUID
904
+ if resolved_trigger_id is not None and trigger_class.__id__ == resolved_trigger_id:
905
+ try:
906
+ return trigger_class(**inputs)
907
+ except Exception as e:
908
+ raise WorkflowInitializationException(
909
+ message=f"Failed to instantiate trigger {trigger_class.__name__}: {e}",
910
+ workflow_definition=cls,
911
+ ) from e
912
+
913
+ # Match by name
914
+ if trigger_name is not None and trigger_class.__trigger_name__ == trigger_name:
915
+ try:
916
+ return trigger_class(**inputs)
917
+ except Exception as e:
918
+ raise WorkflowInitializationException(
919
+ message=f"Failed to instantiate trigger {trigger_class.__name__}: {e}",
920
+ workflow_definition=cls,
921
+ ) from e
832
922
 
833
923
  trigger_classes.append(trigger_class)
834
924
 
835
- raise WorkflowInitializationException(
836
- message=f"No trigger class found with id {trigger_id} in workflow {cls.__name__}. "
837
- f"Available trigger classes: {[trigger_class.__name__ for trigger_class in trigger_classes]}"
838
- )
925
+ # Build helpful error message
926
+ if trigger_name is not None:
927
+ available_names = [trigger_class.__trigger_name__ for trigger_class in trigger_classes]
928
+ raise WorkflowInitializationException(
929
+ message=f"No trigger class found with name '{trigger_name}' in workflow {cls.__name__}. "
930
+ f"Available trigger names: {available_names}"
931
+ )
932
+ else:
933
+ raise WorkflowInitializationException(
934
+ message=f"No trigger class found with id {trigger_id} in workflow {cls.__name__}. "
935
+ f"Available trigger classes: {[trigger_class.__name__ for trigger_class in trigger_classes]}"
936
+ )
839
937
 
840
938
  @staticmethod
841
939
  def load_from_module(module_path: str) -> Type["BaseWorkflow"]:
@@ -874,6 +972,19 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
874
972
  raise WorkflowInitializationException(message=f"Invalid variable reference: {e}") from e
875
973
  except Exception as e:
876
974
  raise WorkflowInitializationException(message=f"Unexpected failure while loading module: {e}") from e
975
+
976
+ # Attempt to load optional display sidecar module to trigger node ID annotations
977
+ display_path = f"{module_path}.display"
978
+ try:
979
+ importlib.import_module(display_path)
980
+ except ModuleNotFoundError:
981
+ # No display package for this workflow; that's fine.
982
+ pass
983
+ except Exception as e:
984
+ raise WorkflowInitializationException(
985
+ message=f"Unexpected failure while loading display module '{display_path}': {e}"
986
+ ) from e
987
+
877
988
  workflows: List[Type[BaseWorkflow]] = []
878
989
  for name in dir(module):
879
990
  if name.startswith("__"):
@@ -905,6 +1016,35 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
905
1016
  for emitter in self.emitters:
906
1017
  emitter.join()
907
1018
 
1019
+ @classmethod
1020
+ def _validate_no_self_edges(cls) -> None:
1021
+ """
1022
+ Validate that the workflow graph doesn't contain unconditional self-edges (infinite loops).
1023
+
1024
+ A node is considered to have an unconditional self-edge if all of its ports target itself.
1025
+
1026
+ Args:
1027
+ edges: List of edge dictionaries from the serialized workflow
1028
+
1029
+ Raises:
1030
+ WorkflowInitializationException: If an unconditional self-edge is detected
1031
+ """
1032
+
1033
+ for node in cls.get_all_nodes():
1034
+ node_ports = [list(port.edges) for port in node.Ports]
1035
+ if (
1036
+ all(
1037
+ all(edge.to_node == node for edge in port_edges) and len(port_edges) > 0
1038
+ for port_edges in node_ports
1039
+ )
1040
+ and len(node_ports) > 0
1041
+ ):
1042
+ raise WorkflowInitializationException(
1043
+ message=f"Graph contains a self-edge ({node.__name__} >> {node.__name__}).",
1044
+ workflow_definition=cls,
1045
+ code=WorkflowErrorCode.INVALID_WORKFLOW,
1046
+ )
1047
+
908
1048
 
909
1049
  WorkflowExecutionInitiatedBody.model_rebuild()
910
1050
  WorkflowExecutionFulfilledBody.model_rebuild()
@@ -920,6 +1060,7 @@ NodeExecutionRejectedBody.model_rebuild()
920
1060
  NodeExecutionPausedBody.model_rebuild()
921
1061
  NodeExecutionResumedBody.model_rebuild()
922
1062
  NodeExecutionStreamingBody.model_rebuild()
1063
+ NodeExecutionLogBody.model_rebuild()
923
1064
 
924
1065
  WorkflowExecutionInitiatedEvent.model_rebuild()
925
1066
  WorkflowExecutionFulfilledEvent.model_rebuild()
@@ -935,5 +1076,6 @@ NodeExecutionRejectedEvent.model_rebuild()
935
1076
  NodeExecutionPausedEvent.model_rebuild()
936
1077
  NodeExecutionResumedEvent.model_rebuild()
937
1078
  NodeExecutionStreamingEvent.model_rebuild()
1079
+ NodeExecutionLogEvent.model_rebuild()
938
1080
 
939
1081
  StateMeta.model_rebuild()
@@ -340,6 +340,32 @@ def test_workflow__unsupported_graph_item():
340
340
  assert "Unexpected graph type: <class 'int'>" in str(exc_info.value)
341
341
 
342
342
 
343
+ def test_resolve_graph__non_class_item_in_set():
344
+ """Test that _resolve_graph properly handles non-class items in a set without crashing on issubclass()."""
345
+ # GIVEN a non-class value (a string, not a class)
346
+ # WHEN we call _resolve_graph with a set containing a non-class value
347
+ with pytest.raises(ValueError) as exc_info:
348
+ BaseWorkflow._resolve_graph({"not_a_class"}) # type: ignore
349
+
350
+ # THEN it should raise ValueError instead of crashing with "issubclass() arg 1 must be a class"
351
+ assert "Unexpected graph type:" in str(exc_info.value)
352
+ assert "<class 'str'>" in str(exc_info.value)
353
+
354
+
355
+ def test_resolve_graph__non_class_value():
356
+ """Test that _resolve_graph properly handles non-class values without crashing on issubclass()."""
357
+ # GIVEN a non-class value (a string, not a class)
358
+ non_class_value = "not_a_class"
359
+
360
+ # WHEN we call _resolve_graph with a non-class value
361
+ with pytest.raises(ValueError) as exc_info:
362
+ BaseWorkflow._resolve_graph(non_class_value) # type: ignore
363
+
364
+ # THEN it should raise ValueError instead of crashing with "issubclass() arg 1 must be a class"
365
+ assert "Unexpected graph type:" in str(exc_info.value)
366
+ assert "<class 'str'>" in str(exc_info.value)
367
+
368
+
343
369
  def test_base_workflow__deserialize_state():
344
370
  # GIVEN a state definition
345
371
  class State(BaseState):
@@ -783,3 +809,28 @@ def test_base_workflow__run_node_with_inputs():
783
809
  # AND the execution result should use the overridden and non-overridden attributes
784
810
  fulfilled_event = events[1]
785
811
  assert fulfilled_event.body.outputs.result == "overridden_overridden_value_default_value2_not_overridden"
812
+
813
+
814
+ def test_base_workflow__invalid_graph__outgoing_edge_with_no_ports():
815
+ """Test that graph construction fails if we attempt to create an outgoing edge from a node with no ports."""
816
+
817
+ # GIVEN
818
+ class StartNode(BaseNode[BaseState]):
819
+ pass
820
+
821
+ class MyFinalOutput(BaseNode[BaseState]):
822
+ class Ports(BaseNode.Ports):
823
+ pass
824
+
825
+ class EndNode(BaseNode[BaseState]):
826
+ pass
827
+
828
+ # THEN
829
+ with pytest.raises(
830
+ ValueError,
831
+ match="Cannot create edges from graph because all terminal nodes have no ports defined: MyFinalOutput. "
832
+ + "Nodes with empty Ports classes cannot be connected to other nodes.",
833
+ ):
834
+
835
+ class InvalidWorkflow(BaseWorkflow[BaseInputs, BaseState]):
836
+ graph = StartNode >> MyFinalOutput >> EndNode
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 1.11.2
3
+ Version: 1.13.5
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -20,10 +20,14 @@ Classifier: Programming Language :: Python :: 3.12
20
20
  Classifier: Programming Language :: Python :: 3.8
21
21
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
22
  Classifier: Typing :: Typed
23
+ Provides-Extra: mypy
24
+ Provides-Extra: zuban
23
25
  Requires-Dist: Jinja2 (>=3.1.0,<4.0.0)
24
26
  Requires-Dist: click (>=8.1.7,<9.0.0)
25
27
  Requires-Dist: docker (>=7.1.0,<8.0.0)
26
28
  Requires-Dist: httpx (>=0.21.2)
29
+ Requires-Dist: jsonschema (>=4.0.0)
30
+ Requires-Dist: mypy (>=1.13.0) ; extra == "mypy"
27
31
  Requires-Dist: orderly-set (>=5.2.2,<6.0.0)
28
32
  Requires-Dist: publication (==0.0.3)
29
33
  Requires-Dist: pydantic (>=1.9.2)
@@ -36,6 +40,7 @@ Requires-Dist: pyyaml (>=6.0.0,<7.0.0)
36
40
  Requires-Dist: requests (>=2.31.0,<3.0.0)
37
41
  Requires-Dist: tomli (>=2.0.0,<3.0.0)
38
42
  Requires-Dist: typing_extensions (>=4.0.0)
43
+ Requires-Dist: zuban (>=0.4.2) ; extra == "zuban"
39
44
  Project-URL: Repository, https://github.com/vellum-ai/vellum-python-sdks
40
45
  Description-Content-Type: text/markdown
41
46