vellum-ai 0.13.1__py3-none-any.whl → 0.13.3__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/workflows/nodes/core/templating_node/node.py +27 -1
- vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py +30 -0
- {vellum_ai-0.13.1.dist-info → vellum_ai-0.13.3.dist-info}/METADATA +2 -2
- {vellum_ai-0.13.1.dist-info → vellum_ai-0.13.3.dist-info}/RECORD +20 -19
- vellum_cli/__init__.py +14 -0
- vellum_cli/push.py +62 -12
- vellum_cli/tests/test_push.py +76 -0
- vellum_ee/workflows/display/nodes/vellum/base_node.py +37 -2
- vellum_ee/workflows/display/tests/test_vellum_workflow_display.py +48 -0
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +238 -18
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py +177 -0
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py +30 -38
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py +7 -8
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +35 -2
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +29 -2
- vellum_ee/workflows/display/workflows/vellum_workflow_display.py +3 -1
- {vellum_ai-0.13.1.dist-info → vellum_ai-0.13.3.dist-info}/LICENSE +0 -0
- {vellum_ai-0.13.1.dist-info → vellum_ai-0.13.3.dist-info}/WHEEL +0 -0
- {vellum_ai-0.13.1.dist-info → vellum_ai-0.13.3.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.13.
|
21
|
+
"X-Fern-SDK-Version": "0.13.3",
|
22
22
|
}
|
23
23
|
headers["X_API_KEY"] = self.api_key
|
24
24
|
return headers
|
@@ -1,5 +1,7 @@
|
|
1
1
|
import json
|
2
|
-
from typing import Any, Callable, ClassVar, Dict, Generic, Mapping, Tuple, Type, TypeVar, Union, get_args
|
2
|
+
from typing import Any, Callable, ClassVar, Dict, Generic, Mapping, Tuple, Type, TypeVar, Union, get_args, get_origin
|
3
|
+
|
4
|
+
from pydantic import BaseModel
|
3
5
|
|
4
6
|
from vellum.utils.templating.constants import DEFAULT_JINJA_CUSTOM_FILTERS, DEFAULT_JINJA_GLOBALS
|
5
7
|
from vellum.utils.templating.exceptions import JinjaTemplateError
|
@@ -71,6 +73,7 @@ class TemplatingNode(BaseNode[StateType], Generic[StateType, _OutputType], metac
|
|
71
73
|
original_base = get_original_base(self.__class__)
|
72
74
|
all_args = get_args(original_base)
|
73
75
|
|
76
|
+
output_type: Any
|
74
77
|
if len(all_args) < 2 or isinstance(all_args[1], TypeVar):
|
75
78
|
output_type = str
|
76
79
|
else:
|
@@ -88,12 +91,35 @@ class TemplatingNode(BaseNode[StateType], Generic[StateType, _OutputType], metac
|
|
88
91
|
if output_type is bool:
|
89
92
|
return bool(rendered_template)
|
90
93
|
|
94
|
+
if get_origin(output_type) is list:
|
95
|
+
try:
|
96
|
+
data = json.loads(rendered_template)
|
97
|
+
except json.JSONDecodeError:
|
98
|
+
raise ValueError("Invalid JSON Array format for rendered_template")
|
99
|
+
|
100
|
+
if not isinstance(data, list):
|
101
|
+
raise ValueError(f"Expected a list of items for rendered_template, received {data.__class__.__name__}")
|
102
|
+
|
103
|
+
inner_type = get_args(output_type)[0]
|
104
|
+
if issubclass(inner_type, BaseModel):
|
105
|
+
return [inner_type.model_validate(item) for item in data]
|
106
|
+
else:
|
107
|
+
return data
|
108
|
+
|
91
109
|
if output_type is Json:
|
92
110
|
try:
|
93
111
|
return json.loads(rendered_template)
|
94
112
|
except json.JSONDecodeError:
|
95
113
|
raise ValueError("Invalid JSON format for rendered_template")
|
96
114
|
|
115
|
+
if issubclass(output_type, BaseModel):
|
116
|
+
try:
|
117
|
+
data = json.loads(rendered_template)
|
118
|
+
except json.JSONDecodeError:
|
119
|
+
raise ValueError("Invalid JSON format for rendered_template")
|
120
|
+
|
121
|
+
return output_type.model_validate(data)
|
122
|
+
|
97
123
|
raise ValueError(f"Unsupported output type: {output_type}")
|
98
124
|
|
99
125
|
def run(self) -> Outputs:
|
@@ -1,5 +1,7 @@
|
|
1
1
|
import json
|
2
|
+
from typing import List
|
2
3
|
|
4
|
+
from vellum.client.types.chat_message import ChatMessage
|
3
5
|
from vellum.client.types.function_call import FunctionCall
|
4
6
|
from vellum.workflows.nodes.bases.base import BaseNode
|
5
7
|
from vellum.workflows.nodes.core.templating_node.node import TemplatingNode
|
@@ -125,3 +127,31 @@ def test_templating_node__pydantic_to_json():
|
|
125
127
|
|
126
128
|
# THEN the output is the expected JSON
|
127
129
|
assert outputs.result == {"name": "test", "arguments": {"key": "value"}, "id": None}
|
130
|
+
|
131
|
+
|
132
|
+
def test_templating_node__chat_history_output():
|
133
|
+
# GIVEN a templating node that outputs a chat history
|
134
|
+
class ChatHistoryTemplateNode(TemplatingNode[BaseState, List[ChatMessage]]):
|
135
|
+
template = '[{"role": "USER", "text": "Hello"}]'
|
136
|
+
inputs = {}
|
137
|
+
|
138
|
+
# WHEN the node is run
|
139
|
+
node = ChatHistoryTemplateNode()
|
140
|
+
outputs = node.run()
|
141
|
+
|
142
|
+
# THEN the output is the expected chat history
|
143
|
+
assert outputs.result == [ChatMessage(role="USER", text="Hello")]
|
144
|
+
|
145
|
+
|
146
|
+
def test_templating_node__function_call_output():
|
147
|
+
# GIVEN a templating node that outputs a function call
|
148
|
+
class FunctionCallTemplateNode(TemplatingNode[BaseState, FunctionCall]):
|
149
|
+
template = '{"name": "test", "arguments": {"key": "value"}}'
|
150
|
+
inputs = {}
|
151
|
+
|
152
|
+
# WHEN the node is run
|
153
|
+
node = FunctionCallTemplateNode()
|
154
|
+
outputs = node.run()
|
155
|
+
|
156
|
+
# THEN the output is the expected function call
|
157
|
+
assert outputs.result == FunctionCall(name="test", arguments={"key": "value"})
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: vellum-ai
|
3
|
-
Version: 0.13.
|
3
|
+
Version: 0.13.3
|
4
4
|
Summary:
|
5
5
|
License: MIT
|
6
6
|
Requires-Python: >=3.9,<4.0
|
@@ -20,7 +20,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.8
|
21
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
22
22
|
Classifier: Typing :: Typed
|
23
|
-
Requires-Dist: Jinja2 (
|
23
|
+
Requires-Dist: Jinja2 (>=3.1.2,<4.0.0)
|
24
24
|
Requires-Dist: cdktf (>=0.20.5,<0.21.0)
|
25
25
|
Requires-Dist: click (==8.1.7)
|
26
26
|
Requires-Dist: docker (==7.1.0)
|
@@ -1,20 +1,20 @@
|
|
1
1
|
vellum_cli/CONTRIBUTING.md,sha256=FtDC7BGxSeMnwCXAUssFsAIElXtmJE-O5Z7BpolcgvI,2935
|
2
2
|
vellum_cli/README.md,sha256=2NudRoLzWxNKqnuVy1JuQ7DerIaxWGYkrH8kMd-asIE,90
|
3
|
-
vellum_cli/__init__.py,sha256=
|
3
|
+
vellum_cli/__init__.py,sha256=L8D7nVwC63WdAe6HTy7J_S3wCvjJBHDWaBkCrw76dtU,9573
|
4
4
|
vellum_cli/aliased_group.py,sha256=ugW498j0yv4ALJ8vS9MsO7ctDW7Jlir9j6nE_uHAP8c,3363
|
5
5
|
vellum_cli/config.py,sha256=998IZbvkrw2avjbvs8bw6NrbEgGz5UBKRbvKAcastJg,5493
|
6
6
|
vellum_cli/image_push.py,sha256=SJwhwWJsLjwGNezNVd_oCVpFMfPsAB3dfLWmriZZUtw,4419
|
7
7
|
vellum_cli/logger.py,sha256=PuRFa0WCh4sAGFS5aqWB0QIYpS6nBWwPJrIXpWxugV4,1022
|
8
8
|
vellum_cli/ping.py,sha256=lWyJw6sziXjyTopTYRdFF5hV-sYPVDdX0yVbG5fzcY4,585
|
9
9
|
vellum_cli/pull.py,sha256=zf0y22XptUYI_hMP_4Q1CEo9s2wALsTJcCXNd-_ibd8,7551
|
10
|
-
vellum_cli/push.py,sha256=
|
10
|
+
vellum_cli/push.py,sha256=KHakWiUwdeZff8QZSDF0l8xmCgMRo9ntan8kaLD02Lc,7677
|
11
11
|
vellum_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
12
|
vellum_cli/tests/conftest.py,sha256=Jv-QUjXoCDhsvVvXEjOenNqRArO_xXhtNuCYt4wiOyE,1421
|
13
13
|
vellum_cli/tests/test_config.py,sha256=uvKGDc8BoVyT9_H0Z-g8469zVxomn6Oi3Zj-vK7O_wU,2631
|
14
14
|
vellum_cli/tests/test_main.py,sha256=qDZG-aQauPwBwM6A2DIu1494n47v3pL28XakTbLGZ-k,272
|
15
15
|
vellum_cli/tests/test_ping.py,sha256=QtbhYKMYn1DFnDyBij2mkQO32j9KOpZ5Pf0yek7k_Ao,1284
|
16
16
|
vellum_cli/tests/test_pull.py,sha256=6gbASF6ASy5YcdWjOCt6b5K0u2VWsFegdrTWu6sEVKs,19613
|
17
|
-
vellum_cli/tests/test_push.py,sha256=
|
17
|
+
vellum_cli/tests/test_push.py,sha256=PVxKwbWHjb1QwQ0n4tTqh2Tj6yg2cOGupOSXaXl08DI,11044
|
18
18
|
vellum_ee/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
19
19
|
vellum_ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
20
20
|
vellum_ee/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -30,7 +30,7 @@ vellum_ee/workflows/display/nodes/types.py,sha256=St1BB6no528OyELGiyRabWao0GGw6m
|
|
30
30
|
vellum_ee/workflows/display/nodes/utils.py,sha256=sloya5TpXsnot1HURc9L51INwflRqUzHxRVnCS9Cd-4,973
|
31
31
|
vellum_ee/workflows/display/nodes/vellum/__init__.py,sha256=_raKY9eKi_OvIFn6nGvf9xKSboKtYLHCWaWCwDQFbOc,1567
|
32
32
|
vellum_ee/workflows/display/nodes/vellum/api_node.py,sha256=hoV-cUtS6H9kmRQXHd2py95GRWI_dAnnaPwvlNBkDOQ,8571
|
33
|
-
vellum_ee/workflows/display/nodes/vellum/base_node.py,sha256=
|
33
|
+
vellum_ee/workflows/display/nodes/vellum/base_node.py,sha256=lbk6pWO2xRzjQmlh-3iWrc-6Hfa7cUycqCwLuVNFuW8,7623
|
34
34
|
vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=z00Z3L0d4PsUQo4S8FRDTtOFLtjdi17TJbatNVF4nM8,4288
|
35
35
|
vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=UJtdeppJFrrgJi48soO1-r5eaKTOExjYCrEx_YCsvtU,10486
|
36
36
|
vellum_ee/workflows/display/nodes/vellum/error_node.py,sha256=Tyx74dUmj_ef0slptoUXHtkjLbNd3f4hXeoEozFaFcw,2023
|
@@ -50,22 +50,23 @@ vellum_ee/workflows/display/nodes/vellum/tests/test_utils.py,sha256=Nqd6mxn0sgL4
|
|
50
50
|
vellum_ee/workflows/display/nodes/vellum/try_node.py,sha256=hB8dcGMGkuC95kk9hmZUgHsCLwEA37fHTFXj0JzbRjM,4692
|
51
51
|
vellum_ee/workflows/display/nodes/vellum/utils.py,sha256=nIZ1DYTYPRaYsVKcel9a-Fm8EniJL0N7f5PowxVGTVU,8318
|
52
52
|
vellum_ee/workflows/display/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
53
|
-
vellum_ee/workflows/display/tests/test_vellum_workflow_display.py,sha256=
|
53
|
+
vellum_ee/workflows/display/tests/test_vellum_workflow_display.py,sha256=4jXe7Wyspw6CxghVqKAOu-_QunKFvY4U6--zOiuXvV0,3069
|
54
54
|
vellum_ee/workflows/display/tests/workflow_serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
55
55
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
56
56
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py,sha256=RatRmgd1O7OX1r2QfMLPs-WvGQpPLfXIjWNGE4s0yLE,2186
|
57
57
|
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py,sha256=MtKRKcPJsUJ3le7PLz9H6iH3vmRBZDRy6c-4LUF76zE,1987
|
58
|
-
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py,sha256=
|
59
|
-
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/
|
60
|
-
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/
|
58
|
+
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py,sha256=apGjJgH2KrUVA5LuD9b2etjVFFG1cqYTmNATfdkngWA,10193
|
59
|
+
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py,sha256=nouAROR-L9_u6BfPbKUl20QjbbRpwPeElGS2wIWozLc,6239
|
60
|
+
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py,sha256=wZekdhIZBwbBoFNAdC9bBLwUxVKk3EnFdNmMwYIdKGA,37308
|
61
|
+
vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py,sha256=os6wCZjcOyig8EJu-pTAFWXa0odMxTaR0mrbL0SS_4Y,4480
|
61
62
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py,sha256=bXZWxOKAVjZlbP3iLHPHGA4aPs0EguKlQqmYPNGv3cU,16391
|
62
63
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py,sha256=UPLEQPwsLzOqZdkXrB5Jo1FdCx0iQNf7ekfcq1byoFw,29392
|
63
64
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py,sha256=Xn8t-SpvQocKdxNofDeDETlFnkCbBxdLGiUG8m6fM6U,48399
|
64
65
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py,sha256=QVlkczxzaKuOhwbRvVP70otPDNnJcSGDfN79j92lFyk,5534
|
65
66
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py,sha256=_gv7vPxBWSOSRKMlXYv8GKj9h1JAXjXIVWkCE_XhkYE,5746
|
66
67
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_guardrail_node_serialization.py,sha256=mEkvKUrt8U6e9bN65QRG7Zd3KdCdoMvHm96LjGwy96k,7427
|
67
|
-
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py,sha256
|
68
|
-
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py,sha256=
|
68
|
+
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py,sha256=-e6svaclv068n66oLnha-CFzW4ihNnhyQuqAfUyI59k,21395
|
69
|
+
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py,sha256=luj_PdJd8e13C4JO7dkbTlNPko6N7cPFM1iAljdElW8,16043
|
69
70
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_merge_node_serialization.py,sha256=mGk21Wp9Gyr-rRwYqhLEyenJF-ArdXjAdj_qYqcldrE,8422
|
70
71
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_prompt_deployment_serialization.py,sha256=W_d-hsjSqbqR5BA3aF3KFoEyfLV6x_yhNKmLA1ai2QY,8622
|
71
72
|
vellum_ee/workflows/display/tests/workflow_serialization/test_basic_search_node_serialization.py,sha256=PKJAYaEnVYZATJibqXEDysDoTtjBL2CK__WE2YyOTN4,12817
|
@@ -81,7 +82,7 @@ vellum_ee/workflows/display/vellum.py,sha256=8xXRI8b8Tt661H-iZreTQTvLNEKUr4lf-Xa
|
|
81
82
|
vellum_ee/workflows/display/workflows/__init__.py,sha256=kapXsC67VJcgSuiBMa86FdePG5A9kMB5Pi4Uy1O2ob4,207
|
82
83
|
vellum_ee/workflows/display/workflows/base_workflow_display.py,sha256=u5u_KnDp_ktLBlfkT5wiGHIloodSlUT3D3_DQXijtMA,13735
|
83
84
|
vellum_ee/workflows/display/workflows/get_vellum_workflow_display_class.py,sha256=kp0u8LN_2IwshLrhMImhpZx1hRyAcD5gXY-kDuuaGMQ,1269
|
84
|
-
vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=
|
85
|
+
vellum_ee/workflows/display/workflows/vellum_workflow_display.py,sha256=V0edhtohqAWbaHvHkj9Sth4ieaIVejsrsRIr7aCCoVc,16871
|
85
86
|
vellum_ee/workflows/server/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
86
87
|
vellum_ee/workflows/server/virtual_file_loader.py,sha256=X_DdNK7MfyOjKWekk6YQpOSCT6klKcdjT6nVJcBH1sM,1481
|
87
88
|
vellum/__init__.py,sha256=iwoL3PQsiTvtX79J4qlAJ2EIqZ77zYJm3q7o1Ei3Awo,35398
|
@@ -89,7 +90,7 @@ vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
|
|
89
90
|
vellum/client/__init__.py,sha256=8nZt88C9SVwWanjLbIQMU3rzb32h5UZfFMBx3VPHB50,111887
|
90
91
|
vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
|
91
92
|
vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
|
92
|
-
vellum/client/core/client_wrapper.py,sha256=
|
93
|
+
vellum/client/core/client_wrapper.py,sha256=BXgs_0BJXe25LvUtKqWjshTlgNC0aIIpzHAD8cSh1p0,1868
|
93
94
|
vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
|
94
95
|
vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
|
95
96
|
vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
|
@@ -1303,8 +1304,8 @@ vellum/workflows/nodes/core/retry_node/node.py,sha256=QEpxhKOyxDkRoAn2b0PToZWtAG
|
|
1303
1304
|
vellum/workflows/nodes/core/retry_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1304
1305
|
vellum/workflows/nodes/core/retry_node/tests/test_node.py,sha256=RM_OHwxrHwyxvlQQBJPqVBxpedFuWQ9h2-Xa3kP75sc,4399
|
1305
1306
|
vellum/workflows/nodes/core/templating_node/__init__.py,sha256=GmyuYo81_A1_Bz6id69ozVFS6FKiuDsZTiA3I6MaL2U,70
|
1306
|
-
vellum/workflows/nodes/core/templating_node/node.py,sha256=
|
1307
|
-
vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=
|
1307
|
+
vellum/workflows/nodes/core/templating_node/node.py,sha256=GxsVS1FoV84kOtluu00V8H74MEnYvEf55p8mt4ub-5w,5188
|
1308
|
+
vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=5iZWQWdJKDHMXBY8bhpb-Dpy9FTfW1HXxGUTivykZAA,4621
|
1308
1309
|
vellum/workflows/nodes/core/try_node/__init__.py,sha256=JVD4DrldTIqFQQFrubs9KtWCCc0YCAc7Fzol5ZWIWeM,56
|
1309
1310
|
vellum/workflows/nodes/core/try_node/node.py,sha256=_lTmSYCiz7lktaxpNWUCglNi8_5Sfy8Rpiov5SeKVMw,3920
|
1310
1311
|
vellum/workflows/nodes/core/try_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -1418,8 +1419,8 @@ vellum/workflows/vellum_client.py,sha256=ODrq_TSl-drX2aezXegf7pizpWDVJuTXH-j6528
|
|
1418
1419
|
vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
|
1419
1420
|
vellum/workflows/workflows/base.py,sha256=k0kUWWko4fHyCqLSU_1cBK_pXZpl9MXekWiG-bdOAo0,18353
|
1420
1421
|
vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
|
1421
|
-
vellum_ai-0.13.
|
1422
|
-
vellum_ai-0.13.
|
1423
|
-
vellum_ai-0.13.
|
1424
|
-
vellum_ai-0.13.
|
1425
|
-
vellum_ai-0.13.
|
1422
|
+
vellum_ai-0.13.3.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
|
1423
|
+
vellum_ai-0.13.3.dist-info/METADATA,sha256=fhSWP8J92-m3lugLxpvbMyiYC7pNO634YrQrXDC_BoA,5334
|
1424
|
+
vellum_ai-0.13.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
1425
|
+
vellum_ai-0.13.3.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
|
1426
|
+
vellum_ai-0.13.3.dist-info/RECORD,,
|
vellum_cli/__init__.py
CHANGED
@@ -64,6 +64,11 @@ def workflows():
|
|
64
64
|
is_flag=True,
|
65
65
|
help="Check the Workflow for errors and expected changes, without updating its state in Vellum.",
|
66
66
|
)
|
67
|
+
@click.option(
|
68
|
+
"--strict",
|
69
|
+
is_flag=True,
|
70
|
+
help="Raises an error if we detect an unexpected discrepancy in the generated artifact.",
|
71
|
+
)
|
67
72
|
def workflows_push(
|
68
73
|
module: Optional[str],
|
69
74
|
deploy: Optional[bool],
|
@@ -72,6 +77,7 @@ def workflows_push(
|
|
72
77
|
deployment_description: Optional[str],
|
73
78
|
release_tag: Optional[List[str]],
|
74
79
|
dry_run: Optional[bool],
|
80
|
+
strict: Optional[bool],
|
75
81
|
) -> None:
|
76
82
|
"""
|
77
83
|
Push Workflows to Vellum. If a module is provided, only the Workflow for that module will be pushed.
|
@@ -86,6 +92,7 @@ def workflows_push(
|
|
86
92
|
deployment_description=deployment_description,
|
87
93
|
release_tags=release_tag,
|
88
94
|
dry_run=dry_run,
|
95
|
+
strict=strict,
|
89
96
|
)
|
90
97
|
|
91
98
|
|
@@ -101,6 +108,11 @@ def workflows_push(
|
|
101
108
|
is_flag=True,
|
102
109
|
help="Check the Workflow for errors and expected changes, without updating its state in Vellum.",
|
103
110
|
)
|
111
|
+
@click.option(
|
112
|
+
"--strict",
|
113
|
+
is_flag=True,
|
114
|
+
help="Raises an error if we detect an unexpected discrepancy in the generated artifact.",
|
115
|
+
)
|
104
116
|
def push_module(
|
105
117
|
ctx: click.Context,
|
106
118
|
deploy: Optional[bool],
|
@@ -109,6 +121,7 @@ def push_module(
|
|
109
121
|
deployment_description: Optional[str],
|
110
122
|
release_tag: Optional[List[str]],
|
111
123
|
dry_run: Optional[bool],
|
124
|
+
strict: Optional[bool],
|
112
125
|
) -> None:
|
113
126
|
"""Push a specific module to Vellum"""
|
114
127
|
|
@@ -121,6 +134,7 @@ def push_module(
|
|
121
134
|
deployment_description=deployment_description,
|
122
135
|
release_tags=release_tag,
|
123
136
|
dry_run=dry_run,
|
137
|
+
strict=strict,
|
124
138
|
)
|
125
139
|
|
126
140
|
|
vellum_cli/push.py
CHANGED
@@ -9,6 +9,7 @@ from typing import List, Optional
|
|
9
9
|
|
10
10
|
from dotenv import load_dotenv
|
11
11
|
|
12
|
+
from vellum.client.core.api_error import ApiError
|
12
13
|
from vellum.resources.workflows.client import OMIT
|
13
14
|
from vellum.types import WorkflowPushDeploymentConfigRequest
|
14
15
|
from vellum.workflows.utils.names import snake_to_title_case
|
@@ -28,6 +29,7 @@ def push_command(
|
|
28
29
|
deployment_description: Optional[str] = None,
|
29
30
|
release_tags: Optional[List[str]] = None,
|
30
31
|
dry_run: Optional[bool] = None,
|
32
|
+
strict: Optional[bool] = None,
|
31
33
|
) -> None:
|
32
34
|
load_dotenv()
|
33
35
|
logger = load_cli_logger()
|
@@ -109,18 +111,66 @@ def push_command(
|
|
109
111
|
artifact.seek(0)
|
110
112
|
artifact.name = f"{workflow_config.module.replace('.', '__')}.tar.gz"
|
111
113
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
114
|
+
try:
|
115
|
+
response = client.workflows.push(
|
116
|
+
# Remove this once we could serialize using the artifact in Vembda
|
117
|
+
# https://app.shortcut.com/vellum/story/5585
|
118
|
+
exec_config=json.dumps(exec_config),
|
119
|
+
label=label,
|
120
|
+
workflow_sandbox_id=workflow_config.workflow_sandbox_id,
|
121
|
+
artifact=artifact,
|
122
|
+
# We should check with fern if we could auto-serialize typed object fields for us
|
123
|
+
# https://app.shortcut.com/vellum/story/5568
|
124
|
+
deployment_config=deployment_config_serialized, # type: ignore[arg-type]
|
125
|
+
dry_run=dry_run,
|
126
|
+
strict=strict,
|
127
|
+
)
|
128
|
+
except ApiError as e:
|
129
|
+
if e.status_code != 400 or not isinstance(e.body, dict) or "diffs" not in e.body:
|
130
|
+
raise e
|
131
|
+
|
132
|
+
diffs: dict = e.body["diffs"]
|
133
|
+
generated_only = diffs.get("generated_only", [])
|
134
|
+
generated_only_str = (
|
135
|
+
"\n".join(
|
136
|
+
["Files that were generated but not found in the original project:"]
|
137
|
+
+ [f"- {file}" for file in generated_only]
|
138
|
+
)
|
139
|
+
if generated_only
|
140
|
+
else ""
|
141
|
+
)
|
142
|
+
|
143
|
+
original_only = diffs.get("original_only", [])
|
144
|
+
original_only_str = (
|
145
|
+
"\n".join(
|
146
|
+
["Files that were found in the original project but not generated:"]
|
147
|
+
+ [f"- {file}" for file in original_only]
|
148
|
+
)
|
149
|
+
if original_only
|
150
|
+
else ""
|
151
|
+
)
|
152
|
+
|
153
|
+
modified = diffs.get("modified", {})
|
154
|
+
modified_str = (
|
155
|
+
"\n\n".join(
|
156
|
+
["Files that were different between the original project and the generated artifact:"]
|
157
|
+
+ ["\n".join(line.strip() for line in lines) for lines in modified.values()]
|
158
|
+
)
|
159
|
+
if modified
|
160
|
+
else ""
|
161
|
+
)
|
162
|
+
|
163
|
+
reported_diffs = f"""\
|
164
|
+
{e.body.get("detail")}
|
165
|
+
|
166
|
+
{generated_only_str}
|
167
|
+
|
168
|
+
{original_only_str}
|
169
|
+
|
170
|
+
{modified_str}
|
171
|
+
"""
|
172
|
+
logger.error(reported_diffs)
|
173
|
+
return
|
124
174
|
|
125
175
|
if dry_run:
|
126
176
|
error_messages = [str(e) for e in workflow_display.errors]
|
vellum_cli/tests/test_push.py
CHANGED
@@ -7,6 +7,7 @@ from uuid import uuid4
|
|
7
7
|
|
8
8
|
from click.testing import CliRunner
|
9
9
|
|
10
|
+
from vellum.client.core.api_error import ApiError
|
10
11
|
from vellum.client.types.workflow_push_response import WorkflowPushResponse
|
11
12
|
from vellum.utils.uuid import is_valid_uuid
|
12
13
|
from vellum_cli import main as cli_main
|
@@ -242,3 +243,78 @@ class ExampleWorkflow(BaseWorkflow):
|
|
242
243
|
assert "Serialization is not supported." in result.output
|
243
244
|
assert "## Proposed Diffs" in result.output
|
244
245
|
assert "iterable_item_added" in result.output
|
246
|
+
|
247
|
+
|
248
|
+
def test_push__strict_option_returns_diffs(mock_module, vellum_client):
|
249
|
+
# GIVEN a single workflow configured
|
250
|
+
temp_dir = mock_module.temp_dir
|
251
|
+
module = mock_module.module
|
252
|
+
|
253
|
+
# AND a workflow exists in the module successfully
|
254
|
+
base_dir = os.path.join(temp_dir, *module.split("."))
|
255
|
+
os.makedirs(base_dir, exist_ok=True)
|
256
|
+
workflow_py_file_content = """\
|
257
|
+
from vellum.workflows import BaseWorkflow
|
258
|
+
|
259
|
+
class ExampleWorkflow(BaseWorkflow):
|
260
|
+
pass
|
261
|
+
"""
|
262
|
+
with open(os.path.join(temp_dir, *module.split("."), "workflow.py"), "w") as f:
|
263
|
+
f.write(workflow_py_file_content)
|
264
|
+
|
265
|
+
# AND the push API call returns a 4xx response with diffs
|
266
|
+
vellum_client.workflows.push.side_effect = ApiError(
|
267
|
+
status_code=400,
|
268
|
+
body={
|
269
|
+
"detail": "Failed to push workflow due to unexpected detected differences in the generated artifact.",
|
270
|
+
"diffs": {
|
271
|
+
"generated_only": ["state.py"],
|
272
|
+
"modified": {
|
273
|
+
"workflow.py": [
|
274
|
+
"--- a/workflow.py\n",
|
275
|
+
"+++ b/workflow.py\n",
|
276
|
+
"@@ -1 +1 @@\n",
|
277
|
+
"-print('hello')",
|
278
|
+
"+print('foo')",
|
279
|
+
]
|
280
|
+
},
|
281
|
+
"original_only": ["inputs.py"],
|
282
|
+
},
|
283
|
+
},
|
284
|
+
)
|
285
|
+
|
286
|
+
# WHEN calling `vellum push` on strict mode
|
287
|
+
runner = CliRunner()
|
288
|
+
result = runner.invoke(cli_main, ["push", module, "--strict"])
|
289
|
+
|
290
|
+
# THEN it should succeed
|
291
|
+
assert result.exit_code == 0
|
292
|
+
|
293
|
+
# AND we should have called the push API with the strict option
|
294
|
+
vellum_client.workflows.push.assert_called_once()
|
295
|
+
call_args = vellum_client.workflows.push.call_args.kwargs
|
296
|
+
assert call_args["strict"] is True
|
297
|
+
|
298
|
+
# AND the report should be in the output
|
299
|
+
assert (
|
300
|
+
result.output
|
301
|
+
== """\
|
302
|
+
\x1b[38;20mLoading workflow from examples.mock.test_push__strict_option_returns_diffs\x1b[0m
|
303
|
+
\x1b[31;20mFailed to push workflow due to unexpected detected differences in the generated artifact.
|
304
|
+
|
305
|
+
Files that were generated but not found in the original project:
|
306
|
+
- state.py
|
307
|
+
|
308
|
+
Files that were found in the original project but not generated:
|
309
|
+
- inputs.py
|
310
|
+
|
311
|
+
Files that were different between the original project and the generated artifact:
|
312
|
+
|
313
|
+
--- a/workflow.py
|
314
|
+
+++ b/workflow.py
|
315
|
+
@@ -1 +1 @@
|
316
|
+
-print('hello')
|
317
|
+
+print('foo')
|
318
|
+
\x1b[0m
|
319
|
+
"""
|
320
|
+
)
|
@@ -1,5 +1,6 @@
|
|
1
|
-
from typing import Any, Generic, TypeVar
|
1
|
+
from typing import Any, Generic, TypeVar, cast
|
2
2
|
|
3
|
+
from vellum.workflows.constants import UNDEF
|
3
4
|
from vellum.workflows.descriptors.base import BaseDescriptor
|
4
5
|
from vellum.workflows.expressions.between import BetweenExpression
|
5
6
|
from vellum.workflows.expressions.is_not_null import IsNotNullExpression
|
@@ -12,6 +13,7 @@ from vellum.workflows.references.vellum_secret import VellumSecretReference
|
|
12
13
|
from vellum.workflows.references.workflow_input import WorkflowInputReference
|
13
14
|
from vellum.workflows.types.core import JsonArray, JsonObject
|
14
15
|
from vellum.workflows.utils.uuids import uuid4_from_hash
|
16
|
+
from vellum.workflows.utils.vellum_variables import primitive_type_to_vellum_variable_type
|
15
17
|
from vellum_ee.workflows.display.nodes.base_node_vellum_display import BaseNodeVellumDisplay
|
16
18
|
from vellum_ee.workflows.display.nodes.vellum.utils import convert_descriptor_to_operator
|
17
19
|
from vellum_ee.workflows.display.types import WorkflowDisplayContext
|
@@ -26,6 +28,18 @@ class BaseNodeDisplay(BaseNodeVellumDisplay[_BaseNodeType], Generic[_BaseNodeTyp
|
|
26
28
|
node = self._node
|
27
29
|
node_id = self.node_id
|
28
30
|
|
31
|
+
attributes: JsonArray = []
|
32
|
+
for attribute in node:
|
33
|
+
id = str(uuid4_from_hash(f"{node_id}|{attribute.name}"))
|
34
|
+
|
35
|
+
attributes.append(
|
36
|
+
{
|
37
|
+
"id": id,
|
38
|
+
"name": attribute.name,
|
39
|
+
"value": self.serialize_value(display_context, cast(BaseDescriptor, attribute.instance)),
|
40
|
+
}
|
41
|
+
)
|
42
|
+
|
29
43
|
ports: JsonArray = []
|
30
44
|
for idx, port in enumerate(node.Ports):
|
31
45
|
id = str(uuid4_from_hash(f"{node_id}|{idx}"))
|
@@ -34,6 +48,7 @@ class BaseNodeDisplay(BaseNodeVellumDisplay[_BaseNodeType], Generic[_BaseNodeTyp
|
|
34
48
|
ports.append(
|
35
49
|
{
|
36
50
|
"id": id,
|
51
|
+
"name": port.name,
|
37
52
|
"type": port._condition_type.value,
|
38
53
|
"expression": (
|
39
54
|
self.serialize_condition(display_context, port._condition) if port._condition else None
|
@@ -44,10 +59,29 @@ class BaseNodeDisplay(BaseNodeVellumDisplay[_BaseNodeType], Generic[_BaseNodeTyp
|
|
44
59
|
ports.append(
|
45
60
|
{
|
46
61
|
"id": id,
|
62
|
+
"name": port.name,
|
47
63
|
"type": "DEFAULT",
|
48
64
|
}
|
49
65
|
)
|
50
66
|
|
67
|
+
outputs: JsonArray = []
|
68
|
+
for output in node.Outputs:
|
69
|
+
type = primitive_type_to_vellum_variable_type(output)
|
70
|
+
value = (
|
71
|
+
self.serialize_value(display_context, output.instance)
|
72
|
+
if output.instance is not None and output.instance != UNDEF
|
73
|
+
else None
|
74
|
+
)
|
75
|
+
|
76
|
+
outputs.append(
|
77
|
+
{
|
78
|
+
"id": str(uuid4_from_hash(f"{node_id}|{output.name}")),
|
79
|
+
"name": output.name,
|
80
|
+
"type": type,
|
81
|
+
"value": value,
|
82
|
+
}
|
83
|
+
)
|
84
|
+
|
51
85
|
return {
|
52
86
|
"id": str(node_id),
|
53
87
|
"label": node.__qualname__,
|
@@ -61,7 +95,8 @@ class BaseNodeDisplay(BaseNodeVellumDisplay[_BaseNodeType], Generic[_BaseNodeTyp
|
|
61
95
|
},
|
62
96
|
"ports": ports,
|
63
97
|
"adornments": None,
|
64
|
-
"attributes":
|
98
|
+
"attributes": attributes,
|
99
|
+
"outputs": outputs,
|
65
100
|
}
|
66
101
|
|
67
102
|
def get_generic_node_display_data(self) -> GenericNodeDisplayData:
|
@@ -1,4 +1,10 @@
|
|
1
|
+
from uuid import UUID
|
2
|
+
|
3
|
+
from vellum.workflows.inputs import BaseInputs
|
4
|
+
from vellum.workflows.nodes import BaseNode
|
5
|
+
from vellum.workflows.state import BaseState
|
1
6
|
from vellum.workflows.workflows.base import BaseWorkflow
|
7
|
+
from vellum_ee.workflows.display.vellum import WorkflowInputsVellumDisplayOverrides
|
2
8
|
from vellum_ee.workflows.display.workflows.get_vellum_workflow_display_class import get_workflow_display
|
3
9
|
from vellum_ee.workflows.display.workflows.vellum_workflow_display import VellumWorkflowDisplay
|
4
10
|
|
@@ -40,3 +46,45 @@ def test_vellum_workflow_display__serialize_empty_workflow():
|
|
40
46
|
],
|
41
47
|
},
|
42
48
|
}
|
49
|
+
|
50
|
+
|
51
|
+
def test_vellum_workflow_display__serialize_input_variables_with_capitalized_variable_override():
|
52
|
+
# GIVEN a workflow with input variables
|
53
|
+
class Inputs(BaseInputs):
|
54
|
+
foo: str
|
55
|
+
|
56
|
+
class StartNode(BaseNode):
|
57
|
+
class Outputs(BaseNode.Outputs):
|
58
|
+
output = Inputs.foo
|
59
|
+
|
60
|
+
class ExampleWorkflow(BaseWorkflow[Inputs, BaseState]):
|
61
|
+
graph = StartNode
|
62
|
+
|
63
|
+
class ExampleWorkflowDisplay(VellumWorkflowDisplay[ExampleWorkflow]):
|
64
|
+
inputs_display = {
|
65
|
+
Inputs.foo: WorkflowInputsVellumDisplayOverrides(
|
66
|
+
id=UUID("97b63d71-5413-417f-9cf5-49e1b4fd56e4"), name="Foo", required=True
|
67
|
+
)
|
68
|
+
}
|
69
|
+
|
70
|
+
display = get_workflow_display(
|
71
|
+
base_display_class=ExampleWorkflowDisplay,
|
72
|
+
workflow_class=ExampleWorkflow,
|
73
|
+
)
|
74
|
+
|
75
|
+
# WHEN serializing the workflow
|
76
|
+
exec_config = display.serialize()
|
77
|
+
|
78
|
+
# THEN the input variables are what we expect
|
79
|
+
input_variables = exec_config["input_variables"]
|
80
|
+
|
81
|
+
assert input_variables == [
|
82
|
+
{
|
83
|
+
"id": "97b63d71-5413-417f-9cf5-49e1b4fd56e4",
|
84
|
+
"key": "Foo",
|
85
|
+
"type": "STRING",
|
86
|
+
"default": None,
|
87
|
+
"required": True,
|
88
|
+
"extensions": {"color": None},
|
89
|
+
}
|
90
|
+
]
|