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
@@ -105,7 +105,13 @@ def test_serialize_node__retry(serialize_node):
105
105
  }
106
106
  ],
107
107
  "outputs": [
108
- {"id": "78eece53-8a20-40a1-8a86-ffebe256282b", "name": "output", "type": "STRING", "value": None}
108
+ {
109
+ "id": "78eece53-8a20-40a1-8a86-ffebe256282b",
110
+ "name": "output",
111
+ "type": "STRING",
112
+ "value": None,
113
+ "schema": {"type": "string"},
114
+ }
109
115
  ],
110
116
  },
111
117
  serialized_node,
@@ -197,7 +203,13 @@ def test_serialize_node__try(serialize_node):
197
203
  }
198
204
  ],
199
205
  "outputs": [
200
- {"id": "d8d0c9a8-0804-4b43-a874-28a7e7d6aec8", "name": "output", "type": "STRING", "value": None}
206
+ {
207
+ "id": "d8d0c9a8-0804-4b43-a874-28a7e7d6aec8",
208
+ "name": "output",
209
+ "type": "STRING",
210
+ "value": None,
211
+ "schema": {"type": "string"},
212
+ }
201
213
  ],
202
214
  },
203
215
  serialized_node,
@@ -190,6 +190,115 @@ def test_serialize_node__lazy_reference_with_string():
190
190
  ]
191
191
 
192
192
 
193
+ def test_serialize_node__lazy_reference_workflow_output():
194
+ """Test that LazyReference to workflow output serializes as WORKFLOW_OUTPUT type."""
195
+
196
+ # GIVEN a node with a LazyReference to a workflow output
197
+ class NodeWithWorkflowOutputReference(BaseNode):
198
+ workflow_output_ref = LazyReference(lambda: TestWorkflow.Outputs.final_result)
199
+
200
+ class TestNode(BaseNode):
201
+ class Outputs(BaseNode.Outputs):
202
+ result: str = "test result"
203
+
204
+ # AND a workflow that defines an output
205
+ class TestWorkflow(BaseWorkflow):
206
+ graph = NodeWithWorkflowOutputReference >> TestNode
207
+
208
+ class Outputs(BaseWorkflow.Outputs):
209
+ final_result = TestNode.Outputs.result
210
+
211
+ # WHEN the node is serialized in the context of the workflow
212
+ workflow_display = get_workflow_display(workflow_class=TestWorkflow)
213
+ serialized_workflow: dict = workflow_display.serialize()
214
+
215
+ # THEN the node should properly serialize the workflow output reference
216
+ node_with_lazy_reference = next(
217
+ node
218
+ for node in serialized_workflow["workflow_raw_data"]["nodes"]
219
+ if node["id"] == str(NodeWithWorkflowOutputReference.__id__)
220
+ )
221
+
222
+ # AND the workflow output reference should serialize as WORKFLOW_OUTPUT type
223
+ assert len(node_with_lazy_reference["attributes"]) == 1
224
+ attr = node_with_lazy_reference["attributes"][0]
225
+ assert attr["name"] == "workflow_output_ref"
226
+ assert attr["value"]["type"] == "WORKFLOW_OUTPUT"
227
+
228
+ # AND the output_variable_id should match the workflow output
229
+ workflow_output = next(
230
+ output for output in serialized_workflow["output_variables"] if output["key"] == "final_result"
231
+ )
232
+ assert attr["value"]["output_variable_id"] == workflow_output["id"]
233
+
234
+
235
+ def test_serialize_node__lazy_reference_workflow_output_with_string():
236
+ """Test that string-based LazyReference to workflow output serializes as WORKFLOW_OUTPUT type."""
237
+
238
+ # GIVEN a node with a string-based LazyReference to a workflow output
239
+ class NodeWithWorkflowOutputReference(BaseNode):
240
+ workflow_output_ref = LazyReference[str]("StringWorkflow.Outputs.final_result")
241
+
242
+ class TestNode(BaseNode):
243
+ class Outputs(BaseNode.Outputs):
244
+ result: str = "test result"
245
+
246
+ # AND a workflow that defines an output
247
+ class StringWorkflow(BaseWorkflow):
248
+ graph = NodeWithWorkflowOutputReference >> TestNode
249
+
250
+ class Outputs(BaseWorkflow.Outputs):
251
+ final_result = TestNode.Outputs.result
252
+
253
+ # WHEN the node is serialized in the context of the workflow
254
+ workflow_display = get_workflow_display(workflow_class=StringWorkflow)
255
+ serialized_workflow: dict = workflow_display.serialize()
256
+
257
+ # THEN the node should properly serialize the workflow output reference
258
+ node_with_lazy_reference = next(
259
+ node
260
+ for node in serialized_workflow["workflow_raw_data"]["nodes"]
261
+ if node["id"] == str(NodeWithWorkflowOutputReference.__id__)
262
+ )
263
+
264
+ # AND the workflow output reference should serialize as WORKFLOW_OUTPUT type
265
+ assert len(node_with_lazy_reference["attributes"]) == 1
266
+ attr = node_with_lazy_reference["attributes"][0]
267
+ assert attr["name"] == "workflow_output_ref"
268
+ assert attr["value"]["type"] == "WORKFLOW_OUTPUT"
269
+
270
+ # AND the output_variable_id should match the workflow output
271
+ workflow_output = next(
272
+ output for output in serialized_workflow["output_variables"] if output["key"] == "final_result"
273
+ )
274
+ assert attr["value"]["output_variable_id"] == workflow_output["id"]
275
+
276
+
277
+ def test_serialize_node__lazy_reference_with_string__class_not_found():
278
+ """Test that InvalidOutputReferenceError is added to display context when the referenced class is not found."""
279
+
280
+ # GIVEN a node with a string-based LazyReference to a non-existent class
281
+ class NodeWithInvalidReference(BaseNode):
282
+ invalid_ref = LazyReference[str]("NonExistentClass.Outputs.result")
283
+
284
+ # AND a workflow that contains the node
285
+ class TestWorkflow(BaseWorkflow):
286
+ graph = NodeWithInvalidReference
287
+
288
+ # WHEN we try to serialize the workflow
289
+ workflow_display = get_workflow_display(workflow_class=TestWorkflow)
290
+ workflow_display.serialize()
291
+
292
+ # THEN the error should be added to the display context
293
+ errors = list(workflow_display.display_context.errors)
294
+ assert len(errors) == 1
295
+
296
+ # AND the error message should mention the class that could not be found
297
+ error_message = str(errors[0])
298
+ assert "NonExistentClass" in error_message
299
+ assert "Could not find node or workflow class" in error_message
300
+
301
+
193
302
  def test_serialize_node__workflow_input(serialize_node):
