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,8 +1,11 @@
1
- from typing import Any, Dict, List, Optional, Sequence, Type, Union
1
+ from datetime import datetime
2
+ import warnings
3
+ from typing import Any, Callable, Dict, List, Optional, Sequence, Union
2
4
 
3
- from pydantic import Field, field_serializer
5
+ from pydantic import ConfigDict, Field, SerializationInfo, field_serializer, model_serializer, model_validator
4
6
 
5
7
  from vellum.client.core.pydantic_utilities import UniversalBaseModel
8
+ from vellum.utils.files.mixin import VellumFileMixin
6
9
  from vellum.workflows.inputs.base import BaseInputs
7
10
  from vellum.workflows.nodes.mocks import MockNodeExecution
8
11
  from vellum.workflows.outputs.base import BaseOutputs
@@ -18,23 +21,107 @@ class DatasetRow(UniversalBaseModel):
18
21
  id: Optional unique identifier for the dataset row
19
22
  label: String label for the dataset row
20
23
  inputs: BaseInputs instance or dict containing the input data
21
- workflow_trigger_id: Optional Trigger identifying the workflow trigger class for this scenario
22
- node_output_mocks: Optional sequence of node output mocks for testing scenarios
24
+ workflow_trigger: Optional Trigger instance for this scenario
25
+ mocks: Optional sequence of node output mocks for testing scenarios
23
26
  """
24
27
 
28
+ model_config = ConfigDict(populate_by_name=True, arbitrary_types_allowed=True)
29
+
25
30
  id: Optional[str] = None
26
31
  label: str
27
32
  inputs: Union[BaseInputs, Dict[str, Any]] = Field(default_factory=BaseInputs)
28
- workflow_trigger: Optional[Type[BaseTrigger]] = None
29
- node_output_mocks: Optional[Sequence[Union[BaseOutputs, MockNodeExecution]]] = None
33
+ workflow_trigger: Optional[BaseTrigger] = None
34
+ mocks: Optional[Sequence[Union[BaseOutputs, MockNodeExecution]]] = None
35
+ # DEPRECATED: node_output_mocks - use mocks instead. Remove in v2.0.0
36
+ node_output_mocks: Optional[Sequence[Union[BaseOutputs, MockNodeExecution]]] = Field(
37
+ default=None,
38
+ exclude=True, # Don't include in serialized output
39
+ )
40
+
41
+ @model_validator(mode="after")
42
+ def _handle_deprecated_node_output_mocks(self) -> "DatasetRow":
43
+ """Handle deprecated node_output_mocks field by mapping it to mocks."""
44
+ if self.node_output_mocks is not None:
45
+ warnings.warn(
46
+ "The 'node_output_mocks' parameter is deprecated and will be removed in v2.0.0. "
47
+ "Please use 'mocks' instead.",
48
+ DeprecationWarning,
49
+ stacklevel=2,
50
+ )
51
+ # If mocks is not set, use node_output_mocks value
52
+ if self.mocks is None:
53
+ object.__setattr__(self, "mocks", self.node_output_mocks)
54
+ return self
55
+
56
+ @model_serializer(mode="wrap")
57
+ def serialize_full_model(self, handler: Callable[[Any], Any], info: SerializationInfo) -> Dict[str, Any]:
58
+ """Serialize the model and add node_id field computed from then_outputs."""
59
+ serialized = handler(self)
60
+ if not isinstance(serialized, dict):
61
+ return serialized
62
+
63
+ # Add deprecation error if node_output_mocks was used - remove in v2.0.0
64
+ if self.node_output_mocks is not None:
65
+ add_error = info.context.get("add_error") if info.context else None
66
+ if add_error is not None:
67
+ add_error(
68
+ Exception(
69
+ f'Dataset row "{self.label}": "node_output_mocks" is deprecated. '
70
+ 'Please use "mocks" instead. This will be removed in v2.0.0.'
71
+ )
72
+ )
73
+
74
+ if "mocks" in serialized and serialized.get("mocks") is None:
75
+ serialized.pop("mocks")
76
+
77
+ if "workflow_trigger" in serialized:
78
+ value = serialized.pop("workflow_trigger")
79
+ if value is not None:
80
+ serialized["workflow_trigger_id"] = value
81
+
82
+ # Merge trigger attribute values into inputs if workflow_trigger is present
83
+ if self.workflow_trigger is not None:
84
+ trigger_attrs = self.workflow_trigger.to_trigger_attribute_values()
85
+ for ref, attr_value in trigger_attrs.items():
86
+ # Convert datetime objects to ISO format strings for JSON serialization
87
+ if isinstance(attr_value, datetime):
88
+ attr_value = attr_value.isoformat()
89
+ serialized["inputs"][ref.name] = attr_value
90
+
91
+ if "id" in serialized and serialized.get("id") is None:
92
+ serialized.pop("id")
93
+
94
+ return serialized
95
+
96
+ @field_serializer("workflow_trigger")
97
+ def serialize_workflow_trigger(self, workflow_trigger: Optional[BaseTrigger]) -> Optional[str]:
98
+ """
99
+ Custom serializer for workflow_trigger that converts it to a string ID.
100
+
101
+ Args:
102
+ workflow_trigger: Optional workflow trigger instance
103
+
104
+ Returns:
105
+ String representation of the trigger ID, or None if no trigger
106
+ """
107
+ if workflow_trigger is None:
108
+ return None
109
+
110
+ return str(workflow_trigger.__class__.__id__)
30
111
 
31
112
  @field_serializer("inputs")
32
- def serialize_inputs(self, inputs: Union[BaseInputs, Dict[str, Any]]) -> Dict[str, Any]:
113
+ def serialize_inputs(self, inputs: Union[BaseInputs, Dict[str, Any]], info: SerializationInfo) -> Dict[str, Any]:
33
114
  """
