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
@@ -0,0 +1,170 @@
1
+ from vellum.client.core.api_error import ApiError
2
+ from vellum.workflows.errors.types import WorkflowErrorCode
3
+ from vellum.workflows.events.node import NodeExecutionInitiatedEvent, NodeExecutionRejectedEvent
4
+ from vellum.workflows.inputs.base import BaseInputs
5
+ from vellum.workflows.nodes.bases.base import BaseNode
6
+ from vellum.workflows.state.base import BaseState
7
+ from vellum.workflows.workflows.base import BaseWorkflow
8
+
9
+
10
+ def test_workflow_runner__handles_400_api_error_with_integration_details():
11
+ """
12
+ Tests that WorkflowRunner handles 400 API errors with integration details
13
+ as INVALID_INPUTS errors.
14
+ """
15
+
16
+ # GIVEN a node that raises an ApiError with status_code 400 and integration details
17
+ class TestInputs(BaseInputs):
18
+ pass
19
+
20
+ class TestState(BaseState):
21
+ pass
22
+
23
+ class TestNode(BaseNode[TestState]):
24
+ class Outputs(BaseNode.Outputs):
25
+ result: str
26
+
27
+ def run(self) -> "TestNode.Outputs":
28
+ raise ApiError(
29
+ status_code=400,
30
+ body={
31
+ "message": "Invalid request to integration",
32
+ "integration": {
33
+ "name": "test_integration",
34
+ "provider": "test_provider",
35
+ },
36
+ },
37
+ )
38
+
39
+ class TestWorkflow(BaseWorkflow[TestInputs, TestState]):
40
+ graph = TestNode
41
+
42
+ class Outputs(BaseWorkflow.Outputs):
43
+ result: str
44
+
45
+ workflow = TestWorkflow()
46
+
47
+ # WHEN we run the node
48
+ events = list(workflow.run_node(node=TestNode))
49
+
50
+ # THEN we should get a rejected event with INVALID_INPUTS error code
51
+ assert len(events) == 2
52
+ assert isinstance(events[0], NodeExecutionInitiatedEvent)
53
+ assert isinstance(events[1], NodeExecutionRejectedEvent)
54
+
55
+ # AND the error should have the correct code and message
56
+ rejected_event = events[1]
57
+ assert rejected_event.body.error.code == WorkflowErrorCode.INVALID_INPUTS
58
+ assert rejected_event.body.error.message == "Invalid request to integration"
59
+
60
+ # AND the raw_data should contain the integration details
61
+ assert rejected_event.body.error.raw_data == {
62
+ "integration": {
63
+ "name": "test_integration",
64
+ "provider": "test_provider",
65
+ }
66
+ }
67
+
68
+
69
+ def test_workflow_runner__handles_403_api_error_with_integration_details():
70
+ """
71
+ Tests that WorkflowRunner handles 403 API errors with integration details correctly.
72
+ """
73
+
74
+ # GIVEN a node that raises an ApiError with status_code 403 and integration details
75
+ class TestInputs(BaseInputs):
76
+ pass
77
+
78
+ class TestState(BaseState):
79
+ pass
80
+
81
+ class TestNode(BaseNode[TestState]):
82
+ class Outputs(BaseNode.Outputs):
83
+ result: str
84
+
85
+ def run(self) -> "TestNode.Outputs":
86
+ raise ApiError(
87
+ status_code=403,
88
+ body={
89
+ "message": "You must authenticate with this integration",
90
+ "integration": {
91
+ "name": "test_integration",
92
+ "provider": "test_provider",
93
+ },
94
+ },
95
+ )
96
+
97
+ class TestWorkflow(BaseWorkflow[TestInputs, TestState]):
98
+ graph = TestNode
99
+
100
+ class Outputs(BaseWorkflow.Outputs):
101
+ result: str
102
+
103
+ workflow = TestWorkflow()
104
+
105
+ # WHEN we run the node
106
+ events = list(workflow.run_node(node=TestNode))
107
+
108
+ # THEN we should get a rejected event with INTEGRATION_CREDENTIALS_UNAVAILABLE error code
109
+ assert len(events) == 2
110
+ assert isinstance(events[0], NodeExecutionInitiatedEvent)
111
+ assert isinstance(events[1], NodeExecutionRejectedEvent)
112
+
113
+ # AND the error should have the correct code and message
114
+ rejected_event = events[1]
115
+ assert rejected_event.body.error.code == WorkflowErrorCode.INTEGRATION_CREDENTIALS_UNAVAILABLE
116
+ assert rejected_event.body.error.message == "You must authenticate with this integration"
117
+
118
+ # AND the raw_data should contain the integration details
119
+ assert rejected_event.body.error.raw_data == {
120
+ "integration": {
121
+ "name": "test_integration",
122
+ "provider": "test_provider",
123
+ }
124
+ }
125
+
126
+
127
+ def test_workflow_runner__handles_400_api_error_without_integration_details():
128
+ """
129
+ Tests that WorkflowRunner handles 400 API errors without integration details
130
+ as generic errors (not INTEGRATION_CREDENTIALS_UNAVAILABLE).
131
+ """
132
+
133
+ # GIVEN a node that raises an ApiError with status_code 400 but no integration details
134
+ class TestInputs(BaseInputs):
135
+ pass
136
+
137
+ class TestState(BaseState):
138
+ pass
139
+
140
+ class TestNode(BaseNode[TestState]):
141
+ class Outputs(BaseNode.Outputs):
142
+ result: str
143
+
144
+ def run(self) -> "TestNode.Outputs":
145
+ raise ApiError(
146
+ status_code=400,
147
+ body={
148
+ "message": "Invalid request parameters",
149
+ },
150
+ )
151
+
152
+ class TestWorkflow(BaseWorkflow[TestInputs, TestState]):
153
+ graph = TestNode
154
+
155
+ class Outputs(BaseWorkflow.Outputs):
156
+ result: str
157
+
158
+ workflow = TestWorkflow()
159
+
160
+ # WHEN we run the node
161
+ events = list(workflow.run_node(node=TestNode))
162
+
163
+ # THEN we should get a rejected event
164
+ assert len(events) == 2
165
+ assert isinstance(events[0], NodeExecutionInitiatedEvent)
166
+ assert isinstance(events[1], NodeExecutionRejectedEvent)
167
+
168
+ # AND the error should NOT have INTEGRATION_CREDENTIALS_UNAVAILABLE code
169
+ rejected_event = events[1]
170
+ assert rejected_event.body.error.code != WorkflowErrorCode.INTEGRATION_CREDENTIALS_UNAVAILABLE
@@ -1,4 +1,4 @@
1
- from typing import Any, Dict, Generic, Optional, Sequence, Type, Union
1
+ from typing import Any, Dict, Generic, Optional, Sequence, Union
2
2
 