194
303
  class WorkflowInputGenericNode(BaseNode):
195
304
  attr: str = Inputs.input
@@ -50,6 +50,7 @@ def test_serialize_node__annotated_output(serialize_node):
50
50
  "name": "output",
51
51
  "type": "NUMBER",
52
52
  "value": None,
53
+ "schema": {"type": "integer"},
53
54
  }
54
55
  ],
55
56
  },
@@ -102,6 +103,7 @@ def test_serialize_node__workflow_input(serialize_node):
102
103
  "type": "WORKFLOW_INPUT",
103
104
  "input_variable_id": str(input_id),
104
105
  },
106
+ "schema": {"type": "string"},
105
107
  }
106
108
  ],
107
109
  },
@@ -168,6 +170,7 @@ def test_serialize_node__node_output_reference(serialize_node):
168
170
  "node_id": "40fcba1f-9b25-4fed-8f15-a2fd80ff85a1",
169
171
  "node_output_id": str(node_output_id),
170
172
  },
173
+ "schema": {"type": "string"},
171
174
  }
172
175
  ],
173
176
  },
@@ -1,14 +1,22 @@
1
1
  from uuid import uuid4
2
+ from typing import cast
2
3
 
3
4
  from deepdiff import DeepDiff
4
5
 
6
+ from vellum.workflows.descriptors.base import BaseDescriptor
5
7
  from vellum.workflows.inputs.base import BaseInputs
6
8
  from vellum.workflows.nodes.bases.base import BaseNode
7
9
  from vellum.workflows.ports.port import Port
8
10
  from vellum.workflows.references.vellum_secret import VellumSecretReference
9
- from vellum_ee.workflows.display.base import WorkflowInputsDisplay
11
+ from vellum.workflows.types.core import JsonArray, JsonObject
12
+ from vellum_ee.workflows.display.base import WorkflowInputsDisplay, WorkflowMetaDisplay
13
+ from vellum_ee.workflows.display.editor.types import NodeDisplayData
10
14
  from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
