vellum-ai 0.14.64__py3-none-any.whl → 0.14.66__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
vellum_cli/pull.py CHANGED
@@ -20,7 +20,7 @@ from vellum_cli.config import (
20
20
  WorkflowDeploymentConfig,
21
21
  load_vellum_cli_config,
22
22
  )
23
- from vellum_cli.logger import load_cli_logger
23
+ from vellum_cli.logger import handle_cli_error, load_cli_logger
24
24
 
25
25
  ERROR_LOG_FILE_NAME = "error.log"
26
26
  METADATA_FILE_NAME = "metadata.json"
@@ -201,16 +201,42 @@ def pull_command(
201
201
  zip_bytes = b"".join(response)
202
202
  except ApiError as e:
203
203
  if e.status_code == 401 or e.status_code == 403:
204
- raise Exception("Please make sure your `VELLUM_API_KEY` environment variable is set correctly.")
204
+ handle_cli_error(
205
+ logger,
206
+ title="Authentication failed",
207
+ message="Unable to authenticate with the Vellum API.",
208
+ suggestion="Please make sure your `VELLUM_API_KEY` environment variable is set correctly and that you have access to this workflow.", # noqa: E501
209
+ )
210
+
211
+ if e.status_code == 404:
212
+ handle_cli_error(
213
+ logger,
214
+ title="Workflow not found",
215
+ message=f"The workflow with ID '{pk}' could not be found.",
216
+ suggestion="Please verify the workflow ID is correct and that you have access to it in your workspace.",
217
+ )
205
218
 
206
219
  if e.status_code == 500:
207
- raise Exception(
208
- "The Pull API failed with an unexpected error. Please try again later and contact support if the problem persists." # noqa: E501
220
+ handle_cli_error(
221
+ logger,
222
+ title="Server error occurred",
223
+ message="The Vellum API encountered an internal server error while processing your request.",
224
+ suggestion="Please try again in a few moments. If the problem persists, contact Vellum support with the workflow ID and timestamp.", # noqa: E501
225
+ )
226
+
227
+ if e.status_code == 502 or e.status_code == 503 or e.status_code == 504:
228
+ handle_cli_error(
229
+ logger,
230
+ title="Service temporarily unavailable",
231
+ message="The Vellum API is temporarily unavailable or experiencing high load.",
232
+ suggestion="Please wait a moment and try again. If the issue continues, check the Vellum status page or contact support.", # noqa: E501
209
233
  )
210
234
 
211
- # TODO: We should return an Origin header in to validate this case
212
- raise Exception(
213
- "The API we tried to pull is invalid. Please make sure your `VELLUM_API_URL` environment variable is set correctly." # noqa: E501
235
+ handle_cli_error(
236
+ logger,
237
+ title="API request failed",
238
+ message=f"The API request failed with status code {e.status_code}.",
239
+ suggestion="Please verify your `VELLUM_API_URL` environment variable is set correctly and try again.",
214
240
  )
215
241
 
216
242
  zip_buffer = io.BytesIO(zip_bytes)
@@ -299,8 +325,11 @@ def pull_command(
299
325
  logger.info(f"Writing to {target_file}...")
300
326
  target.write(content)
301
327
  except zipfile.BadZipFile:
302
- raise Exception(
303
- "The API we tried to pull from returned an invalid zip file. Please make sure your `VELLUM_API_URL` environment variable is set correctly." # noqa: E501
328
+ handle_cli_error(
329
+ logger,
330
+ title="Invalid response format",
331
+ message="The API returned an invalid zip file format.",
332
+ suggestion="Please verify your `VELLUM_API_URL` environment variable is set correctly and try again.",
304
333
  )
305
334
 
306
335
  if include_json:
@@ -0,0 +1,184 @@
1
+ import subprocess
2
+ from unittest.mock import patch
3
+ from uuid import uuid4
4
+
5
+ from click.testing import CliRunner
6
+
7
+ from vellum.client.core.api_error import ApiError
8
+ from vellum_cli import main as cli_main
9
+
10
+
11
+ @patch("subprocess.run")
12
+ @patch("docker.from_env")
13
+ def test_image_push_docker_service_token_401_error(mock_docker_from_env, mock_run, vellum_client, monkeypatch):
14
+ monkeypatch.setenv("VELLUM_API_URL", "https://api.vellum.ai")
15
+ monkeypatch.setenv("VELLUM_API_KEY", "123456abcdef")
16
+
17
+ mock_docker_client = mock_docker_from_env.return_value
18
+ mock_docker_client.images.get.return_value.id = "test-image-id"
19
+
20
+ mock_run.side_effect = [
21
+ subprocess.CompletedProcess(
22
+ args="", returncode=0, stdout=b'{"manifests": [{"platform": {"architecture": "amd64"}}]}'
23
+ ),
24
+ ]
25
+
26
+ vellum_client.container_images.docker_service_token.side_effect = ApiError(status_code=401, body="Unauthorized")
27
+
28
+ runner = CliRunner()
29
+ result = runner.invoke(cli_main, ["image", "push", "myimage:latest"])
30
+
31
+ assert result.exit_code == 1
32
+ assert "Authentication failed" in result.output
33
+ assert "VELLUM_API_KEY" in result.output
34
+
35
+
36
+ @patch("subprocess.run")
37
+ @patch("docker.from_env")
38
+ def test_image_push_docker_service_token_500_error(mock_docker_from_env, mock_run, vellum_client, monkeypatch):
39
+ monkeypatch.setenv("VELLUM_API_URL", "https://api.vellum.ai")
40
+ monkeypatch.setenv("VELLUM_API_KEY", "123456abcdef")
41
+
42
+ mock_docker_client = mock_docker_from_env.return_value
43
+ mock_docker_client.images.get.return_value.id = "test-image-id"
44
+
45
+ mock_run.side_effect = [
46
+ subprocess.CompletedProcess(
47
+ args="", returncode=0, stdout=b'{"manifests": [{"platform": {"architecture": "amd64"}}]}'
48
+ ),
49
+ ]
50
+
51
+ vellum_client.container_images.docker_service_token.side_effect = ApiError(
52
+ status_code=500, body="Internal Server Error"
53
+ )
54
+
55
+ runner = CliRunner()
56
+ result = runner.invoke(cli_main, ["image", "push", "myimage:latest"])
57
+
58
+ assert result.exit_code == 1
59
+ assert "Server error" in result.output
60
+ assert "try again later" in result.output
61
+
62
+
63
+ @patch("subprocess.run")
64
+ @patch("docker.from_env")
65
+ def test_image_push_docker_service_token_other_error(mock_docker_from_env, mock_run, vellum_client, monkeypatch):
66
+ monkeypatch.setenv("VELLUM_API_URL", "https://api.vellum.ai")
67
+ monkeypatch.setenv("VELLUM_API_KEY", "123456abcdef")
68
+
69
+ mock_docker_client = mock_docker_from_env.return_value
70
+ mock_docker_client.images.get.return_value.id = "test-image-id"
71
+
72
+ mock_run.side_effect = [
73
+ subprocess.CompletedProcess(
74
+ args="", returncode=0, stdout=b'{"manifests": [{"platform": {"architecture": "amd64"}}]}'
75
+ ),
76
+ ]
77
+
78
+ vellum_client.container_images.docker_service_token.side_effect = ApiError(
79
+ status_code=429, body="Too Many Requests"
80
+ )
81
+
82
+ runner = CliRunner()
83
+ result = runner.invoke(cli_main, ["image", "push", "myimage:latest"])
84
+
85
+ assert result.exit_code == 1
86
+ assert "API request failed" in result.output
87
+ assert "HTTP 429" in result.output
88
+
89
+
90
+ @patch("subprocess.run")
91
+ @patch("docker.from_env")
92
+ def test_image_push_container_image_401_error(mock_docker_from_env, mock_run, vellum_client, monkeypatch):
93
+ monkeypatch.setenv("VELLUM_API_URL", "https://api.vellum.ai")
94
+ monkeypatch.setenv("VELLUM_API_KEY", "123456abcdef")
95
+
96
+ mock_docker_client = mock_docker_from_env.return_value
97
+ mock_docker_client.images.get.return_value.id = "test-image-id"
98
+ mock_docker_client.images.push.return_value = ["pushed"]
99
+
100
+ mock_run.side_effect = [
101
+ subprocess.CompletedProcess(
102
+ args="", returncode=0, stdout=b'{"manifests": [{"platform": {"architecture": "amd64"}}]}'
103
+ ),
104
+ subprocess.CompletedProcess(args="", returncode=0, stdout=b'[{"RepoDigests": ["test-repo@sha256:abcd1234"]}]'),
105
+ ]
106
+
107
+ vellum_client.container_images.docker_service_token.return_value = type(
108
+ "obj", (object,), {"access_token": "345678mnopqr", "organization_id": str(uuid4()), "repository": "myrepo.net"}
109
+ )()
110
+
111
+ vellum_client.container_images.push_container_image.side_effect = ApiError(status_code=401, body="Unauthorized")
112
+
113
+ runner = CliRunner()
114
+ result = runner.invoke(cli_main, ["image", "push", "myimage:latest"])
115
+
116
+ assert result.exit_code == 1
117
+ assert "Authentication failed" in result.output
118
+ assert "VELLUM_API_KEY" in result.output
119
+
120
+
121
+ @patch("subprocess.run")
122
+ @patch("docker.from_env")
123
+ def test_image_push_container_image_500_error(mock_docker_from_env, mock_run, vellum_client, monkeypatch):
124
+ monkeypatch.setenv("VELLUM_API_URL", "https://api.vellum.ai")
125
+ monkeypatch.setenv("VELLUM_API_KEY", "123456abcdef")
126
+
127
+ mock_docker_client = mock_docker_from_env.return_value
128
+ mock_docker_client.images.get.return_value.id = "test-image-id"
129
+ mock_docker_client.images.push.return_value = ["pushed"]
130
+
131
+ mock_run.side_effect = [
132
+ subprocess.CompletedProcess(
133
+ args="", returncode=0, stdout=b'{"manifests": [{"platform": {"architecture": "amd64"}}]}'
134
+ ),
135
+ subprocess.CompletedProcess(args="", returncode=0, stdout=b'[{"RepoDigests": ["test-repo@sha256:abcd1234"]}]'),
136
+ ]
137
+
138
+ vellum_client.container_images.docker_service_token.return_value = type(
139
+ "obj", (object,), {"access_token": "345678mnopqr", "organization_id": str(uuid4()), "repository": "myrepo.net"}
140
+ )()
141
+
142
+ vellum_client.container_images.push_container_image.side_effect = ApiError(
143
+ status_code=500, body="Internal Server Error"
144
+ )
145
+
146
+ runner = CliRunner()
147
+ result = runner.invoke(cli_main, ["image", "push", "myimage:latest"])
148
+
149
+ assert result.exit_code == 1
150
+ assert "Server error" in result.output
151
+ assert "try again later" in result.output
152
+
153
+
154
+ @patch("subprocess.run")
155
+ @patch("docker.from_env")
156
+ def test_image_push_container_image_other_error(mock_docker_from_env, mock_run, vellum_client, monkeypatch):
157
+ monkeypatch.setenv("VELLUM_API_URL", "https://api.vellum.ai")
158
+ monkeypatch.setenv("VELLUM_API_KEY", "123456abcdef")
159
+
160
+ mock_docker_client = mock_docker_from_env.return_value
161
+ mock_docker_client.images.get.return_value.id = "test-image-id"
162
+ mock_docker_client.images.push.return_value = ["pushed"]
163
+
164
+ mock_run.side_effect = [
165
+ subprocess.CompletedProcess(
166
+ args="", returncode=0, stdout=b'{"manifests": [{"platform": {"architecture": "amd64"}}]}'
167
+ ),
168
+ subprocess.CompletedProcess(args="", returncode=0, stdout=b'[{"RepoDigests": ["test-repo@sha256:abcd1234"]}]'),
169
+ ]
170
+
171
+ vellum_client.container_images.docker_service_token.return_value = type(
172
+ "obj", (object,), {"access_token": "345678mnopqr", "organization_id": str(uuid4()), "repository": "myrepo.net"}
173
+ )()
174
+
175
+ vellum_client.container_images.push_container_image.side_effect = ApiError(
176
+ status_code=429, body="Too Many Requests"
177
+ )
178
+
179
+ runner = CliRunner()
180
+ result = runner.invoke(cli_main, ["image", "push", "myimage:latest"])
181
+
182
+ assert result.exit_code == 1
183
+ assert "API request failed" in result.output
184
+ assert "HTTP 429" in result.output
@@ -890,6 +890,7 @@ def test_pull__module_name_from_deployment_name(vellum_client):
890
890
  assert f.read() == "print('hello')"
891
891
 
892
892
 
893
+ @pytest.mark.usefixtures("mock_module")
893
894
  def test_pull__invalid_zip_file(vellum_client):
894
895
  workflow_deployment = "test-workflow-deployment-id"
895
896
 
@@ -903,11 +904,12 @@ def test_pull__invalid_zip_file(vellum_client):
903
904
  # THEN the command returns an error
904
905
  assert result.exit_code == 1
905
906
  assert (
906
- str(result.exception)
907
- == "The API we tried to pull from returned an invalid zip file. Please make sure your `VELLUM_API_URL` environment variable is set correctly." # noqa: E501
907
+ "Invalid response format" in result.output
908
+ or "Please verify your `VELLUM_API_URL` environment variable is set correctly" in result.output
908
909
  )
909
910
 
910
911
 
912
+ @pytest.mark.usefixtures("mock_module")
911
913
  def test_pull__json_decode_error(vellum_client):
912
914
  workflow_deployment = "test-workflow-deployment-id"
913
915
 
@@ -927,11 +929,12 @@ def test_pull__json_decode_error(vellum_client):
927
929
  # THEN the command returns an error
928
930
  assert result.exit_code == 1
929
931
  assert (
930
- str(result.exception)
931
- == "The API we tried to pull is invalid. Please make sure your `VELLUM_API_URL` environment variable is set correctly." # noqa: E501
932
+ "API request failed" in result.output
933
+ or "Please verify your `VELLUM_API_URL` environment variable is set correctly" in result.output
932
934
  )
933
935
 
934
936
 
937
+ @pytest.mark.usefixtures("mock_module")
935
938
  def test_pull__unauthorized_error_path(vellum_client):
936
939
  workflow_deployment = "test-workflow-deployment-id"
937
940
 
@@ -948,9 +951,10 @@ def test_pull__unauthorized_error_path(vellum_client):
948
951
 
949
952
  # THEN the command returns an error
950
953
  assert result.exit_code == 1
951
- assert str(result.exception) == "Please make sure your `VELLUM_API_KEY` environment variable is set correctly."
954
+ assert "Please make sure your `VELLUM_API_KEY` environment variable is set correctly" in result.output
952
955
 
953
956
 
957
+ @pytest.mark.usefixtures("mock_module")
954
958
  def test_pull__unexpected_error_path(vellum_client):
955
959
  workflow_deployment = "test-workflow-deployment-id"
956
960
 
@@ -968,9 +972,8 @@ def test_pull__unexpected_error_path(vellum_client):
968
972
  # THEN the command returns an error
969
973
  assert result.exit_code == 1
970
974
  assert (
971
- str(result.exception)
972
- == """The Pull API failed with an unexpected error. \
973
- Please try again later and contact support if the problem persists."""
975
+ "Server error occurred" in result.output
976
+ or "Please try again in a few moments. If the problem persists, contact Vellum support" in result.output
974
977
  )
975
978
 
976
979