vellum-ai 0.13.15__py3-none-any.whl → 0.13.18__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 (25) hide show
  1. vellum/client/core/client_wrapper.py +1 -1
  2. vellum/client/resources/workflows/client.py +0 -10
  3. vellum/workflows/nodes/core/templating_node/node.py +4 -47
  4. vellum/workflows/nodes/displayable/code_execution_node/node.py +29 -23
  5. vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py +169 -5
  6. vellum/workflows/nodes/displayable/code_execution_node/utils.py +98 -1
  7. vellum/workflows/nodes/utils.py +50 -1
  8. vellum/workflows/references/external_input.py +14 -0
  9. vellum/workflows/state/base.py +7 -0
  10. vellum/workflows/state/tests/test_state.py +42 -0
  11. {vellum_ai-0.13.15.dist-info → vellum_ai-0.13.18.dist-info}/METADATA +1 -1
  12. {vellum_ai-0.13.15.dist-info → vellum_ai-0.13.18.dist-info}/RECORD +25 -24
  13. vellum_cli/pull.py +57 -20
  14. vellum_cli/push.py +1 -5
  15. vellum_cli/tests/test_pull.py +115 -8
  16. vellum_cli/tests/test_push.py +0 -8
  17. vellum_ee/workflows/display/nodes/base_node_display.py +2 -2
  18. vellum_ee/workflows/display/nodes/get_node_display_class.py +16 -20
  19. vellum_ee/workflows/display/nodes/vellum/__init__.py +2 -0
  20. vellum_ee/workflows/display/nodes/vellum/retry_node.py +10 -0
  21. vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py +23 -0
  22. vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py +2 -2
  23. {vellum_ai-0.13.15.dist-info → vellum_ai-0.13.18.dist-info}/LICENSE +0 -0
  24. {vellum_ai-0.13.15.dist-info → vellum_ai-0.13.18.dist-info}/WHEEL +0 -0
  25. {vellum_ai-0.13.15.dist-info → vellum_ai-0.13.18.dist-info}/entry_points.txt +0 -0
@@ -64,6 +64,22 @@ def test_state_snapshot__nested_dictionary_edit():
64
64
  assert snapshot_count[id(state)] == 1
65
65
 
66
66
 
67
+ def test_state_snapshot__external_input_edit():
68
+ # GIVEN an initial state instance
69
+ state = MockState(foo="bar")
70
+ assert snapshot_count[id(state)] == 0
71
+
72
+ # WHEN we add an external input to state
73
+ class MockExternalInputs(BaseNode.ExternalInputs):
74
+ message: str
75
+
76
+ # WHEN we edit external inputs dictionary
77
+ state.meta.external_inputs[MockExternalInputs.message] = "hello"
78
+
79
+ # THEN the snapshot is emitted
80
+ assert snapshot_count[id(state)] == 1
81
+
82
+
67
83
  def test_state_deepcopy():
68
84
  # GIVEN an initial state instance
69
85
  state = MockState(foo="bar")
@@ -116,6 +132,32 @@ def test_state_json_serialization__with_node_output_updates():
116
132
  assert json_state["meta"]["node_outputs"] == {"MockNode.Outputs.baz": "hello"}
117
133
 
118
134
 
135
+ def test_state_deepcopy__with_external_input_updates():
136
+ # GIVEN an initial state instance
137
+ state = MockState(foo="bar")
138
+
139
+ # AND we add an external input to state
140
+ class MockExternalInputs(BaseNode.ExternalInputs):
141
+ message: str
142
+
143
+ state.meta.external_inputs[MockExternalInputs.message] = "hello"
144
+
145
+ # AND we deepcopy the state
146
+ deepcopied_state = deepcopy(state)
147
+
148
+ # AND we update the original state
149
+ state.meta.external_inputs[MockExternalInputs.message] = "world"
150
+
151
+ # THEN the copied state is not updated
152
+ assert deepcopied_state.meta.external_inputs[MockExternalInputs.message] == "hello"
153
+
154
+ # AND the original state has had the correct number of snapshots
155
+ assert snapshot_count[id(state)] == 2
156
+
157
+ # AND the copied state has had the correct number of snapshots
158
+ assert snapshot_count[id(deepcopied_state)] == 0
159
+
160
+
119
161
  def test_state_json_serialization__with_queue():
120
162
  # GIVEN an initial state instance
121
163
  state = MockState(foo="bar")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vellum-ai
3
- Version: 0.13.15
3
+ Version: 0.13.18
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.9,<4.0
@@ -6,29 +6,29 @@ vellum_cli/config.py,sha256=LVRB-SEJcpQYfg2QGcjKHmRSAijdSFADbS90gDY4AI8,6829
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
- vellum_cli/pull.py,sha256=zf0y22XptUYI_hMP_4Q1CEo9s2wALsTJcCXNd-_ibd8,7551
10
- vellum_cli/push.py,sha256=VuAQ26stoweihwiY8ms8RpU6i200fO5SjU7MiMIDvAo,9217
9
+ vellum_cli/pull.py,sha256=7yvg4oBOgsbBEsgXtCpYlNR4AOR8hPICamY-4HI-3kM,9031
10
+ vellum_cli/push.py,sha256=qQCI_cHvVjeDGA8FLYmSN7UE_wIJQnUXX_5cGsm9OTs,9076
11
11
  vellum_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  vellum_cli/tests/conftest.py,sha256=AFYZryKA2qnUuCPBxBKmHLFoPiE0WhBFFej9tNwSHdc,1526
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
- vellum_cli/tests/test_pull.py,sha256=KRvQ_fwsFH6tBU49O-KOOAzcHxdB-DPbUkdIdahjuTs,19841
17
- vellum_cli/tests/test_push.py,sha256=AgJVaDeIDa--iVvDrUMxjuvOoC-ylwD_A5KumUJTtJU,18810
16
+ vellum_cli/tests/test_pull.py,sha256=CEnVpb9i-_I0wOSabjgJxleRuvvAHFfDB0_f4CvpwMI,23742
17
+ vellum_cli/tests/test_push.py,sha256=kcM8jp6vTRBwfk6cAC_tHa4Eliuy_zZ-X1DfIpSEIW0,18299
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
21
21
  vellum_ee/workflows/display/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  vellum_ee/workflows/display/base.py,sha256=3ZFUYRNKL24fBqXhKpa_Dq2W1a-a86J20dmJYA3H2eY,1755
