vellum-ai 1.7.8__py3-none-any.whl → 1.7.9__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 (32) hide show
  1. vellum/client/core/client_wrapper.py +2 -2
  2. vellum/workflows/errors/types.py +3 -0
  3. vellum/workflows/graph/graph.py +4 -1
  4. vellum/workflows/runner/runner.py +17 -5
  5. vellum/workflows/utils/functions.py +1 -1
  6. vellum/workflows/workflows/base.py +2 -2
  7. {vellum_ai-1.7.8.dist-info → vellum_ai-1.7.9.dist-info}/METADATA +1 -1
  8. {vellum_ai-1.7.8.dist-info → vellum_ai-1.7.9.dist-info}/RECORD +32 -32
  9. vellum_ee/assets/node-definitions.json +91 -65
  10. vellum_ee/workflows/display/nodes/base_node_display.py +46 -26
  11. vellum_ee/workflows/display/nodes/vellum/code_execution_node.py +1 -1
  12. vellum_ee/workflows/display/nodes/vellum/conditional_node.py +1 -1
  13. vellum_ee/workflows/display/nodes/vellum/error_node.py +1 -1
  14. vellum_ee/workflows/display/nodes/vellum/final_output_node.py +1 -1
  15. vellum_ee/workflows/display/nodes/vellum/guardrail_node.py +1 -1
  16. vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py +1 -1
  17. vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py +1 -1
  18. vellum_ee/workflows/display/nodes/vellum/map_node.py +1 -1
  19. vellum_ee/workflows/display/nodes/vellum/merge_node.py +1 -1
  20. vellum_ee/workflows/display/nodes/vellum/note_node.py +1 -1
  21. vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py +1 -1
  22. vellum_ee/workflows/display/nodes/vellum/search_node.py +1 -1
  23. vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py +1 -1
  24. vellum_ee/workflows/display/nodes/vellum/templating_node.py +1 -1
  25. vellum_ee/workflows/display/nodes/vellum/tests/test_api_node.py +34 -0
  26. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py +8 -0
  27. vellum_ee/workflows/display/tests/workflow_serialization/test_manual_trigger_serialization.py +33 -70
  28. vellum_ee/workflows/display/utils/expressions.py +12 -0
  29. vellum_ee/workflows/display/workflows/base_workflow_display.py +1 -1
  30. {vellum_ai-1.7.8.dist-info → vellum_ai-1.7.9.dist-info}/LICENSE +0 -0
  31. {vellum_ai-1.7.8.dist-info → vellum_ai-1.7.9.dist-info}/WHEEL +0 -0
  32. {vellum_ai-1.7.8.dist-info → vellum_ai-1.7.9.dist-info}/entry_points.txt +0 -0
@@ -27,10 +27,10 @@ class BaseClientWrapper:
27
27
 
28
28
  def get_headers(self) -> typing.Dict[str, str]:
29
29
  headers: typing.Dict[str, str] = {
30
- "User-Agent": "vellum-ai/1.7.8",
30
+ "User-Agent": "vellum-ai/1.7.9",
31
31
  "X-Fern-Language": "Python",
32
32
  "X-Fern-SDK-Name": "vellum-ai",
33
- "X-Fern-SDK-Version": "1.7.8",
33
+ "X-Fern-SDK-Version": "1.7.9",
34
34
  **(self.get_custom_headers() or {}),
35
35
  }
36
36
  if self._api_version is not None:
@@ -23,6 +23,7 @@ class WorkflowErrorCode(Enum):
23
23
  PROVIDER_CREDENTIALS_UNAVAILABLE = "PROVIDER_CREDENTIALS_UNAVAILABLE"
24
24
  INTEGRATION_CREDENTIALS_UNAVAILABLE = "INTEGRATION_CREDENTIALS_UNAVAILABLE"
25
25
  PROVIDER_ERROR = "PROVIDER_ERROR"
26
+ PROVIDER_QUOTA_EXCEEDED = "PROVIDER_QUOTA_EXCEEDED"
26
27
  USER_DEFINED_ERROR = "USER_DEFINED_ERROR"
27
28
  WORKFLOW_CANCELLED = "WORKFLOW_CANCELLED"
28
29
  NODE_CANCELLED = "NODE_CANCELLED"
