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,14 +1,15 @@
1
- from typing import Any, Dict, Generic, Tuple, Type, TypeVar, get_args, get_origin
1
+ from typing import Any, Dict, Generic, Tuple, Type, TypeVar, get_args
2
2
 
3
3
  from vellum.workflows.constants import undefined
4
+ from vellum.workflows.descriptors.base import BaseDescriptor
4
5
  from vellum.workflows.nodes.bases import BaseNode
5
6
  from vellum.workflows.nodes.bases.base import BaseNodeMeta
6
7
  from vellum.workflows.nodes.utils import cast_to_output_type
7
8
  from vellum.workflows.ports import NodePorts
8
- from vellum.workflows.references.output import OutputReference
9
9
  from vellum.workflows.types import MergeBehavior
10
10
  from vellum.workflows.types.generics import StateType
11
11
  from vellum.workflows.types.utils import get_original_base
12
+ from vellum.workflows.utils.validate import validate_target_type
12
13
 
13
14
  _OutputType = TypeVar("_OutputType")
14
15
 
@@ -40,73 +41,6 @@ class _FinalOutputNodeMeta(BaseNodeMeta):
40
41
  else:
41
42
  return all_args[1]
42
43
 
43
- def __validate__(cls) -> None:
44
- cls._validate_output_type_consistency(cls)
45
-
46
- @classmethod
47
- def _validate_output_type_consistency(mcs, cls: Type) -> None:
48
- """
49
- Validates that the declared output type of FinalOutputNode matches
50
- the type of the descriptor assigned to the 'value' attribute in its Outputs class.
51
-
52
- Raises ValueError if there's a type mismatch.
53
- """
54
- if not hasattr(cls, "Outputs"):
55
- return
56
-
57
- outputs_class = cls.Outputs
58
- if not hasattr(outputs_class, "value"):
59
- return
60
-
61
- declared_output_type = cls.get_output_type()
62
-
63
- if declared_output_type is Any:
64
- return
65
-
66
- value_descriptor = None
67
-
68
- if "value" in outputs_class.__dict__:
69
- value_descriptor = outputs_class.__dict__["value"]
70
- else:
71
- value_descriptor = getattr(outputs_class, "value")
72
-
73
- if isinstance(value_descriptor, OutputReference):
74
- descriptor_types = value_descriptor.types
75
-
76
- type_mismatch = True
77
- for descriptor_type in descriptor_types:
78
- if descriptor_type == declared_output_type:
79
- type_mismatch = False
80
- break
81
- if (
82
- get_origin(descriptor_type) == declared_output_type
83
- or get_origin(declared_output_type) == descriptor_type
84
- ):
85
- type_mismatch = False
86
- break
87
- try:
88
- if issubclass(descriptor_type, declared_output_type) or issubclass(
89
- declared_output_type, descriptor_type
90
- ):
91
- type_mismatch = False
92
- break
93
- except TypeError:
94
- # Handle cases where types aren't classes (e.g., Union)
95
- if str(descriptor_type) == str(declared_output_type):
96
- type_mismatch = False
97
- break
98
-
99
- if type_mismatch:
100
- declared_type_name = getattr(declared_output_type, "__name__", str(declared_output_type))
101
- descriptor_type_names = [getattr(t, "__name__", str(t)) for t in descriptor_types]
102
-
103
- raise ValueError(
104
- f"Output type mismatch in {cls.__name__}: "
105
- f"FinalOutputNode is declared with output type '{declared_type_name}' "
106
- f"but the 'value' descriptor has type(s) {descriptor_type_names}. "
107
- f"The output descriptor type must match the declared FinalOutputNode output type."
108
- )
109
-
110
44
 
111
45
  class FinalOutputNode(BaseNode[StateType], Generic[StateType, _OutputType], metaclass=_FinalOutputNodeMeta):
