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
@@ -9,7 +9,7 @@ from typing import TYPE_CHECKING, Any, ClassVar, Dict, Iterator, Optional, Tuple
9
9
  from vellum.workflows.references.trigger import TriggerAttributeReference
10
10
  from vellum.workflows.types.utils import get_class_attr_names, infer_types
11
11
  from vellum.workflows.utils.files import virtual_open
12
- from vellum.workflows.utils.uuids import uuid4_from_hash
12
+ from vellum.workflows.utils.uuids import get_trigger_attribute_id, uuid4_from_hash
13
13
  from vellum_ee.workflows.display.editor import NodeDisplayComment
14
14
 
15
15
  if TYPE_CHECKING:
@@ -53,6 +53,37 @@ def _convert_to_relative_module_path(absolute_module_path: str, workflow_root: s
53
53
  return "." + remaining_path
54
54
 
55
55
 
56
+ def _find_workflow_root_with_metadata(trigger_module: str) -> Optional[str]:
57
+ """
58
+ Find the workflow root module by searching for metadata.json up the module hierarchy.
59
+
60
+ Args:
61
+ trigger_module: The trigger's module path (e.g., "workflows.my_workflow.triggers.my_trigger")
62
+
63
+ Returns:
64
+ The workflow root module path if found, None otherwise
65
+ """
66
+ module_parts = trigger_module.split(".")
67
+
68
+ # Try searching up the module hierarchy for metadata.json
69
+ for i in range(len(module_parts), 0, -1):
70
+ potential_root = ".".join(module_parts[:i])
71
+ module_dir = potential_root.replace(".", os.path.sep)
72
+ metadata_path = os.path.join(module_dir, "metadata.json")
73
+
74
+ # Try to open the file using virtual_open to support both regular and virtual filesystems
75
+ # virtual_open checks BaseWorkflowFinder instances before falling back to regular open()
76
+ try:
77
+ file_handle = virtual_open(metadata_path)
78
+ if file_handle is not None:
79
+ file_handle.close()
80
+ return potential_root
81
+ except (FileNotFoundError, OSError):
82
+ pass
83
+
84
+ return None
85
+
86
+
56
87
  def _get_trigger_id_from_metadata(trigger_class: Type["BaseTrigger"]) -> Optional[UUID]:
57
88
  """
58
89
  Get the trigger ID from metadata.json for a given trigger class.
@@ -82,50 +113,56 @@ def _get_trigger_id_from_metadata(trigger_class: Type["BaseTrigger"]) -> Optiona
82
113
  return trigger_path_to_id_mapping.get(relative_trigger_path)
83
114
 
84
115
 
85
- def _find_workflow_root_with_metadata(trigger_module: str) -> Optional[str]:
116
+ @lru_cache(maxsize=128)
117
+ def _get_trigger_path_to_id_mapping(module_path: str) -> Dict[str, UUID]:
86
118
  """
87
- Find the workflow root module by searching for metadata.json up the module hierarchy.
119
+ Read trigger path to ID mapping from metadata.json for a given module.
120
+
121
+ This function is cached to avoid repeated file reads. It searches up the module
122
+ hierarchy for metadata.json and extracts the trigger_path_to_id_mapping.
88
123
 
89
124
  Args:
90
- trigger_module: The trigger's module path (e.g., "workflows.my_workflow.triggers.my_trigger")
125
+ module_path: The module path to search from (e.g., "workflows.my_workflow.triggers.my_trigger")
91
126
 
92
127
  Returns:
93
- The workflow root module path if found, None otherwise
128
+ Dictionary mapping trigger module paths to their UUIDs
94
129
  """
95
- module_parts = trigger_module.split(".")
130
+ try:
131
+ # Find the workflow root that contains metadata.json
132
+ workflow_root = _find_workflow_root_with_metadata(module_path)
133
+ if not workflow_root:
134
+ return {}
96
135
 
97
- # Try searching up the module hierarchy for metadata.json
98
- for i in range(len(module_parts), 0, -1):
99
- potential_root = ".".join(module_parts[:i])
100
- module_dir = potential_root.replace(".", os.path.sep)
136
+ # Convert module path to file path
137
+ module_dir = workflow_root.replace(".", os.path.sep)
101
138
  metadata_path = os.path.join(module_dir, "metadata.json")
102
139
 
103
- # Try to open the file using virtual_open to support both regular and virtual filesystems
104
- # virtual_open checks BaseWorkflowFinder instances before falling back to regular open()
105
- try:
106
- file_handle = virtual_open(metadata_path)
107
- if file_handle is not None:
108
- file_handle.close()
109
- return potential_root
110
- except (FileNotFoundError, OSError):
111
- pass
140
+ # Use virtual_open to support both regular and virtual environments
141
+ with virtual_open(metadata_path) as f:
142
+ metadata_json = json.load(f)
143
+ trigger_mapping = metadata_json.get("trigger_path_to_id_mapping", {})
112
144
 
113
- return None
145
+ # Convert string IDs to UUIDs
146
+ return {path: UUID(id_str) for path, id_str in trigger_mapping.items()}
147
+
148
+ except (FileNotFoundError, json.JSONDecodeError, ValueError, KeyError):
149
+ # If there's any error reading or parsing the file, return empty dict
150
+ return {}
114
151
 
115
152
 
116
153
  @lru_cache(maxsize=128)
117
- def _get_trigger_path_to_id_mapping(module_path: str) -> Dict[str, UUID]:
154
+ def _get_trigger_attribute_id_mapping(module_path: str) -> Dict[str, UUID]:
118
155
  """
119
- Read trigger path to ID mapping from metadata.json for a given module.
156
+ Read trigger attribute ID mapping from metadata.json for a given module.
120
157
 
121
158
  This function is cached to avoid repeated file reads. It searches up the module
122
- hierarchy for metadata.json and extracts the trigger_path_to_id_mapping.
159
+ hierarchy for metadata.json and extracts the trigger_attribute_id_mapping.
123
160
 
124
161
  Args:
125
162
  module_path: The module path to search from (e.g., "workflows.my_workflow.triggers.my_trigger")
126
163
 
127
164
  Returns:
128
- Dictionary mapping trigger module paths to their UUIDs
165
+ Dictionary mapping "<trigger_path>|<attribute_key>" to their UUIDs
129
166
  """
130
167
  try:
131
168
  # Find the workflow root that contains metadata.json
@@ -140,16 +177,55 @@ def _get_trigger_path_to_id_mapping(module_path: str) -> Dict[str, UUID]:
140
177
  # Use virtual_open to support both regular and virtual environments
141
178
  with virtual_open(metadata_path) as f:
142
179
  metadata_json = json.load(f)
143
- trigger_mapping = metadata_json.get("trigger_path_to_id_mapping", {})
180
+ raw_mapping = metadata_json.get("trigger_attribute_id_mapping", {}) or {}
181
+
182
+ # Convert string IDs to UUIDs, skipping invalid entries
183
+ result: Dict[str, UUID] = {}
184
+ for key, id_str in raw_mapping.items():
185
+ try:
186
+ result[key] = UUID(id_str)
187
+ except ValueError:
188
+ # Skip invalid UUID entries; they will fall back to hash-based IDs
189
+ continue
144
190
 
145
- # Convert string IDs to UUIDs
146
- return {path: UUID(id_str) for path, id_str in trigger_mapping.items()}
191
+ return result
147
192
 
148
- except (FileNotFoundError, json.JSONDecodeError, ValueError, KeyError):
193
+ except (FileNotFoundError, json.JSONDecodeError, KeyError):
149
194
  # If there's any error reading or parsing the file, return empty dict
150
195
  return {}
151
196
 
152
197
 
198
+ def _get_trigger_attribute_id_from_metadata(trigger_class: Type["BaseTrigger"], attribute_name: str) -> Optional[UUID]:
199
+ """
200
+ Get the trigger attribute ID from metadata.json for a given trigger class and attribute name.
201
+
202
+ Args:
203
+ trigger_class: The trigger class containing the attribute
204
+ attribute_name: The name of the attribute
205
+
206
+ Returns:
207
+ The UUID from metadata.json, or None if not found
208
+ """
209
+ workflow_root = _find_workflow_root_with_metadata(trigger_class.__module__)
210
+ if not workflow_root:
211
+ return None
212
+
213
+ attribute_mapping = _get_trigger_attribute_id_mapping(trigger_class.__module__)
214
+ if not attribute_mapping:
215
+ return None
216
+
217
+ # Convert module path to relative path and append class name
218
+ # e.g., "root_module.triggers.scheduled" + "ScheduleTrigger" -> ".triggers.scheduled.ScheduleTrigger"
219
+ relative_module_path = _convert_to_relative_module_path(trigger_class.__module__, workflow_root)
220
+ if not relative_module_path:
221
+ return None
222
+
223
+ # Build the key: "<trigger_path>|<attribute_key>"
224
+ relative_trigger_path = f"{relative_module_path}.{trigger_class.__qualname__}"
225
+ key = f"{relative_trigger_path}|{attribute_name}"
226
+ return attribute_mapping.get(key)
227
+
228
+
153
229
  class BaseTriggerMeta(ABCMeta):
154
230
  def __new__(mcs, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]) -> Any:
155
231
  if "Display" not in dct:
@@ -388,4 +464,60 @@ class BaseTrigger(ABC, metaclass=BaseTriggerMeta):
388
464
  def bind_to_state(self, state: "BaseState") -> None:
389
465
  """Persist this trigger's attribute values onto the provided state."""
390
466
 
467
+ if state.meta.trigger_attributes is None:
468
+ state.meta.trigger_attributes = {}
391
469
  state.meta.trigger_attributes.update(self.to_trigger_attribute_values())
470
+
471
+ __trigger_name__: ClassVar[str] = "trigger"
472
+
473
+ @classmethod
474
+ def get_attribute_id(cls, attribute_name: str) -> UUID:
475
+ """
476
+ Get the ID for a trigger attribute, first checking metadata.json for a stable ID,
477
+ then falling back to a deterministic hash-based ID.
478
+
479
+ This ensures trigger attribute IDs remain stable across serialization round-trips
480
+ when metadata.json is present.
481
+
482
+ Args:
483
+ attribute_name: The name of the attribute
484
+
485
+ Returns:
486
+ The UUID for the attribute
487
+ """
488
+ # First try to get the ID from metadata.json
489
+ metadata_id = _get_trigger_attribute_id_from_metadata(cls, attribute_name)
490
+ if metadata_id is not None:
491
+ return metadata_id
492
+
493
+ # Fall back to deterministic hash-based ID
494
+ return get_trigger_attribute_id(cls, attribute_name)
495
+
496
+ class Config:
497
+ """Configuration for trigger behavior. Subclasses can override."""
498
+
499
+ pass
500
+
501
+ def __on_workflow_initiated__(self, state: "BaseState") -> None:
502
+ """
503
+ Lifecycle hook called by WorkflowRunner when workflow execution starts.
504
+
505
+ Subclasses can override to perform state initialization (e.g., appending
506
+ user messages to chat history so nodes can reference them).
507
+
508
+ Args:
509
+ state: The initial workflow state
510
+ """
511
+ pass
512
+
513
+ def __on_workflow_fulfilled__(self, state: "BaseState") -> None:
514
+ """
515
+ Lifecycle hook called by WorkflowRunner before workflow outputs are resolved.
516
+
517
+ Subclasses can override to perform state updates that should be reflected
518
+ in workflow outputs (e.g., appending assistant messages to chat history).
519
+
520
+ Args:
521
+ state: The final workflow state
522
+ """
523
+ pass
@@ -0,0 +1,141 @@
1
+ from typing import TYPE_CHECKING, Any, ClassVar, List, Optional, Union
2
+
3
+ from vellum.client.types import (
4
+ ArrayChatMessageContent,
5
+ ArrayChatMessageContentItem,
6
+ AudioChatMessageContent,
7
+ ChatMessage,
8
+ ChatMessageContent,
9
+ DocumentChatMessageContent,
10
+ FunctionCallChatMessageContent,
11
+ ImageChatMessageContent,
12
+ StringChatMessageContent,
13
+ VideoChatMessageContent,
14
+ )
15
+ from vellum.workflows.descriptors.base import BaseDescriptor
16
+ from vellum.workflows.descriptors.utils import resolve_value
17
+ from vellum.workflows.references.lazy import LazyReference
18
+ from vellum.workflows.references.output import OutputReference
19
+ from vellum.workflows.triggers.base import BaseTrigger
20
+ from vellum.workflows.utils.pydantic_schema import validate_obj_as
21
+
22
+ if TYPE_CHECKING:
23
+ from vellum.workflows.state.base import BaseState
24
+
25
+
26
+ class ChatMessageTrigger(BaseTrigger):
27
+ """
28
+ Trigger for chat-based workflows that supports multi-turn conversations.
29
+
30
+ Appends user message to state.chat_history at workflow start, and assistant
31
+ response after workflow completion. Use previous_execution_id to maintain
32
+ conversation state across executions.
33
+
34
+ Attributes:
35
+ message: The incoming chat message content. Can be a string or a list of content items.
36
+ """
37
+
38
+ message: Union[str, List[ArrayChatMessageContentItem]]
39
+
40
+ class Config(BaseTrigger.Config):
41
+ output: Optional[BaseDescriptor[Any]] = None
42
+
43
+ def __init__(self, **kwargs: Any):
44
+ """Initialize ChatMessageTrigger, converting VellumValue objects to ChatMessageContent if needed."""
45
+ # Convert message from VellumValue format to ChatMessageContent format if needed
46
+ if "message" in kwargs:
47
+ message = kwargs["message"]
48
+ # Handle string messages by converting to a list with a single StringChatMessageContent
49
+ if isinstance(message, str):
50
+ kwargs["message"] = [StringChatMessageContent(value=message)]
51
+ elif isinstance(message, list):
52
+ converted_message = []
53
+ for item in message:
54
+ # If it's already a ChatMessageContent type, keep it as-is
55
+ if isinstance(
56
+ item,
57
+ (
58
+ StringChatMessageContent,
59
+ ImageChatMessageContent,
60
+ AudioChatMessageContent,
61
+ VideoChatMessageContent,
62
+ DocumentChatMessageContent,
63
+ FunctionCallChatMessageContent,
64
+ ),
65
+ ):
66
+ converted_message.append(item)
67
+ # Handle raw strings in the array by wrapping them in StringChatMessageContent
68
+ elif isinstance(item, str):
69
+ converted_message.append(StringChatMessageContent(value=item))
70
+ # Convert VellumValue objects or dicts to ChatMessageContent
71
+ # Use discriminated union validation
72
+ else:
73
+ # Get the dict representation (either from Pydantic model or already a dict)
74
+ item_dict = item.model_dump() if hasattr(item, "model_dump") else item
75
+ converted_message.append(
76
+ validate_obj_as(ArrayChatMessageContentItem, item_dict) # type: ignore[arg-type]
77
+ )
78
+
79
+ kwargs["message"] = converted_message
80
+
81
+ super().__init__(**kwargs)
82
+
83
+ def __on_workflow_initiated__(self, state: "BaseState") -> None:
84
+ """Appends user message to state.chat_history at workflow start."""
85
+ if not hasattr(state, "chat_history"):
86
+ return
87
+
88
+ if state.chat_history is None:
89
+ state.chat_history = []
90
+
91
+ if isinstance(self.message, str):
92
+ user_message = ChatMessage(
93
+ role="USER",
94
+ text=self.message,
95
+ )
96
+ else:
97
+ user_message = ChatMessage(
98
+ role="USER",
99
+ content=ArrayChatMessageContent(value=self.message),
100
+ )
101
+ state.chat_history.append(user_message)
102
+
103
+ def __on_workflow_fulfilled__(self, state: "BaseState") -> None:
104
+ """Appends assistant response to state.chat_history after workflow completion."""
105
+ if not hasattr(state, "chat_history"):
106
+ return
107
+
108
+ if state.chat_history is None:
109
+ state.chat_history = []
110
+
111
+ output = self.Config.output
112
+ if output is not None:
113
+ resolved_output = self._resolve_output(output, state)
114
+ assistant_message = ChatMessage(
115
+ role="ASSISTANT",
116
+ content=resolved_output if not isinstance(resolved_output, str) else None,
117
+ text=resolved_output if isinstance(resolved_output, str) else None,
118
+ )
119
+ state.chat_history.append(assistant_message)
120
+
121
+ def _resolve_output(
122
+ self,
123
+ output: BaseDescriptor[Any],
124
+ state: "BaseState",
125
+ ) -> Union[str, ChatMessageContent]:
126
+ """Resolves output reference, handling workflow output references."""
127
+ descriptor = output
128
+ if isinstance(output, LazyReference) and callable(output._get):
129
+ descriptor = output._get()
130
+
131
+ if isinstance(descriptor, OutputReference) and isinstance(descriptor.instance, BaseDescriptor):
132
+ return resolve_value(descriptor.instance, state)
133
+
134
+ return resolve_value(output, state)
135
+
136
+ __trigger_name__: ClassVar[str] = "chat"
137
+
138
+ class Display(BaseTrigger.Display):
139
+ label: str = "Chat Message"
140
+ icon: Optional[str] = "vellum:icon:message-dots"
141
+ color: Optional[str] = "blue"
@@ -13,6 +13,8 @@ class IntegrationTriggerMeta(BaseTriggerMeta):
13
13
  This metaclass extends BaseTriggerMeta to automatically convert type annotations
14
14
  into TriggerAttributeReference objects during class creation. This enables trigger
15
15
  attributes to be referenced in workflow graphs while maintaining type safety.
16
+
17
+ It also sets __trigger_name__ based on Config.slug for trigger name resolution.
16
18
  """
17
19
 
18
20
  def __new__(mcs, name: str, bases: tuple, namespace: dict, **kwargs: Any) -> "IntegrationTriggerMeta":
@@ -36,6 +38,16 @@ class IntegrationTriggerMeta(BaseTriggerMeta):
36
38
  # Set as class attribute so it's directly accessible
37
39
  setattr(cls, attr_name, reference)
38
40
 
41
+ # Set __trigger_name__ based on Config.slug if available
42
+ if has_config:
43
+ config_class = namespace.get("Config")
44
+ if config_class is not None:
45
+ slug = getattr(config_class, "slug", None)
46
+ if slug:
47
+ cls.__trigger_name__ = str(slug)
48
+ else:
49
+ cls.__trigger_name__ = "integration"
50
+
39
51
  return cls
40
52
 
41
53
 
@@ -1,3 +1,5 @@
1
+ from typing import ClassVar
2
+
1
3
  from vellum.workflows.triggers.base import BaseTrigger
2
4
 
3
5
 
@@ -34,4 +36,4 @@ class ManualTrigger(BaseTrigger):
34
36
  - ManualTrigger: Executes when explicitly called
35
37
  """
36
38
 
37
- pass
39
+ __trigger_name__: ClassVar[str] = "manual"
@@ -1,5 +1,5 @@
1
1
  from datetime import datetime
2
- from typing import Optional
2
+ from typing import ClassVar, Optional
3
3
 
4
4
  from vellum.workflows.triggers.base import BaseTrigger
5
5
 
@@ -16,3 +16,5 @@ class ScheduleTrigger(BaseTrigger):
16
16
  class Config:
17
17
  cron: str
18
18
  timezone: Optional[str] = None
19
+
20
+ __trigger_name__: ClassVar[str] = "scheduled"