vellum-ai 1.7.5__py3-none-any.whl → 1.7.7__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 (29) hide show
  1. vellum/client/core/client_wrapper.py +2 -2
  2. vellum/workflows/nodes/bases/base.py +28 -9
  3. vellum/workflows/nodes/bases/base_adornment_node.py +53 -1
  4. vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py +3 -1
  5. vellum/workflows/nodes/displayable/search_node/node.py +2 -1
  6. vellum/workflows/nodes/displayable/search_node/tests/test_node.py +14 -0
  7. vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py +7 -1
  8. vellum/workflows/nodes/displayable/subworkflow_deployment_node/tests/test_node.py +1 -1
  9. vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py +54 -0
  10. vellum/workflows/nodes/displayable/tool_calling_node/utils.py +26 -24
  11. vellum/workflows/runner/runner.py +42 -52
  12. vellum/workflows/triggers/__init__.py +2 -1
  13. vellum/workflows/triggers/integration.py +62 -0
  14. vellum/workflows/triggers/tests/__init__.py +1 -0
  15. vellum/workflows/triggers/tests/test_integration.py +102 -0
  16. vellum/workflows/workflows/base.py +17 -3
  17. {vellum_ai-1.7.5.dist-info → vellum_ai-1.7.7.dist-info}/METADATA +1 -1
  18. {vellum_ai-1.7.5.dist-info → vellum_ai-1.7.7.dist-info}/RECORD +29 -26
  19. vellum_cli/push.py +1 -5
  20. vellum_cli/tests/test_push.py +86 -0
  21. vellum_ee/workflows/display/nodes/base_node_display.py +1 -1
  22. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +16 -0
  23. vellum_ee/workflows/display/tests/workflow_serialization/test_manual_trigger_serialization.py +16 -19
  24. vellum_ee/workflows/display/utils/expressions.py +11 -11
  25. vellum_ee/workflows/display/workflows/base_workflow_display.py +23 -14
  26. vellum_ee/workflows/tests/test_server.py +40 -1
  27. {vellum_ai-1.7.5.dist-info → vellum_ai-1.7.7.dist-info}/LICENSE +0 -0
  28. {vellum_ai-1.7.5.dist-info → vellum_ai-1.7.7.dist-info}/WHEEL +0 -0
  29. {vellum_ai-1.7.5.dist-info → vellum_ai-1.7.7.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,102 @@
1
+ """Tests for IntegrationTrigger base class."""
2
+
3
+ import pytest
4
+
5
+ from vellum.workflows.nodes.bases.base import BaseNode
6
+ from vellum.workflows.triggers.integration import IntegrationTrigger
7
+
8
+
9
+ def test_integration_trigger__is_abstract():
10
+ """IntegrationTrigger cannot be instantiated directly (ABC)."""
11
+ # WHEN we try to call process_event on IntegrationTrigger directly
12
+ # THEN it raises NotImplementedError
13
+ with pytest.raises(NotImplementedError, match="must implement process_event"):
14
+ IntegrationTrigger.process_event({})
15
+
16
+
17
+ def test_integration_trigger__outputs_class_exists():
18
+ """IntegrationTrigger has Outputs class."""
19
+ # GIVEN IntegrationTrigger
20
+ # THEN it has an Outputs class
21
+ assert hasattr(IntegrationTrigger, "Outputs")
22
+
23
+
24
+ def test_integration_trigger__can_be_subclassed():
25
+ """IntegrationTrigger can be subclassed to create concrete triggers."""
26
+
27
+ # GIVEN a concrete implementation of IntegrationTrigger
28
+ class TestTrigger(IntegrationTrigger):
29
+ class Outputs(IntegrationTrigger.Outputs):
30
+ data: str
31
+
32
+ @classmethod
33
+ def process_event(cls, event_data: dict):
34
+ return cls.Outputs(data=event_data.get("data", ""))
35
+
36
+ # WHEN we process an event
37
+ result = TestTrigger.process_event({"data": "test"})
38
+
39
+ # THEN it returns the expected outputs
40
+ assert result.data == "test"
41
+
42
+
43
+ def test_integration_trigger__graph_syntax():
44
+ """IntegrationTrigger can be used in graph syntax."""
45
+
46
+ # GIVEN a concrete trigger and a node
47
+ class TestTrigger(IntegrationTrigger):
48
+ class Outputs(IntegrationTrigger.Outputs):
49
+ value: str
50
+
51
+ @classmethod
52
+ def process_event(cls, event_data: dict):
53
+ return cls.Outputs(value=event_data.get("value", ""))
54
+
55
+ class TestNode(BaseNode):
56
+ pass
57
+
58
+ # WHEN we use trigger >> node syntax
59
+ graph = TestTrigger >> TestNode
60
+
61
+ # THEN a graph is created
62
+ assert graph is not None
63
+ assert len(list(graph.trigger_edges)) == 1
64
+ assert list(graph.trigger_edges)[0].trigger_class == TestTrigger
65
+ assert list(graph.trigger_edges)[0].to_node == TestNode
66
+
67
+
68
+ def test_integration_trigger__multiple_entrypoints():
69
+ """IntegrationTrigger works with multiple entry points."""
70
+
71
+ # GIVEN a trigger and multiple nodes
72
+ class TestTrigger(IntegrationTrigger):
73
+ class Outputs(IntegrationTrigger.Outputs):
74
+ msg: str
75
+
76
+ @classmethod
77
+ def process_event(cls, event_data: dict):
78
+ return cls.Outputs(msg=event_data.get("msg", ""))
79
+
80
+ class NodeA(BaseNode):
81
+ pass
82
+
83
+ class NodeB(BaseNode):
84
+ pass
85
+
86
+ # WHEN we use trigger >> {nodes} syntax
87
+ graph = TestTrigger >> {NodeA, NodeB}
88
+
89
+ # THEN both nodes are entrypoints
90
+ trigger_edges = list(graph.trigger_edges)
91
+ assert len(trigger_edges) == 2
92
+ target_nodes = {edge.to_node for edge in trigger_edges}
93
+ assert target_nodes == {NodeA, NodeB}
94
+
95
+
96
+ def test_integration_trigger__config_attribute():
97
+ """IntegrationTrigger has optional config attribute."""
98
+
99
+ # GIVEN IntegrationTrigger
100
+ # THEN it has a config class variable
101
+ assert hasattr(IntegrationTrigger, "config")
102
+ assert IntegrationTrigger.config is None
@@ -26,6 +26,8 @@ from typing import (
26
26
  overload,
27
27
  )
28
28
 
29
+ from pydantic import ValidationError
30
+
29
31
  from vellum.workflows.edges import Edge
30
32
  from vellum.workflows.emitters.base import BaseWorkflowEmitter
31
33
  from vellum.workflows.errors import WorkflowError, WorkflowErrorCode
@@ -684,6 +686,10 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
684
686
  workflow_path = f"{module_path}.workflow"
685
687
  try:
686
688
  module = importlib.import_module(workflow_path)
689
+ except ValidationError as e:
690
+ raise WorkflowInitializationException(
691
+ message=f"Pydantic Model Validation defined in Workflow Failed: {e}"
692
+ ) from e
687
693
  except TypeError as e:
688
694
  if "Unexpected graph type" in str(e) or "unhashable type: 'set'" in str(e):
689
695
  raise WorkflowInitializationException(
@@ -691,9 +697,17 @@ class BaseWorkflow(Generic[InputsType, StateType], BaseExecutable, metaclass=_Ba
691
697
  "Please contact Vellum support for assistance with Workflow configuration."
692
698
  ) from e
693
699
  else:
694
- raise
695
- except (SyntaxError, ImportError, ModuleNotFoundError) as e:
696
- raise WorkflowInitializationException(message=f"Failed to load workflow module: {e}") from e
700
+ raise WorkflowInitializationException(message=f"Type Error raised while loading Workflow: {e}") from e
701
+ except SyntaxError as e:
702
+ raise WorkflowInitializationException(message=f"Syntax Error raised while loading Workflow: {e}") from e
703
+ except ModuleNotFoundError as e:
704
+ raise WorkflowInitializationException(message=f"Workflow module not found: {e}") from e
705
+ except ImportError as e:
706
+ raise WorkflowInitializationException(message=f"Invalid import found while loading Workflow: {e}") from e
707
+ except NameError as e:
708
+ raise WorkflowInitializationException(message=f"Invalid variable reference: {e}") from e
709
+ except Exception as e:
710
+ raise WorkflowInitializationException(message=f"Unexpected failure while loading module: {e}") from e
697
711
  workflows: List[Type[BaseWorkflow]] = []
698
712
  for name in dir(module):
699
713
  if name.startswith("__"):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 1.7.5
3
+ Version: 1.7.7
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -9,7 +9,7 @@ vellum_cli/logger.py,sha256=dcM_OmgqXLo93vDYswO5ylyUQQcTfnA5GTd5tbIt3wM,1446
9
9
  vellum_cli/move.py,sha256=lCHQ-U4BspgS512GxFFvUrglitaHkWfuKn1Hpfcn7-Q,2053
10
10
  vellum_cli/ping.py,sha256=p_BCCRjgPhng6JktuECtkDQLbhopt6JpmrtGoLnLJT8,1161
11
11
  vellum_cli/pull.py,sha256=udYyPlJ6VKDdh78rApNJOZgxHl82fcV6iGnRPSdX1LY,14750
12
- vellum_cli/push.py,sha256=KpBGq7B-ffwa9QTHsTRSk73l-tfKc3gyiBSn9Pwlsak,11878
12
+ vellum_cli/push.py,sha256=epoQNdFdXUzLlfwE2ZTklfy3DW6bI4-Pgv1QRS8CZXk,11803
13
13
  vellum_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
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
@@ -20,7 +20,7 @@ vellum_cli/tests/test_main.py,sha256=qDZG-aQauPwBwM6A2DIu1494n47v3pL28XakTbLGZ-k
20
20
  vellum_cli/tests/test_move.py,sha256=FIrL1xlH5oFKGX2MugcTKL8JilpopmUC7hP5OaqF5zw,5213
21
21
  vellum_cli/tests/test_ping.py,sha256=b3aQLd-N59_8w2rRiWqwpB1rlHaKEYVbAj1Y3hi7A-g,2605
22
22
  vellum_cli/tests/test_pull.py,sha256=e2XHzcHIx9k-FyuNAl7wMSNsSSebPGyP6U05JGcddFs,49447
23
- vellum_cli/tests/test_push.py,sha256=2MjkNKr_9Guv5Exjsm3L1BeVXmPkKUcCSiKnp90HgW4,41996
23
+ vellum_cli/tests/test_push.py,sha256=oQ3x28G6IxplmMWCcPEYY46nOYAEPaihcMVsN4quQ5Q,45000
24
24
  vellum_ee/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  vellum_ee/assets/node-definitions.json,sha256=Mm3c1nfEa1QjWWzNvIJlhahDcY4SM3wQm8og_x3jyd8,30755
26
26
  vellum_ee/py.typed,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=C0Dxwss5yoxSLotTRUP2h7oxGjb0dDIdsc-83HVFCP8,19493
35
+ vellum_ee/workflows/display/nodes/base_node_display.py,sha256=ceKQdHZmDSOY5-M2DtJnuLbdkuFujCChFtRABDoV1jo,19497
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
@@ -80,7 +80,7 @@ vellum_ee/workflows/display/tests/workflow_serialization/__init__.py,sha256=47DE
80
80
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
81
81
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py,sha256=Y-ajeT65b5varmrZCw6L3hir4hJCFq-eO0jZfRcrs7g,1886
82
82
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py,sha256=wdVaFvxicj48Kj-6VUlu61KR0oSArLTjownRi2p0NWQ,14941
83
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py,sha256=bPTwkLpB7trFLpAaDvXMfMP0c9H1u_c1cdnj7K-gtnw,24962
83
+ vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py,sha256=rDh6PxhdoVDNuKdUrtZpoMtX8T92ZUMonkVxDjeEE4g,25507
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
@@ -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=MQiZZfMtCps-_Me-SqH3TnC7sh52ApcF0_6ctoYZ63g,3798
113
+ vellum_ee/workflows/display/tests/workflow_serialization/test_manual_trigger_serialization.py,sha256=sd0lbity6yVIJ3HbMZEcL1wJEYoihqVW2Bjx8YMmrAM,3657
114
114
  vellum_ee/workflows/display/tests/workflow_serialization/test_terminal_node_any_serialization.py,sha256=4WAmSEJZlDBLPhsD1f4GwY9ahB9F6qJKGnL6j7ZYlzQ,1740
115
115
  vellum_ee/workflows/display/tests/workflow_serialization/test_web_search_node_serialization.py,sha256=vbDFBrWUPeeW7cxjNA6SXrsHlYcbOAhlQ4C45Vdnr1c,3428
116
116
  vellum_ee/workflows/display/tests/workflow_serialization/test_workflow_input_parameterization_error.py,sha256=vAdmn3YTBDpo55znbydQxsgg9ASqHcvsUPwiBR_7wfo,1461
@@ -119,7 +119,7 @@ vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeR
119
119
  vellum_ee/workflows/display/utils/auto_layout.py,sha256=f4GiLn_LazweupfqTpubcdtdfE_vrOcmZudSsnYIY9E,3906
120
120
  vellum_ee/workflows/display/utils/events.py,sha256=DE33uoKW78BZtITJ6L22dMZN3KR1BuZBVC98C_gIyzU,1943
121
121
  vellum_ee/workflows/display/utils/exceptions.py,sha256=E8Lvo7LY1BoZ54M_NR_opDjJsAAiCUfow1HgoHcTHmg,989
122
- vellum_ee/workflows/display/utils/expressions.py,sha256=mg-AHKGvnRjB1y4nXFJp7uRuAkUGITnuZ6aRDnEzPXk,20512
122
+ vellum_ee/workflows/display/utils/expressions.py,sha256=IIieWILv2zGLYTRgeDwgjhtga1ttV-ss2remJ1HBVXA,20688
123
123
  vellum_ee/workflows/display/utils/registry.py,sha256=1qXiBTdsnro6FeCX0FGBEK7CIf6wa--Jt50iZ_nEp_M,3460
124
124
  vellum_ee/workflows/display/utils/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
125
125
  vellum_ee/workflows/display/utils/tests/test_auto_layout.py,sha256=vfXI769418s9vda5Gb5NFBH747WMOwSgHRXeLCTLVm8,2356
@@ -127,7 +127,7 @@ vellum_ee/workflows/display/utils/tests/test_events.py,sha256=42IEBnMbaQrH8gigw5
127
127
  vellum_ee/workflows/display/utils/vellum.py,sha256=Bt7kdLdXoBsHn5dVEY2uKcF542VL09jwu8J_30rl2vk,6413
128
128
  vellum_ee/workflows/display/vellum.py,sha256=J2mdJZ1sdLW535DDUkq_Vm8Z572vhuxHxVZF9deKSdk,391
129
129
  vellum_ee/workflows/display/workflows/__init__.py,sha256=JTB9ObEV3l4gGGdtfBHwVJtTTKC22uj-a-XjTVwXCyA,148
130
- vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=qek7zT4V6XoqmU3seVZwrNzGNVb0CKU4GmEK8VLrWbc,46651
130
+ vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=DAGqGCg2WTBTNeO1akDpeCAJ5sAtUu65dnXsagnO6Jk,46937
131
131
  vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=gxz76AeCqgAZ9D2lZeTiZzxY9eMgn3qOSfVgiqYcOh8,2028
132
132
  vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=lg-c_3P3ldtqWq2VFsk_2Mkn3pVdXWuT59QpH7QwXVs,39764
133
133
  vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -154,14 +154,14 @@ vellum_ee/workflows/tests/local_workflow/workflow.py,sha256=A4qOzOPNwePYxWbcAgIP
154
154
  vellum_ee/workflows/tests/test_display_meta.py,sha256=PkXJVnMZs9GNooDkd59n4YTBAX3XGPQWeSSVbhehVFM,5112
155
155
  vellum_ee/workflows/tests/test_registry.py,sha256=B8xRIuEyLWfSqrYoPldNQXhKPfe50PllvtAZoI8-uPs,6066
156
156
  vellum_ee/workflows/tests/test_serialize_module.py,sha256=zleQTcGZa5_nzwu4zpFoqEHhk7pb64hGrhObR4anhPQ,4471
157
- vellum_ee/workflows/tests/test_server.py,sha256=DtQdVlRlfIvq0L9mSs0SncI0jHgDAq05HQCLj29aiZo,24728
157
+ vellum_ee/workflows/tests/test_server.py,sha256=RmLE2s9Cs7NejvWf7aPRNuAeEER-hGk-9Q9fYrn5wps,26013
158
158
  vellum_ee/workflows/tests/test_virtual_files.py,sha256=TJEcMR0v2S8CkloXNmCHA0QW0K6pYNGaIjraJz7sFvY,2762
159
159
  vellum/__init__.py,sha256=6dkyRHmIKuQPzL_z3QLVUrbkAF-HJKDhDSMCnf4ZsKw,49502
160
160
  vellum/client/README.md,sha256=flqu57ubZNTfpq60CdLtJC9gp4WEkyjb_n_eZ4OYf9w,6497
161
161
  vellum/client/__init__.py,sha256=rMnKRqL5-356SBc-rfm56MkO87PuAi2mtcfBszcJU1M,74316
162
162
  vellum/client/core/__init__.py,sha256=lTcqUPXcx4112yLDd70RAPeqq6tu3eFMe1pKOqkW9JQ,1562
163
163
  vellum/client/core/api_error.py,sha256=44vPoTyWN59gonCIZMdzw7M1uspygiLnr3GNFOoVL2Q,614
164
- vellum/client/core/client_wrapper.py,sha256=1YhfnMUVmjKkdmg9jR46ZTIffeCPF5RezCBeInW45hc,2840
164
+ vellum/client/core/client_wrapper.py,sha256=A3sL2WdcS11xGFV5Yq5h9yzf-96DpP7AL8dayvAKlUk,2840
165
165
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
166
166
  vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
167
167
  vellum/client/core/force_multipart.py,sha256=awxh5MtcRYe74ehY8U76jzv6fYM_w_D3Rur7KQQzSDk,429
@@ -1885,8 +1885,8 @@ vellum/workflows/integrations/vellum_integration_service.py,sha256=qhFoLzHlMli1P
1885
1885
  vellum/workflows/logging.py,sha256=_a217XogktV4Ncz6xKFz7WfYmZAzkfVRVuC0rWob8ls,437
1886
1886
  vellum/workflows/nodes/__init__.py,sha256=zymtc3_iW2rFmMR-sayTLuN6ZsAw8VnJweWPsjQk2-Q,1197
1887
1887
  vellum/workflows/nodes/bases/__init__.py,sha256=cniHuz_RXdJ4TQgD8CBzoiKDiPxg62ErdVpCbWICX64,58
1888
- vellum/workflows/nodes/bases/base.py,sha256=jitrO95AvJF2iCrqDgbNXZVLCx4LuApFk9Xx24_h7Bk,21653
1889
- vellum/workflows/nodes/bases/base_adornment_node.py,sha256=hrgzuTetM4ynPd9YGHoK8Vwwn4XITi3aZZ_OCnQrq4Y,3433
1888
+ vellum/workflows/nodes/bases/base.py,sha256=u7NmYWhQZm91KZV53PGD-gpZvcWAItOme95TjZkHwDI,22094
1889
+ vellum/workflows/nodes/bases/base_adornment_node.py,sha256=TgL-tfX84GyTb7ONQ2z7OJDkx57kW3qbQNDuiw-Zrqo,5814
1890
1890
  vellum/workflows/nodes/bases/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1891
1891
  vellum/workflows/nodes/bases/tests/test_base_adornment_node.py,sha256=fXZI9KqpS4XMBrBnIEkK3foHaBVvyHwYcQWWDKay7ic,1148
1892
1892
  vellum/workflows/nodes/bases/tests/test_base_node.py,sha256=igJrcSxSZADk5tLpssAt0uAXe9oJoLbilzueJJvA5p4,10942
@@ -1925,7 +1925,7 @@ vellum/workflows/nodes/displayable/bases/api_node/tests/test_node.py,sha256=5C59
1925
1925
  vellum/workflows/nodes/displayable/bases/base_prompt_node/__init__.py,sha256=Org3xTvgp1pA0uUXFfnJr29D3HzCey2lEdYF4zbIUgo,70
1926
1926
  vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py,sha256=w02vgydiwNoKru324QLSkH3BiGUvHTgKbf05BEx945s,4657
1927
1927
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/__init__.py,sha256=Hl35IAoepRpE-j4cALaXVJIYTYOF3qszyVbxTj4kS1s,82
1928
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=Medz-nM5dqaaNRSPAJ0LUfnv4o57RGBwKFoYNAmCf48,18484
1928
+ vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=ITxAa1HWF6OPdcKg0DdTK7YP94ezzzWVyZzCRMiybIg,18638
1929
1929
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1930
1930
  vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/test_inline_prompt_node.py,sha256=xc53wGwVqxBnN7eoyWkJ-RJ-FeUpHKekkKjViASHAFg,27495
1931
1931
  vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py,sha256=H9mM75EQpP6PUvsXCTbwjw4CqMMLf36m1G2XqiPEvH4,12139
@@ -1966,13 +1966,13 @@ vellum/workflows/nodes/displayable/prompt_deployment_node/node.py,sha256=XeBn6d3
1966
1966
  vellum/workflows/nodes/displayable/prompt_deployment_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1967
1967
  vellum/workflows/nodes/displayable/prompt_deployment_node/tests/test_node.py,sha256=c_nuuqrwiIjgj4qIbVypfDuOc-3TlgO6CbXFqQl2Nqw,19725
1968
1968
  vellum/workflows/nodes/displayable/search_node/__init__.py,sha256=hpBpvbrDYf43DElRZFLzieSn8weXiwNiiNOJurERQbs,62
1969
- vellum/workflows/nodes/displayable/search_node/node.py,sha256=1dGCB1kb7MvX3fUJ5zP__Bh02mdPdRRsx_vwGyQFBVc,1981
1969
+ vellum/workflows/nodes/displayable/search_node/node.py,sha256=hbPsZhyXfq9dx0mfBKOyivluDmV2iXmDTT7loEyu9nI,2057
1970
1970
  vellum/workflows/nodes/displayable/search_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1971
- vellum/workflows/nodes/displayable/search_node/tests/test_node.py,sha256=WVZR3BI_CvxBG9hulv0-tcAc_gW5ozs0nH4uVNRJa2U,8863
1971
+ vellum/workflows/nodes/displayable/search_node/tests/test_node.py,sha256=YXgIIAJHVQxrfyJ0gxeJC0fAJaic10_zbqvsS8hyZSc,9368
1972
1972
  vellum/workflows/nodes/displayable/subworkflow_deployment_node/__init__.py,sha256=9yYM6001YZeqI1VOk1QuEM_yrffk_EdsO7qaPzINKds,92
1973
- vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py,sha256=SZIdk7aBmKKFgPYsxZaHIjT6p5W9HBYT1lj5yE4oax8,14169
1973
+ vellum/workflows/nodes/displayable/subworkflow_deployment_node/node.py,sha256=AHlW7aPDza1FuSR_9xONOe3tzDFDg8EIJtO1NJ7ILhk,14445
1974
1974
  vellum/workflows/nodes/displayable/subworkflow_deployment_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1975
- vellum/workflows/nodes/displayable/subworkflow_deployment_node/tests/test_node.py,sha256=c98nMPogZ6iN_pTvVUMTB3J72Hj--H-XVgvvRXhdSQE,19085
1975
+ vellum/workflows/nodes/displayable/subworkflow_deployment_node/tests/test_node.py,sha256=PII44speqT4fJvj60y_3KDAnH1L6Ivtq9R4BykY-X_A,19092
1976
1976
  vellum/workflows/nodes/displayable/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1977
1977
  vellum/workflows/nodes/displayable/tests/test_inline_text_prompt_node.py,sha256=E6PQ9OKcDlsJbWoxVKWgiAPgz9p59g1ONMgNggfOeiI,4868
1978
1978
  vellum/workflows/nodes/displayable/tests/test_search_node_error_handling.py,sha256=8aw8hDFL0ZXThvAa7yxrJN026EYGD4-Q1si3Phu9-_0,6307
@@ -1983,9 +1983,9 @@ vellum/workflows/nodes/displayable/tool_calling_node/node.py,sha256=rvCGtnCM1bFk
1983
1983
  vellum/workflows/nodes/displayable/tool_calling_node/state.py,sha256=CcBVb_YtwfSSka4ze678k6-qwmzMSfjfVP8_Y95feSo,302
1984
1984
  vellum/workflows/nodes/displayable/tool_calling_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1985
1985
  vellum/workflows/nodes/displayable/tool_calling_node/tests/test_composio_service.py,sha256=in1fbEz5x1tx3uKv9YXdvOncsHucNL8Ro6Go7lBuuOQ,8962
1986
- vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py,sha256=Idjtlly6GTotNa4isXJ23RxKzQA2oE10MOm793aipLA,13892
1986
+ vellum/workflows/nodes/displayable/tool_calling_node/tests/test_node.py,sha256=uXz9tbPK6ikGlF8Tfx-qiPeF-8qq_E5ZbmCmOMWkz24,15506
1987
1987
  vellum/workflows/nodes/displayable/tool_calling_node/tests/test_utils.py,sha256=EmKFA-ELdTzlK0xMqWnuSZPoGNLYCwk6b0amTqirZo0,11305
1988
- vellum/workflows/nodes/displayable/tool_calling_node/utils.py,sha256=VfrW8OZfw6NNxY9xP_jhST8FaRX2xpl-eGNVR3YGClI,24519
1988
+ vellum/workflows/nodes/displayable/tool_calling_node/utils.py,sha256=sReCkqd2pwUQ6pX9xZ_zQh-Wza2_2eV7HYRAHYaAaHo,24580
1989
1989
  vellum/workflows/nodes/displayable/web_search_node/__init__.py,sha256=8FOnEP-n-U68cvxTlJW9wphIAGHq5aqjzLM-DoSSXnU,61
1990
1990
  vellum/workflows/nodes/displayable/web_search_node/node.py,sha256=NQYux2bOtuBF5E4tn-fXi5y3btURPRrNqMSM9MAZYI4,5091
1991
1991
  vellum/workflows/nodes/displayable/web_search_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -2025,7 +2025,7 @@ vellum/workflows/resolvers/resolver.py,sha256=3uEYscB_2PHTazc0Y9SzOe_yiQZhVLfey1
2025
2025
  vellum/workflows/resolvers/tests/test_resolver.py,sha256=PnUGzsulo1It_LjjhHsRNiILvvl5G_IaK8ZX56zKC28,6204
2026
2026
  vellum/workflows/resolvers/types.py,sha256=Hndhlk69g6EKLh_LYg5ILepW5U_h_BYNllfzhS9k8p4,237
2027
2027
  vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8vyT-bqM,72
2028
- vellum/workflows/runner/runner.py,sha256=nq-DsIZWLSofsMxZnDZZgV_MUod5Baz6mrEs7cgYPQY,45795
2028
+ vellum/workflows/runner/runner.py,sha256=IYjeFx6_jescg8tEW-et5k96X9EsB6PDw4Kw4Qvy-Xo,45599
2029
2029
  vellum/workflows/sandbox.py,sha256=mezSZmilR_fwR8164n8CEfzlMeQ55IqfapHp4ftImvQ,3212
2030
2030
  vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
2031
2031
  vellum/workflows/state/base.py,sha256=A8s0PC8UvFjPpkHDY6u-yIeb2KHjoAmu-GW-GYrDl0E,24654
@@ -2039,9 +2039,12 @@ vellum/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
2039
2039
  vellum/workflows/tests/test_dataset_row.py,sha256=S8aIiYU9TRzJ8GTl5qCjnJ-fuHdxatHJFGLlKTVHPr4,4174
2040
2040
  vellum/workflows/tests/test_sandbox.py,sha256=JKwaluI-lODQo7Ek9sjDstjL_WTdSqUlVik6ZVTfVOA,1826
2041
2041
  vellum/workflows/tests/test_undefined.py,sha256=zMCVliCXVNLrlC6hEGyOWDnQADJ2g83yc5FIM33zuo8,353
2042
- vellum/workflows/triggers/__init__.py,sha256=yMSJHI_UbD_CnHJn2VHiX6qcdkyZ0BU8BS6fFQBbgZs,158
2042
+ vellum/workflows/triggers/__init__.py,sha256=kistj6wgRgYe3fO82_4giZ_xFkElfnsDGRKAbzxrGnc,249
2043
2043
  vellum/workflows/triggers/base.py,sha256=HIZkKPdb9GOYjpGzk2DoSqkxsUyjBnzmsLyhVTWm1iE,4857
2044
+ vellum/workflows/triggers/integration.py,sha256=_RyNrNnIBIa08FMHmLh2QEZPb-u5j75Bye_FbR82nkg,2043
2044
2045
  vellum/workflows/triggers/manual.py,sha256=PgbZ92gcK25yz6REXm98zWic1QBfhxLKfGCeHpZEUx4,1266
2046
+ vellum/workflows/triggers/tests/__init__.py,sha256=R8lag_iCRyulijHMK4e3Gf6YVB5NplfvwZeTkaRj8gQ,30
2047
+ vellum/workflows/triggers/tests/test_integration.py,sha256=5N7ejz4AFmgQAZlHGV466rzWyb79iLeXU-pAfx9o4TA,3213
2045
2048
  vellum/workflows/types/__init__.py,sha256=fZ3Xxly7YSsu4kCIYD5aYpYucNM97zTyInb9CA24mf0,102
2046
2049
  vellum/workflows/types/code_execution_node_wrappers.py,sha256=fewX9bqF_4TZuK-gZYIn12s31-k03vHMGRpvFAPm11Y,3206
2047
2050
  vellum/workflows/types/core.py,sha256=R7snCd7ci4tiRuHi5ALGh_5DIIF0T9eze3sf6EnJN-c,1126
@@ -2067,13 +2070,13 @@ vellum/workflows/utils/vellum_variables.py,sha256=X3lZn-EoWengRWBWRhTNW7hqbj7LkV
2067
2070
  vellum/workflows/utils/zip.py,sha256=HVg_YZLmBOTXKaDV3Xhaf3V6sYnfqqZXQ8CpuafkbPY,1181
2068
2071
  vellum/workflows/vellum_client.py,sha256=3iDR7VV_NgLSm1iZQCKDvrmfEaX1bOJiU15QrxyHpv0,1237
2069
2072
  vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
2070
- vellum/workflows/workflows/base.py,sha256=Fb5Bw1ZdysIoex-P7gOqRKqHrpYgj3qYIdeFqv6_y1k,30061
2073
+ vellum/workflows/workflows/base.py,sha256=2XQMntFPXUO3p1js14TEKun4oNWRmZOzga-pUiv5vVk,30935
2071
2074
  vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
2072
2075
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2073
2076
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=Boa-_m9ii2Qsa1RvVM-VYniF7zCpzGgEGy-OnPZkrHg,23941
2074
2077
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
2075
- vellum_ai-1.7.5.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
2076
- vellum_ai-1.7.5.dist-info/METADATA,sha256=ShJgnNyDKIuEsE07Cut9gBsA8vALEZ4XkdDUeuOn7Ms,5547
2077
- vellum_ai-1.7.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
2078
- vellum_ai-1.7.5.dist-info/entry_points.txt,sha256=xVavzAKN4iF_NbmhWOlOkHluka0YLkbN_pFQ9pW3gLI,117
2079
- vellum_ai-1.7.5.dist-info/RECORD,,
2078
+ vellum_ai-1.7.7.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
2079
+ vellum_ai-1.7.7.dist-info/METADATA,sha256=AUowwbBfQrczLwFHQeRVeZoiVw6lIuKUlK52uh9Qb6o,5547
2080
+ vellum_ai-1.7.7.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
2081
+ vellum_ai-1.7.7.dist-info/entry_points.txt,sha256=xVavzAKN4iF_NbmhWOlOkHluka0YLkbN_pFQ9pW3gLI,117
2082
+ vellum_ai-1.7.7.dist-info/RECORD,,
vellum_cli/push.py CHANGED
@@ -77,11 +77,7 @@ def push_command(
77
77
 
78
78
  logger.info(f"Loading workflow from {workflow_config.module}")
79
79
  resolved_workspace = workspace or workflow_config.workspace or DEFAULT_WORKSPACE_CONFIG.name
80
- workspace_config = (
81
- next((w for w in config.workspaces if w.name == resolved_workspace), DEFAULT_WORKSPACE_CONFIG)
82
- if workspace
83
- else DEFAULT_WORKSPACE_CONFIG
84
- )
80
+ workspace_config = next((w for w in config.workspaces if w.name == resolved_workspace), DEFAULT_WORKSPACE_CONFIG)
85
81
  api_key = os.getenv(workspace_config.api_key)
86
82
  if not api_key:
87
83
  raise ValueError(f"No API key value found in environment for workspace '{workspace_config.name}'.")
@@ -1134,3 +1134,89 @@ def test_push__deploy_stores_deployment_config_in_lock_file(mock_module, vellum_
1134
1134
  deployment_config_str = call_args["deployment_config"]
1135
1135
  deployment_config = json.loads(deployment_config_str)
1136
1136
  assert deployment_config["name"] == "test-push-deploy-stores-deployment-config-in-lock-file"
1137
+
1138
+
1139
+ def test_push__custom_workspace_in_lockfile__uses_custom_workspace_without_flag(mock_module, vellum_client_class):
1140
+ """
1141
+ Tests that push respects the custom workspace from the lockfile when no --workspace flag is provided.
1142
+ """
1143
+
1144
+ # GIVEN a workflow already configured in the lockfile with a custom workspace
1145
+ temp_dir = mock_module.temp_dir
1146
+ module = mock_module.module
1147
+ workflow_sandbox_id = mock_module.workflow_sandbox_id
1148
+ set_pyproject_toml = mock_module.set_pyproject_toml
1149
+
1150
+ # AND the lockfile has the workflow with a custom workspace
1151
+ with open(os.path.join(temp_dir, "vellum.lock.json"), "w") as f:
1152
+ json.dump(
1153
+ {
1154
+ "version": "1.0",
1155
+ "workflows": [
1156
+ {
1157
+ "module": module,
1158
+ "workflow_sandbox_id": workflow_sandbox_id,
1159
+ "workspace": "my_custom_workspace",
1160
+ "container_image_name": None,
1161
+ "container_image_tag": None,
1162
+ "deployments": [],
1163
+ "ignore": None,
1164
+ "target_directory": None,
1165
+ }
1166
+ ],
1167
+ "workspaces": [],
1168
+ },
1169
+ f,
1170
+ indent=2,
1171
+ )
1172
+
1173
+ # AND the custom workspace is defined in pyproject.toml
1174
+ set_pyproject_toml(
1175
+ {
1176
+ "workflows": [],
1177
+ "workspaces": [
1178
+ {
1179
+ "name": "my_custom_workspace",
1180
+ "api_key": "MY_CUSTOM_VELLUM_API_KEY",
1181
+ }
1182
+ ],
1183
+ }
1184
+ )
1185
+
1186
+ # AND the .env file has the custom api key
1187
+ with open(os.path.join(temp_dir, ".env"), "w") as f:
1188
+ f.write(
1189
+ """\
1190
+ VELLUM_API_KEY=abcdef123456
1191
+ MY_CUSTOM_VELLUM_API_KEY=custom-key-xyz
1192
+ """
1193
+ )
1194
+
1195
+ # AND a workflow exists in the module
1196
+ _ensure_workflow_py(temp_dir, module)
1197
+
1198
+ # AND the push API returns successfully
1199
+ vellum_client_class.return_value.workflows.push.return_value = WorkflowPushResponse(
1200
+ workflow_sandbox_id=workflow_sandbox_id,
1201
+ )
1202
+ vellum_client_class.return_value._client_wrapper._environment.default = "https://api.vellum.ai/v1"
1203
+
1204
+ # WHEN calling `vellum push` WITHOUT the --workspace flag
1205
+ runner = CliRunner()
1206
+ result = runner.invoke(cli_main, ["push", module])
1207
+
1208
+ # THEN it should succeed
1209
+ assert result.exit_code == 0, result.output
1210
+
1211
+ # AND the custom workspace API key should have been used
1212
+ vellum_client_class.assert_called_once_with(
1213
+ api_key="custom-key-xyz",
1214
+ environment=mock.ANY,
1215
+ api_version=None,
1216
+ )
1217
+
1218
+ with open(os.path.join(temp_dir, "vellum.lock.json")) as f:
1219
+ lock_file_content = json.load(f)
1220
+ assert len(lock_file_content["workflows"]) == 1
1221
+ assert lock_file_content["workflows"][0]["workspace"] == "my_custom_workspace"
1222
+ assert lock_file_content["workflows"][0]["workflow_sandbox_id"] == workflow_sandbox_id
@@ -188,7 +188,7 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
188
188
  type = primitive_type_to_vellum_variable_type(output)
189
189
  value = (
190
190
  serialize_value(node_id, display_context, output.instance)
191
- if output.instance is not None and output.instance != undefined
191
+ if output.instance is not None and output.instance is not undefined
192
192
  else None
193
193
  )
194
194
 
@@ -688,3 +688,19 @@ def test_serialize_node__comment_expanded_preserved_when_explicitly_set(serializ
688
688
  assert "comment" in display_data
689
689
  assert display_data["comment"]["value"] == "This is a test comment."
690
690
  assert display_data["comment"]["expanded"] is False
691
+
692
+
693
+ def test_serialize_node__attribute_with_type_annotation_no_default(serialize_node):
694
+ """
695
+ Tests that attributes with type annotations but no default values serialize as None.
696
+ """
697
+
698
+ # GIVEN a node with typed attributes but no default values
699
+ class NodeWithTypedAttributesNoDefault(BaseNode):
700
+ attr: str
701
+
702
+ # WHEN the node is serialized
703
+ serialized_node = serialize_node(NodeWithTypedAttributesNoDefault)
704
+
705
+ # THEN the attribute should serialize as None
706
+ assert serialized_node["attributes"][0]["value"] is None
@@ -3,8 +3,6 @@
3
3
  import pytest
4
4
  from typing import cast
5
5
 
6
- from deepdiff import DeepDiff
7
-
8
6
  from vellum.workflows import BaseWorkflow
9
7
  from vellum.workflows.inputs.base import BaseInputs
10
8
  from vellum.workflows.nodes.bases.base import BaseNode
@@ -42,27 +40,24 @@ def serialize(workflow_class) -> JsonObject:
42
40
 
43
41
 
44
42
  def test_manual_trigger_serialization():
45
- """Workflow with ManualTrigger serializes with trigger field."""
43
+ """Workflow with ManualTrigger serializes with triggers field."""
46
44
  result = serialize(create_workflow(ManualTrigger))
47
- workflow_raw_data = cast(JsonObject, result["workflow_raw_data"])
48
- trigger = cast(JsonObject, workflow_raw_data["trigger"])
45
+ triggers = cast(JsonArray, result["triggers"])
46
+
47
+ assert len(triggers) == 1
48
+ trigger = cast(JsonObject, triggers[0])
49
49
 
50
50
  assert trigger["type"] == "MANUAL"
51
- assert not DeepDiff(
52
- {
53
- "type": "MANUAL",
54
- "definition": {"name": "ManualTrigger", "module": ["vellum", "workflows", "triggers", "manual"]},
55
- },
56
- trigger,
57
- ignore_order=True,
58
- )
51
+ assert "id" in trigger
52
+ assert "attributes" in trigger
53
+ assert trigger["attributes"] == []
54
+ assert "definition" not in trigger
59
55
 
60
56
 
61
57
  def test_no_trigger_serialization():
62
- """Workflow without trigger has no trigger field."""
58
+ """Workflow without trigger has no triggers field."""
63
59
  result = serialize(create_workflow())
64
- workflow_raw_data = cast(JsonObject, result["workflow_raw_data"])
65
- assert "trigger" not in workflow_raw_data
60
+ assert "triggers" not in result
66
61
 
67
62
 
68
63
  def test_manual_trigger_multiple_entrypoints():
@@ -84,10 +79,12 @@ def test_manual_trigger_multiple_entrypoints():
84
79
  output_b = NodeB.Outputs.output
85
80
 
86
81
  result = serialize(MultiWorkflow)
82
+ triggers = cast(JsonArray, result["triggers"])
87
83
  workflow_data = cast(JsonObject, result["workflow_raw_data"])
88
- trigger = cast(JsonObject, workflow_data["trigger"])
89
84
  nodes = cast(JsonArray, workflow_data["nodes"])
90
85
 
86
+ assert len(triggers) == 1
87
+ trigger = cast(JsonObject, triggers[0])
91
88
  assert trigger["type"] == "MANUAL"
92
89
  assert len([n for n in nodes if cast(JsonObject, n)["type"] == "GENERIC"]) >= 2
93
90
 
@@ -98,8 +95,8 @@ def test_serialized_workflow_structure():
98
95
  workflow_raw_data = cast(JsonObject, result["workflow_raw_data"])
99
96
  definition = cast(JsonObject, workflow_raw_data["definition"])
100
97
 
101
- assert result.keys() == {"workflow_raw_data", "input_variables", "state_variables", "output_variables"}
102
- assert workflow_raw_data.keys() == {"nodes", "edges", "display_data", "definition", "output_values", "trigger"}
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"}
103
100
  assert definition["name"] == "TestWorkflow"
104
101
 
105
102
 
@@ -3,7 +3,7 @@ import inspect
3
3
  from io import StringIO
4
4
  import sys
5
5
  from uuid import UUID
6
- from typing import TYPE_CHECKING, Any, Dict, List, cast
6
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, cast
7
7
 
8
8
  from pydantic import BaseModel
9
9
 
@@ -248,13 +248,11 @@ def serialize_key(key: Any) -> str:
248
248
  return str(key)
249
249
 
250
250
 
251
- # Sentinel value to indicate a value should be omitted from serialization
252
- _UNDEFINED_SENTINEL: JsonObject = {"__undefined__": True}
253
-
254
-
255
- def serialize_value(executable_id: UUID, display_context: "WorkflowDisplayContext", value: Any) -> JsonObject:
251
+ def serialize_value(executable_id: UUID, display_context: "WorkflowDisplayContext", value: Any) -> Optional[JsonObject]:
256
252
  """
257
- Serialize a value to a JSON object.
253
+ Serialize a value to a JSON object. Returns `None` if the value resolves to `undefined`.
254
+ This is safe because all valid values are a JSON object, including the `None` constant:
255
+ > `{"type": "CONSTANT_VALUE", "value": {"type": "JSON", "value": None}}`
258
256
 
259
257
  Args:
260
258
  executable_id: node id or workflow id
@@ -265,7 +263,7 @@ def serialize_value(executable_id: UUID, display_context: "WorkflowDisplayContex
265
263
  serialized value
266
264
  """
267
265
  if value is undefined:
268
- return _UNDEFINED_SENTINEL
266
+ return None
269
267
 
270
268
  if isinstance(value, ConstantValueReference):
271
269
  return serialize_value(executable_id, display_context, value._value)
@@ -353,7 +351,7 @@ def serialize_value(executable_id: UUID, display_context: "WorkflowDisplayContex
353
351
  serialized_items = []
354
352
  for item in value:
355
353
  serialized_item = serialize_value(executable_id, display_context, item)
356
- if serialized_item != _UNDEFINED_SENTINEL:
354
+ if serialized_item is not None:
357
355
  serialized_items.append(serialized_item)
358
356
 
359
357
  if all(isinstance(item, dict) and item["type"] == "CONSTANT_VALUE" for item in serialized_items):
@@ -389,7 +387,7 @@ def serialize_value(executable_id: UUID, display_context: "WorkflowDisplayContex
389
387
  serialized_entries: List[Dict[str, Any]] = []
390
388
  for key, val in value.items():
391
389
  serialized_val = serialize_value(executable_id, display_context, val)
392
- if serialized_val != _UNDEFINED_SENTINEL:
390
+ if serialized_val is not None:
393
391
  serialized_entries.append(
394
392
  {
395
393
  "id": str(uuid4_from_hash(f"{executable_id}|{key}")),
@@ -455,7 +453,9 @@ def serialize_value(executable_id: UUID, display_context: "WorkflowDisplayContex
455
453
  if inputs:
456
454
  serialized_inputs = {}
457
455
  for param_name, input_ref in inputs.items():
458
- serialized_inputs[param_name] = serialize_value(executable_id, display_context, input_ref)
456
+ serialized_input = serialize_value(executable_id, display_context, input_ref)
457
+ if serialized_input is not None:
458
+ serialized_inputs[param_name] = serialized_input
459
459
 
460
460
  model_data = function_definition.model_dump()
461
461
  model_data["inputs"] = serialized_inputs