34
115
  Custom serializer for inputs that converts it to a dictionary.
35
116
 
117
+ When add_error is provided in the serialization context, this method will
118
+ validate file-type inputs (VellumFileMixin) by attempting to serialize them,
119
+ and collect errors for fields that fail to serialize while still including
120
+ valid fields.
121
+
36
122
  Args:
37
123
  inputs: Either a BaseInputs instance or dict to serialize
124
+ info: Serialization info containing context
38
125
 
39
126
  Returns:
40
127
  Dictionary representation of the inputs
@@ -43,31 +130,49 @@ class DatasetRow(UniversalBaseModel):
43
130
  return inputs
44
131
 
45
132
  result = {}
133
+ add_error = info.context.get("add_error") if info.context else None
134
+
46
135
  for input_descriptor, value in inputs:
47
- if not input_descriptor.name.startswith("__"):
136
+ if input_descriptor.name.startswith("__"):
137
+ continue
138
+
139
+ # For file types, validate by attempting to serialize and catch errors
140
+ if add_error is not None and isinstance(value, VellumFileMixin):
141
+ try:
142
+ # Trigger validation by serializing the file type
143
+ value.model_dump(mode="json", by_alias=True, exclude_none=True)
144
+ result[input_descriptor.name] = value
145
+ except Exception as field_error:
146
+ error_msg = (
147
+ f'Dataset row "{self.label}": input "{input_descriptor.name}" '
148
+ f"failed serialization: {field_error}"
149
+ )
150
+ add_error(Exception(error_msg))
151
+ else:
48
152
  result[input_descriptor.name] = value
49
153
 
50
154
  return result
51
155
 
52
- @field_serializer("node_output_mocks")
53
- def serialize_node_output_mocks(
54
- self, node_output_mocks: Optional[Sequence[Union[BaseOutputs, MockNodeExecution]]]
156
+ @field_serializer("mocks")
157
+ def serialize_mocks(
158
+ self, mocks: Optional[Sequence[Union[BaseOutputs, MockNodeExecution]]], info
55
159
  ) -> Optional[List[Dict[str, Any]]]:
56
160
  """
57
- Custom serializer for node_output_mocks that normalizes both BaseOutputs and MockNodeExecution
161
+ Custom serializer for mocks that normalizes both BaseOutputs and MockNodeExecution
58
162
  to a consistent dict format with node_id, when_condition, and then_outputs.
59
163
 
60
164
  Args:
61
- node_output_mocks: Optional sequence of BaseOutputs or MockNodeExecution instances
165
+ mocks: Optional sequence of BaseOutputs or MockNodeExecution instances
166
+ info: Serialization info containing context
62
167
 
63
168
  Returns:
64
169
  List of normalized mock execution dicts, or None if input is None
65
170
  """
66
- if node_output_mocks is None:
171
+ if mocks is None:
67
172
  return None
68
173
 
69
174
  result = []
70
- for mock in node_output_mocks:
175
+ for mock in mocks:
71
176
  if isinstance(mock, MockNodeExecution):
72
177
  mock_exec = mock
73
178
  else:
@@ -76,6 +181,6 @@ class DatasetRow(UniversalBaseModel):
76
181
  then_outputs=mock,
77
182
  )