@@ -44,6 +45,7 @@ _VELLUM_ERROR_CODE_TO_WORKFLOW_ERROR_CODE: Dict[VellumErrorCodeEnum, WorkflowErr
44
45
  "INVALID_INPUTS": WorkflowErrorCode.INVALID_INPUTS,
45
46
  "PROVIDER_ERROR": WorkflowErrorCode.PROVIDER_ERROR,
46
47
  "PROVIDER_CREDENTIALS_UNAVAILABLE": WorkflowErrorCode.PROVIDER_CREDENTIALS_UNAVAILABLE,
48
+ "PROVIDER_QUOTA_EXCEEDED": WorkflowErrorCode.PROVIDER_QUOTA_EXCEEDED,
47
49
  "INTEGRATION_CREDENTIALS_UNAVAILABLE": WorkflowErrorCode.INTEGRATION_CREDENTIALS_UNAVAILABLE,
48
50
  "REQUEST_TIMEOUT": WorkflowErrorCode.PROVIDER_ERROR,
49
51
  "INTERNAL_SERVER_ERROR": WorkflowErrorCode.INTERNAL_ERROR,
@@ -99,6 +101,7 @@ _WORKFLOW_ERROR_CODE_TO_VELLUM_ERROR_CODE: Dict[WorkflowErrorCode, VellumErrorCo
99
101
  WorkflowErrorCode.NODE_EXECUTION: "USER_DEFINED_ERROR",
100
102
  WorkflowErrorCode.PROVIDER_ERROR: "PROVIDER_ERROR",
101
103
  WorkflowErrorCode.PROVIDER_CREDENTIALS_UNAVAILABLE: "PROVIDER_CREDENTIALS_UNAVAILABLE",
104
+ WorkflowErrorCode.PROVIDER_QUOTA_EXCEEDED: "PROVIDER_QUOTA_EXCEEDED",
102
105
  WorkflowErrorCode.INTEGRATION_CREDENTIALS_UNAVAILABLE: "INTEGRATION_CREDENTIALS_UNAVAILABLE",
103
106
  WorkflowErrorCode.USER_DEFINED_ERROR: "USER_DEFINED_ERROR",
104
107
  WorkflowErrorCode.WORKFLOW_CANCELLED: "REQUEST_TIMEOUT",
@@ -175,7 +175,10 @@ class Graph:
175
175
  )
176
176
 
177
177
  if not self._edges and not self._entrypoints:
178
- raise ValueError("Graph instance can only create new edges from nodes within existing edges")
178
+ raise ValueError(
179
+ "Cannot create edges from nodes with empty Ports classes (like TERMINAL/FinalOutputNode). "
180
+ "TERMINAL nodes are designed to be workflow outputs and cannot connect to other nodes."
181
+ )
179
182
 
180
183
  if self._terminals and all(isinstance(terminal, NoPortsNode) for terminal in self._terminals):
181
184
  terminal_names = [terminal.node_class.__name__ for terminal in self._terminals]
@@ -82,7 +82,7 @@ if TYPE_CHECKING:
82
82
 
83
83
  logger = logging.getLogger(__name__)
84
84
 
85
- RunFromNodeArg = Sequence[Type[BaseNode]]
85
+ RunFromNodeArg = Sequence[Union[Type[BaseNode], UUID]]
86
86
  ExternalInputsArg = Dict[ExternalInputReference, Any]
87
87
  BackgroundThreadItem = Union[BaseState, WorkflowEvent, None]
88
88
 
@@ -117,19 +117,31 @@ class WorkflowRunner(Generic[StateType]):
117
117
  self._should_emit_initial_state = True
118
118
  self._span_link_info: Optional[Tuple[str, str, str, str]] = None
119
119
  if entrypoint_nodes:
120
- if len(list(entrypoint_nodes)) > 1:
121
- raise ValueError("Cannot resume from multiple nodes")
120
+ nodes_by_id = {node.__id__: node for node in self.workflow.get_all_nodes()}
121
+
122
+ resolved_nodes = []
123
+ for item in entrypoint_nodes:
124
+ if isinstance(item, UUID):
125
+ matching_node = nodes_by_id.get(item)
126
+ if matching_node is None:
127
+ raise ValueError(f"No node found with UUID {item}")
128
+ resolved_nodes.append(matching_node)
129
+ else:
130
+ resolved_nodes.append(item)
131
+
132
+ if len(list(resolved_nodes)) > 1:
133
+ raise WorkflowInitializationException("Cannot resume from multiple nodes")
122
134
 
123
135
  # TODO: Support resuming from multiple nodes
124
136
  # https://app.shortcut.com/vellum/story/4408
125
- node = next(iter(entrypoint_nodes))
137
+ node = next(iter(resolved_nodes))
126
138
  if state:
127
139
  self._initial_state = deepcopy(state)
128
140
  self._initial_state.meta.span_id = uuid4()
129
141
  self._initial_state.meta.workflow_definition = self.workflow.__class__
130
142
  else:
131
143
  self._initial_state = self.workflow.get_state_at_node(node)
132
- self._entrypoints = entrypoint_nodes
144
+ self._entrypoints = resolved_nodes
133
145
  elif external_inputs:
134
146
  self._initial_state = self.workflow.get_most_recent_state()
135
147
  for descriptor, value in external_inputs.items():
@@ -283,7 +283,7 @@ def compile_mcp_tool_definition(server_def: MCPServer) -> List[MCPToolDefinition
283
283
  We do tool discovery on the MCP server to get the tool definitions.
284
284
 
285
285
  Args:
286
- tool_def: The basic MCPToolDefinition to enhance
286
+ server_def: The basic MCPToolDefinition to enhance
287
287
 
288
288
  Returns:
289
289
  MCPToolDefinition with detailed parameters and description
@@ -723,9 +723,9 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
723
723
  workflows.append(attr)
724
724
 
725
725
  if len(workflows) == 0:
726
- raise ValueError(f"No workflows found in {module_path}")
726
+ raise WorkflowInitializationException(f"No workflows found in {module_path}")
727
727
  elif len(workflows) > 1:
728
- raise ValueError(f"Multiple workflows found in {module_path}")
728
+ raise WorkflowInitializationException(f"Multiple workflows found in {module_path}")
729
729
  return workflows[0]
730
730
 
731
731
  def join(self) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 1.7.8
3
+ Version: 1.7.9
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -22,7 +22,7 @@ vellum_cli/tests/test_ping.py,sha256=b3aQLd-N59_8w2rRiWqwpB1rlHaKEYVbAj1Y3hi7A-g
22
22
  vellum_cli/tests/test_pull.py,sha256=e2XHzcHIx9k-FyuNAl7wMSNsSSebPGyP6U05JGcddFs,49447
23
23
  vellum_cli/tests/test_push.py,sha256=oQ3x28G6IxplmMWCcPEYY46nOYAEPaihcMVsN4quQ5Q,45000
24
24
  vellum_ee/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
- vellum_ee/assets/node-definitions.json,sha256=Mm3c1nfEa1QjWWzNvIJlhahDcY4SM3wQm8og_x3jyd8,30755
25
+ vellum_ee/assets/node-definitions.json,sha256=FHw1RlnD7n8nHBSN9hMsrjBGq9SnM2wga7URiSCDGBY,31408
26
26
  vellum_ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  vellum_ee/scripts/generate_node_definitions.py,sha256=FOYQsXIqU45I0OAcsyZUGODF9JK44yunf58rR6YaAdA,3303
28
28
  vellum_ee/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -32,7 +32,7 @@ vellum_ee/workflows/display/editor/__init__.py,sha256=MSAgY91xCEg2neH5d8jXx5wRdR
32
32
  vellum_ee/workflows/display/editor/types.py,sha256=rmaNXkNZUNRgK-mJJ_g1-Fm3OGxoQfqEB7zn-zzgJtc,664
33
33
  vellum_ee/workflows/display/exceptions.py,sha256=_FDhK-lfuBPHY2GSywp70ewM0k55Ji5Bp-wZlEZenz4,112
34
34
  vellum_ee/workflows/display/nodes/__init__.py,sha256=jI1aPBQf8DkmrYoZ4O-wR1duqZByOf5mDFmo_wFJPE4,307
35
- vellum_ee/workflows/display/nodes/base_node_display.py,sha256=ceKQdHZmDSOY5-M2DtJnuLbdkuFujCChFtRABDoV1jo,19497
35
+ vellum_ee/workflows/display/nodes/base_node_display.py,sha256=2sToAwhdVCU0DssQhHcGwC2r1rQCloW18-mp12JZeqs,20332
36
36
  vellum_ee/workflows/display/nodes/get_node_display_class.py,sha256=jI_kUi9LnNLDpY63QtlC4TfN8P571VN4LpzH0I1ZtLk,1149
37
37
  vellum_ee/workflows/display/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
38
38
  vellum_ee/workflows/display/nodes/tests/test_base_node_display.py,sha256=JLa9nlG38dLfCo1Y8ISsJ21hrfDyy4-ae3pZ9H01yFs,5578
@@ -41,23 +41,23 @@ vellum_ee/workflows/display/nodes/utils.py,sha256=sloya5TpXsnot1HURc9L51INwflRqU
41
41
  vellum_ee/workflows/display/nodes/vellum/__init__.py,sha256=nUIgH2s0-7IbQRNrBhLPyRNe8YIrx3Yo9HeeW-aXXFk,1668
42
42
  vellum_ee/workflows/display/nodes/vellum/api_node.py,sha256=nCtFc5f9u0-OOZvVcMZtc6wwq7MyqAGATH-jPgRTMNM,9028
43
43
  vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py,sha256=FHhPoGmmny4Xcxi2pm12Sk3ZOREanWEVrOWcjRhncH4,6337
44
- vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=qvPSqYKz9op7gKg-ZjKa3U6dsaaxuv76TIIbUMeGKjY,5152
45
- vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=dtO9A-rjbDEJrywwrOxwEXahqrW-S493OIDHOti9Sjs,11498
46
- vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=8tSb8qGVoRIELubu0qPeoDlt1LpiIqc6d9_30GWRd_k,2266
47
- vellum_ee/workflows/display/nodes/vellum/final_output_node.py,sha256=z4oeTgKnMGVaGig8XOZm5B_xYL4H7zweYlFweCbhnyA,3000
48
- vellum_ee/workflows/display/nodes/vellum/guardrail_node.py,sha256=9_AslWjzj4RHH2sq3SIaq9FU0NCg7ex5TIWrNMybqXg,2173
49
- vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=KA4U5NADWCBBbTG0f76OOFW9HvlWd5DfZoqN5Y0ZK9c,12167
50
- vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py,sha256=dfZuJEENgP5uvF4IzyWJdoqopjjZa_u1-VWtWBHADqI,6440
51
- vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=we7NENZpci-LiWXJFTPWzliCMdjzCMMMWUCfgJ-oP0g,4297
52
- vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=xMHaPfTSZWYprQenlHm2g47u0a5O9Me_dhAjfqo8nKQ,3116
53
- vellum_ee/workflows/display/nodes/vellum/note_node.py,sha256=6PcAFA_EJn7vEMdqgoRjYTLHwnXCrJv80B10zuUx4jE,1026
54
- vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=Ue727X7g7jUo9Yi_AXoLD-IQHi0crENkMJjPBY2hZO8,3709
44
+ vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=4fuMztGTeyNeg_JFDaVsbM-MFB-RMQwAF9iCpkO7Coc,5173
45
+ vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=lzYPDO7JRdIKshbwPJr7u6QsjAJuRPFz7PSbqewhPJM,11519
46
+ vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=BXNgRk9YY6CZT056ManeIqsJbEnkDui_JPe47Xy6VVw,2287
47
+ vellum_ee/workflows/display/nodes/vellum/final_output_node.py,sha256=SPMrXqDIJ_YBlnjjdF5ymH5stPkp4apyCLYRBjJep9M,3021
48
+ vellum_ee/workflows/display/nodes/vellum/guardrail_node.py,sha256=gNa5COUKKO1VioJAg2ZWxpNKbY5aAfHN9QYAcS2HJF0,2194
49
+ vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=JQ-dmkgdBuQisY7ycKd_BNEjaZ6EnHm61S1BALBOUxE,12188
50
+ vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py,sha256=ocpe8sHbievEqUix4a7psjFvTrjGBuGrzz_uGU1wnPw,6461
51
+ vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=LZHY8XZLzHHWUQdk70KHOLMJb17jeZVjvy7VAWP8wzg,4318
52
+ vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=S39Oo8PXHbvSwOjtFcXE-Cg4Dey6GveNtR0wQnzUQ34,3137
53
+ vellum_ee/workflows/display/nodes/vellum/note_node.py,sha256=_jOqj1IFfwYNVBgL4ZbSl46utM-aVGsFBT7joL3YHUw,1047
54
+ vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=32DgNokx8ZvsEn19zE-rz6zFlj1BG8PE8cZ2TlpzWDk,3730
55
55
  vellum_ee/workflows/display/nodes/vellum/retry_node.py,sha256=5xv5OR4xRuI1R5yqJDZTNdCFYY-z8PkTdpWM4ziGjw0,3192
56
- vellum_ee/workflows/display/nodes/vellum/search_node.py,sha256=ZHqkwzCRsM0TkZmh3ePHreZAoQXeT-SFS7zbUrSjMsw,12037
57
- vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py,sha256=hC5hTSRf1f9ppiZ_wXzpcp-jl0fwNAUB9PY8s0AykHs,3131
58
- vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=sWupmR7Ri2ZeCLx8713JbuawabR_kpWp_-HwT1tCrtU,3163
56
+ vellum_ee/workflows/display/nodes/vellum/search_node.py,sha256=z_D2VOwt1YUkBMiChzsnObhVHIseqGtQzRoMeREMn54,12058
57
+ vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py,sha256=99Jv6QYAcavrm00MhcCXVDno7ZQzdtfmj_FPvXfMGvk,3152
58
+ vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=Ne6BGRdqVhiSrCraY06u2TXMArRmP6Qahj1XBsUgI-4,3184
59
59
  vellum_ee/workflows/display/nodes/vellum/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
60
- vellum_ee/workflows/display/nodes/vellum/tests/test_api_node.py,sha256=DQAtsabvn6BE6xWwKNHzMOppzoy1-1dssNnrwbHUdRU,1490
60
+ vellum_ee/workflows/display/nodes/vellum/tests/test_api_node.py,sha256=vHV5MThwb0caYfNp5ZoPnYcHwld5CgCOIjTmtxNvDd0,2661
61
61
  vellum_ee/workflows/display/nodes/vellum/tests/test_code_execution_node.py,sha256=6_1yVYftVnqXJn3hLUGHlcHvbgKbQgBfS6vYQ6R79oc,10891
62
62
  vellum_ee/workflows/display/nodes/vellum/tests/test_error_node.py,sha256=540FoWMpJ3EN_DPjHsr9ODJWCRVcUa5hZBn-5T2GiHU,1665
63
63
  vellum_ee/workflows/display/nodes/vellum/tests/test_final_output_node.py,sha256=KVftEQxXARlcr-Uuo1ZK_wEHTcTH64OZJ3Ub3mN8x7I,3006
@@ -84,7 +84,7 @@ vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attr
84
84
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py,sha256=SwXRzjdoEZLvkzaRMvRV8_UqbBm0EB_UtAHD_zXKZBY,6303
85
85
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py,sha256=jubGYgLJImOqILd5LjnYJ4B1UMIrToDrQbPZOvaQCX4,40035
86
86
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py,sha256=FLvcD8eoABHUPv08oSpIp_P-65sw2gl4whMXEJJj4f8,6785
87
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py,sha256=D_ZGMOgr5Ek33ZQagv08b8up1U0Lc0dCgAjnO8ZEpzQ,16336
87
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py,sha256=NLvHs8dXG3JiSC2HDUT1-Qi0xvRKuxbtcrTRiWI59SU,16909
88
88
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py,sha256=arl-tho6f0qstUM2ejONEO4ru_6TxCPbni3FS-UZouQ,30108
89
89
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py,sha256=-BiFVw3JUx_79isQNgAtZB2362oByRcuAuUVK9uzCJI,54204
90
90
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_default_state_serialization.py,sha256=E1ww97BFoGbnRkxf84TScat5NQhP8nLVrdaOlpGnSKU,8615
@@ -110,7 +110,7 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling
110
110
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py,sha256=pLCyMScV88DTBXRH7jXaXOEA1GBq8NIipCUFwIAWnwI,2771
111
111
  vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py,sha256=exT7U-axwtYgFylagScflSQLJEND51qIAx2UATju6JM,6023
112
112
  vellum_ee/workflows/display/tests/workflow_serialization/test_final_output_node_map_reference_serialization.py,sha256=vl3pxUJlrYRA8zzFJ-gRm7fe-5fviLNSIsUC7imnMqk,3502
113
- vellum_ee/workflows/display/tests/workflow_serialization/test_manual_trigger_serialization.py,sha256=sd0lbity6yVIJ3HbMZEcL1wJEYoihqVW2Bjx8YMmrAM,3657
113
+ vellum_ee/workflows/display/tests/workflow_serialization/test_manual_trigger_serialization.py,sha256=L4bJWW94eq5cl6pjIaX7pQrDHoF67Gudfx-41dmmd10,2330
114
114
  vellum_ee/workflows/display/tests/workflow_serialization/test_slack_trigger_serialization.py,sha256=lBCzNJHmP_Z7_QEnzBO2FYvjJzVm8-lHxb9buivrqZk,4971
115
115
  vellum_ee/workflows/display/tests/workflow_serialization/test_terminal_node_any_serialization.py,sha256=4WAmSEJZlDBLPhsD1f4GwY9ahB9F6qJKGnL6j7ZYlzQ,1740
116
116
  vellum_ee/workflows/display/tests/workflow_serialization/test_web_search_node_serialization.py,sha256=vbDFBrWUPeeW7cxjNA6SXrsHlYcbOAhlQ4C45Vdnr1c,3428
@@ -120,7 +120,7 @@ vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeR
120
120
  vellum_ee/workflows/display/utils/auto_layout.py,sha256=f4GiLn_LazweupfqTpubcdtdfE_vrOcmZudSsnYIY9E,3906
121
121
  vellum_ee/workflows/display/utils/events.py,sha256=DE33uoKW78BZtITJ6L22dMZN3KR1BuZBVC98C_gIyzU,1943
122
122
  vellum_ee/workflows/display/utils/exceptions.py,sha256=E8Lvo7LY1BoZ54M_NR_opDjJsAAiCUfow1HgoHcTHmg,989
123
- vellum_ee/workflows/display/utils/expressions.py,sha256=IIieWILv2zGLYTRgeDwgjhtga1ttV-ss2remJ1HBVXA,20688
123
+ vellum_ee/workflows/display/utils/expressions.py,sha256=9rcpoXhUIxcWy407Ziu-zJfP5OEFq3pHIh7XSZZ1Y6E,21169
124
124
  vellum_ee/workflows/display/utils/registry.py,sha256=1qXiBTdsnro6FeCX0FGBEK7CIf6wa--Jt50iZ_nEp_M,3460
125
125
  vellum_ee/workflows/display/utils/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
126
126
  vellum_ee/workflows/display/utils/tests/test_auto_layout.py,sha256=vfXI769418s9vda5Gb5NFBH747WMOwSgHRXeLCTLVm8,2356
@@ -128,7 +128,7 @@ vellum_ee/workflows/display/utils/tests/test_events.py,sha256=42IEBnMbaQrH8gigw5
128
128
  vellum_ee/workflows/display/utils/vellum.py,sha256=Bt7kdLdXoBsHn5dVEY2uKcF542VL09jwu8J_30rl2vk,6413
129
129
  vellum_ee/workflows/display/vellum.py,sha256=J2mdJZ1sdLW535DDUkq_Vm8Z572vhuxHxVZF9deKSdk,391
130
130
  vellum_ee/workflows/display/workflows/__init__.py,sha256=JTB9ObEV3l4gGGdtfBHwVJtTTKC22uj-a-XjTVwXCyA,148
131
- vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=h2r_w7cvbN3RGrUOf0kAdFDtwhvLlphzkuDjXEWc2Kk,47663
131
+ vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=rY-mK4dLouCUtsq8TAm2ufkODVn8E2_OXix5-Ig0EHA,47629
132
132
  vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=gxz76AeCqgAZ9D2lZeTiZzxY9eMgn3qOSfVgiqYcOh8,2028
133
133
  vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=lg-c_3P3ldtqWq2VFsk_2Mkn3pVdXWuT59QpH7QwXVs,39764
134
134
  vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -162,7 +162,7 @@ vellum/client/README.md,sha256=flqu57ubZNTfpq60CdLtJC9gp4WEkyjb_n_eZ4OYf9w,6497
162
162
  vellum/client/__init__.py,sha256=rMnKRqL5-356SBc-rfm56MkO87PuAi2mtcfBszcJU1M,74316
163
163
  vellum/client/core/__init__.py,sha256=lTcqUPXcx4112yLDd70RAPeqq6tu3eFMe1pKOqkW9JQ,1562
164
164
  vellum/client/core/api_error.py,sha256=44vPoTyWN59gonCIZMdzw7M1uspygiLnr3GNFOoVL2Q,614
165
- vellum/client/core/client_wrapper.py,sha256=9B_jlACsiDJUp2QZ0RL-la5fjjWa1aeDXcm_Kgs8Ex4,2840
165
+ vellum/client/core/client_wrapper.py,sha256=SDfeITPfYIuFYtFxDzo3M3ePxfx4Mu0Yw6Y3QEnkCts,2840
166
166
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
167
167
  vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
168
168
  vellum/client/core/force_multipart.py,sha256=awxh5MtcRYe74ehY8U76jzv6fYM_w_D3Rur7KQQzSDk,429
@@ -1809,7 +1809,7 @@ vellum/workflows/emitters/vellum_emitter.py,sha256=t4ixrN0NNXrydMP9PVKYvcOMxoMqs
1809
1809
  vellum/workflows/environment/__init__.py,sha256=TJz0m9dwIs6YOwCTeuN0HHsU-ecyjc1OJXx4AFy83EQ,121
1810
1810
  vellum/workflows/environment/environment.py,sha256=Ck3RPKXJvtMGx_toqYQQQF-ZwXm5ijVwJpEPTeIJ4_Q,471
1811
1811
  vellum/workflows/errors/__init__.py,sha256=tWGPu5xyAU8gRb8_bl0fL7OfU3wxQ9UH6qVwy4X4P_Q,113
1812
- vellum/workflows/errors/types.py,sha256=N43BsnhnYZvuPql-Xu86Y6QQ07BxKv5ypD2lq4eugpk,4749
1812
+ vellum/workflows/errors/types.py,sha256=TgWRhhsLLfiQ5Wxk26Dr01clPTYlq3pM-4umDTXLhWQ,4953
1813
1813
  vellum/workflows/events/__init__.py,sha256=V4mh766fyA70WvHelm9kfVZGrUgEKcJ9tJt8EepfQYU,832
1814
1814
  vellum/workflows/events/context.py,sha256=vCfMIPmz4j9Om36rRWa35A_JU_VccWWS52_mZkkqxak,3345
1815
1815
  vellum/workflows/events/exception_handling.py,sha256=2okFtCzrOzaCP-HEwBPMvHn-evlyyE1zRkmIYjR__jQ,1975
@@ -1868,7 +1868,7 @@ vellum/workflows/expressions/tests/test_length.py,sha256=pQA1tYSwqxE6euclboY024N
1868
1868
  vellum/workflows/expressions/tests/test_minus.py,sha256=pq7dvdRGNhSSn95LGNzRErsYUsFk5SpOKHDcSR5QToc,1632
1869
1869
  vellum/workflows/expressions/tests/test_parse_json.py,sha256=zpB_qE5_EwWQL7ULQUJm0o1PRSfWZdAqZNW6Ah13oJE,1059
1870
1870
  vellum/workflows/graph/__init__.py,sha256=3sHlay5d_-uD7j3QJXiGl0WHFZZ_QScRvgyDhN2GhHY,74
1871
- vellum/workflows/graph/graph.py,sha256=lUqREmkROzRtyw1ncRnN7CR9vbztJYR1IgR3hHIG9Xw,14537
1871
+ vellum/workflows/graph/graph.py,sha256=2Yyp6qhm0qcbmbdXS8ZW2jwWFdhSQD2w7hJ2-ltolvs,14687
1872
1872
  vellum/workflows/graph/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1873
1873
  vellum/workflows/graph/tests/test_graph.py,sha256=OLGkEQh-B1CucwSf4nOP79RFXB4R2isHo3LFlr-24yA,20132
1874
1874
  vellum/workflows/inputs/__init__.py,sha256=02pj0IbJkN1AxTreswK39cNi45tA8GWcAAdRJve4cuM,116
@@ -2027,7 +2027,7 @@ vellum/workflows/resolvers/resolver.py,sha256=3uEYscB_2PHTazc0Y9SzOe_yiQZhVLfey1
2027
2027
  vellum/workflows/resolvers/tests/test_resolver.py,sha256=PnUGzsulo1It_LjjhHsRNiILvvl5G_IaK8ZX56zKC28,6204
2028
2028
  vellum/workflows/resolvers/types.py,sha256=Hndhlk69g6EKLh_LYg5ILepW5U_h_BYNllfzhS9k8p4,237
2029
2029
  vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
2030
- vellum/workflows/runner/runner.py,sha256=IYjeFx6_jescg8tEW-et5k96X9EsB6PDw4Kw4Qvy-Xo,45599
2030
+ vellum/workflows/runner/runner.py,sha256=hmr7DIt8Yo2VzVLDVjVBx366foQQVNu6iBBkQjoNNls,46141
2031
2031
  vellum/workflows/sandbox.py,sha256=mezSZmilR_fwR8164n8CEfzlMeQ55IqfapHp4ftImvQ,3212
2032
2032
  vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
2033
2033
  vellum/workflows/state/base.py,sha256=8Zr7UIM_eC0O2N6w_1gYqyrjgJ9D9z-VqLFnBETEF7Q,26753
@@ -2060,7 +2060,7 @@ vellum/workflows/types/tests/test_definition.py,sha256=QUI9_Wsm2k-ZxQTXogsB0L4cs
2060
2060
  vellum/workflows/types/tests/test_utils.py,sha256=UnZog59tR577mVwqZRqqWn2fScoOU1H6up0EzS8zYhw,2536
2061
2061
  vellum/workflows/types/utils.py,sha256=mTctHITBybpt4855x32oCKALBEcMNLn-9cCmfEKgJHQ,6498
2062
2062
  vellum/workflows/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2063
- vellum/workflows/utils/functions.py,sha256=whqLOWWRDFVumOoP0IjUpw66y6H9M3iPZe1NSlvm8Gg,13155
2063
+ vellum/workflows/utils/functions.py,sha256=uIg8As0c1BGwXmwectbAQWLWWIWoeHCduGhhoOqQVVU,13157
2064
2064
  vellum/workflows/utils/hmac.py,sha256=JJCczc6pyV6DuE1Oa0QVfYPUN_of3zEYmGFib3OZnrE,1135
2065
2065
  vellum/workflows/utils/names.py,sha256=QtHquoaGqRseu5gg2OcVGI2d_CMcEOvjb9KspwH4C-A,552
2066
2066
  vellum/workflows/utils/pydantic_schema.py,sha256=eR_bBtY-T0pttJP-ARwagSdCOnwPUtiT3cegm2lzDTQ,1310
@@ -2074,13 +2074,13 @@ vellum/workflows/utils/vellum_variables.py,sha256=X3lZn-EoWengRWBWRhTNW7hqbj7LkV
2074
2074
  vellum/workflows/utils/zip.py,sha256=HVg_YZLmBOTXKaDV3Xhaf3V6sYnfqqZXQ8CpuafkbPY,1181
2075
2075
  vellum/workflows/vellum_client.py,sha256=3iDR7VV_NgLSm1iZQCKDvrmfEaX1bOJiU15QrxyHpv0,1237
2076
2076
  vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
2077
- vellum/workflows/workflows/base.py,sha256=2XQMntFPXUO3p1js14TEKun4oNWRmZOzga-pUiv5vVk,30935
2077
+ vellum/workflows/workflows/base.py,sha256=9ILdqYIfB6rIpP-3QeQTOgYeCohjR7xC6c9BqCuAvb4,30977
2078
2078
  vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
2079
2079
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2080
2080
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=Boa-_m9ii2Qsa1RvVM-VYniF7zCpzGgEGy-OnPZkrHg,23941
2081
2081
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
2082
- vellum_ai-1.7.8.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
2083
- vellum_ai-1.7.8.dist-info/METADATA,sha256=nLEmbdMVVg8ZzhJ0MXKOLdXmUF96KvZym2nn-kS7RhE,5547
2084
- vellum_ai-1.7.8.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
2085
- vellum_ai-1.7.8.dist-info/entry_points.txt,sha256=xVavzAKN4iF_NbmhWOlOkHluka0YLkbN_pFQ9pW3gLI,117
2086
- vellum_ai-1.7.8.dist-info/RECORD,,
2082
+ vellum_ai-1.7.9.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
2083
+ vellum_ai-1.7.9.dist-info/METADATA,sha256=W9uh0R7Qv8FtGnts5F2-3PAMz4ki8JwFXCa43kinV8U,5547
2084
+ vellum_ai-1.7.9.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
2085
+ vellum_ai-1.7.9.dist-info/entry_points.txt,sha256=xVavzAKN4iF_NbmhWOlOkHluka0YLkbN_pFQ9pW3gLI,117
2086
+ vellum_ai-1.7.9.dist-info/RECORD,,
@@ -46,6 +46,32 @@
46
46
  "type": "DEFAULT"
47
47
  }
48
48
  ],
