vellum-ai 0.14.20__py3-none-any.whl → 0.14.22__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 +1 -1
- vellum/utils/templating/constants.py +7 -2
- vellum/utils/templating/custom_filters.py +9 -0
- vellum/workflows/README.md +3 -2
- vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py +18 -0
- vellum/workflows/nodes/displayable/api_node/node.py +1 -1
- vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py +4 -2
- vellum/workflows/nodes/displayable/bases/api_node/node.py +8 -3
- vellum/workflows/nodes/tests/test_utils.py +34 -2
- vellum/workflows/nodes/utils.py +26 -0
- {vellum_ai-0.14.20.dist-info → vellum_ai-0.14.22.dist-info}/METADATA +2 -3
- {vellum_ai-0.14.20.dist-info → vellum_ai-0.14.22.dist-info}/RECORD +25 -25
- vellum_ee/workflows/display/base.py +0 -4
- vellum_ee/workflows/display/nodes/vellum/api_node.py +9 -16
- vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py +63 -42
- vellum_ee/workflows/display/nodes/vellum/merge_node.py +8 -5
- vellum_ee/workflows/display/types.py +2 -2
- vellum_ee/workflows/display/vellum.py +1 -1
- vellum_ee/workflows/display/workflows/base_workflow_display.py +12 -41
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +40 -0
- vellum_ee/workflows/display/workflows/vellum_workflow_display.py +2 -4
- vellum_ee/workflows/tests/local_workflow/display/workflow.py +3 -5
- {vellum_ai-0.14.20.dist-info → vellum_ai-0.14.22.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.20.dist-info → vellum_ai-0.14.22.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.20.dist-info → vellum_ai-0.14.22.dist-info}/entry_points.txt +0 -0
@@ -18,7 +18,7 @@ class BaseClientWrapper:
|
|
18
18
|
headers: typing.Dict[str, str] = {
|
19
19
|
"X-Fern-Language": "Python",
|
20
20
|
"X-Fern-SDK-Name": "vellum-ai",
|
21
|
-
"X-Fern-SDK-Version": "0.14.
|
21
|
+
"X-Fern-SDK-Version": "0.14.22",
|
22
22
|
}
|
23
23
|
headers["X_API_KEY"] = self.api_key
|
24
24
|
return headers
|
@@ -10,7 +10,7 @@ import pydash
|
|
10
10
|
import pytz
|
11
11
|
import yaml
|
12
12
|
|
13
|
-
from vellum.utils.templating.custom_filters import is_valid_json_string, replace
|
13
|
+
from vellum.utils.templating.custom_filters import is_valid_json_string, replace, safe_tojson
|
14
14
|
|
15
15
|
DEFAULT_JINJA_GLOBALS: Dict[str, Any] = {
|
16
16
|
"datetime": datetime,
|
@@ -24,9 +24,14 @@ DEFAULT_JINJA_GLOBALS: Dict[str, Any] = {
|
|
24
24
|
"yaml": yaml,
|
25
25
|
}
|
26
26
|
|
27
|
-
FilterFunc = Union[
|
27
|
+
FilterFunc = Union[
|
28
|
+
Callable[[Union[str, bytes]], bool], # is_valid_json_string
|
29
|
+
Callable[[Any, Any, Any], str], # replace
|
30
|
+
Callable[[Any], str], # safe_tojson
|
31
|
+
]
|
28
32
|
|
29
33
|
DEFAULT_JINJA_CUSTOM_FILTERS: Dict[str, FilterFunc] = {
|
30
34
|
"is_valid_json_string": is_valid_json_string,
|
31
35
|
"replace": replace,
|
36
|
+
"tojson": safe_tojson,
|
32
37
|
}
|
@@ -1,6 +1,9 @@
|
|
1
1
|
import json
|
2
2
|
from typing import Any, Union
|
3
3
|
|
4
|
+
from jinja2 import Undefined
|
5
|
+
from jinja2.utils import htmlsafe_json_dumps
|
6
|
+
|
4
7
|
from vellum.workflows.state.encoder import DefaultStateEncoder
|
5
8
|
|
6
9
|
|
@@ -31,3 +34,9 @@ def replace(s: Any, old: Any, new: Any) -> str:
|
|
31
34
|
old_str = encode_to_str(old)
|
32
35
|
new_str = encode_to_str(new)
|
33
36
|
return s_str.replace(old_str, new_str)
|
37
|
+
|
38
|
+
|
39
|
+
def safe_tojson(value: Any) -> str:
|
40
|
+
if isinstance(value, Undefined):
|
41
|
+
return ""
|
42
|
+
return htmlsafe_json_dumps(value, cls=DefaultStateEncoder)
|
vellum/workflows/README.md
CHANGED
@@ -77,7 +77,8 @@ code to UI and vice versa.
|
|
77
77
|
```bash
|
78
78
|
python my_workflow.py
|
79
79
|
|
80
|
-
Note: To use most out-of-box Nodes, and to push/pull to/from the
|
80
|
+
Note: To use most out-of-box Nodes, and to push/pull to/from the Vellum UI, you'll need a Vellum account and API key.
|
81
|
+
You can [sign up for free here](https://app.vellum.ai/signup?f=wsdk&utm_source=github&utm_medium=repo_quickstart&utm_campaign=sdk).
|
81
82
|
|
82
83
|
|
83
84
|
## Documentation
|
@@ -86,5 +87,5 @@ Complete documentation for the Vellum Workflows SDK can be found at https://docs
|
|
86
87
|
|
87
88
|
## Stability
|
88
89
|
|
89
|
-
This SDK is currently in <Availability type="beta" /> and is subject to change. If you'd like to
|
90
|
+
This SDK is currently in <Availability type="beta" /> and is subject to change. If you'd like to participate in
|
90
91
|
our beta program, please [contact us](https://docs.vellum.ai/home/getting-started/support).
|
@@ -299,3 +299,21 @@ def test_templating_node__empty_string_to_list():
|
|
299
299
|
|
300
300
|
# THEN the output should be an empty list, not raise an exception
|
301
301
|
assert outputs.result == []
|
302
|
+
|
303
|
+
|
304
|
+
def test_api_error_templating_node():
|
305
|
+
class UndefinedTemplatingNode(TemplatingNode[BaseState, str]):
|
306
|
+
template = """{{ foo | tojson }}"""
|
307
|
+
inputs = {
|
308
|
+
"bar": "bar",
|
309
|
+
# foo is not define
|
310
|
+
}
|
311
|
+
|
312
|
+
# GIVEN a templating node with an undefined value
|
313
|
+
node = UndefinedTemplatingNode()
|
314
|
+
|
315
|
+
# WHEN the node is run
|
316
|
+
outputs = node.run()
|
317
|
+
|
318
|
+
# THEN the output should be empty string
|
319
|
+
assert outputs.result == ""
|
@@ -20,7 +20,7 @@ def test_run_workflow__secrets(vellum_client):
|
|
20
20
|
method = APIRequestMethod.POST
|
21
21
|
authorization_type = AuthorizationType.BEARER_TOKEN
|
22
22
|
url = "https://api.vellum.ai"
|
23
|
-
|
23
|
+
json = {
|
24
24
|
"key": "value",
|
25
25
|
}
|
26
26
|
headers = {
|
@@ -32,6 +32,7 @@ def test_run_workflow__secrets(vellum_client):
|
|
32
32
|
terminal = node.run()
|
33
33
|
|
34
34
|
assert vellum_client.execute_api.call_count == 1
|
35
|
+
assert vellum_client.execute_api.call_args.kwargs["body"] == {"key": "value"}
|
35
36
|
bearer_token = vellum_client.execute_api.call_args.kwargs["bearer_token"]
|
36
37
|
assert bearer_token == ClientVellumSecret(name="secret")
|
37
38
|
assert terminal.headers == {"X-Response-Header": "bar"}
|
@@ -45,7 +46,7 @@ def test_api_node_raises_error_when_api_call_fails(vellum_client):
|
|
45
46
|
method = APIRequestMethod.GET
|
46
47
|
authorization_type = AuthorizationType.BEARER_TOKEN
|
47
48
|
url = "https://api.vellum.ai"
|
48
|
-
|
49
|
+
json = {
|
49
50
|
"key": "value",
|
50
51
|
}
|
51
52
|
headers = {
|
@@ -64,6 +65,7 @@ def test_api_node_raises_error_when_api_call_fails(vellum_client):
|
|
64
65
|
|
65
66
|
# AND the API call should have been made
|
66
67
|
assert vellum_client.execute_api.call_count == 1
|
68
|
+
assert vellum_client.execute_api.call_args.kwargs["body"] == {"key": "value"}
|
67
69
|
|
68
70
|
|
69
71
|
def test_api_node_defaults_to_get_method(vellum_client):
|
@@ -28,7 +28,7 @@ class BaseAPINode(BaseNode, Generic[StateType]):
|
|
28
28
|
class Trigger(BaseNode.Trigger):
|
29
29
|
merge_behavior = MergeBehavior.AWAIT_ANY
|
30
30
|
|
31
|
-
url: str
|
31
|
+
url: str = ""
|
32
32
|
method: Optional[APIRequestMethod] = APIRequestMethod.GET
|
33
33
|
data: Optional[str] = None
|
34
34
|
json: Optional[Json] = None
|
@@ -57,13 +57,18 @@ class BaseAPINode(BaseNode, Generic[StateType]):
|
|
57
57
|
if isinstance(headers[header], VellumSecret):
|
58
58
|
vellum_instance = True
|
59
59
|
if vellum_instance or bearer_token:
|
60
|
-
return self._vellum_execute_api(bearer_token,
|
60
|
+
return self._vellum_execute_api(bearer_token, json, headers, method, url)
|
61
61
|
else:
|
62
62
|
return self._local_execute_api(data, headers, json, method, url)
|
63
63
|
|
64
64
|
def _local_execute_api(self, data, headers, json, method, url):
|
65
65
|
try:
|
66
|
-
|
66
|
+
if data is not None:
|
67
|
+
prepped = Request(method=method.value, url=url, data=data, headers=headers).prepare()
|
68
|
+
elif json is not None:
|
69
|
+
prepped = Request(method=method.value, url=url, json=json, headers=headers).prepare()
|
70
|
+
else:
|
71
|
+
prepped = Request(method=method.value, url=url, headers=headers).prepare()
|
67
72
|
except Exception as e:
|
68
73
|
raise NodeException(f"Failed to prepare HTTP request: {e}", code=WorkflowErrorCode.PROVIDER_ERROR)
|
69
74
|
try:
|
@@ -1,11 +1,14 @@
|
|
1
1
|
import pytest
|
2
|
-
from typing import List, Union
|
2
|
+
from typing import Any, List, Union
|
3
3
|
|
4
4
|
from pydantic import BaseModel
|
5
5
|
|
6
|
+
from vellum.client.types.chat_message import ChatMessage
|
7
|
+
from vellum.client.types.function_call import FunctionCall as FunctionCallType
|
8
|
+
from vellum.client.types.vellum_value import VellumValue
|
6
9
|
from vellum.workflows.errors.types import WorkflowErrorCode
|
7
10
|
from vellum.workflows.exceptions import NodeException
|
8
|
-
from vellum.workflows.nodes.utils import parse_type_from_str
|
11
|
+
from vellum.workflows.nodes.utils import cast_to_output_type, parse_type_from_str
|
9
12
|
from vellum.workflows.types.core import Json
|
10
13
|
|
11
14
|
|
@@ -131,3 +134,32 @@ def test_parse_type_from_str_error_cases(input_str, output_type, expected_except
|
|
131
134
|
assert excinfo.value.code == expected_code
|
132
135
|
|
133
136
|
assert expected_message in str(excinfo.value)
|
137
|
+
|
138
|
+
|
139
|
+
@pytest.mark.parametrize(
|
140
|
+
"output_type,expected_result",
|
141
|
+
[
|
142
|
+
(str, ""), # String
|
143
|
+
(int, 0), # Number
|
144
|
+
(float, 0.0), # Number
|
145
|
+
(Any, None), # Json
|
146
|
+
(FunctionCallType, {}), # FunctionCall
|
147
|
+
(List[ChatMessage], []), # Chat History
|
148
|
+
(List[VellumValue], []), # Array
|
149
|
+
(Union[float, int], 0.0), # Union
|
150
|
+
],
|
151
|
+
ids=[
|
152
|
+
"string",
|
153
|
+
"integer",
|
154
|
+
"float",
|
155
|
+
"json",
|
156
|
+
"function_call",
|
157
|
+
"chat_history",
|
158
|
+
"array",
|
159
|
+
"union",
|
160
|
+
],
|
161
|
+
)
|
162
|
+
def test_cast_to_output_type_none_value(output_type, expected_result):
|
163
|
+
"""Test that cast_to_output_type returns appropriate default values when None is provided."""
|
164
|
+
result = cast_to_output_type(None, output_type)
|
165
|
+
assert result == expected_result
|
vellum/workflows/nodes/utils.py
CHANGED
@@ -6,6 +6,7 @@ from typing import Any, Callable, Dict, ForwardRef, List, Optional, Type, TypeVa
|
|
6
6
|
|
7
7
|
from pydantic import BaseModel, create_model
|
8
8
|
|
9
|
+
from vellum.client.types.function_call import FunctionCall
|
9
10
|
from vellum.workflows.errors.types import WorkflowErrorCode
|
10
11
|
from vellum.workflows.exceptions import NodeException
|
11
12
|
from vellum.workflows.nodes import BaseNode
|
@@ -195,7 +196,32 @@ def _clean_output_type(output_type: Any) -> Any:
|
|
195
196
|
return output_type
|
196
197
|
|
197
198
|
|
199
|
+
def _get_default_value(output_type: Any) -> Any:
|
200
|
+
origin = get_origin(output_type)
|
201
|
+
args = get_args(output_type)
|
202
|
+
|
203
|
+
if output_type is int or output_type is float:
|
204
|
+
return 0.0
|
205
|
+
elif output_type is str:
|
206
|
+
return ""
|
207
|
+
elif origin is list:
|
208
|
+
return []
|
209
|
+
elif output_type is FunctionCall:
|
210
|
+
return {}
|
211
|
+
elif origin is Union:
|
212
|
+
# Always use the first argument type's default value
|
213
|
+
if args:
|
214
|
+
return _get_default_value(args[0])
|
215
|
+
return None
|
216
|
+
else:
|
217
|
+
# We defined Json as Any now which is why it returns None
|
218
|
+
return None
|
219
|
+
|
220
|
+
|
198
221
|
def cast_to_output_type(result: Any, output_type: Any) -> Any:
|
222
|
+
if result is None:
|
223
|
+
return _get_default_value(output_type)
|
224
|
+
|
199
225
|
clean_output_type = _clean_output_type(output_type)
|
200
226
|
DynamicModel = create_model("Output", output_type=(clean_output_type, ...))
|
201
227
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: vellum-ai
|
3
|
-
Version: 0.14.
|
3
|
+
Version: 0.14.22
|
4
4
|
Summary:
|
5
5
|
License: MIT
|
6
6
|
Requires-Python: >=3.9,<4.0
|
@@ -90,8 +90,7 @@ Description-Content-Type: text/markdown
|
|
90
90
|
|
91
91
|
## Get Started
|
92
92
|
|
93
|
-
Most functionality within the SDK requires a Vellum account and API key.
|
94
|
-
or visit our [pricing page](https://www.vellum.ai/pricing).
|
93
|
+
Most functionality within the Vellum SDK requires a Vellum account and API key. You can sign up for free [here](https://app.vellum.ai/signup?f=wsdk&utm_source=github&utm_medium=repo_readme&utm_campaign=sdk) or visit our [pricing page](https://www.vellum.ai/pricing) for paid options.
|
95
94
|
|
96
95
|
Even without a Vellum account, you can use the Workflows SDK to define the control flow of your AI systems. [Learn
|
97
96
|
more below](#workflows-sdk).
|
@@ -22,7 +22,7 @@ vellum_ee/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
22
22
|
vellum_ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
23
23
|
vellum_ee/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
24
|
vellum_ee/workflows/display/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
25
|
-
vellum_ee/workflows/display/base.py,sha256
|
25
|
+
vellum_ee/workflows/display/base.py,sha256=xhrMfirVLBuIeDuxdOUmtJ6CcfCx8DChFo-4HfMY6uc,1883
|
26
26
|
vellum_ee/workflows/display/nodes/__init__.py,sha256=436iSAh_Ex5tC68oEYvNgPu05ZVIAVXnS4PKGrQeZ0Y,321
|
27
27
|
vellum_ee/workflows/display/nodes/base_node_display.py,sha256=4xIkrKXVryaD0fPd7RxSpzIqPg6Y14NEB7j7m0DrJvM,18974
|
28
28
|
vellum_ee/workflows/display/nodes/base_node_vellum_display.py,sha256=rhbWYBQSXkpEPIvTa--LPwhsl8RnX4KyU6Ax0M0A0pU,1701
|
@@ -32,8 +32,8 @@ vellum_ee/workflows/display/nodes/tests/test_base_node_display.py,sha256=QqR3Ly0
|
|
32
32
|
vellum_ee/workflows/display/nodes/types.py,sha256=St1BB6no528OyELGiyRabWao0GGw6mLhstQAvEACbGk,247
|
33
33
|
vellum_ee/workflows/display/nodes/utils.py,sha256=sloya5TpXsnot1HURc9L51INwflRqUzHxRVnCS9Cd-4,973
|
34
34
|
vellum_ee/workflows/display/nodes/vellum/__init__.py,sha256=nUIgH2s0-7IbQRNrBhLPyRNe8YIrx3Yo9HeeW-aXXFk,1668
|
35
|
-
vellum_ee/workflows/display/nodes/vellum/api_node.py,sha256=
|
36
|
-
vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py,sha256=
|
35
|
+
vellum_ee/workflows/display/nodes/vellum/api_node.py,sha256=lumtypr0JEZfiA32hcs_olTmnT0wIUzPBy0pnZVfyU4,8532
|
36
|
+
vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py,sha256=Bzqplrnx-bDIRD1JgU7036m8pSWSO45zEReNv8RJTu4,6379
|
37
37
|
vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=IYx0nll0t-tsPcjfgpfHMZE4FJgMFIsOiaQanGLYF0Q,4410
|
38
38
|
vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=ybLIa4uclqVIy3VAQvI1ivg2tnK5Ug_1R5a69DFqL7E,11104
|
39
39
|
vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=I1Jkp2htRINJATtv1e-zs9BrReFX842djpiVgBPHDYg,2186
|
@@ -42,7 +42,7 @@ vellum_ee/workflows/display/nodes/vellum/guardrail_node.py,sha256=aYZSJTxknU4LMi
|
|
42
42
|
vellum_ee/workflows/display/nodes/vellum/inline_prompt_node.py,sha256=3fJzrFoGJX_igzWKgXp7f7-owdVBG6xuy-kBSWNbNxc,8734
|
43
43
|
vellum_ee/workflows/display/nodes/vellum/inline_subworkflow_node.py,sha256=MU9I8CB1X1TgL1aa1eT6DHWwNJ-2v79t74xl0oy-fBo,5510
|
44
44
|
vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=8CPnn06HIBxBOiECevUffeVmQmCpec6WtPQnNl9gj9Y,3748
|
45
|
-
vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=
|
45
|
+
vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=xtyecs9mJ_WEwVpP12jxYwvehLXynhqLrPJ-Ahdk2GA,3232
|
46
46
|
vellum_ee/workflows/display/nodes/vellum/note_node.py,sha256=TMb8txILu2uWjzoxaghjgjlzeBAgzn4vkP_8zSh2qoE,1151
|
47
47
|
vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=9YR6MtxVq8Bavb0ud2lZX0t2Y_NUd-dY9kRgq8WBz-Y,3093
|
48
48
|
vellum_ee/workflows/display/nodes/vellum/retry_node.py,sha256=LgokATi7sSS38Fil-XjqoR4t7AMOJ-GzXRw6p606Svo,3397
|
@@ -87,16 +87,16 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_templating_n
|
|
87
87
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_terminal_node_serialization.py,sha256=NdhE3lm7RMQ8DqkraPSq24IbOxNla9unbs4tsMWRzm4,3781
|
88
88
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_try_node_serialization.py,sha256=eD5686C9nWC5s6t08vbAnm9qf9t53gYQM-E1FwAa75c,3035
|
89
89
|
vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_node_serialization.py,sha256=huKAOeMJ2MKmp6XtbvMJTUadqynoV40Ypoz9jsBEBEQ,7431
|
90
|
-
vellum_ee/workflows/display/types.py,sha256=
|
90
|
+
vellum_ee/workflows/display/types.py,sha256=UZ23Hv2ULWFrMaG1mRSbVnPEa9YvuLlv0dOMkA2j5_Y,2466
|
91
91
|
vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
92
92
|
vellum_ee/workflows/display/utils/expressions.py,sha256=9FpOslDI-RCR5m4TgAu9KCHh4aTVnh7CHR2ykyMUDw0,1151
|
93
93
|
vellum_ee/workflows/display/utils/vellum.py,sha256=EVPQUSsZ3OIeLTEbV6LHPor37t9fnj9kJxDqP4PmTLQ,8234
|
94
|
-
vellum_ee/workflows/display/vellum.py,sha256=
|
94
|
+
vellum_ee/workflows/display/vellum.py,sha256=bevbLCd2KqJBKqJ3lQayeRfjY7x1Djf57F9iJ-6KBJw,5162
|
95
95
|
vellum_ee/workflows/display/workflows/__init__.py,sha256=kapXsC67VJcgSuiBMa86FdePG5A9kMB5Pi4Uy1O2ob4,207
|
96
|
-
vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=
|
96
|
+
vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=cvpDj5gDzoO1Sdt8BCo1acvZsNb30bpX-xGftl3u0xE,19940
|
97
97
|
vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=kp0u8LN_2IwshLrhMImhpZx1hRyAcD5gXY-kDuuaGMQ,1269
|
98
|
-
vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=
|
99
|
-
vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=
|
98
|
+
vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=TwxfmIpCL1xrJOjA54d52q5Ko0CFUT2bW60DVD0wrlY,10095
|
99
|
+
vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=AS-vMrM93KEjb02-ye0Il29l3bX74o0Q8P2Nvf9NIZ0,16683
|
100
100
|
vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
101
101
|
vellum_ee/workflows/server/virtual_file_loader.py,sha256=ET-Q83W5Cgqzqz3qtFNwtS2nJEIcm3VtvR5kffsT3VY,2262
|
102
102
|
vellum_ee/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -111,7 +111,7 @@ vellum_ee/workflows/tests/local_workflow/display/__init__.py,sha256=xo75Uqb4aErO
|
|
111
111
|
vellum_ee/workflows/tests/local_workflow/display/nodes/__init__.py,sha256=szW_mgOUriyZ6v1vlnevBgkzNi8g83-ihS98UOLHVcE,155
|
112
112
|
vellum_ee/workflows/tests/local_workflow/display/nodes/final_output.py,sha256=Kv92TCREiZsB9531KZYaBIq83uHn7e_ECw_yAbD1qfk,1017
|
113
113
|
vellum_ee/workflows/tests/local_workflow/display/nodes/templating_node.py,sha256=5cankEe1rDZlXKgILFSPbmN0tUZhIdmcFgz_AguXTJc,1229
|
114
|
-
vellum_ee/workflows/tests/local_workflow/display/workflow.py,sha256=
|
114
|
+
vellum_ee/workflows/tests/local_workflow/display/workflow.py,sha256=rYjUO0TN6GPCDldD4liWFPOLquJmw-sP4HYtyGspe08,2024
|
115
115
|
vellum_ee/workflows/tests/local_workflow/inputs.py,sha256=4cgsZBoUbIY0Rs8gknC9yqxQ-sSoULclx_SAm1FT2RA,96
|
116
116
|
vellum_ee/workflows/tests/local_workflow/metadata.json,sha256=rdu3h5qkFZiqhCAMxoyoWyMI0O8QALC5-URvSIW6F00,24
|
117
117
|
vellum_ee/workflows/tests/local_workflow/nodes/__init__.py,sha256=1F6jxUpSKfPXPj4ZZKSbnX6Mg-VwF3euLJSZfGn6xkM,127
|
@@ -126,7 +126,7 @@ vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
|
|
126
126
|
vellum/client/__init__.py,sha256=tKtdM1_GqmGq1gpi9ydWD_T-MM7fPn8QdHh8ww19cNI,117564
|
127
127
|
vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
|
128
128
|
vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
|
129
|
-
vellum/client/core/client_wrapper.py,sha256=
|
129
|
+
vellum/client/core/client_wrapper.py,sha256=DpiUwVloITIZTAtfFwbXdgMJxWZ-qVWlr-Qx6Kc1pZc,1869
|
130
130
|
vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
|
131
131
|
vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
|
132
132
|
vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
|
@@ -1292,8 +1292,8 @@ vellum/types/workspace_read.py,sha256=9CvgvK8Li8vL6qC5KX7f3-nEHslJ4lw2w07bvXcrjA
|
|
1292
1292
|
vellum/types/workspace_secret_read.py,sha256=Z6QNXHxVHRdrLXSI31KxngePRwJTVoJYMXVbtPQwrxs,159
|
1293
1293
|
vellum/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1294
1294
|
vellum/utils/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1295
|
-
vellum/utils/templating/constants.py,sha256=
|
1296
|
-
vellum/utils/templating/custom_filters.py,sha256=
|
1295
|
+
vellum/utils/templating/constants.py,sha256=mvAcJloHe1D9-LzM_jpzVZEJVYy326OCUMtSqD_vmo0,838
|
1296
|
+
vellum/utils/templating/custom_filters.py,sha256=qdbDSBPVIz-jKbetD4OnZ4In6O-SdeV0oZsixYoiWOY,1116
|
1297
1297
|
vellum/utils/templating/exceptions.py,sha256=cDp140PP4OnInW4qAvg3KqiSiF70C71UyEAKRBR1Abo,46
|
1298
1298
|
vellum/utils/templating/render.py,sha256=P2t9qU4w_WdXAVLM5Nj3bc1-XlIKOkwK-czQ80pHBag,2172
|
1299
1299
|
vellum/utils/templating/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -1301,7 +1301,7 @@ vellum/utils/templating/tests/test_custom_filters.py,sha256=mkJwc7t1gE13SKgPxhF-
|
|
1301
1301
|
vellum/utils/typing.py,sha256=wx_daFqD69cYkuJTVnvNrpjhqC3uuhbnyJ9_bIwC9OU,327
|
1302
1302
|
vellum/utils/uuid.py,sha256=Ch6wWRgwICxLxJCTl5iE3EdRlZj2zADR-zUMUtjcMWM,214
|
1303
1303
|
vellum/version.py,sha256=jq-1PlAYxN9AXuaZqbYk9ak27SgE2lw9Ia5gx1b1gVI,76
|
1304
|
-
vellum/workflows/README.md,sha256=
|
1304
|
+
vellum/workflows/README.md,sha256=hZdTKBIcsTKPofK68oPkBhyt0nnRh0csqC12k4FMHHA,3597
|
1305
1305
|
vellum/workflows/__init__.py,sha256=CssPsbNvN6rDhoLuqpEv7MMKGa51vE6dvAh6U31Pcio,71
|
1306
1306
|
vellum/workflows/constants.py,sha256=2yg4_uo5gpqViy3ZLSwfC8qTybleYCtOnhA4Rj6bacM,1310
|
1307
1307
|
vellum/workflows/context.py,sha256=DwSf8lO9NHABiqOoD3exgrjUoRuNsKtutaL5TgRbD-A,1441
|
@@ -1390,19 +1390,19 @@ vellum/workflows/nodes/core/retry_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TI
|
|
1390
1390
|
vellum/workflows/nodes/core/retry_node/tests/test_node.py,sha256=fNgDufkIsrTC-6ftvogqSpWhqqBj9iNESdfK19B1Yx0,5159
|
1391
1391
|
vellum/workflows/nodes/core/templating_node/__init__.py,sha256=GmyuYo81_A1_Bz6id69ozVFS6FKiuDsZTiA3I6MaL2U,70
|
1392
1392
|
vellum/workflows/nodes/core/templating_node/node.py,sha256=iqBmr2i-f-BqhisNQJiDfewjol0ur7-XpupLStyMJsg,3731
|
1393
|
-
vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=
|
1393
|
+
vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=nXkgGDBg4wP36NwykdMEVWwx_xjv8oGT2rYkwuCB_VU,10075
|
1394
1394
|
vellum/workflows/nodes/core/try_node/__init__.py,sha256=JVD4DrldTIqFQQFrubs9KtWCCc0YCAc7Fzol5ZWIWeM,56
|
1395
1395
|
vellum/workflows/nodes/core/try_node/node.py,sha256=RbxL0NRXS0IxRP0MJAnLABolF6dkwVniiqsagzy-lwk,4445
|
1396
1396
|
vellum/workflows/nodes/core/try_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1397
1397
|
vellum/workflows/nodes/core/try_node/tests/test_node.py,sha256=h6eUc3SggvhzBWlOD0PrPUlkoCSQHwjqYn81VkxSIxU,4948
|
1398
1398
|
vellum/workflows/nodes/displayable/__init__.py,sha256=6F_4DlSwvHuilWnIalp8iDjjDXl0Nmz4QzJV2PYe5RI,1023
|
1399
1399
|
vellum/workflows/nodes/displayable/api_node/__init__.py,sha256=MoxdQSnidIj1Nf_d-hTxlOxcZXaZnsWFDbE-PkTK24o,56
|
1400
|
-
vellum/workflows/nodes/displayable/api_node/node.py,sha256=
|
1400
|
+
vellum/workflows/nodes/displayable/api_node/node.py,sha256=cp0nAukcOpM6TcNhbz12h08TMJxp_LM-MLDl1dAzYsk,2534
|
1401
1401
|
vellum/workflows/nodes/displayable/api_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1402
|
-
vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py,sha256=
|
1402
|
+
vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py,sha256=J21H1dQT0BJ0oAalaA-9mgKv-NRcCJaTImhnKXp-cX4,3294
|
1403
1403
|
vellum/workflows/nodes/displayable/bases/__init__.py,sha256=0mWIx3qUrzllV7jqt7wN03vWGMuI1WrrLZeMLT2Cl2c,304
|
1404
1404
|
vellum/workflows/nodes/displayable/bases/api_node/__init__.py,sha256=1jwx4WC358CLA1jgzl_UD-rZmdMm2v9Mps39ndwCD7U,64
|
1405
|
-
vellum/workflows/nodes/displayable/bases/api_node/node.py,sha256=
|
1405
|
+
vellum/workflows/nodes/displayable/bases/api_node/node.py,sha256=70pLGU0UzWvSbKwNkx3YlUYrDSkl7MmhVHoI8bzN79c,4343
|
1406
1406
|
vellum/workflows/nodes/displayable/bases/base_prompt_node/__init__.py,sha256=Org3xTvgp1pA0uUXFfnJr29D3HzCey2lEdYF4zbIUgo,70
|
1407
1407
|
vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py,sha256=HGNoGLJ9lbqflGdYFDIiuHFyi0iJ-agJu4kkJ7D3dGs,3212
|
1408
1408
|
vellum/workflows/nodes/displayable/bases/inline_prompt_node/__init__.py,sha256=Hl35IAoepRpE-j4cALaXVJIYTYOF3qszyVbxTj4kS1s,82
|
@@ -1462,8 +1462,8 @@ vellum/workflows/nodes/experimental/openai_chat_completion_node/node.py,sha256=c
|
|
1462
1462
|
vellum/workflows/nodes/mocks.py,sha256=a1FjWEIocseMfjzM-i8DNozpUsaW0IONRpZmXBoWlyc,10455
|
1463
1463
|
vellum/workflows/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1464
1464
|
vellum/workflows/nodes/tests/test_mocks.py,sha256=mfPvrs75PKcsNsbJLQAN6PDFoVqs9TmQxpdyFKDdO60,7837
|
1465
|
-
vellum/workflows/nodes/tests/test_utils.py,sha256=
|
1466
|
-
vellum/workflows/nodes/utils.py,sha256=
|
1465
|
+
vellum/workflows/nodes/tests/test_utils.py,sha256=qNB6ApIsnFtE_9HDaEah9KD-zX8e10FhDixewS1uRcc,5199
|
1466
|
+
vellum/workflows/nodes/utils.py,sha256=Tc3TAmAytb-gi30BAyzGY7DG0uS1_7KTGYdjrvKUSt0,8362
|
1467
1467
|
vellum/workflows/outputs/__init__.py,sha256=AyZ4pRh_ACQIGvkf0byJO46EDnSix1ZCAXfvh-ms1QE,94
|
1468
1468
|
vellum/workflows/outputs/base.py,sha256=b4Dnha1miKu3uFJYptKKflIHNuajPF2BNKy0BTt8Tjc,8622
|
1469
1469
|
vellum/workflows/ports/__init__.py,sha256=bZuMt-R7z5bKwpu4uPW7LlJeePOQWmCcDSXe5frUY5g,101
|
@@ -1522,8 +1522,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
|
|
1522
1522
|
vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1523
1523
|
vellum/workflows/workflows/tests/test_base_workflow.py,sha256=NRteiICyJvDM5zrtUfq2fZoXcGQVaWC9xmNlLLVW0cU,7979
|
1524
1524
|
vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
|
1525
|
-
vellum_ai-0.14.
|
1526
|
-
vellum_ai-0.14.
|
1527
|
-
vellum_ai-0.14.
|
1528
|
-
vellum_ai-0.14.
|
1529
|
-
vellum_ai-0.14.
|
1525
|
+
vellum_ai-0.14.22.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
|
1526
|
+
vellum_ai-0.14.22.dist-info/METADATA,sha256=n1LtcR20giCKtaq1b6qilNyA4MKvG4EON_hG8EC8A9U,5484
|
1527
|
+
vellum_ai-0.14.22.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
1528
|
+
vellum_ai-0.14.22.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
|
1529
|
+
vellum_ai-0.14.22.dist-info/RECORD,,
|
@@ -59,10 +59,6 @@ class EdgeDisplayOverrides(EdgeDisplay):
|
|
59
59
|
pass
|
60
60
|
|
61
61
|
|
62
|
-
EdgeDisplayType = TypeVar("EdgeDisplayType", bound=EdgeDisplay)
|
63
|
-
EdgeDisplayOverridesType = TypeVar("EdgeDisplayOverridesType", bound=EdgeDisplayOverrides)
|
64
|
-
|
65
|
-
|
66
62
|
@dataclass
|
67
63
|
class EntrypointDisplayOverrides:
|
68
64
|
id: UUID
|
@@ -14,15 +14,6 @@ _APINodeType = TypeVar("_APINodeType", bound=APINode)
|
|
14
14
|
|
15
15
|
|
16
16
|
class BaseAPINodeDisplay(BaseNodeVellumDisplay[_APINodeType], Generic[_APINodeType]):
|
17
|
-
url_input_id: ClassVar[Optional[UUID]] = None
|
18
|
-
method_input_id: ClassVar[Optional[UUID]] = None
|
19
|
-
body_input_id: ClassVar[Optional[UUID]] = None
|
20
|
-
|
21
|
-
authorization_type_input_id: ClassVar[Optional[UUID]] = None
|
22
|
-
bearer_token_value_input_id: ClassVar[Optional[UUID]] = None
|
23
|
-
api_key_header_key_input_id: ClassVar[Optional[UUID]] = None
|
24
|
-
api_key_header_value_input_id: ClassVar[Optional[UUID]] = None
|
25
|
-
|
26
17
|
# A mapping between node input keys and their ids for inputs representing additional header keys
|
27
18
|
additional_header_key_input_ids: ClassVar[Optional[Dict[str, UUID]]] = None
|
28
19
|
|
@@ -41,7 +32,7 @@ class BaseAPINodeDisplay(BaseNodeVellumDisplay[_APINodeType], Generic[_APINodeTy
|
|
41
32
|
input_name="url",
|
42
33
|
value=node_url,
|
43
34
|
display_context=display_context,
|
44
|
-
input_id=self.
|
35
|
+
input_id=self.node_input_ids_by_name.get(APINode.url.name),
|
45
36
|
)
|
46
37
|
|
47
38
|
node_method = raise_if_descriptor(node.method)
|
@@ -50,7 +41,7 @@ class BaseAPINodeDisplay(BaseNodeVellumDisplay[_APINodeType], Generic[_APINodeTy
|
|
50
41
|
input_name="method",
|
51
42
|
value=node_method,
|
52
43
|
display_context=display_context,
|
53
|
-
input_id=self.
|
44
|
+
input_id=self.node_input_ids_by_name.get(APINode.method.name),
|
54
45
|
)
|
55
46
|
|
56
47
|
node_data = raise_if_descriptor(node.data)
|
@@ -60,7 +51,9 @@ class BaseAPINodeDisplay(BaseNodeVellumDisplay[_APINodeType], Generic[_APINodeTy
|
|
60
51
|
input_name="body",
|
61
52
|
value=node_data if node_data else node_json,
|
62
53
|
display_context=display_context,
|
63
|
-
input_id=self.
|
54
|
+
input_id=self.node_input_ids_by_name.get(APINode.json.name)
|
55
|
+
# Kept for backwards compatibility with a bug in previous versions of SDK serialization
|
56
|
+
or self.node_input_ids_by_name.get("body"),
|
64
57
|
)
|
65
58
|
|
66
59
|
headers = raise_if_descriptor(node.headers)
|
@@ -75,7 +68,7 @@ class BaseAPINodeDisplay(BaseNodeVellumDisplay[_APINodeType], Generic[_APINodeTy
|
|
75
68
|
input_name="authorization_type",
|
76
69
|
value=authorization_type,
|
77
70
|
display_context=display_context,
|
78
|
-
input_id=self.
|
71
|
+
input_id=self.node_input_ids_by_name.get(APINode.authorization_type.name),
|
79
72
|
)
|
80
73
|
if authorization_type
|
81
74
|
else None
|
@@ -85,7 +78,7 @@ class BaseAPINodeDisplay(BaseNodeVellumDisplay[_APINodeType], Generic[_APINodeTy
|
|
85
78
|
input_name="bearer_token_value",
|
86
79
|
value=bearer_token_value,
|
87
80
|
display_context=display_context,
|
88
|
-
input_id=self.
|
81
|
+
input_id=self.node_input_ids_by_name.get(APINode.bearer_token_value.name),
|
89
82
|
pointer_type=WorkspaceSecretPointer,
|
90
83
|
)
|
91
84
|
api_key_header_key_node_input = (
|
@@ -94,7 +87,7 @@ class BaseAPINodeDisplay(BaseNodeVellumDisplay[_APINodeType], Generic[_APINodeTy
|
|
94
87
|
input_name="api_key_header_key",
|
95
88
|
value=api_key_header_key,
|
96
89
|
display_context=display_context,
|
97
|
-
input_id=self.
|
90
|
+
input_id=self.node_input_ids_by_name.get(APINode.api_key_header_key.name),
|
98
91
|
)
|
99
92
|
if api_key_header_key
|
100
93
|
else None
|
@@ -104,7 +97,7 @@ class BaseAPINodeDisplay(BaseNodeVellumDisplay[_APINodeType], Generic[_APINodeTy
|
|
104
97
|
input_name="api_key_header_value",
|
105
98
|
value=api_key_header_value,
|
106
99
|
display_context=display_context,
|
107
|
-
input_id=self.
|
100
|
+
input_id=self.node_input_ids_by_name.get(APINode.api_key_header_value.name),
|
108
101
|
pointer_type=WorkspaceSecretPointer,
|
109
102
|
)
|
110
103
|
|
@@ -3,6 +3,7 @@ import types
|
|
3
3
|
from uuid import UUID
|
4
4
|
from typing import Any, Callable, Dict, Generic, Optional, Type, TypeVar, cast
|
5
5
|
|
6
|
+
from vellum.workflows.nodes.bases.base import BaseNode
|
6
7
|
from vellum.workflows.nodes.bases.base_adornment_node import BaseAdornmentNode
|
7
8
|
from vellum.workflows.nodes.utils import get_wrapped_node
|
8
9
|
from vellum.workflows.types.core import JsonArray, JsonObject
|
@@ -17,7 +18,62 @@ from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
|
17
18
|
_BaseAdornmentNodeType = TypeVar("_BaseAdornmentNodeType", bound=BaseAdornmentNode)
|
18
19
|
|
19
20
|
|
21
|
+
def _recursively_replace_wrapped_node(node_class: Type[BaseNode], wrapped_node_display_class: Type["BaseNodeDisplay"]):
|
22
|
+
if not issubclass(node_class, BaseAdornmentNode):
|
23
|
+
return
|
24
|
+
|
25
|
+
# We must edit the node display class to use __wrapped_node__ everywhere it
|
26
|
+
# references the adorned node class, which is three places:
|
27
|
+
wrapped_node_class = node_class.__wrapped_node__
|
28
|
+
if not wrapped_node_class:
|
29
|
+
raise ValueError(f"Node {node_class.__name__} must be used as an adornment with the `wrap` method.")
|
30
|
+
|
31
|
+
# 1. The node display class' parameterized type
|
32
|
+
original_base_node_display = get_original_base(wrapped_node_display_class)
|
33
|
+
original_base_node_display.__args__ = (wrapped_node_class,)
|
34
|
+
wrapped_node_display_class._node_display_registry[wrapped_node_class] = wrapped_node_display_class
|
35
|
+
wrapped_node_display_class.__annotate_node__()
|
36
|
+
|
37
|
+
# 2. The node display class' output displays
|
38
|
+
for old_output in node_class.Outputs:
|
39
|
+
new_output = getattr(wrapped_node_class.Outputs, old_output.name, None)
|
40
|
+
if new_output is None:
|
41
|
+
# If the adornment is adding a new output, such as TryNode adding an "error" output,
|
42
|
+
# we skip it, since it should not be included in the adorned node's output displays
|
43
|
+
wrapped_node_display_class.output_display.pop(old_output, None)
|
44
|
+
continue
|
45
|
+
|
46
|
+
if old_output not in wrapped_node_display_class.output_display:
|
47
|
+
# If the adorned node doesn't have an output display defined for this output, we define one
|
48
|
+
wrapped_node_display_class.output_display[new_output] = NodeOutputDisplay(
|
49
|
+
id=wrapped_node_class.__output_ids__[old_output.name],
|
50
|
+
name=old_output.name,
|
51
|
+
)
|
52
|
+
else:
|
53
|
+
wrapped_node_display_class.output_display[new_output] = wrapped_node_display_class.output_display.pop(
|
54
|
+
old_output
|
55
|
+
)
|
56
|
+
|
57
|
+
# 3. The node display class' port displays
|
58
|
+
old_ports = list(wrapped_node_display_class.port_displays.keys())
|
59
|
+
for old_port in old_ports:
|
60
|
+
new_port = getattr(wrapped_node_class.Ports, old_port.name)
|
61
|
+
wrapped_node_display_class.port_displays[new_port] = wrapped_node_display_class.port_displays.pop(old_port)
|
62
|
+
|
63
|
+
# Now we keep drilling down recursively
|
64
|
+
if (
|
65
|
+
issubclass(wrapped_node_display_class, BaseAdornmentNodeDisplay)
|
66
|
+
and wrapped_node_display_class.__wrapped_node_display__
|
67
|
+
):
|
68
|
+
_recursively_replace_wrapped_node(
|
69
|
+
wrapped_node_class,
|
70
|
+
wrapped_node_display_class.__wrapped_node_display__,
|
71
|
+
)
|
72
|
+
|
73
|
+
|
20
74
|
class BaseAdornmentNodeDisplay(BaseNodeVellumDisplay[_BaseAdornmentNodeType], Generic[_BaseAdornmentNodeType]):
|
75
|
+
__wrapped_node_display__: Optional[Type[BaseNodeDisplay]] = None
|
76
|
+
|
21
77
|
def serialize(
|
22
78
|
self,
|
23
79
|
display_context: "WorkflowDisplayContext",
|
@@ -52,10 +108,6 @@ class BaseAdornmentNodeDisplay(BaseNodeVellumDisplay[_BaseAdornmentNodeType], Ge
|
|
52
108
|
if not issubclass(node_class, BaseAdornmentNode):
|
53
109
|
raise ValueError(f"Node {node_class.__name__} must be wrapped with a {BaseAdornmentNode.__name__}")
|
54
110
|
|
55
|
-
wrapped_node_class = node_class.__wrapped_node__
|
56
|
-
if not wrapped_node_class:
|
57
|
-
raise ValueError(f"Node {node_class.__name__} must be used as an adornment with the `wrap` method.")
|
58
|
-
|
59
111
|
# `mypy` is wrong here, `cls` is indexable bc it's Generic
|
60
112
|
BaseAdornmentDisplay = cls[node_class] # type: ignore[index]
|
61
113
|
|
@@ -64,6 +116,7 @@ class BaseAdornmentNodeDisplay(BaseNodeVellumDisplay[_BaseAdornmentNodeType], Ge
|
|
64
116
|
ns[key] = kwarg
|
65
117
|
|
66
118
|
ns["node_id"] = node_id or uuid4_from_hash(node_class.__qualname__)
|
119
|
+
ns["__wrapped_node_display__"] = wrapped_node_display_class
|
67
120
|
|
68
121
|
AdornmentDisplay = types.new_class(
|
69
122
|
re.sub(r"^Base", "", cls.__name__), bases=(BaseAdornmentDisplay,), exec_body=exec_body
|
@@ -71,43 +124,11 @@ class BaseAdornmentNodeDisplay(BaseNodeVellumDisplay[_BaseAdornmentNodeType], Ge
|
|
71
124
|
|
72
125
|
setattr(wrapped_node_display_class, "__adorned_by__", AdornmentDisplay)
|
73
126
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
wrapped_node_display_class._node_display_registry[wrapped_node_class] = wrapped_node_display_class
|
81
|
-
wrapped_node_display_class.__annotate_node__()
|
82
|
-
|
83
|
-
# 2. The node display class' output displays
|
84
|
-
for old_output in node_class.Outputs:
|
85
|
-
new_output = getattr(wrapped_node_class.Outputs, old_output.name, None)
|
86
|
-
if new_output is None:
|
87
|
-
# If the adornment is adding a new output, such as TryNode adding an "error" output,
|
88
|
-
# we skip it, since it should not be included in the adorned node's output displays
|
89
|
-
wrapped_node_display_class.output_display.pop(old_output, None)
|
90
|
-
continue
|
91
|
-
|
92
|
-
if old_output not in wrapped_node_display_class.output_display:
|
93
|
-
# If the adorned node doesn't have an output display defined for this output, we define one
|
94
|
-
wrapped_node_display_class.output_display[new_output] = NodeOutputDisplay(
|
95
|
-
id=wrapped_node_class.__output_ids__[old_output.name],
|
96
|
-
name=old_output.name,
|
97
|
-
)
|
98
|
-
else:
|
99
|
-
wrapped_node_display_class.output_display[new_output] = (
|
100
|
-
wrapped_node_display_class.output_display.pop(old_output)
|
101
|
-
)
|
102
|
-
|
103
|
-
# 3. The node display class' port displays
|
104
|
-
old_ports = list(wrapped_node_display_class.port_displays.keys())
|
105
|
-
for old_port in old_ports:
|
106
|
-
new_port = getattr(wrapped_node_class.Ports, old_port.name)
|
107
|
-
wrapped_node_display_class.port_displays[new_port] = wrapped_node_display_class.port_displays.pop(
|
108
|
-
old_port
|
109
|
-
)
|
110
|
-
|
111
|
-
return wrapped_node_display_class
|
127
|
+
_recursively_replace_wrapped_node(
|
128
|
+
node_class,
|
129
|
+
wrapped_node_display_class,
|
130
|
+
)
|
131
|
+
|
132
|
+
return AdornmentDisplay
|
112
133
|
|
113
134
|
return decorator
|
@@ -6,7 +6,6 @@ from vellum.workflows.types.core import JsonObject
|
|
6
6
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
7
7
|
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
8
8
|
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
9
|
-
from vellum_ee.workflows.display.vellum import EdgeVellumDisplay
|
10
9
|
|
11
10
|
_MergeNodeType = TypeVar("_MergeNodeType", bound=MergeNode)
|
12
11
|
|
@@ -22,16 +21,20 @@ class BaseMergeNodeDisplay(BaseNodeVellumDisplay[_MergeNodeType], Generic[_Merge
|
|
22
21
|
node = self._node
|
23
22
|
node_id = self.node_id
|
24
23
|
|
25
|
-
|
26
|
-
|
24
|
+
merged_source_ports = [
|
25
|
+
source_node_port
|
26
|
+
for (source_node_port, target_node), _ in display_context.edge_displays.items()
|
27
|
+
if target_node.__id__ == self.node_id
|
28
|
+
]
|
27
29
|
|
28
30
|
target_handle_ids = self.get_target_handle_ids()
|
29
31
|
|
30
32
|
if target_handle_ids is None:
|
31
33
|
target_handle_ids = [
|
32
|
-
uuid4_from_hash(f"{node_id}|target_handle|{
|
34
|
+
uuid4_from_hash(f"{node_id}|target_handle|{source_node_port.node_class.__id__}")
|
35
|
+
for source_node_port in merged_source_ports
|
33
36
|
]
|
34
|
-
elif len(target_handle_ids) != len(
|
37
|
+
elif len(target_handle_ids) != len(merged_source_ports):
|
35
38
|
raise ValueError("If you explicitly specify target_handle_ids, you must specify one for each incoming edge")
|
36
39
|
|
37
40
|
return {
|
@@ -7,6 +7,7 @@ from vellum.workflows.nodes import BaseNode
|
|
7
7
|
from vellum.workflows.ports import Port
|
8
8
|
from vellum.workflows.references import OutputReference, StateValueReference, WorkflowInputReference
|
9
9
|
from vellum_ee.workflows.display.base import (
|
10
|
+
EdgeDisplay,
|
10
11
|
EntrypointDisplayType,
|
11
12
|
StateValueDisplayType,
|
12
13
|
WorkflowInputsDisplayType,
|
@@ -17,7 +18,6 @@ from vellum_ee.workflows.display.base import (
|
|
17
18
|
if TYPE_CHECKING:
|
18
19
|
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
19
20
|
from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay, PortDisplay
|
20
|
-
from vellum_ee.workflows.display.vellum import EdgeVellumDisplay
|
21
21
|
from vellum_ee.workflows.display.workflows import BaseWorkflowDisplay
|
22
22
|
|
23
23
|
WorkflowDisplayType = TypeVar("WorkflowDisplayType", bound="BaseWorkflowDisplay")
|
@@ -47,5 +47,5 @@ class WorkflowDisplayContext(
|
|
47
47
|
)
|
48
48
|
entrypoint_displays: Dict[Type[BaseNode], EntrypointDisplayType] = field(default_factory=dict)
|
49
49
|
workflow_output_displays: Dict[BaseDescriptor, WorkflowOutputDisplay] = field(default_factory=dict)
|
50
|
-
edge_displays: Dict[Tuple[Port, Type[BaseNode]],
|
50
|
+
edge_displays: Dict[Tuple[Port, Type[BaseNode]], EdgeDisplay] = field(default_factory=dict)
|
51
51
|
port_displays: Dict[Port, "PortDisplay"] = field(default_factory=dict)
|
@@ -121,7 +121,7 @@ class EntrypointVellumDisplayOverrides(EntrypointDisplay, EntrypointDisplayOverr
|
|
121
121
|
|
122
122
|
@dataclass
|
123
123
|
class EntrypointVellumDisplay(EntrypointVellumDisplayOverrides):
|
124
|
-
edge_display:
|
124
|
+
edge_display: EdgeDisplay
|
125
125
|
|
126
126
|
|
127
127
|
@dataclass
|
@@ -12,7 +12,7 @@ from vellum.workflows.edges import Edge
|
|
12
12
|
from vellum.workflows.events.workflow import NodeEventDisplayContext, WorkflowEventDisplayContext
|
13
13
|
from vellum.workflows.expressions.coalesce_expression import CoalesceExpression
|
14
14
|
from vellum.workflows.nodes.bases import BaseNode
|
15
|
-
from vellum.workflows.nodes.utils import get_unadorned_node,
|
15
|
+
from vellum.workflows.nodes.utils import get_unadorned_node, get_wrapped_node
|
16
16
|
from vellum.workflows.ports import Port
|
17
17
|
from vellum.workflows.references import OutputReference, StateValueReference, WorkflowInputReference
|
18
18
|
from vellum.workflows.types.core import JsonObject
|
@@ -36,7 +36,6 @@ from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_di
|
|
36
36
|
from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay, PortDisplay, PortDisplayOverrides
|
37
37
|
from vellum_ee.workflows.display.nodes.utils import raise_if_descriptor
|
38
38
|
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
39
|
-
from vellum_ee.workflows.display.vellum import EdgeVellumDisplay
|
40
39
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
41
40
|
|
42
41
|
logger = logging.getLogger(__name__)
|
@@ -151,8 +150,6 @@ class BaseWorkflowDisplay(
|
|
151
150
|
if node_output in node_output_displays:
|
152
151
|
continue
|
153
152
|
|
154
|
-
# TODO: Make sure this output ID matches the workflow output ID of the subworkflow node's workflow
|
155
|
-
# https://app.shortcut.com/vellum/story/5660/fix-output-id-in-subworkflow-nodes
|
156
153
|
node_output_displays[node_output] = node_display.get_node_output_display(node_output)
|
157
154
|
|
158
155
|
def _enrich_node_port_displays(
|
@@ -267,14 +264,14 @@ class BaseWorkflowDisplay(
|
|
267
264
|
entrypoint, workflow_display, node_displays, overrides=entrypoint_display_overrides
|
268
265
|
)
|
269
266
|
|
270
|
-
edge_displays: Dict[Tuple[Port, Type[BaseNode]],
|
267
|
+
edge_displays: Dict[Tuple[Port, Type[BaseNode]], EdgeDisplay] = {}
|
271
268
|
for edge in self._workflow.get_edges():
|
272
269
|
if edge in edge_displays:
|
273
270
|
continue
|
274
271
|
|
275
272
|
edge_display_overrides = self.edge_displays.get((edge.from_port, edge.to_node))
|
276
|
-
edge_displays[(edge.from_port, edge.to_node)] = self._generate_edge_display(
|
277
|
-
edge, node_displays
|
273
|
+
edge_displays[(edge.from_port, edge.to_node)] = edge_display_overrides or self._generate_edge_display(
|
274
|
+
edge, node_displays
|
278
275
|
)
|
279
276
|
|
280
277
|
for edge in self._workflow.get_unused_edges():
|
@@ -282,8 +279,8 @@ class BaseWorkflowDisplay(
|
|
282
279
|
continue
|
283
280
|
|
284
281
|
edge_display_overrides = self.edge_displays.get((edge.from_port, edge.to_node))
|
285
|
-
edge_displays[(edge.from_port, edge.to_node)] = self._generate_edge_display(
|
286
|
-
edge, node_displays
|
282
|
+
edge_displays[(edge.from_port, edge.to_node)] = edge_display_overrides or self._generate_edge_display(
|
283
|
+
edge, node_displays
|
287
284
|
)
|
288
285
|
|
289
286
|
workflow_output_displays: Dict[BaseDescriptor, WorkflowOutputDisplay] = {}
|
@@ -444,46 +441,20 @@ class BaseWorkflowDisplay(
|
|
444
441
|
|
445
442
|
return additional_node_displays
|
446
443
|
|
447
|
-
def _generate_edge_display(
|
448
|
-
self,
|
449
|
-
edge: Edge,
|
450
|
-
node_displays: Dict[Type[BaseNode], BaseNodeDisplay],
|
451
|
-
port_displays: Dict[Port, PortDisplay],
|
452
|
-
overrides: Optional[EdgeDisplay] = None,
|
453
|
-
) -> EdgeVellumDisplay:
|
444
|
+
def _generate_edge_display(self, edge: Edge, node_displays: Dict[Type[BaseNode], BaseNodeDisplay]) -> EdgeDisplay:
|
454
445
|
source_node = get_unadorned_node(edge.from_port.node_class)
|
455
446
|
target_node = get_unadorned_node(edge.to_node)
|
456
447
|
|
457
448
|
source_node_id = node_displays[source_node].node_id
|
458
|
-
|
459
|
-
source_handle_id = port_displays[from_port].id
|
449
|
+
target_node_id = node_displays[target_node].node_id
|
460
450
|
|
461
|
-
|
462
|
-
target_node_id = target_node_display.node_id
|
463
|
-
target_handle_id = target_node_display.get_target_handle_id_by_source_node_id(source_node_id)
|
464
|
-
|
465
|
-
return self._generate_edge_display_from_source(
|
466
|
-
source_node_id, source_handle_id, target_node_id, target_handle_id, overrides
|
467
|
-
)
|
451
|
+
return self._generate_edge_display_from_source(source_node_id, target_node_id)
|
468
452
|
|
469
453
|
def _generate_edge_display_from_source(
|
470
454
|
self,
|
471
455
|
source_node_id: UUID,
|
472
|
-
source_handle_id: UUID,
|
473
456
|
target_node_id: UUID,
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
edge_id: UUID
|
478
|
-
if overrides:
|
479
|
-
edge_id = overrides.id
|
480
|
-
else:
|
481
|
-
edge_id = uuid4_from_hash(f"{self.workflow_id}|id|{source_node_id}|{target_node_id}")
|
482
|
-
|
483
|
-
return EdgeVellumDisplay(
|
484
|
-
id=edge_id,
|
485
|
-
source_node_id=source_node_id,
|
486
|
-
target_node_id=target_node_id,
|
487
|
-
source_handle_id=source_handle_id,
|
488
|
-
target_handle_id=target_handle_id,
|
457
|
+
) -> EdgeDisplay:
|
458
|
+
return EdgeDisplay(
|
459
|
+
id=uuid4_from_hash(f"{self.workflow_id}|id|{source_node_id}|{target_node_id}"),
|
489
460
|
)
|
@@ -235,3 +235,43 @@ def test_get_event_display_context__templating_node_input_display():
|
|
235
235
|
node_event_display = display_context.node_displays[MyNode.__id__]
|
236
236
|
|
237
237
|
assert node_event_display.input_display.keys() == {"inputs.foo"}
|
238
|
+
|
239
|
+
|
240
|
+
def test_get_event_display_context__node_display_for_mutiple_adornments():
|
241
|
+
# GIVEN a simple workflow with multiple adornments
|
242
|
+
@TryNode.wrap()
|
243
|
+
@RetryNode.wrap()
|
244
|
+
class MyNode(BaseNode):
|
245
|
+
class Outputs(BaseNode.Outputs):
|
246
|
+
foo: str
|
247
|
+
|
248
|
+
class MyWorkflow(BaseWorkflow):
|
249
|
+
graph = MyNode
|
250
|
+
|
251
|
+
# AND a display class for the node
|
252
|
+
node_id = uuid4()
|
253
|
+
inner_node_id = uuid4()
|
254
|
+
innermost_node_id = uuid4()
|
255
|
+
|
256
|
+
@BaseTryNodeDisplay.wrap(node_id=node_id)
|
257
|
+
@BaseRetryNodeDisplay.wrap(node_id=inner_node_id)
|
258
|
+
class MyNodeDisplay(BaseNodeDisplay[MyNode]):
|
259
|
+
node_id = innermost_node_id
|
260
|
+
|
261
|
+
# WHEN we gather the event display context
|
262
|
+
display_context = VellumWorkflowDisplay(MyWorkflow).get_event_display_context()
|
263
|
+
|
264
|
+
# THEN the subworkflow display should be included
|
265
|
+
assert node_id in display_context.node_displays
|
266
|
+
node_event_display = display_context.node_displays[node_id]
|
267
|
+
assert node_event_display.subworkflow_display
|
268
|
+
|
269
|
+
# AND the inner node should be included
|
270
|
+
assert inner_node_id in node_event_display.subworkflow_display.node_displays
|
271
|
+
inner_node_event_display = node_event_display.subworkflow_display.node_displays[inner_node_id]
|
272
|
+
assert inner_node_event_display.subworkflow_display
|
273
|
+
|
274
|
+
# AND the innermost node should be included
|
275
|
+
assert innermost_node_id in inner_node_event_display.subworkflow_display.node_displays
|
276
|
+
innermost_node_event_display = inner_node_event_display.subworkflow_display.node_displays[innermost_node_id]
|
277
|
+
assert not innermost_node_event_display.subworkflow_display
|
@@ -351,7 +351,6 @@ class VellumWorkflowDisplay(
|
|
351
351
|
overrides: Optional[EntrypointVellumDisplayOverrides] = None,
|
352
352
|
) -> EntrypointVellumDisplay:
|
353
353
|
entrypoint_node_id = workflow_display.entrypoint_node_id
|
354
|
-
source_handle_id = workflow_display.entrypoint_node_source_handle_id
|
355
354
|
|
356
355
|
edge_display_overrides = overrides.edge_display if overrides else None
|
357
356
|
entrypoint_id = (
|
@@ -363,10 +362,9 @@ class VellumWorkflowDisplay(
|
|
363
362
|
entrypoint_target = get_unadorned_node(entrypoint)
|
364
363
|
target_node_display = node_displays[entrypoint_target]
|
365
364
|
target_node_id = target_node_display.node_id
|
366
|
-
target_handle_id = target_node_display.get_target_handle_id_by_source_node_id(entrypoint_node_id)
|
367
365
|
|
368
|
-
edge_display = self._generate_edge_display_from_source(
|
369
|
-
entrypoint_node_id,
|
366
|
+
edge_display = edge_display_overrides or self._generate_edge_display_from_source(
|
367
|
+
entrypoint_node_id, target_node_id
|
370
368
|
)
|
371
369
|
|
372
370
|
return EntrypointVellumDisplay(id=entrypoint_id, edge_display=edge_display)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
from uuid import UUID
|
2
2
|
|
3
|
+
from vellum_ee.workflows.display.base import EdgeDisplay
|
3
4
|
from vellum_ee.workflows.display.vellum import (
|
4
|
-
EdgeVellumDisplayOverrides,
|
5
5
|
EntrypointVellumDisplayOverrides,
|
6
6
|
NodeDisplayData,
|
7
7
|
NodeDisplayPosition,
|
@@ -36,13 +36,11 @@ class WorkflowDisplay(VellumWorkflowDisplay[Workflow]):
|
|
36
36
|
entrypoint_displays = {
|
37
37
|
TemplatingNode: EntrypointVellumDisplayOverrides(
|
38
38
|
id=UUID("0bf86989-13f2-438c-ab9c-d172e5771d31"),
|
39
|
-
edge_display=
|
39
|
+
edge_display=EdgeDisplay(id=UUID("38532a0e-9432-4ed2-8a34-48a29fd6984d")),
|
40
40
|
)
|
41
41
|
}
|
42
42
|
edge_displays = {
|
43
|
-
(TemplatingNode.Ports.default, FinalOutput):
|
44
|
-
id=UUID("417c56a4-cdc1-4f9d-a10c-b535163f51e8")
|
45
|
-
)
|
43
|
+
(TemplatingNode.Ports.default, FinalOutput): EdgeDisplay(id=UUID("417c56a4-cdc1-4f9d-a10c-b535163f51e8"))
|
46
44
|
}
|
47
45
|
output_displays = {
|
48
46
|
Workflow.Outputs.final_output: WorkflowOutputVellumDisplayOverrides(
|
File without changes
|
File without changes
|
File without changes
|