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,4 +1,5 @@
1
1
  import pytest
2
+ from unittest.mock import patch
2
3
  from uuid import UUID, uuid4
3
4
  from typing import List
4
5
 
@@ -8,11 +9,15 @@ from vellum.workflows.inputs import BaseInputs
8
9
  from vellum.workflows.nodes.bases import BaseNode
9
10
  from vellum.workflows.outputs import BaseOutputs
10
11
  from vellum.workflows.references import LazyReference
11
- from vellum_ee.workflows.display.base import WorkflowInputsDisplay, WorkflowMetaDisplay
12
+ from vellum.workflows.references.state_value import StateValueReference
13
+ from vellum.workflows.references.trigger import TriggerAttributeReference
14
+ from vellum.workflows.state import BaseState
15
+ from vellum.workflows.triggers.base import BaseTrigger
16
+ from vellum_ee.workflows.display.base import StateValueDisplay, WorkflowInputsDisplay, WorkflowMetaDisplay
12
17
  from vellum_ee.workflows.display.editor.types import NodeDisplayData
13
18
  from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
14
19
  from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay
15
- from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input_value_pointer_rules
20
+ from vellum_ee.workflows.display.nodes.vellum.utils import create_node_input, create_node_input_value_pointer_rules
16
21
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
17
22
  from vellum_ee.workflows.display.utils.vellum import (
18
23
  ConstantValuePointer,
@@ -21,6 +26,11 @@ from vellum_ee.workflows.display.utils.vellum import (
21
26
  NodeInputValuePointerRule,
22
27
  NodeOutputData,
23
28
  NodeOutputPointer,
29
+ TriggerAttributeData,
30
+ TriggerAttributePointer,
31
+ WorkflowStateData,
32
+ WorkflowStatePointer,
33
+ create_node_input_value_pointer_rule,
24
34
  )
25
35
  from vellum_ee.workflows.display.workflows.base_workflow_display import BaseWorkflowDisplay
26
36
 
@@ -129,5 +139,100 @@ def test_create_node_input_value_pointer_rules(
129
139
  MyNodeA: MyNodeADisplay(),
130
140
  },
131
141
  ),
142
+ uuid4(),
132
143
  )
133
144
  assert rules == expected_rules
145
+
146
+
147
+ class MyState(BaseState):
148
+ my_attribute: str
149
+
150
+
151
+ def test_create_node_input_value_pointer_rule__state_value_reference() -> None:
152
+ """
153
+ Tests that StateValueReference is serialized to WorkflowStatePointer using the display override ID.
154
+ """
155
+
156
+ # GIVEN a StateValueReference
157
+ state_value_reference: StateValueReference[str] = MyState.my_attribute # type: ignore[assignment]
158
+
159
+ # AND a display context with a state value display override
160
+ override_id = uuid4()
161
+ display_context = WorkflowDisplayContext(
162
+ global_state_value_displays={
163
+ state_value_reference: StateValueDisplay(id=override_id),
164
+ },
165
+ )
166
+
167
+ # WHEN we create a node input value pointer rule
168
+ result = create_node_input_value_pointer_rule(state_value_reference, display_context)
169
+
170
+ # THEN we should get a WorkflowStatePointer with the overridden display ID
171
+ assert isinstance(result, WorkflowStatePointer)
172
+ assert result.type == "WORKFLOW_STATE"
173
+ assert isinstance(result.data, WorkflowStateData)
174
+ assert result.data.state_variable_id == str(override_id)
175
+
176
+
177
+ class MyTrigger(BaseTrigger):
178
+ my_attribute: str
179
+
180
+
181
+ def test_create_node_input_value_pointer_rule__trigger_attribute_reference() -> None:
182
+ """
183
+ Tests that TriggerAttributeReference is serialized to TriggerAttributePointer.
184
+ """
185
+
186
+ # GIVEN a TriggerAttributeReference
187
+ trigger_attribute_reference: TriggerAttributeReference[str] = MyTrigger.my_attribute # type: ignore[assignment]
188
+
189
+ # AND a display context
190
+ display_context = WorkflowDisplayContext()
191
+
192
+ # WHEN we create a node input value pointer rule
193
+ result = create_node_input_value_pointer_rule(trigger_attribute_reference, display_context)
194
+
195
+ # THEN we should get a TriggerAttributePointer with the correct data
196
+ assert isinstance(result, TriggerAttributePointer)
197
+ assert result.type == "TRIGGER_ATTRIBUTE"
198
+ assert isinstance(result.data, TriggerAttributeData)
199
+ assert result.data.trigger_id == str(MyTrigger.__id__)
200
+ assert result.data.attribute_id == str(trigger_attribute_reference.id)
201
+
202
+
203
+ def test_create_node_input__unexpected_error_returns_node_input_with_empty_rules() -> None:
204
+ """
205
+ Tests that when create_node_input_value_pointer_rules raises an unexpected error,
206
+ create_node_input still returns a NodeInput with empty rules and adds the error to display context.
207
+ """
208
+
209
+ # GIVEN a display context
210
+ display_context = WorkflowDisplayContext()
211
+
212
+ # AND a node_id and input_name
213
+ node_id = uuid4()
214
+ input_name = "test_input"
215
+
216
+ # AND create_node_input_value_pointer_rules will raise an unexpected error
217
+ unexpected_error = RuntimeError("Unexpected error during serialization")
218
+ with patch(
219
+ "vellum_ee.workflows.display.nodes.vellum.utils.create_node_input_value_pointer_rules",
220
+ side_effect=unexpected_error,
221
+ ):
222
+ # WHEN we call create_node_input
223
+ result = create_node_input(
224
+ node_id=node_id,
225
+ input_name=input_name,
226
+ value="test_value",
227
+ display_context=display_context,
228
+ )
229
+
230
+ # THEN we should still get a NodeInput with empty rules
231
+ assert result.key == input_name
232
+ assert result.value.rules == []
233
+ assert result.value.combinator == "OR"
234
+
235
+ # AND the error should be added to the display context
236
+ errors = list(display_context.errors)
237
+ assert len(errors) == 1
238
+ assert errors[0] is unexpected_error
@@ -1,6 +1,8 @@
1
+ import logging
1
2
  from uuid import UUID
