vellum-ai 0.13.1__py3-none-any.whl → 0.13.2__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.2",
22
22
  }
23
23
  headers["X_API_KEY"] = self.api_key
24
24
  return headers
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.13.1
3
+ Version: 0.13.2
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.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=AO_2SZDUQmHi5QG-jZCS2IRnrrOAOowR3Mx8RJ0wqe4,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
@@ -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.2.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1423
+ vellum_ai-0.13.2.dist-info/METADATA,sha256=OZhBbZ5uJfG4C4BhBLJr-J6TiSA3GLLdwJHLss8Hm6o,5327
1424
+ vellum_ai-0.13.2.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1425
+ vellum_ai-0.13.2.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1426
+ vellum_ai-0.13.2.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
+ ]