23
23
  vellum_ee/workflows/display/nodes/__init__.py,sha256=5XOcZJXYUgaLS55QgRJzyQ_W1tpeprjnYAeYVezqoGw,160
24
- vellum_ee/workflows/display/nodes/base_node_display.py,sha256=YBXHPwf453j-xaJoeJsoGhnW039WPSmhbQO-G5UGtcY,15787
24
+ vellum_ee/workflows/display/nodes/base_node_display.py,sha256=ErIK_1DYax0LlFX4AvV1oua8I7JlpXNncjGNadVe-bo,15801
25
25
  vellum_ee/workflows/display/nodes/base_node_vellum_display.py,sha256=pLO0dORfRu--Ne9NgoyFT_CNjfpr5fGCsgbsMkUF5GM,2845
26
- vellum_ee/workflows/display/nodes/get_node_display_class.py,sha256=VpGL-tnMM9wEOkuHNxX74RHAFhz5jcVXvmBkKciOBg0,1367
26
+ vellum_ee/workflows/display/nodes/get_node_display_class.py,sha256=0S6ksPp53WXWh1RQVH71nj2bkCWBj4ZaFYhTxW3N2F4,1230
27
27
  vellum_ee/workflows/display/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  vellum_ee/workflows/display/nodes/tests/test_base_node_display.py,sha256=QqR3Ly0RNrXwOeLdW5nERDFt0gRPf76n1bPES6o5UN4,1093
29
29
  vellum_ee/workflows/display/nodes/types.py,sha256=St1BB6no528OyELGiyRabWao0GGw6mLhstQAvEACbGk,247
30
30
  vellum_ee/workflows/display/nodes/utils.py,sha256=sloya5TpXsnot1HURc9L51INwflRqUzHxRVnCS9Cd-4,973
31
- vellum_ee/workflows/display/nodes/vellum/__init__.py,sha256=nmPLj8vkbVCS46XQqmHq8Xj8Mr36wCK_vWf26A9KIkw,1505
31
+ vellum_ee/workflows/display/nodes/vellum/__init__.py,sha256=Moeuvm0u7c_6wtxUWOJqTJYbrnG4Rp_5mKRoYpfewvQ,1578
32
32
  vellum_ee/workflows/display/nodes/vellum/api_node.py,sha256=hoV-cUtS6H9kmRQXHd2py95GRWI_dAnnaPwvlNBkDOQ,8571
33
33
  vellum_ee/workflows/display/nodes/vellum/code_execution_node.py,sha256=z00Z3L0d4PsUQo4S8FRDTtOFLtjdi17TJbatNVF4nM8,4288
34
34
  vellum_ee/workflows/display/nodes/vellum/conditional_node.py,sha256=ybLIa4uclqVIy3VAQvI1ivg2tnK5Ug_1R5a69DFqL7E,11104
@@ -41,6 +41,7 @@ vellum_ee/workflows/display/nodes/vellum/map_node.py,sha256=VlO3UwkspCOdDQ-h3v8k
41
41
  vellum_ee/workflows/display/nodes/vellum/merge_node.py,sha256=HkNMgdQELiON42jdO-xDLmqrEKdGx1RVqrz2DXNTLS8,3239
42
42
  vellum_ee/workflows/display/nodes/vellum/note_node.py,sha256=TMb8txILu2uWjzoxaghjgjlzeBAgzn4vkP_8zSh2qoE,1151
43
43
  vellum_ee/workflows/display/nodes/vellum/prompt_deployment_node.py,sha256=LFjLUrH6sJ4czPnExdRqFr0PB_yKBMLXLvK5GAzIAgc,3273
44
+ vellum_ee/workflows/display/nodes/vellum/retry_node.py,sha256=i0XGbKecLiHtf7mBf2rbqldPgLcs1TitIphzcHRIvkA,341
44
45
  vellum_ee/workflows/display/nodes/vellum/search_node.py,sha256=TxcAGZDl_hvJ7Y1hUi9YVEVrj9Ie0hKkASdpfRL4_cs,9227
45
46
  vellum_ee/workflows/display/nodes/vellum/subworkflow_deployment_node.py,sha256=lfevlHpGEX14dEDym6qmnkw3nvzQPTB1_D2ch12B_Rk,2701
46
47
  vellum_ee/workflows/display/nodes/vellum/templating_node.py,sha256=JVIMPR3WpveOCWZubHKZkE04mavnTdb_9QY_r3XliRg,3424
@@ -55,13 +56,13 @@ vellum_ee/workflows/display/tests/test_vellum_workflow_display.py,sha256=h4bE187
55
56
  vellum_ee/workflows/display/tests/workflow_serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
57
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
58
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/conftest.py,sha256=EenmEdBtHUFQ0OS-kE7Vboax3JnDdj-K4Qixt5oR0Po,2253
58
- vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py,sha256=pYBubuTzwNczAGFTYhaMCwvMLAcoJfCLkn5PMPbrlMI,7187
59
+ vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_adornments_serialization.py,sha256=6wgLNd-Ae-33SPIYhOvQ9KJ8-5ach98v_Z3g7nrN_xs,8026
59
60
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_attributes_serialization.py,sha256=yPXhdZxEDunNl5LL5Obb0jeO34djt7F-GiaTJhp_fmo,16742
60
61
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_outputs_serialization.py,sha256=-12ZkZb3f5gyoNASV2yeQtMo5HmNsVEo8nXwL6IC-I8,6261
61
62
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_ports_serialization.py,sha256=6th6kCwzql6lddjkTQx4Jbvvs4ChqtJwctW-B4QuBhI,37352
62
63
  vellum_ee/workflows/display/tests/workflow_serialization/generic_nodes/test_trigger_serialization.py,sha256=EbVgg_3_ipTt3MOop4RARX0fmNjwqZtkhIXzx9nGw7Y,4487