49
+ "outputs": [
50
+ {
51
+ "id": "99a8d2c4-080f-4605-810e-f5084a52f019",
52
+ "name": "headers",
53
+ "type": "JSON",
54
+ "value": null
55
+ },
56
+ {
57
+ "id": "c5737084-f688-4695-ab81-5f97a6b1a5c2",
58
+ "name": "json",
59
+ "type": "JSON",
60
+ "value": null
61
+ },
62
+ {
63
+ "id": "a2a63a64-75f1-49ba-8743-4db660179ab3",
64
+ "name": "status_code",
65
+ "type": "NUMBER",
66
+ "value": null
67
+ },
68
+ {
69
+ "id": "baa40ce0-35e7-4439-b280-085aaf4f54da",
70
+ "name": "text",
71
+ "type": "STRING",
72
+ "value": null
73
+ }
74
+ ],
49
75
  "attributes": [
50
76
  {
51
77
  "id": "aacb36f1-c980-460d-8002-43bb57128e05",
@@ -747,48 +773,6 @@
747
773
  {
748
774
  "id": "035842e0-34ed-43af-97de-a706e40912ae",
749
775
  "label": "Tool Calling Node",
750
- "display_data": {
751
- "position": {
752
- "x": 0.0,
753
- "y": 0.0
754
- },
755
- "comment": {
756
- "value": "\n A Node that dynamically invokes the provided functions to the underlying Prompt\n\n Attributes:\n ml_model: str - The model to use for tool calling (e.g., \"gpt-4o-mini\")\n blocks: List[PromptBlock] - The prompt blocks to use (same format as InlinePromptNode)\n functions: List[Tool] - The functions that can be called\n prompt_inputs: Optional[EntityInputsInterface] - Mapping of input variable names to values\n parameters: PromptParameters - The parameters for the Prompt\n max_prompt_iterations: Optional[int] - Maximum number of prompt iterations before stopping\n ",
757
- "expanded": true
758
- }
759
- },
760
- "base": {
761
- "name": "BaseNode",
762
- "module": [
763
- "vellum",
764
- "workflows",
765
- "nodes",
766
- "bases",
767
- "base"
768
- ]
769
- },
770
- "definition": {
771
- "name": "ToolCallingNode",
772
- "module": [
773
- "vellum",
774
- "workflows",
775
- "nodes",
776
- "displayable",
777
- "tool_calling_node",
778
- "node"
779
- ]
780
- },
781
- "trigger": {
782
- "id": "f9b4a4ce-68ae-45f4-9e29-38b588abdf97",
783
- "merge_behavior": "AWAIT_ATTRIBUTES"
784
- },
785
- "ports": [
786
- {
787
- "id": "72157fce-fc16-44d8-a00d-506738a20730",
788
- "name": "default",
789
- "type": "DEFAULT"
790
- }
791
- ],
792
776
  "attributes": [
793
777
  {
794
778
  "id": "df3761db-067a-4fe9-9fc3-ba270786fcf6",
@@ -878,31 +862,13 @@
878
862
  }
879
863
  }
880
864
  ],
881
- "outputs": [
882
- {
883
- "id": "c18a8725-21e1-4736-a77c-76d0fc035176",
884
- "name": "text",
885
- "type": "STRING",
886
- "value": null
887
- },
888
- {
889
- "id": "5a9ad073-c670-43d2-9198-a08e9fdda7ad",
890
- "name": "chat_history",
891
- "type": "CHAT_HISTORY",
892
- "value": null
893
- }
894
- ]
895
- },
896
- {
897
- "id": "187ed9ed-ca2b-49e4-943d-ee8da5fad973",
898
- "label": "Web Search Node",
899
865
  "display_data": {
900
866
  "position": {
901
867
  "x": 0.0,
902
868
  "y": 0.0
903
869
  },
904
870
  "comment": {
905
- "value": "\n Used to perform web search using SerpAPI.\n\n query: str - The search query to execute\n api_key: str - SerpAPI authentication key\n num_results: int - Number of search results to return (default: 10)\n location: Optional[str] - Geographic location filter for search\n ",
871
+ "value": "\n A Node that dynamically invokes the provided functions to the underlying Prompt\n\n Attributes:\n ml_model: str - The model to use for tool calling (e.g., \"gpt-4o-mini\")\n blocks: List[PromptBlock] - The prompt blocks to use (same format as InlinePromptNode)\n functions: List[Tool] - The functions that can be called\n prompt_inputs: Optional[EntityInputsInterface] - Mapping of input variable names to values\n parameters: PromptParameters - The parameters for the Prompt\n max_prompt_iterations: Optional[int] - Maximum number of prompt iterations before stopping\n ",
906
872
  "expanded": true
907
873
  }
908
874
  },
@@ -917,27 +883,45 @@
917
883
  ]
918
884
  },
