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,7 +1,10 @@
1
+ from typing import Any
2
+
1
3
  from vellum.workflows import BaseWorkflow
2
4
  from vellum.workflows.nodes import BaseNode
3
5
  from vellum.workflows.nodes.displayable.final_output_node import FinalOutputNode
4
6
  from vellum.workflows.state.base import BaseState
7
+ from vellum_ee.workflows.display.utils.exceptions import UnsupportedSerializationException
5
8
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
6
9
 
7
10
 
@@ -78,3 +81,136 @@ def test_final_output_node_display__serialize_with_invalid_types_should_raise_er
78
81
 
79
82
  # AND the serialized workflow should still be created
80
83
  assert "workflow_raw_data" in serialized_workflow
84
+
85
+
86
+ def test_final_output_node_display__serialize_with_list_str_type():
87
+ """
88
+ Tests that FinalOutputNode with list[str] as second type parameter serializes correctly.
89
+ """
90
+
91
+ # GIVEN a node that outputs a list of strings
92
+ class ListNode(BaseNode):
93
+
94
+ class Outputs:
95
+ result: list[str]
96
+
97
+ # AND a FinalOutputNode with list[str] as the second type parameter
98
+ class ListOutput(FinalOutputNode[BaseState, list[str]]):
99
+ class Outputs(FinalOutputNode.Outputs):
100
+ value = ListNode.Outputs.result
101
+
102
+ # AND a workflow referencing the node
103
+ class MyWorkflow(BaseWorkflow):
104
+ graph = ListNode >> ListOutput
105
+
106
+ class Outputs(BaseWorkflow.Outputs):
107
+ final_result = ListOutput.Outputs.value
108
+
109
+ # WHEN we serialize the workflow
110
+ workflow_display = get_workflow_display(workflow_class=MyWorkflow)
111
+ serialized_workflow: dict = workflow_display.serialize()
112
+
113
+ # THEN serialization should succeed without raising validation errors
114
+ assert "workflow_raw_data" in serialized_workflow
115
+ assert "nodes" in serialized_workflow["workflow_raw_data"]
116
+
117
+ # AND the terminal node should be properly serialized
118
+ terminal_node = next(
119
+ node for node in serialized_workflow["workflow_raw_data"]["nodes"] if node["type"] == "TERMINAL"
120
+ )
121
+ assert terminal_node is not None
122
+ assert terminal_node["id"] == str(ListOutput.__id__)
123
+
124
+ # AND the output type should be correctly serialized as JSON
125
+ assert terminal_node["data"]["output_type"] == "JSON"
126
+
127
+ # AND the outputs should contain the correct type information
128
+ assert len(terminal_node["outputs"]) == 1
129
+ assert terminal_node["outputs"][0]["type"] == "JSON"
130
+
131
+ # AND the output should have an inline type reference with JSON Schema
132
+ schema = terminal_node["outputs"][0]["schema"]
133
+ assert schema["type"] == "array"
134
+ assert schema["items"]["type"] == "string"
135
+
136
+
137
+ def test_final_output_node_display__serialize_with_nested_node_output_reference():
138
+ """
139
+ Tests that FinalOutputNode with a dict containing nested node output references serializes correctly.
140
+ """
141
+
142
+ # GIVEN a node that outputs multiple values
143
+ class DataNode(BaseNode):
144
+ class Outputs:
145
+ url: str
146
+ count: int
147
+
148
+ # AND a FinalOutputNode with a dict containing nested references to node outputs
149
+ class NestedOutput(FinalOutputNode[BaseState, dict[str, Any]]):
150
+ class Outputs(FinalOutputNode.Outputs):
151
+ value = {
152
+ "download_url": DataNode.Outputs.url,
153
+ "row_count": DataNode.Outputs.count,
154
+ "message": "Data processed successfully",
155
+ }
156
+
157
+ # AND a workflow referencing the node
158
+ class MyWorkflow(BaseWorkflow):
159
+ graph = DataNode >> NestedOutput
160
+
161
+ class Outputs(BaseWorkflow.Outputs):
162
+ final_result = NestedOutput.Outputs.value
163
+
164
+ # WHEN we serialize the workflow
165
+ workflow_display = get_workflow_display(workflow_class=MyWorkflow)
166
+ serialized_workflow: dict = workflow_display.serialize()
167
+
168
+ # THEN serialization should succeed
169
+ assert "workflow_raw_data" in serialized_workflow
170
+ assert "nodes" in serialized_workflow["workflow_raw_data"]
171
+
172
+ # AND the terminal node should be properly serialized
173
+ terminal_node = next(
174
+ node for node in serialized_workflow["workflow_raw_data"]["nodes"] if node["type"] == "TERMINAL"
175
+ )
176
+ assert terminal_node is not None
177
+ assert terminal_node["id"] == str(NestedOutput.__id__)
178
+
179
+ # AND the node should have inputs
180
+ assert len(terminal_node["inputs"]) == 1
181
+ node_input = terminal_node["inputs"][0]
182
+ assert node_input["key"] == "node_input"
183
+
184
+ # AND the input value should have rules with a CONSTANT_VALUE containing the serialized dict
185
+ assert node_input["value"]["combinator"] == "OR"
186
+ assert len(node_input["value"]["rules"]) == 1
187
+ rule = node_input["value"]["rules"][0]
188
+ assert rule["type"] == "CONSTANT_VALUE"
189
+ assert rule["data"]["type"] == "JSON"
190
+
191
+ # AND the serialized value should be a DICTIONARY_REFERENCE with entries
192
+ serialized_value = rule["data"]["value"]
193
+ assert serialized_value["type"] == "DICTIONARY_REFERENCE"
194
+ assert "entries" in serialized_value
195
+ assert len(serialized_value["entries"]) == 3
196
+
197
+ # AND the entries should contain the nested references and constant value
198
+ entry_keys = {entry["key"] for entry in serialized_value["entries"]}
199
+ assert entry_keys == {"download_url", "row_count", "message"}
200
+
201
+ # AND the outputs should contain the DICTIONARY_REFERENCE value
202
+ assert len(terminal_node["outputs"]) == 1
203
+ output = terminal_node["outputs"][0]
204
+ assert output["name"] == "value"
205
+ assert output["type"] == "JSON"
206
+ assert output["value"]["type"] == "DICTIONARY_REFERENCE"
207
+ assert len(output["value"]["entries"]) == 3
208
+
209
+ # AND the attributes should be empty for this node
210
+ assert terminal_node.get("attributes", []) == []
211
+
212
+ # AND a validation error should be recorded for the nested references
213
+ errors = list(workflow_display.display_context.errors)
214
+ assert len(errors) == 1
215
+ assert isinstance(errors[0], UnsupportedSerializationException)
216
+ assert "nested references" in str(errors[0]).lower()
@@ -1,22 +1,21 @@
1
1
  from vellum.workflows import BaseWorkflow