78
183
 
79
- result.append(mock_exec.model_dump())
184
+ result.append(mock_exec.model_dump(context=info.context))
80
185
 
81
186
  return result
@@ -54,7 +54,7 @@ def test_base_inputs_explicit_none_should_raise_on_fields_without_defaults(field
54
54
  TestInputs() # type: ignore[call-arg]
55
55
 
56
56
  assert exc_info.value.code == WorkflowErrorCode.INVALID_INPUTS
57
- assert "Required input variables required_string should have defined value" == str(exc_info.value)
57
+ assert "Required input variables 'required_string' should have defined value" == str(exc_info.value)
58
58
 
59
59
 
60
60
  def test_base_inputs_explicit_none_should_raise_on_required_fields_with_none():
@@ -69,7 +69,7 @@ def test_base_inputs_explicit_none_should_raise_on_required_fields_with_none():
69
69
  TestInputs(required_string=None) # type: ignore[arg-type]
70
70
 
71
71
  assert exc_info.value.code == WorkflowErrorCode.INVALID_INPUTS
72
- assert "Required input variables required_string should have defined value" == str(exc_info.value)
72
+ assert "Required input variables 'required_string' should have defined value" == str(exc_info.value)
73
73
 
74
74
 
75
75
  def test_base_inputs_empty_value():
@@ -84,7 +84,7 @@ def test_base_inputs_empty_value():
84
84
 
85
85
  # THEN it should raise a NodeException with the correct error message and code
86
86
  assert exc_info.value.code == WorkflowErrorCode.INVALID_INPUTS
87
- assert "Required input variables required_string should have defined value" == str(exc_info.value)
87
+ assert "Required input variables 'required_string' should have defined value" == str(exc_info.value)
88
88
 
89
89
 
90
90
  def test_base_inputs_with_default():
@@ -33,6 +33,7 @@ def test_vellum_integration_service_get_tool_definition_success(vellum_client):
33
33
  "required": ["repo", "title"],
34
34
  },
35
35
  output_parameters={},
36
+ toolkit_version="1.0.0",
36
37
  )
37
38
  mock_client.integrations.retrieve_integration_tool_definition.return_value = tool_definition_response
38
39
 
@@ -61,6 +62,55 @@ def test_vellum_integration_service_get_tool_definition_success(vellum_client):
61
62
  integration_name="GITHUB",
62
63
  integration_provider="COMPOSIO",
63
64
  tool_name="GITHUB_CREATE_AN_ISSUE",
65
+ toolkit_version=None,
66
+ )
67
+
68
+
69
+ def test_vellum_integration_service_get_tool_definition_with_toolkit_version(vellum_client):
70
+ """Test that toolkit_version is passed through when retrieving tool definitions"""
71
+ # GIVEN a mock client configured to return a tool definition
72
+ mock_client = vellum_client
73
+ tool_definition_response = ComponentsSchemasComposioToolDefinition(
74
+ integration=Integration(
75
+ id=str(uuid4()),
76
+ provider="COMPOSIO",
77
+ name="GITHUB",
78
+ ),
79
+ label="GITHUB_CREATE_AN_ISSUE",
80
+ name="GITHUB_CREATE_AN_ISSUE",
81
+ description="Create a new issue in a GitHub repository",
82
+ input_parameters={
83
+ "type": "object",
84
+ "properties": {
85
+ "repo": {"type": "string", "description": "Repository name"},
86
+ },
87
+ "required": ["repo"],
88
+ },
89
+ output_parameters={},
90
+ toolkit_version="2.0.0",
91
+ )
92
+ mock_client.integrations.retrieve_integration_tool_definition.return_value = tool_definition_response
93
+
94
+ # WHEN we request a tool definition with a specific toolkit_version
95
+ service = VellumIntegrationService(client=mock_client)
96
+ result = service.get_tool_definition(
97
+ integration="GITHUB",
98
+ provider="COMPOSIO",
99
+ tool_name="GITHUB_CREATE_AN_ISSUE",
100
+ toolkit_version="2.0.0",
101
+ )
102
+
103
+ # THEN the tool definition should be returned with the toolkit_version preserved
104
+ assert isinstance(result, VellumIntegrationToolDetails)
105
+ assert result.name == "GITHUB_CREATE_AN_ISSUE"
106
+ assert result.toolkit_version == "2.0.0"
107
+
108
+ # AND the API should have been called with the toolkit_version parameter
109
+ mock_client.integrations.retrieve_integration_tool_definition.assert_called_once_with(
110
+ integration_name="GITHUB",
111
+ integration_provider="COMPOSIO",
112
+ tool_name="GITHUB_CREATE_AN_ISSUE",
113
+ toolkit_version="2.0.0",
64
114
  )