919
885
  "definition": {
920
- "name": "WebSearchNode",
886
+ "name": "ToolCallingNode",
921
887
  "module": [
922
888
  "vellum",
923
889
  "workflows",
924
890
  "nodes",
925
891
  "displayable",
926
- "web_search_node",
892
+ "tool_calling_node",
927
893
  "node"
928
894
  ]
929
895
  },
930
896
  "trigger": {
931
- "id": "036c7a0c-fff3-44f7-b49b-429d8f44f212",
897
+ "id": "f9b4a4ce-68ae-45f4-9e29-38b588abdf97",
932
898
  "merge_behavior": "AWAIT_ATTRIBUTES"
933
899
  },
934
900
  "ports": [
935
901
  {
936
- "id": "ef824748-d00d-41e7-b7b4-d0edc536c3af",
902
+ "id": "72157fce-fc16-44d8-a00d-506738a20730",
937
903
  "name": "default",
938
904
  "type": "DEFAULT"
939
905
  }
940
906
  ],
907
+ "outputs": [
908
+ {
909
+ "id": "c18a8725-21e1-4736-a77c-76d0fc035176",
910
+ "name": "text",
911
+ "type": "STRING",
912
+ "value": null
913
+ },
914
+ {
915
+ "id": "5a9ad073-c670-43d2-9198-a08e9fdda7ad",
916
+ "name": "chat_history",
917
+ "type": "CHAT_HISTORY",
918
+ "value": null
919
+ }
920
+ ]
921
+ },
922
+ {
923
+ "id": "187ed9ed-ca2b-49e4-943d-ee8da5fad973",
924
+ "label": "Web Search Node",
941
925
  "attributes": [
942
926
  {
943
927
  "id": "b5ba3c60-e02c-46c8-b470-d86c4f8f42af",
@@ -984,6 +968,48 @@
984
968
  }
985
969
  }
986
970
  ],