63
64
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_api_node_serialization.py,sha256=bXZWxOKAVjZlbP3iLHPHGA4aPs0EguKlQqmYPNGv3cU,16391
64
- vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py,sha256=uvABhpstyxNNOz50-XJMAr3SKp8gluPp1mUtJjvRL0c,29410
65
+ vellum_ee/workflows/display/tests/workflow_serialization/test_basic_code_execution_node_serialization.py,sha256=s2mBoL5Vvpoc-rcWcSGQjLp4yzVVwFmkTbS3--ErzCI,29486
65
66
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_conditional_node_serialization.py,sha256=n3F_Eyru0DYOszBH4jplz7Mzt2FfBNxGlCkTFqvrX-M,48399
66
67
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_error_node_serialization.py,sha256=8LhQgW0uzVOhzz0AwdM-EYugVO-0mGWglxWo_lON4D8,6079
67
68
  vellum_ee/workflows/display/tests/workflow_serialization/test_basic_generic_node_serialization.py,sha256=yWDykoHUjuiVYdevcJxWmYDDmbIszpHKYBu19vqR-I8,5746
@@ -112,7 +113,7 @@ vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
112
113
  vellum/client/__init__.py,sha256=8nZt88C9SVwWanjLbIQMU3rzb32h5UZfFMBx3VPHB50,111887
113
114
  vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
114
115
  vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
115
- vellum/client/core/client_wrapper.py,sha256=_D3P4oNIluyIpVSoNUIGx4jK18hJGMdrTL62pI-ApYg,1869
116
+ vellum/client/core/client_wrapper.py,sha256=JapYtCCqy-ryd-fk1i1krQS6K630bgNpUw4zE0Lct8s,1869
116
117
  vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
117
118
  vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
118
119
  vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
@@ -168,7 +169,7 @@ vellum/client/resources/workflow_deployments/types/workflow_deployments_list_req
168
169
  vellum/client/resources/workflow_sandboxes/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
169
170
  vellum/client/resources/workflow_sandboxes/client.py,sha256=3wVQxkjrJ5bIS8fB5FpKXCP2dX38299ghWrJ8YmXxwQ,7435
170
171
  vellum/client/resources/workflows/__init__.py,sha256=Z4xi8Nxd9U4t35FQSepTt1p-ns0X1xtdNs168kUcuBk,153
171
- vellum/client/resources/workflows/client.py,sha256=9y2Tak0kduS0D1yu57FlgvV_svJZzJlRbGTJ6bz-QuA,11419
172
+ vellum/client/resources/workflows/client.py,sha256=hM7FDn05XHhQk599ti8CI4moIg0RVoEFil3Wp9v9UZk,11215
172
173
  vellum/client/resources/workflows/types/__init__.py,sha256=-uFca4ypncAOvfsg6sjD-5C9zWdA5qNvU6m675GphVg,177
173
174
  vellum/client/resources/workflows/types/workflows_pull_request_format.py,sha256=dOWE_jnDnniIJLoeseeCms23aklghyBkoPmBFzcqqZk,165
174
175
  vellum/client/resources/workspace_secrets/__init__.py,sha256=FTtvy8EDg9nNNg9WCatVgKTRYV8-_v1roeGPAKoa_pw,65
@@ -1326,7 +1327,7 @@ vellum/workflows/nodes/core/retry_node/node.py,sha256=QEpxhKOyxDkRoAn2b0PToZWtAG
1326
1327
  vellum/workflows/nodes/core/retry_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1327
1328
  vellum/workflows/nodes/core/retry_node/tests/test_node.py,sha256=RM_OHwxrHwyxvlQQBJPqVBxpedFuWQ9h2-Xa3kP75sc,4399
1328
1329
  vellum/workflows/nodes/core/templating_node/__init__.py,sha256=GmyuYo81_A1_Bz6id69ozVFS6FKiuDsZTiA3I6MaL2U,70
1329
- vellum/workflows/nodes/core/templating_node/node.py,sha256=GxsVS1FoV84kOtluu00V8H74MEnYvEf55p8mt4ub-5w,5188
1330
+ vellum/workflows/nodes/core/templating_node/node.py,sha256=zCYhq88qLTvoC9LetVrD9sLXkwHZsaWekxMhru_nV70,3752
1330
1331
  vellum/workflows/nodes/core/templating_node/tests/test_templating_node.py,sha256=5iZWQWdJKDHMXBY8bhpb-Dpy9FTfW1HXxGUTivykZAA,4621
1331
1332
  vellum/workflows/nodes/core/try_node/__init__.py,sha256=JVD4DrldTIqFQQFrubs9KtWCCc0YCAc7Fzol5ZWIWeM,56
1332
1333
  vellum/workflows/nodes/core/try_node/node.py,sha256=_lTmSYCiz7lktaxpNWUCglNi8_5Sfy8Rpiov5SeKVMw,3920
@@ -1350,12 +1351,12 @@ vellum/workflows/nodes/displayable/bases/tests/test_utils.py,sha256=eqdqbKNRWVMD
1350
1351
  vellum/workflows/nodes/displayable/bases/types.py,sha256=C37B2Qh2YP7s7pUjd-EYKc2Zl1TbnCgI_mENuUSb8bo,1706
1351
1352
  vellum/workflows/nodes/displayable/bases/utils.py,sha256=ckMUenSsNkiYmSw6FmjSMHYaCk8Y8_sUjL6lkFFEqts,5412
1352
1353
  vellum/workflows/nodes/displayable/code_execution_node/__init__.py,sha256=0FLWMMktpzSnmBMizQglBpcPrP80fzVsoJwJgf822Cg,76
1353
- vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=Hzc0h9sogX8DfFbJqdDLxGU2CMY1WvGosZ7taJC4sPE,8754
1354
+ vellum/workflows/nodes/displayable/code_execution_node/node.py,sha256=KZ5d3_mdpsrPF_ScmEqSfBhfup421RscO9hNiGa52T4,9068
1354
1355
  vellum/workflows/nodes/displayable/code_execution_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1355
