vellum-ai 0.14.21__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/tests/test_api_node.py +4 -2
- vellum/workflows/nodes/displayable/bases/api_node/node.py +1 -1
- {vellum_ai-0.14.21.dist-info → vellum_ai-0.14.22.dist-info}/METADATA +2 -3
- {vellum_ai-0.14.21.dist-info → vellum_ai-0.14.22.dist-info}/RECORD +16 -16
- vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py +63 -42
- vellum_ee/workflows/display/workflows/base_workflow_display.py +9 -21
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +40 -0
- vellum_ee/workflows/display/workflows/vellum_workflow_display.py +2 -2
- {vellum_ai-0.14.21.dist-info → vellum_ai-0.14.22.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.21.dist-info → vellum_ai-0.14.22.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.21.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):
|
@@ -57,7 +57,7 @@ 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
|
|
@@ -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).
|
@@ -33,7 +33,7 @@ vellum_ee/workflows/display/nodes/types.py,sha256=St1BB6no528OyELGiyRabWao0GGw6m
|
|
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
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=
|
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
|
@@ -93,10 +93,10 @@ vellum_ee/workflows/display/utils/expressions.py,sha256=9FpOslDI-RCR5m4TgAu9KCHh
|
|
93
93
|
vellum_ee/workflows/display/utils/vellum.py,sha256=EVPQUSsZ3OIeLTEbV6LHPor37t9fnj9kJxDqP4PmTLQ,8234
|
94
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
|
@@ -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,7 +1390,7 @@ 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
|
@@ -1399,10 +1399,10 @@ vellum/workflows/nodes/displayable/__init__.py,sha256=6F_4DlSwvHuilWnIalp8iDjjDX
|
|
1399
1399
|
vellum/workflows/nodes/displayable/api_node/__init__.py,sha256=MoxdQSnidIj1Nf_d-hTxlOxcZXaZnsWFDbE-PkTK24o,56
|
1400
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
|
@@ -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,,
|
@@ -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
|
@@ -150,8 +150,6 @@ class BaseWorkflowDisplay(
|
|
150
150
|
if node_output in node_output_displays:
|
151
151
|
continue
|
152
152
|
|
153
|
-
# TODO: Make sure this output ID matches the workflow output ID of the subworkflow node's workflow
|
154
|
-
# https://app.shortcut.com/vellum/story/5660/fix-output-id-in-subworkflow-nodes
|
155
153
|
node_output_displays[node_output] = node_display.get_node_output_display(node_output)
|
156
154
|
|
157
155
|
def _enrich_node_port_displays(
|
@@ -272,8 +270,8 @@ class BaseWorkflowDisplay(
|
|
272
270
|
continue
|
273
271
|
|
274
272
|
edge_display_overrides = self.edge_displays.get((edge.from_port, edge.to_node))
|
275
|
-
edge_displays[(edge.from_port, edge.to_node)] = self._generate_edge_display(
|
276
|
-
edge, node_displays
|
273
|
+
edge_displays[(edge.from_port, edge.to_node)] = edge_display_overrides or self._generate_edge_display(
|
274
|
+
edge, node_displays
|
277
275
|
)
|
278
276
|
|
279
277
|
for edge in self._workflow.get_unused_edges():
|
@@ -281,8 +279,8 @@ class BaseWorkflowDisplay(
|
|
281
279
|
continue
|
282
280
|
|
283
281
|
edge_display_overrides = self.edge_displays.get((edge.from_port, edge.to_node))
|
284
|
-
edge_displays[(edge.from_port, edge.to_node)] = self._generate_edge_display(
|
285
|
-
edge, node_displays
|
282
|
+
edge_displays[(edge.from_port, edge.to_node)] = edge_display_overrides or self._generate_edge_display(
|
283
|
+
edge, node_displays
|
286
284
|
)
|
287
285
|
|
288
286
|
workflow_output_displays: Dict[BaseDescriptor, WorkflowOutputDisplay] = {}
|
@@ -443,30 +441,20 @@ class BaseWorkflowDisplay(
|
|
443
441
|
|
444
442
|
return additional_node_displays
|
445
443
|
|
446
|
-
def _generate_edge_display(
|
447
|
-
self,
|
448
|
-
edge: Edge,
|
449
|
-
node_displays: Dict[Type[BaseNode], BaseNodeDisplay],
|
450
|
-
overrides: Optional[EdgeDisplay] = None,
|
451
|
-
) -> EdgeDisplay:
|
444
|
+
def _generate_edge_display(self, edge: Edge, node_displays: Dict[Type[BaseNode], BaseNodeDisplay]) -> EdgeDisplay:
|
452
445
|
source_node = get_unadorned_node(edge.from_port.node_class)
|
453
446
|
target_node = get_unadorned_node(edge.to_node)
|
454
447
|
|
455
448
|
source_node_id = node_displays[source_node].node_id
|
456
449
|
target_node_id = node_displays[target_node].node_id
|
457
450
|
|
458
|
-
return self._generate_edge_display_from_source(source_node_id, target_node_id
|
451
|
+
return self._generate_edge_display_from_source(source_node_id, target_node_id)
|
459
452
|
|
460
453
|
def _generate_edge_display_from_source(
|
461
454
|
self,
|
462
455
|
source_node_id: UUID,
|
463
456
|
target_node_id: UUID,
|
464
|
-
overrides: Optional[EdgeDisplay] = None,
|
465
457
|
) -> EdgeDisplay:
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
else:
|
470
|
-
edge_id = uuid4_from_hash(f"{self.workflow_id}|id|{source_node_id}|{target_node_id}")
|
471
|
-
|
472
|
-
return EdgeDisplay(id=edge_id)
|
458
|
+
return EdgeDisplay(
|
459
|
+
id=uuid4_from_hash(f"{self.workflow_id}|id|{source_node_id}|{target_node_id}"),
|
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
|
@@ -363,8 +363,8 @@ class VellumWorkflowDisplay(
|
|
363
363
|
target_node_display = node_displays[entrypoint_target]
|
364
364
|
target_node_id = target_node_display.node_id
|
365
365
|
|
366
|
-
edge_display = self._generate_edge_display_from_source(
|
367
|
-
entrypoint_node_id, target_node_id
|
366
|
+
edge_display = edge_display_overrides or self._generate_edge_display_from_source(
|
367
|
+
entrypoint_node_id, target_node_id
|
368
368
|
)
|
369
369
|
|
370
370
|
return EntrypointVellumDisplay(id=entrypoint_id, edge_display=edge_display)
|
File without changes
|
File without changes
|
File without changes
|