15
+ from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
11
16
  from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay
17
+ from vellum_ee.workflows.display.types import WorkflowDisplayContext
18
+ from vellum_ee.workflows.display.utils.exceptions import UnsupportedSerializationException
19
+ from vellum_ee.workflows.display.workflows.base_workflow_display import BaseWorkflowDisplay
12
20
 
13
21
 
14
22
  class Inputs(BaseInputs):
@@ -1001,6 +1009,133 @@ def test_serialize_node__or_then_and(serialize_node):
1001
1009
  )
1002
1010
 
1003
1011
 
1012
+ def test_serialize_node__blank(serialize_node):
1013
+ """
1014
+ Tests that a node with an is_blank() port condition serializes correctly.
1015
+ """
1016
+
1017
+ # GIVEN a node with an is_blank() port condition
1018
+ class BlankGenericNode(BaseNode):
1019
+
1020
+ class Ports(BaseNode.Ports):
1021
+ if_branch = Port.on_if(Inputs.input.is_blank())
1022
+
1023
+ input_id = uuid4()
1024
+
1025
+ # WHEN we serialize the node
1026
+ serialized_node = serialize_node(
1027
+ node_class=BlankGenericNode, global_workflow_input_displays={Inputs.input: WorkflowInputsDisplay(id=input_id)}
1028
+ )
1029
+
1030
+ # THEN the serialized node should have the correct structure with a UNARY_EXPRESSION and "blank" operator
1031
+ assert not DeepDiff(
1032
+ {
1033
+ "id": "a9067b0e-6c3b-4ed0-8b0a-a4eb861f75a8",
1034
+ "label": "Blank Generic Node",
1035
+ "type": "GENERIC",
1036
+ "display_data": {"position": {"x": 0.0, "y": 0.0}},
1037
+ "base": {"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"]},
1038
+ "definition": {
1039
+ "name": "BlankGenericNode",
1040
+ "module": [
1041
+ "vellum_ee",
1042
+ "workflows",
1043
+ "display",
1044
+ "tests",
1045
+ "workflow_serialization",
1046
+ "generic_nodes",
1047
+ "test_ports_serialization",
1048
+ ],
1049
+ },
1050
+ "trigger": {"id": "f4cc239d-9feb-4eeb-84d9-62633a7883e7", "merge_behavior": "AWAIT_ATTRIBUTES"},
1051
+ "ports": [
1052
+ {
1053
+ "id": "3ac47591-d4dc-470b-be44-74e1445518e2",
1054
+ "type": "IF",
1055
+ "name": "if_branch",
1056
+ "expression": {
1057
+ "type": "UNARY_EXPRESSION",
1058
+ "lhs": {
1059
+ "type": "WORKFLOW_INPUT",
1060
+ "input_variable_id": str(input_id),
1061
+ },
1062
+ "operator": "blank",
1063
+ },
1064
+ }
1065
+ ],
1066
+ "adornments": None,
1067
+ "attributes": [],
1068
+ "outputs": [],
1069
+ },
1070
+ serialized_node,
1071
+ ignore_order=True,
1072
+ )
1073
+
1074
+
1075
+ def test_serialize_node__not_blank(serialize_node):
1076
+ """
1077
+ Tests that a node with an is_not_blank() port condition serializes correctly.
1078
+ """
1079
+
1080
+ # GIVEN a node with an is_not_blank() port condition
1081
+ class NotBlankGenericNode(BaseNode):
1082
+
1083
+ class Ports(BaseNode.Ports):
1084
+ if_branch = Port.on_if(Inputs.input.is_not_blank())
1085
+
1086
+ input_id = uuid4()
1087
+
1088
+ # WHEN we serialize the node
1089
+ serialized_node = serialize_node(
1090
+ node_class=NotBlankGenericNode,
1091
+ global_workflow_input_displays={Inputs.input: WorkflowInputsDisplay(id=input_id)},
1092
+ )
1093
+
1094
+ # THEN the serialized node should have the correct structure with a UNARY_EXPRESSION and "notBlank" operator
1095
+ assert not DeepDiff(
1096
+ {
1097
+ "id": "a9349004-bd02-458e-af66-7b515cdd4d3a",
1098
+ "label": "Not Blank Generic Node",
1099
+ "type": "GENERIC",
1100
+ "display_data": {"position": {"x": 0.0, "y": 0.0}},
1101
+ "base": {"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"]},
1102
+ "definition": {
1103
+ "name": "NotBlankGenericNode",
1104
+ "module": [
1105
+ "vellum_ee",
1106
+ "workflows",
1107
+ "display",
1108
+ "tests",
1109
+ "workflow_serialization",
1110
+ "generic_nodes",
1111
+ "test_ports_serialization",
1112
+ ],
1113
+ },
1114
+ "trigger": {"id": "60521d64-adc6-4f49-8605-0bf617a23a2d", "merge_behavior": "AWAIT_ATTRIBUTES"},
1115
+ "ports": [
1116
+ {
1117
+ "id": "4824d11b-9d44-4183-8e7f-c4eb21262cc8",
1118
+ "type": "IF",
1119
+ "name": "if_branch",
1120
+ "expression": {
1121
+ "type": "UNARY_EXPRESSION",
1122
+ "lhs": {
1123
+ "type": "WORKFLOW_INPUT",
1124
+ "input_variable_id": str(input_id),
1125
+ },
1126
+ "operator": "notBlank",
1127
+ },
1128
+ }
1129
+ ],
1130
+ "adornments": None,
1131
+ "attributes": [],
1132
+ "outputs": [],
1133
+ },
1134
+ serialized_node,
1135
+ ignore_order=True,
1136
+ )
1137
+
1138
+
1004
1139
  def test_serialize_node__parse_json(serialize_node):