1356
  vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1356
1357
  vellum/workflows/nodes/displayable/code_execution_node/tests/fixtures/main.py,sha256=5QsbmkzSlSbcbWTG_JmIqcP-JNJzOPTKxGzdHos19W4,79
1357
- vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py,sha256=s93M_EnU-4n60iSKv3FCf0kppwzFH5FNi9o9E58fQ3I,7510
1358
- vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=LfI3kj2zQz6UGMld_uA9z2LjZobqRcgxQO4jdUWkg7o,376
1358
+ vellum/workflows/nodes/displayable/code_execution_node/tests/test_code_execution_node.py,sha256=ueVDw0GTSGzBZMFLs0NTir_0AE-pUrAYvpgg3Stex7Q,12350
1359
+ vellum/workflows/nodes/displayable/code_execution_node/utils.py,sha256=zRYM7B2t7355LzaAn6LkIn4tM5K7eQ0Kvje4NO6Kq30,3443
1359
1360
  vellum/workflows/nodes/displayable/conditional_node/__init__.py,sha256=AS_EIqFdU1F9t8aLmbZU-rLh9ry6LCJ0uj0D8F0L5Uw,72
1360
1361
  vellum/workflows/nodes/displayable/conditional_node/node.py,sha256=Qjfl33gZ3JEgxBA1EgzSUebboGvsARthIxxcQyvx5Gg,1152
1361
1362
  vellum/workflows/nodes/displayable/final_output_node/__init__.py,sha256=G7VXM4OWpubvSJtVkGmMNeqgb9GkM7qZT838eL18XU4,72
@@ -1386,7 +1387,7 @@ vellum/workflows/nodes/experimental/README.md,sha256=eF6DfIL8t-HbF9-mcofOMymKrra
1386
1387
  vellum/workflows/nodes/experimental/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1387
1388
  vellum/workflows/nodes/experimental/openai_chat_completion_node/__init__.py,sha256=lsyD9laR9p7kx5-BXGH2gUTM242UhKy8SMV0SR6S2iE,90
1388
1389
  vellum/workflows/nodes/experimental/openai_chat_completion_node/node.py,sha256=1EGeiaT-Zoo6pttQFKKBcdf3dmhAbjKGaErYD5FFwlc,10185
1389
- vellum/workflows/nodes/utils.py,sha256=tjBsootwm7vUq9qU4ttDL3CWH0C9Sd6QlX7IhfCCy34,2512
1390
+ vellum/workflows/nodes/utils.py,sha256=T7krLipDSI007JkkH2zsfyQ-tfOBH4hyFzQ0YJKYpuw,4025
1390
1391
  vellum/workflows/outputs/__init__.py,sha256=AyZ4pRh_ACQIGvkf0byJO46EDnSix1ZCAXfvh-ms1QE,94
1391
1392
  vellum/workflows/outputs/base.py,sha256=a7W6rNSDSawwGAXYjNTF2iHb9lnZu7WFSOagZIyy__k,7976
1392
1393
  vellum/workflows/ports/__init__.py,sha256=bZuMt-R7z5bKwpu4uPW7LlJeePOQWmCcDSXe5frUY5g,101
@@ -1397,7 +1398,7 @@ vellum/workflows/references/__init__.py,sha256=glHFC1VfXmcbNvH5VzFbkT03d8_D7MMcv
1397
1398
  vellum/workflows/references/constant.py,sha256=6yUT4q1sMj1hkI_tzzQ9AYcmeeDYFUNCqUq_W2DN0S8,540
1398
1399
  vellum/workflows/references/environment_variable.py,sha256=-gfOcdYwVp9ztSUYz6h2WI2Cg95zqxq5hhFf3Yr7aQg,791
1399
1400
  vellum/workflows/references/execution_count.py,sha256=JILHqt8ELdc9ct-WsVCA5X-rKiP1rmJODw-XTf4kpHI,722
1400
- vellum/workflows/references/external_input.py,sha256=XHugauKYvAmsGoFnjgJh00FcXjSMIqBvRun_CZuJD64,1662
1401
+ vellum/workflows/references/external_input.py,sha256=WyBC6uMDu77431YVSU_WvTt-nGLC_bW65tIsplUJXa4,2056
1401
1402
  vellum/workflows/references/input.py,sha256=3INu-TLTi4dziWmva6LO3WvgDlPzsjayUx61cVvqLJA,325
1402
1403
  vellum/workflows/references/lazy.py,sha256=SXwZUCTzUR-R2-uK0XHALtvp1x84l-QkNY-Ds6KynYA,1932
1403
1404
  vellum/workflows/references/node.py,sha256=LP854wDVs-9I_aZ7-nkbwXqL2H7W2_3LED2e9FixNS8,1418
@@ -1411,12 +1412,12 @@ vellum/workflows/runner/__init__.py,sha256=i1iG5sAhtpdsrlvwgH6B-m49JsINkiWyPWs8v
1411
1412
  vellum/workflows/runner/runner.py,sha256=LvP1UwLmwV1nCZYYrsiwtCzay73voMp1TDRVVBrlMj8,29196
1412
1413
  vellum/workflows/sandbox.py,sha256=GVJzVjMuYzOBnSrboB0_6MMRZWBluAyQ2o7syeaeBd0,2235
1413
1414
  vellum/workflows/state/__init__.py,sha256=yUUdR-_Vl7UiixNDYQZ-GEM_kJI9dnOia75TtuNEsnE,60
1414
- vellum/workflows/state/base.py,sha256=jpSzF1OQd3-fqi6dMGlNsQl-7JnJxCdzWIigmX8Wz-I,14425
1415
+ vellum/workflows/state/base.py,sha256=IIl76sJtn0GfbFWBqMnpGuvtZyVyQMEXv0QKDfLy8Wg,14763
1415
1416
  vellum/workflows/state/context.py,sha256=_NeGQpYo8yNuh0Tfh3OvcB_bG_-GC8b3ZLLl83Pf8-I,1279
