vellum-ai 1.6.2__py3-none-any.whl → 1.6.4__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.
@@ -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.6.2",
30
+ "User-Agent": "vellum-ai/1.6.4",
31
31
  "X-Fern-Language": "Python",
32
32
  "X-Fern-SDK-Name": "vellum-ai",
33
- "X-Fern-SDK-Version": "1.6.2",
33
+ "X-Fern-SDK-Version": "1.6.4",
34
34
  **(self.get_custom_headers() or {}),
35
35
  }
36
36
  if self._api_version is not None:
@@ -6,6 +6,19 @@ if TYPE_CHECKING:
6
6
  from vellum.workflows.workflows.base import BaseWorkflow
7
7
 
8
8
 
9
+ def import_workflow_class() -> Type["BaseWorkflow"]:
10
+ """
11
+ Helper function to help avoid circular imports.
12
+
13
+ Ideally, we use the one in types.generics, but _that_ causes circular imports
14
+ due to the `src/vellum/workflows/types/definition.py` module's import of `EnvironmentVariableReference`
15
+ """
16
+
17
+ from vellum.workflows.workflows import BaseWorkflow
18
+
19
+ return BaseWorkflow
20
+
21
+
9
22
  class NodeException(Exception):
10
23
  def __init__(
11
24
  self,
@@ -35,12 +48,13 @@ class WorkflowInitializationException(Exception):
35
48
  def __init__(
36
49
  self,
37
50
  message: str,
38
- workflow_definition: Type["BaseWorkflow"],
51
+ workflow_definition: Optional[Type["BaseWorkflow"]] = None,
39
52
  code: WorkflowErrorCode = WorkflowErrorCode.INVALID_INPUTS,
40
53
  ):
54
+
41
55
  self.message = message
42
56
  self.code = code
43
- self.definition = workflow_definition
57
+ self.definition = workflow_definition if workflow_definition is not None else import_workflow_class()
44
58
  super().__init__(message)
45
59
 
46
60
  @property
@@ -1,13 +1,14 @@
1
- from typing import ClassVar, Union
1
+ from typing import ClassVar, Generic, Union
2
2
 
3
3
  from vellum.client.types.vellum_error import VellumError
4
4
  from vellum.workflows.errors.types import WorkflowError, WorkflowErrorCode, vellum_error_to_workflow_error
5
5
  from vellum.workflows.exceptions import NodeException
6
6
  from vellum.workflows.nodes.bases.base import BaseNode
7
7
  from vellum.workflows.ports import NodePorts
8
+ from vellum.workflows.types.generics import StateType
8
9
 
9
10
 
10
- class ErrorNode(BaseNode):
11
+ class ErrorNode(BaseNode[StateType], Generic[StateType]):
11
12
  """
12
13
  Used to raise an error to reject the surrounding Workflow.
13
14
 
@@ -23,6 +23,7 @@ from vellum import (
23
23
  from vellum.client.core import RequestOptions
24
24
  from vellum.client.core.api_error import ApiError
25
25
  from vellum.client.types.code_executor_secret_input import CodeExecutorSecretInput
26
+ from vellum.workflows.constants import undefined
26
27
  from vellum.workflows.errors.types import WorkflowErrorCode
27
28
  from vellum.workflows.exceptions import NodeException
28
29
  from vellum.workflows.nodes.bases import BaseNode
@@ -147,6 +148,8 @@ class CodeExecutionNode(BaseNode[StateType], Generic[StateType, _OutputType], me
147
148
  compiled_inputs: List[CodeExecutorInput] = []
148
149
 
149
150
  for input_name, input_value in self.code_inputs.items():
151
+ if input_value is undefined:
152
+ continue
150
153
  if isinstance(input_value, str):
151
154
  compiled_inputs.append(
152
155
  StringInput(
@@ -13,6 +13,7 @@ from vellum.client.types.code_executor_secret_input import CodeExecutorSecretInp
13
13
  from vellum.client.types.function_call import FunctionCall
14
14
  from vellum.client.types.number_input import NumberInput
15
15
  from vellum.client.types.string_chat_message_content import StringChatMessageContent
16
+ from vellum.workflows.constants import undefined
16
17
  from vellum.workflows.errors import WorkflowErrorCode
17
18
  from vellum.workflows.exceptions import NodeException
18
19
  from vellum.workflows.inputs.base import BaseInputs
@@ -1278,3 +1279,85 @@ def main(secret: str) -> str:
1278
1279
  packages=[],
1279
1280
  request_options=None,
1280
1281
  )
1282
+
1283
+
1284
+ def test_run_node__undefined_input_skipped():
1285
+ """
1286
+ Confirm that when an undefined value is passed as an input, it is skipped rather than raising an error.
1287
+ The function should use the default parameter value when undefined input is skipped.
1288
+ """
1289
+
1290
+ # GIVEN a node with both a valid input and an undefined input that runs inline
1291
+ class State(BaseState):
1292
+ pass
1293
+
1294
+ class ExampleCodeExecutionNode(CodeExecutionNode[State, int]):
1295
+ code = """\
1296
+ def main(word: str, undefined_input: str = "default") -> int:
1297
+ return len(word) + len(undefined_input)
1298
+ """
1299
+ runtime = "PYTHON_3_11_6"
1300
+ packages = []
1301
+
1302
+ code_inputs = {
1303
+ "word": "hello",
1304
+ "undefined_input": undefined,
1305
+ }
1306
+
1307
+ # WHEN we run the node
1308
+ node = ExampleCodeExecutionNode(state=State())
1309
+ outputs = node.run()
1310
+
1311
+ # THEN the node should run successfully without raising an error
1312
+ assert outputs == {"result": 12, "log": ""} # len("hello") + len("default") = 5 + 7 = 12
1313
+
1314
+
1315
+ def test_run_node__undefined_input_skipped_api_execution(vellum_client):
1316
+ """
1317
+ Confirm that when an undefined value is passed as an input in API execution mode,
1318
+ it is skipped rather than raising an error.
1319
+ """
1320
+
1321
+ # GIVEN a node with both a valid input and an undefined input that forces API execution
1322
+ class State(BaseState):
1323
+ pass
1324
+
1325
+ class ExampleCodeExecutionNode(CodeExecutionNode[State, int]):
1326
+ code = """\
1327
+ def main(word: str) -> int:
1328
+ return len(word)
1329
+ """
1330
+ runtime = "PYTHON_3_11_6"
1331
+ packages = [CodeExecutionPackage(name="requests", version="2.0.0")]
1332
+
1333
+ code_inputs = {
1334
+ "word": "hello",
1335
+ "undefined_input": undefined,
1336
+ }
1337
+
1338
+ # AND we know what the Code Execution Node will respond with
1339
+ mock_code_execution = CodeExecutorResponse(
1340
+ log="",
1341
+ output=NumberVellumValue(value=5),
1342
+ )
1343
+ vellum_client.execute_code.return_value = mock_code_execution
1344
+
1345
+ # WHEN we run the node
1346
+ node = ExampleCodeExecutionNode(state=State())
1347
+ outputs = node.run()
1348
+
1349
+ # THEN the node should run successfully without raising an error
1350
+ assert outputs == {"result": 5, "log": ""}
1351
+
1352
+ # AND the API should be called with only the non-undefined input
1353
+ vellum_client.execute_code.assert_called_once_with(
1354
+ input_values=[StringInput(name="word", value="hello")],
1355
+ code="""\
1356
+ def main(word: str) -> int:
1357
+ return len(word)
1358
+ """,
1359
+ runtime="PYTHON_3_11_6",
1360
+ output_type="NUMBER",
1361
+ packages=[CodeExecutionPackage(name="requests", version="2.0.0")],
1362
+ request_options=None,
1363
+ )
@@ -4,6 +4,7 @@ import sys
4
4
  import traceback
5
5
  from typing import Any, Optional, Tuple, Union
6
6
 
7
+ from vellum.workflows.constants import undefined
7
8
  from vellum.workflows.errors.types import WorkflowErrorCode
8
9
  from vellum.workflows.exceptions import NodeException
9
10
  from vellum.workflows.nodes.utils import cast_to_output_type, wrap_inputs_for_backward_compatibility
@@ -54,7 +55,7 @@ def run_code_inline(
54
55
  "__arg__out": None,
55
56
  "print": _inline_print,
56
57
  }
57
- run_args = [f"{name}=__arg__inputs['{name}']" for name in inputs.keys()]
58
+ run_args = [f"{name}=__arg__inputs['{name}']" for name, value in inputs.items() if value is not undefined]
58
59
  execution_code = f"""\
59
60
  {code}
60
61
 
@@ -1,4 +1,4 @@
1
- from typing import Set
1
+ from typing import Generic, Set
2
2
 
3
3
  from vellum.workflows.nodes.bases import BaseNode
4
4
  from vellum.workflows.outputs.base import BaseOutputs
@@ -6,9 +6,10 @@ from vellum.workflows.ports.port import Port
6
6
  from vellum.workflows.ports.utils import validate_ports
7
7
  from vellum.workflows.state.base import BaseState
8
8
  from vellum.workflows.types import MergeBehavior
9
+ from vellum.workflows.types.generics import StateType
9
10
 
10
11
 
11
- class ConditionalNode(BaseNode):
12
+ class ConditionalNode(BaseNode[StateType], Generic[StateType]):
12
13
  """
13
14
  Used to conditionally determine which port to invoke next. This node exists to be backwards compatible with
14
15
  Vellum's Conditional Node, and for most cases, you should extend `BaseNode.Ports` directly.
@@ -1,8 +1,11 @@
1
+ from typing import Generic
2
+
1
3
  from vellum.workflows.nodes.bases import BaseNode
2
4
  from vellum.workflows.types import MergeBehavior
5
+ from vellum.workflows.types.generics import StateType
3
6
 
4
7
 
5
- class MergeNode(BaseNode):
8
+ class MergeNode(BaseNode[StateType], Generic[StateType]):
6
9
  """
7
10
  Used to merge the control flow of multiple nodes into a single node. This node exists to be backwards compatible
8
11
  with Vellum's Merge Node, and for most cases, you should extend from `BaseNode.Trigger` directly.
@@ -1,8 +1,11 @@
1
+ from typing import Generic
2
+
1
3
  from vellum.workflows.nodes.bases import BaseNode
2
4
  from vellum.workflows.types import MergeBehavior
5
+ from vellum.workflows.types.generics import StateType
3
6
 
4
7
 
5
- class NoteNode(BaseNode):
8
+ class NoteNode(BaseNode[StateType], Generic[StateType]):
6
9
  """
7
10
  A no-op Node purely used to display a note in the Vellum UI.
8
11
  """
@@ -145,6 +145,14 @@ class WorkflowRunner(Generic[StateType]):
145
145
  ]