112
46
  """
@@ -140,3 +74,24 @@ class FinalOutputNode(BaseNode[StateType], Generic[StateType, _OutputType], meta
140
74
  )
141
75
 
142
76
  __simulates_workflow_output__ = True
77
+
78
+ @classmethod
79
+ def __validate__(cls) -> None:
80
+ cls._validate_output_type_consistency()
81
+
82
+ @classmethod
83
+ def _validate_output_type_consistency(cls) -> None:
84
+ """
85
+ Validates that the declared output type of FinalOutputNode matches
86
+ the type of the descriptor assigned to the 'value' attribute in its Outputs class.
87
+
88
+ Raises ValueError if there's a type mismatch.
89
+ """
90
+ declared_output_type = cls.get_output_type()
91
+ value_descriptor = cls.Outputs.value.instance
92
+
93
+ if isinstance(value_descriptor, BaseDescriptor):
94
+ try:
95
+ validate_target_type(declared_output_type, value_descriptor.normalized_type)
96
+ except ValueError as e:
97
+ raise ValueError(f"Failed to validate output type for node '{cls.__name__}': {e}")
@@ -2,12 +2,14 @@ import pytest
2
2
  from typing import Any, Dict
3
3
 
4
4
  from vellum.workflows.exceptions import NodeException
5
+ from vellum.workflows.nodes.core.map_node import MapNode
5
6
  from vellum.workflows.nodes.core.templating_node import TemplatingNode
6
7
  from vellum.workflows.nodes.displayable.final_output_node import FinalOutputNode
7
8
  from vellum.workflows.nodes.displayable.inline_prompt_node import InlinePromptNode
8
9
  from vellum.workflows.references.output import OutputReference
9
10
  from vellum.workflows.state.base import BaseState
10
11
  from vellum.workflows.types.core import Json
12
+ from vellum.workflows.workflows.base import BaseWorkflow
11
13
 
12
14
 
13
15
  def test_final_output_node__mismatched_output_type_should_raise_exception_when_ran():
@@ -41,9 +43,31 @@ def test_final_output_node__mismatched_output_type_should_raise_exception():
41
43
  # AND the error message should indicate the type mismatch
42
44
  assert (
43
45
  str(exc_info.value)
44
- == "Output type mismatch in Output: FinalOutputNode is declared with output type 'list' but "
45
- "the 'value' descriptor has type(s) ['str']. The output descriptor type must match the "
46
- "declared FinalOutputNode output type."
46
+ == "Failed to validate output type for node 'Output': Output type mismatch: declared type 'list' but "
47
+ "the 'value' Output has type(s) 'str'. "
48
+ )
49
+
50
+
51
+ def test_final_output_node__mismatched_output_type_in_state_should_raise_exception():
52
+ # GIVEN a state with a str type
53
+ class State(BaseState):
54
+ foo: str
55
+
56
+ # AND a FinalOutputNode declared with list output type
57
+ class Output(FinalOutputNode[BaseState, list]):
58
+ class Outputs(FinalOutputNode.Outputs):
59
+ value = State.foo
60
+
61
+ # WHEN attempting to validate the node class
62
+ # THEN a ValueError should be raised during validation
63
+ with pytest.raises(ValueError) as exc_info:
64
+ Output.__validate__()
65
+
66
+ # AND the error message should indicate the type mismatch
67
+ assert (
68
+ str(exc_info.value)
69
+ == "Failed to validate output type for node 'Output': Output type mismatch: declared type 'list' but "
70
+ "the 'value' Output has type(s) 'str'. "
47
71
  )
48
72
 
49
73
 
@@ -113,3 +137,29 @@ def test_final_output_node__any_output_type_should_accept_json():
113
137
  AnyOutputNode.__validate__()
114
138
  except ValueError as e:
115
139
  pytest.fail(f"Validation should not raise an exception when Any accepts Json: {e}")
140
+
141
+
142
+ def test_final_output_node__list_str_output_type_should_pass_validation():
143
+ """
144
+ Tests that FinalOutputNode with list[str] output type accepts a descriptor with List[str] type.
145
+ """
146
+
147
+ # GIVEN value descriptor has List[str] type
148
+ class MySubworkflow(BaseWorkflow):
149
+ class Outputs(BaseWorkflow.Outputs):
150
+ result: str
151
+
152
+ class MyMap(MapNode):
153
+ subworkflow = MySubworkflow
154
+
155
+ # AND a FinalOutputNode declared with list[str] output type
156
+ class ListStrOutputNode(FinalOutputNode[BaseState, list[str]]):
157
+ class Outputs(FinalOutputNode.Outputs):
158
+ value = MyMap.Outputs.result
159
+
160
+ # WHEN attempting to validate the node class
161
+ try:
162
+ ListStrOutputNode.__validate__()
163
+ except Exception as e:
164
+ # THEN validation should pass without raising an exception
165
+ pytest.fail(f"Validation should not raise an exception for list[str]/List[str] compatibility: {e}")
@@ -1,4 +1,4 @@
1
- from typing import Generic
1
+ from typing import Any, Dict, Generic, Optional
2
2
 
3
3
  from vellum.workflows.nodes.bases import BaseNode
4
4
  from vellum.workflows.types import MergeBehavior
@@ -10,6 +10,9 @@ class NoteNode(BaseNode[StateType], Generic[StateType]):
10
10
  A no-op Node purely used to display a note in the Vellum UI.
11
11
  """