2
2
  from vellum.workflows.nodes.displayable.note_node.node import NoteNode
3
- from vellum_ee.workflows.display.nodes.vellum.note_node import BaseNoteNodeDisplay
4
3
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
5
4
 
6
5
 
7
6
  def test_serialize_node__note_node():
8
- # GIVEN a note node
9
- class MyNoteNode(NoteNode):
10
- pass
7
+ """
8
+ Tests that a single note node is properly serialized in a workflow.
9
+ """
11
10
 
12
- # AND a display class for the note node
13
- class MyNoteNodeDisplay(BaseNoteNodeDisplay[MyNoteNode]):
11
+ # GIVEN a note node with text and style defined on the node class
12
+ class MyNoteNode(NoteNode):
14
13
  text = "This makes sense"
15
14
  style = {
16
15
  "fontSize": 24,
17
16
  }
18
17
 
19
- # AND a workflow with the code node
18
+ # AND a workflow with the note node
20
19
  class Workflow(BaseWorkflow):
21
20
  graph = MyNoteNode
22
21
 
@@ -30,3 +29,41 @@ def test_serialize_node__note_node():
30
29
  assert my_note_node["inputs"] == []
31
30
  assert my_note_node["data"]["text"] == "This makes sense"
32
31
  assert my_note_node["data"]["style"] == {"fontSize": 24}
