vellum-ai 0.14.65__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/client/README.md +1 -1
- vellum/client/core/client_wrapper.py +1 -1
- vellum/client/types/document_read.py +0 -1
- vellum/client/types/folder_entity_prompt_sandbox_data.py +1 -0
- vellum/client/types/folder_entity_workflow_sandbox_data.py +1 -0
- vellum/workflows/expressions/accessor.py +22 -5
- vellum/workflows/expressions/tests/test_accessor.py +189 -0
- vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/test_inline_prompt_node.py +26 -23
- vellum/workflows/nodes/displayable/inline_prompt_node/node.py +4 -14
- vellum/workflows/nodes/experimental/tool_calling_node/utils.py +4 -13
- {vellum_ai-0.14.65.dist-info → vellum_ai-0.14.66.dist-info}/METADATA +2 -2
- {vellum_ai-0.14.65.dist-info → vellum_ai-0.14.66.dist-info}/RECORD +19 -17
- vellum_cli/image_push.py +62 -7
- vellum_cli/pull.py +38 -9
- vellum_cli/tests/test_image_push_error_handling.py +184 -0
- vellum_cli/tests/test_pull.py +11 -8
- {vellum_ai-0.14.65.dist-info → vellum_ai-0.14.66.dist-info}/LICENSE +0 -0
- {vellum_ai-0.14.65.dist-info → vellum_ai-0.14.66.dist-info}/WHEEL +0 -0
- {vellum_ai-0.14.65.dist-info → vellum_ai-0.14.66.dist-info}/entry_points.txt +0 -0
vellum/client/README.md
CHANGED
@@ -20,7 +20,7 @@ pip install vellum-ai
|
|
20
20
|
|
21
21
|
## Reference
|
22
22
|
|
23
|
-
A full reference for this library is available [here](
|
23
|
+
A full reference for this library is available [here](https://github.com/vellum-ai/vellum-python-sdks/blob/HEAD/./reference.md).
|
24
24
|
|
25
25
|
## Usage
|
26
26
|
|
@@ -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.14.
|
21
|
+
"X-Fern-SDK-Version": "0.14.66",
|
22
22
|
}
|
23
23
|
headers["X-API-KEY"] = self.api_key
|
24
24
|
return headers
|
@@ -32,7 +32,6 @@ class DocumentRead(UniversalBaseModel):
|
|
32
32
|
"""
|
33
33
|
|
34
34
|
original_file_url: typing.Optional[str] = None
|
35
|
-
processed_file_url: typing.Optional[str] = None
|
36
35
|
document_to_document_indexes: typing.List[DocumentDocumentToDocumentIndex]
|
37
36
|
metadata: typing.Optional[typing.Dict[str, typing.Optional[typing.Any]]] = pydantic.Field(default=None)
|
38
37
|
"""
|
@@ -35,20 +35,37 @@ class AccessorExpression(BaseDescriptor[Any]):
|
|
35
35
|
if isinstance(self._field, int):
|
36
36
|
raise InvalidExpressionException("Cannot access field by index on a dataclass")
|
37
37
|
|
38
|
-
|
38
|
+
try:
|
39
|
+
return getattr(base, self._field)
|
40
|
+
except AttributeError:
|
41
|
+
raise InvalidExpressionException(f"Field '{self._field}' not found on dataclass {type(base).__name__}")
|
39
42
|
|
40
43
|
if isinstance(base, BaseModel):
|
41
44
|
if isinstance(self._field, int):
|
42
45
|
raise InvalidExpressionException("Cannot access field by index on a BaseModel")
|
43
46
|
|
44
|
-
|
47
|
+
try:
|
48
|
+
return getattr(base, self._field)
|
49
|
+
except AttributeError:
|
50
|
+
raise InvalidExpressionException(f"Field '{self._field}' not found on BaseModel {type(base).__name__}")
|
45
51
|
|
46
52
|
if isinstance(base, Mapping):
|
47
|
-
|
53
|
+
try:
|
54
|
+
return base[self._field]
|
55
|
+
except KeyError:
|
56
|
+
raise InvalidExpressionException(f"Key '{self._field}' not found in mapping")
|
48
57
|
|
49
58
|
if isinstance(base, Sequence):
|
50
|
-
|
51
|
-
|
59
|
+
try:
|
60
|
+
index = int(self._field)
|
61
|
+
return base[index]
|
62
|
+
except (IndexError, ValueError):
|
63
|
+
if isinstance(self._field, int) or (isinstance(self._field, str) and self._field.lstrip("-").isdigit()):
|
64
|
+
raise InvalidExpressionException(
|
65
|
+
f"Index {self._field} is out of bounds for sequence of length {len(base)}"
|
66
|
+
)
|
67
|
+
else:
|
68
|
+
raise InvalidExpressionException(f"Invalid index '{self._field}' for sequence access")
|
52
69
|
|
53
70
|
raise InvalidExpressionException(f"Cannot get field {self._field} from {base}")
|
54
71
|
|
@@ -0,0 +1,189 @@
|
|
1
|
+
import pytest
|
2
|
+
from dataclasses import dataclass
|
3
|
+
|
4
|
+
from pydantic import BaseModel
|
5
|
+
|
6
|
+
from vellum.workflows.descriptors.exceptions import InvalidExpressionException
|
7
|
+
from vellum.workflows.expressions.accessor import AccessorExpression
|
8
|
+
from vellum.workflows.references.constant import ConstantValueReference
|
9
|
+
from vellum.workflows.state.base import BaseState
|
10
|
+
|
11
|
+
|
12
|
+
@dataclass
|
13
|
+
class TestDataclass:
|
14
|
+
name: str
|
15
|
+
value: int
|
16
|
+
|
17
|
+
|
18
|
+
class TestBaseModel(BaseModel):
|
19
|
+
name: str
|
20
|
+
value: int
|
21
|
+
|
22
|
+
|
23
|
+
class TestState(BaseState):
|
24
|
+
pass
|
25
|
+
|
26
|
+
|
27
|
+
def test_accessor_expression_dict_valid_key():
|
28
|
+
state = TestState()
|
29
|
+
base_ref = ConstantValueReference({"name": "test", "value": 42})
|
30
|
+
accessor = AccessorExpression(base=base_ref, field="name")
|
31
|
+
|
32
|
+
result = accessor.resolve(state)
|
33
|
+
|
34
|
+
assert result == "test"
|
35
|
+
|
36
|
+
|
37
|
+
def test_accessor_expression_dict_invalid_key():
|
38
|
+
state = TestState()
|
39
|
+
base_ref = ConstantValueReference({"name": "test", "value": 42})
|
40
|
+
accessor = AccessorExpression(base=base_ref, field="missing_key")
|
41
|
+
|
42
|
+
with pytest.raises(InvalidExpressionException) as exc_info:
|
43
|
+
accessor.resolve(state)
|
44
|
+
|
45
|
+
assert "Key 'missing_key' not found in mapping" in str(exc_info.value)
|
46
|
+
|
47
|
+
|
48
|
+
def test_accessor_expression_list_valid_index():
|
49
|
+
state = TestState()
|
50
|
+
base_ref = ConstantValueReference(["first", "second", "third"])
|
51
|
+
accessor = AccessorExpression(base=base_ref, field=1)
|
52
|
+
|
53
|
+
result = accessor.resolve(state)
|
54
|
+
|
55
|
+
assert result == "second"
|
56
|
+
|
57
|
+
|
58
|
+
def test_accessor_expression_list_invalid_index():
|
59
|
+
state = TestState()
|
60
|
+
base_ref = ConstantValueReference(["first", "second"])
|
61
|
+
accessor = AccessorExpression(base=base_ref, field=5)
|
62
|
+
|
63
|
+
with pytest.raises(InvalidExpressionException) as exc_info:
|
64
|
+
accessor.resolve(state)
|
65
|
+
|
66
|
+
assert "Index 5 is out of bounds for sequence of length 2" in str(exc_info.value)
|
67
|
+
|
68
|
+
|
69
|
+
def test_accessor_expression_list_negative_index():
|
70
|
+
state = TestState()
|
71
|
+
base_ref = ConstantValueReference(["first", "second", "third"])
|
72
|
+
accessor = AccessorExpression(base=base_ref, field=-1)
|
73
|
+
|
74
|
+
result = accessor.resolve(state)
|
75
|
+
|
76
|
+
assert result == "third"
|
77
|
+
|
78
|
+
|
79
|
+
def test_accessor_expression_list_invalid_negative_index():
|
80
|
+
state = TestState()
|
81
|
+
base_ref = ConstantValueReference(["first", "second"])
|
82
|
+
accessor = AccessorExpression(base=base_ref, field=-5)
|
83
|
+
|
84
|
+
with pytest.raises(InvalidExpressionException) as exc_info:
|
85
|
+
accessor.resolve(state)
|
86
|
+
|
87
|
+
assert "Index -5 is out of bounds for sequence of length 2" in str(exc_info.value)
|
88
|
+
|
89
|
+
|
90
|
+
def test_accessor_expression_list_string_index():
|
91
|
+
state = TestState()
|
92
|
+
base_ref = ConstantValueReference(["first", "second", "third"])
|
93
|
+
accessor = AccessorExpression(base=base_ref, field="1")
|
94
|
+
|
95
|
+
result = accessor.resolve(state)
|
96
|
+
|
97
|
+
assert result == "second"
|
98
|
+
|
99
|
+
|
100
|
+
def test_accessor_expression_list_invalid_string_index():
|
101
|
+
state = TestState()
|
102
|
+
base_ref = ConstantValueReference(["first", "second"])
|
103
|
+
accessor = AccessorExpression(base=base_ref, field="invalid")
|
104
|
+
|
105
|
+
with pytest.raises(InvalidExpressionException) as exc_info:
|
106
|
+
accessor.resolve(state)
|
107
|
+
|
108
|
+
assert "Invalid index 'invalid' for sequence access" in str(exc_info.value)
|
109
|
+
|
110
|
+
|
111
|
+
def test_accessor_expression_dataclass_valid_field():
|
112
|
+
state = TestState()
|
113
|
+
test_obj = TestDataclass(name="test", value=42)
|
114
|
+
base_ref = ConstantValueReference(test_obj)
|
115
|
+
accessor = AccessorExpression(base=base_ref, field="name")
|
116
|
+
|
117
|
+
result = accessor.resolve(state)
|
118
|
+
|
119
|
+
assert result == "test"
|
120
|
+
|
121
|
+
|
122
|
+
def test_accessor_expression_dataclass_invalid_field():
|
123
|
+
state = TestState()
|
124
|
+
test_obj = TestDataclass(name="test", value=42)
|
125
|
+
base_ref = ConstantValueReference(test_obj)
|
126
|
+
accessor = AccessorExpression(base=base_ref, field="missing_field")
|
127
|
+
|
128
|
+
with pytest.raises(InvalidExpressionException) as exc_info:
|
129
|
+
accessor.resolve(state)
|
130
|
+
|
131
|
+
assert "Field 'missing_field' not found on dataclass TestDataclass" in str(exc_info.value)
|
132
|
+
|
133
|
+
|
134
|
+
def test_accessor_expression_basemodel_valid_field():
|
135
|
+
state = TestState()
|
136
|
+
test_obj = TestBaseModel(name="test", value=42)
|
137
|
+
base_ref = ConstantValueReference(test_obj)
|
138
|
+
accessor = AccessorExpression(base=base_ref, field="name")
|
139
|
+
|
140
|
+
result = accessor.resolve(state)
|
141
|
+
|
142
|
+
assert result == "test"
|
143
|
+
|
144
|
+
|
145
|
+
def test_accessor_expression_basemodel_invalid_field():
|
146
|
+
state = TestState()
|
147
|
+
test_obj = TestBaseModel(name="test", value=42)
|
148
|
+
base_ref = ConstantValueReference(test_obj)
|
149
|
+
accessor = AccessorExpression(base=base_ref, field="missing_field")
|
150
|
+
|
151
|
+
with pytest.raises(InvalidExpressionException) as exc_info:
|
152
|
+
accessor.resolve(state)
|
153
|
+
|
154
|
+
assert "Field 'missing_field' not found on BaseModel TestBaseModel" in str(exc_info.value)
|
155
|
+
|
156
|
+
|
157
|
+
def test_accessor_expression_dataclass_index_access():
|
158
|
+
state = TestState()
|
159
|
+
test_obj = TestDataclass(name="test", value=42)
|
160
|
+
base_ref = ConstantValueReference(test_obj)
|
161
|
+
accessor = AccessorExpression(base=base_ref, field=0)
|
162
|
+
|
163
|
+
with pytest.raises(InvalidExpressionException) as exc_info:
|
164
|
+
accessor.resolve(state)
|
165
|
+
|
166
|
+
assert "Cannot access field by index on a dataclass" in str(exc_info.value)
|
167
|
+
|
168
|
+
|
169
|
+
def test_accessor_expression_basemodel_index_access():
|
170
|
+
state = TestState()
|
171
|
+
test_obj = TestBaseModel(name="test", value=42)
|
172
|
+
base_ref = ConstantValueReference(test_obj)
|
173
|
+
accessor = AccessorExpression(base=base_ref, field=0)
|
174
|
+
|
175
|
+
with pytest.raises(InvalidExpressionException) as exc_info:
|
176
|
+
accessor.resolve(state)
|
177
|
+
|
178
|
+
assert "Cannot access field by index on a BaseModel" in str(exc_info.value)
|
179
|
+
|
180
|
+
|
181
|
+
def test_accessor_expression_unsupported_type():
|
182
|
+
state = TestState()
|
183
|
+
base_ref = ConstantValueReference(42)
|
184
|
+
accessor = AccessorExpression(base=base_ref, field="field")
|
185
|
+
|
186
|
+
with pytest.raises(InvalidExpressionException) as exc_info:
|
187
|
+
accessor.resolve(state)
|
188
|
+
|
189
|
+
assert "Cannot get field field from 42" in str(exc_info.value)
|
vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/test_inline_prompt_node.py
CHANGED
@@ -192,7 +192,30 @@ def test_validation_with_extra_variables(vellum_adhoc_prompt_client):
|
|
192
192
|
]
|
193
193
|
|
194
194
|
|
195
|
-
|
195
|
+
@pytest.mark.parametrize(
|
196
|
+
"custom_parameters,test_description",
|
197
|
+
[
|
198
|
+
(
|
199
|
+
{
|
200
|
+
"json_mode": False,
|
201
|
+
"json_schema": {
|
202
|
+
"name": "get_result",
|
203
|
+
"schema": {
|
204
|
+
"type": "object",
|
205
|
+
"required": ["result"],
|
206
|
+
"properties": {"result": {"type": "string", "description": ""}},
|
207
|
+
},
|
208
|
+
},
|
209
|
+
},
|
210
|
+
"with json_schema configured",
|
211
|
+
),
|
212
|
+
(
|
213
|
+
{},
|
214
|
+
"without json_mode or json_schema configured",
|
215
|
+
),
|
216
|
+
],
|
217
|
+
)
|
218
|
+
def test_inline_prompt_node__json_output(vellum_adhoc_prompt_client, custom_parameters, test_description):
|
196
219
|
"""Confirm that InlinePromptNodes output the expected JSON when run."""
|
197
220
|
|
198
221
|
# GIVEN a node that subclasses InlinePromptNode
|
@@ -214,17 +237,7 @@ def test_inline_prompt_node__json_output(vellum_adhoc_prompt_client):
|
|
214
237
|
frequency_penalty=0.0,
|
215
238
|
presence_penalty=0.0,
|
216
239
|
logit_bias=None,
|
217
|
-
custom_parameters=
|
218
|
-
"json_mode": False,
|
219
|
-
"json_schema": {
|
220
|
-
"name": "get_result",
|
221
|
-
"schema": {
|
222
|
-
"type": "object",
|
223
|
-
"required": ["result"],
|
224
|
-
"properties": {"result": {"type": "string", "description": ""}},
|
225
|
-
},
|
226
|
-
},
|
227
|
-
},
|
240
|
+
custom_parameters=custom_parameters,
|
228
241
|
)
|
229
242
|
|
230
243
|
# AND a known JSON response from invoking an inline prompt
|
@@ -284,17 +297,7 @@ def test_inline_prompt_node__json_output(vellum_adhoc_prompt_client):
|
|
284
297
|
frequency_penalty=0.0,
|
285
298
|
presence_penalty=0.0,
|
286
299
|
logit_bias=None,
|
287
|
-
custom_parameters=
|
288
|
-
"json_mode": False,
|
289
|
-
"json_schema": {
|
290
|
-
"name": "get_result",
|
291
|
-
"schema": {
|
292
|
-
"type": "object",
|
293
|
-
"required": ["result"],
|
294
|
-
"properties": {"result": {"type": "string", "description": ""}},
|
295
|
-
},
|
296
|
-
},
|
297
|
-
},
|
300
|
+
custom_parameters=custom_parameters,
|
298
301
|
),
|
299
302
|
request_options=mock.ANY,
|
300
303
|
settings=None,
|
@@ -48,26 +48,16 @@ class InlinePromptNode(BaseInlinePromptNode[StateType]):
|
|
48
48
|
string_outputs = []
|
49
49
|
json_output = None
|
50
50
|
|
51
|
-
should_parse_json = False
|
52
|
-
if hasattr(self, "parameters"):
|
53
|
-
custom_params = self.parameters.custom_parameters
|
54
|
-
if custom_params and isinstance(custom_params, dict):
|
55
|
-
json_schema = custom_params.get("json_schema", {})
|
56
|
-
if (isinstance(json_schema, dict) and "schema" in json_schema) or custom_params.get("json_mode", {}):
|
57
|
-
should_parse_json = True
|
58
|
-
|
59
51
|
for output in outputs:
|
60
52
|
if output.value is None:
|
61
53
|
continue
|
62
54
|
|
63
55
|
if output.type == "STRING":
|
64
56
|
string_outputs.append(output.value)
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
except (json.JSONDecodeError, TypeError):
|
70
|
-
pass
|
57
|
+
try:
|
58
|
+
json_output = json.loads(output.value)
|
59
|
+
except (json.JSONDecodeError, TypeError):
|
60
|
+
pass
|
71
61
|
elif output.type == "JSON":
|
72
62
|
string_outputs.append(json.dumps(output.value, indent=4))
|
73
63
|
json_output = output.value
|
@@ -15,7 +15,6 @@ from vellum.client.types.string_chat_message_content import StringChatMessageCon
|
|
15
15
|
from vellum.client.types.variable_prompt_block import VariablePromptBlock
|
16
16
|
from vellum.workflows.errors.types import WorkflowErrorCode
|
17
17
|
from vellum.workflows.exceptions import NodeException
|
18
|
-
from vellum.workflows.inputs.base import BaseInputs
|
19
18
|
from vellum.workflows.nodes.bases import BaseNode
|
20
19
|
from vellum.workflows.nodes.displayable.code_execution_node.node import CodeExecutionNode
|
21
20
|
from vellum.workflows.nodes.displayable.inline_prompt_node.node import InlinePromptNode
|
@@ -143,24 +142,16 @@ def create_function_node(
|
|
143
142
|
packages: Optional list of packages to install for code execution (only used for regular functions)
|
144
143
|
runtime: The runtime to use for code execution (default: "PYTHON_3_11_6")
|
145
144
|
"""
|
146
|
-
|
147
145
|
if is_workflow_class(function):
|
148
|
-
|
146
|
+
# Create a class-level wrapper that calls the original function
|
149
147
|
def execute_function(self) -> BaseNode.Outputs:
|
150
148
|
outputs = self.state.meta.node_outputs.get(tool_router_node.Outputs.text)
|
151
|
-
|
149
|
+
|
152
150
|
outputs = json.loads(outputs)
|
153
151
|
arguments = outputs["arguments"]
|
154
152
|
|
155
|
-
#
|
156
|
-
|
157
|
-
"Inputs",
|
158
|
-
(BaseInputs,),
|
159
|
-
{"__annotations__": {k: type(v) for k, v in arguments.items()}},
|
160
|
-
)
|
161
|
-
|
162
|
-
# Create an instance with arguments
|
163
|
-
inputs_instance = Inputs(**arguments)
|
153
|
+
# Call the function based on its type
|
154
|
+
inputs_instance = function.get_inputs_class()(**arguments)
|
164
155
|
|
165
156
|
workflow = function()
|
166
157
|
terminal_event = workflow.run(
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: vellum-ai
|
3
|
-
Version: 0.14.
|
3
|
+
Version: 0.14.66
|
4
4
|
Summary:
|
5
5
|
License: MIT
|
6
6
|
Requires-Python: >=3.9,<4.0
|
@@ -21,7 +21,7 @@ Classifier: Programming Language :: Python :: 3.8
|
|
21
21
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
22
22
|
Classifier: Typing :: Typed
|
23
23
|
Requires-Dist: Jinja2 (>=3.1.0,<4.0.0)
|
24
|
-
Requires-Dist: click (>=8.1.
|
24
|
+
Requires-Dist: click (>=8.1.7,<9.0.0)
|
25
25
|
Requires-Dist: docker (>=7.1.0,<8.0.0)
|
26
26
|
Requires-Dist: httpx (>=0.21.2)
|
27
27
|
Requires-Dist: openai (>=1.0.0,<2.0.0)
|
@@ -3,20 +3,21 @@ vellum_cli/README.md,sha256=2NudRoLzWxNKqnuVy1JuQ7DerIaxWGYkrH8kMd-asIE,90
|
|
3
3
|
vellum_cli/__init__.py,sha256=2_6oGoVcLFUh4L63Kz4SBL4Y6XevJ70oYbg7BJ3cb5Q,12569
|
4
4
|
vellum_cli/aliased_group.py,sha256=ugW498j0yv4ALJ8vS9MsO7ctDW7Jlir9j6nE_uHAP8c,3363
|
5
5
|
vellum_cli/config.py,sha256=v5BmZ-t_v4Jmqd7KVuQMZF2pRI-rbMspSkVYXIRoTmI,9448
|
6
|
-
vellum_cli/image_push.py,sha256=
|
6
|
+
vellum_cli/image_push.py,sha256=mjK3Fj3_MIhIGmurYVz_OC-hCoAef5LqDb0OppKJIJc,9909
|
7
7
|
vellum_cli/init.py,sha256=WpnMXPItPmh0f0bBGIer3p-e5gu8DUGwSArT_FuoMEw,5093
|
8
8
|
vellum_cli/logger.py,sha256=dcM_OmgqXLo93vDYswO5ylyUQQcTfnA5GTd5tbIt3wM,1446
|
9
9
|
vellum_cli/ping.py,sha256=p_BCCRjgPhng6JktuECtkDQLbhopt6JpmrtGoLnLJT8,1161
|
10
|
-
vellum_cli/pull.py,sha256=
|
10
|
+
vellum_cli/pull.py,sha256=udYyPlJ6VKDdh78rApNJOZgxHl82fcV6iGnRPSdX1LY,14750
|
11
11
|
vellum_cli/push.py,sha256=wxRlFu2mYW9SvwODYxwajri1mDQ2be0n-9i0d9QAc30,10194
|
12
12
|
vellum_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
13
|
vellum_cli/tests/conftest.py,sha256=AFYZryKA2qnUuCPBxBKmHLFoPiE0WhBFFej9tNwSHdc,1526
|
14
14
|
vellum_cli/tests/test_config.py,sha256=uvKGDc8BoVyT9_H0Z-g8469zVxomn6Oi3Zj-vK7O_wU,2631
|
15
15
|
vellum_cli/tests/test_image_push.py,sha256=QM-JlR_aJappvwbCLteQZZf76sd7SE1sRj3armvFK-I,5706
|
16
|
+
vellum_cli/tests/test_image_push_error_handling.py,sha256=_Wjfkn1orI2K4Ahzqz4u8T13or7NOX01K4BtcTuTIOM,7107
|
16
17
|
vellum_cli/tests/test_init.py,sha256=8UOc_ThfouR4ja5cCl_URuLk7ohr9JXfCnG4yka1OUQ,18754
|
17
18
|
vellum_cli/tests/test_main.py,sha256=qDZG-aQauPwBwM6A2DIu1494n47v3pL28XakTbLGZ-k,272
|
18
19
|
vellum_cli/tests/test_ping.py,sha256=3ucVRThEmTadlV9LrJdCCrr1Ofj3rOjG6ue0BNR2UC0,2523
|
19
|
-
vellum_cli/tests/test_pull.py,sha256=
|
20
|
+
vellum_cli/tests/test_pull.py,sha256=91aOAYNzYcJuIrTwRvGX_Qa1_UOmdCfVAfvZVfwCKZ4,49902
|
20
21
|
vellum_cli/tests/test_push.py,sha256=I8XICg3pUb3yxAFLXziVHHf5CRm354LO-uUfwtca3bU,33897
|
21
22
|
vellum_ee/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
22
23
|
vellum_ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -130,11 +131,11 @@ vellum_ee/workflows/tests/test_display_meta.py,sha256=DIzjNbwK1-4mlttPML6NskQ4rP
|
|
130
131
|
vellum_ee/workflows/tests/test_server.py,sha256=SsOkS6sGO7uGC4mxvk4iv8AtcXs058P9hgFHzTWmpII,14519
|
131
132
|
vellum_ee/workflows/tests/test_virtual_files.py,sha256=TJEcMR0v2S8CkloXNmCHA0QW0K6pYNGaIjraJz7sFvY,2762
|
132
133
|
vellum/__init__.py,sha256=U4MVBzBx8I8-rOG_feluSz5U8_hD969e85F4cRgmVv4,42104
|
133
|
-
vellum/client/README.md,sha256=
|
134
|
+
vellum/client/README.md,sha256=CuGUYnaE0Imt0KqQ4sIPaUghCjLHkF3DdEvZWu14-8s,4807
|
134
135
|
vellum/client/__init__.py,sha256=AYopGv2ZRVn3zsU8_km6KOvEHDbXiTPCVuYVI7bWvdA,120166
|
135
136
|
vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
|
136
137
|
vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
|
137
|
-
vellum/client/core/client_wrapper.py,sha256=
|
138
|
+
vellum/client/core/client_wrapper.py,sha256=gluL9LJQaydz9m87P7bWACAysxbElOZWqF6JrbjOjDw,1869
|
138
139
|
vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
|
139
140
|
vellum/client/core/file.py,sha256=d4NNbX8XvXP32z8KpK2Xovv33nFfruIrpz0QWxlgpZk,2663
|
140
141
|
vellum/client/core/http_client.py,sha256=Z77OIxIbL4OAB2IDqjRq_sYa5yNYAWfmdhdCSSvh6Y4,19552
|
@@ -288,7 +289,7 @@ vellum/client/types/document_index_indexing_config_request.py,sha256=Wt-ys1o_acH
|
|
288
289
|
vellum/client/types/document_index_read.py,sha256=ePngiRszr65HLl9D0_FUdhAdMe84nRwyM3cKbr8rFpg,1177
|
289
290
|
vellum/client/types/document_processing_state.py,sha256=ISlurj7jQzwHzxPzDZTqeAIgSIIGMBBPgcOSoe04pTU,211
|
290
291
|
vellum/client/types/document_prompt_block.py,sha256=sgFxN48PILFuuF2KUIwks6PbJ3XH6sCE_8ydLEE_doU,1019
|
291
|
-
vellum/client/types/document_read.py,sha256=
|
292
|
+
vellum/client/types/document_read.py,sha256=U3YcFLF9exeZYAhLErEdAc_vVTtWZ8efXDKmmcabshg,1661
|
292
293
|
vellum/client/types/document_status.py,sha256=GD_TSoFmZUBJnPl-chAmaQFzQ2_TYO3PSqi3-9QfEHE,122
|
293
294
|
vellum/client/types/document_vellum_value.py,sha256=a8WQhyntwy80iN9j8L9F5v6Jmq1L4j0ETJo9c9VGabs,768
|
294
295
|
vellum/client/types/document_vellum_value_request.py,sha256=utpoRMMVhMIsa4S4ZOaOr2lX76BgrOVolPxCwy9-pUw,797
|
@@ -329,11 +330,11 @@ vellum/client/types/folder_entity_document_index_data.py,sha256=UceusLf7dLYDHq4Z
|
|
329
330
|
vellum/client/types/folder_entity_folder.py,sha256=1GWfyHdenyAI2GXiouIbnFhPK4ADqZGFh-6fpp_C6-U,792
|
330
331
|
vellum/client/types/folder_entity_folder_data.py,sha256=JLYkhvRY63-ojNY84lfRTaUg25KjslSE-iNAC2NYCTI,674
|
331
332
|
vellum/client/types/folder_entity_prompt_sandbox.py,sha256=7CGVcK5maoqO7CC7sFfi6F5X0QWdHVbEygqLyci_VDY,839
|
332
|
-
vellum/client/types/folder_entity_prompt_sandbox_data.py,sha256=
|
333
|
+
vellum/client/types/folder_entity_prompt_sandbox_data.py,sha256=LMTtOshCdp0MpYHdKtrtBSdpXw9B9id5BfIpkg9SsY4,826
|
333
334
|
vellum/client/types/folder_entity_test_suite.py,sha256=M5sb7ntUX2VsCyD0AYnz7dalt7G1ejOYQZvsUU33q2w,811
|
334
335
|
vellum/client/types/folder_entity_test_suite_data.py,sha256=RGITy3Pip6NeIb77YGavDWgRr8GLFDB9sXrTU_Dm2OI,719
|
335
336
|
vellum/client/types/folder_entity_workflow_sandbox.py,sha256=gFmkwKZGh5O9-9y4w6VGK1IRsKLf7NZ4SCC-pgucm8M,853
|
336
|
-
vellum/client/types/folder_entity_workflow_sandbox_data.py,sha256=
|
337
|
+
vellum/client/types/folder_entity_workflow_sandbox_data.py,sha256=4_kzQSDrFXVJCSF1st73jo0LxKOicn_gE82XAk-QKJI,828
|
337
338
|
vellum/client/types/fulfilled_ad_hoc_execute_prompt_event.py,sha256=19rFjVrzobaaAQhZlP_WGV9f_Rwrz4EPRXbT2aYkaJw,1016
|
338
339
|
vellum/client/types/fulfilled_execute_prompt_event.py,sha256=E-iBwlMWvwQyRfzqPlgtX8KurE3IYsTRd5vWjXtbOmk,994
|
339
340
|
vellum/client/types/fulfilled_execute_prompt_response.py,sha256=nKhDk2ZloCuE0uijX5XOE63_cq8PBo4UWs4hK4e3jUE,1227
|
@@ -1505,7 +1506,7 @@ vellum/workflows/events/types.py,sha256=LwgFlMRbptJvdPtPO1POUtGtbhGw7BSuvgHxNSgS
|
|
1505
1506
|
vellum/workflows/events/workflow.py,sha256=i9JSCANjAhf5uc57gYspdII2V2OLItbc0BfT8yB9mF0,7728
|
1506
1507
|
vellum/workflows/exceptions.py,sha256=NiBiR3ggfmPxBVqD-H1SqmjI-7mIn0EStSN1BqApvCM,1213
|
1507
1508
|
vellum/workflows/expressions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1508
|
-
vellum/workflows/expressions/accessor.py,sha256=
|
1509
|
+
vellum/workflows/expressions/accessor.py,sha256=1kY0sYfednQ_u0kh1Df2WTsDREVIOzU7UuZc5jvj5xw,2861
|
1509
1510
|
vellum/workflows/expressions/and_.py,sha256=I7lNqrUM3-m_5hmjjiMhaHhJtKcLj39kEFVWPDOqwfo,916
|
1510
1511
|
vellum/workflows/expressions/begins_with.py,sha256=FnWsQXbENm0ZwkfEP7dR8Qx4_MMrzj6C1yqAV2KaNHw,1123
|
1511
1512
|
vellum/workflows/expressions/between.py,sha256=dVeddT6YA91eOAlE1Utg7C7gnCiYE7WP-dg17yXUeAY,1492
|
@@ -1535,6 +1536,7 @@ vellum/workflows/expressions/not_in.py,sha256=pFvwkFPsn3WJw61ssFgM2U1dqWEeglfz4F
|
|
1535
1536
|
vellum/workflows/expressions/or_.py,sha256=s-8YdMSSCDS2yijR38kguwok3iqmDMMgDYKV93b4O4s,914
|
1536
1537
|
vellum/workflows/expressions/parse_json.py,sha256=xsk6j3HF7bU1yF6fwt5P9Ugcyd5D9ZXrdng11FRilUI,1088
|
1537
1538
|
vellum/workflows/expressions/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1539
|
+
vellum/workflows/expressions/tests/test_accessor.py,sha256=ijmGjS1pipdurAgYGmfV8Gcle4HIJ5lQHVG18xHjNiQ,5839
|
1538
1540
|
vellum/workflows/expressions/tests/test_expressions.py,sha256=3b6k8xs-CItBBw95NygFLUNoNPKxI4VA1GyWbkMtqyI,11623
|
1539
1541
|
vellum/workflows/expressions/tests/test_parse_json.py,sha256=zpB_qE5_EwWQL7ULQUJm0o1PRSfWZdAqZNW6Ah13oJE,1059
|
1540
1542
|
vellum/workflows/graph/__init__.py,sha256=3sHlay5d_-uD7j3QJXiGl0WHFZZ_QScRvgyDhN2GhHY,74
|
@@ -1589,7 +1591,7 @@ vellum/workflows/nodes/displayable/bases/inline_prompt_node/__init__.py,sha256=H
|
|
1589
1591
|
vellum/workflows/nodes/displayable/bases/inline_prompt_node/constants.py,sha256=fnjiRWLoRlC4Puo5oQcpZD5Hd-EesxsAo9l5tGAkpZQ,270
|
1590
1592
|
vellum/workflows/nodes/displayable/bases/inline_prompt_node/node.py,sha256=cD2RzOX9WE6xTKgm09dsEw4xHATZbOjeGyYCSdl8fjU,11785
|
1591
1593
|
vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1592
|
-
vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/test_inline_prompt_node.py,sha256=
|
1594
|
+
vellum/workflows/nodes/displayable/bases/inline_prompt_node/tests/test_inline_prompt_node.py,sha256=terQtPqLGiKKGtU7zbkM3XkbVddLQI87bBj31M0zaRE,21356
|
1593
1595
|
vellum/workflows/nodes/displayable/bases/prompt_deployment_node.py,sha256=0a40fkkZkFMmZN0CsWf6EP_y1H6x36EGa3WcfVNyOsM,9797
|
1594
1596
|
vellum/workflows/nodes/displayable/bases/search_node.py,sha256=3UtbqY3QO4kzfJHbmUNZGnEEfJmaoiF892u8H6TGjp8,5381
|
1595
1597
|
vellum/workflows/nodes/displayable/bases/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -1616,7 +1618,7 @@ vellum/workflows/nodes/displayable/guardrail_node/test_node.py,sha256=SAGv6hSFcB
|
|
1616
1618
|
vellum/workflows/nodes/displayable/guardrail_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1617
1619
|
vellum/workflows/nodes/displayable/guardrail_node/tests/test_node.py,sha256=X2pd6TI8miYxIa7rgvs1pHTEreyWcf77EyR0_Jsa700,2055
|
1618
1620
|
vellum/workflows/nodes/displayable/inline_prompt_node/__init__.py,sha256=gSUOoEZLlrx35-tQhSAd3An8WDwBqyiQh-sIebLU9wU,74
|
1619
|
-
vellum/workflows/nodes/displayable/inline_prompt_node/node.py,sha256=
|
1621
|
+
vellum/workflows/nodes/displayable/inline_prompt_node/node.py,sha256=ST_V1Z3-7lzZ__Ntfm6iry70u9D3GD2a2vxsl2d4Q_c,2843
|
1620
1622
|
vellum/workflows/nodes/displayable/inline_prompt_node/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1621
1623
|
vellum/workflows/nodes/displayable/inline_prompt_node/tests/test_node.py,sha256=bBHs90mV5SZ3rJPAL0wx4WWyawUA406LgMPOdvpZC_A,10923
|
1622
1624
|
vellum/workflows/nodes/displayable/merge_node/__init__.py,sha256=J8IC08dSH7P76wKlNuxe1sn7toNGtSQdFirUbtPDEs0,60
|
@@ -1646,7 +1648,7 @@ vellum/workflows/nodes/experimental/openai_chat_completion_node/node.py,sha256=c
|
|
1646
1648
|
vellum/workflows/nodes/experimental/tool_calling_node/__init__.py,sha256=S7OzT3I4cyOU5Beoz87nPwCejCMP2FsHBFL8OcVmxJ4,118
|
1647
1649
|
vellum/workflows/nodes/experimental/tool_calling_node/node.py,sha256=FkhaJccpCbx2be_IZ5V2v6Lo-jPJ0WgSC5tveLvAW4A,5774
|
1648
1650
|
vellum/workflows/nodes/experimental/tool_calling_node/tests/test_node.py,sha256=sxG26mOwt4N36RLoPJ-ngginPqC5qFzD_kGj9izdCFI,1833
|
1649
|
-
vellum/workflows/nodes/experimental/tool_calling_node/utils.py,sha256=
|
1651
|
+
vellum/workflows/nodes/experimental/tool_calling_node/utils.py,sha256=vB-tdN-44iVrTFvwGLVZW3dv62imIp5fPo0FYCW0l3M,9815
|
1650
1652
|
vellum/workflows/nodes/mocks.py,sha256=a1FjWEIocseMfjzM-i8DNozpUsaW0IONRpZmXBoWlyc,10455
|
1651
1653
|
vellum/workflows/nodes/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1652
1654
|
vellum/workflows/nodes/tests/test_mocks.py,sha256=mfPvrs75PKcsNsbJLQAN6PDFoVqs9TmQxpdyFKDdO60,7837
|
@@ -1712,8 +1714,8 @@ vellum/workflows/workflows/event_filters.py,sha256=GSxIgwrX26a1Smfd-6yss2abGCnad
|
|
1712
1714
|
vellum/workflows/workflows/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1713
1715
|
vellum/workflows/workflows/tests/test_base_workflow.py,sha256=fROqff6AZpCIzaSwOKSdtYy4XR0UZQ6ejxL3RJOSJVs,20447
|
1714
1716
|
vellum/workflows/workflows/tests/test_context.py,sha256=VJBUcyWVtMa_lE5KxdhgMu0WYNYnUQUDvTF7qm89hJ0,2333
|
1715
|
-
vellum_ai-0.14.
|
1716
|
-
vellum_ai-0.14.
|
1717
|
-
vellum_ai-0.14.
|
1718
|
-
vellum_ai-0.14.
|
1719
|
-
vellum_ai-0.14.
|
1717
|
+
vellum_ai-0.14.66.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
|
1718
|
+
vellum_ai-0.14.66.dist-info/METADATA,sha256=bkr1Oj0n0HAwEiTLSb4lZEiDQQM_mtpz1uXZ1Lt7VQM,5556
|
1719
|
+
vellum_ai-0.14.66.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
1720
|
+
vellum_ai-0.14.66.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
|
1721
|
+
vellum_ai-0.14.66.dist-info/RECORD,,
|
vellum_cli/image_push.py
CHANGED
@@ -9,9 +9,10 @@ import docker
|
|
9
9
|
from docker import DockerClient
|
10
10
|
from dotenv import load_dotenv
|
11
11
|
|
12
|
+
from vellum.client.core.api_error import ApiError
|
12
13
|
from vellum.workflows.vellum_client import create_vellum_client, create_vellum_environment
|
13
14
|
from vellum_cli.config import DEFAULT_WORKSPACE_CONFIG, load_vellum_cli_config
|
14
|
-
from vellum_cli.logger import load_cli_logger
|
15
|
+
from vellum_cli.logger import handle_cli_error, load_cli_logger
|
15
16
|
|
16
17
|
_SUPPORTED_ARCHITECTURE = "amd64"
|
17
18
|
|
@@ -82,7 +83,34 @@ def image_push_command(image: str, tags: Optional[List[str]] = None, workspace:
|
|
82
83
|
exit(1)
|
83
84
|
else:
|
84
85
|
logger.info("Authenticating...")
|
85
|
-
|
86
|
+
try:
|
87
|
+
auth = vellum_client.container_images.docker_service_token()
|
88
|
+
except ApiError as e:
|
89
|
+
if e.status_code == 401 or e.status_code == 403:
|
90
|
+
handle_cli_error(
|
91
|
+
logger,
|
92
|
+
title="Authentication failed",
|
93
|
+
message="Unable to authenticate with Vellum API. Please check your API key.",
|
94
|
+
suggestion="Make sure your VELLUM_API_KEY environment variable is set correctly.",
|
95
|
+
)
|
96
|
+
return
|
97
|
+
elif e.status_code == 500:
|
98
|
+
handle_cli_error(
|
99
|
+
logger,
|
100
|
+
title="Server error",
|
101
|
+
message="The Vellum API failed with an unexpected server error.",
|
102
|
+
suggestion="Please try again later and contact support if the problem persists.",
|
103
|
+
)
|
104
|
+
return
|
105
|
+
else:
|
106
|
+
handle_cli_error(
|
107
|
+
logger,
|
108
|
+
title="API request failed",
|
109
|
+
message=f"Failed to get Docker service token from Vellum API (HTTP {e.status_code}).",
|
110
|
+
suggestion="Please check your configuration and try again. If the problem persists, "
|
111
|
+
"contact support.",
|
112
|
+
)
|
113
|
+
return
|
86
114
|
|
87
115
|
docker_client.login(
|
88
116
|
username="oauth2accesstoken",
|
@@ -143,11 +171,38 @@ def image_push_command(image: str, tags: Optional[List[str]] = None, workspace:
|
|
143
171
|
|
144
172
|
logger.info(f"Updating Vellum metadata and validating image works in our system with image digest: {sha}...")
|
145
173
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
174
|
+
try:
|
175
|
+
vellum_client.container_images.push_container_image(
|
176
|
+
name=image_name,
|
177
|
+
sha=sha,
|
178
|
+
tags=all_tags,
|
179
|
+
)
|
180
|
+
except ApiError as e:
|
181
|
+
if e.status_code == 401 or e.status_code == 403:
|
182
|
+
handle_cli_error(
|
183
|
+
logger,
|
184
|
+
title="Authentication failed",
|
185
|
+
message="Unable to push container image metadata to Vellum API. Please check your API key.",
|
186
|
+
suggestion="Make sure your VELLUM_API_KEY environment variable is set correctly.",
|
187
|
+
)
|
188
|
+
return
|
189
|
+
elif e.status_code == 500:
|
190
|
+
handle_cli_error(
|
191
|
+
logger,
|
192
|
+
title="Server error",
|
193
|
+
message="The Vellum API failed with an unexpected server error while pushing container image metadata.",
|
194
|
+
suggestion="Please try again later and contact support if the problem persists.",
|
195
|
+
)
|
196
|
+
return
|
197
|
+
else:
|
198
|
+
handle_cli_error(
|
199
|
+
logger,
|
200
|
+
title="API request failed",
|
201
|
+
message=f"Failed to push container image metadata to Vellum API (HTTP {e.status_code}).",
|
202
|
+
suggestion="Please check your configuration and try again. If the problem persists, contact support.",
|
203
|
+
)
|
204
|
+
return
|
205
|
+
|
151
206
|
logger.info(f"Image successfully pushed as {image_name} to vellum with tags: {all_tags}.")
|
152
207
|
|
153
208
|
|
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
|
-
|
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
|
-
|
208
|
-
|
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
|
-
|
212
|
-
|
213
|
-
"
|
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
|
-
|
303
|
-
|
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
|
vellum_cli/tests/test_pull.py
CHANGED
@@ -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
|
-
|
907
|
-
|
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
|
-
|
931
|
-
|
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
|
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
|
-
|
972
|
-
|
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
|
|
File without changes
|
File without changes
|
File without changes
|