orient_express 0.3.4__tar.gz → 0.4.2__tar.gz
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.
- {orient_express-0.3.4 → orient_express-0.4.2}/PKG-INFO +80 -3
- {orient_express-0.3.4 → orient_express-0.4.2}/README.md +78 -0
- orient_express-0.4.2/orient_express/deployment.py +99 -0
- {orient_express-0.3.4 → orient_express-0.4.2}/pyproject.toml +1 -2
- {orient_express-0.3.4 → orient_express-0.4.2}/orient_express/__init__.py +0 -0
- {orient_express-0.3.4 → orient_express-0.4.2}/orient_express/model_wrapper.py +0 -0
- {orient_express-0.3.4 → orient_express-0.4.2}/orient_express/sklearn_pipeline.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: orient_express
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: A library to simplify model deployment to Vertex AI
|
|
5
5
|
Author: Alexey Zankevich
|
|
6
6
|
Author-email: alex.zankevich@shiftsmart.com
|
|
@@ -12,7 +12,6 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
13
|
Requires-Dist: google-cloud-aiplatform
|
|
14
14
|
Requires-Dist: google-cloud-storage
|
|
15
|
-
Requires-Dist: jupyter (>=1.1.1,<2.0.0)
|
|
16
15
|
Requires-Dist: pandas
|
|
17
16
|
Description-Content-Type: text/markdown
|
|
18
17
|
|
|
@@ -172,3 +171,81 @@ df = pd.DataFrame(titanic_data)
|
|
|
172
171
|
model_wrapper.remote_predict(df)
|
|
173
172
|
```
|
|
174
173
|
|
|
174
|
+
## Pipeline Deployment Function
|
|
175
|
+
|
|
176
|
+
Orient express library also have a helper function to simplify Vertex AI pipeline deployment.
|
|
177
|
+
|
|
178
|
+
Create `deploy.py` script
|
|
179
|
+
```python
|
|
180
|
+
|
|
181
|
+
from orient_express.deployment import deploy_pipeline
|
|
182
|
+
|
|
183
|
+
import argparse
|
|
184
|
+
import conf
|
|
185
|
+
|
|
186
|
+
from pipeline import pipeline
|
|
187
|
+
from orient_express.deployment import deploy_pipeline
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
if __name__ == "__main__":
|
|
191
|
+
parser = argparse.ArgumentParser()
|
|
192
|
+
parser.add_argument("--run-type", required=True)
|
|
193
|
+
|
|
194
|
+
args = parser.parse_args()
|
|
195
|
+
deploy_pipeline(run_type=args.run_type,
|
|
196
|
+
pipeline_dsl=pipeline,
|
|
197
|
+
pipeline_root=conf.PIPELINE_ROOT,
|
|
198
|
+
pipeline_name=conf.PIPELINE_NAME,
|
|
199
|
+
pipeline_display_name=conf.PIPELINE_DISPLAY_NAME,
|
|
200
|
+
pipeline_schedule_name=conf.SCHEDULE_NAME,
|
|
201
|
+
gcp_project=conf.PROJECT_ID,
|
|
202
|
+
gcp_location='us-central1',
|
|
203
|
+
gcp_service_account=conf.SERVICE_ACCOUNT,
|
|
204
|
+
gcp_network=conf.NETWORK_NAME,
|
|
205
|
+
gcp_labels={"team": "ml"})
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
And conf.py, make sure to replace the sample values with yours.
|
|
209
|
+
```python
|
|
210
|
+
|
|
211
|
+
import os
|
|
212
|
+
|
|
213
|
+
BASE_PATH = "gs://pipelines-bucket/vertex-ai/pipelines"
|
|
214
|
+
|
|
215
|
+
PIPELINE_NAME = "my-pipeline"
|
|
216
|
+
PIPELINE_ROOT = f"{BASE_PATH}/{PIPELINE_NAME}"
|
|
217
|
+
PIPELINE_TEMP_ROOT = f"{BASE_PATH}/{PIPELINE_NAME}-temp"
|
|
218
|
+
|
|
219
|
+
PIPELINE_DISPLAY_NAME = "My Pipeline"
|
|
220
|
+
PIPELINE_DESCRIPTION = "My example pipeline"
|
|
221
|
+
|
|
222
|
+
NETWORK_NAME = "project network id"
|
|
223
|
+
|
|
224
|
+
DOCKER_IMAGE = "us-docker.pkg.dev/my-project/my-artifactory/my-pipeline:latest
|
|
225
|
+
BASE_IMAGE = "python:3.11"
|
|
226
|
+
PROJECT_ID = "my-project"
|
|
227
|
+
PROJECT_REGION = "us-central1"
|
|
228
|
+
|
|
229
|
+
SERVICE_ACCOUNT = "my-service-account@my-project.iam.gserviceaccount.com"
|
|
230
|
+
SCHEDULE_NAME = "My Pipeline"
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
For testing it on a local machine, make sure to authorize to GCP first
|
|
234
|
+
```shell
|
|
235
|
+
|
|
236
|
+
gcloud auth application-default login
|
|
237
|
+
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Finally, run the pipeline (it will execute once)
|
|
241
|
+
```shell
|
|
242
|
+
|
|
243
|
+
python deploy.py --run-type single-run
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Or, create a scheduler to run continuously
|
|
247
|
+
```shell
|
|
248
|
+
|
|
249
|
+
python deploy.py --run-type scheduled
|
|
250
|
+
```
|
|
251
|
+
|
|
@@ -153,3 +153,81 @@ df = pd.DataFrame(titanic_data)
|
|
|
153
153
|
|
|
154
154
|
model_wrapper.remote_predict(df)
|
|
155
155
|
```
|
|
156
|
+
|
|
157
|
+
## Pipeline Deployment Function
|
|
158
|
+
|
|
159
|
+
Orient express library also have a helper function to simplify Vertex AI pipeline deployment.
|
|
160
|
+
|
|
161
|
+
Create `deploy.py` script
|
|
162
|
+
```python
|
|
163
|
+
|
|
164
|
+
from orient_express.deployment import deploy_pipeline
|
|
165
|
+
|
|
166
|
+
import argparse
|
|
167
|
+
import conf
|
|
168
|
+
|
|
169
|
+
from pipeline import pipeline
|
|
170
|
+
from orient_express.deployment import deploy_pipeline
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
if __name__ == "__main__":
|
|
174
|
+
parser = argparse.ArgumentParser()
|
|
175
|
+
parser.add_argument("--run-type", required=True)
|
|
176
|
+
|
|
177
|
+
args = parser.parse_args()
|
|
178
|
+
deploy_pipeline(run_type=args.run_type,
|
|
179
|
+
pipeline_dsl=pipeline,
|
|
180
|
+
pipeline_root=conf.PIPELINE_ROOT,
|
|
181
|
+
pipeline_name=conf.PIPELINE_NAME,
|
|
182
|
+
pipeline_display_name=conf.PIPELINE_DISPLAY_NAME,
|
|
183
|
+
pipeline_schedule_name=conf.SCHEDULE_NAME,
|
|
184
|
+
gcp_project=conf.PROJECT_ID,
|
|
185
|
+
gcp_location='us-central1',
|
|
186
|
+
gcp_service_account=conf.SERVICE_ACCOUNT,
|
|
187
|
+
gcp_network=conf.NETWORK_NAME,
|
|
188
|
+
gcp_labels={"team": "ml"})
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
And conf.py, make sure to replace the sample values with yours.
|
|
192
|
+
```python
|
|
193
|
+
|
|
194
|
+
import os
|
|
195
|
+
|
|
196
|
+
BASE_PATH = "gs://pipelines-bucket/vertex-ai/pipelines"
|
|
197
|
+
|
|
198
|
+
PIPELINE_NAME = "my-pipeline"
|
|
199
|
+
PIPELINE_ROOT = f"{BASE_PATH}/{PIPELINE_NAME}"
|
|
200
|
+
PIPELINE_TEMP_ROOT = f"{BASE_PATH}/{PIPELINE_NAME}-temp"
|
|
201
|
+
|
|
202
|
+
PIPELINE_DISPLAY_NAME = "My Pipeline"
|
|
203
|
+
PIPELINE_DESCRIPTION = "My example pipeline"
|
|
204
|
+
|
|
205
|
+
NETWORK_NAME = "project network id"
|
|
206
|
+
|
|
207
|
+
DOCKER_IMAGE = "us-docker.pkg.dev/my-project/my-artifactory/my-pipeline:latest
|
|
208
|
+
BASE_IMAGE = "python:3.11"
|
|
209
|
+
PROJECT_ID = "my-project"
|
|
210
|
+
PROJECT_REGION = "us-central1"
|
|
211
|
+
|
|
212
|
+
SERVICE_ACCOUNT = "my-service-account@my-project.iam.gserviceaccount.com"
|
|
213
|
+
SCHEDULE_NAME = "My Pipeline"
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
For testing it on a local machine, make sure to authorize to GCP first
|
|
217
|
+
```shell
|
|
218
|
+
|
|
219
|
+
gcloud auth application-default login
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Finally, run the pipeline (it will execute once)
|
|
224
|
+
```shell
|
|
225
|
+
|
|
226
|
+
python deploy.py --run-type single-run
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Or, create a scheduler to run continuously
|
|
230
|
+
```shell
|
|
231
|
+
|
|
232
|
+
python deploy.py --run-type scheduled
|
|
233
|
+
```
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import argparse
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from kfp import compiler, dsl
|
|
6
|
+
|
|
7
|
+
import conf
|
|
8
|
+
import google.cloud.aiplatform as aip
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def delete_old_schedules(schedule_name):
|
|
12
|
+
for schedule in aip.PipelineJobSchedule.list(
|
|
13
|
+
filter=f'display_name="{schedule_name}"', order_by="create_time desc"
|
|
14
|
+
):
|
|
15
|
+
logging.info(f"Deleting the old schedule {schedule}")
|
|
16
|
+
schedule.delete()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def deploy_pipeline(
|
|
20
|
+
run_type: str,
|
|
21
|
+
pipeline_dsl: str,
|
|
22
|
+
pipeline_root: str,
|
|
23
|
+
pipeline_name: str,
|
|
24
|
+
pipeline_display_name: str,
|
|
25
|
+
pipeline_schedule_name: str,
|
|
26
|
+
gcp_project: str,
|
|
27
|
+
gcp_location: str,
|
|
28
|
+
gcp_service_account: str,
|
|
29
|
+
gcp_network: str,
|
|
30
|
+
gcp_labels: Optional[dict[str, str]] = {},
|
|
31
|
+
cron_string: Optional[str] = "*/15 * * * *",
|
|
32
|
+
template_path: Optional[str] = "pipeline.yaml",
|
|
33
|
+
pipeline_job_parameters: Optional[dict] = {},
|
|
34
|
+
max_concurrent_run_count: Optional[int] = 1,
|
|
35
|
+
):
|
|
36
|
+
"""
|
|
37
|
+
:param run_type: "single-run" or "scheduled"
|
|
38
|
+
:param pipeline_dsl: pipeline DSL object
|
|
39
|
+
:param pipeline_root: a root folder for the pipelines to keep their intermediate data
|
|
40
|
+
:param pipeline_name: pipeline name
|
|
41
|
+
:param pipeline_display_name: human readable name of the pipeline
|
|
42
|
+
:param cron_string: a string that defines how often the pipeline should run
|
|
43
|
+
:param template_path: a path to a template yaml file (pipeline.yaml by default)
|
|
44
|
+
:param gcp_labels: additional labels to add to the pipeline
|
|
45
|
+
:param pipeline_job_parameters: additional parameters to pass to the pipeline.
|
|
46
|
+
Make sure to set the correct signatures of the pipeline function
|
|
47
|
+
:param max_concurrent_run_count: maximum number of concurrent runs
|
|
48
|
+
"""
|
|
49
|
+
if run_type == "scheduled":
|
|
50
|
+
is_scheduled_run = True
|
|
51
|
+
elif run_type == "single-run":
|
|
52
|
+
is_scheduled_run = False
|
|
53
|
+
else:
|
|
54
|
+
raise Exception(f"Unknown run type {run_type}")
|
|
55
|
+
|
|
56
|
+
print("### Deployment Summary")
|
|
57
|
+
compiler.Compiler().compile(pipeline_dsl, package_path=template_path)
|
|
58
|
+
|
|
59
|
+
aip.init(
|
|
60
|
+
project=gcp_project,
|
|
61
|
+
location=gcp_location,
|
|
62
|
+
service_account=gcp_service_account,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
labels = {"run_type": run_type, "service": pipeline_name}
|
|
66
|
+
labels.update(gcp_labels)
|
|
67
|
+
|
|
68
|
+
# Prepare the pipeline job
|
|
69
|
+
job = aip.PipelineJob(
|
|
70
|
+
enable_caching=False,
|
|
71
|
+
display_name=pipeline_display_name,
|
|
72
|
+
template_path=template_path,
|
|
73
|
+
pipeline_root=pipeline_root,
|
|
74
|
+
location=gcp_location,
|
|
75
|
+
labels=labels,
|
|
76
|
+
parameter_values=pipeline_job_parameters,
|
|
77
|
+
)
|
|
78
|
+
if is_scheduled_run:
|
|
79
|
+
delete_old_schedules(pipeline_schedule_name)
|
|
80
|
+
|
|
81
|
+
pipeline_job_schedule = aip.PipelineJobSchedule(
|
|
82
|
+
pipeline_job=job, display_name=pipeline_schedule_name
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
pipeline_job_schedule.create(
|
|
86
|
+
cron=cron_string,
|
|
87
|
+
max_concurrent_run_count=max_concurrent_run_count,
|
|
88
|
+
service_account=gcp_service_account,
|
|
89
|
+
network=gcp_network,
|
|
90
|
+
)
|
|
91
|
+
else:
|
|
92
|
+
job.submit(
|
|
93
|
+
service_account=gcp_service_account,
|
|
94
|
+
network=gcp_network,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def validate_pipeline(pipeline_dsl, package_path="pipeline.yaml"):
|
|
99
|
+
compiler.Compiler().compile(pipeline_dsl, package_path=package_path)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "orient_express"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.2"
|
|
4
4
|
description = "A library to simplify model deployment to Vertex AI"
|
|
5
5
|
authors = ["Alexey Zankevich <alex.zankevich@shiftsmart.com>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -10,7 +10,6 @@ python = ">=3.9,<3.13"
|
|
|
10
10
|
google-cloud-aiplatform = "*"
|
|
11
11
|
google-cloud-storage = "*"
|
|
12
12
|
pandas = "*"
|
|
13
|
-
jupyter = "^1.1.1"
|
|
14
13
|
|
|
15
14
|
[tool.poetry.dev-dependencies]
|
|
16
15
|
black = "24.10.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|