146
146
  self._is_resuming = True
147
147
  elif previous_execution_id:
148
+ if not self.workflow.resolvers:
149
+ raise WorkflowInitializationException(
150
+ message=f"No resolvers configured to load initial state for execution ID: {previous_execution_id}",
151
+ workflow_definition=self.workflow.__class__,
152
+ code=WorkflowErrorCode.INVALID_INPUTS,
153
+ )
154
+
155
+ resolver_failed = True
148
156
  for resolver in self.workflow.resolvers:
149
157
  try:
150
158
  load_state_result = resolver.load_state(previous_execution_id)
@@ -161,10 +169,19 @@ class WorkflowRunner(Generic[StateType]):
161
169
  load_state_result.root_trace_id,
162
170
  load_state_result.root_span_id,
163
171
  )
172
+ resolver_failed = False
164
173
  break
165
174
  except Exception as e:
166
175
  logger.warning(f"Failed to load state from resolver {type(resolver).__name__}: {e}")
167
176
  continue
177
+
178
+ if resolver_failed:
179
+ raise WorkflowInitializationException(
180
+ message=f"All resolvers failed to load initial state for execution ID: {previous_execution_id}",
181
+ workflow_definition=self.workflow.__class__,
182
+ code=WorkflowErrorCode.INVALID_INPUTS,
183
+ )
184
+
168
185
  self._entrypoints = self.workflow.get_entrypoints()
