vellum-ai 0.11.3__py3-none-any.whl → 0.11.4__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/core/client_wrapper.py +1 -1
- vellum/workflows/graph/graph.py +23 -4
- vellum/workflows/graph/tests/test_graph.py +25 -0
- vellum/workflows/ports/port.py +3 -0
- {vellum_ai-0.11.3.dist-info → vellum_ai-0.11.4.dist-info}/METADATA +1 -1
- {vellum_ai-0.11.3.dist-info → vellum_ai-0.11.4.dist-info}/RECORD +14 -14
- vellum_cli/config.py +36 -0
- vellum_cli/push.py +4 -0
- vellum_cli/tests/conftest.py +19 -4
- vellum_cli/tests/test_pull.py +85 -7
- vellum_cli/tests/test_push.py +8 -8
- {vellum_ai-0.11.3.dist-info → vellum_ai-0.11.4.dist-info}/LICENSE +0 -0
- {vellum_ai-0.11.3.dist-info → vellum_ai-0.11.4.dist-info}/WHEEL +0 -0
- {vellum_ai-0.11.3.dist-info → vellum_ai-0.11.4.dist-info}/entry_points.txt +0 -0
@@ -17,7 +17,7 @@ class BaseClientWrapper:
|
|
17
17
|
headers: typing.Dict[str, str] = {
|
18
18
|
"X-Fern-Language": "Python",
|
19
19
|
"X-Fern-SDK-Name": "vellum-ai",
|
20
|
-
"X-Fern-SDK-Version": "0.11.
|
20
|
+
"X-Fern-SDK-Version": "0.11.4",
|
21
21
|
}
|
22
22
|
headers["X_API_KEY"] = self.api_key
|
23
23
|
return headers
|
vellum/workflows/graph/graph.py
CHANGED
@@ -12,11 +12,13 @@ if TYPE_CHECKING:
|
|
12
12
|
GraphTargetOfSets = Union[
|
13
13
|
Set[NodeType],
|
14
14
|
Set["Graph"],
|
15
|
-
Set[
|
15
|
+
Set["Port"],
|
16
|
+
Set[Union[Type["BaseNode"], "Graph", "Port"]],
|
16
17
|
]
|
17
18
|
|
18
19
|
GraphTarget = Union[
|
19
20
|
Type["BaseNode"],
|
21
|
+
"Port",
|
20
22
|
"Graph",
|
21
23
|
GraphTargetOfSets,
|
22
24
|
]
|
@@ -53,9 +55,13 @@ class Graph:
|
|
53
55
|
entrypoints.update(target._entrypoints)
|
54
56
|
edges.update(target._edges)
|
55
57
|
terminals.update(target._terminals)
|
56
|
-
|
58
|
+
elif hasattr(target, "Ports"):
|
57
59
|
entrypoints.update({port for port in target.Ports})
|
58
60
|
terminals.update({port for port in target.Ports})
|
61
|
+
else:
|
62
|
+
# target is a Port
|
63
|
+
entrypoints.update({target})
|
64
|
+
terminals.update({target})
|
59
65
|
|
60
66
|
return Graph(entrypoints=entrypoints, edges=list(edges), terminals=terminals)
|
61
67
|
|
@@ -77,11 +83,16 @@ class Graph:
|
|
77
83
|
self._extend_edges(elem.edges)
|
78
84
|
for other_terminal in elem._terminals:
|
79
85
|
new_terminals.add(other_terminal)
|
80
|
-
|
86
|
+
elif hasattr(elem, "Ports"):
|
81
87
|
midgraph = final_output_node >> elem
|
82
88
|
self._extend_edges(midgraph.edges)
|
83
89
|
for other_terminal in elem.Ports:
|
84
90
|
new_terminals.add(other_terminal)
|
91
|
+
else:
|
92
|
+
# elem is a Port
|
93
|
+
midgraph = final_output_node >> elem
|
94
|
+
self._extend_edges(midgraph.edges)
|
95
|
+
new_terminals.add(elem)
|
85
96
|
self._terminals = new_terminals
|
86
97
|
return self
|
87
98
|
|
@@ -93,10 +104,18 @@ class Graph:
|
|
93
104
|
self._terminals = other._terminals
|
94
105
|
return self
|
95
106
|
|
107
|
+
if hasattr(other, "Ports"):
|
108
|
+
for final_output_node in self._terminals:
|
109
|
+
subgraph = final_output_node >> other
|
110
|
+
self._extend_edges(subgraph.edges)
|
111
|
+
self._terminals = {port for port in other.Ports}
|
112
|
+
return self
|
113
|
+
|
114
|
+
# other is a Port
|
96
115
|
for final_output_node in self._terminals:
|
97
116
|
subgraph = final_output_node >> other
|
98
117
|
self._extend_edges(subgraph.edges)
|
99
|
-
self._terminals = {
|
118
|
+
self._terminals = {other}
|
100
119
|
return self
|
101
120
|
|
102
121
|
@property
|
@@ -435,3 +435,28 @@ def test_graph__set_to_node():
|
|
435
435
|
|
436
436
|
# AND two edges
|
437
437
|
assert len(list(graph.edges)) == 2
|
438
|
+
|
439
|
+
|
440
|
+
def test_graph__node_to_port():
|
441
|
+
# GIVEN two nodes, one with a port
|
442
|
+
class SourceNode(BaseNode):
|
443
|
+
pass
|
444
|
+
|
445
|
+
class MiddleNode(BaseNode):
|
446
|
+
class Ports(BaseNode.Ports):
|
447
|
+
custom = Port.on_else()
|
448
|
+
|
449
|
+
class TargetNode(BaseNode):
|
450
|
+
pass
|
451
|
+
|
452
|
+
# WHEN we create a graph from the source node to the target node
|
453
|
+
graph = SourceNode >> MiddleNode.Ports.custom >> TargetNode
|
454
|
+
|
455
|
+
# THEN the graph has the source node as the entrypoint
|
456
|
+
assert set(graph.entrypoints) == {SourceNode}
|
457
|
+
|
458
|
+
# AND three nodes
|
459
|
+
assert len(list(graph.nodes)) == 3
|
460
|
+
|
461
|
+
# AND two edges
|
462
|
+
assert len(list(graph.edges)) == 2
|
vellum/workflows/ports/port.py
CHANGED
@@ -52,6 +52,9 @@ class Port:
|
|
52
52
|
if isinstance(other, set) or isinstance(other, Graph):
|
53
53
|
return Graph.from_port(self) >> other
|
54
54
|
|
55
|
+
if isinstance(other, Port):
|
56
|
+
return Graph.from_port(self) >> Graph.from_port(other)
|
57
|
+
|
55
58
|
edge = Edge(from_port=self, to_node=other)
|
56
59
|
if edge not in self._edges:
|
57
60
|
self._edges.append(edge)
|
@@ -2,17 +2,17 @@ vellum_cli/CONTRIBUTING.md,sha256=FtDC7BGxSeMnwCXAUssFsAIElXtmJE-O5Z7BpolcgvI,29
|
|
2
2
|
vellum_cli/README.md,sha256=2NudRoLzWxNKqnuVy1JuQ7DerIaxWGYkrH8kMd-asIE,90
|
3
3
|
vellum_cli/__init__.py,sha256=pftUQ6FiyfebNEB8xcfwzLjpfFDCAiH15xHBU6xr_wY,6733
|
4
4
|
vellum_cli/aliased_group.py,sha256=ugW498j0yv4ALJ8vS9MsO7ctDW7Jlir9j6nE_uHAP8c,3363
|
5
|
-
vellum_cli/config.py,sha256=
|
5
|
+
vellum_cli/config.py,sha256=wJQnv3tCgu1BOugg0AOP94yQ-x1yAg8juX_QoFN9Y7w,5223
|
6
6
|
vellum_cli/image_push.py,sha256=SJwhwWJsLjwGNezNVd_oCVpFMfPsAB3dfLWmriZZUtw,4419
|
7
7
|
vellum_cli/logger.py,sha256=PuRFa0WCh4sAGFS5aqWB0QIYpS6nBWwPJrIXpWxugV4,1022
|
8
8
|
vellum_cli/pull.py,sha256=6wIiorqSx2rmR6atZJHHBuLSviocxK_n0DQxEDGmCzo,4008
|
9
|
-
vellum_cli/push.py,sha256=
|
9
|
+
vellum_cli/push.py,sha256=kbvlzZ9KnkS5DxxKHQP5ZvHHk1-CbCDg9LqnIRAWyt4,5258
|
10
10
|
vellum_cli/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
|
-
vellum_cli/tests/conftest.py,sha256=
|
11
|
+
vellum_cli/tests/conftest.py,sha256=eFGwBxib3Nki830lIFintB0b6r4x8T_KMnmzhlTY5x0,1337
|
12
12
|
vellum_cli/tests/test_config.py,sha256=uvKGDc8BoVyT9_H0Z-g8469zVxomn6Oi3Zj-vK7O_wU,2631
|
13
13
|
vellum_cli/tests/test_main.py,sha256=qDZG-aQauPwBwM6A2DIu1494n47v3pL28XakTbLGZ-k,272
|
14
|
-
vellum_cli/tests/test_pull.py,sha256=
|
15
|
-
vellum_cli/tests/test_push.py,sha256=
|
14
|
+
vellum_cli/tests/test_pull.py,sha256=N6ZphvHYGokclbpbTpgOmpu_m2GtocDEesbdeHFjO5Y,13194
|
15
|
+
vellum_cli/tests/test_push.py,sha256=V2iGcskh2X3OHj2uV5Vx_BhmtyfmUkyx0lrp8DDOExc,5824
|
16
16
|
vellum_ee/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
17
17
|
vellum_ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
18
18
|
vellum_ee/workflows/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -78,7 +78,7 @@ vellum/client/README.md,sha256=JkCJjmMZl4jrPj46pkmL9dpK4gSzQQmP5I7z4aME4LY,4749
|
|
78
78
|
vellum/client/__init__.py,sha256=o4m7iRZWEV8rP3GkdaztHAjNmjxjWERlarviFoHzuKI,110927
|
79
79
|
vellum/client/core/__init__.py,sha256=SQ85PF84B9MuKnBwHNHWemSGuy-g_515gFYNFhvEE0I,1438
|
80
80
|
vellum/client/core/api_error.py,sha256=RE8LELok2QCjABadECTvtDp7qejA1VmINCh6TbqPwSE,426
|
81
|
-
vellum/client/core/client_wrapper.py,sha256=
|
81
|
+
vellum/client/core/client_wrapper.py,sha256=2kb_aZbadYgFs7fHp9vZCvmnzLko_U_VPmjLg8Jnnyw,1890
|
82
82
|
vellum/client/core/datetime_utils.py,sha256=nBys2IsYrhPdszxGKCNRPSOCwa-5DWOHG95FB8G9PKo,1047
|
83
83
|
vellum/client/core/file.py,sha256=X9IbmkZmB2bB_DpmZAO3crWdXagOakAyn6UCOCImCPg,2322
|
84
84
|
vellum/client/core/http_client.py,sha256=R0pQpCppnEtxccGvXl4uJ76s7ro_65Fo_erlNNLp_AI,19228
|
@@ -1251,9 +1251,9 @@ vellum/workflows/expressions/not_between.py,sha256=H2huRR95D9Qb7lCHmK7BcK-Ug-E1g
|
|
1251
1251
|
vellum/workflows/expressions/not_in.py,sha256=OQkN5G1E6VoTDpoLvx7X3GbohLlqEAYHV0rVVUV7ow4,1049
|
1252
1252
|
vellum/workflows/expressions/or_.py,sha256=s-8YdMSSCDS2yijR38kguwok3iqmDMMgDYKV93b4O4s,914
|
1253
1253
|
vellum/workflows/graph/__init__.py,sha256=3sHlay5d_-uD7j3QJXiGl0WHFZZ_QScRvgyDhN2GhHY,74
|
1254
|
-
vellum/workflows/graph/graph.py,sha256=
|
1254
|
+
vellum/workflows/graph/graph.py,sha256=rmPPs2gtQhaYIuZKETdWrdacgwQRvT6soj12UOl1Tm0,5316
|
1255
1255
|
vellum/workflows/graph/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
1256
|
-
vellum/workflows/graph/tests/test_graph.py,sha256=
|
1256
|
+
vellum/workflows/graph/tests/test_graph.py,sha256=PQI1lO2IY-izBSbkwxjKR5a1z-aN-ieHV_p0m-g5eAM,11256
|
1257
1257
|
vellum/workflows/inputs/__init__.py,sha256=AbFEteIYEvCb14fM3EK7bhM-40-6s494rSlIhQ4Dsss,62
|
1258
1258
|
vellum/workflows/inputs/base.py,sha256=1kMgr0WqCYdWUqgFvgSoAMw2067FAlgwhGXLgbIOrLY,2391
|
1259
1259
|
vellum/workflows/logging.py,sha256=_a217XogktV4Ncz6xKFz7WfYmZAzkfVRVuC0rWob8ls,437
|
@@ -1334,7 +1334,7 @@ vellum/workflows/outputs/__init__.py,sha256=AyZ4pRh_ACQIGvkf0byJO46EDnSix1ZCAXfv
|
|
1334
1334
|
vellum/workflows/outputs/base.py,sha256=a7W6rNSDSawwGAXYjNTF2iHb9lnZu7WFSOagZIyy__k,7976
|
1335
1335
|
vellum/workflows/ports/__init__.py,sha256=bZuMt-R7z5bKwpu4uPW7LlJeePOQWmCcDSXe5frUY5g,101
|
1336
1336
|
vellum/workflows/ports/node_ports.py,sha256=g4A-8iUAvEJSkaWppbvzAR8XU02R9U-qLN4rP2Kq4Aw,2743
|
1337
|
-
vellum/workflows/ports/port.py,sha256=
|
1337
|
+
vellum/workflows/ports/port.py,sha256=4vTvhe9FE5SO5WwwAmJm2ApBBBxZv9fsyUoikPIgiaI,2947
|
1338
1338
|
vellum/workflows/ports/utils.py,sha256=pEjVNJKw9LhD_cFN-o0MWBOW2ejno7jv26qqzjLxwS4,1662
|
1339
1339
|
vellum/workflows/references/__init__.py,sha256=glHFC1VfXmcbNvH5VzFbkT03d8_D7MMcvEcsUBrzLIs,591
|
1340
1340
|
vellum/workflows/references/environment_variable.py,sha256=7FFtiKfc4eyVkkfUbhc666OBNDqvFlMoNQEYmGpEVVE,661
|
@@ -1377,8 +1377,8 @@ vellum/workflows/vellum_client.py,sha256=ODrq_TSl-drX2aezXegf7pizpWDVJuTXH-j6528
|
|
1377
1377
|
vellum/workflows/workflows/__init__.py,sha256=KY45TqvavCCvXIkyCFMEc0dc6jTMOUci93U2DUrlZYc,66
|
1378
1378
|
vellum/workflows/workflows/base.py,sha256=mnI-kZ78yt7u6NFSTUo-tYjDnarP-RJ7uZjwjCn6PCQ,16795
|
1379
1379
|
vellum/workflows/workflows/event_filters.py,sha256=-uQcMB7IpPd-idMku8f2QNVhPXPFWo6FZLlGjRf8rCo,1996
|
1380
|
-
vellum_ai-0.11.
|
1381
|
-
vellum_ai-0.11.
|
1382
|
-
vellum_ai-0.11.
|
1383
|
-
vellum_ai-0.11.
|
1384
|
-
vellum_ai-0.11.
|
1380
|
+
vellum_ai-0.11.4.dist-info/LICENSE,sha256=hOypcdt481qGNISA784bnAGWAE6tyIf9gc2E78mYC3E,1574
|
1381
|
+
vellum_ai-0.11.4.dist-info/METADATA,sha256=jsthltxAbW8_ohEuJ38axnhG6JBG-DLG6p2bwAgcAwA,5128
|
1382
|
+
vellum_ai-0.11.4.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
|
1383
|
+
vellum_ai-0.11.4.dist-info/entry_points.txt,sha256=HCH4yc_V3J_nDv3qJzZ_nYS8llCHZViCDP1ejgCc5Ak,42
|
1384
|
+
vellum_ai-0.11.4.dist-info/RECORD,,
|
vellum_cli/config.py
CHANGED
@@ -20,6 +20,15 @@ class WorkflowDeploymentConfig(UniversalBaseModel):
|
|
20
20
|
description: Optional[str] = None
|
21
21
|
release_tags: Optional[List[str]] = None
|
22
22
|
|
23
|
+
def merge(self, other: "WorkflowDeploymentConfig") -> "WorkflowDeploymentConfig":
|
24
|
+
return WorkflowDeploymentConfig(
|
25
|
+
id=self.id or other.id,
|
26
|
+
label=self.label or other.label,
|
27
|
+
name=self.name or other.name,
|
28
|
+
description=self.description or other.description,
|
29
|
+
release_tags=self.release_tags or other.release_tags,
|
30
|
+
)
|
31
|
+
|
23
32
|
|
24
33
|
class WorkflowConfig(UniversalBaseModel):
|
25
34
|
module: str
|
@@ -28,10 +37,37 @@ class WorkflowConfig(UniversalBaseModel):
|
|
28
37
|
deployments: List[WorkflowDeploymentConfig] = field(default_factory=list)
|
29
38
|
|
30
39
|
def merge(self, other: "WorkflowConfig") -> "WorkflowConfig":
|
40
|
+
self_deployment_by_id = {
|
41
|
+
deployment.id: deployment for deployment in self.deployments if deployment.id is not None
|
42
|
+
}
|
43
|
+
other_deployment_by_id = {
|
44
|
+
deployment.id: deployment for deployment in other.deployments if deployment.id is not None
|
45
|
+
}
|
46
|
+
all_ids = sorted(set(self_deployment_by_id.keys()).union(set(other_deployment_by_id.keys())))
|
47
|
+
merged_deployments = []
|
48
|
+
for id in all_ids:
|
49
|
+
self_deployment = self_deployment_by_id.get(id)
|
50
|
+
other_deployment = other_deployment_by_id.get(id)
|
51
|
+
if self_deployment and other_deployment:
|
52
|
+
merged_deployments.append(self_deployment.merge(other_deployment))
|
53
|
+
elif self_deployment:
|
54
|
+
merged_deployments.append(self_deployment)
|
55
|
+
elif other_deployment:
|
56
|
+
merged_deployments.append(other_deployment)
|
57
|
+
|
58
|
+
for deployment in self.deployments:
|
59
|
+
if deployment.id is None:
|
60
|
+
merged_deployments.append(deployment)
|
61
|
+
|
62
|
+
for deployment in other.deployments:
|
63
|
+
if deployment.id is None:
|
64
|
+
merged_deployments.append(deployment)
|
65
|
+
|
31
66
|
return WorkflowConfig(
|
32
67
|
module=self.module,
|
33
68
|
workflow_sandbox_id=self.workflow_sandbox_id or other.workflow_sandbox_id,
|
34
69
|
ignore=self.ignore or other.ignore,
|
70
|
+
deployments=merged_deployments,
|
35
71
|
)
|
36
72
|
|
37
73
|
|
vellum_cli/push.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
from importlib import metadata
|
1
2
|
import io
|
2
3
|
import json
|
3
4
|
import os
|
@@ -50,6 +51,9 @@ def push_command(
|
|
50
51
|
workflow = BaseWorkflow.load_from_module(workflow_config.module)
|
51
52
|
workflow_display = get_workflow_display(base_display_class=VellumWorkflowDisplay, workflow_class=workflow)
|
52
53
|
exec_config = workflow_display.serialize()
|
54
|
+
exec_config["runner_config"] = {
|
55
|
+
"sdk_version": metadata.version("vellum-ai"),
|
56
|
+
}
|
53
57
|
|
54
58
|
label = snake_to_title_case(workflow_config.module.split(".")[-1])
|
55
59
|
|
vellum_cli/tests/conftest.py
CHANGED
@@ -1,19 +1,29 @@
|
|
1
1
|
import pytest
|
2
|
+
from dataclasses import dataclass
|
2
3
|
import os
|
3
4
|
import shutil
|
4
5
|
import tempfile
|
5
6
|
from uuid import uuid4
|
6
|
-
from typing import Any, Callable, Dict, Generator
|
7
|
+
from typing import Any, Callable, Dict, Generator
|
7
8
|
|
8
9
|
import tomli_w
|
9
10
|
|
10
11
|
|
12
|
+
@dataclass
|
13
|
+
class MockModuleResult:
|
14
|
+
temp_dir: str
|
15
|
+
module: str
|
16
|
+
set_pyproject_toml: Callable[[Dict[str, Any]], None]
|
17
|
+
workflow_sandbox_id: str
|
18
|
+
|
19
|
+
|
11
20
|
@pytest.fixture
|
12
|
-
def mock_module() -> Generator[
|
21
|
+
def mock_module() -> Generator[MockModuleResult, None, None]:
|
13
22
|
current_dir = os.getcwd()
|
14
23
|
temp_dir = tempfile.mkdtemp()
|
15
24
|
os.chdir(temp_dir)
|
16
25
|
module = "examples.mock"
|
26
|
+
workflow_sandbox_id = str(uuid4())
|
17
27
|
|
18
28
|
def set_pyproject_toml(vellum_config: Dict[str, Any]) -> None:
|
19
29
|
pyproject_toml_path = os.path.join(temp_dir, "pyproject.toml")
|
@@ -28,13 +38,18 @@ def mock_module() -> Generator[Tuple[str, str, Callable[[Dict[str, Any]], None]]
|
|
28
38
|
"workflows": [
|
29
39
|
{
|
30
40
|
"module": module,
|
31
|
-
"workflow_sandbox_id":
|
41
|
+
"workflow_sandbox_id": workflow_sandbox_id,
|
32
42
|
}
|
33
43
|
]
|
34
44
|
}
|
35
45
|
)
|
36
46
|
|
37
|
-
yield
|
47
|
+
yield MockModuleResult(
|
48
|
+
temp_dir=temp_dir,
|
49
|
+
module=module,
|
50
|
+
set_pyproject_toml=set_pyproject_toml,
|
51
|
+
workflow_sandbox_id=workflow_sandbox_id,
|
52
|
+
)
|
38
53
|
|
39
54
|
os.chdir(current_dir)
|
40
55
|
shutil.rmtree(temp_dir)
|
vellum_cli/tests/test_pull.py
CHANGED
@@ -37,7 +37,8 @@ def _zip_file_map(file_map: dict[str, str]) -> bytes:
|
|
37
37
|
)
|
38
38
|
def test_pull(vellum_client, mock_module, base_command):
|
39
39
|
# GIVEN a module on the user's filesystem
|
40
|
-
temp_dir
|
40
|
+
temp_dir = mock_module.temp_dir
|
41
|
+
module = mock_module.module
|
41
42
|
|
42
43
|
# AND the workflow pull API call returns a zip file
|
43
44
|
vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
|
@@ -58,7 +59,9 @@ def test_pull(vellum_client, mock_module, base_command):
|
|
58
59
|
|
59
60
|
def test_pull__second_module(vellum_client, mock_module):
|
60
61
|
# GIVEN a module on the user's filesystem
|
61
|
-
temp_dir
|
62
|
+
temp_dir = mock_module.temp_dir
|
63
|
+
module = mock_module.module
|
64
|
+
set_pyproject_toml = mock_module.set_pyproject_toml
|
62
65
|
|
63
66
|
# AND the workflow pull API call returns a zip file
|
64
67
|
vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
|
@@ -134,7 +137,7 @@ def test_pull__sandbox_id_with_no_config(vellum_client):
|
|
134
137
|
|
135
138
|
def test_pull__sandbox_id_with_other_workflow_configured(vellum_client, mock_module):
|
136
139
|
# GIVEN a pyproject.toml with a workflow configured
|
137
|
-
temp_dir
|
140
|
+
temp_dir = mock_module.temp_dir
|
138
141
|
|
139
142
|
# AND a different workflow sandbox id
|
140
143
|
workflow_sandbox_id = "87654321-0000-0000-0000-000000000000"
|
@@ -163,7 +166,8 @@ def test_pull__sandbox_id_with_other_workflow_configured(vellum_client, mock_mod
|
|
163
166
|
|
164
167
|
def test_pull__remove_missing_files(vellum_client, mock_module):
|
165
168
|
# GIVEN a module on the user's filesystem
|
166
|
-
temp_dir
|
169
|
+
temp_dir = mock_module.temp_dir
|
170
|
+
module = mock_module.module
|
167
171
|
|
168
172
|
# AND the workflow pull API call returns a zip file
|
169
173
|
vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
|
@@ -192,7 +196,9 @@ def test_pull__remove_missing_files(vellum_client, mock_module):
|
|
192
196
|
|
193
197
|
def test_pull__remove_missing_files__ignore_pattern(vellum_client, mock_module):
|
194
198
|
# GIVEN a module on the user's filesystem
|
195
|
-
temp_dir
|
199
|
+
temp_dir = mock_module.temp_dir
|
200
|
+
module = mock_module.module
|
201
|
+
set_pyproject_toml = mock_module.set_pyproject_toml
|
196
202
|
|
197
203
|
# AND the workflow pull API call returns a zip file
|
198
204
|
vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
|
@@ -243,7 +249,7 @@ def test_pull__remove_missing_files__ignore_pattern(vellum_client, mock_module):
|
|
243
249
|
|
244
250
|
def test_pull__include_json(vellum_client, mock_module):
|
245
251
|
# GIVEN a module on the user's filesystem
|
246
|
-
|
252
|
+
module = mock_module.module
|
247
253
|
|
248
254
|
# AND the workflow pull API call returns a zip file
|
249
255
|
vellum_client.workflows.pull.return_value = iter(
|
@@ -265,7 +271,7 @@ def test_pull__include_json(vellum_client, mock_module):
|
|
265
271
|
|
266
272
|
def test_pull__exclude_code(vellum_client, mock_module):
|
267
273
|
# GIVEN a module on the user's filesystem
|
268
|
-
|
274
|
+
module = mock_module.module
|
269
275
|
|
270
276
|
# AND the workflow pull API call returns a zip file
|
271
277
|
vellum_client.workflows.pull.return_value = iter(
|
@@ -283,3 +289,75 @@ def test_pull__exclude_code(vellum_client, mock_module):
|
|
283
289
|
vellum_client.workflows.pull.assert_called_once()
|
284
290
|
call_args = vellum_client.workflows.pull.call_args.kwargs
|
285
291
|
assert call_args["request_options"]["additional_query_parameters"] == {"exclude_code": True}
|
292
|
+
|
293
|
+
|
294
|
+
def test_pull__sandbox_id_with_other_workflow_deployment_in_lock(vellum_client, mock_module):
|
295
|
+
# GIVEN a pyproject.toml with a workflow configured
|
296
|
+
temp_dir = mock_module.temp_dir
|
297
|
+
module = mock_module.module
|
298
|
+
workflow_sandbox_id = mock_module.workflow_sandbox_id
|
299
|
+
|
300
|
+
# AND there's a workflow deployment in the lock file
|
301
|
+
vellum_lock_json = os.path.join(temp_dir, "vellum.lock.json")
|
302
|
+
with open(vellum_lock_json, "w") as f:
|
303
|
+
json.dump(
|
304
|
+
{
|
305
|
+
"version": "1.0",
|
306
|
+
"workflows": [
|
307
|
+
{
|
308
|
+
"module": module,
|
309
|
+
"workflow_sandbox_id": "0edc07cd-45b9-43e8-99bc-1f181972a857",
|
310
|
+
"ignore": "tests/*",
|
311
|
+
"deployments": [
|
312
|
+
{
|
313
|
+
"id": "7e5a7610-4c46-4bc9-b06e-0fc6a9e28959",
|
314
|
+
"label": None,
|
315
|
+
"name": None,
|
316
|
+
"description": None,
|
317
|
+
"release_tags": None,
|
318
|
+
}
|
319
|
+
],
|
320
|
+
}
|
321
|
+
],
|
322
|
+
},
|
323
|
+
f,
|
324
|
+
)
|
325
|
+
|
326
|
+
# AND a different workflow sandbox id
|
327
|
+
new_workflow_sandbox_id = "87654321-0000-0000-0000-000000000000"
|
328
|
+
|
329
|
+
# AND the workflow pull API call returns a zip file
|
330
|
+
vellum_client.workflows.pull.return_value = iter([_zip_file_map({"workflow.py": "print('hello')"})])
|
331
|
+
|
332
|
+
# WHEN the user runs the pull command with the new workflow sandbox id
|
333
|
+
runner = CliRunner()
|
334
|
+
result = runner.invoke(cli_main, ["workflows", "pull", "--workflow-sandbox-id", new_workflow_sandbox_id])
|
335
|
+
|
336
|
+
# THEN the command returns successfully
|
337
|
+
assert result.exit_code == 0
|
338
|
+
|
339
|
+
# AND the lock file is updated to preserve the deployment and include the new workflow
|
340
|
+
with open(vellum_lock_json) as f:
|
341
|
+
lock_data = json.load(f)
|
342
|
+
assert lock_data["workflows"] == [
|
343
|
+
{
|
344
|
+
"module": module,
|
345
|
+
"workflow_sandbox_id": workflow_sandbox_id,
|
346
|
+
"ignore": "tests/*",
|
347
|
+
"deployments": [
|
348
|
+
{
|
349
|
+
"id": "7e5a7610-4c46-4bc9-b06e-0fc6a9e28959",
|
350
|
+
"label": None,
|
351
|
+
"name": None,
|
352
|
+
"description": None,
|
353
|
+
"release_tags": None,
|
354
|
+
},
|
355
|
+
],
|
356
|
+
},
|
357
|
+
{
|
358
|
+
"module": "workflow_87654321",
|
359
|
+
"workflow_sandbox_id": new_workflow_sandbox_id,
|
360
|
+
"ignore": None,
|
361
|
+
"deployments": [],
|
362
|
+
},
|
363
|
+
]
|
vellum_cli/tests/test_push.py
CHANGED
@@ -29,8 +29,7 @@ def _extract_tar_gz(tar_gz_bytes: bytes) -> dict[str, str]:
|
|
29
29
|
|
30
30
|
def test_push__no_config(mock_module):
|
31
31
|
# GIVEN no config file set
|
32
|
-
|
33
|
-
set_pyproject_toml({"workflows": []})
|
32
|
+
mock_module.set_pyproject_toml({"workflows": []})
|
34
33
|
|
35
34
|
# WHEN calling `vellum push`
|
36
35
|
runner = CliRunner()
|
@@ -44,8 +43,7 @@ def test_push__no_config(mock_module):
|
|
44
43
|
|
45
44
|
def test_push__multiple_workflows_configured__no_module_specified(mock_module):
|
46
45
|
# GIVEN multiple workflows configured
|
47
|
-
|
48
|
-
set_pyproject_toml({"workflows": [{"module": "examples.mock"}, {"module": "examples.mock2"}]})
|
46
|
+
mock_module.set_pyproject_toml({"workflows": [{"module": "examples.mock"}, {"module": "examples.mock2"}]})
|
49
47
|
|
50
48
|
# WHEN calling `vellum push` without a module specified
|
51
49
|
runner = CliRunner()
|
@@ -62,8 +60,8 @@ def test_push__multiple_workflows_configured__no_module_specified(mock_module):
|
|
62
60
|
|
63
61
|
def test_push__multiple_workflows_configured__not_found_module(mock_module):
|
64
62
|
# GIVEN multiple workflows configured
|
65
|
-
|
66
|
-
set_pyproject_toml({"workflows": [{"module": "examples.mock2"}, {"module": "examples.mock3"}]})
|
63
|
+
module = mock_module.module
|
64
|
+
mock_module.set_pyproject_toml({"workflows": [{"module": "examples.mock2"}, {"module": "examples.mock3"}]})
|
67
65
|
|
68
66
|
# WHEN calling `vellum push` with a module that doesn't exist
|
69
67
|
runner = CliRunner()
|
@@ -85,7 +83,8 @@ def test_push__multiple_workflows_configured__not_found_module(mock_module):
|
|
85
83
|
)
|
86
84
|
def test_push__happy_path(mock_module, vellum_client, base_command):
|
87
85
|
# GIVEN a single workflow configured
|
88
|
-
temp_dir
|
86
|
+
temp_dir = mock_module.temp_dir
|
87
|
+
module = mock_module.module
|
89
88
|
|
90
89
|
# AND a workflow exists in the module successfully
|
91
90
|
base_dir = os.path.join(temp_dir, *module.split("."))
|
@@ -134,7 +133,8 @@ class ExampleWorkflow(BaseWorkflow):
|
|
134
133
|
)
|
135
134
|
def test_push__deployment(mock_module, vellum_client, base_command):
|
136
135
|
# GIVEN a single workflow configured
|
137
|
-
temp_dir
|
136
|
+
temp_dir = mock_module.temp_dir
|
137
|
+
module = mock_module.module
|
138
138
|
|
139
139
|
# AND a workflow exists in the module successfully
|
140
140
|
base_dir = os.path.join(temp_dir, *module.split("."))
|
File without changes
|
File without changes
|
File without changes
|