1005
1140
 
1006
1141
  class ParseJsonGenericNode(BaseNode):
@@ -1067,3 +1202,54 @@ def test_serialize_node__parse_json(serialize_node):
1067
1202
  serialized_node,
1068
1203
  ignore_order=True,
1069
1204
  )
1205
+
1206
+
1207
+ def test_serialize_node__unsupported_descriptor_type():
1208
+ """
1209
+ Tests that serializing a generic node with an unsupported descriptor type
1210
+ returns the node with an empty expression and adds the error to the display context.
1211
+ """
1212
+
1213
+ # GIVEN a custom descriptor that is not a supported expression type
1214
+ class UnsupportedDescriptor(BaseDescriptor[bool]):
1215
+ def __init__(self) -> None:
1216
+ super().__init__(name="unsupported", types=(bool,), instance=None)
1217
+
1218
+ # AND a generic node that uses this unsupported descriptor in a port condition
1219
+ class UnsupportedDescriptorNode(BaseNode):
1220
+ class Ports(BaseNode.Ports):
1221
+ if_branch = Port.on_if(UnsupportedDescriptor())
1222
+
1223
+ # AND a display context with dry_run=True to capture errors
1224
+ node_display_class = get_node_display_class(UnsupportedDescriptorNode)
1225
+ node_display = node_display_class()
1226
+
1227
+ context = WorkflowDisplayContext(
1228
+ workflow_display_class=BaseWorkflowDisplay,
1229
+ workflow_display=WorkflowMetaDisplay(
1230
+ entrypoint_node_id=uuid4(),
1231
+ entrypoint_node_source_handle_id=uuid4(),
1232
+ entrypoint_node_display=NodeDisplayData(),
1233
+ ),
1234
+ node_displays={UnsupportedDescriptorNode: node_display},
1235
+ dry_run=True,
1236
+ )
1237
+
1238
+ # WHEN we serialize the node
1239
+ serialized_node = node_display.serialize(context)
1240
+
1241
+ # THEN the node should still be returned
1242
+ assert serialized_node["type"] == "GENERIC"
1243
+
1244
+ # AND the port should have a None expression
1245
+ ports = cast(JsonArray, serialized_node["ports"])
1246
+ assert len(ports) == 1
1247
+ port = cast(JsonObject, ports[0])
1248
+ assert port["type"] == "IF"
1249
+ assert port["expression"] is None
1250
+
1251
+ # AND the error should be added to the display context
1252
+ errors = list(context.errors)
1253
+ assert len(errors) == 1
1254
+ assert isinstance(errors[0], UnsupportedSerializationException)
1255
+ assert "Unsupported condition type" in str(errors[0])