169
186
  else:
170
187
  normalized_inputs = deepcopy(inputs) if inputs else self.workflow.get_default_inputs()
@@ -87,6 +87,9 @@ def primitive_type_to_vellum_variable_type(type_: Union[Type, BaseDescriptor]) -
87
87
  elif _is_type_optionally_in(type_, (VellumError, VellumErrorRequest)):
88
88
  return "ERROR"
89
89
 
90
+ if type_ is typing.Any:
91
+ return "JSON"
92
+
90
93
  builtin_list_type = _builtin_list_to_vellum_type(type_)
91
94
  if builtin_list_type:
92
95
  return builtin_list_type
@@ -63,6 +63,7 @@ from vellum.workflows.events.workflow import (
63
63
  WorkflowExecutionStreamingBody,
64
64
  WorkflowExecutionStreamingEvent,
65
65
  )
66
+ from vellum.workflows.exceptions import WorkflowInitializationException
66
67
  from vellum.workflows.executable import BaseExecutable
67
68
  from vellum.workflows.graph import Graph
68
69
  from vellum.workflows.inputs.base import BaseInputs
@@ -681,7 +682,16 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
681
682
  @staticmethod
682
683
  def load_from_module(module_path: str) -> Type["BaseWorkflow"]:
683
684
  workflow_path = f"{module_path}.workflow"
684
- module = importlib.import_module(workflow_path)
685
+ try:
686
+ module = importlib.import_module(workflow_path)
687
+ except TypeError as e:
688
+ if "Unexpected graph type" in str(e) or "unhashable type: 'set'" in str(e):
689
+ raise WorkflowInitializationException(
690
+ message="Invalid graph structure detected. Nested sets or unsupported graph types are not allowed. "
691
+ "Please contact Vellum support for assistance with Workflow configuration."
692
+ ) from e
693
+ else:
694
+ raise
685
695
  workflows: List[Type[BaseWorkflow]] = []
686
696
  for name in dir(module):
687
697
  if name.startswith("__"):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 1.6.2
3
+ Version: 1.6.4
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -3,7 +3,7 @@ vellum_cli/README.md,sha256=2NudRoLzWxNKqnuVy1JuQ7DerIaxWGYkrH8kMd-asIE,90
3
3
  vellum_cli/__init__.py,sha256=CCTCiCvwxjFHXVSKhzUtZ3GE_h4HrbLEX_XC3qjPuAk,13618
4
4
  vellum_cli/aliased_group.py,sha256=ugW498j0yv4ALJ8vS9MsO7ctDW7Jlir9j6nE_uHAP8c,3363
5
5
  vellum_cli/config.py,sha256=qJrd8W__UZZaUMAG6BO3sxfkgpCoOS4NA3QfqneW-jE,9588
6
- vellum_cli/image_push.py,sha256=eeOBqKtKkPu6Kgm_jQCVCivogzAcdlIlkv61-QxH67c,11006
6
+ vellum_cli/image_push.py,sha256=OLHfIlhKpqfaU2L2DhSkk0J_hQPUZ111lN2tqVDAywo,11554
7
7
  vellum_cli/init.py,sha256=WpnMXPItPmh0f0bBGIer3p-e5gu8DUGwSArT_FuoMEw,5093
8
8
  vellum_cli/logger.py,sha256=dcM_OmgqXLo93vDYswO5ylyUQQcTfnA5GTd5tbIt3wM,1446
9
9
  vellum_cli/move.py,sha256=lCHQ-U4BspgS512GxFFvUrglitaHkWfuKn1Hpfcn7-Q,2053
@@ -14,7 +14,7 @@ vellum_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
14
14
  vellum_cli/tests/conftest.py,sha256=wx3PlJjVB0HRf5dr2b_idOIw27WPPl0J0FNbhIJJaVk,1689
15
15
  vellum_cli/tests/test_config.py,sha256=uvKGDc8BoVyT9_H0Z-g8469zVxomn6Oi3Zj-vK7O_wU,2631
16
16
  vellum_cli/tests/test_image_push.py,sha256=X0YOPdoaAnWtK9IQTxaN_wWpw08-5G3v76k1Wu53JpU,12801
17
- vellum_cli/tests/test_image_push_error_handling.py,sha256=QRH0eYNEEIkD2-EVFQWYexOKlhMB6puh1GouovrE-VU,7647
17
+ vellum_cli/tests/test_image_push_error_handling.py,sha256=iKtbvii9sP1aFTlfm0fFBVeYltHf7RMGPPt_d4Te73A,10375
18
18
  vellum_cli/tests/test_init.py,sha256=C_rV4lu-ab5iFoLRizs1XAUnSPdMf7oFuc1i4N4udOU,18285
19
19
  vellum_cli/tests/test_main.py,sha256=qDZG-aQauPwBwM6A2DIu1494n47v3pL28XakTbLGZ-k,272
20
20
  vellum_cli/tests/test_move.py,sha256=FIrL1xlH5oFKGX2MugcTKL8JilpopmUC7hP5OaqF5zw,5213
@@ -48,7 +48,7 @@ vellum_ee/workflows/display/nodes/vellum/final_output_node.py,sha256=z4oeTgKnMGV
48
48
  vellum_ee/workflows/display/nodes/vellum/guardrail_node.py,sha256=9_AslWjzj4RHH2sq3SIaq9FU0NCg7ex5TIWrNMybqXg,2173
49
49
  vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=KA4U5NADWCBBbTG0f76OOFW9HvlWd5DfZoqN5Y0ZK9c,12167
50
50
  vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py,sha256=m20_ZZ3Au0ZCpI3TNC9xh54ld1X13CNq-T51VOtP23k,6434
51
- vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=AW5JuHXtmtcJKeRiZKd7iB1re_D7G7jl_OlaZs8nUl0,4219
51
+ vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=we7NENZpci-LiWXJFTPWzliCMdjzCMMMWUCfgJ-oP0g,4297
52
52
  vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=xMHaPfTSZWYprQenlHm2g47u0a5O9Me_dhAjfqo8nKQ,3116
