vellum-ai 1.6.1__py3-none-any.whl → 1.6.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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.1",
30
+ "User-Agent": "vellum-ai/1.6.3",
31
31
  "X-Fern-Language": "Python",
32
32
  "X-Fern-SDK-Name": "vellum-ai",
33
- "X-Fern-SDK-Version": "1.6.1",
33
+ "X-Fern-SDK-Version": "1.6.3",
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,11 +1,11 @@
1
- from typing import Any, Dict, Optional
1
+ from typing import Any, Dict
2
2
 
3
3
  from vellum.client.core.api_error import ApiError
4
4
  from vellum.workflows.constants import VellumIntegrationProviderType
5
5
  from vellum.workflows.errors.types import WorkflowErrorCode
6
6
  from vellum.workflows.exceptions import NodeException
7
7
  from vellum.workflows.types.definition import VellumIntegrationToolDetails
8
- from vellum.workflows.vellum_client import create_vellum_client
8
+ from vellum.workflows.vellum_client import Vellum
9
9
 
10
10
 
11
11
  class VellumIntegrationService:
@@ -16,9 +16,9 @@ class VellumIntegrationService:
16
16
  own integration infrastructure.
17
17
  """
18
18
 
19
- def __init__(self, client: Optional[Any] = None) -> None:
19
+ def __init__(self, client: Vellum) -> None:
20
20
  """Initialize the VellumIntegrationService with a Vellum client."""
21
- self._client = client or create_vellum_client()
21
+ self._client = client
22
22
 
23
23
  def get_tool_definition(
24
24
  self,
@@ -152,7 +152,9 @@ class BaseInlinePromptNode(BasePromptNode[StateType], Generic[StateType]):
152
152
  elif isinstance(function, ComposioToolDefinition):
153
153
  normalized_functions.append(compile_composio_tool_definition(function))
154
154
  elif isinstance(function, VellumIntegrationToolDefinition):
155
- normalized_functions.append(compile_vellum_integration_tool_definition(function))
155
+ normalized_functions.append(
156
+ compile_vellum_integration_tool_definition(function, self._context.vellum_client)
157
+ )
156
158
  elif isinstance(function, MCPServer):
157
159
  tool_definitions = compile_mcp_tool_definition(function)
158
160
  for tool_def in tool_definitions:
@@ -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
 
@@ -271,9 +271,10 @@ class VellumIntegrationNode(BaseNode[ToolCallingState], FunctionCallNodeMixin):
271
271
 
272
272
  def run(self) -> Iterator[BaseOutput]:
273
273
  arguments = self._extract_function_arguments()
274
+ vellum_client = self._context.vellum_client
274
275
 
275
276
  try:
276
- vellum_service = VellumIntegrationService()
277
+ vellum_service = VellumIntegrationService(vellum_client)
277
278
  result = vellum_service.execute_tool(
278
279
  integration=self.vellum_integration_tool.integration_name,
279
280
  provider=self.vellum_integration_tool.provider.value,
@@ -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()
@@ -323,17 +323,21 @@ def compile_composio_tool_definition(tool_def: ComposioToolDefinition) -> Functi
323
323
  )
324
324
 
325
325
 
326
- def compile_vellum_integration_tool_definition(tool_def: VellumIntegrationToolDefinition) -> FunctionDefinition:
326
+ def compile_vellum_integration_tool_definition(
327
+ tool_def: VellumIntegrationToolDefinition,
328
+ vellum_client: Vellum,
329
+ ) -> FunctionDefinition:
327
330
  """Compile a VellumIntegrationToolDefinition into a FunctionDefinition.
328
331
 
329
332
  Args:
330
333
  tool_def: The VellumIntegrationToolDefinition to compile
334
+ vellum_client: Vellum client instance
331
335
 
332
336
  Returns:
333
337
  FunctionDefinition with tool parameters and description
