vellum-ai 1.2.1__py3-none-any.whl → 1.2.3__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.
Files changed (128) hide show
  1. vellum/__init__.py +40 -0
  2. vellum/client/core/client_wrapper.py +2 -2
  3. vellum/client/core/pydantic_utilities.py +3 -2
  4. vellum/client/reference.md +16 -0
  5. vellum/client/resources/workflow_executions/client.py +28 -4
  6. vellum/client/resources/workflow_executions/raw_client.py +32 -2
  7. vellum/client/types/__init__.py +40 -0
  8. vellum/client/types/audio_input_request.py +30 -0
  9. vellum/client/types/delimiter_chunker_config.py +20 -0
  10. vellum/client/types/delimiter_chunker_config_request.py +20 -0
  11. vellum/client/types/delimiter_chunking.py +21 -0
  12. vellum/client/types/delimiter_chunking_request.py +21 -0
  13. vellum/client/types/document_index_chunking.py +4 -1
  14. vellum/client/types/document_index_chunking_request.py +2 -1
  15. vellum/client/types/document_input_request.py +30 -0
  16. vellum/client/types/execution_audio_vellum_value.py +31 -0
  17. vellum/client/types/execution_document_vellum_value.py +31 -0
  18. vellum/client/types/execution_image_vellum_value.py +31 -0
  19. vellum/client/types/execution_vellum_value.py +8 -0
  20. vellum/client/types/execution_video_vellum_value.py +31 -0
  21. vellum/client/types/image_input_request.py +30 -0
  22. vellum/client/types/logical_operator.py +1 -0
  23. vellum/client/types/node_input_compiled_audio_value.py +23 -0
  24. vellum/client/types/node_input_compiled_document_value.py +23 -0
  25. vellum/client/types/node_input_compiled_image_value.py +23 -0
  26. vellum/client/types/node_input_compiled_video_value.py +23 -0
  27. vellum/client/types/node_input_variable_compiled_value.py +8 -0
  28. vellum/client/types/prompt_deployment_input_request.py +13 -1
  29. vellum/client/types/prompt_request_audio_input.py +26 -0
  30. vellum/client/types/prompt_request_document_input.py +26 -0
  31. vellum/client/types/prompt_request_image_input.py +26 -0
  32. vellum/client/types/prompt_request_input.py +13 -1
  33. vellum/client/types/prompt_request_video_input.py +26 -0
  34. vellum/client/types/video_input_request.py +30 -0
  35. vellum/prompts/blocks/compilation.py +13 -11
  36. vellum/types/audio_input_request.py +3 -0
  37. vellum/types/delimiter_chunker_config.py +3 -0
  38. vellum/types/delimiter_chunker_config_request.py +3 -0
  39. vellum/types/delimiter_chunking.py +3 -0
  40. vellum/types/delimiter_chunking_request.py +3 -0
  41. vellum/types/document_input_request.py +3 -0
  42. vellum/types/execution_audio_vellum_value.py +3 -0
  43. vellum/types/execution_document_vellum_value.py +3 -0
  44. vellum/types/execution_image_vellum_value.py +3 -0
  45. vellum/types/execution_video_vellum_value.py +3 -0
  46. vellum/types/image_input_request.py +3 -0
  47. vellum/types/node_input_compiled_audio_value.py +3 -0
  48. vellum/types/node_input_compiled_document_value.py +3 -0
  49. vellum/types/node_input_compiled_image_value.py +3 -0
  50. vellum/types/node_input_compiled_video_value.py +3 -0
  51. vellum/types/prompt_request_audio_input.py +3 -0
  52. vellum/types/prompt_request_document_input.py +3 -0
  53. vellum/types/prompt_request_image_input.py +3 -0
  54. vellum/types/prompt_request_video_input.py +3 -0
  55. vellum/types/video_input_request.py +3 -0
  56. vellum/workflows/context.py +27 -9
  57. vellum/workflows/emitters/vellum_emitter.py +16 -69
  58. vellum/workflows/events/context.py +53 -78
  59. vellum/workflows/events/node.py +5 -5
  60. vellum/workflows/events/relational_threads.py +41 -0
  61. vellum/workflows/events/tests/test_basic_workflow.py +50 -0
  62. vellum/workflows/events/tests/test_event.py +1 -0
  63. vellum/workflows/events/workflow.py +15 -1
  64. vellum/workflows/expressions/contains.py +7 -0
  65. vellum/workflows/expressions/tests/test_contains.py +175 -0
  66. vellum/workflows/graph/graph.py +52 -8
  67. vellum/workflows/graph/tests/test_graph.py +17 -0
  68. vellum/workflows/integrations/mcp_service.py +35 -5
  69. vellum/workflows/integrations/tests/test_mcp_service.py +81 -0
  70. vellum/workflows/nodes/bases/base.py +0 -1
  71. vellum/workflows/nodes/core/error_node/node.py +4 -0
  72. vellum/workflows/nodes/core/inline_subworkflow_node/tests/test_node.py +35 -0
  73. vellum/workflows/nodes/core/map_node/node.py +7 -0
  74. vellum/workflows/nodes/core/map_node/tests/test_node.py +19 -0
  75. vellum/workflows/nodes/displayable/bases/utils.py +4 -2
  76. vellum/workflows/nodes/displayable/final_output_node/node.py +4 -0
  77. vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +88 -2
  78. vellum/workflows/nodes/displayable/tool_calling_node/node.py +1 -0
  79. vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py +85 -1
  80. vellum/workflows/nodes/displayable/tool_calling_node/tests/test_utils.py +12 -0
  81. vellum/workflows/nodes/displayable/tool_calling_node/utils.py +5 -2
  82. vellum/workflows/ports/node_ports.py +3 -0
  83. vellum/workflows/ports/port.py +8 -11
  84. vellum/workflows/state/context.py +47 -2
  85. vellum/workflows/types/definition.py +4 -4
  86. vellum/workflows/utils/uuids.py +15 -0
  87. vellum/workflows/utils/vellum_variables.py +5 -3
  88. vellum/workflows/workflows/base.py +1 -0
  89. {vellum_ai-1.2.1.dist-info → vellum_ai-1.2.3.dist-info}/METADATA +1 -1
  90. {vellum_ai-1.2.1.dist-info → vellum_ai-1.2.3.dist-info}/RECORD +128 -82
  91. vellum_ee/workflows/display/nodes/base_node_display.py +19 -10
  92. vellum_ee/workflows/display/nodes/vellum/api_node.py +1 -4
  93. vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +1 -4
  94. vellum_ee/workflows/display/nodes/vellum/conditional_node.py +1 -4
  95. vellum_ee/workflows/display/nodes/vellum/error_node.py +1 -3
  96. vellum_ee/workflows/display/nodes/vellum/final_output_node.py +1 -3
  97. vellum_ee/workflows/display/nodes/vellum/guardrail_node.py +1 -4
  98. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +1 -8
  99. vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +1 -4
  100. vellum_ee/workflows/display/nodes/vellum/map_node.py +1 -4
  101. vellum_ee/workflows/display/nodes/vellum/merge_node.py +1 -4
  102. vellum_ee/workflows/display/nodes/vellum/note_node.py +2 -4
  103. vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +1 -4
  104. vellum_ee/workflows/display/nodes/vellum/search_node.py +1 -4
  105. vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +1 -4
  106. vellum_ee/workflows/display/nodes/vellum/templating_node.py +1 -4
  107. vellum_ee/workflows/display/nodes/vellum/tests/test_code_execution_node.py +1 -0
  108. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py +4 -0
  109. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py +12 -0
  110. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py +16 -0
  111. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py +5 -0
  112. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py +4 -0
  113. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +4 -0
  114. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +4 -0
  115. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py +4 -0
  116. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_prompt_deployment_serialization.py +12 -0
  117. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_serialization.py +4 -0
  118. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_deployment_serialization.py +4 -0
  119. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_node_serialization.py +4 -0
  120. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py +5 -0
  121. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_composio_serialization.py +1 -0
  122. vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py +5 -0
  123. vellum_ee/workflows/display/utils/events.py +24 -0
  124. vellum_ee/workflows/display/utils/tests/test_events.py +69 -0
  125. vellum_ee/workflows/tests/test_server.py +95 -0
  126. {vellum_ai-1.2.1.dist-info → vellum_ai-1.2.3.dist-info}/LICENSE +0 -0
  127. {vellum_ai-1.2.1.dist-info → vellum_ai-1.2.3.dist-info}/WHEEL +0 -0
  128. {vellum_ai-1.2.1.dist-info → vellum_ai-1.2.3.dist-info}/entry_points.txt +0 -0