53
53
  vellum_ee/workflows/display/nodes/vellum/note_node.py,sha256=6PcAFA_EJn7vEMdqgoRjYTLHwnXCrJv80B10zuUx4jE,1026
54
54
  vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=uo4YZRV48iX4peGAA1xkGniSPrepywvW_gS7codt-VQ,3378
@@ -93,7 +93,7 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node
93
93
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py,sha256=LnZp1YXDn-AaaxiYgxrhCQeH-rLgmlu_r_lvM65EQ5w,7517
94
94
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_prompt_node_serialization.py,sha256=-A9CAnzIhPMn44l_dwZcofBXBPNFa-oHke9p0w3W5PA,26358
95
95
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py,sha256=gn7kisqAEG9JCZ18V88qMGvlcsTulSwpWvvEwjCfh6Y,21871
96
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py,sha256=ALNOn5BdcWxw0myi6pXi5IwWtTE9H2CGrodP1BANuAg,16790
96
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py,sha256=I1Mlt2Iy88C98IpJ21jl4Xu7TtSHqyx_4A4bjJgTJUE,16990
97
97
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py,sha256=UnfWTfKN8DrOLpjCfWMgENJBjgNLWcRVfexbwERKY-c,8501
98
98
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_prompt_deployment_serialization.py,sha256=SGlBiwohTTuf7oXqO3R1lP6u7gPea8Elt1CpZU_W9Bk,21482
99
99
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_serialization.py,sha256=JKfYNBCmy7n6v2fd6NyYGHI1_TJRlfDNWiZMvHrdCWU,13079
@@ -110,6 +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_terminal_node_any_serialization.py,sha256=4WAmSEJZlDBLPhsD1f4GwY9ahB9F6qJKGnL6j7ZYlzQ,1740
113
114
  vellum_ee/workflows/display/tests/workflow_serialization/test_web_search_node_serialization.py,sha256=vbDFBrWUPeeW7cxjNA6SXrsHlYcbOAhlQ4C45Vdnr1c,3428
114
115
  vellum_ee/workflows/display/tests/workflow_serialization/test_workflow_input_parameterization_error.py,sha256=vAdmn3YTBDpo55znbydQxsgg9ASqHcvsUPwiBR_7wfo,1461
115
116
  vellum_ee/workflows/display/types.py,sha256=LgRIZeEtV7bQe-nvrC4A0T6vMooSWT1rFtw-uTn45BQ,3752
@@ -151,7 +152,7 @@ vellum_ee/workflows/tests/local_workflow/nodes/templating_node.py,sha256=NQwFN61
151
152
  vellum_ee/workflows/tests/local_workflow/workflow.py,sha256=A4qOzOPNwePYxWbcAgIPLsmrVS_aVEZEc-wULSv787Q,393
152
153
  vellum_ee/workflows/tests/test_display_meta.py,sha256=PkXJVnMZs9GNooDkd59n4YTBAX3XGPQWeSSVbhehVFM,5112
153
154
  vellum_ee/workflows/tests/test_registry.py,sha256=B8xRIuEyLWfSqrYoPldNQXhKPfe50PllvtAZoI8-uPs,6066
154
- vellum_ee/workflows/tests/test_serialize_module.py,sha256=d4ZpMd3oIxiq-sBXeSQESS6ix6-1P6rdCRFqBEReJIU,2882
155
+ vellum_ee/workflows/tests/test_serialize_module.py,sha256=zleQTcGZa5_nzwu4zpFoqEHhk7pb64hGrhObR4anhPQ,4471
155
156
  vellum_ee/workflows/tests/test_server.py,sha256=dXFBraU99Y6cKp2aBhLFXQTScSRcE9WaWjo1z9piqdU,23344
156
157
  vellum_ee/workflows/tests/test_virtual_files.py,sha256=TJEcMR0v2S8CkloXNmCHA0QW0K6pYNGaIjraJz7sFvY,2762
157
158
  vellum/__init__.py,sha256=i7aah42VTp3735JX69uHUipLZBD8PwX_CXUY_9t-zuY,49356
@@ -159,7 +160,7 @@ vellum/client/README.md,sha256=flqu57ubZNTfpq60CdLtJC9gp4WEkyjb_n_eZ4OYf9w,6497
159
160
  vellum/client/__init__.py,sha256=rMnKRqL5-356SBc-rfm56MkO87PuAi2mtcfBszcJU1M,74316
160
161
  vellum/client/core/__init__.py,sha256=lTcqUPXcx4112yLDd70RAPeqq6tu3eFMe1pKOqkW9JQ,1562
161
162
  vellum/client/core/api_error.py,sha256=44vPoTyWN59gonCIZMdzw7M1uspygiLnr3GNFOoVL2Q,614
162
- vellum/client/core/client_wrapper.py,sha256=YjwRn4-30uWxm0Owf9bmnnSrmjminxXkRvDqEYfProI,2840
163
+ vellum/client/core/client_wrapper.py,sha256=EQroyMygQaKudbqG-NTWixPzjdmHV_wnRNH5sxTldIY,2840
163
164
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
164
165
  vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
165
166
  vellum/client/core/force_multipart.py,sha256=awxh5MtcRYe74ehY8U76jzv6fYM_w_D3Rur7KQQzSDk,429
@@ -1813,7 +1814,7 @@ vellum/workflows/events/tests/test_basic_workflow.py,sha256=Pj6orHsXz37jWC5FARi0
1813
1814
  vellum/workflows/events/tests/test_event.py,sha256=jsC8APdrddLs5ejk9NO0waPLLslsJlBMIvN0M_vpAHI,19692
1814
1815
  vellum/workflows/events/types.py,sha256=mVrqAH9Hs9SpXm08Hcxdyap_ImQPwGsxRr56rSNMP34,5043
1815
1816
  vellum/workflows/events/workflow.py,sha256=kLSWFXiDZH0TELWoDjQ_kHVomFnw8MVVUPDGIzgyosw,8997