334
338
  """
335
339
  try:
336
- service = VellumIntegrationService()
340
+ service = VellumIntegrationService(vellum_client)
337
341
  tool_details = service.get_tool_definition(
338
342
  integration=tool_def.integration_name,
339
343
  provider=tool_def.provider.value,
@@ -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.1
3
+ Version: 1.6.3
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
@@ -46,7 +46,7 @@ vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=dtO9A-rjbDEJ
46
46
  vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=8tSb8qGVoRIELubu0qPeoDlt1LpiIqc6d9_30GWRd_k,2266
47
47
  vellum_ee/workflows/display/nodes/vellum/final_output_node.py,sha256=z4oeTgKnMGVaGig8XOZm5B_xYL4H7zweYlFweCbhnyA,3000
48
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=muyLv3KMIsUnnXhiPbPhw5B0TO1Z8LUwytpVQKlz4tM,11906
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
51
  vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=AW5JuHXtmtcJKeRiZKd7iB1re_D7G7jl_OlaZs8nUl0,4219
52
52
  vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=xMHaPfTSZWYprQenlHm2g47u0a5O9Me_dhAjfqo8nKQ,3116
@@ -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=LaakgOZiN1gNGntzdfYIrMKVQIUpVzSxGP8CafEgnlc,2840
163
+ vellum/client/core/client_wrapper.py,sha256=sFGuikZMgPFZOHfH3muMMTew0bqB89ar8-4OrulZHu0,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
@@ -1874,7 +1875,7 @@ vellum/workflows/integrations/mcp_service.py,sha256=9DYb8dg2_kgc1UOu830kxhaFlt9y
1874
1875
  vellum/workflows/integrations/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1875
1876
  vellum/workflows/integrations/tests/test_mcp_service.py,sha256=q_DYrDkIqI4sQBNgID4YdbM4e9tneLVWY8YmI4R26d8,8859
1876
1877
  vellum/workflows/integrations/tests/test_vellum_integration_service.py,sha256=n_n42FnwbR7YMd1sEVMphdOUWsWXCPw_caY6FnxNxAU,11322
1877
- vellum/workflows/integrations/vellum_integration_service.py,sha256=xgWOpjgVT7VZJStExw0_o3egimbCt8tQHBsEbc85DP8,5256
1878
+ vellum/workflows/integrations/vellum_integration_service.py,sha256=JFkrXBhkTEn_tXnRbjt289Z56_SLDSsuU7Msx2Nh9AM,5192
1878
1879
  vellum/workflows/logging.py,sha256=_a217XogktV4Ncz6xKFz7WfYmZAzkfVRVuC0rWob8ls,437
1879
1880
  vellum/workflows/nodes/__init__.py,sha256=zymtc3_iW2rFmMR-sayTLuN6ZsAw8VnJweWPsjQk2-Q,1197
1880
1881
  vellum/workflows/nodes/bases/__init__.py,sha256=cniHuz_RXdJ4TQgD8CBzoiKDiPxg62ErdVpCbWICX64,58
@@ -1918,7 +1919,7 @@ vellum/workflows/nodes/displayable/bases/api_node/tests/test_node.py,sha256=5C59
1918
1919
  vellum/workflows/nodes/displayable/bases/base_prompt_node/__init__.py,sha256=Org3xTvgp1pA0uUXFfnJr29D3HzCey2lEdYF4zbIUgo,70
1919
1920
  vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py,sha256=w02vgydiwNoKru324QLSkH3BiGUvHTgKbf05BEx945s,4657
1920
1921
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/__init__.py,sha256=Hl35IAoepRpE-j4cALaXVJIYTYOF3qszyVbxTj4kS1s,82
1921
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=0qI94axa6xYaOPw9fFHLMd0uIGTxON9NbfPsu390MEQ,18409
1922
+ vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=Medz-nM5dqaaNRSPAJ0LUfnv4o57RGBwKFoYNAmCf48,18484
1922
1923
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1923
1924
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/test_inline_prompt_node.py,sha256=xc53wGwVqxBnN7eoyWkJ-RJ-FeUpHKekkKjViASHAFg,27495
1924
1925
  vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py,sha256=H9mM75EQpP6PUvsXCTbwjw4CqMMLf36m1G2XqiPEvH4,12139
@@ -1928,12 +1929,12 @@ 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
1939
  vellum/workflows/nodes/displayable/conditional_node/node.py,sha256=71ZUNfTiD7t2Kai2ypw0tmv1lSf1brQaHAQD-SeUrGE,1101
1939
1940
  vellum/workflows/nodes/displayable/conftest.py,sha256=K2kLM2JGAfcrmmd92u8DXInUO5klFdggPWblg5RVcx4,5729
@@ -1978,7 +1979,7 @@ vellum/workflows/nodes/displayable/tool_calling_node/tests/__init__.py,sha256=47
1978
1979
  vellum/workflows/nodes/displayable/tool_calling_node/tests/test_composio_service.py,sha256=in1fbEz5x1tx3uKv9YXdvOncsHucNL8Ro6Go7lBuuOQ,8962
1979
1980
  vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py,sha256=GZoeybB9uM7ai8sBLAtUMHrMVgh-WrJDWrKZci6feDs,11892
1980
1981
  vellum/workflows/nodes/displayable/tool_calling_node/tests/test_utils.py,sha256=EmKFA-ELdTzlK0xMqWnuSZPoGNLYCwk6b0amTqirZo0,11305
1981
- vellum/workflows/nodes/displayable/tool_calling_node/utils.py,sha256=-6Qb8GuHlpYaOLlQcJSNYmRlZXEcnYzaCjIvH-scfMY,23657
1982
+ vellum/workflows/nodes/displayable/tool_calling_node/utils.py,sha256=7xNiNPD4vzd-4NuIaeCZhs0AshkuxlWVdx1NnbX-fdg,23722
1982
1983
  vellum/workflows/nodes/displayable/web_search_node/__init__.py,sha256=8FOnEP-n-U68cvxTlJW9wphIAGHq5aqjzLM-DoSSXnU,61
1983
1984
  vellum/workflows/nodes/displayable/web_search_node/node.py,sha256=NQYux2bOtuBF5E4tn-fXi5y3btURPRrNqMSM9MAZYI4,5091
1984
1985
  vellum/workflows/nodes/displayable/web_search_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
@@ -2043,7 +2044,7 @@ vellum/workflows/types/tests/test_definition.py,sha256=QUI9_Wsm2k-ZxQTXogsB0L4cs
2043
2044
  vellum/workflows/types/tests/test_utils.py,sha256=UnZog59tR577mVwqZRqqWn2fScoOU1H6up0EzS8zYhw,2536
2044
2045
  vellum/workflows/types/utils.py,sha256=mTctHITBybpt4855x32oCKALBEcMNLn-9cCmfEKgJHQ,6498
2045
2046
  vellum/workflows/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2046
- vellum/workflows/utils/functions.py,sha256=QbRoymgG_NgEvZcoyb_ofmP4o9fzuBlFRZ8s0Ivk11U,13062
2047
+ vellum/workflows/utils/functions.py,sha256=whqLOWWRDFVumOoP0IjUpw66y6H9M3iPZe1NSlvm8Gg,13155
2047
2048
  vellum/workflows/utils/hmac.py,sha256=JJCczc6pyV6DuE1Oa0QVfYPUN_of3zEYmGFib3OZnrE,1135
2048
2049
  vellum/workflows/utils/names.py,sha256=QtHquoaGqRseu5gg2OcVGI2d_CMcEOvjb9KspwH4C-A,552
2049
2050
  vellum/workflows/utils/pydantic_schema.py,sha256=eR_bBtY-T0pttJP-ARwagSdCOnwPUtiT3cegm2lzDTQ,1310
@@ -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.1.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
2066
- vellum_ai-1.6.1.dist-info/METADATA,sha256=54F9VuqNf1JOunvTHHiKdgMKdsWSQw-muy-G0MFV3HM,5547
2067
- vellum_ai-1.6.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
2068
- vellum_ai-1.6.1.dist-info/entry_points.txt,sha256=xVavzAKN4iF_NbmhWOlOkHluka0YLkbN_pFQ9pW3gLI,117
2069
- vellum_ai-1.6.1.dist-info/RECORD,,
2066
+ vellum_ai-1.6.3.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
2067
+ vellum_ai-1.6.3.dist-info/METADATA,sha256=4U7TNEZ9YCRzcEOlmFSgbJuzwscBVf182IlYSFW2kOc,5547
2068
+ vellum_ai-1.6.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
2069
+ vellum_ai-1.6.3.dist-info/entry_points.txt,sha256=xVavzAKN4iF_NbmhWOlOkHluka0YLkbN_pFQ9pW3gLI,117
2070
+ vellum_ai-1.6.3.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):
@@ -5,11 +5,12 @@ from vellum import FunctionDefinition, PromptBlock, RichTextChildBlock, VellumVa
5
5
  from vellum.workflows.descriptors.base import BaseDescriptor
6
6
  from vellum.workflows.nodes import InlinePromptNode
7
7
  from vellum.workflows.types.core import JsonObject
8
- from vellum.workflows.types.definition import DeploymentDefinition
8
+ from vellum.workflows.types.definition import DeploymentDefinition, VellumIntegrationToolDefinition
9
9
  from vellum.workflows.types.generics import is_workflow_class
10
10
  from vellum.workflows.utils.functions import (
11
11
  compile_function_definition,
12
12
  compile_inline_workflow_function_definition,
13
+ compile_vellum_integration_tool_definition,
13
14
  compile_workflow_deployment_function_definition,
14
15
  )
15
16
  from vellum.workflows.utils.uuids import uuid4_from_hash
@@ -164,6 +165,8 @@ class BaseInlinePromptNodeDisplay(BaseNodeDisplay[_InlinePromptNodeType], Generi
164
165
  normalized_functions = compile_function_definition(function)
165
166
  elif isinstance(function, DeploymentDefinition):
166
167
  normalized_functions = compile_workflow_deployment_function_definition(function, display_context.client)
168
+ elif isinstance(function, VellumIntegrationToolDefinition):
169
+ normalized_functions = compile_vellum_integration_tool_definition(function, display_context.client)
167
170
  else:
168
171
  raise ValueError(f"Unsupported function type: {type(function)}")
169
172
  return {
@@ -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