12
12
 
13
+ text: str = ""
14
+ style: Optional[Dict[str, Any]] = None
15
+
13
16
  class Display(BaseNode.Display):
14
17
  icon = "vellum:icon:note"
15
18
  color = "cyan"
@@ -21,6 +21,7 @@ from vellum.workflows.constants import LATEST_RELEASE_TAG, OMIT, undefined
21
21
  from vellum.workflows.context import execution_context, get_execution_context, get_parent_context
22
22
  from vellum.workflows.errors import WorkflowErrorCode
23
23
  from vellum.workflows.errors.types import workflow_event_error_to_workflow_error
24
+ from vellum.workflows.events.exception_handling import stream_initialization_exception
24
25
  from vellum.workflows.events.types import WorkflowDeploymentParentContext, default_serializer
25
26
  from vellum.workflows.events.workflow import is_workflow_event
26
27
  from vellum.workflows.exceptions import NodeException, WorkflowInitializationException
@@ -191,16 +192,26 @@ class SubworkflowDeploymentNode(BaseNode[StateType], Generic[StateType]):
191
192
  resolved_workflow = workflow_class(
192
193
  context=WorkflowContext.create_from(self._context), parent_state=self.state
193
194
  )
194
- subworkflow_stream = resolved_workflow.stream(
195
- inputs=self._compile_subworkflow_inputs_for_direct_invocation(resolved_workflow),
196
- event_filter=all_workflow_event_filter,
197
- node_output_mocks=self._context._get_all_node_output_mocks(),
198
- )
199
195
 
200
196
  try:
197
+ # The stream creation and first event retrieval are wrapped in try/except because
198
+ # WorkflowInitializationException can be raised during stream creation (e.g., when
199
+ # inputs are invalid) or when getting the first event
200
+ subworkflow_inputs = self._compile_subworkflow_inputs_for_direct_invocation(resolved_workflow)
201
+ subworkflow_stream = resolved_workflow.stream(
202
+ inputs=subworkflow_inputs,
203
+ event_filter=all_workflow_event_filter,
204
+ node_output_mocks=self._context._get_all_node_output_mocks(),
205
+ event_max_size=self._context.event_max_size,
206
+ )
201
207
  first_event = next(subworkflow_stream)
202
208
  self._context._emit_subworkflow_event(first_event)
203
209
  except WorkflowInitializationException as e:
210
+ # Emit initiated and rejected events for the subworkflow so that
211
+ # the parent workflow can see the subworkflow's lifecycle events
212
+ for init_failure_event in stream_initialization_exception(e):
213
+ self._context._emit_subworkflow_event(init_failure_event)
214
+
204
215
  hashed_module = e.definition.__module__