65
115
 
66
116
 
@@ -128,6 +178,40 @@ def test_vellum_integration_service_execute_tool_success(vellum_client):
128
178
  "title": "Test Issue",
129
179
  "body": "Test body",
130
180
  },
181
+ toolkit_version=None,
182
+ )
183
+
184
+
185
+ def test_vellum_integration_service_execute_tool_with_toolkit_version(vellum_client):
186
+ """Test that toolkit_version is passed through when executing tools"""
187
+ # GIVEN a mock client configured to return a successful response
188
+ mock_client = vellum_client
189
+ mock_client.integrations = mock.MagicMock()
190
+
191
+ mock_response = mock.MagicMock()
192
+ mock_response.data = {"success": True, "result": "executed with version"}
193
+ mock_client.integrations.execute_integration_tool.return_value = mock_response
194
+
195
+ # WHEN we execute a tool with a specific toolkit_version
196
+ service = VellumIntegrationService(client=mock_client)
197
+ result = service.execute_tool(
198
+ integration="GITHUB",
199
+ provider="COMPOSIO",
200
+ tool_name="GITHUB_CREATE_AN_ISSUE",
201
+ arguments={"repo": "user/repo"},
202
+ toolkit_version="2.0.0",
203
+ )
204
+
205
+ # THEN the execution result should contain expected data
206
+ assert result["success"] is True
207
+
208
+ # AND the API should have been called with the toolkit_version parameter
209
+ mock_client.integrations.execute_integration_tool.assert_called_once_with(
210
+ integration_name="GITHUB",
211
+ integration_provider="COMPOSIO",
212
+ tool_name="GITHUB_CREATE_AN_ISSUE",
213
+ arguments={"repo": "user/repo"},
214
+ toolkit_version="2.0.0",
131
215
  )
132
216
 
133
217
 
@@ -1,4 +1,4 @@
1
- from typing import Any, Dict
1
+ from typing import Any, Dict, Optional
2
2
 
3
3
  from vellum.client.core.api_error import ApiError
4
4
  from vellum.workflows.constants import VellumIntegrationProviderType
@@ -25,6 +25,7 @@ class VellumIntegrationService:
25
25
  integration: str,
26
26
  provider: str,
27
27
  tool_name: str,
28
+ toolkit_version: Optional[str] = None,
28
29
  ) -> VellumIntegrationToolDetails:
29
30
  """Retrieve a tool definition from Vellum integrations.
30
31
 
@@ -32,6 +33,9 @@ class VellumIntegrationService:
32
33
  integration: The integration name (e.g., "GITHUB", "SLACK")
33
34
  provider: The integration provider name (e.g., "COMPOSIO")
34
35
  tool_name: The tool's unique name as specified by the provider
36
+ toolkit_version: The version of the toolkit to use. Pass 'latest' to get the
37
+ latest version, or a specific version string to pin it. If not provided,
38
+ uses the provider's default.
35
39
 
36
40
  Returns:
37
41
  VellumIntegrationToolDetails containing the tool definition with parameters
@@ -44,6 +48,7 @@ class VellumIntegrationService:
44
48
  integration_name=integration,
45
49
  integration_provider=provider,
46
50
  tool_name=tool_name,
51
+ toolkit_version=toolkit_version,
47
52
  )
48
53
 
49
54
  return VellumIntegrationToolDetails(
@@ -52,6 +57,7 @@ class VellumIntegrationService:
52
57
  name=response.name,
53
58
  description=response.description,
54
59
  parameters=response.input_parameters,
60
+ toolkit_version=response.toolkit_version,
55
61
  )
56
62
  except Exception as e:
57
63
  error_message = f"Failed to retrieve tool definition for {tool_name}: {str(e)}"
@@ -66,6 +72,7 @@ class VellumIntegrationService:
66
72
  provider: str,
67
73
  tool_name: str,
68
74
  arguments: Dict[str, Any],
75
+ toolkit_version: Optional[str] = None,
69
76
  ) -> Dict[str, Any]:
70
77
  """Execute a tool through Vellum integrations.