@@ -183,8 +183,6 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
183
183
  existing_adornments = adornments if adornments is not None else []
184
184
  return display_class().serialize(display_context, adornments=existing_adornments + [adornment])
185
185
 
186
- ports = self.serialize_ports(display_context)
187
-
188
186
  outputs: JsonArray = []
189
187
  for output in node.Outputs:
190
188
  type = primitive_type_to_vellum_variable_type(output)
@@ -207,14 +205,7 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
207
205
  "id": str(node_id),
208
206
  "label": self.label,
209
207
  "type": "GENERIC",
210
- "display_data": self.get_display_data().dict(),
211
- "base": self.get_base().dict(),
212
- "definition": self.get_definition().dict(),
213
- "trigger": {
214
- "id": str(self.get_target_handle_id()),
215
- "merge_behavior": node.Trigger.merge_behavior.value,
216
- },
217
- "ports": ports,
208
+ **self.serialize_generic_fields(display_context),
218
209
  "adornments": adornments,
219
210
  "attributes": attributes,
220
211
  "outputs": outputs,
@@ -247,6 +238,14 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
247
238
 
248
239
  return ports
249
240
 
241
+ def serialize_trigger(self) -> JsonObject:
242
+ """Serialize the trigger of the node."""
243
+ node = self._node
244
+ return {
245
+ "id": str(self.get_target_handle_id()),
246
+ "merge_behavior": node.Trigger.merge_behavior.value,
247
+ }
248
+
250
249
  def _serialize_attributes(self, display_context: "WorkflowDisplayContext") -> JsonArray:
