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.
Files changed (20) hide show
  1. vellum/client/core/client_wrapper.py +1 -1
  2. vellum/workflows/nodes/core/templating_node/node.py +27 -1
  3. vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py +30 -0
  4. {vellum_ai-0.13.1.dist-info → vellum_ai-0.13.3.dist-info}/METADATA +2 -2
  5. {vellum_ai-0.13.1.dist-info → vellum_ai-0.13.3.dist-info}/RECORD +20 -19
  6. vellum_cli/__init__.py +14 -0
  7. vellum_cli/push.py +62 -12
  8. vellum_cli/tests/test_push.py +76 -0
  9. vellum_ee/workflows/display/nodes/vellum/base_node.py +37 -2
  10. vellum_ee/workflows/display/tests/test_vellum_workflow_display.py +48 -0
  11. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py +238 -18
  12. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py +177 -0
  13. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py +30 -38
  14. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py +7 -8
  15. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_inline_subworkflow_serialization.py +35 -2
  16. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py +29 -2
  17. vellum_ee/workflows/display/workflows/vellum_workflow_display.py +3 -1
  18. {vellum_ai-0.13.1.dist-info → vellum_ai-0.13.3.dist-info}/LICENSE +0 -0
  19. {vellum_ai-0.13.1.dist-info → vellum_ai-0.13.3.dist-info}/WHEEL +0 -0
  20. {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.1",
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.1
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 (==3.1.2)
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=9iXxS2FmJ0KbreuoBMc5Eh4TnShP12EpSN-xp433M8w,9177
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=iQ2H-vY8njqiYgIifGooqopqkb1BUUA3I7IN7VIHKv8,6149
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=8o8DFW9HCakhfZMS1Iql13RC90hF9pM630Y-rQbo234,8593
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=5jsLW-xPSDq20QMd1QfP-1wKOC6LqEKdQgRT81RSH3I,6343
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=PNns-_fXMR2amAvM39Lkc_yXLAh06U2kaHeCxV_abdI,1576
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=3Rzj7m0O_BwrZQOj3TiJIjp0sk7Sa_mhXVK4fNURvYA,2020
59
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py,sha256=aDkWS_cXNgxz9TkvJXbNzw10WxGnE_Mj6ODtZQz6TOo,37217
60
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py,sha256=1oOLjUxr7jJzNvMEsLOAKhuGIBiIf8IAkNc5cSlTStQ,4476
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=vj6nxfCFHPt-FssX8-6ArMjBwGtlDCdzagdjym9a1f4,19672
68
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_map_node_serialization.py,sha256=xRiQrFov_UkNoS9g8W-XckrkOchtzKBVdQqAvQ0i5ls,14640
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=xwFkPG_vYqVd3bUPhaYbfirLbN3vlDxPaxJ_33ADrck,16806
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=GjSMbrCyeaeaduT1VW1zMQYR48NPqQEDnuhO3I88JWo,1868
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=N-NOBd-UY91qO9orCcW4KEbhNvDQivZPA-PCxs-M0RM,4204
1307
- vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=3rN5Ncwq87Y2YKyobNXALCih6OnUTJf55otnR4V20C8,3557
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.1.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1422
- vellum_ai-0.13.1.dist-info/METADATA,sha256=4wEaXFcoDkNev8uoMpdroK3TDq2U5rm2tTzlfm6V-mg,5327
1423
- vellum_ai-0.13.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1424
- vellum_ai-0.13.1.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1425
- vellum_ai-0.13.1.dist-info/RECORD,,
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
- response = client.workflows.push(
113
- # Remove this once we could serialize using the artifact in Vembda
114
- # https://app.shortcut.com/vellum/story/5585
115
- exec_config=json.dumps(exec_config),
116
- label=label,
117
- workflow_sandbox_id=workflow_config.workflow_sandbox_id,
118
- artifact=artifact,
119
- # We should check with fern if we could auto-serialize typed object fields for us
120
- # https://app.shortcut.com/vellum/story/5568
121
- deployment_config=deployment_config_serialized, # type: ignore[arg-type]
122
- dry_run=dry_run,
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]
@@ -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
+ ]