71
78
 
@@ -74,6 +81,9 @@ class VellumIntegrationService:
74
81
  provider: The integration provider name (e.g., "COMPOSIO")
75
82
  tool_name: The tool's unique name as specified by the provider
76
83
  arguments: Arguments to pass to the tool
84
+ toolkit_version: The version of the toolkit to use. Pass 'latest' to get the
85
+ latest version, or a specific version string to pin it. If not provided,
86
+ uses the provider's default.
77
87
 
78
88
  Returns:
79
89
  Dict containing the execution result data
@@ -88,6 +98,7 @@ class VellumIntegrationService:
88
98
  integration_provider=provider,
89
99
  tool_name=tool_name,
90
100
  arguments=arguments,
101
+ toolkit_version=toolkit_version,
91
102
  )
92
103
 
93
104
  # Return the data from the response
@@ -9,6 +9,8 @@ class BaseWorkflowFinder(importlib.abc.MetaPathFinder, ABC):
9
9
  Abstract base class for workflow finders that support custom error message formatting.
10
10
  """
11
11
 
12
+ namespace: str
13
+
12
14
  @abstractmethod
13
15
  def format_error_message(self, error_message: str) -> str:
14
16
  """
@@ -1,4 +1,4 @@
1
- from abc import ABC, ABCMeta, abstractmethod
1
+ from abc import ABC, ABCMeta
2
2
  from collections.abc import Callable as CollectionsCallable
3
3
  from dataclasses import field
4
4
  from functools import cached_property, reduce
@@ -46,7 +46,7 @@ from vellum.workflows.state.context import WorkflowContext
46
46
  from vellum.workflows.types.core import MergeBehavior
47
47
  from vellum.workflows.types.generics import StateType
48
48
  from vellum.workflows.types.utils import get_class_attr_names, get_original_base, infer_types
49
- from vellum.workflows.utils.uuids import uuid4_from_hash
49
+ from vellum.workflows.utils.uuids import generate_entity_id_from_path, uuid4_from_hash
50
50
 
51
51
 
52
52
  def _is_nested_class(nested: Any, parent: Type) -> bool:
@@ -115,7 +115,21 @@ class BaseNodeMeta(ABCMeta):
115
115
  dct["Ports"] = type(f"{name}.Ports", (base.Ports,), ports_dct)
116
116
  break
117
117
 
118
- if "Display" not in dct:
118
+ if "Display" in dct:
119
+ display_class = dct["Display"]
120
+ parent_display_class = next(
121
+ (base.Display for base in bases if hasattr(base, "Display")),
122
+ None,
123
+ )
124
+ # Ensure user-defined Display class inherits from parent's Display
125
+ if parent_display_class and not issubclass(display_class, parent_display_class):
126
+ filtered_bases = tuple(base for base in display_class.__bases__ if base is not object)
127
+ dct["Display"] = type(
128
+ f"{name}.Display",
129
+ (parent_display_class,) + filtered_bases,
130
+ {**display_class.__dict__, "__module__": dct["__module__"]},
131
+ )
132
+ else:
119
133
  for base in reversed(bases):
120
134
  if issubclass(base, BaseNode):
