vellum-ai 0.14.14__py3-none-any.whl → 0.14.15__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/client/core/pydantic_utilities.py +4 -5
- vellum/client/types/logical_operator.py +1 -0
- vellum/workflows/nodes/core/retry_node/tests/test_node.py +23 -0
- vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py +29 -0
- vellum/workflows/nodes/displayable/bases/api_node/node.py +2 -2
- {vellum_ai-0.14.14.dist-info → vellum_ai-0.14.15.dist-info}/METADATA +2 -2
- {vellum_ai-0.14.14.dist-info → vellum_ai-0.14.15.dist-info}/RECORD +23 -23
- vellum_ee/workflows/display/nodes/base_node_display.py +8 -5
- vellum_ee/workflows/display/nodes/base_node_vellum_display.py +1 -4
- vellum_ee/workflows/display/nodes/get_node_display_class.py +34 -8
- vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py +55 -1
- vellum_ee/workflows/display/nodes/vellum/retry_node.py +1 -54
- vellum_ee/workflows/display/nodes/vellum/templating_node.py +1 -8
- vellum_ee/workflows/display/nodes/vellum/try_node.py +1 -42
- vellum_ee/workflows/display/tests/test_vellum_workflow_display.py +47 -10
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py +67 -0
- vellum_ee/workflows/display/utils/vellum.py +3 -0
- vellum_ee/workflows/display/workflows/base_workflow_display.py +3 -6
- vellum_ee/workflows/display/workflows/tests/test_workflow_display.py +52 -0
- {vellum_ai-0.14.14.dist-info → vellum_ai-0.14.15.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.14.dist-info → vellum_ai-0.14.15.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.14.dist-info → vellum_ai-0.14.15.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.15",
|
22
22
|
}
|
23
23
|
headers["X_API_KEY"] = self.api_key
|
24
24
|
return headers
|
@@ -133,12 +133,11 @@ class UniversalBaseModel(pydantic.BaseModel):
|
|
133
133
|
#
|
134
134
|
# We'd ideally do the same for Pydantic V2, but it shells out to a library to serialize models
|
135
135
|
# that we have less control over, and this is less intrusive than custom serializers for now.
|
136
|
-
kwargs = {
|
137
|
-
**kwargs,
|
138
|
-
"warnings": False,
|
139
|
-
}
|
140
|
-
|
141
136
|
if IS_PYDANTIC_V2:
|
137
|
+
kwargs = {
|
138
|
+
**kwargs,
|
139
|
+
"warnings": False,
|
140
|
+
}
|
142
141
|
kwargs_with_defaults_exclude_unset: typing.Any = {
|
143
142
|
**kwargs,
|
144
143
|
"by_alias": True,
|
@@ -33,6 +33,29 @@ def test_retry_node__retry_on_error_code__successfully_retried():
|
|
33
33
|
assert outputs.execution_count == 3
|
34
34
|
|
35
35
|
|
36
|
+
def test_retry_node__retry_on_error_code_all():
|
37
|
+
# GIVEN a retry node that is configured to retry on all errors
|
38
|
+
@RetryNode.wrap(max_attempts=3)
|
39
|
+
class TestNode(BaseNode):
|
40
|
+
attempt_number = RetryNode.SubworkflowInputs.attempt_number
|
41
|
+
|
42
|
+
class Outputs(BaseOutputs):
|
43
|
+
execution_count: int
|
44
|
+
|
45
|
+
def run(self) -> Outputs:
|
46
|
+
if self.attempt_number < 3:
|
47
|
+
raise NodeException(message="This will be retried", code=WorkflowErrorCode.PROVIDER_ERROR)
|
48
|
+
|
49
|
+
return self.Outputs(execution_count=self.attempt_number)
|
50
|
+
|
51
|
+
# WHEN the node is run and throws a None
|
52
|
+
node = TestNode(state=BaseState())
|
53
|
+
outputs = node.run()
|
54
|
+
|
55
|
+
# THEN the exception is retried
|
56
|
+
assert outputs.execution_count == 3
|
57
|
+
|
58
|
+
|
36
59
|
def test_retry_node__retry_on_error_code__missed():
|
37
60
|
# GIVEN a retry node that is configured to retry on PROVIDER_ERROR
|
38
61
|
@RetryNode.wrap(max_attempts=3, retry_on_error_code=WorkflowErrorCode.PROVIDER_ERROR)
|
@@ -64,3 +64,32 @@ def test_api_node_raises_error_when_api_call_fails(vellum_client):
|
|
64
64
|
|
65
65
|
# AND the API call should have been made
|
66
66
|
assert vellum_client.execute_api.call_count == 1
|
67
|
+
|
68
|
+
|
69
|
+
def test_api_node_defaults_to_get_method(vellum_client):
|
70
|
+
# GIVEN a successful API response
|
71
|
+
vellum_client.execute_api.return_value = ExecuteApiResponse(
|
72
|
+
status_code=200,
|
73
|
+
text='{"status": 200, "data": [1, 2, 3]}',
|
74
|
+
json_={"data": [1, 2, 3]},
|
75
|
+
headers={"X-Response-Header": "bar"},
|
76
|
+
)
|
77
|
+
|
78
|
+
# AND an API node without a method specified
|
79
|
+
class SimpleAPINodeWithoutMethod(APINode):
|
80
|
+
authorization_type = AuthorizationType.BEARER_TOKEN
|
81
|
+
url = "https://api.vellum.ai"
|
82
|
+
headers = {
|
83
|
+
"X-Test-Header": "foo",
|
84
|
+
}
|
85
|
+
bearer_token_value = VellumSecret(name="secret")
|
86
|
+
|
87
|
+
node = SimpleAPINodeWithoutMethod()
|
88
|
+
|
89
|
+
# WHEN we run the node
|
90
|
+
node.run()
|
91
|
+
|
92
|
+
# THEN the API call should be made with GET method
|
93
|
+
assert vellum_client.execute_api.call_count == 1
|
94
|
+
method = vellum_client.execute_api.call_args.kwargs["method"]
|
95
|
+
assert method == APIRequestMethod.GET.value
|
@@ -29,7 +29,7 @@ class BaseAPINode(BaseNode, Generic[StateType]):
|
|
29
29
|
merge_behavior = MergeBehavior.AWAIT_ANY
|
30
30
|
|
31
31
|
url: str
|
32
|
-
method: APIRequestMethod
|
32
|
+
method: Optional[APIRequestMethod] = APIRequestMethod.GET
|
33
33
|
data: Optional[str] = None
|
34
34
|
json: Optional[Json] = None
|
35
35
|
headers: Optional[Dict[str, Union[str, VellumSecret]]] = None
|
@@ -45,8 +45,8 @@ class BaseAPINode(BaseNode, Generic[StateType]):
|
|
45
45
|
|
46
46
|
def _run(
|
47
47
|
self,
|
48
|
-
method: APIRequestMethod,
|
49
48
|
url: str,
|
49
|
+
method: Optional[APIRequestMethod] = APIRequestMethod.GET,
|
50
50
|
data: Optional[Union[str, Any]] = None,
|
51
51
|
json: Any = None,
|
52
52
|
headers: Any = None,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: vellum-ai
|
3
|
-
Version: 0.14.
|
3
|
+
Version: 0.14.15
|
4
4
|
Summary:
|
5
5
|
License: MIT
|
6
6
|
Requires-Python: >=3.9,<4.0
|
@@ -33,7 +33,7 @@ Requires-Dist: pydantic-core (>=2.18.2,<3.0.0)
|
|
33
33
|
Requires-Dist: pydash (==7.0.6)
|
34
34
|
Requires-Dist: python-dotenv (==1.0.1)
|
35
35
|
Requires-Dist: pytz (==2025.1)
|
36
|
-
Requires-Dist: pyyaml (==6.0.
|
36
|
+
Requires-Dist: pyyaml (==6.0.2)
|
37
37
|
Requires-Dist: requests (==2.32.3)
|
38
38
|
Requires-Dist: tomli (==2.0.2)
|
39
39
|
Requires-Dist: typing_extensions (>=4.0.0)
|
@@ -23,16 +23,16 @@ vellum_ee/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
23
23
|
vellum_ee/workflows/display/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
24
24
|
vellum_ee/workflows/display/base.py,sha256=ak29FIsawhaFa9_paZUHThlZRFJ1xB486JWKuSt1PYY,1965
|
25
25
|
vellum_ee/workflows/display/nodes/__init__.py,sha256=436iSAh_Ex5tC68oEYvNgPu05ZVIAVXnS4PKGrQeZ0Y,321
|
26
|
-
vellum_ee/workflows/display/nodes/base_node_display.py,sha256=
|
27
|
-
vellum_ee/workflows/display/nodes/base_node_vellum_display.py,sha256=
|
28
|
-
vellum_ee/workflows/display/nodes/get_node_display_class.py,sha256=
|
26
|
+
vellum_ee/workflows/display/nodes/base_node_display.py,sha256=pYmIdmC2dxLyRlZEBlgARIJMLcBX3GxZPpWZISv5Bbg,17170
|
27
|
+
vellum_ee/workflows/display/nodes/base_node_vellum_display.py,sha256=ZLKQ8Xa3h9nGkj4t4V_7OeU7CBFWY3gXB9CkaCLOhEk,2699
|
28
|
+
vellum_ee/workflows/display/nodes/get_node_display_class.py,sha256=RWCGm29-Tabi-qgVEIi_sdTWBv4bEzxAgwAoYI5T-Cc,2566
|
29
29
|
vellum_ee/workflows/display/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
30
30
|
vellum_ee/workflows/display/nodes/tests/test_base_node_display.py,sha256=QqR3Ly0RNrXwOeLdW5nERDFt0gRPf76n1bPES6o5UN4,1093
|
31
31
|
vellum_ee/workflows/display/nodes/types.py,sha256=St1BB6no528OyELGiyRabWao0GGw6mLhstQAvEACbGk,247
|
32
32
|
vellum_ee/workflows/display/nodes/utils.py,sha256=sloya5TpXsnot1HURc9L51INwflRqUzHxRVnCS9Cd-4,973
|
33
33
|
vellum_ee/workflows/display/nodes/vellum/__init__.py,sha256=nUIgH2s0-7IbQRNrBhLPyRNe8YIrx3Yo9HeeW-aXXFk,1668
|
34
34
|
vellum_ee/workflows/display/nodes/vellum/api_node.py,sha256=hoV-cUtS6H9kmRQXHd2py95GRWI_dAnnaPwvlNBkDOQ,8571
|
35
|
-
vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py,sha256=
|
35
|
+
vellum_ee/workflows/display/nodes/vellum/base_adornment_node.py,sha256=t-KSaai6cXRLkNkzCvEbM5SHh03B9fqPwVTH-Gei_-0,4419
|
36
36
|
vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=z00Z3L0d4PsUQo4S8FRDTtOFLtjdi17TJbatNVF4nM8,4288
|
37
37
|
vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=ybLIa4uclqVIy3VAQvI1ivg2tnK5Ug_1R5a69DFqL7E,11104
|
38
38
|
vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=I1Jkp2htRINJATtv1e-zs9BrReFX842djpiVgBPHDYg,2186
|
@@ -44,27 +44,27 @@ vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=8CPnn06HIBxBOiECevUf
|
|
44
44
|
vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=HkNMgdQELiON42jdO-xDLmqrEKdGx1RVqrz2DXNTLS8,3239
|
45
45
|
vellum_ee/workflows/display/nodes/vellum/note_node.py,sha256=TMb8txILu2uWjzoxaghjgjlzeBAgzn4vkP_8zSh2qoE,1151
|
46
46
|
vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=LFjLUrH6sJ4czPnExdRqFr0PB_yKBMLXLvK5GAzIAgc,3273
|
47
|
-
vellum_ee/workflows/display/nodes/vellum/retry_node.py,sha256
|
47
|
+
vellum_ee/workflows/display/nodes/vellum/retry_node.py,sha256=LgokATi7sSS38Fil-XjqoR4t7AMOJ-GzXRw6p606Svo,3397
|
48
48
|
vellum_ee/workflows/display/nodes/vellum/search_node.py,sha256=TxcAGZDl_hvJ7Y1hUi9YVEVrj9Ie0hKkASdpfRL4_cs,9227
|
49
49
|
vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py,sha256=62baAElKoRKIoba0lLhnrXGWWx96B73VxKGxh7BaIxc,2612
|
50
|
-
vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=
|
50
|
+
vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=wP-ZAZq6ZmkS1fj0WkFtRkYqi8c6MWKLh4CEw8JD9mg,3207
|
51
51
|
vellum_ee/workflows/display/nodes/vellum/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
52
52
|
vellum_ee/workflows/display/nodes/vellum/tests/test_error_node.py,sha256=ulrpoYUW-5kIxfG4Lf5F2p0k_EoYKhmahEbF3P_eruM,1648
|
53
53
|
vellum_ee/workflows/display/nodes/vellum/tests/test_prompt_node.py,sha256=bg9INsXiWfyK047u8TD1oEOFYrqDq8GC7Hvgz69n7BE,1988
|
54
54
|
vellum_ee/workflows/display/nodes/vellum/tests/test_retry_node.py,sha256=NuIw8Yb42KUdoGi3Ur8_7VPg50IC4hNrwAkCociwqNk,2091
|
55
55
|
vellum_ee/workflows/display/nodes/vellum/tests/test_try_node.py,sha256=mtzB8LJlFCHVFM4H5AanLp29gQfaVmnN4A4iaRGJHoI,2427
|
56
56
|
vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py,sha256=4YUaTeD_OWF-UaPMyOTBTu9skGC1jgSHlAYrzbH7Z04,5039
|
57
|
-
vellum_ee/workflows/display/nodes/vellum/try_node.py,sha256=
|
57
|
+
vellum_ee/workflows/display/nodes/vellum/try_node.py,sha256=0ipBZMYm521tuwefQFgHOvTyTgiWVTkzxpQtnnmOAI0,4203
|
58
58
|
vellum_ee/workflows/display/nodes/vellum/utils.py,sha256=F_0BrlSszllK_BhryPbojIleLq2dGXOfQD1rVp3fNFg,4733
|
59
59
|
vellum_ee/workflows/display/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
60
|
-
vellum_ee/workflows/display/tests/test_vellum_workflow_display.py,sha256=
|
60
|
+
vellum_ee/workflows/display/tests/test_vellum_workflow_display.py,sha256=VD-4USiRlCcdC3Qe9WfdkxwFdircai0vqvuZCbELR84,9556
|
61
61
|
vellum_ee/workflows/display/tests/workflow_serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
62
62
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
63
63
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py,sha256=A1-tIpC5KIKG9JA_rkd1nLS8zUG3Kb4QiVdvb3boFxE,2509
|
64
64
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py,sha256=mFmxXfnUPrwndaBurW9E-VSBUQjF3cGv3JpRuNmwWh8,15475
|
65
65
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py,sha256=1cszL6N6FNGVm61MOa7AEiHnF0QjZWqDQuPOp4yiG94,18277
|
66
66
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py,sha256=-12ZkZb3f5gyoNASV2yeQtMo5HmNsVEo8nXwL6IC-I8,6261
|
67
|
-
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py,sha256=
|
67
|
+
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py,sha256=FD94h96w5wWYlLVC8_tqr2aUHPyO2UQh4f03jDX4re4,39783
|
68
68
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py,sha256=EbVgg_3_ipTt3MOop4RARX0fmNjwqZtkhIXzx9nGw7Y,4487
|
69
69
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py,sha256=IRazH2QR6F8RGqNemEnHueyj5DtEa6rFTYhT16S4jI8,15917
|
70
70
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py,sha256=V__y7uu-dy6TJjPeu4UDvaoO2yYwBRbPiW9uJdzWRx4,29828
|
@@ -87,12 +87,12 @@ vellum_ee/workflows/display/tests/workflow_serialization/test_complex_terminal_n
|
|
87
87
|
vellum_ee/workflows/display/types.py,sha256=ixfmcQn51Rhsm4_0hWfG0_WpzLE89ZrDZpeYBklsP1Q,2592
|
88
88
|
vellum_ee/workflows/display/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
89
89
|
vellum_ee/workflows/display/utils/expressions.py,sha256=9FpOslDI-RCR5m4TgAu9KCHh4aTVnh7CHR2ykyMUDw0,1151
|
90
|
-
vellum_ee/workflows/display/utils/vellum.py,sha256=
|
90
|
+
vellum_ee/workflows/display/utils/vellum.py,sha256=EVPQUSsZ3OIeLTEbV6LHPor37t9fnj9kJxDqP4PmTLQ,8234
|
91
91
|
vellum_ee/workflows/display/vellum.py,sha256=7mqQaKZPPrLMcXSAQkPIxCy5x8HkKs5PbCu3GRaC2o8,8507
|
92
92
|
vellum_ee/workflows/display/workflows/__init__.py,sha256=kapXsC67VJcgSuiBMa86FdePG5A9kMB5Pi4Uy1O2ob4,207
|
93
|
-
vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=
|
93
|
+
vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=QKMSmV--UoE8L7pYmKvZqtaatygrxmSm9PDSH71Yr0Y,19937
|
94
94
|
vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=kp0u8LN_2IwshLrhMImhpZx1hRyAcD5gXY-kDuuaGMQ,1269
|
95
|
-
vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=
|
95
|
+
vellum_ee/workflows/display/workflows/tests/test_workflow_display.py,sha256=TzLziIh1fJ429LRUYoe88yd_HZflEQAw60TW8PAvvnk,6917
|
96
96
|
vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=mbAzCpswOek34ITeTkesbVreCXpulj4NFjIg3RcdVZ8,18243
|
97
97
|
vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
98
98
|
vellum_ee/workflows/server/virtual_file_loader.py,sha256=X_DdNK7MfyOjKWekk6YQpOSCT6klKcdjT6nVJcBH1sM,1481
|
@@ -123,12 +123,12 @@ vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
|
|
123
123
|
vellum/client/__init__.py,sha256=tKtdM1_GqmGq1gpi9ydWD_T-MM7fPn8QdHh8ww19cNI,117564
|
124
124
|
vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
|
125
125
|
vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
|
126
|
-
vellum/client/core/client_wrapper.py,sha256=
|
126
|
+
vellum/client/core/client_wrapper.py,sha256=x6cZlx3EA5bHNlGRnX0klOVCrqkvqWv_oUW4xjYEehU,1869
|
127
127
|
vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
|
128
128
|
vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
|
129
129
|
vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
|
130
130
|
vellum/client/core/jsonable_encoder.py,sha256=qaF1gtgH-kQZb4kJskETwcCsOPUof-NnYVdszHkb-dM,3656
|
131
|
-
vellum/client/core/pydantic_utilities.py,sha256=
|
131
|
+
vellum/client/core/pydantic_utilities.py,sha256=6ev3gtER-hjlq7PcPL9XT_YSCdgyCE8ZKHJ9Uc-gHIg,12071
|
132
132
|
vellum/client/core/query_encoder.py,sha256=ekulqNd0j8TgD7ox-Qbz7liqX8-KP9blvT9DsRCenYM,2144
|
133
133
|
vellum/client/core/remove_none_from_dict.py,sha256=EU9SGgYidWq7SexuJbNs4-PZ-5Bl3Vppd864mS6vQZw,342
|
134
134
|
vellum/client/core/request_options.py,sha256=5cCGt5AEGgtP5xifDl4oVQUmSjlIA8FmRItAlJawM18,1417
|
@@ -367,7 +367,7 @@ vellum/client/types/json_input.py,sha256=ZUA2O9YueBCx0IMMdB8uYNSWJiSDZxMm5ogwbwC
|
|
367
367
|
vellum/client/types/json_input_request.py,sha256=x5sA-VXxF4QH-98xRcIKPZhsMVbnJNUQofiUQqyfGk4,768
|
368
368
|
vellum/client/types/json_vellum_value.py,sha256=8irlw6NkRRVafysfTc1Q5BFFhRrWJYzdwrDYTdJK4JY,689
|
369
369
|
vellum/client/types/json_vellum_value_request.py,sha256=IUlkdwFGgBeLl9sCmAJhoaxomWiEMpWgRcLa_WnlK8g,696
|
370
|
-
vellum/client/types/logical_operator.py,sha256=
|
370
|
+
vellum/client/types/logical_operator.py,sha256=_3-gBnfsz4g5-sw5_ThRuS0NdNLQ2n2vV2Dl1RqbXe4,617
|
371
371
|
vellum/client/types/logprobs_enum.py,sha256=D_458cZX2CAb6dX_ovrQ6HARlJkYcZRadKwsi1Cr-JM,151
|
372
372
|
vellum/client/types/map_node_result.py,sha256=e2YqEP-aKig2TbbZIlfn5LCeVEVhEJJBR7o7xTBKXaY,752
|
373
373
|
vellum/client/types/map_node_result_data.py,sha256=3xJXC1JrS9lo3-3_u1S79sYwLxMknNntDyspN24vQzY,699
|
@@ -1380,7 +1380,7 @@ vellum/workflows/nodes/core/map_node/tests/test_node.py,sha256=uMR0AyIFn539LqTKH
|
|
1380
1380
|
vellum/workflows/nodes/core/retry_node/__init__.py,sha256=lN2bIy5a3Uzhs_FYCrooADyYU6ZGShtvLKFWpelwPvo,60
|
1381
1381
|
vellum/workflows/nodes/core/retry_node/node.py,sha256=Vt3fx4G-DRIb9a-IHIUfaAclgfbzOPEQVkcumwhl9HE,4355
|
1382
1382
|
vellum/workflows/nodes/core/retry_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1383
|
-
vellum/workflows/nodes/core/retry_node/tests/test_node.py,sha256=
|
1383
|
+
vellum/workflows/nodes/core/retry_node/tests/test_node.py,sha256=fNgDufkIsrTC-6ftvogqSpWhqqBj9iNESdfK19B1Yx0,5159
|
1384
1384
|
vellum/workflows/nodes/core/templating_node/__init__.py,sha256=GmyuYo81_A1_Bz6id69ozVFS6FKiuDsZTiA3I6MaL2U,70
|
1385
1385
|
vellum/workflows/nodes/core/templating_node/node.py,sha256=-JIqLUv6Xpx_LTVZt7whQ2X2VatgHDdTxjMrz64luEs,3721
|
1386
1386
|
vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=MHofz-BwAgt7EXkab8VIyacYznDEIJ7Er7MJUaxNQQo,9614
|
@@ -1392,10 +1392,10 @@ vellum/workflows/nodes/displayable/__init__.py,sha256=6F_4DlSwvHuilWnIalp8iDjjDX
|
|
1392
1392
|
vellum/workflows/nodes/displayable/api_node/__init__.py,sha256=MoxdQSnidIj1Nf_d-hTxlOxcZXaZnsWFDbE-PkTK24o,56
|
1393
1393
|
vellum/workflows/nodes/displayable/api_node/node.py,sha256=QdpsyGVxo5PcN8nwGZpcpW_YMKHr3_VvmbK1BlrdOFk,2547
|
1394
1394
|
vellum/workflows/nodes/displayable/api_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1395
|
-
vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py,sha256=
|
1395
|
+
vellum/workflows/nodes/displayable/api_node/tests/test_api_node.py,sha256=fiskhfcI4c6CxFlbSWb1JKsuq-98zAeUWOExc848ALw,3130
|
1396
1396
|
vellum/workflows/nodes/displayable/bases/__init__.py,sha256=0mWIx3qUrzllV7jqt7wN03vWGMuI1WrrLZeMLT2Cl2c,304
|
1397
1397
|
vellum/workflows/nodes/displayable/bases/api_node/__init__.py,sha256=1jwx4WC358CLA1jgzl_UD-rZmdMm2v9Mps39ndwCD7U,64
|
1398
|
-
vellum/workflows/nodes/displayable/bases/api_node/node.py,sha256
|
1398
|
+
vellum/workflows/nodes/displayable/bases/api_node/node.py,sha256=Ev_So7D_4Qfvl2_E8veVfxAxWfbJIA2ujyW5istLg5I,4066
|
1399
1399
|
vellum/workflows/nodes/displayable/bases/base_prompt_node/__init__.py,sha256=Org3xTvgp1pA0uUXFfnJr29D3HzCey2lEdYF4zbIUgo,70
|
1400
1400
|
vellum/workflows/nodes/displayable/bases/base_prompt_node/node.py,sha256=nvhoWb8EyRlgtyotYp-wh194n30yQP81UnOH_a8FghY,3140
|
1401
1401
|
vellum/workflows/nodes/displayable/bases/inline_prompt_node/__init__.py,sha256=Hl35IAoepRpE-j4cALaXVJIYTYOF3qszyVbxTj4kS1s,82
|
@@ -1512,8 +1512,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
|
|
1512
1512
|
vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1513
1513
|
vellum/workflows/workflows/tests/test_base_workflow.py,sha256=NRteiICyJvDM5zrtUfq2fZoXcGQVaWC9xmNlLLVW0cU,7979
|
1514
1514
|
vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
|
1515
|
-
vellum_ai-0.14.
|
1516
|
-
vellum_ai-0.14.
|
1517
|
-
vellum_ai-0.14.
|
1518
|
-
vellum_ai-0.14.
|
1519
|
-
vellum_ai-0.14.
|
1515
|
+
vellum_ai-0.14.15.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
|
1516
|
+
vellum_ai-0.14.15.dist-info/METADATA,sha256=8TM16vJdsQpUXVDgasscuRmbLESHxIgmMj8QlKoLUQU,5408
|
1517
|
+
vellum_ai-0.14.15.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
1518
|
+
vellum_ai-0.14.15.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
|
1519
|
+
vellum_ai-0.14.15.dist-info/RECORD,,
|
@@ -4,6 +4,7 @@ from uuid import UUID
|
|
4
4
|
from typing import (
|
5
5
|
TYPE_CHECKING,
|
6
6
|
Any,
|
7
|
+
ClassVar,
|
7
8
|
Dict,
|
8
9
|
ForwardRef,
|
9
10
|
Generic,
|
@@ -59,17 +60,20 @@ _NodeDisplayAttrType = TypeVar("_NodeDisplayAttrType")
|
|
59
60
|
class BaseNodeDisplayMeta(type):
|
60
61
|
def __new__(mcs, name: str, bases: Tuple[Type, ...], dct: Dict[str, Any]) -> Any:
|
61
62
|
cls = super().__new__(mcs, name, bases, dct)
|
63
|
+
return cls.__annotate_node__()
|
64
|
+
|
65
|
+
def __annotate_node__(cls):
|
62
66
|
base_node_display_class = cast(Type["BaseNodeDisplay"], cls)
|
63
67
|
node_class = base_node_display_class.infer_node_class()
|
64
68
|
if not issubclass(node_class, BaseNode):
|
65
69
|
return cls
|
66
70
|
|
67
|
-
display_node_id =
|
71
|
+
display_node_id = getattr(cls, "node_id", None)
|
68
72
|
if isinstance(display_node_id, UUID):
|
69
73
|
# Display classes are able to override the id of the node class it's parameterized by
|
70
74
|
node_class.__id__ = display_node_id
|
71
75
|
|
72
|
-
output_display =
|
76
|
+
output_display = getattr(cls, "output_display", None)
|
73
77
|
if isinstance(output_display, dict):
|
74
78
|
# And the node class' output ids
|
75
79
|
for reference, node_output_display in output_display.items():
|
@@ -86,6 +90,7 @@ class BaseNodeDisplayMeta(type):
|
|
86
90
|
class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
87
91
|
output_display: Dict[OutputReference, NodeOutputDisplay] = {}
|
88
92
|
port_displays: Dict[Port, PortDisplayOverrides] = {}
|
93
|
+
node_input_ids_by_name: ClassVar[Dict[str, UUID]] = {}
|
89
94
|
|
90
95
|
# Used to store the mapping between node types and their display classes
|
91
96
|
_node_display_registry: Dict[Type[NodeType], Type["BaseNodeDisplay"]] = {}
|
@@ -326,6 +331,7 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
|
326
331
|
IsNotNilExpression,
|
327
332
|
IsUndefinedExpression,
|
328
333
|
IsNotUndefinedExpression,
|
334
|
+
ParseJsonExpression,
|
329
335
|
),
|
330
336
|
):
|
331
337
|
lhs = self.serialize_value(display_context, condition._expression)
|
@@ -403,9 +409,6 @@ class BaseNodeDisplay(Generic[NodeType], metaclass=BaseNodeDisplayMeta):
|
|
403
409
|
"node_id": str(node_class_display.node_id),
|
404
410
|
}
|
405
411
|
|
406
|
-
if isinstance(value, ParseJsonExpression):
|
407
|
-
raise ValueError("ParseJsonExpression is not supported in the UI")
|
408
|
-
|
409
412
|
if not isinstance(value, BaseDescriptor):
|
410
413
|
vellum_value = primitive_to_vellum_value(value)
|
411
414
|
return {
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from uuid import UUID
|
2
|
-
from typing import ClassVar, Dict, Optional
|
2
|
+
from typing import ClassVar, Dict, Optional
|
3
3
|
|
4
4
|
from vellum.workflows.nodes.utils import get_unadorned_node
|
5
5
|
from vellum.workflows.ports import Port
|
@@ -17,9 +17,6 @@ class BaseNodeVellumDisplay(BaseNodeDisplay[NodeType]):
|
|
17
17
|
# Used to explicitly set the target handle id for a node
|
18
18
|
target_handle_id: ClassVar[Optional[UUID]] = None
|
19
19
|
|
20
|
-
# Used to explicitly set the node input ids by name for a node
|
21
|
-
node_input_ids_by_name: ClassVar[Dict[str, Union[UUID, str]]] = {}
|
22
|
-
|
23
20
|
def _get_node_display_uuid(self, attribute: str) -> UUID:
|
24
21
|
explicit_value = self._get_explicit_node_display_attr(attribute, UUID)
|
25
22
|
return explicit_value if explicit_value else uuid4_from_hash(f"{self.node_id}|{attribute}")
|
@@ -1,7 +1,10 @@
|
|
1
1
|
import types
|
2
|
-
from
|
2
|
+
from uuid import UUID
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional, Type
|
3
4
|
|
5
|
+
from vellum.workflows.descriptors.base import BaseDescriptor
|
4
6
|
from vellum.workflows.types.generics import NodeType
|
7
|
+
from vellum.workflows.utils.uuids import uuid4_from_hash
|
5
8
|
from vellum_ee.workflows.display.nodes.types import NodeOutputDisplay
|
6
9
|
|
7
10
|
if TYPE_CHECKING:
|
@@ -26,16 +29,39 @@ def get_node_display_class(
|
|
26
29
|
|
27
30
|
# `base_node_display_class` is always a Generic class, so it's safe to index into it
|
28
31
|
NodeDisplayBaseClass = base_node_display_class[node_class] # type: ignore[index]
|
32
|
+
|
33
|
+
def _get_node_input_ids_by_ref(path: str, inst: Any):
|
34
|
+
if isinstance(inst, dict):
|
35
|
+
node_input_ids_by_name: Dict[str, UUID] = {}
|
36
|
+
for key, value in inst.items():
|
37
|
+
node_input_ids_by_name.update(_get_node_input_ids_by_ref(f"{path}.{key}", value))
|
38
|
+
return node_input_ids_by_name
|
39
|
+
|
40
|
+
if isinstance(inst, BaseDescriptor):
|
41
|
+
return {path: uuid4_from_hash(f"{node_class.__id__}|{path}")}
|
42
|
+
|
43
|
+
return {}
|
44
|
+
|
45
|
+
def exec_body(ns: Dict):
|
46
|
+
output_display = {
|
47
|
+
ref: NodeOutputDisplay(id=node_class.__output_ids__[ref.name], name=ref.name)
|
48
|
+
for ref in node_class.Outputs
|
49
|
+
if ref.name in node_class.__output_ids__
|
50
|
+
}
|
51
|
+
if output_display:
|
52
|
+
ns["output_display"] = output_display
|
53
|
+
|
54
|
+
node_input_ids_by_name: Dict[str, UUID] = {}
|
55
|
+
for ref in node_class:
|
56
|
+
node_input_ids_by_name.update(_get_node_input_ids_by_ref(ref.name, ref.instance))
|
57
|
+
|
58
|
+
if node_input_ids_by_name:
|
59
|
+
ns["node_input_ids_by_name"] = node_input_ids_by_name
|
60
|
+
|
29
61
|
NodeDisplayClass = types.new_class(
|
30
62
|
f"{node_class.__name__}Display",
|
31
63
|
bases=(NodeDisplayBaseClass,),
|
64
|
+
exec_body=exec_body,
|
32
65
|
)
|
33
|
-
output_display = {
|
34
|
-
ref: NodeOutputDisplay(id=node_class.__output_ids__[ref.name], name=ref.name)
|
35
|
-
for ref in node_class.Outputs
|
36
|
-
if ref.name in node_class.__output_ids__
|
37
|
-
}
|
38
|
-
if output_display:
|
39
|
-
setattr(NodeDisplayClass, "output_display", output_display)
|
40
66
|
|
41
67
|
return NodeDisplayClass
|
@@ -1,9 +1,14 @@
|
|
1
|
+
import re
|
2
|
+
import types
|
1
3
|
from uuid import UUID
|
2
|
-
from typing import Any, Callable, Generic, Optional, TypeVar, cast
|
4
|
+
from typing import Any, Callable, Dict, Generic, Optional, Type, TypeVar, cast
|
3
5
|
|
6
|
+
from vellum.workflows.nodes.bases.base import BaseNode
|
4
7
|
from vellum.workflows.nodes.bases.base_adornment_node import BaseAdornmentNode
|
5
8
|
from vellum.workflows.nodes.utils import get_wrapped_node
|
6
9
|
from vellum.workflows.types.core import JsonArray, JsonObject
|
10
|
+
from vellum.workflows.types.utils import get_original_base
|
11
|
+
from vellum.workflows.utils.uuids import uuid4_from_hash
|
7
12
|
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
8
13
|
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
9
14
|
from vellum_ee.workflows.display.nodes.get_node_display_class import get_node_display_class
|
@@ -37,3 +42,52 @@ class BaseAdornmentNodeDisplay(BaseNodeVellumDisplay[_BaseAdornmentNodeType], Ge
|
|
37
42
|
serialized_wrapped_node["adornments"] = adornments + [adornment] if adornment else adornments
|
38
43
|
|
39
44
|
return serialized_wrapped_node
|
45
|
+
|
46
|
+
@classmethod
|
47
|
+
def wrap(cls, **kwargs: Any) -> Callable[..., Type[BaseNodeDisplay]]:
|
48
|
+
NodeDisplayType = TypeVar("NodeDisplayType", bound=BaseNodeDisplay)
|
49
|
+
|
50
|
+
def decorator(inner_cls: Type[NodeDisplayType]) -> Type[NodeDisplayType]:
|
51
|
+
node_class = inner_cls.infer_node_class()
|
52
|
+
wrapped_node_class = cast(Type[BaseNode], node_class.__wrapped_node__)
|
53
|
+
|
54
|
+
# `mypy` is wrong here, `cls` is indexable bc it's Generic
|
55
|
+
BaseAdornmentDisplay = cls[node_class] # type: ignore[index]
|
56
|
+
|
57
|
+
def exec_body(ns: Dict):
|
58
|
+
for key, kwarg in kwargs.items():
|
59
|
+
ns[key] = kwarg
|
60
|
+
|
61
|
+
if "node_id" not in kwargs:
|
62
|
+
ns["node_id"] = uuid4_from_hash(node_class.__qualname__)
|
63
|
+
|
64
|
+
AdornmentDisplay = types.new_class(
|
65
|
+
re.sub(r"^Base", "", cls.__name__), bases=(BaseAdornmentDisplay,), exec_body=exec_body
|
66
|
+
)
|
67
|
+
|
68
|
+
setattr(inner_cls, "__adorned_by__", AdornmentDisplay)
|
69
|
+
|
70
|
+
# We must edit the node display class to use __wrapped_node__ everywhere it
|
71
|
+
# references the adorned node class, which is three places:
|
72
|
+
|
73
|
+
# 1. The node display class' parameterized type
|
74
|
+
original_base_node_display = get_original_base(inner_cls)
|
75
|
+
original_base_node_display.__args__ = (wrapped_node_class,)
|
76
|
+
inner_cls._node_display_registry[wrapped_node_class] = inner_cls
|
77
|
+
inner_cls.__annotate_node__()
|
78
|
+
|
79
|
+
# 2. The node display class' output displays
|
80
|
+
old_outputs = list(inner_cls.output_display.keys())
|
81
|
+
for old_output in old_outputs:
|
82
|
+
new_output = getattr(wrapped_node_class.Outputs, old_output.name)
|
83
|
+
inner_cls.output_display[new_output] = inner_cls.output_display.pop(old_output)
|
84
|
+
|
85
|
+
# 3. The node display class' port displays
|
86
|
+
old_ports = list(inner_cls.port_displays.keys())
|
87
|
+
for old_port in old_ports:
|
88
|
+
new_port = getattr(wrapped_node_class.Ports, old_port.name)
|
89
|
+
inner_cls.port_displays[new_port] = inner_cls.port_displays.pop(old_port)
|
90
|
+
|
91
|
+
return inner_cls
|
92
|
+
|
93
|
+
return decorator
|
@@ -1,14 +1,12 @@
|
|
1
1
|
import inspect
|
2
|
-
from typing import Any,
|
2
|
+
from typing import Any, Generic, Tuple, Type, TypeVar, cast
|
3
3
|
|
4
4
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
5
|
-
from vellum.workflows.errors.types import WorkflowErrorCode
|
6
5
|
from vellum.workflows.nodes.bases.base import BaseNode
|
7
6
|
from vellum.workflows.nodes.core.retry_node.node import RetryNode
|
8
7
|
from vellum.workflows.nodes.utils import ADORNMENT_MODULE_NAME
|
9
8
|
from vellum.workflows.references.output import OutputReference
|
10
9
|
from vellum.workflows.types.core import JsonArray, JsonObject
|
11
|
-
from vellum.workflows.types.utils import get_original_base
|
12
10
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
13
11
|
from vellum.workflows.workflows.base import BaseWorkflow
|
14
12
|
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
@@ -79,54 +77,3 @@ class BaseRetryNodeDisplay(BaseAdornmentNodeDisplay[_RetryNodeType], Generic[_Re
|
|
79
77
|
|
80
78
|
inner_output = getattr(inner_node.Outputs, output.name)
|
81
79
|
return node_display.get_node_output_display(inner_output)
|
82
|
-
|
83
|
-
@classmethod
|
84
|
-
def wrap(
|
85
|
-
cls,
|
86
|
-
max_attempts: int,
|
87
|
-
delay: Optional[float] = None,
|
88
|
-
retry_on_error_code: Optional[WorkflowErrorCode] = None,
|
89
|
-
retry_on_condition: Optional[BaseDescriptor] = None,
|
90
|
-
) -> Callable[..., Type["BaseRetryNodeDisplay"]]:
|
91
|
-
_max_attempts = max_attempts
|
92
|
-
_delay = delay
|
93
|
-
_retry_on_error_code = retry_on_error_code
|
94
|
-
_retry_on_condition = retry_on_condition
|
95
|
-
|
96
|
-
NodeDisplayType = TypeVar("NodeDisplayType", bound=BaseNodeDisplay)
|
97
|
-
|
98
|
-
def decorator(inner_cls: Type[NodeDisplayType]) -> Type[NodeDisplayType]:
|
99
|
-
node_class = inner_cls.infer_node_class()
|
100
|
-
wrapped_node_class = cast(Type[BaseNode], node_class.__wrapped_node__)
|
101
|
-
|
102
|
-
class RetryNodeDisplay(BaseRetryNodeDisplay[node_class]): # type: ignore[valid-type]
|
103
|
-
max_attempts = _max_attempts
|
104
|
-
delay = _delay
|
105
|
-
retry_on_error_code = _retry_on_error_code
|
106
|
-
retry_on_condition = _retry_on_condition
|
107
|
-
|
108
|
-
setattr(inner_cls, "__adorned_by__", RetryNodeDisplay)
|
109
|
-
|
110
|
-
# We must edit the node display class to use __wrapped_node__ everywhere it
|
111
|
-
# references the adorned node class, which is three places:
|
112
|
-
|
113
|
-
# 1. The node display class' parameterized type
|
114
|
-
original_base_node_display = get_original_base(inner_cls)
|
115
|
-
original_base_node_display.__args__ = (wrapped_node_class,)
|
116
|
-
inner_cls._node_display_registry[wrapped_node_class] = inner_cls
|
117
|
-
|
118
|
-
# 2. The node display class' output displays
|
119
|
-
old_outputs = list(inner_cls.output_display.keys())
|
120
|
-
for old_output in old_outputs:
|
121
|
-
new_output = getattr(wrapped_node_class.Outputs, old_output.name)
|
122
|
-
inner_cls.output_display[new_output] = inner_cls.output_display.pop(old_output)
|
123
|
-
|
124
|
-
# 3. The node display class' port displays
|
125
|
-
old_ports = list(inner_cls.port_displays.keys())
|
126
|
-
for old_port in old_ports:
|
127
|
-
new_port = getattr(wrapped_node_class.Ports, old_port.name)
|
128
|
-
inner_cls.port_displays[new_port] = inner_cls.port_displays.pop(old_port)
|
129
|
-
|
130
|
-
return inner_cls
|
131
|
-
|
132
|
-
return decorator
|
@@ -23,14 +23,7 @@ class BaseTemplatingNodeDisplay(BaseNodeVellumDisplay[_TemplatingNodeType], Gene
|
|
23
23
|
node = self._node
|
24
24
|
node_id = self.node_id
|
25
25
|
|
26
|
-
template_input_id = self.template_input_id or
|
27
|
-
(
|
28
|
-
UUID(input_id) if isinstance(input_id, str) else input_id
|
29
|
-
for input_name, input_id in self.node_input_ids_by_name.items()
|
30
|
-
if input_name == TEMPLATE_INPUT_NAME
|
31
|
-
),
|
32
|
-
None,
|
33
|
-
)
|
26
|
+
template_input_id = self.template_input_id or self.node_input_ids_by_name.get(TEMPLATE_INPUT_NAME)
|
34
27
|
|
35
28
|
template_node_input = create_node_input(
|
36
29
|
node_id=node_id,
|
@@ -1,6 +1,6 @@
|
|
1
1
|
import inspect
|
2
2
|
from uuid import UUID
|
3
|
-
from typing import Any,
|
3
|
+
from typing import Any, ClassVar, Generic, Optional, Tuple, Type, TypeVar, cast
|
4
4
|
|
5
5
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
6
6
|
from vellum.workflows.nodes.bases.base import BaseNode
|
@@ -8,7 +8,6 @@ from vellum.workflows.nodes.core.try_node.node import TryNode
|
|
8
8
|
from vellum.workflows.nodes.utils import ADORNMENT_MODULE_NAME
|
9
9
|
from vellum.workflows.references.output import OutputReference
|
10
10
|
from vellum.workflows.types.core import JsonArray, JsonObject
|
11
|
-
from vellum.workflows.types.utils import get_original_base
|
12
11
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
13
12
|
from vellum.workflows.workflows.base import BaseWorkflow
|
14
13
|
from vellum_ee.workflows.display.nodes.base_node_display import BaseNodeDisplay
|
@@ -93,43 +92,3 @@ class BaseTryNodeDisplay(BaseAdornmentNodeDisplay[_TryNodeType], Generic[_TryNod
|
|
93
92
|
|
94
93
|
inner_output = getattr(inner_node.Outputs, output.name)
|
95
94
|
return node_display.get_node_output_display(inner_output)
|
96
|
-
|
97
|
-
@classmethod
|
98
|
-
def wrap(cls, error_output_id: Optional[UUID] = None) -> Callable[..., Type["BaseTryNodeDisplay"]]:
|
99
|
-
_error_output_id = error_output_id
|
100
|
-
|
101
|
-
NodeDisplayType = TypeVar("NodeDisplayType", bound=BaseNodeDisplay)
|
102
|
-
|
103
|
-
def decorator(inner_cls: Type[NodeDisplayType]) -> Type[NodeDisplayType]:
|
104
|
-
node_class = inner_cls.infer_node_class()
|
105
|
-
wrapped_node_class = cast(Type[BaseNode], node_class.__wrapped_node__)
|
106
|
-
|
107
|
-
# Mypy gets mad about dynamic parameter types like this, but it's fine
|
108
|
-
class TryNodeDisplay(BaseTryNodeDisplay[node_class]): # type: ignore[valid-type]
|
109
|
-
error_output_id = _error_output_id
|
110
|
-
|
111
|
-
setattr(inner_cls, "__adorned_by__", TryNodeDisplay)
|
112
|
-
|
113
|
-
# We must edit the node display class to use __wrapped_node__ everywhere it
|
114
|
-
# references the adorned node class, which is three places:
|
115
|
-
|
116
|
-
# 1. The node display class' parameterized type
|
117
|
-
original_base_node_display = get_original_base(inner_cls)
|
118
|
-
original_base_node_display.__args__ = (wrapped_node_class,)
|
119
|
-
inner_cls._node_display_registry[wrapped_node_class] = inner_cls
|
120
|
-
|
121
|
-
# 2. The node display class' output displays
|
122
|
-
old_outputs = list(inner_cls.output_display.keys())
|
123
|
-
for old_output in old_outputs:
|
124
|
-
new_output = getattr(wrapped_node_class.Outputs, old_output.name)
|
125
|
-
inner_cls.output_display[new_output] = inner_cls.output_display.pop(old_output)
|
126
|
-
|
127
|
-
# 3. The node display class' port displays
|
128
|
-
old_ports = list(inner_cls.port_displays.keys())
|
129
|
-
for old_port in old_ports:
|
130
|
-
new_port = getattr(wrapped_node_class.Ports, old_port.name)
|
131
|
-
inner_cls.port_displays[new_port] = inner_cls.port_displays.pop(old_port)
|
132
|
-
|
133
|
-
return inner_cls
|
134
|
-
|
135
|
-
return decorator
|
@@ -1,4 +1,3 @@
|
|
1
|
-
import pytest
|
2
1
|
from uuid import UUID
|
3
2
|
from typing import Dict
|
4
3
|
|
@@ -231,11 +230,7 @@ def test_vellum_workflow_display__serialize_with_unused_nodes_and_edges():
|
|
231
230
|
assert edge_found, "Edge between unused nodes NodeB and NodeC not found in serialized output"
|
232
231
|
|
233
232
|
|
234
|
-
def
|
235
|
-
"""
|
236
|
-
Test that verifies ParseJsonExpression is not yet supported in the UI.
|
237
|
-
This test should fail once UI support is added, at which point it should be updated.
|
238
|
-
"""
|
233
|
+
def test_vellum_workflow_display__serialize_with_parse_json_expression():
|
239
234
|
# GIVEN a workflow that uses the parse_json function
|
240
235
|
from vellum.workflows.references.constant import ConstantValueReference
|
241
236
|
|
@@ -249,13 +244,55 @@ def test_parse_json_not_supported_in_ui():
|
|
249
244
|
class Outputs(BaseWorkflow.Outputs):
|
250
245
|
final = JsonNode.Outputs.json_result
|
251
246
|
|
252
|
-
#
|
247
|
+
# AND a display class for this workflow
|
253
248
|
workflow_display = get_workflow_display(
|
254
249
|
base_display_class=VellumWorkflowDisplay,
|
255
250
|
workflow_class=Workflow,
|
256
251
|
)
|
257
252
|
|
258
|
-
|
259
|
-
|
253
|
+
# WHEN we serialize the workflow
|
254
|
+
exec_config = workflow_display.serialize()
|
255
|
+
|
256
|
+
# THEN the serialized workflow contains the parse_json expression
|
257
|
+
raw_data = exec_config["workflow_raw_data"]
|
258
|
+
assert isinstance(raw_data, dict)
|
260
259
|
|
261
|
-
|
260
|
+
nodes = raw_data["nodes"]
|
261
|
+
assert isinstance(nodes, list)
|
262
|
+
|
263
|
+
json_node = None
|
264
|
+
for node in nodes:
|
265
|
+
assert isinstance(node, dict)
|
266
|
+
definition = node.get("definition")
|
267
|
+
if node.get("type") == "GENERIC" and isinstance(definition, dict) and definition.get("name") == "JsonNode":
|
268
|
+
json_node = node
|
269
|
+
break
|
270
|
+
|
271
|
+
assert json_node is not None
|
272
|
+
|
273
|
+
outputs = json_node.get("outputs", [])
|
274
|
+
assert isinstance(outputs, list)
|
275
|
+
|
276
|
+
json_result = None
|
277
|
+
for output in outputs:
|
278
|
+
assert isinstance(output, dict)
|
279
|
+
if output.get("name") == "json_result":
|
280
|
+
json_result = output
|
281
|
+
break
|
282
|
+
|
283
|
+
assert json_result == {
|
284
|
+
"id": "44c7d94c-a76a-4151-9b95-85a31764f18f",
|
285
|
+
"name": "json_result",
|
286
|
+
"type": "JSON",
|
287
|
+
"value": {
|
288
|
+
"type": "UNARY_EXPRESSION",
|
289
|
+
"lhs": {
|
290
|
+
"type": "CONSTANT_VALUE",
|
291
|
+
"value": {
|
292
|
+
"type": "STRING",
|
293
|
+
"value": '{"key": "value"}',
|
294
|
+
},
|
295
|
+
},
|
296
|
+
"operator": "parseJson",
|
297
|
+
},
|
298
|
+
}
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py
CHANGED
@@ -1003,3 +1003,70 @@ def test_serialize_node__or_then_and(serialize_node):
|
|
1003
1003
|
serialized_node,
|
1004
1004
|
ignore_order=True,
|
1005
1005
|
)
|
1006
|
+
|
1007
|
+
|
1008
|
+
class ParseJsonGenericNode(BaseNode):
|
1009
|
+
class Ports(BaseNode.Ports):
|
1010
|
+
if_branch = Port.on_if(Inputs.input.parse_json().equals({"key": "value"}))
|
1011
|
+
|
1012
|
+
|
1013
|
+
def test_serialize_node__parse_json(serialize_node):
|
1014
|
+
input_id = uuid4()
|
1015
|
+
serialized_node = serialize_node(
|
1016
|
+
node_class=ParseJsonGenericNode,
|
1017
|
+
global_workflow_input_displays={Inputs.input: WorkflowInputsDisplay(id=input_id)},
|
1018
|
+
)
|
1019
|
+
|
1020
|
+
assert not DeepDiff(
|
1021
|
+
{
|
1022
|
+
"id": "60edd9b2-9bad-470f-832c-c5f1aa9a253e",
|
1023
|
+
"label": "ParseJsonGenericNode",
|
1024
|
+
"type": "GENERIC",
|
1025
|
+
"display_data": {"position": {"x": 0.0, "y": 0.0}},
|
1026
|
+
"base": {"name": "BaseNode", "module": ["vellum", "workflows", "nodes", "bases", "base"]},
|
1027
|
+
"definition": {
|
1028
|
+
"name": "ParseJsonGenericNode",
|
1029
|
+
"module": [
|
1030
|
+
"vellum_ee",
|
1031
|
+
"workflows",
|
1032
|
+
"display",
|
1033
|
+
"tests",
|
1034
|
+
"workflow_serialization",
|
1035
|
+
"generic_nodes",
|
1036
|
+
"test_ports_serialization",
|
1037
|
+
],
|
1038
|
+
},
|
1039
|
+
"trigger": {"id": "0ccc19cb-174b-440b-8d08-6c84c571fb8f", "merge_behavior": "AWAIT_ATTRIBUTES"},
|
1040
|
+
"ports": [
|
1041
|
+
{
|
1042
|
+
"id": "abe1e6db-14df-4d3c-b059-d17933ab8c02",
|
1043
|
+
"type": "IF",
|
1044
|
+
"name": "if_branch",
|
1045
|
+
"expression": {
|
1046
|
+
"type": "BINARY_EXPRESSION",
|
1047
|
+
"lhs": {
|
1048
|
+
"type": "UNARY_EXPRESSION",
|
1049
|
+
"lhs": {
|
1050
|
+
"type": "WORKFLOW_INPUT",
|
1051
|
+
"input_variable_id": str(input_id),
|
1052
|
+
},
|
1053
|
+
"operator": "parseJson",
|
1054
|
+
},
|
1055
|
+
"operator": "=",
|
1056
|
+
"rhs": {
|
1057
|
+
"type": "CONSTANT_VALUE",
|
1058
|
+
"value": {
|
1059
|
+
"type": "JSON",
|
1060
|
+
"value": {"key": "value"},
|
1061
|
+
},
|
1062
|
+
},
|
1063
|
+
},
|
1064
|
+
}
|
1065
|
+
],
|
1066
|
+
"adornments": None,
|
1067
|
+
"attributes": [],
|
1068
|
+
"outputs": [],
|
1069
|
+
},
|
1070
|
+
serialized_node,
|
1071
|
+
ignore_order=True,
|
1072
|
+
)
|
@@ -28,6 +28,7 @@ from vellum.workflows.expressions.less_than_or_equal_to import LessThanOrEqualTo
|
|
28
28
|
from vellum.workflows.expressions.not_between import NotBetweenExpression
|
29
29
|
from vellum.workflows.expressions.not_in import NotInExpression
|
30
30
|
from vellum.workflows.expressions.or_ import OrExpression
|
31
|
+
from vellum.workflows.expressions.parse_json import ParseJsonExpression
|
31
32
|
from vellum.workflows.nodes.bases.base import BaseNode
|
32
33
|
from vellum.workflows.nodes.displayable.bases.utils import primitive_to_vellum_value
|
33
34
|
from vellum.workflows.references import OutputReference, WorkflowInputReference
|
@@ -163,5 +164,7 @@ def convert_descriptor_to_operator(descriptor: BaseDescriptor) -> LogicalOperato
|
|
163
164
|
return "or"
|
164
165
|
elif isinstance(descriptor, CoalesceExpression):
|
165
166
|
return "coalesce"
|
167
|
+
elif isinstance(descriptor, ParseJsonExpression):
|
168
|
+
return "parseJson"
|
166
169
|
else:
|
167
170
|
raise ValueError(f"Unsupported descriptor type: {descriptor}")
|
@@ -212,8 +212,6 @@ class BaseWorkflowDisplay(
|
|
212
212
|
|
213
213
|
port_displays: Dict[Port, PortDisplay] = {}
|
214
214
|
|
215
|
-
# TODO: We should still serialize nodes that are in the workflow's directory but aren't used in the graph.
|
216
|
-
# https://app.shortcut.com/vellum/story/5394
|
217
215
|
for node in self._workflow.get_nodes():
|
218
216
|
extracted_node_displays = self._extract_node_displays(node)
|
219
217
|
|
@@ -403,11 +401,11 @@ class BaseWorkflowDisplay(
|
|
403
401
|
for input in display_context.workflow_input_displays
|
404
402
|
}
|
405
403
|
node_displays = {
|
406
|
-
|
404
|
+
node.__id__: (node, display_context.node_displays[node]) for node in display_context.node_displays
|
407
405
|
}
|
408
406
|
node_event_displays = {}
|
409
407
|
for node_id in node_displays:
|
410
|
-
current_node_display = node_displays[node_id]
|
408
|
+
node, current_node_display = node_displays[node_id]
|
411
409
|
input_display = {}
|
412
410
|
if isinstance(current_node_display, BaseNodeVellumDisplay):
|
413
411
|
input_display = current_node_display.node_input_ids_by_name
|
@@ -418,7 +416,6 @@ class BaseWorkflowDisplay(
|
|
418
416
|
port_display_meta = {
|
419
417
|
port.name: current_node_display.port_displays[port].id for port in current_node_display.port_displays
|
420
418
|
}
|
421
|
-
node = current_node_display._node
|
422
419
|
subworkflow_display_context: Optional[WorkflowEventDisplayContext] = None
|
423
420
|
if hasattr(node, "subworkflow"):
|
424
421
|
# All nodes that have a subworkflow attribute are currently expected to call them `subworkflow`
|
@@ -432,7 +429,7 @@ class BaseWorkflowDisplay(
|
|
432
429
|
)
|
433
430
|
subworkflow_display_context = subworkflow_display.get_event_display_context()
|
434
431
|
|
435
|
-
node_event_displays[node_id] = NodeEventDisplayContext(
|
432
|
+
node_event_displays[str(node_id)] = NodeEventDisplayContext(
|
436
433
|
input_display=input_display,
|
437
434
|
output_display=output_display,
|
438
435
|
port_display=port_display_meta,
|
@@ -1,9 +1,13 @@
|
|
1
1
|
import pytest
|
2
|
+
from uuid import uuid4
|
2
3
|
|
3
4
|
from vellum.workflows.nodes.bases.base import BaseNode
|
4
5
|
from vellum.workflows.nodes.core.inline_subworkflow_node.node import InlineSubworkflowNode
|
6
|
+
from vellum.workflows.nodes.core.retry_node.node import RetryNode
|
7
|
+
from vellum.workflows.nodes.core.templating_node.node import TemplatingNode
|
5
8
|
from vellum.workflows.workflows.base import BaseWorkflow
|
6
9
|
from vellum_ee.workflows.display.nodes import BaseNodeDisplay
|
10
|
+
from vellum_ee.workflows.display.nodes.vellum.retry_node import BaseRetryNodeDisplay
|
7
11
|
from vellum_ee.workflows.display.vellum import NodeDisplayData, NodeDisplayPosition
|
8
12
|
from vellum_ee.workflows.display.workflows import VellumWorkflowDisplay
|
9
13
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
@@ -139,3 +143,51 @@ def test_get_event_display_context__node_display_to_include_subworkflow_display(
|
|
139
143
|
|
140
144
|
assert node_event_display.subworkflow_display is not None
|
141
145
|
assert str(InnerNode.__id__) in node_event_display.subworkflow_display.node_displays
|
146
|
+
|
147
|
+
|
148
|
+
def test_get_event_display_context__node_display_for_adornment_nodes():
|
149
|
+
# GIVEN a simple workflow with a retry node adornment
|
150
|
+
@RetryNode.wrap(max_attempts=4)
|
151
|
+
class MyNode(BaseNode):
|
152
|
+
pass
|
153
|
+
|
154
|
+
class MyWorkflow(BaseWorkflow):
|
155
|
+
graph = MyNode
|
156
|
+
|
157
|
+
# AND a display class for the node
|
158
|
+
inner_node_id = uuid4()
|
159
|
+
|
160
|
+
@BaseRetryNodeDisplay.wrap()
|
161
|
+
class MyNodeDisplay(BaseNodeDisplay[MyNode]):
|
162
|
+
node_id = inner_node_id
|
163
|
+
|
164
|
+
# WHEN we gather the event display context
|
165
|
+
display_context = VellumWorkflowDisplay(MyWorkflow).get_event_display_context()
|
166
|
+
|
167
|
+
# THEN the subworkflow display should be included
|
168
|
+
assert str(MyNode.__id__) in display_context.node_displays
|
169
|
+
node_event_display = display_context.node_displays[str(MyNode.__id__)]
|
170
|
+
assert node_event_display.subworkflow_display is not None
|
171
|
+
assert str(inner_node_id) in node_event_display.subworkflow_display.node_displays
|
172
|
+
|
173
|
+
|
174
|
+
def test_get_event_display_context__templating_node_input_display():
|
175
|
+
# GIVEN a simple workflow with a templating node referencing another node output
|
176
|
+
class DataNode(BaseNode):
|
177
|
+
class Outputs:
|
178
|
+
bar: str
|
179
|
+
|
180
|
+
class MyNode(TemplatingNode):
|
181
|
+
inputs = {"foo": DataNode.Outputs.bar}
|
182
|
+
|
183
|
+
class MyWorkflow(BaseWorkflow):
|
184
|
+
graph = DataNode >> MyNode
|
185
|
+
|
186
|
+
# WHEN we gather the event display context
|
187
|
+
display_context = VellumWorkflowDisplay(MyWorkflow).get_event_display_context()
|
188
|
+
|
189
|
+
# THEN the subworkflow display should be included
|
190
|
+
assert str(MyNode.__id__) in display_context.node_displays
|
191
|
+
node_event_display = display_context.node_displays[str(MyNode.__id__)]
|
192
|
+
|
193
|
+
assert node_event_display.input_display.keys() == {"inputs.foo"}
|
File without changes
|
File without changes
|
File without changes
|