251
250
  """Serialize node attributes, skipping unserializable ones."""
252
251
  attributes: JsonArray = []
@@ -271,6 +270,16 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
271
270
 
272
271
  return attributes
273
272
 
273
+ def serialize_generic_fields(self, display_context: "WorkflowDisplayContext") -> JsonObject:
274
+ """Serialize generic fields that are common to all nodes."""
275
+ return {
276
+ "display_data": self.get_display_data().dict(),
277
+ "base": self.get_base().dict(),
278
+ "definition": self.get_definition().dict(),
279
+ "trigger": self.serialize_trigger(),
280
+ "ports": self.serialize_ports(display_context),
281
+ }
282
+
274
283
  def get_base(self) -> CodeResourceDefinition:
275
284
  node = self._node
276
285
 
@@ -213,10 +213,7 @@ class BaseAPINodeDisplay(BaseNodeDisplay[_APINodeType], Generic[_APINodeType]):
213
213
  "json_output_id": str(json_output_display.id),
214
214
  "status_code_output_id": str(status_code_output_display.id),
215
215
  },
216
- "display_data": self.get_display_data().dict(),
217
- "base": self.get_base().dict(),
218
- "definition": self.get_definition().dict(),
219
- "ports": self.serialize_ports(display_context),
216
+ **self.serialize_generic_fields(display_context),
220
217
  }
221
218
 
222
219
  attributes = self._serialize_attributes(display_context)
@@ -103,8 +103,5 @@ class BaseCodeExecutionNodeDisplay(BaseNodeDisplay[_CodeExecutionNodeType], Gene
103
103
  "output_id": str(self.output_id) if self.output_id else str(output_display.id),
104
104
  "log_output_id": str(self.log_output_id) if self.log_output_id else str(log_output_display.id),
105
105
  },
106
- "display_data": self.get_display_data().dict(),
107
- "base": self.get_base().dict(),
108
- "definition": self.get_definition().dict(),
109
- "ports": self.serialize_ports(display_context),
106
+ **self.serialize_generic_fields(display_context),
110
107
  }
