vellum-ai 0.13.1__py3-none-any.whl → 0.13.2__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.
@@ -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
+ ]