32
+
33
+
34
+ def test_serialize_workflow__graph_with_only_note_nodes():
35
+ """
36
+ Tests that a workflow with only note nodes properly serializes all note nodes in the final payload.
37
+ """
38
+
39
+ # GIVEN multiple note nodes with text and style defined on the node classes
40
+ class FirstNoteNode(NoteNode):
41
+ text = "First note"
42
+ style = {"fontSize": 16}
43
+
44
+ class SecondNoteNode(NoteNode):
45
+ text = "Second note"
46
+ style = {"fontSize": 20}
47
+
48
+ # AND a workflow with only note nodes in unused_graphs
49
+ class Workflow(BaseWorkflow):
50
+ unused_graphs = {FirstNoteNode, SecondNoteNode} # type: ignore[assignment]
51
+
52
+ # WHEN the workflow is serialized
53
+ workflow_display = get_workflow_display(workflow_class=Workflow)
54
+ serialized_workflow: dict = workflow_display.serialize()
55
+
56
+ # THEN the serialized workflow should contain both note nodes
57
+ note_nodes = [node for node in serialized_workflow["workflow_raw_data"]["nodes"] if node["type"] == "NOTE"]
58
+ assert len(note_nodes) == 2
59
+
60
+ # AND the note nodes should have the correct data
61
+ note_texts = {node["data"]["text"] for node in note_nodes}
62
+ assert note_texts == {"First note", "Second note"}
63
+
64
+ # AND each note node should have the correct style
65
+ first_note = next(node for node in note_nodes if node["data"]["text"] == "First note")
66
+ second_note = next(node for node in note_nodes if node["data"]["text"] == "Second note")
67
+
68
+ assert first_note["data"]["style"] == {"fontSize": 16}
69
+ assert second_note["data"]["style"] == {"fontSize": 20}
@@ -154,17 +154,17 @@ def test_serialize_node__prompt_inputs__state_reference():
154
154
  workflow_display = get_workflow_display(workflow_class=Workflow)
155
155
  serialized_workflow: dict = workflow_display.serialize()
156
156
 
157
- # THEN the node should skip the state reference input rule
158
157
  my_prompt_node = next(
159
158
  node for node in serialized_workflow["workflow_raw_data"]["nodes"] if node["id"] == str(MyPromptNode.__id__)
160
159
  )
161
-
162
160
  assert my_prompt_node["inputs"] == [
163
161
  {
164
162
  "id": "7c5d23b3-c5ed-4ed6-a685-43fbe9a9baf8",
165
163
  "key": "foo",
166
164
  "value": {
167
- "rules": [],
165
+ "rules": [
166
+ {"type": "WORKFLOW_STATE", "data": {"state_variable_id": "dd3391bf-c818-4eba-aac5-912618ba412f"}}
167
+ ],
168
168
  "combinator": "OR",
169
169
  },
170
170
  },
@@ -172,15 +172,7 @@ def test_serialize_node__prompt_inputs__state_reference():
172
172
  "id": "e138f06e-d705-46bc-8ac4-c844b0e9131a",
173
173
  "key": "bar",
174
174
  "value": {
175
- "rules": [
176
- {
177
- "type": "CONSTANT_VALUE",
178
- "data": {
179
- "type": "STRING",
180
- "value": "baz",
181
- },
182
- }
183
- ],
175
+ "rules": [{"type": "CONSTANT_VALUE", "data": {"type": "STRING", "value": "baz"}}],
184
176
  "combinator": "OR",
185
177
  },
186
178
  },
@@ -201,7 +193,7 @@ def test_serialize_node__prompt_inputs__state_reference():
201
193
  "key": "foo",
202
194
  "value": {
203
195
  "type": "WORKFLOW_STATE",
204
- "state_variable_id": "45649791-c642-4405-aff9-a1fafd780ea1",
196
+ "state_variable_id": "dd3391bf-c818-4eba-aac5-912618ba412f",
205
197
  },
206
198
  },