1816
- vellum/workflows/exceptions.py,sha256=jyqfR-oq6iMAXSqaOexc-RjL1uFVEuxgcZ_r8AMaAn8,1463
1817
+ vellum/workflows/exceptions.py,sha256=nb05hAh-afzZ9hek7vhegq-zDtmHopv-4No3aZ-gKO4,1941
1817
1818
  vellum/workflows/executable.py,sha256=um-gLJMVYfGJwGJfZIPlCRHhHIYm6pn8PUEfeqrNx5k,218
1818
1819
  vellum/workflows/expressions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1819
1820
  vellum/workflows/expressions/accessor.py,sha256=3lu1-_-dBfZdJvtX-q66jbmRAZtqIfdsh_3_JNuzg1E,4462
@@ -1885,7 +1886,7 @@ vellum/workflows/nodes/bases/tests/test_base_adornment_node.py,sha256=fXZI9KqpS4
1885
1886
  vellum/workflows/nodes/bases/tests/test_base_node.py,sha256=igJrcSxSZADk5tLpssAt0uAXe9oJoLbilzueJJvA5p4,10942
1886
1887
  vellum/workflows/nodes/core/__init__.py,sha256=5zDMCmyt1v0HTJzlUBwq3U9L825yZGZhT9JL18-mRR4,455
1887
1888
  vellum/workflows/nodes/core/error_node/__init__.py,sha256=g7RRnlHhqu4qByfLjBwCunmgGA8dI5gNsjS3h6TwlSI,60
1888
- vellum/workflows/nodes/core/error_node/node.py,sha256=GpvvJS0F1TzOHFPRYJVQMYy96C_MCOeZGo_pzt63bfU,1357
1889
+ vellum/workflows/nodes/core/error_node/node.py,sha256=CwGMLIq6z_MogmjlicBLZm2pE7VW69ZL8fnJzNEVKHs,1451
1889
1890
  vellum/workflows/nodes/core/inline_subworkflow_node/__init__.py,sha256=nKNEH1QTl-1PcvmYoqSWEl0-t6gAur8GLTXHzklRQfM,84
1890
1891
  vellum/workflows/nodes/core/inline_subworkflow_node/node.py,sha256=N6Yh1z_xlZif9bBPmpwOC3xzxaru3_F5sbFaYHm2x3A,7222
1891
1892
  vellum/workflows/nodes/core/inline_subworkflow_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -1928,14 +1929,14 @@ vellum/workflows/nodes/displayable/bases/tests/test_utils.py,sha256=eqdqbKNRWVMD
1928
1929
  vellum/workflows/nodes/displayable/bases/types.py,sha256=C37B2Qh2YP7s7pUjd-EYKc2Zl1TbnCgI_mENuUSb8bo,1706
1929
1930
  vellum/workflows/nodes/displayable/bases/utils.py,sha256=X1YSPmbBlxDrTAw6dy2-9HrIG8Vb_J-2k1lP3i-SOsk,6951
1930
1931
  vellum/workflows/nodes/displayable/code_execution_node/__init__.py,sha256=0FLWMMktpzSnmBMizQglBpcPrP80fzVsoJwJgf822Cg,76
1931
- vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=cCpZPna1Exw060KlI8j3oMCfru1GCzUFJz9ucWdcZVM,10311
1932
+ vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=t4AuqyGOSgitYUUs6Tqzo7lDeyOyJ5g8x7Q9j4UkH7M,10426
1932
1933
  vellum/workflows/nodes/displayable/code_execution_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1933
1934
  vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1934
1935
  vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/main.py,sha256=5QsbmkzSlSbcbWTG_JmIqcP-JNJzOPTKxGzdHos19W4,79
1935
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py,sha256=1XadX-I240ghXDSLBejYfDNUpDZcdQbB6aroCHlIBt4,38285
1936
- vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=GxSjRBiH8kNg2dXUVTtnX47fqPIFoGHxRB4ySU6uVPk,3225
1936
+ vellum/workflows/nodes/displayable/code_execution_node/tests/test_node.py,sha256=RQy7SsA39kTJAudzpYNN3jK_kuq6bWNZUld54H5pRZA,40966
1937
+ vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=0F_4PVvB7vjsV0RS48Brv_4djebWOMR_zgzHxzA9iV4,3308
1937
1938
  vellum/workflows/nodes/displayable/conditional_node/__init__.py,sha256=AS_EIqFdU1F9t8aLmbZU-rLh9ry6LCJ0uj0D8F0L5Uw,72
1938
- vellum/workflows/nodes/displayable/conditional_node/node.py,sha256=71ZUNfTiD7t2Kai2ypw0tmv1lSf1brQaHAQD-SeUrGE,1101
1939
+ vellum/workflows/nodes/displayable/conditional_node/node.py,sha256=2g32hWosE3zwVaK2UPFnVEer1Nbn04s3friF2rGztmE,1195
1939
1940
  vellum/workflows/nodes/displayable/conftest.py,sha256=K2kLM2JGAfcrmmd92u8DXInUO5klFdggPWblg5RVcx4,5729
1940
1941
  vellum/workflows/nodes/displayable/final_output_node/__init__.py,sha256=G7VXM4OWpubvSJtVkGmMNeqgb9GkM7qZT838eL18XU4,72
1941
1942
  vellum/workflows/nodes/displayable/final_output_node/node.py,sha256=1_F9S7w-gIEUoXIdilgpqDeCKxJ0_J0oTYvE35dbjHk,4908
@@ -1951,9 +1952,9 @@ vellum/workflows/nodes/displayable/inline_prompt_node/node.py,sha256=0O1dksk0h6-
1951
1952
  vellum/workflows/nodes/displayable/inline_prompt_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1952
1953
  vellum/workflows/nodes/displayable/inline_prompt_node/tests/test_node.py,sha256=bBHs90mV5SZ3rJPAL0wx4WWyawUA406LgMPOdvpZC_A,10923
1953
1954
  vellum/workflows/nodes/displayable/merge_node/__init__.py,sha256=J8IC08dSH7P76wKlNuxe1sn7toNGtSQdFirUbtPDEs0,60