@@ -217,10 +217,7 @@ but the defined conditions have length {len(condition_ids)}"""
217
217
  "conditions": conditions, # type: ignore
218
218
  "version": "2",
219
219
  },
220
- "display_data": self.get_display_data().dict(),
221
- "base": self.get_base().dict(),
222
- "definition": self.get_definition().dict(),
223
- "ports": self.serialize_ports(display_context),
220
+ **self.serialize_generic_fields(display_context),
224
221
  }
225
222
 
226
223
  def get_nested_rule_details_by_path(
@@ -47,9 +47,7 @@ class BaseErrorNodeDisplay(BaseNodeDisplay[_ErrorNodeType], Generic[_ErrorNodeTy
47
47
  "target_handle_id": str(self.get_target_handle_id()),
48
48
  "error_source_input_id": str(error_source_input_id),
49
49
  },
50
- "display_data": self.get_display_data().dict(),
51
- "base": self.get_base().dict(),
52
- "definition": self.get_definition().dict(),
50
+ **self.serialize_generic_fields(display_context),
53
51
  }
54
52
 
55
53
  if self.name:
@@ -45,9 +45,7 @@ class BaseFinalOutputNodeDisplay(BaseNodeDisplay[_FinalOutputNodeType], Generic[
45
45
  "node_input_id": str(node_input.id),
46
46
  },
47
47
  "inputs": [node_input.dict()],
48
- "display_data": self.get_display_data().dict(),
49
- "base": self.get_base().dict(),
50
- "definition": self.get_definition().dict(),
48
+ **self.serialize_generic_fields(display_context),
51
49
  "outputs": [
52
50
  {
53
51
  "id": str(self._get_output_id()),
@@ -45,8 +45,5 @@ class BaseGuardrailNodeDisplay(BaseNodeDisplay[_GuardrailNodeType], Generic[_Gua
45
45
  "metric_definition_id": str(raise_if_descriptor(node.metric_definition)),
46
46
  "release_tag": raise_if_descriptor(node.release_tag),
47
47
  },
48
- "display_data": self.get_display_data().dict(),
49
- "base": self.get_base().dict(),
50
- "definition": self.get_definition().dict(),
51
- "ports": self.serialize_ports(display_context),
48
+ **self.serialize_generic_fields(display_context),
52
49
  }
@@ -109,19 +109,12 @@ class BaseInlinePromptNodeDisplay(BaseNodeDisplay[_InlinePromptNodeType], Generi
109
109
  },
110
110
  "ml_model_name": ml_model,
111
111
  },
112
- "display_data": self.get_display_data().dict(),
113
- "base": self.get_base().dict(),
114
- "definition": self.get_definition().dict(),
115
- "trigger": {
116
- "id": str(self.get_target_handle_id()),
117
- "merge_behavior": node.Trigger.merge_behavior.value,
118
- },
112
+ **self.serialize_generic_fields(display_context),
119
113
  "outputs": [
120
114
  {"id": str(json_display.id), "name": "json", "type": "JSON", "value": None},
121
115
  {"id": str(output_display.id), "name": "text", "type": "STRING", "value": None},
122
116
  {"id": str(array_display.id), "name": "results", "type": "ARRAY", "value": None},
123
117
  ],
124
- "ports": self.serialize_ports(display_context),
125
118
  }
126
119
  attributes = self._serialize_attributes(display_context)
127
120
  if attributes:
@@ -55,10 +55,7 @@ class BaseInlineSubworkflowNodeDisplay(
55
55
  "input_variables": [workflow_input.dict() for workflow_input in workflow_inputs],
56
56
  "output_variables": [workflow_output.dict() for workflow_output in workflow_outputs],
57
57
  },
58
- "display_data": self.get_display_data().dict(),
59
- "base": self.get_base().dict(),
60
- "definition": self.get_definition().dict(),
61
- "ports": self.serialize_ports(display_context),
58
+ **self.serialize_generic_fields(display_context),
62
59
  }
63
60
 
64
61
  def _generate_node_and_workflow_inputs(
@@ -79,8 +79,5 @@ class BaseMapNodeDisplay(BaseAdornmentNodeDisplay[_MapNodeType], Generic[_MapNod
79
79
  "item_input_id": item_workflow_input_id,
80
80
  "index_input_id": index_workflow_input_id,
81
81
  },
82
- "display_data": self.get_display_data().dict(),
83
- "base": self.get_base().dict(),
84
- "definition": self.get_definition().dict(),
85
- "ports": self.serialize_ports(display_context),
82
+ **self.serialize_generic_fields(display_context),
86
83
  }
@@ -47,10 +47,7 @@ class BaseMergeNodeDisplay(BaseNodeDisplay[_MergeNodeType], Generic[_MergeNodeTy
47
47
  "target_handles": [{"id": str(target_handle_id)} for target_handle_id in target_handle_ids],
48
48
  "source_handle_id": str(self.get_source_handle_id(display_context.port_displays)),
49
49
  },
50
- "display_data": self.get_display_data().dict(),
51
- "base": self.get_base().dict(),
52
- "definition": self.get_definition().dict(),
53
- "ports": self.serialize_ports(display_context),
50
+ **self.serialize_generic_fields(display_context),
54
51
  }
55
52
 
56
53
  def get_target_handle_ids(self) -> Optional[List[UUID]]:
@@ -13,7 +13,7 @@ class BaseNoteNodeDisplay(BaseNodeDisplay[_NoteNodeType], Generic[_NoteNodeType]
13
13
  style: ClassVar[Union[Dict[str, Any], None]] = None
14
14
 
15
15
  def serialize(self, display_context: WorkflowDisplayContext, **kwargs: Any) -> JsonObject:
16
- del display_context, kwargs # Unused parameters
16
+ del kwargs # Unused parameters
17
17
  node_id = self.node_id
18
18
 
19
19
  return {
@@ -25,7 +25,5 @@ class BaseNoteNodeDisplay(BaseNodeDisplay[_NoteNodeType], Generic[_NoteNodeType]
25
25
  "text": self.text,
26
26
  "style": self.style,
27
27
  },
28
- "display_data": self.get_display_data().dict(),
29
- "base": self.get_base().dict(),
30
- "definition": self.get_definition().dict(),
28
+ **self.serialize_generic_fields(display_context),
31
29
  }
@@ -64,10 +64,7 @@ class BasePromptDeploymentNodeDisplay(BaseNodeDisplay[_PromptDeploymentNodeType]
64
64
  "release_tag": raise_if_descriptor(node.release_tag),
65
65
  "ml_model_fallbacks": list(ml_model_fallbacks) if ml_model_fallbacks else None,
66
66
  },
67
- "display_data": self.get_display_data().dict(),
68
- "base": self.get_base().dict(),
69
- "definition": self.get_definition().dict(),
70
- "ports": self.serialize_ports(display_context),
67
+ **self.serialize_generic_fields(display_context),
71
68
  "outputs": [
72
69
  {"id": str(json_display.id), "name": "json", "type": "JSON", "value": None},
73
70
  {"id": str(output_display.id), "name": "text", "type": "STRING", "value": None},
@@ -74,10 +74,7 @@ class BaseSearchNodeDisplay(BaseNodeDisplay[_SearchNodeType], Generic[_SearchNod
74
74
  "external_id_filters_node_input_id": str(node_inputs["external_id_filters"].id),
75
75
  "metadata_filters_node_input_id": str(node_inputs["metadata_filters"].id),
76
76
  },
77
- "display_data": self.get_display_data().dict(),
78
- "base": self.get_base().dict(),
79
- "definition": self.get_definition().dict(),
80
- "ports": self.serialize_ports(display_context),
77
+ **self.serialize_generic_fields(display_context),
81
78
  }
82
79
 
83
80
  def _generate_search_node_inputs(
@@ -61,8 +61,5 @@ class BaseSubworkflowDeploymentNodeDisplay(
61
61
  "workflow_deployment_id": str(deployment.id),
62
62
  "release_tag": raise_if_descriptor(node.release_tag),
63
63
  },
64
- "display_data": self.get_display_data().dict(),
65
- "base": self.get_base().dict(),
66
- "definition": self.get_definition().dict(),
67
- "ports": self.serialize_ports(display_context),
64
+ **self.serialize_generic_fields(display_context),
68
65
  }
@@ -66,8 +66,5 @@ class BaseTemplatingNodeDisplay(BaseNodeDisplay[_TemplatingNodeType], Generic[_T
66
66
  "template_node_input_id": str(template_node_input.id),
67
67
  "output_type": inferred_output_type,
68
68
  },
69
- "display_data": self.get_display_data().dict(),
70
- "base": self.get_base().dict(),
71
- "definition": self.get_definition().dict(),
72
- "ports": self.serialize_ports(display_context),
69
+ **self.serialize_generic_fields(display_context),
73
70
  }
@@ -251,6 +251,7 @@ def test_serialize_node__with_non_exist_code_input_path_with_dry_run():
251
251
  ],
252
252
  },
253
253
  "ports": [{"id": "7afa3858-f50c-4116-936a-a401e3b2c60f", "name": "default", "type": "DEFAULT"}],
254
+ "trigger": {"id": "3a39ea63-9f86-4891-a902-0216a7190720", "merge_behavior": "AWAIT_ANY"},
254
255
  },
255
256
  ],
256
257
  "edges": [
@@ -203,6 +203,10 @@ def test_serialize_workflow(vellum_client):
203
203
  "name": "SimpleAPINode",
204
204
  "module": ["tests", "workflows", "basic_api_node", "workflow"],
205
205
  },
206
+ "trigger": {
207
+ "id": "14b538a5-aedb-41f3-b579-039956b7c7ed",
208
+ "merge_behavior": "AWAIT_ANY",
209
+ },
206
210
  "ports": [{"id": "7c33b4d3-9204-4bd5-9371-80ee34f83073", "name": "default", "type": "DEFAULT"}],
207
211
  },
208
212
  api_node,
@@ -110,6 +110,10 @@ def test_serialize_workflow_with_filepath():
110
110
  "module": ["tests", "workflows", "basic_code_execution_node", "workflow"],
111
111
  "name": "SimpleCodeExecutionNode",
112
112
  },
113
+ "trigger": {
114
+ "id": "e02a2701-22c0-4533-8b00-175998e7350a",
115
+ "merge_behavior": "AWAIT_ANY",
116
+ },
113
117
  "ports": [{"id": "832f81ec-427b-42a8-825c-e62c43c1f961", "name": "default", "type": "DEFAULT"}],
114
118
  }
115
119
  assert not DeepDiff(
@@ -342,6 +346,10 @@ def test_serialize_workflow_with_code():
342
346
  "name": "SimpleCodeExecutionNode",
343
347
  "module": ["tests", "workflows", "basic_code_execution_node", "workflow_with_code"],
344
348
  },
349
+ "trigger": {
350
+ "id": "e02a2701-22c0-4533-8b00-175998e7350a",
351
+ "merge_behavior": "AWAIT_ANY",
352
+ },
345
353
  "ports": [{"id": "832f81ec-427b-42a8-825c-e62c43c1f961", "name": "default", "type": "DEFAULT"}],
346
354
  }
347
355
  assert not DeepDiff(
@@ -602,6 +610,10 @@ def test_serialize_workflow__try_wrapped():
602
610
  ],
603
611
  }
604
612
  ],
613
+ "trigger": {
614
+ "id": "e02a2701-22c0-4533-8b00-175998e7350a",
615
+ "merge_behavior": "AWAIT_ANY",
616
+ },
605
617
  "ports": [{"id": "832f81ec-427b-42a8-825c-e62c43c1f961", "name": "default", "type": "DEFAULT"}],
606
618
  }
607
619
 
@@ -424,6 +424,10 @@ def test_serialize_workflow():
424
424
  "name": "CategoryConditionalNode",
425
425
  "module": ["tests", "workflows", "basic_conditional_node", "workflow"],
426
426
  },
427
+ "trigger": {
428
+ "id": "dd89e228-a23e-422b-80b2-34362c1c050e",
429
+ "merge_behavior": "AWAIT_ANY",
430
+ },
427
431
  "ports": [
428
432
  {
429
433
  "id": "3a45b81f-95e4-4cbd-8997-bfdbe30251e8",
@@ -952,6 +956,10 @@ def test_conditional_node_serialize_all_operators_with_lhs_and_rhs(descriptor, o
952
956
  "name": "SimpleConditionalNode",
953
957
  "module": ["tests", "workflows", "basic_conditional_node", "workflow_with_only_one_conditional_node"],
954
958
  },
959
+ "trigger": {
960
+ "id": "c6e99e94-bc8e-47a4-b75c-cc96c6bedbb0",
961
+ "merge_behavior": "AWAIT_ANY",
962
+ },
955
963
  "ports": [
956
964
  {
957
965
  "id": "2ff87aa6-37cf-43dd-af9d-13b9198ab70a",
@@ -1063,6 +1071,10 @@ def test_conditional_node_serialize_all_operators_with_expression(descriptor, op
1063
1071
  "name": "SimpleConditionalNode",
1064
1072
  "module": ["tests", "workflows", "basic_conditional_node", "workflow_with_only_one_conditional_node"],
1065
1073
  },
1074
+ "trigger": {
1075
+ "id": "c6e99e94-bc8e-47a4-b75c-cc96c6bedbb0",
1076
+ "merge_behavior": "AWAIT_ANY",
1077
+ },
1066
1078
  "ports": [
1067
1079
  {
1068
1080
  "id": "2ff87aa6-37cf-43dd-af9d-13b9198ab70a",
@@ -1186,6 +1198,10 @@ def test_conditional_node_serialize_all_operators_with_value_and_start_and_end(d
1186
1198
  "name": "SimpleConditionalNode",
1187
1199
  "module": ["tests", "workflows", "basic_conditional_node", "workflow_with_only_one_conditional_node"],
1188
1200
  },
1201
+ "trigger": {
1202
+ "id": "c6e99e94-bc8e-47a4-b75c-cc96c6bedbb0",
1203
+ "merge_behavior": "AWAIT_ANY",
1204
+ },
1189
1205
  "ports": [
1190
1206
  {
1191
1207
  "id": "2ff87aa6-37cf-43dd-af9d-13b9198ab70a",
@@ -114,6 +114,11 @@ def test_serialize_workflow():
114
114
  "name": "FailNode",
115
115
  "module": ["tests", "workflows", "basic_error_node", "workflow"],
116
116
  },
117
+ "trigger": {
118
+ "id": "70c19f1c-309c-4a5d-ba65-664c0bb2fedf",
119
+ "merge_behavior": "AWAIT_ATTRIBUTES",
120
+ },
121
+ "ports": [],
117
122
  },
118
123
  error_node,
119
124
  ignore_order=True,
@@ -117,6 +117,10 @@ def test_serialize_workflow():
117
117
  "module": ["tests", "workflows", "basic_guardrail_node", "workflow"],
118
118
  "name": "ExampleGuardrailNode",
119
119
  },
120
+ "trigger": {
121
+ "id": "ce5b85b1-eded-46dd-b4b7-020afcdc67ab",
122
+ "merge_behavior": "AWAIT_ANY",
123
+ },
120
124
  "ports": [{"id": "0ed87407-697e-4ae9-ab9b-6c5cc2e57cf7", "name": "default", "type": "DEFAULT"}],
121
125
  }
122
126
 
@@ -324,6 +324,10 @@ def test_serialize_workflow():
324
324
  "name": "ExampleInlineSubworkflowNode",
325
325
  "module": ["tests", "workflows", "basic_inline_subworkflow", "workflow"],
326
326
  },
327
+ "trigger": {
328
+ "id": "859a75a6-1bd2-4350-9509-4af66245e8e4",
329
+ "merge_behavior": "AWAIT_ATTRIBUTES",
330
+ },
327
331
  "ports": [{"id": "cfd831bc-ee7f-44d0-8d76-0ba0cd0277dc", "name": "default", "type": "DEFAULT"}],
328
332
  },
329
333
  subworkflow_node,
@@ -286,6 +286,10 @@ def test_serialize_workflow():
286
286
  "name": "MapFruitsNode",
287
287
  "module": ["tests", "workflows", "basic_map_node", "workflow"],
288
288
  },
289
+ "trigger": {
290
+ "id": "b5e8182e-20c5-482b-b4c5-4dde48c01472",
291
+ "merge_behavior": "AWAIT_ATTRIBUTES",
292
+ },
289
293
  "ports": [{"id": "a2171a61-0657-43ad-b6d9-cf93ce3270d0", "name": "default", "type": "DEFAULT"}],
290
294
  },
291
295
  map_node,
@@ -83,6 +83,10 @@ def test_serialize_workflow__await_all():
83
83
  "module": ["tests", "workflows", "basic_merge_node", "await_all_workflow"],
84
84
  "name": "AwaitAllMergeNode",
85
85
  },
86
+ "trigger": {
87
+ "id": "0efd256f-f5f6-45fe-9adb-651780f5e63d",
88
+ "merge_behavior": "AWAIT_ALL",
89
+ },
86
90
  "ports": [{"id": "3bbc469f-0fb0-4b3d-a28b-746fefec2818", "name": "default", "type": "DEFAULT"}],
87
91
  },
88
92
  merge_node,
@@ -150,6 +150,10 @@ def test_serialize_workflow(vellum_client):
150
150
  "name": "ExamplePromptDeploymentNode",
151
151
  "module": ["tests", "workflows", "basic_text_prompt_deployment", "workflow"],
152
152
  },
153
+ "trigger": {
154
+ "id": "b7605c48-0937-4ecc-914e-0d1058130e65",
155
+ "merge_behavior": "AWAIT_ANY",
156
+ },
153
157
  "ports": [{"id": "2f26c7e0-283d-4f04-b639-adebb56bc679", "name": "default", "type": "DEFAULT"}],
154
158
  "outputs": [
155
159
  {"id": "180355a8-e67c-4ce6-9ac3-e5dbb75a6629", "name": "json", "type": "JSON", "value": None},
@@ -384,6 +388,10 @@ def test_serialize_workflow_with_prompt_and_templating(vellum_client):
384
388
  "workflow_with_prompt_deployment_json_reference",
385
389
  ],
386
390
  },
391
+ "trigger": {
392
+ "id": "b7605c48-0937-4ecc-914e-0d1058130e65",
393
+ "merge_behavior": "AWAIT_ANY",
394
+ },
387
395
  "ports": [{"id": "2f26c7e0-283d-4f04-b639-adebb56bc679", "name": "default", "type": "DEFAULT"}],
388
396
  "outputs": [
389
397
  {"id": "180355a8-e67c-4ce6-9ac3-e5dbb75a6629", "name": "json", "type": "JSON", "value": None},
@@ -479,6 +487,10 @@ def test_serialize_workflow_with_prompt_and_templating(vellum_client):
479
487
  "workflow_with_prompt_deployment_json_reference",
480
488
  ],
481
489
  },
490
+ "trigger": {
491
+ "id": "58427684-3848-498a-8299-c6b0fc70265d",
492
+ "merge_behavior": "AWAIT_ATTRIBUTES",
493
+ },
482
494
  "ports": [{"id": "39317827-df43-4f5a-bfbc-20bffc839748", "name": "default", "type": "DEFAULT"}],
483
495
  }
484
496
 
@@ -238,6 +238,10 @@ def test_serialize_workflow():
238
238
  "name": "SimpleSearchNode",
239
239
  "module": ["tests", "workflows", "basic_search_node", "workflow"],
240
240
  },
241
+ "trigger": {
242
+ "id": "6d50305f-588b-469f-a042-b0767d3f99b1",
243
+ "merge_behavior": "AWAIT_ANY",
244
+ },
241
245
  "ports": [{"id": "00ae06b3-f8d9-4ae6-9fbf-e4ff4d520e9b", "name": "default", "type": "DEFAULT"}],
242
246
  }
243
247
 
@@ -153,6 +153,10 @@ def test_serialize_workflow(vellum_client):
153
153
  "module": ["tests", "workflows", "basic_subworkflow_deployment", "workflow"],
154
154
  "name": "ExampleSubworkflowDeploymentNode",
155
155
  },
156
+ "trigger": {
157
+ "id": "e4d80502-9281-42c8-91e3-10817bcd7d9e",
158
+ "merge_behavior": "AWAIT_ANY",
159
+ },
156
160
  "ports": [{"id": "ab0db8a9-7b53-4d88-8667-273b31303273", "name": "default", "type": "DEFAULT"}],
157
161
  }
158
162
 
@@ -112,6 +112,10 @@ def test_serialize_workflow():
112
112
  "name": "ExampleTemplatingNode",
113
113
  "module": ["tests", "workflows", "basic_templating_node", "workflow_with_json_input"],
114
114
  },
115
+ "trigger": {
116
+ "id": "58427684-3848-498a-8299-c6b0fc70265d",
117
+ "merge_behavior": "AWAIT_ATTRIBUTES",
118
+ },
115
119
  "ports": [{"id": "39317827-df43-4f5a-bfbc-20bffc839748", "name": "default", "type": "DEFAULT"}],
116
120
  },
117
121
  templating_node,
@@ -103,4 +103,9 @@ def test_serialize_workflow():
103
103
  "value": {"type": "WORKFLOW_INPUT", "input_variable_id": "e39a7b63-de15-490a-ae9b-8112c767aea0"},
104
104
  }
105
105
  ],
106
+ "trigger": {
107
+ "id": "0173d3c6-11d1-44b7-b070-ca9ff5119046",
108
+ "merge_behavior": "AWAIT_ANY",
109
+ },
110
+ "ports": [],
106
111
  }
@@ -54,6 +54,7 @@ def test_serialize_workflow():
54
54
  "action": "GITHUB_CREATE_AN_ISSUE",
55
55
  "description": "Create a new issue in a GitHub repository",
56
56
  "user_id": None,
57
+ "name": "github_create_an_issue",
57
58
  }
58
59
 
59
60
  # AND the rest of the node structure should be correct
@@ -81,6 +81,11 @@ def test_serialize_workflow__missing_final_output_node():
81
81
  },
82
82
  }
83
83
  ],
84
+ "trigger": {
85
+ "id": "a0c2eb7a-398e-4f28-b63d-f3bae9b563ee",
86
+ "merge_behavior": "AWAIT_ANY",
87
+ },
88
+ "ports": [],
84
89
  },
85
90
  {
86
91
  "id": "bb88768d-472e-4997-b7ea-de09163d1b4c",
@@ -0,0 +1,24 @@
1
+ from vellum.workflows.events.workflow import WorkflowExecutionInitiatedEvent
2
+ from vellum_ee.workflows.display.utils.registry import (
3
+ get_parent_display_context_from_event,
4
+ register_workflow_display_class,
5
+ register_workflow_display_context,
6
+ )
7
+ from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
8
+
9
+
10
+ def event_enricher(event: WorkflowExecutionInitiatedEvent) -> WorkflowExecutionInitiatedEvent:
11
+ workflow_definition = event.body.workflow_definition
12
+ workflow_display = get_workflow_display(
13
+ workflow_class=workflow_definition,
14
+ parent_display_context=get_parent_display_context_from_event(event),
15
+ dry_run=True,
16
+ )
17
+ register_workflow_display_context(event.span_id, workflow_display.display_context)
18
+
19
+ if event.body.workflow_definition.is_dynamic:
20
+ register_workflow_display_class(workflow_definition, workflow_display.__class__)
21
+ workflow_version_exec_config = workflow_display.serialize()
22
+ setattr(event.body, "workflow_version_exec_config", workflow_version_exec_config)
23
+
24
+ return event
@@ -0,0 +1,69 @@
1
+ import pytest
2
+ from uuid import uuid4
3
+ from typing import Optional
4
+
5
+ from vellum.workflows.events.workflow import WorkflowExecutionInitiatedBody, WorkflowExecutionInitiatedEvent
6
+ from vellum.workflows.inputs.base import BaseInputs
7
+ from vellum.workflows.workflows.base import BaseWorkflow
8
+ from vellum_ee.workflows.display.utils.events import event_enricher
9
+
10
+
11
+ @pytest.mark.parametrize(
12
+ "is_dynamic,expected_config",
13
+ [
14
+ (False, None),
15
+ (
16
+ True,
17
+ {
18
+ "workflow_raw_data": {
19
+ "nodes": [
20
+ {
21
+ "id": "86607b18-7872-49f3-a592-fda1428f70aa",
22
+ "type": "ENTRYPOINT",
23
+ "inputs": [],
24
+ "data": {
25
+ "label": "Entrypoint Node",
26
+ "source_handle_id": "d1fe8f4c-53d7-43a0-b210-73ebdc60bf57",
27
+ },
28
+ "display_data": {"position": {"x": 0.0, "y": -50.0}},
29
+ "base": None,
30
+ "definition": None,
31
+ }
32
+ ],
33
+ "edges": [],
34
+ "display_data": {"viewport": {"x": 0.0, "y": 0.0, "zoom": 1.0}},
35
+ "definition": {
36
+ "name": "TestWorkflow",
37
+ "module": ["vellum_ee", "workflows", "display", "utils", "tests", "test_events"],
38
+ },
39
+ "output_values": [],
40
+ },
41
+ "input_variables": [],
42
+ "state_variables": [],
43
+ "output_variables": [],
44
+ },
45
+ ),
46
+ ],
47
+ )
48
+ def test_event_enricher_static_workflow(is_dynamic: bool, expected_config: Optional[dict]):
49
+ """Test event_enricher with a static workflow (is_dynamic=False)."""
50
+ # GIVEN a workflow class with the specified is_dynamic value
51
+ _is_dynamic = is_dynamic
52
+
53
+ class TestWorkflow(BaseWorkflow):
54
+ is_dynamic = _is_dynamic
55
+
56
+ event: WorkflowExecutionInitiatedEvent = WorkflowExecutionInitiatedEvent(
57
+ trace_id=uuid4(),
58
+ span_id=uuid4(),
59
+ body=WorkflowExecutionInitiatedBody(
60
+ workflow_definition=TestWorkflow,
61
+ inputs=BaseInputs(),
62
+ ),
63
+ )
64
+
65
+ # WHEN the event_enricher is called with mocked dependencies
66
+ event_enricher(event)
67
+
68
+ # AND workflow_version_exec_config is set to the expected config
69
+ assert event.body.workflow_version_exec_config == expected_config