3
3
  import dotenv
4
4
 
@@ -53,12 +53,12 @@ class WorkflowSandboxRunner(Generic[WorkflowType]):
53
53
  selected_inputs = self._inputs[index]
54
54
 
55
55
  raw_inputs: Union[BaseInputs, Dict[str, Any]]
56
- trigger_class: Optional[Type[BaseTrigger]] = None
56
+ trigger_value: Optional[BaseTrigger] = None
57
57
  node_output_mocks = None
58
58
  if isinstance(selected_inputs, DatasetRow):
59
59
  raw_inputs = selected_inputs.inputs
60
- trigger_class = selected_inputs.workflow_trigger
61
- node_output_mocks = selected_inputs.node_output_mocks
60
+ trigger_value = selected_inputs.workflow_trigger
61
+ node_output_mocks = selected_inputs.mocks
62
62
  else:
63
63
  raw_inputs = selected_inputs
64
64
 
@@ -69,10 +69,7 @@ class WorkflowSandboxRunner(Generic[WorkflowType]):
69
69
  else:
70
70
  inputs_for_stream = raw_inputs
71
71
 
72
- trigger_instance: Optional[BaseTrigger] = None
73
- if trigger_class is not None:
74
- # Instantiate the trigger with the inputs
75
- trigger_instance = trigger_class(**raw_inputs) if isinstance(raw_inputs, dict) else trigger_class()
72
+ trigger_instance: Optional[BaseTrigger] = trigger_value
76
73
 