1954
- vellum/workflows/nodes/displayable/merge_node/node.py,sha256=nZtGGVAvY4fvGg8vwV6sTQ8_QLRnigeXt0vf2FL272A,450
1955
+ vellum/workflows/nodes/displayable/merge_node/node.py,sha256=_zHW9fReWdpiM3GiUzdEgyOSznRqkPsTj-XKD6Oja8I,563
1955
1956
  vellum/workflows/nodes/displayable/note_node/__init__.py,sha256=KWA3P4fyYJ-fOTky8qNGlcOotQ-HeHJ9AjZt6mRQmCE,58
1956
- vellum/workflows/nodes/displayable/note_node/node.py,sha256=sIN1VBQ7zeT3GhN0kupXbFfdpvgedWV79k4woJNp5IQ,394
1957
+ vellum/workflows/nodes/displayable/note_node/node.py,sha256=aO54y6p5FjQe4XOSlQVRSVby4a5Ikp_ZS98GiNkM8Gs,507
1957
1958
  vellum/workflows/nodes/displayable/prompt_deployment_node/__init__.py,sha256=krX1Hds-TSVYZsx0wJFX4wsAKkEFYOX1ifwRGiIM-EA,82
1958
1959
  vellum/workflows/nodes/displayable/prompt_deployment_node/node.py,sha256=XeBn6d3ce-RJuUiTwpDKUKUJvcYLQw61YufpIGVWPHY,2641
1959
1960
  vellum/workflows/nodes/displayable/prompt_deployment_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -2018,7 +2019,7 @@ vellum/workflows/resolvers/resolver.py,sha256=yK-oY0HDsFJcjlNKAm3vpsPKRIFerIh59F
2018
2019
  vellum/workflows/resolvers/tests/test_resolver.py,sha256=jXkJBb9SwtoH__bBN-ECohpyD0aTIB9ErEvtFhuTMQM,9750
2019
2020
  vellum/workflows/resolvers/types.py,sha256=Hndhlk69g6EKLh_LYg5ILepW5U_h_BYNllfzhS9k8p4,237
2020
2021
  vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
2021
- vellum/workflows/runner/runner.py,sha256=uG2iu_rMwEApUFbhv4BPfD5-RwqASQmL_AAedOZr6eQ,41502
2022
+ vellum/workflows/runner/runner.py,sha256=-x4Ud63HwH81LufNJXzUDP1LTv6yJZ9bBG-OLkPRHMA,42299
2022
2023
  vellum/workflows/sandbox.py,sha256=mezSZmilR_fwR8164n8CEfzlMeQ55IqfapHp4ftImvQ,3212
2023
2024
  vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
2024
2025
  vellum/workflows/state/base.py,sha256=m9fCqbZn21GshCVCjJTD1dPZEQjFrsMXqlg7tM9fIwM,24283
@@ -2053,17 +2054,17 @@ vellum/workflows/utils/tests/test_names.py,sha256=DnRRnuORxQXx9ESegCzkxiWcHy2_bB
2053
2054
  vellum/workflows/utils/tests/test_uuids.py,sha256=i77ABQ0M3S-aFLzDXHJq_yr5FPkJEWCMBn1HJ3DObrE,437
2054
2055
  vellum/workflows/utils/tests/test_vellum_variables.py,sha256=X7b-bbN3bFRx0WG31bowcaOgsXxEPYnh2sgpsqgKIsQ,2096
2055
2056
  vellum/workflows/utils/uuids.py,sha256=IaZQANz7fhF7la0_J1O50Y6D2PIYv_taRDTRzBT9aWw,1284
2056
- vellum/workflows/utils/vellum_variables.py,sha256=Tgv09yYROgq8QZbrKKIOEdg0IQ8Vfgz_vRjY4tYzaTQ,7152
2057
+ vellum/workflows/utils/vellum_variables.py,sha256=X3lZn-EoWengRWBWRhTNW7hqbj7LkV-6NSMwCskWEbg,7203
2057
2058
  vellum/workflows/utils/zip.py,sha256=HVg_YZLmBOTXKaDV3Xhaf3V6sYnfqqZXQ8CpuafkbPY,1181
2058
2059
  vellum/workflows/vellum_client.py,sha256=3iDR7VV_NgLSm1iZQCKDvrmfEaX1bOJiU15QrxyHpv0,1237
2059
2060
  vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
2060
- vellum/workflows/workflows/base.py,sha256=3fx2nq6TXRjut-kg5K7umX-IVuw3QhsTL_6orRm8pMk,29274
2061
+ vellum/workflows/workflows/base.py,sha256=gV3qMaN1hMNEoO3s1Q1G7lMeacTObYDs9LHEwXNTYHE,29819
2061
2062
  vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
2062
2063
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2063
2064
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=Boa-_m9ii2Qsa1RvVM-VYniF7zCpzGgEGy-OnPZkrHg,23941
2064
2065
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
2065
- vellum_ai-1.6.2.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
2066
- vellum_ai-1.6.2.dist-info/METADATA,sha256=uLqOztz36EpdnTmgOFriqcWfXXxEkc5a8MctyqbVR5M,5547
2067
- vellum_ai-1.6.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
2068
- vellum_ai-1.6.2.dist-info/entry_points.txt,sha256=xVavzAKN4iF_NbmhWOlOkHluka0YLkbN_pFQ9pW3gLI,117
2069
- vellum_ai-1.6.2.dist-info/RECORD,,
2066
+ vellum_ai-1.6.4.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
2067
+ vellum_ai-1.6.4.dist-info/METADATA,sha256=kRTvoaljnXd0hIt_CQhEMNHXgv8YFwb5Momhc8sRRjY,5547
2068
+ vellum_ai-1.6.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
2069
+ vellum_ai-1.6.4.dist-info/entry_points.txt,sha256=xVavzAKN4iF_NbmhWOlOkHluka0YLkbN_pFQ9pW3gLI,117
2070
+ vellum_ai-1.6.4.dist-info/RECORD,,
vellum_cli/image_push.py CHANGED
@@ -123,6 +123,13 @@ def image_push_command(
123
123
  suggestion="Make sure your VELLUM_API_KEY environment variable is set correctly.",
124
124
  )
