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.
- vellum/client/core/client_wrapper.py +2 -2
- vellum/workflows/emitters/vellum_emitter.py +5 -0
- vellum/workflows/state/context.py +8 -52
- vellum/workflows/workflows/base.py +1 -1
- {vellum_ai-1.1.4.dist-info → vellum_ai-1.1.5.dist-info}/METADATA +1 -1
- {vellum_ai-1.1.4.dist-info → vellum_ai-1.1.5.dist-info}/RECORD +17 -17
- vellum_ee/workflows/display/nodes/base_node_display.py +3 -3
- vellum_ee/workflows/display/nodes/tests/test_base_node_display.py +1 -1
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +50 -0
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_inline_workflow_serialization.py +4 -1
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_tool_calling_node_serialization.py +4 -1
- vellum_ee/workflows/display/types.py +13 -2
- vellum_ee/workflows/display/workflows/base_workflow_display.py +15 -0
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +44 -2
- {vellum_ai-1.1.4.dist-info → vellum_ai-1.1.5.dist-info}/LICENSE +0 -0
- {vellum_ai-1.1.4.dist-info → vellum_ai-1.1.5.dist-info}/WHEEL +0 -0
- {vellum_ai-1.1.4.dist-info → vellum_ai-1.1.5.dist-info}/entry_points.txt +0 -0
@@ -27,10 +27,10 @@ class BaseClientWrapper:
|
|
27
27
|
|
28
28
|
def get_headers(self) -> typing.Dict[str, str]:
|
29
29
|
headers: typing.Dict[str, str] = {
|
30
|
-
"User-Agent": "vellum-ai/1.1.
|
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.
|
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
|
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
|
-
|
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
|
-
|
53
|
-
|
54
|
-
parent_context
|
55
|
-
|
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
|
-
|
59
|
-
|
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.
|
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
|
@@ -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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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.
|
1846
|
-
vellum_ai-1.1.
|
1847
|
-
vellum_ai-1.1.
|
1848
|
-
vellum_ai-1.1.
|
1849
|
-
vellum_ai-1.1.
|
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": {
|
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": {
|
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
|
File without changes
|
File without changes
|
File without changes
|