121
135
  dct["Display"] = type(
@@ -159,7 +173,8 @@ class BaseNodeMeta(ABCMeta):
159
173
  node_class.ExternalInputs.__parent_class__ = node_class
160
174
 
161
175
  # Use new ID generation (module + qualname)
162
- node_class.__id__ = uuid4_from_hash(f"{node_class.__module__}.{node_class.__qualname__}")
176
+ # generate_entity_id_from_path normalizes the module path to filter out UUID namespace for stable ID generation
177
+ node_class.__id__ = generate_entity_id_from_path(f"{node_class.__module__}.{node_class.__qualname__}")
163
178
 
164
179
  node_class.__output_ids__ = {
165
180
  ref.name: uuid4_from_hash(f"{node_class.__id__}|{ref.name}")
@@ -247,17 +262,6 @@ class BaseNodeMeta(ABCMeta):
247
262
  yield attr_value
248
263
  yielded_attr_names.add(attr_name)
249
264
 
250
- @abstractmethod
251
- def __validate__(cls) -> None:
252
- """
253
- Validates the node.
254
- Subclasses can override this method to implement their specific validation logic.
255
- Called during serialization or explicit validation.
256
-
257
- Default implementation performs no validation.
258
- """
259
- pass
260
-
261
265
 
262
266
  class _BaseNodeTriggerMeta(type):
263
267
  def __eq__(self, other: Any) -> bool:
@@ -325,6 +329,9 @@ class BaseNode(Generic[StateType], ABC, BaseExecutable, metaclass=BaseNodeMeta):
325
329
 
326
330
  icon: Optional[str] = None
327
331
  color: Optional[str] = None
332
+ x: Optional[float] = None
333
+ y: Optional[float] = None
334
+ z_index: Optional[int] = None
328
335
 
329
336
  class Trigger(metaclass=_BaseNodeTriggerMeta):
330
337
  node_class: Type["BaseNode"]
@@ -383,8 +390,11 @@ class BaseNode(Generic[StateType], ABC, BaseExecutable, metaclass=BaseNodeMeta):
383
390
  all_deps_invoked = all(dep in node_classes_invoked for dep in dependencies)
384
391
  return all_deps_invoked
385
392
 
393
+ if cls.merge_behavior == MergeBehavior.CUSTOM:
394
+ return False
395
+
386
396
  raise NodeException(
387
- message="Invalid Trigger Node Specification",
397
+ message=f"Invalid Trigger Node Specification: {cls.merge_behavior}",
388
398
  code=WorkflowErrorCode.INVALID_INPUTS,
389
399
  )
390
400
 
@@ -571,3 +581,14 @@ class BaseNode(Generic[StateType], ABC, BaseExecutable, metaclass=BaseNodeMeta):
571
581
  """
572
582
 
573
583
  return False
584
+
585
+ @classmethod
586
+ def __validate__(cls) -> None:
587
+ """
588
+ Validates the node.
589
+ Subclasses can override this method to implement their specific validation logic.
590
+ Called during serialization or explicit validation.
591
+
592
+ Default implementation performs no validation.
593
+ """
594
+ pass
@@ -1,11 +1,13 @@
1
1
  import pytest
2
2
  from uuid import UUID
3
- from typing import Optional, Set
3
+ from typing import Any, Dict, Optional, Set
4
4
 
5
5
  from vellum.client.core.pydantic_utilities import UniversalBaseModel
6
6
  from vellum.client.types.string_vellum_value_request import StringVellumValueRequest
7
+ from vellum.workflows import BaseWorkflow
7
8
  from vellum.workflows.constants import undefined
8
9
  from vellum.workflows.descriptors.tests.test_utils import FixtureState
10
+ from vellum.workflows.errors.types import WorkflowErrorCode
9
11
  from vellum.workflows.inputs.base import BaseInputs
10
12
  from vellum.workflows.nodes import FinalOutputNode
11
13
  from vellum.workflows.nodes.bases.base import BaseNode
@@ -15,6 +17,7 @@ from vellum.workflows.references.constant import ConstantValueReference
15
17
  from vellum.workflows.references.node import NodeReference
16
18
  from vellum.workflows.references.output import OutputReference
17
19
  from vellum.workflows.state.base import BaseState, StateMeta
20
+ from vellum.workflows.workflows.event_filters import all_workflow_event_filter
18
21
 
19
22
 
20
23
  def test_base_node__node_resolution__unset_pydantic_fields():
@@ -379,3 +382,103 @@ def test_base_node__ports_inheritance__cumulative_ports():
379
382
  # Potentially in the future, we support inheriting ports from multiple parents.
380
383
  # For now, we take only the declared ports, so that not all nodes have the default port.
381
384
  assert ports == ["bar"]
385
+
386
+
387
+ def test_base_node__trigger_should_initiate__invalid_merge_behavior():
388
+ """
389
+ Tests that an invalid merge behavior raises a NodeException with the invalid value in the error message.
390
+ """
391
+
392
+ # GIVEN a node with an invalid merge behavior
393
+ class InvalidMergeBehaviorNode(BaseNode):
394
+ class Trigger(BaseNode.Trigger):
395
+ merge_behavior = "INVALID_MERGE_BEHAVIOR" # type: ignore[assignment]
396
+
397
+ # AND a workflow that uses the node
398
+ class InvalidMergeBehaviorWorkflow(BaseWorkflow):
399
+ graph = InvalidMergeBehaviorNode
400
+
401
+ # WHEN we stream the workflow
402
+ workflow = InvalidMergeBehaviorWorkflow()
403
+ events = list(workflow.stream(event_filter=all_workflow_event_filter))
404
+
405
+ # THEN the workflow should reject
406
+ workflow_rejected_event = events[-1]
407
+ assert workflow_rejected_event.name == "workflow.execution.rejected"
408
+
409
+ # AND the error should have the correct code
410
+ assert workflow_rejected_event.error.code == WorkflowErrorCode.INVALID_INPUTS
411
+
412
+ # AND the error message should contain the invalid merge behavior
413
+ assert "INVALID_MERGE_BEHAVIOR" in workflow_rejected_event.error.message
414
+
415
+
416
+ def test_base_node__int_input_preserves_type_when_float_passed():
417
+ """
418
+ Tests that an int workflow input is correctly coerced to int when a float value is passed.
419
+ """
420
+
421
+ # GIVEN an Inputs class with an int field
422
+ class Inputs(BaseInputs):
423
+ total_requests: int
424
+
425
+ # AND a custom node that references the int input
426
+ class CustomNode(BaseNode):
427
+ total_requests = Inputs.total_requests
428
+
429
+ class Outputs(BaseOutputs):
430
+ result: int
431
+
432
+ def run(self) -> BaseOutputs:
433
+ # This should work without error - range() requires an int, not a float
434
+ result = len(range(self.total_requests))
435
+ return self.Outputs(result=result)
436
+
437
+ # AND a workflow that uses the custom node
438
+ class IntInputWorkflow(BaseWorkflow[Inputs, BaseState]):
439
+ graph = CustomNode
440
+
441
+ class Outputs(BaseWorkflow.Outputs):
442
+ result = CustomNode.Outputs.result
443
+
444
+ # WHEN we run the workflow with a float value for an int input
445
+ # This simulates what happens when the API returns a NUMBER value as a float
446
+ workflow = IntInputWorkflow()
447
+ raw_inputs: Dict[str, Any] = {"total_requests": 5.0}
448
+ inputs = Inputs(**raw_inputs)
449
+
450
+ # THEN the input should be coerced to an integer at construction time
451
+ assert inputs.total_requests == 5
452
+ assert type(inputs.total_requests) is int
453
+
454
+ # AND the workflow should complete successfully
455
+ final_event = workflow.run(inputs=inputs)
456
+ assert final_event.name == "workflow.execution.fulfilled"
457
+
458
+ # AND the result should be correct
459
+ assert final_event.outputs.result == 5
460
+
461
+
462
+ def test_base_node__bytes_output_raises_serialization_error():
463
+ """Test that returning bytes in node outputs rejects the workflow execution."""
464
+
465
+ class BytesOutputNode(BaseNode):
466
+ class Outputs(BaseNode.Outputs):
467
+ result: str
468
+
469
+ def run(self) -> "BytesOutputNode.Outputs":
470
+ b = b"hello"
471
+ return self.Outputs(result=b) # type: ignore[arg-type]
472
+
473
+ class BytesWorkflow(BaseWorkflow):
474
+ graph = BytesOutputNode
475
+
476
+ workflow = BytesWorkflow()
477
+
478
+ # WHEN we run the workflow
479
+ result = workflow.run()
480
+
481
+ # THEN the execution is rejected with a helpful error
482
+ assert result.name == "workflow.execution.rejected"
483
+ assert result.error.code == WorkflowErrorCode.INVALID_OUTPUTS
484
+ assert "bytes" in result.error.message.lower()
@@ -84,6 +84,7 @@ class InlineSubworkflowNode(
84
84
  event_filter=all_workflow_event_filter,
85
85
  node_output_mocks=self._context._get_all_node_output_mocks(),
86
86
  cancel_signal=self._child_cancel_signal,
87
+ event_max_size=self._context.event_max_size,
87
88
  )
88
89
 
89
90
  outputs: Optional[BaseOutputs] = None
@@ -116,7 +116,7 @@ def test_inline_subworkflow_node__base_inputs_validation():
116
116
 
117
117
  # AND the error message should indicate the missing required input
118
118
  assert e.value.code == WorkflowErrorCode.INVALID_INPUTS
119
- assert "Required input variables required_input should have defined value" == str(e.value)
119
+ assert "Required input variables 'required_input' should have defined value" == str(e.value)
120
120
 
121
121
 
122
122
  def test_inline_subworkflow_node__with_adornment():