971
+ "display_data": {
972
+ "position": {
973
+ "x": 0.0,
974
+ "y": 0.0
975
+ },
976
+ "comment": {
977
+ "value": "\n Used to perform web search using SerpAPI.\n\n query: str - The search query to execute\n api_key: str - SerpAPI authentication key\n num_results: int - Number of search results to return (default: 10)\n location: Optional[str] - Geographic location filter for search\n ",
978
+ "expanded": true
979
+ }
980
+ },
981
+ "base": {
982
+ "name": "BaseNode",
983
+ "module": [
984
+ "vellum",
985
+ "workflows",
986
+ "nodes",
987
+ "bases",
988
+ "base"
989
+ ]
990
+ },
991
+ "definition": {
992
+ "name": "WebSearchNode",
993
+ "module": [
994
+ "vellum",
995
+ "workflows",
996
+ "nodes",
997
+ "displayable",
998
+ "web_search_node",
999
+ "node"
1000
+ ]
1001
+ },
1002
+ "trigger": {
1003
+ "id": "036c7a0c-fff3-44f7-b49b-429d8f44f212",
1004
+ "merge_behavior": "AWAIT_ATTRIBUTES"
1005
+ },
1006
+ "ports": [
1007
+ {
1008
+ "id": "ef824748-d00d-41e7-b7b4-d0edc536c3af",
1009
+ "name": "default",
1010
+ "type": "DEFAULT"
1011
+ }
1012
+ ],
987
1013
  "outputs": [
988
1014
  {
989
1015
  "id": "704a4ba3-1afe-482d-876a-91dcdbe90fb7",
@@ -8,6 +8,7 @@ from typing import (
8
8
  Dict,
9
9
  ForwardRef,
10
10
  Generic,
11
+ List,
11
12
  Optional,
12
13
  Set,
13
14
  Tuple,
@@ -183,32 +184,13 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
183
184
  existing_adornments = adornments if adornments is not None else []
184
185
  return display_class().serialize(display_context, adornments=existing_adornments + [adornment])
185
186
 
186
- outputs: JsonArray = []
187
- for output in node.Outputs:
188
- type = primitive_type_to_vellum_variable_type(output)
189
- value = (
190
- serialize_value(node_id, display_context, output.instance)
191
- if output.instance is not None and output.instance is not undefined
192
- else None
193
- )
194
-
195
- outputs.append(
196
- {
197
- "id": str(uuid4_from_hash(f"{node_id}|{output.name}")),
198
- "name": output.name,
199
- "type": type,
200
- "value": value,
201
- }
202
- )
203
-
204
187
  return {
205
188
  "id": str(node_id),
206
189
  "label": self.label,
207
190
  "type": "GENERIC",
208
- **self.serialize_generic_fields(display_context),
209
191
  "adornments": adornments,
210
192
  "attributes": attributes,
211
- "outputs": outputs,
193
+ **self.serialize_generic_fields(display_context),
212
194
  }
213
195
 
214
196
  def serialize_ports(self, display_context: "WorkflowDisplayContext") -> JsonArray:
@@ -275,14 +257,49 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
275
257
 
276
258
  return attributes
277
259
 
278
- def serialize_generic_fields(self, display_context: "WorkflowDisplayContext") -> JsonObject:
260
+ def _serialize_outputs(self, display_context: "WorkflowDisplayContext") -> JsonArray:
261
+ """Generate outputs array from node output displays or node.Outputs."""
262
+ outputs: JsonArray = []
263
+ node = self._node
264
+
265
+ for output in node.Outputs:
266
+ output_type = primitive_type_to_vellum_variable_type(output)
267
+ value = (
268
+ serialize_value(self.node_id, display_context, output.instance)
269
+ if output.instance is not None and output.instance != undefined
270
+ else None
271
+ )
272
+
273
+ output_id = (
274
+ str(self.output_display[output].id)
275
+ if output in self.output_display
276
+ else str(uuid4_from_hash(f"{self.node_id}|{output.name}"))
277
+ )
278
+
279
+ outputs.append(
280
+ {
281
+ "id": output_id,
282
+ "name": output.name,
283
+ "type": output_type,
284
+ "value": value,
285
+ }
286
+ )
287
+
288
+ return outputs
289
+
290
+ def serialize_generic_fields(
291
+ self, display_context: "WorkflowDisplayContext", exclude: Optional[List[str]] = None
292
+ ) -> JsonObject:
279
293
  """Serialize generic fields that are common to all nodes."""
294
+ exclude = exclude or []
295
+
280
296
  result: JsonObject = {
281
- "display_data": self.get_display_data().dict(),
282
- "base": self.get_base().dict(),
283
- "definition": self.get_definition().dict(),
284
- "trigger": self.serialize_trigger(),
285
- "ports": self.serialize_ports(display_context),
297
+ "display_data": self.get_display_data().dict() if "display_data" not in exclude else None,
298
+ "base": self.get_base().dict() if "base" not in exclude else None,
299
+ "definition": self.get_definition().dict() if "definition" not in exclude else None,
300
+ "trigger": self.serialize_trigger() if "trigger" not in exclude else None,
301
+ "ports": self.serialize_ports(display_context) if "ports" not in exclude else None,
302
+ "outputs": self._serialize_outputs(display_context) if "outputs" not in exclude else None,
286
303
  }
287
304
 
288
305
  # Only include should_file_merge if there are custom methods defined
@@ -299,6 +316,9 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
299
316
  except Exception:
300
317
  pass
301
318
 
319
+ for key in exclude:
320
+ result.pop(key, None)
321
+
302
322
  return result
303
323
 
304
324
  def get_base(self) -> CodeResourceDefinition:
@@ -119,5 +119,5 @@ class BaseCodeExecutionNodeDisplay(BaseNodeDisplay[_CodeExecutionNodeType], Gene
119
119
  "output_id": str(self.output_id) if self.output_id else str(output_display.id),
120
120
  "log_output_id": str(self.log_output_id) if self.log_output_id else str(log_output_display.id),
121
121
  },
122
- **self.serialize_generic_fields(display_context),
122
+ **self.serialize_generic_fields(display_context, exclude=["outputs"]),
123
123
  }
@@ -217,7 +217,7 @@ but the defined conditions have length {len(condition_ids)}"""
217
217
  "conditions": conditions, # type: ignore
218
218
  "version": "2",
219
219
  },
220
- **self.serialize_generic_fields(display_context),
220
+ **self.serialize_generic_fields(display_context, exclude=["outputs"]),
221
221
  }
222
222
 
223
223
  def get_nested_rule_details_by_path(
@@ -47,7 +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
- **self.serialize_generic_fields(display_context),
50
+ **self.serialize_generic_fields(display_context, exclude=["outputs"]),
51
51
  }
52
52
 
53
53
  if self.name:
@@ -45,7 +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
- **self.serialize_generic_fields(display_context),
48
+ **self.serialize_generic_fields(display_context, exclude=["outputs"]),
49
49
  "outputs": [
50
50
  {
51
51
  "id": str(self._get_output_id()),
@@ -45,5 +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
- **self.serialize_generic_fields(display_context),
48
+ **self.serialize_generic_fields(display_context, exclude=["outputs"]),
49
49
  }
@@ -110,7 +110,7 @@ class BaseInlinePromptNodeDisplay(BaseNodeDisplay[_InlinePromptNodeType], Generi
110
110
  },
111
111
  "ml_model_name": ml_model,
112
112
  },
113
- **self.serialize_generic_fields(display_context),
113
+ **self.serialize_generic_fields(display_context, exclude=["outputs"]),
114
114
  "outputs": [
115
115
  {"id": str(json_display.id), "name": "json", "type": "JSON", "value": None},
116
116
  {"id": str(output_display.id), "name": "text", "type": "STRING", "value": None},
@@ -68,7 +68,7 @@ class BaseInlineSubworkflowNodeDisplay(
68
68
  "input_variables": [workflow_input.dict() for workflow_input in workflow_inputs],
69
69
  "output_variables": [workflow_output.dict() for workflow_output in workflow_outputs],
70
70
  },
71
- **self.serialize_generic_fields(display_context),
71
+ **self.serialize_generic_fields(display_context, exclude=["outputs"]),
72
72
  }
73
73
 
74
74
  def _generate_node_and_workflow_inputs(
@@ -88,7 +88,7 @@ class BaseMapNodeDisplay(BaseAdornmentNodeDisplay[_MapNodeType], Generic[_MapNod
88
88
  "item_input_id": item_workflow_input_id,
89
89
  "index_input_id": index_workflow_input_id,
90
90
  },
91
- **self.serialize_generic_fields(display_context),
91
+ **self.serialize_generic_fields(display_context, exclude=["outputs"]),
92
92
  }
93
93
 
94
94
  def _default_workflow_class(self) -> Type[BaseWorkflow]:
@@ -47,7 +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
- **self.serialize_generic_fields(display_context),
50
+ **self.serialize_generic_fields(display_context, exclude=["outputs"]),
51
51
  }
52
52
 
53
53
  def get_target_handle_ids(self) -> Optional[List[UUID]]:
@@ -25,5 +25,5 @@ class BaseNoteNodeDisplay(BaseNodeDisplay[_NoteNodeType], Generic[_NoteNodeType]
25
25
  "text": self.text,
26
26
  "style": self.style,
27
27
  },
28
- **self.serialize_generic_fields(display_context),
28
+ **self.serialize_generic_fields(display_context, exclude=["outputs"]),
29
29
  }
@@ -71,7 +71,7 @@ class BasePromptDeploymentNodeDisplay(BaseNodeDisplay[_PromptDeploymentNodeType]
71
71
  "release_tag": raise_if_descriptor(node.release_tag),
72
72
  "ml_model_fallbacks": list(ml_model_fallbacks) if ml_model_fallbacks else None,
73
73
  },
74
- **self.serialize_generic_fields(display_context),
74
+ **self.serialize_generic_fields(display_context, exclude=["outputs"]),
75
75
  "outputs": [
76
76
  {"id": str(json_display.id), "name": "json", "type": "JSON", "value": None},
77
77
  {"id": str(output_display.id), "name": "text", "type": "STRING", "value": None},
@@ -71,7 +71,7 @@ class BaseSearchNodeDisplay(BaseNodeDisplay[_SearchNodeType], Generic[_SearchNod
71
71
  "external_id_filters_node_input_id": str(node_inputs["external_id_filters"].id),
72
72
  "metadata_filters_node_input_id": str(node_inputs["metadata_filters"].id),
73
73
  },
74
- **self.serialize_generic_fields(display_context),
74
+ **self.serialize_generic_fields(display_context, exclude=["outputs"]),
75
75
  }
76
76
 
77
77
  def _generate_search_node_inputs(
@@ -68,5 +68,5 @@ class BaseSubworkflowDeploymentNodeDisplay(
68
68
  "workflow_deployment_id": deployment_id,
69
69
  "release_tag": raise_if_descriptor(node.release_tag),
70
70
  },
71
- **self.serialize_generic_fields(display_context),
71
+ **self.serialize_generic_fields(display_context, exclude=["outputs"]),
72
72
  }
@@ -66,5 +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
- **self.serialize_generic_fields(display_context),
69
+ **self.serialize_generic_fields(display_context, exclude=["outputs"]),
70
70
  }
@@ -29,3 +29,37 @@ def test_serialize_node__api_node_with_timeout():
29
29
  assert timeout_attribute["value"]["type"] == "CONSTANT_VALUE"
30
30
  assert timeout_attribute["value"]["value"]["type"] == "NUMBER"
31
31
  assert timeout_attribute["value"]["value"]["value"] == 30.0
32
+
33
+
34
+ def test_serialize_node__api_node_outputs():
35
+ """
36
+ Tests that API node serialization includes the outputs array.
37
+ """
38
+
39
+ class MyAPINode(APINode):
40
+ url = "https://api.example.com"
41
+ method = APIRequestMethod.GET
42
+
43
+ class Workflow(BaseWorkflow):
44
+ graph = MyAPINode
45
+
46
+ workflow_display = get_workflow_display(workflow_class=Workflow)
47
+ serialized_workflow: dict = workflow_display.serialize()
48
+
49
+ my_api_node = next(node for node in serialized_workflow["workflow_raw_data"]["nodes"] if node["type"] == "API")
50
+
51
+ assert "outputs" in my_api_node
52
+ outputs = my_api_node["outputs"]
53
+ assert len(outputs) == 4
54
+
55
+ text_output = next(output for output in outputs if output["name"] == "text")
56
+ assert text_output["type"] == "STRING"
57
+ assert text_output["value"] is None
58
+
59
+ json_output = next(output for output in outputs if output["name"] == "json")
60
+ assert json_output["type"] == "JSON"
61
+ assert json_output["value"] is None
62
+
63
+ status_code_output = next(output for output in outputs if output["name"] == "status_code")
64
+ assert status_code_output["type"] == "NUMBER"
65
+ assert status_code_output["value"] is None
@@ -8,6 +8,8 @@ from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class imp
8
8
 
9
9
  from tests.workflows.basic_api_node.workflow import SimpleAPIWorkflow
10
10
 
11
+ # 0d76e1e1-3a4b-4eb4-a606-f73d62c -> 12e4a99d-883d-4da5-aa51-35817d94013e
12
+
11
13
 
12
14
  def test_serialize_workflow(vellum_client):
13
15
  # GIVEN a Workflow that uses a vellum API node
@@ -208,6 +210,12 @@ def test_serialize_workflow(vellum_client):
208
210
  "merge_behavior": "AWAIT_ANY",
209
211
  },
210
212
  "ports": [{"id": "7c33b4d3-9204-4bd5-9371-80ee34f83073", "name": "default", "type": "DEFAULT"}],
213
+ "outputs": [
214
+ {"id": "12e4a99d-883d-4da5-aa51-35817d94013e", "name": "json", "type": "JSON", "value": None},
215
+ {"id": "0d76e1e1-3a4b-4eb4-a606-f73d62cf1a7e", "name": "headers", "type": "JSON", "value": None},
216
+ {"id": "fecc16c3-400e-4fd3-8223-08366070e3b1", "name": "status_code", "type": "NUMBER", "value": None},
217
+ {"id": "17342c21-12bb-49ab-88ce-f144e0376b32", "name": "text", "type": "STRING", "value": None},
218
+ ],
211
219
  },
212
220
  api_node,
213
221
  )
@@ -1,7 +1,4 @@
1
- """Tests for serialization of workflows with ManualTrigger."""
2
-
3
1
  import pytest
4
- from typing import cast
5
2
 
6
3
  from vellum.workflows import BaseWorkflow
7
4
  from vellum.workflows.inputs.base import BaseInputs
@@ -9,95 +6,55 @@ from vellum.workflows.nodes.bases.base import BaseNode
9
6
  from vellum.workflows.state.base import BaseState
10
7
  from vellum.workflows.triggers.base import BaseTrigger
11
8
  from vellum.workflows.triggers.manual import ManualTrigger
12
- from vellum.workflows.types.core import JsonArray, JsonObject
13
9
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
14
10
 
15
11
 
16
- class Inputs(BaseInputs):
17
- input: str
18
-
19
-
20
- class SimpleNode(BaseNode):
21
- class Outputs(BaseNode.Outputs):
22
- output = Inputs.input
23
-
24
-
25
- def create_workflow(trigger=None):
26
- """Factory for creating test workflows."""
27
-
28
- class TestWorkflow(BaseWorkflow[Inputs, BaseState]):
29
- graph = trigger >> SimpleNode if trigger else SimpleNode
30
-
31
- class Outputs(BaseWorkflow.Outputs):
32
- output = SimpleNode.Outputs.output
33
-
34
- return TestWorkflow
35
-
36
-
37
- def serialize(workflow_class) -> JsonObject:
38
- """Helper to serialize a workflow."""
39
- return get_workflow_display(workflow_class=workflow_class).serialize()
40
-
41
-
42
12
  def test_manual_trigger_serialization():
43
13
  """Workflow with ManualTrigger serializes with triggers field."""
44
- result = serialize(create_workflow(ManualTrigger))
45
- triggers = cast(JsonArray, result["triggers"])
46
14
 
47
- assert len(triggers) == 1
48
- trigger = cast(JsonObject, triggers[0])
15
+ class SimpleNode(BaseNode):
16
+ pass
49
17
 
50
- assert trigger["type"] == "MANUAL"
51
- assert "id" in trigger
52
- assert "attributes" in trigger
53
- assert trigger["attributes"] == []
54
- assert "definition" not in trigger
18
+ class TestWorkflow(BaseWorkflow[BaseInputs, BaseState]):
19
+ graph = ManualTrigger >> SimpleNode
55
20
 
21
+ result = get_workflow_display(workflow_class=TestWorkflow).serialize()
22
+ assert "triggers" in result
23
+ triggers = result["triggers"]
24
+ assert isinstance(triggers, list)
56
25
 
57
- def test_no_trigger_serialization():
58
- """Workflow without trigger has no triggers field."""
59
- result = serialize(create_workflow())
60
- assert "triggers" not in result
26
+ assert len(triggers) == 1
27
+ assert triggers[0] == {"id": "b09c1902-3cca-4c79-b775-4c32e3e88466", "type": "MANUAL", "attributes": []}
61
28
 
62
29
 
63
30
  def test_manual_trigger_multiple_entrypoints():
64
31
  """ManualTrigger with multiple entrypoints."""
65
32
 
66
33
  class NodeA(BaseNode):
67
- class Outputs(BaseNode.Outputs):
68
- output = Inputs.input
34
+ pass
69
35
 
70
36
  class NodeB(BaseNode):
71
- class Outputs(BaseNode.Outputs):
72
- output = Inputs.input
37
+ pass
73
38
 
74
- class MultiWorkflow(BaseWorkflow[Inputs, BaseState]):
39
+ class MultiWorkflow(BaseWorkflow[BaseInputs, BaseState]):
75
40
  graph = ManualTrigger >> {NodeA, NodeB}
76
41
 
77
- class Outputs(BaseWorkflow.Outputs):
78
- output_a = NodeA.Outputs.output
79
- output_b = NodeB.Outputs.output
80
-
81
- result = serialize(MultiWorkflow)
82
- triggers = cast(JsonArray, result["triggers"])
83
- workflow_data = cast(JsonObject, result["workflow_raw_data"])
84
- nodes = cast(JsonArray, workflow_data["nodes"])
85
-
86
- assert len(triggers) == 1
87
- trigger = cast(JsonObject, triggers[0])
88
- assert trigger["type"] == "MANUAL"
89
- assert len([n for n in nodes if cast(JsonObject, n)["type"] == "GENERIC"]) >= 2
42
+ result = get_workflow_display(workflow_class=MultiWorkflow).serialize()
43
+ workflow_data = result["workflow_raw_data"]
44
+ assert isinstance(workflow_data, dict)
45
+ assert "nodes" in workflow_data
46
+ nodes = workflow_data["nodes"]
47
+ assert isinstance(nodes, list)
90
48
 
49
+ # entrypoint + 2 nodes
50
+ assert len(nodes) == 3
91
51
 
92
- def test_serialized_workflow_structure():
93
- """Verify complete structure of serialized workflow."""
94
- result = serialize(create_workflow(ManualTrigger))
95
- workflow_raw_data = cast(JsonObject, result["workflow_raw_data"])
96
- definition = cast(JsonObject, workflow_raw_data["definition"])
52
+ assert "triggers" in result
53
+ triggers = result["triggers"]
54
+ assert isinstance(triggers, list)
97
55
 
98
- assert result.keys() == {"workflow_raw_data", "input_variables", "state_variables", "output_variables", "triggers"}
99
- assert workflow_raw_data.keys() == {"nodes", "edges", "display_data", "definition", "output_values"}
100
- assert definition["name"] == "TestWorkflow"
56
+ assert len(triggers) == 1
57
+ assert triggers[0] == {"id": "b09c1902-3cca-4c79-b775-4c32e3e88466", "type": "MANUAL", "attributes": []}
101
58
 
102
59
 
103
60
  def test_unknown_trigger_type():
@@ -106,5 +63,11 @@ def test_unknown_trigger_type():
106
63
  class UnknownTrigger(BaseTrigger):
107
64
  pass
108
65
 
66
+ class SimpleNode(BaseNode):
67
+ pass
68
+
69
+ class TestWorkflow(BaseWorkflow[BaseInputs, BaseState]):
70
+ graph = UnknownTrigger >> SimpleNode
71
+
109
72
  with pytest.raises(ValueError, match="Unknown trigger type: UnknownTrigger"):
110
- serialize(create_workflow(UnknownTrigger))
73
+ get_workflow_display(workflow_class=TestWorkflow).serialize()
@@ -50,6 +50,7 @@ from vellum.workflows.references.execution_count import ExecutionCountReference
50
50
  from vellum.workflows.references.lazy import LazyReference
51
51
  from vellum.workflows.references.output import OutputReference
52
52
  from vellum.workflows.references.state_value import StateValueReference
53
+ from vellum.workflows.references.trigger import TriggerAttributeReference
53
54
  from vellum.workflows.references.vellum_secret import VellumSecretReference
54
55
  from vellum.workflows.references.workflow_input import WorkflowInputReference
55
56
  from vellum.workflows.types.core import JsonArray, JsonObject
@@ -347,6 +348,17 @@ def serialize_value(executable_id: UUID, display_context: "WorkflowDisplayContex
347
348
  "node_id": str(node_class_display.node_id),
348
349
  }
349
350
 
351
+ if isinstance(value, TriggerAttributeReference):
352
+ # Generate trigger ID using the same hash formula as in base_workflow_display.py
353
+ trigger_class = value.trigger_class
354
+ trigger_id = uuid4_from_hash(trigger_class.__qualname__)
355
+
356
+ return {
357
+ "type": "TRIGGER_ATTRIBUTE",
358
+ "trigger_id": str(trigger_id),
359
+ "attribute_id": str(value.id),
360
+ }
361
+
350
362
  if isinstance(value, list):
351
363
  serialized_items = []
352
364
  for item in value:
@@ -474,7 +474,7 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
474
474
  )
475
475
 
476
476
  # Return as a list with a single trigger object matching Django schema
477
- trigger_id = uuid4_from_hash(f"{trigger_class.__module__} | {trigger_class.__qualname__}")
477
+ trigger_id = uuid4_from_hash(trigger_class.__qualname__)
478
478
 
479
479
  # Serialize trigger attributes like node outputs
480
480
  attribute_references = trigger_class.attribute_references().values()