vellum-ai 1.1.4__py3-none-any.whl → 1.1.5__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.1.4",
30
+ "User-Agent": "vellum-ai/1.1.5",
31
31
  "X-Fern-Language": "Python",
32
32
  "X-Fern-SDK-Name": "vellum-ai",
33
- "X-Fern-SDK-Version": "1.1.4",
33
+ "X-Fern-SDK-Version": "1.1.5",
34
34
  **(self.get_custom_headers() or {}),
35
35
  }
36
36
  if self._api_version is not None:
@@ -11,6 +11,8 @@ from vellum.workflows.state.base import BaseState
11
11
 
12
12
  logger = logging.getLogger(__name__)
13
13
 
14
+ DISALLOWED_EVENTS = {"workflow.execution.streaming", "node.execution.streaming"}
15
+
14
16
 
15
17
  class VellumEmitter(BaseWorkflowEmitter):
16
18
  """
@@ -53,6 +55,9 @@ class VellumEmitter(BaseWorkflowEmitter):
53
55
  if not self._context:
54
56
  return
55
57
 
58
+ if event.name in DISALLOWED_EVENTS:
59
+ return
60
+
56
61
  try:
57
62
  event_data = default_serializer(event)
58
63
 
@@ -1,12 +1,10 @@
1
1
  from functools import cached_property
2
- import inspect
3
2
  from queue import Queue
4
3
  from uuid import uuid4
5
4
  from typing import TYPE_CHECKING, Dict, List, Optional, Type
6
5
 
7
6
  from vellum import Vellum
8
- from vellum.workflows.context import ExecutionContext, get_execution_context, set_execution_context
9
- from vellum.workflows.emitters.vellum_emitter import VellumEmitter
7
+ from vellum.workflows.context import ExecutionContext, get_execution_context
10
8
  from vellum.workflows.events.types import ExternalParentContext
11
9
  from vellum.workflows.nodes.mocks import MockNodeExecution, MockNodeExecutionArg
12
10
  from vellum.workflows.outputs.base import BaseOutputs
@@ -14,7 +12,6 @@ from vellum.workflows.references.constant import ConstantValueReference
14
12
  from vellum.workflows.vellum_client import create_vellum_client
15
13
 
16
14
  if TYPE_CHECKING:
17
- from vellum.workflows.emitters.base import BaseWorkflowEmitter
18
15
  from vellum.workflows.events.workflow import WorkflowEvent
19
16
 
20
17
 
@@ -29,36 +26,15 @@ class WorkflowContext:
29
26
  self._vellum_client = vellum_client
30
27
  self._event_queue: Optional[Queue["WorkflowEvent"]] = None
31
28
  self._node_output_mocks_map: Dict[Type[BaseOutputs], List[MockNodeExecution]] = {}
32
- # Clone the current thread-local execution context to avoid mutating global state
33
- current_execution_context = get_execution_context()
34
-
35
- # Resolve parent_context preference: provided > current > new external
36
- resolved_parent_context = (
37
- execution_context.parent_context
38
- if execution_context is not None and execution_context.parent_context is not None
39
- else current_execution_context.parent_context
40
- )
41
- if resolved_parent_context is None:
42
- resolved_parent_context = ExternalParentContext(span_id=uuid4())
43
-
44
- # Resolve trace_id preference: provided (if set) > current (if set) > new uuid
45
- if execution_context is not None and int(execution_context.trace_id) != 0:
46
- resolved_trace_id = execution_context.trace_id
47
- elif int(current_execution_context.trace_id) != 0:
48
- resolved_trace_id = current_execution_context.trace_id
49
- else:
50
- resolved_trace_id = uuid4()
29
+ self._execution_context = get_execution_context()
51
30
 
52
- # Construct a single, resolved execution context for this workflow instance
53
- self._execution_context = ExecutionContext(
54
- parent_context=resolved_parent_context,
55
- trace_id=resolved_trace_id,
56
- )
31
+ if execution_context is not None:
32
+ self._execution_context.trace_id = execution_context.trace_id
33
+ if execution_context.parent_context is not None:
34
+ self._execution_context.parent_context = execution_context.parent_context
57
35
 
58
- # Ensure the thread-local context has a parent_context for nodes that read it directly
59
- if current_execution_context.parent_context is None:
60
- current_execution_context.parent_context = resolved_parent_context
61
- set_execution_context(current_execution_context)
36
+ if self._execution_context.parent_context is None:
37
+ self._execution_context.parent_context = ExternalParentContext(span_id=uuid4())
62
38
 
63
39
  self._generated_files = generated_files
64
40
 
@@ -128,26 +104,6 @@ class WorkflowContext:
128
104
  # For custom domains, assume the same pattern: api.* -> app.*
129
105
  return api_url.replace("api.", "app.", 1)
130
106
 
131
- def get_emitters_for_workflow(self) -> List["BaseWorkflowEmitter"]:
132
- """
133
- Get the default emitters that should be attached to workflows using this context.
134
-
135
- Returns:
136
- List of emitters, including VellumEmitter if monitoring is enabled.
137
- """
138
- try:
139
- frame = inspect.currentframe()
140
- caller = frame.f_back if frame else None
141
- if caller and "self" in caller.f_locals:
142
- workflow_instance = caller.f_locals["self"]
143
- class_level_emitters = getattr(workflow_instance.__class__, "emitters", None)
144
- if isinstance(class_level_emitters, list) and len(class_level_emitters) > 0:
145
- return class_level_emitters
146
- except Exception:
147
- pass
148
-
149
- return [VellumEmitter()]
150
-
151
107
  def _emit_subworkflow_event(self, event: "WorkflowEvent") -> None:
152
108
  if self._event_queue:
153
109
  self._event_queue.put(event)
@@ -241,7 +241,7 @@ class BaseWorkflow(Generic[InputsType, StateType], metaclass=_BaseWorkflowMeta):
241
241
  ):
242
242
  self._parent_state = parent_state
243
243
  self._context = context or WorkflowContext()
244
- self.emitters = emitters or self._context.get_emitters_for_workflow()
244
+ self.emitters = emitters or (self.emitters if hasattr(self, "emitters") else [])
245
245
  self.resolvers = resolvers or (self.resolvers if hasattr(self, "resolvers") else [])
246
246
  self._store = store or Store()
247
247
  self._execution_context = self._context.execution_context
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 1.1.4
3
+ Version: 1.1.5
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -28,10 +28,10 @@ vellum_ee/workflows/display/editor/__init__.py,sha256=MSAgY91xCEg2neH5d8jXx5wRdR
28
28
  vellum_ee/workflows/display/editor/types.py,sha256=x-tOOCJ6CF4HmiKDfCmcc3bOVfc1EBlP5o6u5WEfLoY,567
29
29
  vellum_ee/workflows/display/exceptions.py,sha256=Oys39dHoW-s-1dnlRSZxTntMq8_macj-b2CT_6dqzJs,355
30
30
  vellum_ee/workflows/display/nodes/__init__.py,sha256=jI1aPBQf8DkmrYoZ4O-wR1duqZByOf5mDFmo_wFJPE4,307
31
- vellum_ee/workflows/display/nodes/base_node_display.py,sha256=oJJiNmF9na2rS5PMMWZLwbajJkssEeBCESDmmz9jy2s,17933
31
+ vellum_ee/workflows/display/nodes/base_node_display.py,sha256=EoAeFrfo8bk4ad0G6ZXeco8iRmfi6rLgvAEQ40Ald4U,17975
32
32
  vellum_ee/workflows/display/nodes/get_node_display_class.py,sha256=jI_kUi9LnNLDpY63QtlC4TfN8P571VN4LpzH0I1ZtLk,1149
33
33
  vellum_ee/workflows/display/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
- vellum_ee/workflows/display/nodes/tests/test_base_node_display.py,sha256=Z4Mf7xLCNiblSbpKI0BrV5modQr-ZcFzhfir_OSyTTs,2997
34
+ vellum_ee/workflows/display/nodes/tests/test_base_node_display.py,sha256=wBoCqULS4XO3s9Vwhd9v4g10opfBFqeZgRqB8CoFz0c,3015
35
35
  vellum_ee/workflows/display/nodes/types.py,sha256=St1BB6no528OyELGiyRabWao0GGw6mLhstQAvEACbGk,247
36
36
  vellum_ee/workflows/display/nodes/utils.py,sha256=sloya5TpXsnot1HURc9L51INwflRqUzHxRVnCS9Cd-4,973
37
37
  vellum_ee/workflows/display/nodes/vellum/__init__.py,sha256=nUIgH2s0-7IbQRNrBhLPyRNe8YIrx3Yo9HeeW-aXXFk,1668
@@ -75,7 +75,7 @@ vellum_ee/workflows/display/tests/workflow_serialization/__init__.py,sha256=47DE
75
75
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
76
76
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py,sha256=Y-ajeT65b5varmrZCw6L3hir4hJCFq-eO0jZfRcrs7g,1886
77
77
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py,sha256=aEmF6zpmmHdFgiG4V81EweOk5qO6cfukR782dYSE960,14060
78
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py,sha256=DFNp_iUxlyE-zCJBH9ab3e1jQEK-NSE9M2CQd4NCjY8,24853
78
+ vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py,sha256=tZ_YK1NpMd8_2ADcbF3PKM2i7fIbSyjUMsP8MCWx2g0,26643
79
79
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py,sha256=s6_mnk0pkztU59wYpSfOFpMhAJaRjmyfxM6WJGtnD4Y,6456
80
80
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py,sha256=PkSgghJDz0fpDB72HHPjLjo8LkZk-HpUkCQzRLX-iVw,40611
81
81
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py,sha256=dsJr8I9AdPwMOGszirfNDzZP2Ychd94aAKuPXAzknMk,4632
@@ -96,14 +96,14 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_subworkflow_
96
96
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_node_serialization.py,sha256=V8b6gKghLlO7PJI8xeNdnfn8aII0W_IFQvSQBQM62UQ,7721
97
97
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py,sha256=hDWtKXmGI1CKhTwTNqpu_d5RkE5n7SolMLtgd87KqTI,3856
98
98
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_composio_serialization.py,sha256=AUfULtIangNYkvLH3jd2Ar8X5ulW4tGmezeCfMmXFUU,3697
99
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py,sha256=4t1lkN2nsZF6lFqP6QnskUQWJlhasF8C2_f6atzk8ZY,26298
99
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py,sha256=K_Wx8QLFeigziK8KvbRFm5M_bBSmVzcsmQnagC97DWo,26363
100
100
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_mcp_serialization.py,sha256=9ORex-okcpwbbkxEDJyyRlbPid6zLSDduK0fBfrp8kk,2415
101
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=B0rDsCvO24qPp0gkmj8SdTDY5CxZYkvKwknsKBuAPyA,10017
101
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py,sha256=txiUpg0BrGJiP2hrTVpwoT4bGqoo0rImSFFvOpigX8c,10082
102
102
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_workflow_deployment_serialization.py,sha256=XIZZr5POo2NLn2uEWm9EC3rejeBMoO4X-JtzTH6mvp4,4074
103
103
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py,sha256=pLCyMScV88DTBXRH7jXaXOEA1GBq8NIipCUFwIAWnwI,2771
104
104
  vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py,sha256=J4ouI8KxbMfxQP2Zq_9cWMGYgbjCWmKzjCJEtnSJb0I,5829
105
105
  vellum_ee/workflows/display/tests/workflow_serialization/test_workflow_input_parameterization_error.py,sha256=vAdmn3YTBDpo55znbydQxsgg9ASqHcvsUPwiBR_7wfo,1461
106
- vellum_ee/workflows/display/types.py,sha256=qRSh7HstBjcAft6dIrij9dLZTanlRWhNwDFfggqJXzs,3174
106
+ vellum_ee/workflows/display/types.py,sha256=cyZruu4sXAdHjwuFc7dydM4DcFNf-pp_CmulXItxac4,3679
107
107
  vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
108
108
  vellum_ee/workflows/display/utils/auto_layout.py,sha256=f4GiLn_LazweupfqTpubcdtdfE_vrOcmZudSsnYIY9E,3906
109
109
  vellum_ee/workflows/display/utils/exceptions.py,sha256=LSwwxCYNxFkf5XMUcFkaZKpQ13OSrI7y_bpEUwbKVk0,169
@@ -114,9 +114,9 @@ vellum_ee/workflows/display/utils/tests/test_auto_layout.py,sha256=vfXI769418s9v
114
114
  vellum_ee/workflows/display/utils/vellum.py,sha256=mtoXmSYwR7rvrq-d6CzCW_auaJXTct0Mi1F0xpRCiNQ,5627
115
115
  vellum_ee/workflows/display/vellum.py,sha256=J2mdJZ1sdLW535DDUkq_Vm8Z572vhuxHxVZF9deKSdk,391
116
116
  vellum_ee/workflows/display/workflows/__init__.py,sha256=JTB9ObEV3l4gGGdtfBHwVJtTTKC22uj-a-XjTVwXCyA,148
117
- vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=ysOq1_con5SvdaeCMeECWEVC1qJzSBTIKA_lJNU0zqQ,41412
117
+ vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=mEJPUZIBPjPdYDUW1ZeWiMTLaDKfnNQQ7kvcwiTJ_ac,42041
118
118
  vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=gxz76AeCqgAZ9D2lZeTiZzxY9eMgn3qOSfVgiqYcOh8,2028
119
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=9CkgKbM_zgq0l2bwst1hLFGyOigpjsBO4ZU6qSMXND4,36027
119
+ vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=pb7BTH-ivRnya1LQU3j-MApWk_m8POpPNOdD0oEK82A,37847
120
120
  vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
121
121
  vellum_ee/workflows/server/virtual_file_loader.py,sha256=7JphJcSO3H85qiC2DpFfBWjC3JjrbRmoynBC6KKHVsA,2710
122
122
  vellum_ee/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -147,7 +147,7 @@ vellum/client/README.md,sha256=gxc7JlJRBrBZpN5LHa2ORxYTRHFLPnWmnIugN8pmQh4,5600
147
147
  vellum/client/__init__.py,sha256=NEcLUhKRIK0OGGXTSQbarRaacFkJWFmM5iY4F9lgIDI,72131
148
148
  vellum/client/core/__init__.py,sha256=lTcqUPXcx4112yLDd70RAPeqq6tu3eFMe1pKOqkW9JQ,1562
149
149
  vellum/client/core/api_error.py,sha256=44vPoTyWN59gonCIZMdzw7M1uspygiLnr3GNFOoVL2Q,614
150
- vellum/client/core/client_wrapper.py,sha256=6vp_q4aKby-T0ukw9WHtdpLBDqY6TQ7mpOcJmVsaiE4,2840
150
+ vellum/client/core/client_wrapper.py,sha256=DpUpUgjS2uzkAZFSPBjxVkQxUTezxAyRpEypFJZvapU,2840
151
151
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
152
152
  vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
153
153
  vellum/client/core/force_multipart.py,sha256=awxh5MtcRYe74ehY8U76jzv6fYM_w_D3Rur7KQQzSDk,429
@@ -1598,7 +1598,7 @@ vellum/workflows/edges/__init__.py,sha256=wSkmAnz9xyi4vZwtDbKxwlplt2skD7n3NsxkvR
1598
1598
  vellum/workflows/edges/edge.py,sha256=N0SnY3gKVuxImPAdCbPMPlHJIXbkQ3fwq_LbJRvVMFc,677
1599
1599
  vellum/workflows/emitters/__init__.py,sha256=d9QFOI3eVg6rzpSFLvrjkDYXWikf1tcp3ruTRa2Boyc,143
1600
1600
  vellum/workflows/emitters/base.py,sha256=Tcp13VMB-GMwEJdl-6XTPckspdOdwpMgBx22-PcQxds,892
1601
- vellum/workflows/emitters/vellum_emitter.py,sha256=xiVlqe3B5fTR-e48nuneeodN1tkrBt0QQa0o9JHdyWg,5077
1601
+ vellum/workflows/emitters/vellum_emitter.py,sha256=c84BNgXkpOZDZglRz26IhmQtLFwk3DnRHwqOmf4GHIY,5223
1602
1602
  vellum/workflows/environment/__init__.py,sha256=TJz0m9dwIs6YOwCTeuN0HHsU-ecyjc1OJXx4AFy83EQ,121
1603
1603
  vellum/workflows/environment/environment.py,sha256=Ck3RPKXJvtMGx_toqYQQQF-ZwXm5ijVwJpEPTeIJ4_Q,471
1604
1604
  vellum/workflows/errors/__init__.py,sha256=tWGPu5xyAU8gRb8_bl0fL7OfU3wxQ9UH6qVwy4X4P_Q,113
@@ -1805,7 +1805,7 @@ vellum/workflows/runner/runner.py,sha256=sADrp593ia4auJ8fIP6jc5na3b5WaTvvBdjpkTt
1805
1805
  vellum/workflows/sandbox.py,sha256=GVJzVjMuYzOBnSrboB0_6MMRZWBluAyQ2o7syeaeBd0,2235
1806
1806
  vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
1807
1807
  vellum/workflows/state/base.py,sha256=m9fCqbZn21GshCVCjJTD1dPZEQjFrsMXqlg7tM9fIwM,24283
1808
- vellum/workflows/state/context.py,sha256=HQbapsGX42rCEn1GuD-jWMpvjrQhosPVJSnashKN2vc,7234
1808
+ vellum/workflows/state/context.py,sha256=i9tQKTB2b2SBpNObtb8skTv63lQcwUDW1ZQ0qDTnGJA,5181
1809
1809
  vellum/workflows/state/delta.py,sha256=7h8wR10lRCm15SykaPj-gSEvvsMjCwYLPsOx3nsvBQg,440
1810
1810
  vellum/workflows/state/encoder.py,sha256=9wj9za3pjLGaKh85IB71YvPDRDtHcw78wR5vyPyKWM8,2708
1811
1811
  vellum/workflows/state/store.py,sha256=uVe-oN73KwGV6M6YLhwZMMUQhzTQomsVfVnb8V91gVo,1147
@@ -1837,13 +1837,13 @@ vellum/workflows/utils/uuids.py,sha256=DFzPv9RCvsKhvdTEIQyfSek2A31D6S_QcmeLPbgrg
1837
1837
  vellum/workflows/utils/vellum_variables.py,sha256=vg7PLTqBoPLWXrcFR2vOfJpKkzvDC85cL2sSUqDWXWU,5503
1838
1838
  vellum/workflows/vellum_client.py,sha256=xkfoucodxNK5JR2-lbRqZx3xzDgExWkP6kySrpi_Ubc,1079
1839
1839
  vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
1840
- vellum/workflows/workflows/base.py,sha256=FTW2y5O8bUgMD1aluavwQ-UYTURRW1DRxDwdynR0OZc,27343
1840
+ vellum/workflows/workflows/base.py,sha256=C17t-YpaBishP5PNThPgmMtYQsint55AcPIweBTVi7I,27354
1841
1841
  vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
1842
1842
  vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1843
1843
  vellum/workflows/workflows/tests/test_base_workflow.py,sha256=ptMntHzVyy8ZuzNgeTuk7hREgKQ5UBdgq8VJFSGaW4Y,20832
1844
1844
  vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
1845
- vellum_ai-1.1.4.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1846
- vellum_ai-1.1.4.dist-info/METADATA,sha256=vtS36kiUPHeBJrpddCNWGFQRpj7hTebtWkd4xCZVdKA,5547
1847
- vellum_ai-1.1.4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1848
- vellum_ai-1.1.4.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1849
- vellum_ai-1.1.4.dist-info/RECORD,,
1845
+ vellum_ai-1.1.5.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1846
+ vellum_ai-1.1.5.dist-info/METADATA,sha256=tyN3xDCmqMqgJmFZweIhi6oIZXIrDFs3Xnac__QYrdI,5547
1847
+ vellum_ai-1.1.5.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1848
+ vellum_ai-1.1.5.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1849
+ vellum_ai-1.1.5.dist-info/RECORD,,
@@ -417,8 +417,8 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
417
417
  if explicit_value and explicit_value.comment and docstring:
418
418
  comment = (
419
419
  NodeDisplayComment(value=docstring, expanded=explicit_value.comment.expanded)
420
- if explicit_value.comment.expanded
421
- else NodeDisplayComment(value=docstring)
420
+ if explicit_value.comment.expanded is not None
421
+ else NodeDisplayComment(value=docstring, expanded=True)
422
422
  )
423
423
  return NodeDisplayData(
424
424
  position=explicit_value.position,
@@ -432,7 +432,7 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
432
432
 
433
433
  if docstring:
434
434
  return NodeDisplayData(
435
- comment=NodeDisplayComment(value=docstring),
435
+ comment=NodeDisplayComment(value=docstring, expanded=True),
436
436
  )
437
437
 
438
438
  return NodeDisplayData()
@@ -98,5 +98,5 @@ def test_serialize_display_data():
98
98
  # THEN the condition should be serialized correctly
99
99
  assert data["display_data"] == {
100
100
  "position": {"x": 0.0, "y": 0.0},
101
- "comment": {"value": "I hope this works"},
101
+ "comment": {"expanded": True, "value": "I hope this works"},
102
102
  }
@@ -670,3 +670,53 @@ def test_serialize_node__pydantic_with_node_output_reference(serialize_node):
670
670
  assert any(
671
671
  entry["key"] == "node_ref" and entry["value"]["type"] == "NODE_OUTPUT" for entry in attr_value["entries"]
672
672
  )
673
+
674
+
675
+ def test_serialize_node__comment_expanded_true_when_content_exists(serialize_node):
676
+ """
677
+ Tests that node comment serialization sets expanded=True when comment has content.
678
+ """
679
+
680
+ class NodeWithComment(BaseNode):
681
+ """This is a test comment for the node."""
682
+
683
+ pass
684
+
685
+ serialized_node = serialize_node(NodeWithComment)
686
+
687
+ # WHEN the node is serialized
688
+ display_data = serialized_node["display_data"]
689
+
690
+ # THEN the comment should have expanded=True
691
+ assert "comment" in display_data
692
+ assert display_data["comment"]["value"] == "This is a test comment for the node."
693
+ assert display_data["comment"]["expanded"] is True
694
+
695
+
696
+ def test_serialize_node__comment_expanded_preserved_when_explicitly_set(serialize_node):
697
+ """
698
+ Tests that explicitly set expanded value is preserved during serialization.
699
+ """
700
+ from vellum_ee.workflows.display.editor.types import NodeDisplayComment, NodeDisplayData
701
+ from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
702
+
703
+ class NodeWithExplicitComment(BaseNode):
704
+ """This is a test comment."""
705
+
706
+ pass
707
+
708
+ class NodeWithExplicitCommentDisplay(BaseNodeDisplay[NodeWithExplicitComment]):
709
+ display_data = NodeDisplayData(comment=NodeDisplayComment(value="Custom comment", expanded=False))
710
+
711
+ serialized_node = serialize_node(
712
+ NodeWithExplicitComment,
713
+ global_node_displays={NodeWithExplicitComment: NodeWithExplicitCommentDisplay()},
714
+ )
715
+
716
+ # WHEN the node is serialized
717
+ display_data = serialized_node["display_data"]
718
+
719
+ # THEN the comment should preserve expanded=False
720
+ assert "comment" in display_data
721
+ assert display_data["comment"]["value"] == "This is a test comment."
722
+ assert display_data["comment"]["expanded"] is False
@@ -44,7 +44,10 @@ def test_serialize_workflow():
44
44
  "type": "GENERIC",
45
45
  "display_data": {
46
46
  "position": {"x": 200.0, "y": -50.0},
47
- "comment": {"value": "\n A tool calling node that calls the get_current_weather function.\n "},
47
+ "comment": {
48
+ "expanded": True,
49
+ "value": "\n A tool calling node that calls the get_current_weather function.\n ",
50
+ },
48
51
  },
49
52
  "base": {
50
53
  "name": "ToolCallingNode",
@@ -44,7 +44,10 @@ def test_serialize_workflow():
44
44
  "type": "GENERIC",
45
45
  "display_data": {
46
46
  "position": {"x": 200.0, "y": -50.0},
47
- "comment": {"value": "\n A tool calling node that calls the get_current_weather function.\n "},
47
+ "comment": {
48
+ "expanded": True,
49
+ "value": "\n A tool calling node that calls the get_current_weather function.\n ",
50
+ },
48
51
  },
49
52
  "base": {
50
53
  "name": "ToolCallingNode",
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass, field
2
- from typing import TYPE_CHECKING, Dict, Iterator, List, Tuple, Type
2
+ from typing import TYPE_CHECKING, Dict, Iterator, List, Optional, Tuple, Type
3
3
 
4
4
  from vellum.client import Vellum as VellumClient
5
5
  from vellum.workflows.descriptors.base import BaseDescriptor
@@ -52,15 +52,26 @@ class WorkflowDisplayContext:
52
52
  edge_displays: EdgeDisplays = field(default_factory=dict)
53
53
  port_displays: PortDisplays = field(default_factory=dict)
54
54
  _errors: List[Exception] = field(default_factory=list)
55
+ _invalid_nodes: List[Type[BaseNode]] = field(default_factory=list)
55
56
  _dry_run: bool = False
56
57
 
57
- def add_error(self, error: Exception) -> None:
58
+ def add_error(self, error: Exception, node: Optional[Type[BaseNode]] = None) -> None:
58
59
  if self._dry_run:
59
60
  self._errors.append(error)
60
61
  return
61
62
 
62
63
  raise error
63
64
 
65
+ def add_invalid_node(self, node: Type[BaseNode]) -> None:
66
+ """Track a node that failed to serialize."""
67
+ if node not in self._invalid_nodes:
68
+ self._invalid_nodes.append(node)
69
+
64
70
  @property
65
71
  def errors(self) -> Iterator[Exception]:
66
72
  return iter(self._errors)
73
+
74
+ @property
75
+ def invalid_nodes(self) -> Iterator[Type[BaseNode]]:
76
+ """Get an iterator over nodes that failed to serialize."""
77
+ return iter(self._invalid_nodes)
@@ -194,6 +194,7 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
194
194
  serialized_node = node_display.serialize(self.display_context)
195
195
  except (NotImplementedError, NodeValidationError) as e:
196
196
  self.display_context.add_error(e)
197
+ self.display_context.add_invalid_node(node)
197
198
  continue
198
199
 
199
200
  serialized_nodes[node_display.node_id] = serialized_node
@@ -319,6 +320,10 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
319
320
  # Add an edge for each edge in the workflow
320
321
  for target_node, entrypoint_display in self.display_context.entrypoint_displays.items():
321
322
  unadorned_target_node = get_unadorned_node(target_node)
323
+ # Skip edges to invalid nodes
324
+ if self._is_node_invalid(unadorned_target_node):
325
+ continue
326
+
322
327
  target_node_display = self.display_context.node_displays[unadorned_target_node]
323
328
  edges.append(
324
329
  {
@@ -335,6 +340,12 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
335
340
  unadorned_source_node_port = get_unadorned_port(source_node_port)
336
341
  unadorned_target_node = get_unadorned_node(target_node)
337
342
 
343
+ # Skip edges that reference invalid nodes
344
+ if self._is_node_invalid(unadorned_target_node) or self._is_node_invalid(
345
+ unadorned_source_node_port.node_class
346
+ ):
347
+ continue
348
+
338
349
  source_node_port_display = self.display_context.port_displays[unadorned_source_node_port]
339
350
  target_node_display = self.display_context.node_displays[unadorned_target_node]
340
351
 
@@ -939,5 +950,9 @@ class BaseWorkflowDisplay(Generic[WorkflowType]):
939
950
  is_required = not has_default and not is_optional
940
951
  return is_required
941
952
 
953
+ def _is_node_invalid(self, node: Type[BaseNode]) -> bool:
954
+ """Check if a node failed to serialize and should be considered invalid."""
955
+ return node in self.display_context.invalid_nodes
956
+
942
957
 
943
958
  register_workflow_display_class(workflow_class=BaseWorkflow, workflow_display_class=BaseWorkflowDisplay)
@@ -1,6 +1,6 @@
1
1
  import pytest
2
- from uuid import uuid4
3
- from typing import Optional
2
+ from uuid import UUID, uuid4
3
+ from typing import Any, Optional
4
4
 
5
5
  from vellum.workflows.inputs import BaseInputs
6
6
  from vellum.workflows.nodes.bases.base import BaseNode
@@ -11,11 +11,13 @@ from vellum.workflows.nodes.core.try_node.node import TryNode
11
11
  from vellum.workflows.nodes.displayable.final_output_node.node import FinalOutputNode
12
12
  from vellum.workflows.references.lazy import LazyReference
13
13
  from vellum.workflows.state.base import BaseState
14
+ from vellum.workflows.types.core import JsonObject
14
15
  from vellum.workflows.workflows.base import BaseWorkflow
15
16
  from vellum_ee.workflows.display.editor.types import NodeDisplayData, NodeDisplayPosition
16
17
  from vellum_ee.workflows.display.nodes import BaseNodeDisplay
17
18
  from vellum_ee.workflows.display.nodes.vellum.retry_node import BaseRetryNodeDisplay
18
19
  from vellum_ee.workflows.display.nodes.vellum.try_node import BaseTryNodeDisplay
20
+ from vellum_ee.workflows.display.types import WorkflowDisplayContext
19
21
  from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
20
22
 
21
23
 
@@ -955,3 +957,43 @@ def test_serialize_workflow__state_variables():
955
957
  "required": False,
956
958
  "extensions": {"color": None},
957
959
  }
960
+
961
+
962
+ def test_serialize_workflow__with_complete_node_failure_prunes_edges():
963
+ """Test that edges are pruned when a node completely fails to serialize (serialized_node is null)."""
964
+
965
+ # GIVEN a node that completely fails to serialize
966
+ class FailingNode(BaseNode):
967
+ class Outputs(BaseNode.Outputs):
968
+ result: str
969
+
970
+ class FailingNodeDisplay(BaseNodeDisplay[FailingNode]):
971
+ def serialize(
972
+ self, display_context: WorkflowDisplayContext, error_output_id: Optional[UUID] = None, **kwargs: Any
973
+ ) -> JsonObject:
974
+ raise NotImplementedError("Complete node serialization failure")
975
+
976
+ # AND a workflow with the failing node connected to another node
977
+ class WorkingNode(BaseNode):
978
+ class Outputs(BaseNode.Outputs):
979
+ value: str
980
+
981
+ class Workflow(BaseWorkflow):
982
+ graph = FailingNode >> WorkingNode
983
+
984
+ # WHEN we serialize the workflow with dry_run=True
985
+ workflow_display = get_workflow_display(workflow_class=Workflow, dry_run=True)
986
+
987
+ # AND we register the failing display class
988
+ workflow_display.display_context.node_displays[FailingNode] = FailingNodeDisplay()
989
+
990
+ data: dict = workflow_display.serialize()
991
+
992
+ # THEN the workflow should serialize but with no edges (pruned due to invalid node)
993
+ assert data["workflow_raw_data"]["edges"] == []
994
+
995
+ # AND only the working node and entrypoint should be in the serialized nodes
996
+ assert len(data["workflow_raw_data"]["nodes"]) == 2
997
+ node_types = [node["type"] for node in data["workflow_raw_data"]["nodes"]]
998
+ assert "ENTRYPOINT" in node_types
999
+ assert "GENERIC" in node_types # This is the WorkingNode that should still be serialized