207
199
  {
@@ -3,6 +3,12 @@ from datetime import datetime
3
3
  from uuid import UUID, uuid4
4
4
  from typing import Type
5
5
 
6
+ from vellum import (
7
+ ReleaseEnvironment,
8
+ WorkflowDeploymentRelease,
9
+ WorkflowDeploymentReleaseWorkflowDeployment,
10
+ WorkflowDeploymentReleaseWorkflowVersion,
11
+ )
6
12
  from vellum.workflows import BaseWorkflow
7
13
  from vellum.workflows.nodes.displayable.subworkflow_deployment_node.node import SubworkflowDeploymentNode
8
14
  from vellum_ee.workflows.display.nodes.vellum.subworkflow_deployment_node import BaseSubworkflowDeploymentNodeDisplay
@@ -29,28 +35,32 @@ def _display_class_with_node_input_ids_by_name_with_inputs_prefix(Node: Type[Sub
29
35
 
30
36
  @pytest.fixture
31
37
  def mock_fetch_deployment(mocker):
32
- # Create a mock deployment response
33
- mock_deployment = mocker.Mock(
34
- id="test-id",
35
- name="test-deployment",
36
- label="Test Deployment",
37
- status="ACTIVE",
38
- environment="DEVELOPMENT",
38
+ # Create a mock deployment release response
39
+ deployment_release = WorkflowDeploymentRelease(
40
+ id=str(uuid4()),
39
41
  created=datetime.now(),
40
- last_deployed_on=datetime.now(),
41
- last_deployed_history_item_id=str(uuid4()),
42
- input_variables=[],
43
- output_variables=[],
44
- description="Test deployment description",
42
+ environment=ReleaseEnvironment(id=str(uuid4()), name="DEVELOPMENT", label="Development"),
43
+ workflow_version=WorkflowDeploymentReleaseWorkflowVersion(
44
+ id=str(uuid4()),
45
+ input_variables=[],
46
+ output_variables=[],
47
+ ),
48
+ deployment=WorkflowDeploymentReleaseWorkflowDeployment(
49
+ id="test-id",
50
+ name="test-deployment",
51
+ ),
52
+ release_tags=[],
53
+ reviews=[],
45
54
  )
46
55
 
47
- # Patch the create_vellum_client function to return our mock client
48
- mocker.patch(
49
- "vellum.client.resources.workflow_deployments.client.WorkflowDeploymentsClient.retrieve",
50
- return_value=mock_deployment,
56
+ # Patch the retrieve_workflow_deployment_release method
57
+ patch_path = (
58
+ "vellum.client.resources.workflow_deployments.client."
59
+ "WorkflowDeploymentsClient.retrieve_workflow_deployment_release"
51
60
  )
61
+ mocker.patch(patch_path, return_value=deployment_release)
52
62
 
53
- return mock_deployment
63
+ return deployment_release
54
64
 
55
65
 
56
66
  @pytest.mark.parametrize(
@@ -153,7 +153,9 @@ def test_serialize_node__prompt_inputs__mixed_values():
153
153
 
154
154
 
155
155
  def test_serialize_node__tool_calling_node__mcp_server_api_key():
156
- # GIVEN a tool calling node with an mcp server
156
+ """Tests that MCPServer with EnvironmentVariableReference serializes as ARRAY_REFERENCE."""
157
+
158
+ # GIVEN a tool calling node with an mcp server using an environment variable for the API key
157
159
  class MyToolCallingNode(ToolCallingNode):
158
160
  functions = [
159
161
  MCPServer(
@@ -184,26 +186,70 @@ def test_serialize_node__tool_calling_node__mcp_server_api_key():
184
186
  attribute for attribute in my_tool_calling_node["attributes"] if attribute["name"] == "functions"
185
187
  )
186
188
 
189
+ # AND the functions attribute should be an ARRAY_REFERENCE with a DICTIONARY_REFERENCE
190
+ # containing the MCP server fields, with api_key_header_value as ENVIRONMENT_VARIABLE
187
191
  assert functions_attribute == {
188
192
  "id": "ff00c2d6-f99c-458b-9bcd-181f8e43b2d1",
189
193
  "name": "functions",
190
194
  "value": {
191
- "type": "CONSTANT_VALUE",
192
- "value": {
193
- "type": "JSON",
194
- "value": [
195
- {
196
- "type": "MCP_SERVER",
197
- "name": "my-mcp-server",
198
- "description": "",
199
- "url": "https://my-mcp-server.com",
200
- "authorization_type": "API_KEY",
201
- "bearer_token_value": None,
202
- "api_key_header_key": "my-api-key-header-key",
203
- "api_key_header_value": "my-api-key-header-value",
204
- }
205
- ],
206
- },
195
+ "type": "ARRAY_REFERENCE",
196
+ "items": [
197
+ {
198
+ "type": "DICTIONARY_REFERENCE",
199
+ "entries": [
200
+ {
201
+ "id": "8cff4f0a-86d9-43fd-8d0b-542c845db53e",
202
+ "key": "type",
203
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "STRING", "value": "MCP_SERVER"}},
204
+ },
205
+ {
206
+ "id": "29203be4-407c-4056-aaa3-6e6d1249113e",
207
+ "key": "name",
208
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "STRING", "value": "my-mcp-server"}},
209
+ },
210
+ {
211
+ "id": "69813bb2-12c1-432b-9ef3-a0071bd29149",
212
+ "key": "description",
213
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "STRING", "value": ""}},
214
+ },
215
+ {
216
+ "id": "83cd2f84-bf35-4996-9840-3754a79e1091",
217
+ "key": "url",
218
+ "value": {
219
+ "type": "CONSTANT_VALUE",
220
+ "value": {"type": "STRING", "value": "https://my-mcp-server.com"},
221
+ },
222
+ },
223
+ {
224
+ "id": "5ad23cee-497e-4ca3-ba51-d13f56985c75",
225
+ "key": "authorization_type",
226
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "STRING", "value": "API_KEY"}},
227
+ },
228
+ {
229
+ "id": "45964d28-1a9b-40a1-b544-62da92d2f62a",
230
+ "key": "bearer_token_value",
231
+ "value": {"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}},
232
+ },
233
+ {
234
+ "id": "c4952fa6-1ffb-4e88-863f-3d0867b19b7a",
235
+ "key": "api_key_header_key",
236
+ "value": {
237
+ "type": "CONSTANT_VALUE",
238
+ "value": {"type": "STRING", "value": "my-api-key-header-key"},
239
+ },
240
+ },
241
+ {
242
+ "id": "7dc9cdb0-d139-4a1d-8c6f-9941c6b82b62",
243
+ "key": "api_key_header_value",
244
+ "value": {
245
+ "type": "ENVIRONMENT_VARIABLE",
246
+ "environment_variable": "my-api-key-header-value",
247
+ },
248
+ },
249
+ ],
250
+ "definition": {"name": "MCPServer", "module": ["vellum", "workflows", "types", "definition"]},
251
+ }
252
+ ],
207
253
  },
208
254
  }