1416
1417
  vellum/workflows/state/encoder.py,sha256=WdUidpOaBDx5lilJl8V8McFDHQYiCLCJR9dmktdzdZY,1836
1417
1418
  vellum/workflows/state/store.py,sha256=VYGBQgN1bpd1as5eGiouV_7scg8QsRs4_1aqZAGIsRQ,793
1418
1419
  vellum/workflows/state/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1419
- vellum/workflows/state/tests/test_state.py,sha256=7ap_Z9GJqyonZ1eVXwNyyuhV0AL5XiQ6uYCRhS7PFN4,3921
1420
+ vellum/workflows/state/tests/test_state.py,sha256=ucy7U8886J3CinIKQhOqv4dvkKWQk0fyK3JjTSiKZC4,5266
1420
1421
  vellum/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1421
1422
  vellum/workflows/tests/test_sandbox.py,sha256=JKwaluI-lODQo7Ek9sjDstjL_WTdSqUlVik6ZVTfVOA,1826
1422
1423
  vellum/workflows/types/__init__.py,sha256=KxUTMBGzuRCfiMqzzsykOeVvrrkaZmTTo1a7SLu8gRM,68
@@ -1441,8 +1442,8 @@ vellum/workflows/vellum_client.py,sha256=ODrq_TSl-drX2aezXegf7pizpWDVJuTXH-j6528
1441
1442
  vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
1442
1443
  vellum/workflows/workflows/base.py,sha256=k0kUWWko4fHyCqLSU_1cBK_pXZpl9MXekWiG-bdOAo0,18353
1443
1444
  vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnadGsrSZGa7t7LpJA,2008
1444
- vellum_ai-0.13.15.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1445
- vellum_ai-0.13.15.dist-info/METADATA,sha256=H6f0l4ZqdCd9ANG3dLIdY-vX9NNebMI9n_hBLBQ49AI,5335
1446
- vellum_ai-0.13.15.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1447
- vellum_ai-0.13.15.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1448
- vellum_ai-0.13.15.dist-info/RECORD,,
1445
+ vellum_ai-0.13.18.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
1446
+ vellum_ai-0.13.18.dist-info/METADATA,sha256=aLUy-xu3bfn69UAWushKWy3YWWoMMruLabea-UEOO1o,5335
1447
+ vellum_ai-0.13.18.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
1448
+ vellum_ai-0.13.18.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
1449
+ vellum_ai-0.13.18.dist-info/RECORD,,
vellum_cli/pull.py CHANGED
@@ -31,6 +31,16 @@ class WorkflowConfigResolutionResult(UniversalBaseModel):
31
31
  pk: Optional[str] = None
32
32
 
33
33
 