125
125
  return
126
+ elif e.status_code == 400 and isinstance(e.body, dict) and "detail" in e.body:
127
+ handle_cli_error(
128
+ logger,
129
+ title="API request failed",
130
+ message=e.body["detail"],
131
+ )
132
+ return
126
133
  elif e.status_code == 500:
127
134
  handle_cli_error(
128
135
  logger,
@@ -215,6 +222,13 @@ def image_push_command(
215
222
  suggestion="Make sure your VELLUM_API_KEY environment variable is set correctly.",
216
223
  )
217
224
  return
225
+ elif e.status_code == 400 and isinstance(e.body, dict) and "detail" in e.body:
226
+ handle_cli_error(
227
+ logger,
228
+ title="API request failed",
229
+ message=e.body["detail"],
230
+ )
231
+ return
218
232
  elif e.status_code == 500:
219
233
  handle_cli_error(
220
234
  logger,
@@ -62,6 +62,37 @@ def test_image_push_docker_service_token_500_error(mock_docker_from_env, mock_ru
62
62
  assert "try again later" in result.output
63
63
 
64
64
 
65
+ @patch("subprocess.run")
66
+ @patch("docker.from_env")
67
+ def test_image_push_docker_service_token_400_error(mock_docker_from_env, mock_run, vellum_client, monkeypatch):
68
+ monkeypatch.setenv("VELLUM_API_URL", "https://api.vellum.ai")
69
+ monkeypatch.setenv("VELLUM_API_KEY", "123456abcdef")
70
+
71
+ mock_docker_client = mock_docker_from_env.return_value
72
+ mock_docker_client.images.get.return_value.id = "test-image-id"
73
+
74
+ mock_run.side_effect = [
75
+ subprocess.CompletedProcess(args="", returncode=0, stdout=b"Pruning successful"),
76
+ subprocess.CompletedProcess(
77
+ args="", returncode=0, stdout=b'{"manifests": [{"platform": {"architecture": "amd64"}}]}'
78
+ ),
79
+ ]
80
+
81
+ vellum_client.container_images.docker_service_token.side_effect = ApiError(
82
+ status_code=400,
83
+ body={
84
+ "detail": "Invalid image configuration: missing required metadata",
85
+ },
86
+ )
87
+
88
+ runner = CliRunner()
89
+ result = runner.invoke(cli_main, ["image", "push", "myimage:latest"])
90
+
91
+ assert result.exit_code == 1
92
+ assert "API request failed" in result.output
93
+ assert "Invalid image configuration: missing required metadata" in result.output
94
+
95
+
65
96
  @patch("subprocess.run")
66
97
  @patch("docker.from_env")
67
98
  def test_image_push_docker_service_token_other_error(mock_docker_from_env, mock_run, vellum_client, monkeypatch):
@@ -156,6 +187,43 @@ def test_image_push_container_image_500_error(mock_docker_from_env, mock_run, ve
156
187
  assert "try again later" in result.output
157
188
 
158
189
 
190
+ @patch("subprocess.run")
191
+ @patch("docker.from_env")
192
+ def test_image_push_container_image_400_error(mock_docker_from_env, mock_run, vellum_client, monkeypatch):
193
+ monkeypatch.setenv("VELLUM_API_URL", "https://api.vellum.ai")
194
+ monkeypatch.setenv("VELLUM_API_KEY", "123456abcdef")
195
+
196
+ mock_docker_client = mock_docker_from_env.return_value
197
+ mock_docker_client.images.get.return_value.id = "test-image-id"
198
+ mock_docker_client.images.push.return_value = ["pushed"]
199
+
200
+ mock_run.side_effect = [
201
+ subprocess.CompletedProcess(args="", returncode=0, stdout=b"Pruning successful"),
202
+ subprocess.CompletedProcess(
203
+ args="", returncode=0, stdout=b'{"manifests": [{"platform": {"architecture": "amd64"}}]}'
204
+ ),
205
+ subprocess.CompletedProcess(args="", returncode=0, stdout=b'[{"RepoDigests": ["test-repo@sha256:abcd1234"]}]'),
206
+ ]
207
+
208
+ vellum_client.container_images.docker_service_token.return_value = type(
209
+ "obj", (object,), {"access_token": "345678mnopqr", "organization_id": str(uuid4()), "repository": "myrepo.net"}
210
+ )()
211
+
212
+ vellum_client.container_images.push_container_image.side_effect = ApiError(
213
+ status_code=400,
214
+ body={
215
+ "detail": "Image validation failed: unsupported architecture",
216
+ },
217
+ )
218
+
219
+ runner = CliRunner()
220
+ result = runner.invoke(cli_main, ["image", "push", "myimage:latest"])
221
+
222
+ assert result.exit_code == 1
223
+ assert "API request failed" in result.output
224
+ assert "Image validation failed: unsupported architecture" in result.output
225
+
226
+
159
227
  @patch("subprocess.run")
160
228
  @patch("docker.from_env")
161
229
  def test_image_push_container_image_other_error(mock_docker_from_env, mock_run, vellum_client, monkeypatch):
@@ -32,15 +32,6 @@ class BaseMapNodeDisplay(BaseAdornmentNodeDisplay[_MapNodeType], Generic[_MapNod
32
32
 
33
33
  items = raise_if_descriptor(node.items)
34
34
 
35
- items_node_input = create_node_input(
36
- node_id=node_id,
37
- input_name="items",
38
- value=items or [],
39
- display_context=display_context,
40
- input_id=self.node_input_ids_by_name.get("items"),
41
- )
42
- node_inputs = [items_node_input]
43
-
44
35
  subworkflow_display = get_workflow_display(
45
36
  base_display_class=display_context.workflow_display_class,
46
37
  workflow_class=subworkflow,
@@ -58,6 +49,16 @@ class BaseMapNodeDisplay(BaseAdornmentNodeDisplay[_MapNodeType], Generic[_MapNod
58
49
  for input_variable in input_variables
59
50
  if isinstance(input_variable, dict) and input_variable["key"] == "items"
60
51
  )
52
+
53
+ # Use the same ID for the map node's items input as the subworkflow's items input
54
+ items_node_input = create_node_input(
55
+ node_id=node_id,
56
+ input_name="items",
57
+ value=items or [],
58
+ display_context=display_context,
59
+ input_id=str(items_workflow_input_id),
60
+ )
61
+ node_inputs = [items_node_input]
61
62
  item_workflow_input_id = next(
62
63
  input_variable["id"]
63
64
  for input_variable in input_variables
@@ -81,7 +81,7 @@ def test_serialize_workflow():
81
81
  "type": "MAP",
82
82
  "inputs": [
83
83
  {
84
- "id": "4c0a109f-599e-4d04-a396-a51474fc2996",
84
+ "id": "0acef1e7-caa1-4d9f-bb69-6981729af18d",
85
85
  "key": "items",
86
86
  "value": {
87
87
  "rules": [
@@ -393,3 +393,7 @@ def test_serialize_workflow():
393
393
  "workflow",
394
394
  ],
395
395
  }
396
+
397
+ # AND the map node's items input ID should match the subworkflow's items input ID
398
+ items_input_id = map_node["data"]["items_input_id"]
399
+ assert map_node["inputs"][0]["id"] == items_input_id
@@ -0,0 +1,49 @@
1
+ from typing import Any
2
+
3
+ from vellum.workflows.inputs.base import BaseInputs
4
+ from vellum.workflows.nodes.displayable.final_output_node import FinalOutputNode
5
+ from vellum.workflows.state.base import BaseState
6
+ from vellum.workflows.workflows.base import BaseWorkflow
7
+ from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
8
+
9
+
10
+ class Inputs(BaseInputs):
11
+ input: Any
12
+
13
+
14
+ class AnyFinalOutputNode(FinalOutputNode[BaseState, Any]):
15
+ class Outputs(FinalOutputNode.Outputs):
16
+ value: Any = Inputs.input
17
+
18
+
19
+ class AnyFinalOutputNodeWorkflow(BaseWorkflow[Inputs, BaseState]):
20
+ graph = AnyFinalOutputNode
21
+
22
+ class Outputs(BaseWorkflow.Outputs):
23
+ value = AnyFinalOutputNode.Outputs.value
24
+
25
+
26
+ def test_serialize_workflow_with_any_output_type():
27
+ """
28
+ Tests that a terminal node with Any output type can be serialized correctly.
29
+ """
30
+ # GIVEN a Workflow that uses a Final Output Node with Any type
31
+ # WHEN we serialize it
32
+ workflow_display = get_workflow_display(workflow_class=AnyFinalOutputNodeWorkflow)
33
+ serialized_workflow: dict = workflow_display.serialize()
34
+
35
+ # THEN we should get a serialized representation of the Workflow
36
+ assert serialized_workflow.keys() == {
37
+ "workflow_raw_data",
38
+ "input_variables",
39
+ "state_variables",
40
+ "output_variables",
41
+ }
42
+
43
+ workflow_raw_data = serialized_workflow["workflow_raw_data"]
44
+ terminal_node = next(node for node in workflow_raw_data["nodes"] if node["type"] == "TERMINAL")
45
+ assert terminal_node["data"]["output_type"] == "JSON"
46
+
47
+ output_variables = serialized_workflow["output_variables"]
48
+ assert len(output_variables) == 1
49
+ assert output_variables[0]["type"] == "JSON"
@@ -1,6 +1,25 @@
1
+ import pytest
2
+ from pathlib import Path
3
+ import shutil
4
+ import sys
5
+ import tempfile
6
+
7
+ from vellum.workflows.exceptions import WorkflowInitializationException
1
8
  from vellum_ee.workflows.display.workflows.base_workflow_display import BaseWorkflowDisplay
2
9
 
3
10
 
11
+ @pytest.fixture
12
+ def temp_module_path():
13
+ """Fixture to manage sys.path for temporary modules."""
14
+ temp_dir = tempfile.mkdtemp()
15
+ sys.path.insert(0, temp_dir)
16
+ try:
17
+ yield temp_dir
18
+ finally:
19
+ sys.path.remove(temp_dir)
20
+ shutil.rmtree(temp_dir)
21
+
22
+
4
23
  def test_serialize_module_with_dataset():
5
24
  """Test that serialize_module correctly serializes dataset from sandbox modules."""
6
25
  module_path = "tests.workflows.basic_inputs_and_outputs"
@@ -75,3 +94,37 @@ def test_serialize_module_includes_additional_files():
75
94
  assert "def helper_function():" in additional_files["helper.py"]
76
95
  assert "sample data file" in additional_files["data.txt"]
77
96
  assert "CONSTANT_VALUE" in additional_files["utils/constants.py"]
97
+
98
+
99
+ def test_serialize_module__with_invalid_nested_set_graph(temp_module_path):
100
+ """
101
+ Tests that serialize_module raises a clear user-facing exception for workflows with nested sets in graph attribute.
102
+ """
103
+ module_dir = Path(temp_module_path) / "test_invalid_workflow"
104
+ module_dir.mkdir()
105
+
106
+ (module_dir / "__init__.py").write_text("")
107
+
108
+ workflow_content = """
109
+ from vellum.workflows import BaseWorkflow
110
+ from vellum.workflows.nodes import BaseNode
111
+
112
+ class TestNode(BaseNode):
113
+ class Outputs(BaseNode.Outputs):
114
+ value = "test"
115
+
116
+ class InvalidWorkflow(BaseWorkflow):
117
+ graph = {TestNode, {TestNode}}
118
+
119
+ class Outputs(BaseWorkflow.Outputs):
120
+ result = TestNode.Outputs.value
121
+ """
122
+ (module_dir / "workflow.py").write_text(workflow_content)
123
+
124
+ with pytest.raises(WorkflowInitializationException) as exc_info:
125
+ BaseWorkflowDisplay.serialize_module("test_invalid_workflow")
126
+
127
+ error_message = str(exc_info.value)
128
+ assert "Invalid graph structure detected" in error_message
129
+ assert "Nested sets or unsupported graph types are not allowed" in error_message
130
+ assert "contact Vellum support" in error_message