209
255
 
@@ -328,7 +374,7 @@ def test_serialize_tool_router_node():
328
374
  "lhs": {
329
375
  "lhs": {
330
376
  "lhs": {
331
- "state_variable_id": "0dd7f5a1-1d73-4153-9191-ca828ace4920",
377
+ "state_variable_id": "30a4672a-96dc-471a-878c-90e0f1f7f043",
332
378
  "type": "WORKFLOW_STATE",
333
379
  },
334
380
  "operator": "<",
@@ -354,7 +400,7 @@ def test_serialize_tool_router_node():
354
400
  },
355
401
  "operator": "accessField",
356
402
  "rhs": {
357
- "state_variable_id": "0dd7f5a1-1d73-4153-9191-ca828ace4920",
403
+ "state_variable_id": "30a4672a-96dc-471a-878c-90e0f1f7f043",
358
404
  "type": "WORKFLOW_STATE",
359
405
  },
360
406
  "type": "BINARY_EXPRESSION",
@@ -381,7 +427,7 @@ def test_serialize_tool_router_node():
381
427
  },
382
428
  "operator": "accessField",
383
429
  "rhs": {
384
- "state_variable_id": "0dd7f5a1-1d73-4153-9191-ca828ace4920",
430
+ "state_variable_id": "30a4672a-96dc-471a-878c-90e0f1f7f043",
385
431
  "type": "WORKFLOW_STATE",
386
432
  },
387
433
  "type": "BINARY_EXPRESSION",
@@ -453,6 +499,83 @@ def test_serialize_function_node():
453
499
  assert display_data["color"] == "purple"
454
500
 
455
501
 