34
+ class RunnerConfig(UniversalBaseModel):
35
+ container_image_name: Optional[str] = None
36
+ container_image_tag: Optional[str] = None
37
+
38
+
39
+ class PullContentsMetadata(UniversalBaseModel):
40
+ label: Optional[str] = None
41
+ runner_config: Optional[RunnerConfig] = None
42
+
43
+
34
44
  def _resolve_workflow_config(
35
45
  config: VellumCliConfig,
36
46
  module: Optional[str] = None,
@@ -42,14 +52,33 @@ def _resolve_workflow_config(
42
52
 
43
53
  if module:
44
54
  workflow_config = next((w for w in config.workflows if w.module == module), None)
55
+ if not workflow_config and workflow_sandbox_id:
56
+ workflow_config = WorkflowConfig(
57
+ workflow_sandbox_id=workflow_sandbox_id,
58
+ module=module,
59
+ )
60
+ config.workflows.append(workflow_config)
61
+ return WorkflowConfigResolutionResult(
62
+ workflow_config=workflow_config,
63
+ pk=workflow_sandbox_id,
64
+ )
65
+
45
66
  return WorkflowConfigResolutionResult(
46
67
  workflow_config=workflow_config,
47
68
  pk=workflow_config.workflow_sandbox_id if workflow_config else None,
48
69
  )
49
70
  elif workflow_sandbox_id:
71
+ workflow_config = next((w for w in config.workflows if w.workflow_sandbox_id == workflow_sandbox_id), None)
72
+ if workflow_config:
73
+ return WorkflowConfigResolutionResult(
74
+ workflow_config=workflow_config,
75
+ pk=workflow_sandbox_id,
76
+ )
77
+
78
+ # We use an empty module name to indicate that we want to backfill it once we have the Workflow Sandbox Label
50
79
  workflow_config = WorkflowConfig(
51
80
  workflow_sandbox_id=workflow_sandbox_id,
52
- module=f"workflow_{workflow_sandbox_id.split('-')[0]}",
81
+ module="",
53
82
  )
54
83
  config.workflows.append(workflow_config)
55
84
  return WorkflowConfigResolutionResult(
@@ -98,7 +127,6 @@ def pull_command(
98
127
  workflow_sandbox_id=workflow_sandbox_id,
99
128
  workflow_deployment=workflow_deployment,
100
129
  )
101
- save_lock_file = not module
102
130
 
103
131
  workflow_config = workflow_config_result.workflow_config
104
132
  if not workflow_config:
@@ -108,7 +136,11 @@ def pull_command(
108
136
  if not pk:
109
137
  raise ValueError("No workflow sandbox ID found in project to pull from.")
110
138
 
111
- logger.info(f"Pulling workflow into {workflow_config.module}")
139
+ if workflow_config.module:
140
+ logger.info(f"Pulling workflow into {workflow_config.module}...")
141
+ else:
142
+ logger.info(f"Pulling workflow from {pk}...")
143
+
112
144
  client = create_vellum_client()
113
145
  query_parameters = {}
114
146
 
@@ -129,11 +161,30 @@ def pull_command(
129
161
  zip_bytes = b"".join(response)
130
162
  zip_buffer = io.BytesIO(zip_bytes)
131
163
 
132
- target_dir = os.path.join(os.getcwd(), *workflow_config.module.split("."))
133
164
  error_content = ""
134
- metadata_json: Optional[dict] = None
135
165
 
136
166
  with zipfile.ZipFile(zip_buffer) as zip_file:
167
+ if METADATA_FILE_NAME in zip_file.namelist():
168
+ metadata_json: Optional[dict] = None
169
+ with zip_file.open(METADATA_FILE_NAME) as source:
170
+ metadata_json = json.load(source)
171
+
172
+ pull_contents_metadata = PullContentsMetadata.model_validate(metadata_json)
173
+
174
+ if pull_contents_metadata.runner_config:
175
+ workflow_config.container_image_name = pull_contents_metadata.runner_config.container_image_name
176
+ workflow_config.container_image_tag = pull_contents_metadata.runner_config.container_image_tag
177
+ if workflow_config.container_image_name and not workflow_config.container_image_tag:
178
+ workflow_config.container_image_tag = "latest"
179
+
180
+ if not workflow_config.module and pull_contents_metadata.label:
181
+ workflow_config.module = snake_case(pull_contents_metadata.label)
182
+
183
+ if not workflow_config.module:
184
+ raise ValueError(f"Failed to resolve a module name for Workflow {pk}")
185
+
186
+ target_dir = os.path.join(os.getcwd(), *workflow_config.module.split("."))
187
+
137
188
  # Delete files in target_dir that aren't in the zip file
138
189
  if os.path.exists(target_dir):
139
190
  ignore_patterns = (
@@ -163,7 +214,6 @@ def pull_command(
163
214
  error_content = content
164
215
  continue
165
216
  if file_name == METADATA_FILE_NAME:
166
- metadata_json = json.loads(content)
167
217
  continue
168
218
 
169
219
  target_file = os.path.join(target_dir, file_name)
@@ -172,15 +222,6 @@ def pull_command(
172
222
  logger.info(f"Writing to {target_file}...")
173
223
  target.write(content)
174
224
 
175
- if metadata_json:
176
- runner_config = metadata_json.get("runner_config")
177
-
178
- if runner_config:
179
- workflow_config.container_image_name = runner_config.get("container_image_name")
180
- workflow_config.container_image_tag = runner_config.get("container_image_tag")
181
- if workflow_config.container_image_name and not workflow_config.container_image_tag:
182
- workflow_config.container_image_tag = "latest"
183
-
184
225
  if include_json:
185
226
  logger.warning(
186
227
  """The pulled JSON representation of the Workflow should be used for debugging purposely only. \
@@ -190,16 +231,12 @@ Its schema should be considered unstable and subject to change at any time."""
190
231
  if include_sandbox:
191
232
  if not workflow_config.ignore:
192
233
  workflow_config.ignore = "sandbox.py"
193
- save_lock_file = True
194
234
  elif isinstance(workflow_config.ignore, str) and "sandbox.py" != workflow_config.ignore:
195
235
  workflow_config.ignore = [workflow_config.ignore, "sandbox.py"]
196
- save_lock_file = True
197
236
  elif isinstance(workflow_config.ignore, list) and "sandbox.py" not in workflow_config.ignore:
198
237
  workflow_config.ignore.append("sandbox.py")
199
- save_lock_file = True
200
238
 
201
- if save_lock_file:
202
- config.save()
239
+ config.save()
203
240
 
204
241
  if error_content:
205
242
  logger.error(error_content)
vellum_cli/push.py CHANGED
@@ -12,7 +12,6 @@ from dotenv import load_dotenv
12
12
  from vellum.client.core.api_error import ApiError
13
13
  from vellum.resources.workflows.client import OMIT
14
14
  from vellum.types import WorkflowPushDeploymentConfigRequest
15
- from vellum.workflows.utils.names import snake_to_title_case
16
15
  from vellum.workflows.vellum_client import create_vellum_client
17
16
  from vellum.workflows.workflows.base import BaseWorkflow
18
17
  from vellum_cli.config import DEFAULT_WORKSPACE_CONFIG, WorkflowConfig, WorkflowDeploymentConfig, load_vellum_cli_config
@@ -111,8 +110,6 @@ def push_command(
111
110
  "container_image_name": workflow_config.container_image_name,
112
111
  }
113
112
 
114
- label = snake_to_title_case(workflow_config.module.split(".")[-1])
115
-
116
113
  deployment_config: WorkflowPushDeploymentConfigRequest = OMIT
117
114
  deployment_config_serialized: str = OMIT
118
115
  if deploy:
@@ -158,7 +155,6 @@ def push_command(
158
155
  # Remove this once we could serialize using the artifact in Vembda
159
156
  # https://app.shortcut.com/vellum/story/5585
160
157
  exec_config=json.dumps(exec_config),
161
- label=label,
162
158
  workflow_sandbox_id=workflow_config.workflow_sandbox_id or workflow_sandbox_id,
163
159
  artifact=artifact,
164
160
  # We should check with fern if we could auto-serialize typed object fields for us
@@ -230,7 +226,7 @@ def push_command(
230
226
  ) # type: ignore[attr-defined]
231
227
  else:
232
228
  logger.info(
233
- f"""Successfully pushed {label} to Vellum!
229
+ f"""Successfully pushed {workflow_config.module} to Vellum!
234
230
  Visit at: https://app.vellum.ai/workflow-sandboxes/{response.workflow_sandbox_id}"""
235
231
  )
236
232
 
@@ -39,6 +39,7 @@ def test_pull(vellum_client, mock_module, base_command):
39
39
  # GIVEN a module on the user's filesystem
40
40
  temp_dir = mock_module.temp_dir
41
41
  module = mock_module.module
42
+ workflow_sandbox_id = mock_module.workflow_sandbox_id
42
43
 
43
44
  # AND the workflow pull API call returns a zip file
44
45
  vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
@@ -56,6 +57,27 @@ def test_pull(vellum_client, mock_module, base_command):
56
57
  with open(workflow_py) as f:
57
58
  assert f.read() == "print('hello')"
58
59
 
60
+ # AND the vellum.lock.json file is created
61
+ vellum_lock_json = os.path.join(temp_dir, "vellum.lock.json")
62
+ assert os.path.exists(vellum_lock_json)
63
+ with open(vellum_lock_json) as f:
64
+ lock_data = json.load(f)
65
+ assert lock_data == {
66
+ "version": "1.0",
67
+ "workflows": [
68
+ {
69
+ "module": module,
70
+ "workflow_sandbox_id": workflow_sandbox_id,
71
+ "container_image_name": None,
72
+ "container_image_tag": None,
73
+ "ignore": None,
74
+ "deployments": [],
75
+ "workspace": "default",
76
+ }
77
+ ],
78
+ "workspaces": [],
79
+ }
80
+
59
81
 
60
82
  def test_pull__second_module(vellum_client, mock_module):
61
83
  # GIVEN a module on the user's filesystem
@@ -95,7 +117,13 @@ def test_pull__sandbox_id_with_no_config(vellum_client):
95
117
  workflow_sandbox_id = "87654321-0000-0000-0000-000000000000"
96
118
 
97
119
  # AND the workflow pull API call returns a zip file
98
- vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
120
+ vellum_client.workflows.pull.return_value = iter(
121
+ [
122
+ _zip_file_map(
123
+ {"workflow.py": "print('hello')", "metadata.json": json.dumps({"label": "Super Cool Workflow"})}
124
+ )
125
+ ]
126
+ )
99
127
 
100
128
  # AND we are currently in a new directory
101
129
  current_dir = os.getcwd()
@@ -112,7 +140,7 @@ def test_pull__sandbox_id_with_no_config(vellum_client):
112
140
 
113
141
  # AND the pull api is called with the workflow sandbox id
114
142
  vellum_client.workflows.pull.assert_called_once()
115
- workflow_py = os.path.join(temp_dir, "workflow_87654321", "workflow.py")
143
+ workflow_py = os.path.join(temp_dir, "super_cool_workflow", "workflow.py")
116
144
  assert os.path.exists(workflow_py)
117
145
  with open(workflow_py) as f:
118
146
  assert f.read() == "print('hello')"
@@ -127,7 +155,7 @@ def test_pull__sandbox_id_with_no_config(vellum_client):
127
155
  "workspaces": [],
128
156
  "workflows": [
129
157
  {
130
- "module": "workflow_87654321",
158
+ "module": "super_cool_workflow",
131
159
  "workflow_sandbox_id": "87654321-0000-0000-0000-000000000000",
132
160
  "ignore": None,
133
161
  "deployments": [],
@@ -147,7 +175,13 @@ def test_pull__sandbox_id_with_other_workflow_configured(vellum_client, mock_mod
147
175
  workflow_sandbox_id = "87654321-0000-0000-0000-000000000000"
148
176
 
149
177
  # AND the workflow pull API call returns a zip file
150
- vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
178
+ vellum_client.workflows.pull.return_value = iter(
179
+ [
180
+ _zip_file_map(
181
+ {"workflow.py": "print('hello')", "metadata.json": json.dumps({"label": "Super Cool Workflow"})}
182
+ )
183
+ ]
184
+ )
151
185
 
152
186
  # WHEN the user runs the pull command with the new workflow sandbox id
153
187
  runner = CliRunner()
@@ -162,7 +196,7 @@ def test_pull__sandbox_id_with_other_workflow_configured(vellum_client, mock_mod
162
196
  assert call_args[0] == workflow_sandbox_id
163
197
 
164
198
  # AND the workflow.py file is written to the module directory
165
- workflow_py = os.path.join(temp_dir, "workflow_87654321", "workflow.py")
199
+ workflow_py = os.path.join(temp_dir, "super_cool_workflow", "workflow.py")
166
200
  assert os.path.exists(workflow_py)
167
201
  with open(workflow_py) as f:
168
202
  assert f.read() == "print('hello')"
@@ -420,8 +454,12 @@ def test_pull__sandbox_id_with_other_workflow_deployment_in_lock(vellum_client,
420
454
  _zip_file_map(
421
455
  {
422
456
  "workflow.py": "print('hello')",
423
- "metadata.json": '{"runner_config": { "container_image_name": "test", '
424
- '"container_image_tag": "1.0" } }',
457
+ "metadata.json": json.dumps(
458
+ {
459
+ "runner_config": {"container_image_name": "test", "container_image_tag": "1.0"},
460
+ "label": "Super Cool Workflow",
461
+ }
462
+ ),
425
463
  }
426
464
  )
427
465
  ]
@@ -456,7 +494,7 @@ def test_pull__sandbox_id_with_other_workflow_deployment_in_lock(vellum_client,
456
494
  "workspace": "default",
457
495
  },
458
496
  {
459
- "module": "workflow_87654321",
497
+ "module": "super_cool_workflow",
460
498
  "workflow_sandbox_id": new_workflow_sandbox_id,
461
499
  "ignore": None,
462
500
  "deployments": [],
@@ -539,3 +577,72 @@ def test_pull__include_sandbox(vellum_client, mock_module):
539
577
  with open(lock_json) as f:
540
578
  lock_data = json.load(f)
541
579
  assert lock_data["workflows"][0]["ignore"] == "sandbox.py"
580
+
581
+
582
+ def test_pull__same_pull_twice__one_entry_in_lockfile(vellum_client, mock_module):
583
+ # GIVEN a module on the user's filesystem
584
+ module = mock_module.module
585
+ temp_dir = mock_module.temp_dir
586
+ workflow_sandbox_id = mock_module.workflow_sandbox_id
587
+
588
+ # AND the workflow pull API call returns a zip file both times
589
+ zip_contents = _zip_file_map({"workflow.py": "print('hello')"})
590
+ responses = iter([zip_contents, zip_contents])
591
+
592
+ def workflows_pull_side_effect(*args, **kwargs):
593
+ return iter([next(responses)])
594
+
595
+ vellum_client.workflows.pull.side_effect = workflows_pull_side_effect
596
+
597
+ # AND the user runs the pull command once
598
+ runner = CliRunner()
599
+ runner.invoke(cli_main, ["pull", module])
600
+
601
+ # WHEN the user runs the pull command again but with the workflow sandbox id
602
+ result = runner.invoke(cli_main, ["workflows", "pull", "--workflow-sandbox-id", workflow_sandbox_id])
603
+
604
+ # THEN the command returns successfully
605
+ assert result.exit_code == 0, (result.output, result.exception)
606
+
607
+ # AND the lockfile should only have one entry
608
+ lock_json = os.path.join(temp_dir, "vellum.lock.json")
609
+ with open(lock_json) as f:
610
+ lock_data = json.load(f)
611
+ assert len(lock_data["workflows"]) == 1
612
+
613
+
614
+ def test_pull__module_not_in_config(vellum_client, mock_module):
615
+ # GIVEN a module on the user's filesystem
616
+ module = mock_module.module
617
+ temp_dir = mock_module.temp_dir
618
+ workflow_sandbox_id = mock_module.workflow_sandbox_id
619
+ set_pyproject_toml = mock_module.set_pyproject_toml
620
+
621
+ # AND the pyproject.toml does not have the module configured
622
+ set_pyproject_toml({"workflows": []})
623
+
624
+ # AND the workflow pull API call returns a zip file
625
+ vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
626
+
627
+ # WHEN the user runs the pull command again with the workflow sandbox id and module
628
+ runner = CliRunner()
629
+ result = runner.invoke(cli_main, ["workflows", "pull", module, "--workflow-sandbox-id", workflow_sandbox_id])
630
+
631
+ # THEN the command returns successfully
632
+ assert result.exit_code == 0, (result.output, result.exception)
633
+
634
+ # AND the lockfile should have the new entry
635
+ lock_json = os.path.join(temp_dir, "vellum.lock.json")
636
+ with open(lock_json) as f:
637
+ lock_data = json.load(f)
638
+ assert lock_data["workflows"] == [
639
+ {
640
+ "module": module,
641
+ "workflow_sandbox_id": workflow_sandbox_id,
642
+ "ignore": None,
643
+ "deployments": [],
644
+ "container_image_name": None,
645
+ "container_image_tag": None,
646
+ "workspace": "default",
647
+ }
648
+ ]
@@ -119,14 +119,12 @@ def test_push__happy_path(mock_module, vellum_client, base_command):
119
119
  assert result.exit_code == 0
120
120
 
121
121
  # Get the last part of the module path and format it
122
- expected_label = mock_module.module.split(".")[-1].replace("_", " ").title()
123
122
  expected_artifact_name = f"{mock_module.module.replace('.', '__')}.tar.gz"
124
123
 
125
124
  # AND we should have called the push API with the correct args
126
125
  vellum_client.workflows.push.assert_called_once()
127
126
  call_args = vellum_client.workflows.push.call_args.kwargs
128
127
  assert json.loads(call_args["exec_config"])["workflow_raw_data"]["definition"]["name"] == "ExampleWorkflow"
129
- assert call_args["label"] == expected_label
130
128
  assert is_valid_uuid(call_args["workflow_sandbox_id"])
131
129
  assert call_args["artifact"].name == expected_artifact_name
132
130
  assert "deplyment_config" not in call_args
@@ -165,14 +163,12 @@ def test_push__workflow_sandbox_option__existing_id(mock_module, vellum_client,
165
163
  assert result.exit_code == 0
166
164
 
167
165
  # Get the last part of the module path and format it
168
- expected_label = mock_module.module.split(".")[-1].replace("_", " ").title()
169
166
  expected_artifact_name = f"{mock_module.module.replace('.', '__')}.tar.gz"
170
167
 
171
168
  # AND we should have called the push API with the correct args
172
169
  vellum_client.workflows.push.assert_called_once()
173
170
  call_args = vellum_client.workflows.push.call_args.kwargs
174
171
  assert json.loads(call_args["exec_config"])["workflow_raw_data"]["definition"]["name"] == "ExampleWorkflow"
175
- assert call_args["label"] == expected_label
176
172
  assert call_args["workflow_sandbox_id"] == existing_workflow_sandbox_id
177
173
  assert call_args["artifact"].name == expected_artifact_name
178
174
  assert "deplyment_config" not in call_args
@@ -216,14 +212,12 @@ def test_push__workflow_sandbox_option__existing_no_module(mock_module, vellum_c
216
212
  assert result.exit_code == 0
217
213
 
218
214
  # Get the last part of the module path and format it
219
- expected_label = second_module.split(".")[-1].replace("_", " ").title()
220
215
  expected_artifact_name = f"{second_module.replace('.', '__')}.tar.gz"
221
216
 
222
217
  # AND we should have called the push API with the correct args
223
218
  vellum_client.workflows.push.assert_called_once()
224
219
  call_args = vellum_client.workflows.push.call_args.kwargs
225
220
  assert json.loads(call_args["exec_config"])["workflow_raw_data"]["definition"]["name"] == "ExampleWorkflow"
226
- assert call_args["label"] == expected_label
227
221
  assert call_args["workflow_sandbox_id"] == second_workflow_sandbox_id
228
222
  assert call_args["artifact"].name == expected_artifact_name
229
223
  assert "deplyment_config" not in call_args
@@ -297,14 +291,12 @@ def test_push__deployment(mock_module, vellum_client, base_command):
297
291
  assert result.exit_code == 0
298
292
 
299
293
  # Get the last part of the module path and format it
300
- expected_label = mock_module.module.split(".")[-1].replace("_", " ").title()
301
294
  expected_artifact_name = f"{mock_module.module.replace('.', '__')}.tar.gz"
302
295
 
303
296
  # AND we should have called the push API with the correct args
304
297
  vellum_client.workflows.push.assert_called_once()
305
298
  call_args = vellum_client.workflows.push.call_args.kwargs
306
299
  assert json.loads(call_args["exec_config"])["workflow_raw_data"]["definition"]["name"] == "ExampleWorkflow"
307
- assert call_args["label"] == expected_label
308
300
  assert is_valid_uuid(call_args["workflow_sandbox_id"])
309
301
  assert call_args["artifact"].name == expected_artifact_name
310
302
  assert call_args["deployment_config"] == "{}"