205
216
  raise NodeException(
206
217
  message=e.message,
@@ -1,3 +1,4 @@
1
+ import pytest
1
2
  from uuid import uuid4
2
3
  from typing import Any, Iterator, List
3
4
 
@@ -6,8 +7,12 @@ from vellum import (
6
7
  FulfilledExecutePromptEvent,
7
8
  InitiatedExecutePromptEvent,
8
9
  PromptOutput,
10
+ RejectedExecutePromptEvent,
9
11
  StringVellumValue,
12
+ VellumError,
10
13
  )
14
+ from vellum.workflows.errors import WorkflowErrorCode
15
+ from vellum.workflows.exceptions import NodeException
11
16
  from vellum.workflows.inputs import BaseInputs
12
17
  from vellum.workflows.nodes import PromptDeploymentNode
13
18
  from vellum.workflows.state import BaseState
@@ -73,3 +78,45 @@ def test_text_prompt_deployment_node__basic(vellum_client):
73
78
  expand_meta = call_kwargs.get("expand_meta")
74
79
  assert expand_meta is not None
75
80
  assert expand_meta.finish_reason is True
81
+
82
+
83
+ def test_prompt_deployment_node__provider_error_from_first_event(vellum_client):
84
+ """
85
+ Tests that PromptDeploymentNode raises NodeException with the actual provider error message
86
+ when the first event is REJECTED, rather than a generic "Expected to receive outputs" error.
87
+ """
88
+
89
+ # GIVEN a PromptDeploymentNode with basic configuration
90
+ class MyPromptDeploymentNode(PromptDeploymentNode):
91
+ deployment = "my-deployment"
92
+
93
+ # AND the API returns a REJECTED event as the first event with a provider quota error
94
+ provider_error = VellumError(
95
+ code="PROVIDER_QUOTA_EXCEEDED",
96
+ message="Google: You exceeded your current quota, please check your plan and billing details.",
97
+ )
98
+
99
+ def generate_prompt_events(*args: Any, **kwargs: Any) -> Iterator[ExecutePromptEvent]:
100
+ execution_id = str(uuid4())
101
+ events: List[ExecutePromptEvent] = [
102
+ RejectedExecutePromptEvent(
103
+ execution_id=execution_id,
104
+ error=provider_error,
105
+ ),
106
+ ]
107
+ yield from events
108
+
109
+ vellum_client.execute_prompt_stream.side_effect = generate_prompt_events
110
+
111
+ # WHEN the node is run
112
+ node = MyPromptDeploymentNode()
113
+
114
+ # THEN it should raise a NodeException with the actual provider error
115
+ with pytest.raises(NodeException) as excinfo:
116
+ list(node.run())
117
+
118
+ # AND the exception should have the correct error code
119
+ assert excinfo.value.code == WorkflowErrorCode.PROVIDER_QUOTA_EXCEEDED
120
+
121
+ # AND the exception message should contain the actual provider error message
122
+ assert "Google: You exceeded your current quota" in excinfo.value.message
@@ -1,14 +1,18 @@
1
- from typing import Any, ClassVar, Dict, Generic, Iterator, List, Optional, Set, Union
1
+ from typing import Any, ClassVar, Dict, Generic, Iterator, List, Optional, Set, Type, Union, cast
2
2
 
3
3
  from vellum import ChatMessage, PromptBlock, PromptOutput
4
4
  from vellum.client.types.prompt_parameters import PromptParameters
5
5
  from vellum.client.types.prompt_settings import PromptSettings
6
6
  from vellum.client.types.string_chat_message_content import StringChatMessageContent
7
7
  from vellum.prompts.constants import DEFAULT_PROMPT_PARAMETERS
8
+ from vellum.workflows.constants import undefined
8
9
  from vellum.workflows.context import execution_context, get_parent_context
10
+ from vellum.workflows.descriptors.base import BaseDescriptor
9
11
  from vellum.workflows.errors.types import WorkflowErrorCode
12
+ from vellum.workflows.events.node import NodeExecutionStreamingEvent
10
13
  from vellum.workflows.events.workflow import is_workflow_event
11
14
  from vellum.workflows.exceptions import NodeException
15
+ from vellum.workflows.expressions.coalesce_expression import CoalesceExpression
12
16
  from vellum.workflows.graph.graph import Graph
13
17
  from vellum.workflows.inputs.base import BaseInputs
14
18
  from vellum.workflows.nodes.bases import BaseNode
@@ -16,20 +20,30 @@ from vellum.workflows.nodes.displayable.tool_calling_node.state import ToolCalli
16
20
  from vellum.workflows.nodes.displayable.tool_calling_node.utils import (
17
21
  create_else_node,
18
22
  create_function_node,
19
- create_mcp_tool_node,
20
23
  create_router_node,
21
24
  create_tool_prompt_node,
22
25
  get_function_name,
23
26
  )
24
27
  from vellum.workflows.outputs.base import BaseOutput, BaseOutputs
28
+ from vellum.workflows.references.output import OutputReference
25
29
  from vellum.workflows.state.context import WorkflowContext
26
30
  from vellum.workflows.types.core import EntityInputsInterface
27
- from vellum.workflows.types.definition import MCPServer, Tool
31
+ from vellum.workflows.types.definition import MCPServer, MCPToolDefinition, Tool, ToolBase
28
32
  from vellum.workflows.types.generics import StateType
29
- from vellum.workflows.utils.functions import compile_mcp_tool_definition, get_mcp_tool_name
33
+ from vellum.workflows.utils.functions import compile_mcp_tool_definition
30
34
  from vellum.workflows.workflows.event_filters import all_workflow_event_filter
31
35
 
32
36
 
37
+ def _contains_reference_to_output(reference: BaseDescriptor, target_reference: OutputReference) -> bool:
38
+ if reference == target_reference:
39
+ return True
40
+ if isinstance(reference, CoalesceExpression):
41
+ return _contains_reference_to_output(reference.lhs, target_reference) or _contains_reference_to_output(
42
+ reference.rhs, target_reference
43
+ )
44
+ return False
45
+
46
+
33
47
  class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
34
48
  """
35
49
  A Node that dynamically invokes the provided functions to the underlying Prompt
@@ -60,10 +74,12 @@ class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
60
74
  The outputs of the ToolCallingNode.
61
75
 
62
76
  text: The final text response after tool calling
77
+ json: The result of the Prompt Execution in JSON format (if parseable)
63
78
  chat_history: The complete chat history including tool calls
64
79
  """
65
80
 
66
81
  text: str
82
+ json: Union[Dict[Any, Any], Type[undefined]] = undefined
67
83
  chat_history: List[ChatMessage]
68
84
 
69
85
  def run(self) -> Iterator[BaseOutput]:
@@ -86,6 +102,7 @@ class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
86
102
 
87
103
  class Outputs(BaseWorkflow.Outputs):
88
104
  text: str = self.tool_prompt_node.Outputs.text
105
+ json: Any = self.tool_prompt_node.Outputs.json
89
106
  chat_history: List[ChatMessage] = ToolCallingState.chat_history
90
107
  results: List[PromptOutput] = self.tool_prompt_node.Outputs.results
91
108
 
@@ -97,6 +114,7 @@ class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
97
114
  subworkflow_stream = subworkflow.stream(
98
115
  event_filter=all_workflow_event_filter,
99
116
  node_output_mocks=self._context._get_all_node_output_mocks(),
117
+ event_max_size=self._context.event_max_size,
100
118
  )
101
119
 
102
120
  outputs: Optional[BaseOutputs] = None
@@ -139,17 +157,6 @@ class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
139
157
  exception = NodeException.of(event.error)
140
158
 
141
159
  if exception:
142
- # In the case of the Prompt Node receiving invalid inputs or schemas, that is a bug with the
143
- # internals of the Agent Node. In the future, we should distinguish further by also checking
144
- # that this exception came from the Tool Calling Node's prompt node instead of anything
145
- # internal to a user defined tool.
146
- if exception.code == WorkflowErrorCode.INVALID_INPUTS:
147
- raise NodeException(
148
- message="Internal server error",
149
- code=WorkflowErrorCode.INTERNAL_ERROR,
150
- raw_data=exception.raw_data,
151
- stacktrace=exception.stacktrace,
152
- ) from exception
153
160
  raise exception
154
161
 
155
162
  if outputs is None:
@@ -170,10 +177,20 @@ class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
170
177
  process_parameters_method = getattr(self.__class__, "process_parameters", None)
171
178
  process_blocks_method = getattr(self.__class__, "process_blocks", None)
172
179
 
180
+ # Hydrate MCP servers upfront and replace them with tool definitions
181
+ hydrated_functions: List[Union[ToolBase, MCPToolDefinition]] = []
182
+ for function in self.functions:
183
+ if isinstance(function, MCPServer):
184
+ hydrated_functions.extend(compile_mcp_tool_definition(function))
185
+ else:
186
+ # After checking, function is ToolBase (not MCPServer)
187
+ # Mypy doesn't narrow Union[ToolBase, MCPServer] to ToolBase, so we cast
188
+ hydrated_functions.append(cast(ToolBase, function))
189
+
173
190
  self.tool_prompt_node = create_tool_prompt_node(
174
191
  ml_model=self.ml_model,
175
192
  blocks=self.blocks,
176
- functions=self.functions,
193
+ functions=hydrated_functions,
177
194
  prompt_inputs=self.prompt_inputs,
178
195
  parameters=self.parameters,
179
196
  max_prompt_iterations=self.max_prompt_iterations,
@@ -184,28 +201,17 @@ class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
184
201
 
185
202
  # Create the router node (handles routing logic only)
186
203
  self.router_node = create_router_node(
187
- functions=self.functions,
204
+ functions=hydrated_functions,
188
205
  tool_prompt_node=self.tool_prompt_node,
189
206
  )
190
207
 
191
208
  self._function_nodes = {}
192
- for function in self.functions:
193
- if isinstance(function, MCPServer):
194
- tool_definitions = compile_mcp_tool_definition(function)
195
- for tool_definition in tool_definitions:
196
- function_name = get_mcp_tool_name(tool_definition)
197
-
198
- self._function_nodes[function_name] = create_mcp_tool_node(
199
- tool_def=tool_definition,
200
- tool_prompt_node=self.tool_prompt_node,
201
- )
202
- else:
203
- function_name = get_function_name(function)
204
-
205
- self._function_nodes[function_name] = create_function_node(
206
- function=function,
207
- tool_prompt_node=self.tool_prompt_node,
208
- )
209
+ for hydrated_function in hydrated_functions:
210
+ function_name = get_function_name(hydrated_function)
211
+ self._function_nodes[function_name] = create_function_node(
212
+ function=hydrated_function,
213
+ tool_prompt_node=self.tool_prompt_node,
214
+ )
209
215
 
210
216
  graph: Graph = self.tool_prompt_node >> self.router_node
211
217
 
@@ -223,3 +229,37 @@ class ToolCallingNode(BaseNode[StateType], Generic[StateType]):
223
229
  graph._extend_edges(default_port_graph.edges)
224
230
 
225
231
  self._graph = graph
232
+
233
+ def __directly_emit_workflow_output__(
234
+ self,
235
+ event: NodeExecutionStreamingEvent,
236
+ workflow_output_descriptor: OutputReference,
237
+ ) -> bool:
238
+ """
239
+ Check if this ToolCallingNode should directly emit workflow output events.
240
+ Similar to BasePromptNode, this allows streaming text and chat_history outputs through FinalOutputNode.
241
+ """
242
+ # Only handle text and chat_history outputs
243
+ if event.output.name != "text":
244
+ return False
245
+
246
+ if not isinstance(event.output.delta, str) and not event.output.is_initiated:
247
+ return False
248
+
249
+ target_nodes = [e.to_node for port in self.Ports for e in port.edges if e.to_node.__simulates_workflow_output__]
250
+ target_node_output = next(
251
+ (
252
+ o
253
+ for target_node in target_nodes
254
+ for o in target_node.Outputs
255
+ if o == workflow_output_descriptor.instance
256
+ ),
257
+ None,
258
+ )
259
+ if not target_node_output:
260
+ return False
261
+
262
+ if not isinstance(target_node_output.instance, BaseDescriptor):
263
+ return False
264
+
265
+ return _contains_reference_to_output(target_node_output.instance, event.node_definition.Outputs.text)