2
3
  from typing import Any, List, Optional, Type, Union, cast
3
4
 
5
+ from vellum.client.types.json_vellum_value import JsonVellumValue
4
6
  from vellum.workflows.descriptors.base import BaseDescriptor
5
7
  from vellum.workflows.expressions.coalesce_expression import CoalesceExpression
6
8
  from vellum.workflows.nodes.displayable.bases.utils import primitive_to_vellum_value
@@ -8,8 +10,12 @@ from vellum.workflows.references import NodeReference
8
10
  from vellum.workflows.references.lazy import LazyReference
9
11
  from vellum.workflows.utils.uuids import uuid4_from_hash
10
12
  from vellum_ee.workflows.display.types import WorkflowDisplayContext
11
- from vellum_ee.workflows.display.utils.exceptions import UnsupportedSerializationException
12
- from vellum_ee.workflows.display.utils.expressions import get_child_descriptor
13
+ from vellum_ee.workflows.display.utils.exceptions import (
14
+ InvalidOutputReferenceError,
15
+ UnsupportedSerializationException,
16
+ UserFacingException,
17
+ )
18
+ from vellum_ee.workflows.display.utils.expressions import get_child_descriptor, serialize_value
13
19
  from vellum_ee.workflows.display.utils.vellum import (
14
20
  ConstantValuePointer,
15
21
  ExecutionCounterData,
@@ -23,6 +29,8 @@ from vellum_ee.workflows.display.utils.vellum import (
23
29
  )
24
30
  from vellum_ee.workflows.display.vellum import NodeInput, NodeInputValuePointer
25
31
 
32
+ logger = logging.getLogger(__name__)
33
+
26
34
 
27
35
  def create_node_input(
28
36
  node_id: UUID,
@@ -33,7 +41,13 @@ def create_node_input(
33
41
  pointer_type: Optional[Type[NodeInputValuePointerRule]] = ConstantValuePointer,
34
42
  ) -> NodeInput:
35
43
  input_id = str(input_id) if input_id else str(uuid4_from_hash(f"{node_id}|{input_name}"))
36
- rules = create_node_input_value_pointer_rules(value, display_context, pointer_type=pointer_type)
44
+ try:
45
+ rules = create_node_input_value_pointer_rules(value, display_context, node_id, pointer_type=pointer_type)
46
+ except UserFacingException:
47
+ raise
48
+ except Exception as e:
49
+ display_context.add_validation_error(e)
50
+ rules = []
37
51
  return NodeInput(
38
52
  id=input_id,
39
53
  key=input_name,
@@ -44,9 +58,21 @@ def create_node_input(
44
58
  )
45
59
 
46
60
 
61
+ def _contains_descriptor(value: Any) -> bool:
62
+ """Check if a value contains any BaseDescriptor objects."""
63
+ if isinstance(value, BaseDescriptor):
64
+ return True
65
+ if isinstance(value, dict):
66
+ return any(_contains_descriptor(v) for v in value.values())
67
+ if isinstance(value, list):
68
+ return any(_contains_descriptor(item) for item in value)
69
+ return False
70
+
71
+
47
72
  def create_node_input_value_pointer_rules(
48
73
  value: Any,
49
74
  display_context: WorkflowDisplayContext,
75
+ node_id: UUID,
50
76
  existing_rules: Optional[List[NodeInputValuePointerRule]] = None,
51
77
  pointer_type: Optional[Type[NodeInputValuePointerRule]] = None,
52
78
  ) -> List[NodeInputValuePointerRule]:
@@ -59,33 +85,49 @@ def create_node_input_value_pointer_rules(
59
85
  value = cast(BaseDescriptor, value.instance)
60
86
 
61
87
  if isinstance(value, LazyReference):
62
- child_descriptor = get_child_descriptor(value, display_context)
88
+ try:
89
+ child_descriptor = get_child_descriptor(value, display_context)
90
+ except InvalidOutputReferenceError as e:
91
+ logger.warning("Failed to parse lazy reference '%s', skipping serialization", value.name)
92
+ display_context.add_validation_error(e)
93
+ return node_input_value_pointer_rules
63
94
  return create_node_input_value_pointer_rules(
64
- child_descriptor, display_context, [], pointer_type=pointer_type
95
+ child_descriptor, display_context, node_id, existing_rules=[], pointer_type=pointer_type
65
96
  )
66
97
 
67
98
  if isinstance(value, CoalesceExpression):
68
- # Recursively handle the left-hand side
69
- lhs_rules = create_node_input_value_pointer_rules(value.lhs, display_context, [], pointer_type=pointer_type)
99
+ lhs_rules = create_node_input_value_pointer_rules(
100
+ value.lhs, display_context, node_id, existing_rules=[], pointer_type=pointer_type
101
+ )
70
102
  node_input_value_pointer_rules.extend(lhs_rules)
71
103
 
72
- # Handle the right-hand side
73
104
  if not isinstance(value.rhs, CoalesceExpression):
74
105
  rhs_rules = create_node_input_value_pointer_rules(
75
- value.rhs, display_context, [], pointer_type=pointer_type
106
+ value.rhs, display_context, node_id, existing_rules=[], pointer_type=pointer_type
76
107
  )
77
108
  node_input_value_pointer_rules.extend(rhs_rules)
78
109
  else:
79
- # Non-CoalesceExpression case
80
110
  try:
81
111
  rule = create_node_input_value_pointer_rule(value, display_context)
82
112
  except UnsupportedSerializationException:
83
113
  return node_input_value_pointer_rules
84
114
 
85
115
  node_input_value_pointer_rules.append(rule)
116
+ elif isinstance(value, (dict, list)) and _contains_descriptor(value):
117
+ display_context.add_validation_error(
118
+ UnsupportedSerializationException(
119
+ "The Vellum UI does not support nested references for this Node attribute. "
120
+ "Consider flattening the references."
121
+ )
122
+ )
123
+ serialized = serialize_value(node_id, display_context, value)
124
+ serialized_pointer = ConstantValuePointer(
125
+ data=JsonVellumValue(value=serialized),
126
+ )
127
+ node_input_value_pointer_rules.append(serialized_pointer)
86
128
  else:
87
- pointer = create_pointer(value, pointer_type)
88
- node_input_value_pointer_rules.append(pointer)
129
+ constant_pointer = create_pointer(value, pointer_type)
130
+ node_input_value_pointer_rules.append(constant_pointer)
89
131
 
90
132
  return node_input_value_pointer_rules
91
133
 
@@ -92,6 +92,7 @@ def test_vellum_workflow_display__serialize_input_variables_with_capitalized_var
92
92
  "default": None,
93
93
  "required": True,
94
94
  "extensions": {"color": None},
95
+ "schema": {"type": "string"},
95
96
  }
96
97
  ]
97
98
 
@@ -129,24 +130,19 @@ def test_vellum_workflow_display_serialize_valid_handle_ids_for_base_nodes():
129
130
  edge_source_handle_ids = {edge.get("source_handle_id") for edge in edges if isinstance(edge, dict)}
130
131
  edge_target_handle_ids = {edge.get("target_handle_id") for edge in edges if isinstance(edge, dict)}
131
132
 
132
- for node in nodes:
133
- assert isinstance(node, dict)
134
-
135
- if node["type"] in {"ENTRYPOINT", "TERMINAL"}:
136
- continue
133
+ start_node = next(
134
+ node for node in nodes if isinstance(node, dict) and node["type"] == "GENERIC" and node["label"] == "Start Node"
135
+ )
136
+ end_node = next(
137
+ node for node in nodes if isinstance(node, dict) and node["type"] == "GENERIC" and node["label"] == "End Node"
138
+ )
137
139
 
138
- ports = node.get("ports")
139
- assert isinstance(ports, list)
140
- for port in ports:
141
- assert isinstance(port, dict)
142
- assert (
143
- port["id"] in edge_source_handle_ids
144
- ), f"Port {port['id']} from node {node['label']} not found in edge source handle ids"
140
+ assert isinstance(start_node["ports"], list)
141
+ assert isinstance(start_node["ports"][0], dict)
142
+ assert start_node["ports"][0]["id"] in edge_source_handle_ids
145
143
 
146
- assert isinstance(node["trigger"], dict)
147
- assert (
148
- node["trigger"]["id"] in edge_target_handle_ids
149
- ), f"Trigger {node['trigger']['id']} from node {node['label']} not found in edge target handle ids"
144
+ assert isinstance(end_node["trigger"], dict)
145
+ assert end_node["trigger"]["id"] in edge_target_handle_ids
150
146
 
151
147
 
152
148
  def test_vellum_workflow_display__serialize_with_unused_nodes_and_edges():
@@ -280,6 +276,7 @@ def test_vellum_workflow_display__serialize_with_parse_json_expression():
280
276
  "id": "e73fd6b1-1109-4a97-8510-c9ba8e6f5dbe",
281
277
  "name": "json_result",
282
278
  "type": "JSON",
279
+ "schema": {},
283
280
  "value": {
284
281
  "type": "UNARY_EXPRESSION",
285
282
  "lhs": {
@@ -0,0 +1,190 @@
1
+ """
2
+ Tests for JSON schema validation in InlinePromptNode during serialization.
3
+
4
+ This uses the jsonschema library to validate schemas against the JSON Schema meta-schema.
5
+ The validation is spec-compliant, meaning some schemas that might seem incomplete
6
+ (like arrays without items) are valid according to the JSON Schema specification.
7
+ """
8
+
9
+ import pytest
10
+
11
+ import jsonschema
12
+
13
+ from vellum import PromptParameters
14
+ from vellum.workflows.nodes import InlinePromptNode
15
+
16
+
17
+ @pytest.mark.parametrize(
18
+ "json_schema",
19
+ [
20
+ pytest.param(
21
+ {"type": "object", "properties": "invalid"},
22
+ id="object with non-dict properties",
23
+ ),
24
+ pytest.param(
25
+ {"anyOf": {"type": "string"}},
26
+ id="anyOf not list",
27
+ ),
28
+ pytest.param(
29
+ {"type": "object", "properties": {"name": "string"}},
30
+ id="object with non-schema property value",
31
+ ),
32
+ ],
33
+ )
34
+ def test_inline_prompt_node_validation__invalid_schemas_raise_error(
35
+ json_schema: dict,
36
+ ) -> None:
37
+ """
38
+ Tests that InlinePromptNode validation rejects structurally invalid JSON Schemas.
39
+ """
40
+
41
+ # GIVEN an InlinePromptNode configured with an invalid JSON Schema
42
+ class MyPromptNode(InlinePromptNode):
43
+ ml_model = "gpt-4"
44
+ blocks = []
45
+ parameters = PromptParameters(custom_parameters={"json_schema": json_schema})
46
+
47
+ # WHEN we call __validate__ on the node
48
+ # THEN it should raise a SchemaError
49
+ with pytest.raises(jsonschema.exceptions.SchemaError):
50
+ MyPromptNode.__validate__()
51
+
52
+
53
+ @pytest.mark.parametrize(
54
+ "json_schema",
55
+ [
56
+ pytest.param(
57
+ {"type": "array"},
58
+ id="array without items (valid per JSON Schema spec)",
59
+ ),
60
+ pytest.param(
61
+ {
62
+ "type": "array",
63
+ "prefixItems": [{"type": "string"}, {"type": "number"}],
64
+ },
65
+ id="array with prefixItems only",
66
+ ),
67
+ pytest.param(
68
+ {"type": "array", "items": {"type": "string"}},
69
+ id="valid array with items",
70
+ ),
71
+ pytest.param(
72
+ {"type": "array", "items": {}},
73
+ id="array with empty items object",
74
+ ),
75
+ pytest.param(
76
+ {"type": "array", "prefixItems": []},
77
+ id="array with empty prefixItems",
78
+ ),
79
+ pytest.param(
80
+ {
81
+ "type": "array",
82
+ "prefixItems": [{"type": "string"}],
83
+ "items": {"type": "number"},
84
+ },
85
+ id="array with both items and prefixItems",
86
+ ),
87
+ pytest.param(
88
+ {
89
+ "type": "array",
90
+ "prefixItems": [{"type": "array", "items": {"type": "string"}}],
91
+ },
92
+ id="valid nested array in prefixItems",
93
+ ),
94
+ pytest.param(
95
+ {
96
+ "type": "array",
97
+ "items": [
98
+ {"type": "string"},
99
+ {"type": "array", "items": {"type": "number"}},
100
+ ],
101
+ },
102
+ id="valid nested array in list-valued items",
103
+ ),
104
+ pytest.param(
105
+ {
106
+ "type": "object",
107
+ "properties": {
108
+ "name": {"type": "string"},
109
+ "age": {"type": "number"},
110
+ "tags": {"type": "array", "items": {"type": "string"}},
111
+ "address": {
112
+ "type": "object",
113
+ "properties": {
114
+ "street": {"type": "string"},
115
+ "city": {"type": "string"},
116
+ },
117
+ },
118
+ },
119
+ "anyOf": [{"required": ["name"]}, {"required": ["age"]}],
120
+ },
121
+ id="valid complex object schema with anyOf",
122
+ ),
123
+ pytest.param(
124
+ {
125
+ "name": "match_scorer_schema",
126
+ "schema": {
127
+ "type": "object",
128
+ "title": "MatchScorerSchema",
129
+ "required": ["recommendation", "score", "remarks"],
130
+ "properties": {
131
+ "score": {
132
+ "type": "integer",
133
+ "title": "Match Score",
134
+ "description": "Match score out of 10",
135
+ },
136
+ "remarks": {
137
+ "type": "string",
138
+ "title": "Remarks",
139
+ },
140
+ "recommendation": {
141
+ "enum": ["Advance", "Defer", "Reject"],
142
+ "type": "string",
143
+ "title": "Status",
144
+ },
145
+ },
146
+ },
147
+ },
148
+ id="wrapper with valid nested schema",
149
+ ),
150
+ pytest.param(
151
+ {"type": "array", "items": True},
152
+ id="array with boolean items (valid per JSON Schema spec)",
153
+ ),
154
+ pytest.param(
155
+ {"anyOf": [True, {"type": "string"}]},
156
+ id="anyOf with boolean element (valid per JSON Schema spec)",
157
+ ),
158
+ ],
159
+ )
160
+ def test_inline_prompt_node_validation__valid_schemas_succeed(
161
+ json_schema: dict,
162
+ ) -> None:
163
+ """
164
+ Tests that InlinePromptNode validation accepts structurally valid JSON Schemas.
165
+ """
166
+
167
+ # GIVEN an InlinePromptNode configured with a valid JSON Schema
168
+ class MyPromptNode(InlinePromptNode):
169
+ ml_model = "gpt-4"
170
+ blocks = []
171
+ parameters = PromptParameters(custom_parameters={"json_schema": json_schema})
172
+
173
+ # WHEN we call __validate__ on the node
174
+ # THEN it should not raise an error
175
+ MyPromptNode.__validate__()
176
+
177
+
178
+ def test_inline_prompt_node_validation__no_json_schema__succeeds():
179
+ """
180
+ Tests that InlinePromptNode without json_schema passes validation.
181
+ """
182
+
183
+ # GIVEN an InlinePromptNode that has no json_schema
184
+ class MyPromptNode(InlinePromptNode):
185
+ ml_model = "gpt-4"
186
+ blocks = []
187
+
188
+ # WHEN we call __validate__() on the node
189
+ # THEN it should not raise any errors
190
+ MyPromptNode.__validate__()