502
+ def test_serialize_inline_prompt_node__mcp_server_not_serialized():
503
+ """
504
+ Test that MCP servers in inline prompt node functions are not serialized as function blocks.
505
+ MCP servers should be skipped during serialization (they return None from _generate_function_tools).
506
+ """
507
+
508
+ # GIVEN an MCP server
509
+ mcp_server = MCPServer(
510
+ name="my-mcp-server",
511
+ url="https://my-mcp-server.com",
512
+ authorization_type=AuthorizationType.API_KEY,
513
+ api_key_header_key="my-api-key-header-key",
514
+ api_key_header_value=EnvironmentVariableReference(name="my-api-key-header-value"),
515
+ )
516
+
517
+ # AND a regular function
518
+ def my_function(arg1: str) -> str:
519
+ return f"Result: {arg1}"
520
+
521
+ # AND an inline prompt node with both MCP server and regular function
522
+ class MyInlinePromptNode(InlinePromptNode):
523
+ ml_model = "gpt-4o-mini"
524
+ blocks = []
525
+ functions = [mcp_server, my_function]
526
+ prompt_inputs = None
527
+ parameters = PromptParameters()
528
+
529
+ # WHEN we serialize the inline prompt node
530
+ inline_prompt_node_display_class = get_node_display_class(MyInlinePromptNode)
531
+ inline_prompt_node_display = inline_prompt_node_display_class()
532
+
533
+ class Workflow(BaseWorkflow[BaseInputs, BaseState]):
534
+ graph = MyInlinePromptNode
535
+
536
+ workflow_display = get_workflow_display(workflow_class=Workflow)
537
+ display_context = workflow_display.display_context
538
+
539
+ serialized_node = inline_prompt_node_display.serialize(display_context)
540
+
541
+ # THEN the serialized node should have exec_config with prompt_template_block_data
542
+ assert isinstance(serialized_node, dict)
543
+ assert serialized_node["type"] == "PROMPT"
544
+ assert isinstance(serialized_node["data"], dict)
545
+ assert "exec_config" in serialized_node["data"]
546
+ exec_config = serialized_node["data"]["exec_config"]
547
+ assert isinstance(exec_config, dict)
548
+ assert "prompt_template_block_data" in exec_config
549
+ prompt_template_block_data = exec_config["prompt_template_block_data"]
550
+ assert isinstance(prompt_template_block_data, dict)
551
+ blocks = prompt_template_block_data["blocks"]
552
+ assert isinstance(blocks, list)
553
+
554
+ # AND there should be only one FUNCTION_DEFINITION block (from the regular function)
555
+ # MCP server should not be serialized
556
+ function_blocks = [
557
+ block for block in blocks if isinstance(block, dict) and block.get("block_type") == "FUNCTION_DEFINITION"
558
+ ]
559
+ assert len(function_blocks) == 1
560
+ assert function_blocks == [
561
+ {
562
+ "id": "b6b7b0c2-78b6-498f-8f97-fdaa81e082ec",
563
+ "block_type": "FUNCTION_DEFINITION",
564
+ "properties": {
565
+ "function_name": "my_function",
566
+ "function_description": None,
567
+ "function_parameters": {
568
+ "type": "object",
569
+ "properties": {"arg1": {"type": "string"}},
570
+ "required": ["arg1"],
571
+ },
572
+ "function_forced": None,
573
+ "function_strict": None,
574
+ },
575
+ }
576
+ ]
577
+
578
+
456
579
  def test_serialize_node__tool_calling_node__subworkflow_with_parent_input_reference():
457
580
  """
458
581
  Test that a tool calling node with a subworkflow that references parent inputs serializes correctly
@@ -594,7 +717,7 @@ def test_serialize_tool_prompt_node_with_inline_workflow():
594
717
  "operator": "concat",
595
718
  "rhs": {
596
719
  "type": "WORKFLOW_STATE",
597
- "state_variable_id": "7a1caaf5-99df-487a-8b2d-6512df2d871a",
720
+ "state_variable_id": "c1d692a7-3d87-4283-8d9c-daee82c61854",
598
721
  },
599
722
  },
600
723
  }
@@ -621,7 +744,7 @@ def test_serialize_tool_prompt_node_with_workflow_deployment(vellum_client):
621
744
  input_variables=[],
622
745
  output_variables=[],
623
746
  ),
624
- deployment=WorkflowDeploymentReleaseWorkflowDeployment(name="test-name"),
747
+ deployment=WorkflowDeploymentReleaseWorkflowDeployment(id="test-deployment-id", name="test-name"),
625
748
  description="test-description",
626
749
  release_tags=[
627
750
  ReleaseReleaseTag(