77
74
  events = self._workflow.stream(
78
75
  inputs=inputs_for_stream,
@@ -97,3 +94,5 @@ class WorkflowSandboxRunner(Generic[WorkflowType]):
97
94
  elif event.name == "node.execution.rejected":
98
95
  self._logger.debug(f"Error: {event.error}")
99
96
  self._logger.error(f"Failed to run Node: {event.node_definition.__name__}")
97
+ elif event.name == "workflow.execution.rejected":
98
+ self._logger.error(f"Workflow rejected! Error: {event.error}")
@@ -5,7 +5,7 @@ from dataclasses import field
5
5
  from datetime import datetime
6
6
  import logging
7
7
  from queue import Queue
8
- from threading import Lock
8
+ from threading import RLock
9
9
  from uuid import UUID, uuid4
10
10
  from typing import (
11
11
  TYPE_CHECKING,
@@ -25,6 +25,7 @@ from typing import (
25
25
  from typing_extensions import dataclass_transform
26
26
 
27
27
  from pydantic import GetCoreSchemaHandler, ValidationInfo, field_serializer, field_validator
28
+ from pydantic.fields import FieldInfo
28
29
  from pydantic_core import core_schema
29
30
 
30
31
  from vellum.client.core.pydantic_utilities import UniversalBaseModel
@@ -53,10 +54,14 @@ logger = logging.getLogger(__name__)
53
54
  class _Snapshottable:
54
55
  _snapshot_callback: Callable[[Optional[StateDelta]], None]
55
56
  _path: str
57
+ _lock: Optional[RLock]
56
58
 
57
- def __bind__(self, path: str, snapshot_callback: Callable[[Optional[StateDelta]], None]) -> None:
59
+ def __bind__(
60
+ self, path: str, snapshot_callback: Callable[[Optional[StateDelta]], None], lock: Optional[RLock] = None
61
+ ) -> None:
58
62
  self._snapshot_callback = snapshot_callback
59
63
  self._path = path
64
+ self._lock = lock
60
65
 
61
66
 
62
67
  @dataclass_transform(kw_only_default=True)
@@ -65,7 +70,9 @@ class _BaseStateMeta(type):
65
70
  if not name.startswith("_"):
66
71
  instance = vars(cls).get(name, undefined)
67
72
  types = infer_types(cls, name)
68
- return StateValueReference(name=name, types=types, instance=instance)
73
+ return StateValueReference(
74
+ name=name, types=types, instance=instance, state_class=cast(Type["BaseState"], cls)
75
+ )
69
76
 
70
77
  return super().__getattribute__(name)
71
78
 
@@ -86,7 +93,11 @@ class _BaseStateMeta(type):
86
93
 
87
94
  class _SnapshottableDict(dict, _Snapshottable):
88
95
  def __setitem__(self, key: Any, value: Any) -> None:
89
- super().__setitem__(key, value)
96
+ if self._lock:
97
+ with self._lock:
98
+ super().__setitem__(key, value)
99
+ else:
100
+ super().__setitem__(key, value)
90
101
  self._snapshot_callback(SetStateDelta(name=f"{self._path}.{key}", delta=value))
91
102
 
92
103
  def __deepcopy__(self, memo: Any) -> "_SnapshottableDict":
@@ -96,19 +107,27 @@ class _SnapshottableDict(dict, _Snapshottable):
96
107
  y[deepcopy(key, memo)] = deepcopy(value, memo)
97
108
 
98
109
  y = _SnapshottableDict(y)
99
- y.__bind__(self._path, self._snapshot_callback)
110
+ y.__bind__(self._path, self._snapshot_callback, self._lock)
100
111
  memo[id(self)] = y
101
112
  return y
102
113
 
103
114
 
104
115
  class _SnapshottableList(list, _Snapshottable):
105
116
  def __setitem__(self, index: Union[SupportsIndex, slice], value: Any) -> None:
106
- super().__setitem__(index, value)
117
+ if self._lock:
118
+ with self._lock:
119
+ super().__setitem__(index, value)
120
+ else:
121
+ super().__setitem__(index, value)
107
122
  if isinstance(index, int):
108
123
  self._snapshot_callback(SetStateDelta(name=f"{self._path}.{index}", delta=value))
109
124
 
110
125
  def append(self, value: Any) -> None:
111
- super().append(value)
126
+ if self._lock:
127
+ with self._lock:
128
+ super().append(value)
129
+ else:
130
+ super().append(value)
112
131
  self._snapshot_callback(AppendStateDelta(name=self._path, delta=value))
113
132
 
114
133
  def __deepcopy__(self, memo: Any) -> "_SnapshottableList":
@@ -119,27 +138,33 @@ class _SnapshottableList(list, _Snapshottable):
119
138
  append(deepcopy(a, memo))
120
139
 
121
140
  y = _SnapshottableList(y)
122
- y.__bind__(self._path, self._snapshot_callback)
141
+ y.__bind__(self._path, self._snapshot_callback, self._lock)
123
142
  memo[id(self)] = y
124
143
  return y
125
144
 
126
145
 
127
- def _make_snapshottable(path: str, value: Any, snapshot_callback: Callable[[Optional[StateDelta]], None]) -> Any:
146
+ def _make_snapshottable(
147
+ path: str,
148
+ value: Any,
149
+ snapshot_callback: Callable[[Optional[StateDelta]], None],
150
+ lock: Optional[RLock] = None,
151
+ ) -> Any:
128
152
  """
129
153
  Edits any value to make it snapshottable on edit. Made as a separate function from `BaseState` to
130
154
  avoid namespace conflicts with subclasses.
131
155
  """
132
156
  if isinstance(value, _Snapshottable):
157
+ value.__bind__(path, snapshot_callback, lock)
133
158
  return value
134
159
 
135
160
  if isinstance(value, dict):
136
161
  snapshottable_dict = _SnapshottableDict(value)
137
- snapshottable_dict.__bind__(path, snapshot_callback)
162
+ snapshottable_dict.__bind__(path, snapshot_callback, lock)
138
163
  return snapshottable_dict
139
164
 
140
165
  if isinstance(value, list):
141
166
  snapshottable_list = _SnapshottableList(value)
142
- snapshottable_list.__bind__(path, snapshot_callback)
167
+ snapshottable_list.__bind__(path, snapshot_callback, lock)
143
168
  return snapshottable_list
144
169
 
145
170
  return value
@@ -307,10 +332,10 @@ class StateMeta(UniversalBaseModel):
307
332
  id: UUID = field(default_factory=uuid4_default_factory)
308
333
  span_id: UUID = field(default_factory=uuid4_default_factory)
309
334
  updated_ts: datetime = field(default_factory=default_datetime_factory)
310
- workflow_inputs: BaseInputs = field(default_factory=BaseInputs)
335
+ trigger_attributes: Optional[Dict[TriggerAttributeReference, Any]] = field(default=None)
336
+ workflow_inputs: Optional[BaseInputs] = field(default=None)
311
337
  external_inputs: Dict[ExternalInputReference, Any] = field(default_factory=dict)
312
338
  node_outputs: Dict[OutputReference, Any] = field(default_factory=dict)
313
- trigger_attributes: Dict[TriggerAttributeReference, Any] = field(default_factory=dict)
314
339
  node_execution_cache: NodeExecutionCache = field(default_factory=NodeExecutionCache)
315
340
  parent: Optional["BaseState"] = None
316
341
  __snapshot_callback__: Optional[Callable[[Optional[StateDelta]], None]] = field(init=False, default=None)
@@ -318,10 +343,27 @@ class StateMeta(UniversalBaseModel):
318
343
  def model_post_init(self, context: Any) -> None:
319
344
  self.__snapshot_callback__ = None
320
345
 
321
- def add_snapshot_callback(self, callback: Callable[[Optional[StateDelta]], None]) -> None:
322
- self.node_outputs = _make_snapshottable("meta.node_outputs", self.node_outputs, callback)
323
- self.external_inputs = _make_snapshottable("meta.external_inputs", self.external_inputs, callback)
324
- self.trigger_attributes = _make_snapshottable("meta.trigger_attributes", self.trigger_attributes, callback)
346
+ # Auto-populate workflow_inputs with defaults only if trigger_attributes is None
347
+ # (trigger_attributes being set indicates a trigger-based workflow where we don't want default inputs)
348
+ if self.workflow_inputs is None and self.trigger_attributes is None:
349
+ workflow_definition = (
350
+ self.workflow_definition if is_workflow_class(self.workflow_definition) else import_workflow_class()
351
+ )
352
+ try:
353
+ inputs_class = workflow_definition.get_inputs_class()
354
+ self.workflow_inputs = inputs_class()
355
+ except Exception:
356
+ self.workflow_inputs = BaseInputs()
357
+
358
+ def add_snapshot_callback(
359
+ self, callback: Callable[[Optional[StateDelta]], None], lock: Optional[RLock] = None
360
+ ) -> None:
361
+ self.node_outputs = _make_snapshottable("meta.node_outputs", self.node_outputs, callback, lock)
362
+ self.external_inputs = _make_snapshottable("meta.external_inputs", self.external_inputs, callback, lock)
363
+ if self.trigger_attributes is not None:
364
+ self.trigger_attributes = _make_snapshottable(
365
+ "meta.trigger_attributes", self.trigger_attributes, callback, lock
366
+ )
325
367
  self.__snapshot_callback__ = callback
326
368
 
327
369
  def __setattr__(self, name: str, value: Any) -> None:
@@ -358,8 +400,10 @@ class StateMeta(UniversalBaseModel):
358
400
 
359
401
  @field_serializer("trigger_attributes")
360
402
  def serialize_trigger_attributes(
361
- self, trigger_attributes: Dict[TriggerAttributeReference, Any], _info: Any
403
+ self, trigger_attributes: Optional[Dict[TriggerAttributeReference, Any]], _info: Any
362
404
  ) -> Dict[str, Any]:
405
+ if trigger_attributes is None:
406
+ return {}
363
407
  return {str(descriptor.id): value for descriptor, value in trigger_attributes.items()}
364
408
 
365
409
  @field_validator("node_outputs", mode="before")
@@ -452,11 +496,10 @@ class StateMeta(UniversalBaseModel):
452
496
  @field_validator("workflow_inputs", mode="before")
453
497
  @classmethod
454
498
  def deserialize_workflow_inputs(cls, workflow_inputs: Any, info: ValidationInfo):
455
- workflow_definition = cls._get_workflow(info)
499
+ context = info.context if isinstance(info.context, dict) else {}
500
+ workflow_definition = context.get("workflow_definition") or cls._get_workflow(info)
456
501
 
457
502
  if workflow_definition:
458
- if workflow_inputs is None:
459
- return workflow_definition.get_inputs_class()()
460
503
  if isinstance(workflow_inputs, dict):
461
504
  return workflow_definition.get_inputs_class()(**workflow_inputs)
462
505
 
@@ -538,7 +581,7 @@ class StateMeta(UniversalBaseModel):
538
581
  class BaseState(metaclass=_BaseStateMeta):
539
582
  meta: StateMeta = field(init=False)
540
583
 
541
- __lock__: Lock = field(init=False)
584
+ __lock__: RLock = field(init=False)
542
585
  __is_quiet__: bool = field(init=False)
543
586
  __is_atomic__: bool = field(init=False)
544
587
  __snapshot_callback__: Callable[["BaseState", List[StateDelta]], None] = field(init=False)
@@ -549,16 +592,23 @@ class BaseState(metaclass=_BaseStateMeta):
549
592
  self.__is_atomic__ = False
550
593
  self.__snapshot_callback__ = lambda state, deltas: None
551
594
  self.__deltas__ = []
552
- self.__lock__ = Lock()
595
+ self.__lock__ = RLock()
553
596
 
554
597
  self.meta = meta or StateMeta()
555
- self.meta.add_snapshot_callback(self.__snapshot__)
598
+ self.meta.add_snapshot_callback(self.__snapshot__, self.__lock__)
556
599
 
557
600
  # Make all class attribute values snapshottable
558
601
  for name, value in self.__class__.__dict__.items():
559
602
  if not name.startswith("_") and name != "meta":
603
+ if isinstance(value, FieldInfo):
604
+ if value.default_factory is not None:
605
+ value = cast(Callable[[], Any], value.default_factory)()
606
+ elif hasattr(value, "default") and value.default is not ...:
607
+ value = value.default
608
+ else:
609
+ continue
560
610
  # Bypass __is_quiet__ instead of `setattr`
561
- snapshottable_value = _make_snapshottable(name, value, self.__snapshot__)
611
+ snapshottable_value = _make_snapshottable(name, value, self.__snapshot__, self.__lock__)
562
612
  super().__setattr__(name, snapshottable_value)
563
613
 
564
614
  for name, value in kwargs.items():
@@ -570,11 +620,17 @@ class BaseState(metaclass=_BaseStateMeta):
570
620
  new_state = deepcopy_with_exclusions(
571
621
  self,
572
622
  exclusions={
573
- "__lock__": Lock(),
623
+ "__lock__": RLock(),
574
624
  },
575
625
  memo=memo,
576
626
  )
577
- new_state.meta.add_snapshot_callback(new_state.__snapshot__)
627
+ new_state.meta.add_snapshot_callback(new_state.__snapshot__, new_state.__lock__)
628
+
629
+ for name, value in list(vars(new_state).items()):
630
+ if not name.startswith("_") and name != "meta":
631
+ rebound_value = _make_snapshottable(name, value, new_state.__snapshot__, new_state.__lock__)
632
+ object.__setattr__(new_state, name, rebound_value)
633
+
578
634
  return new_state
579
635
 
580
636
  def __repr__(self) -> str:
@@ -615,8 +671,9 @@ class BaseState(metaclass=_BaseStateMeta):
615
671
  super().__setattr__(name, delta)
616
672
  return
617
673
 
618
- snapshottable_value = _make_snapshottable(name, delta, self.__snapshot__)
619
- super().__setattr__(name, snapshottable_value)
674
+ snapshottable_value = _make_snapshottable(name, delta, self.__snapshot__, self.__lock__)
675
+ with self.__lock__:
676
+ super().__setattr__(name, snapshottable_value)
620
677
  self.meta.updated_ts = datetime_now()
621
678
  self.__snapshot__(SetStateDelta(name=name, delta=delta))
622
679
 
@@ -662,7 +719,9 @@ class BaseState(metaclass=_BaseStateMeta):
662
719
  return
663
720
 
664
721
  try:
665
- self.__snapshot_callback__(deepcopy(self), self.__deltas__)
722
+ with self.__lock__:
723
+ state_copy = deepcopy(self)
724
+ self.__snapshot_callback__(state_copy, self.__deltas__)
666
725
  except Exception:
667
726
  logger.exception("Failed to snapshot Workflow state.")
668
727
 
@@ -1,13 +1,17 @@
1
1
  from dataclasses import dataclass
2
2
  from functools import cached_property
3
3
  import json
4
+ import logging
4
5
  from queue import Queue
6
+ import traceback
5
7
  from uuid import UUID, uuid4
6
- from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, Type
8
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Type
7
9
 
8
10
  from vellum import Vellum, __version__
11
+ from vellum.client.types import SeverityEnum
9
12
  from vellum.workflows.context import ExecutionContext, get_execution_context, set_execution_context
10
- from vellum.workflows.events.types import ExternalParentContext
13
+ from vellum.workflows.events.node import NodeExecutionLogBody, NodeExecutionLogEvent
14
+ from vellum.workflows.events.types import ExternalParentContext, NodeParentContext
11
15
  from vellum.workflows.nodes.mocks import MockNodeExecution, MockNodeExecutionArg
12
16
  from vellum.workflows.outputs.base import BaseOutputs
13
17
  from vellum.workflows.references.constant import ConstantValueReference
@@ -21,6 +25,8 @@ if TYPE_CHECKING:
21
25
  from vellum.workflows.state.base import BaseState
22
26
  from vellum.workflows.workflows.base import BaseWorkflow
23
27
 
28
+ logger = logging.getLogger(__name__)
29
+
24
30
 
25
31
  @dataclass
26
32
  class WorkflowDeploymentMetadata:
@@ -43,6 +49,7 @@ class WorkflowContext:
43
49
  generated_files: Optional[dict[str, str]] = None,
44
50
  namespace: Optional[str] = None,
45
51
  store_class: Optional[Type[Store]] = None,
52
+ event_max_size: Optional[int] = None,
46
53
  ):
47
54
  self._vellum_client = vellum_client
48
55
  self._event_queue: Optional[Queue["WorkflowEvent"]] = None
@@ -50,6 +57,7 @@ class WorkflowContext:
50
57
  self._execution_context = get_execution_context()
51
58
  self._namespace = namespace
52
59
  self._store_class = store_class if store_class is not None else Store
60
+ self._event_max_size = event_max_size
53
61
 
54
62
  if execution_context is not None:
55
63
  self._execution_context.trace_id = execution_context.trace_id
@@ -93,6 +101,10 @@ class WorkflowContext:
93
101
  def store_class(self) -> Type[Store]:
94
102
  return self._store_class
95
103
 
104
+ @property
105
+ def event_max_size(self) -> Optional[int]:
106
+ return self._event_max_size
107
+
96
108
  @property
97
109
  def monitoring_url(self) -> Optional[str]:
98
110
  """
@@ -140,6 +152,61 @@ class WorkflowContext:
140
152
  # For custom domains, assume the same pattern: api.* -> app.*
141
153
  return api_url.replace("api.", "app.", 1)
142
154
 
155
+ def emit_log_event(
156
+ self,
157
+ severity: SeverityEnum,
158
+ message: str,
159
+ attributes: Optional[Dict[str, Any]] = None,
160
+ exc_info: Optional[bool] = None,
161
+ ) -> None:
162
+ """Emit a log event for a particular node.
163
+
164
+ This is in active development and may have breaking changes.
165
+ """
166
+ from vellum.workflows.nodes.bases import BaseNode
167
+
168
+ if self._event_queue is None:
169
+ return
170
+
171
+ execution_context = get_execution_context()
172
+ parent_context = execution_context.parent_context
173
+ while parent_context:
174
+ if isinstance(parent_context, NodeParentContext):
175
+ break
176
+ parent_context = parent_context.parent
177
+
178
+ if not isinstance(parent_context, NodeParentContext):
179
+ return
180
+
181
+ try:
182
+ node_class = parent_context.node_definition.decode()
183
+ except Exception:
184
+ logger.exception("Failed to decode node definition.")
185
+ return
186
+
187
+ if not isinstance(node_class, type) or not issubclass(node_class, BaseNode):
188
+ logger.warning("Node definition is not a subclass of BaseNode.")
189
+ return
190
+
191
+ if exc_info:
192
+ attributes = {
193
+ **(attributes or {}),
194
+ "exc_info": traceback.format_exc(),
195
+ }
196
+
197
+ self._event_queue.put(
198
+ NodeExecutionLogEvent(
199
+ trace_id=execution_context.trace_id,
200
+ span_id=parent_context.span_id,
201
+ body=NodeExecutionLogBody(
202
+ node_definition=node_class,
203
+ severity=severity,
204
+ message=message,
205
+ attributes=attributes,
206
+ ),
207
+ )
208
+ )
209
+
143
210
  def _emit_subworkflow_event(self, event: "WorkflowEvent") -> None:
144
211
  if self._event_queue:
145
212
  self._event_queue.put(event)
@@ -147,6 +214,9 @@ class WorkflowContext:
147
214
  def _register_event_queue(self, event_queue: Queue["WorkflowEvent"]) -> None:
148
215
  self._event_queue = event_queue
149
216
 
217
+ def _register_event_max_size(self, event_max_size: Optional[int]) -> None:
218
+ self._event_max_size = event_max_size
219
+
150
220
  def _register_node_output_mocks(self, node_output_mocks: MockNodeExecutionArg) -> None:
151
221
  for mock in node_output_mocks:
152
222
  if isinstance(mock, MockNodeExecution):
@@ -290,10 +360,11 @@ class WorkflowContext:
290
360
  return None
291
361
 
292
362
  @classmethod
293
- def create_from(cls, context):
363
+ def create_from(cls, context: "WorkflowContext") -> "WorkflowContext":
294
364
  return cls(
295
365
  vellum_client=context.vellum_client,
296
366
  generated_files=context.generated_files,
297
367
  namespace=context.namespace,
298
368
  store_class=context.store_class,
369
+ event_max_size=context.event_max_size,
299
370
  )