mlrun 1.7.0rc43__py3-none-any.whl → 1.7.0rc56__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.
Potentially problematic release.
This version of mlrun might be problematic. Click here for more details.
- mlrun/__main__.py +4 -2
- mlrun/artifacts/manager.py +3 -1
- mlrun/common/formatters/__init__.py +1 -0
- mlrun/{model_monitoring/application.py → common/formatters/feature_set.py} +20 -6
- mlrun/common/formatters/run.py +3 -0
- mlrun/common/schemas/__init__.py +1 -0
- mlrun/common/schemas/alert.py +11 -11
- mlrun/common/schemas/auth.py +5 -0
- mlrun/common/schemas/client_spec.py +0 -1
- mlrun/common/schemas/model_monitoring/__init__.py +2 -1
- mlrun/common/schemas/model_monitoring/constants.py +23 -9
- mlrun/common/schemas/model_monitoring/model_endpoints.py +24 -47
- mlrun/common/schemas/notification.py +12 -2
- mlrun/common/schemas/workflow.py +10 -2
- mlrun/config.py +28 -21
- mlrun/data_types/data_types.py +6 -1
- mlrun/datastore/base.py +4 -4
- mlrun/datastore/s3.py +12 -9
- mlrun/datastore/storeytargets.py +9 -6
- mlrun/db/base.py +3 -0
- mlrun/db/httpdb.py +28 -16
- mlrun/db/nopdb.py +24 -4
- mlrun/errors.py +7 -1
- mlrun/execution.py +40 -7
- mlrun/feature_store/api.py +1 -0
- mlrun/feature_store/retrieval/spark_merger.py +7 -7
- mlrun/frameworks/_common/plan.py +3 -3
- mlrun/frameworks/_ml_common/plan.py +1 -1
- mlrun/frameworks/parallel_coordinates.py +2 -3
- mlrun/launcher/client.py +6 -6
- mlrun/model.py +29 -0
- mlrun/model_monitoring/api.py +1 -12
- mlrun/model_monitoring/applications/__init__.py +1 -2
- mlrun/model_monitoring/applications/_application_steps.py +5 -1
- mlrun/model_monitoring/applications/base.py +2 -182
- mlrun/model_monitoring/applications/context.py +2 -9
- mlrun/model_monitoring/applications/evidently_base.py +0 -74
- mlrun/model_monitoring/applications/histogram_data_drift.py +2 -2
- mlrun/model_monitoring/applications/results.py +4 -4
- mlrun/model_monitoring/controller.py +46 -209
- mlrun/model_monitoring/db/stores/base/store.py +1 -0
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +15 -1
- mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +12 -0
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +17 -16
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +49 -39
- mlrun/model_monitoring/helpers.py +13 -15
- mlrun/model_monitoring/writer.py +3 -1
- mlrun/projects/operations.py +11 -8
- mlrun/projects/pipelines.py +35 -16
- mlrun/projects/project.py +52 -24
- mlrun/render.py +3 -3
- mlrun/runtimes/daskjob.py +1 -1
- mlrun/runtimes/kubejob.py +6 -6
- mlrun/runtimes/nuclio/api_gateway.py +12 -0
- mlrun/runtimes/nuclio/application/application.py +3 -3
- mlrun/runtimes/nuclio/function.py +41 -0
- mlrun/runtimes/nuclio/serving.py +2 -2
- mlrun/runtimes/pod.py +19 -13
- mlrun/serving/server.py +2 -0
- mlrun/utils/helpers.py +62 -16
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.0rc43.dist-info → mlrun-1.7.0rc56.dist-info}/METADATA +126 -44
- {mlrun-1.7.0rc43.dist-info → mlrun-1.7.0rc56.dist-info}/RECORD +67 -68
- {mlrun-1.7.0rc43.dist-info → mlrun-1.7.0rc56.dist-info}/WHEEL +1 -1
- mlrun/model_monitoring/evidently_application.py +0 -20
- {mlrun-1.7.0rc43.dist-info → mlrun-1.7.0rc56.dist-info}/LICENSE +0 -0
- {mlrun-1.7.0rc43.dist-info → mlrun-1.7.0rc56.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.0rc43.dist-info → mlrun-1.7.0rc56.dist-info}/top_level.txt +0 -0
|
@@ -18,11 +18,16 @@ from typing import Union
|
|
|
18
18
|
|
|
19
19
|
import pandas as pd
|
|
20
20
|
import taosws
|
|
21
|
+
from taoswswrap.tdengine_connection import (
|
|
22
|
+
Statement,
|
|
23
|
+
TDEngineConnection,
|
|
24
|
+
)
|
|
21
25
|
|
|
22
26
|
import mlrun.common.schemas.model_monitoring as mm_schemas
|
|
23
27
|
import mlrun.model_monitoring.db.tsdb.tdengine.schemas as tdengine_schemas
|
|
24
28
|
import mlrun.model_monitoring.db.tsdb.tdengine.stream_graph_steps
|
|
25
29
|
from mlrun.model_monitoring.db import TSDBConnector
|
|
30
|
+
from mlrun.model_monitoring.db.tsdb.tdengine.schemas import TDEngineSchema
|
|
26
31
|
from mlrun.model_monitoring.helpers import get_invocations_fqn
|
|
27
32
|
from mlrun.utils import logger
|
|
28
33
|
|
|
@@ -52,25 +57,18 @@ class TDEngineConnector(TSDBConnector):
|
|
|
52
57
|
self._init_super_tables()
|
|
53
58
|
|
|
54
59
|
@property
|
|
55
|
-
def connection(self) ->
|
|
60
|
+
def connection(self) -> TDEngineConnection:
|
|
56
61
|
if not self._connection:
|
|
57
62
|
self._connection = self._create_connection()
|
|
58
63
|
return self._connection
|
|
59
64
|
|
|
60
|
-
def _create_connection(self) ->
|
|
65
|
+
def _create_connection(self) -> TDEngineConnection:
|
|
61
66
|
"""Establish a connection to the TSDB server."""
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
pass
|
|
68
|
-
try:
|
|
69
|
-
conn.execute(f"USE {self.database}")
|
|
70
|
-
except taosws.QueryError as e:
|
|
71
|
-
raise mlrun.errors.MLRunTSDBConnectionFailureError(
|
|
72
|
-
f"Failed to use TDEngine database {self.database}, {mlrun.errors.err_to_str(e)}"
|
|
73
|
-
)
|
|
67
|
+
logger.debug("Creating a new connection to TDEngine", project=self.project)
|
|
68
|
+
conn = TDEngineConnection(self._tdengine_connection_string)
|
|
69
|
+
conn.run(statements=f"CREATE DATABASE IF NOT EXISTS {self.database}")
|
|
70
|
+
conn.prefix_statements = [f"USE {self.database}"]
|
|
71
|
+
logger.debug("Connected to TDEngine", project=self.project)
|
|
74
72
|
return conn
|
|
75
73
|
|
|
76
74
|
def _init_super_tables(self):
|
|
@@ -91,13 +89,13 @@ class TDEngineConnector(TSDBConnector):
|
|
|
91
89
|
"""Create TDEngine supertables."""
|
|
92
90
|
for table in self.tables:
|
|
93
91
|
create_table_query = self.tables[table]._create_super_table_query()
|
|
94
|
-
self.connection.
|
|
92
|
+
self.connection.run(statements=create_table_query)
|
|
95
93
|
|
|
96
94
|
def write_application_event(
|
|
97
95
|
self,
|
|
98
96
|
event: dict,
|
|
99
97
|
kind: mm_schemas.WriterEventKind = mm_schemas.WriterEventKind.RESULT,
|
|
100
|
-
):
|
|
98
|
+
) -> None:
|
|
101
99
|
"""
|
|
102
100
|
Write a single result or metric to TSDB.
|
|
103
101
|
"""
|
|
@@ -113,7 +111,7 @@ class TDEngineConnector(TSDBConnector):
|
|
|
113
111
|
# Write a new result
|
|
114
112
|
table = self.tables[mm_schemas.TDEngineSuperTables.APP_RESULTS]
|
|
115
113
|
table_name = (
|
|
116
|
-
f"{table_name}_
|
|
114
|
+
f"{table_name}_{event[mm_schemas.ResultData.RESULT_NAME]}"
|
|
117
115
|
).replace("-", "_")
|
|
118
116
|
event.pop(mm_schemas.ResultData.CURRENT_STATS, None)
|
|
119
117
|
|
|
@@ -121,9 +119,13 @@ class TDEngineConnector(TSDBConnector):
|
|
|
121
119
|
# Write a new metric
|
|
122
120
|
table = self.tables[mm_schemas.TDEngineSuperTables.METRICS]
|
|
123
121
|
table_name = (
|
|
124
|
-
f"{table_name}_
|
|
122
|
+
f"{table_name}_{event[mm_schemas.MetricData.METRIC_NAME]}"
|
|
125
123
|
).replace("-", "_")
|
|
126
124
|
|
|
125
|
+
# Escape the table name for case-sensitivity (ML-7908)
|
|
126
|
+
# https://github.com/taosdata/taos-connector-python/issues/260
|
|
127
|
+
table_name = f"`{table_name}`"
|
|
128
|
+
|
|
127
129
|
# Convert the datetime strings to datetime objects
|
|
128
130
|
event[mm_schemas.WriterEvent.END_INFER_TIME] = self._convert_to_datetime(
|
|
129
131
|
val=event[mm_schemas.WriterEvent.END_INFER_TIME]
|
|
@@ -132,18 +134,19 @@ class TDEngineConnector(TSDBConnector):
|
|
|
132
134
|
val=event[mm_schemas.WriterEvent.START_INFER_TIME]
|
|
133
135
|
)
|
|
134
136
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
+
create_table_sql = table._create_subtable_sql(subtable=table_name, values=event)
|
|
138
|
+
|
|
139
|
+
insert_statement = Statement(
|
|
140
|
+
TDEngineSchema._insert_subtable_stmt,
|
|
141
|
+
dict(columns=table.columns, subtable=table_name, values=event),
|
|
137
142
|
)
|
|
138
|
-
self.connection.execute(create_table_query)
|
|
139
143
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
+
self.connection.run(
|
|
145
|
+
statements=[
|
|
146
|
+
create_table_sql,
|
|
147
|
+
insert_statement,
|
|
148
|
+
]
|
|
144
149
|
)
|
|
145
|
-
insert_statement.add_batch()
|
|
146
|
-
insert_statement.execute()
|
|
147
150
|
|
|
148
151
|
@staticmethod
|
|
149
152
|
def _convert_to_datetime(val: typing.Union[str, datetime]) -> datetime:
|
|
@@ -200,18 +203,24 @@ class TDEngineConnector(TSDBConnector):
|
|
|
200
203
|
"""
|
|
201
204
|
Delete all project resources in the TSDB connector, such as model endpoints data and drift results.
|
|
202
205
|
"""
|
|
206
|
+
logger.debug(
|
|
207
|
+
"Deleting all project resources using the TDEngine connector",
|
|
208
|
+
project=self.project,
|
|
209
|
+
)
|
|
203
210
|
for table in self.tables:
|
|
204
211
|
get_subtable_names_query = self.tables[table]._get_subtables_query(
|
|
205
212
|
values={mm_schemas.EventFieldType.PROJECT: self.project}
|
|
206
213
|
)
|
|
207
|
-
subtables = self.connection.query
|
|
214
|
+
subtables = self.connection.run(query=get_subtable_names_query).data
|
|
215
|
+
drop_statements = []
|
|
208
216
|
for subtable in subtables:
|
|
209
|
-
|
|
210
|
-
subtable=subtable[0]
|
|
217
|
+
drop_statements.append(
|
|
218
|
+
self.tables[table]._drop_subtable_query(subtable=subtable[0])
|
|
211
219
|
)
|
|
212
|
-
|
|
213
|
-
logger.
|
|
214
|
-
|
|
220
|
+
self.connection.run(statements=drop_statements)
|
|
221
|
+
logger.debug(
|
|
222
|
+
"Deleted all project resources using the TDEngine connector",
|
|
223
|
+
project=self.project,
|
|
215
224
|
)
|
|
216
225
|
|
|
217
226
|
def get_model_endpoint_real_time_metrics(
|
|
@@ -262,7 +271,7 @@ class TDEngineConnector(TSDBConnector):
|
|
|
262
271
|
|
|
263
272
|
project_condition = f"project = '{self.project}'"
|
|
264
273
|
filter_query = (
|
|
265
|
-
f"{filter_query} AND {project_condition}"
|
|
274
|
+
f"({filter_query}) AND ({project_condition})"
|
|
266
275
|
if filter_query
|
|
267
276
|
else project_condition
|
|
268
277
|
)
|
|
@@ -280,15 +289,16 @@ class TDEngineConnector(TSDBConnector):
|
|
|
280
289
|
timestamp_column=timestamp_column,
|
|
281
290
|
database=self.database,
|
|
282
291
|
)
|
|
292
|
+
logger.debug("Querying TDEngine", query=full_query)
|
|
283
293
|
try:
|
|
284
|
-
query_result = self.connection.query
|
|
294
|
+
query_result = self.connection.run(query=full_query)
|
|
285
295
|
except taosws.QueryError as e:
|
|
286
296
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
287
297
|
f"Failed to query table {table} in database {self.database}, {str(e)}"
|
|
288
298
|
)
|
|
289
299
|
|
|
290
|
-
df_columns = [field.name
|
|
291
|
-
return pd.DataFrame(query_result, columns=df_columns)
|
|
300
|
+
df_columns = [field.name for field in query_result.fields]
|
|
301
|
+
return pd.DataFrame(query_result.data, columns=df_columns)
|
|
292
302
|
|
|
293
303
|
def read_metrics_data(
|
|
294
304
|
self,
|
|
@@ -336,11 +346,11 @@ class TDEngineConnector(TSDBConnector):
|
|
|
336
346
|
|
|
337
347
|
metrics_condition = " OR ".join(
|
|
338
348
|
[
|
|
339
|
-
f"({mm_schemas.WriterEvent.APPLICATION_NAME}
|
|
349
|
+
f"({mm_schemas.WriterEvent.APPLICATION_NAME}='{metric.app}' AND {name}='{metric.name}')"
|
|
340
350
|
for metric in metrics
|
|
341
351
|
]
|
|
342
352
|
)
|
|
343
|
-
filter_query = f"endpoint_id='{endpoint_id}' AND ({metrics_condition})"
|
|
353
|
+
filter_query = f"(endpoint_id='{endpoint_id}') AND ({metrics_condition})"
|
|
344
354
|
|
|
345
355
|
df = self._get_records(
|
|
346
356
|
table=table,
|
|
@@ -18,6 +18,10 @@ import typing
|
|
|
18
18
|
import numpy as np
|
|
19
19
|
import pandas as pd
|
|
20
20
|
|
|
21
|
+
if typing.TYPE_CHECKING:
|
|
22
|
+
from mlrun.db.base import RunDBInterface
|
|
23
|
+
from mlrun.projects import MlrunProject
|
|
24
|
+
|
|
21
25
|
import mlrun
|
|
22
26
|
import mlrun.artifacts
|
|
23
27
|
import mlrun.common.model_monitoring.helpers
|
|
@@ -26,16 +30,11 @@ import mlrun.data_types.infer
|
|
|
26
30
|
import mlrun.model_monitoring
|
|
27
31
|
from mlrun.common.schemas.model_monitoring.model_endpoints import (
|
|
28
32
|
ModelEndpointMonitoringMetric,
|
|
29
|
-
ModelEndpointMonitoringMetricType,
|
|
30
33
|
_compose_full_name,
|
|
31
34
|
)
|
|
32
35
|
from mlrun.model_monitoring.model_endpoint import ModelEndpoint
|
|
33
36
|
from mlrun.utils import logger
|
|
34
37
|
|
|
35
|
-
if typing.TYPE_CHECKING:
|
|
36
|
-
from mlrun.db.base import RunDBInterface
|
|
37
|
-
from mlrun.projects import MlrunProject
|
|
38
|
-
|
|
39
38
|
|
|
40
39
|
class _BatchDict(typing.TypedDict):
|
|
41
40
|
minutes: int
|
|
@@ -63,7 +62,6 @@ def get_stream_path(
|
|
|
63
62
|
)
|
|
64
63
|
|
|
65
64
|
if not stream_uri or stream_uri == "v3io":
|
|
66
|
-
# TODO : remove the first part of this condition in 1.9.0
|
|
67
65
|
stream_uri = mlrun.mlconf.get_model_monitoring_file_target_path(
|
|
68
66
|
project=project,
|
|
69
67
|
kind=mm_constants.FileTargetKind.STREAM,
|
|
@@ -71,8 +69,6 @@ def get_stream_path(
|
|
|
71
69
|
function_name=function_name,
|
|
72
70
|
)
|
|
73
71
|
|
|
74
|
-
if isinstance(stream_uri, list): # ML-6043 - user side gets only the new stream uri
|
|
75
|
-
stream_uri = stream_uri[1] # get new stream path, under projects
|
|
76
72
|
return mlrun.common.model_monitoring.helpers.parse_monitoring_stream_path(
|
|
77
73
|
stream_uri=stream_uri, project=project, function_name=function_name
|
|
78
74
|
)
|
|
@@ -179,7 +175,7 @@ def _get_monitoring_time_window_from_controller_run(
|
|
|
179
175
|
def update_model_endpoint_last_request(
|
|
180
176
|
project: str,
|
|
181
177
|
model_endpoint: ModelEndpoint,
|
|
182
|
-
current_request: datetime,
|
|
178
|
+
current_request: datetime.datetime,
|
|
183
179
|
db: "RunDBInterface",
|
|
184
180
|
) -> None:
|
|
185
181
|
"""
|
|
@@ -190,7 +186,8 @@ def update_model_endpoint_last_request(
|
|
|
190
186
|
:param current_request: current request time
|
|
191
187
|
:param db: DB interface.
|
|
192
188
|
"""
|
|
193
|
-
|
|
189
|
+
is_model_server_endpoint = model_endpoint.spec.stream_path != ""
|
|
190
|
+
if is_model_server_endpoint:
|
|
194
191
|
current_request = current_request.isoformat()
|
|
195
192
|
logger.info(
|
|
196
193
|
"Update model endpoint last request time (EP with serving)",
|
|
@@ -204,12 +201,13 @@ def update_model_endpoint_last_request(
|
|
|
204
201
|
endpoint_id=model_endpoint.metadata.uid,
|
|
205
202
|
attributes={mm_constants.EventFieldType.LAST_REQUEST: current_request},
|
|
206
203
|
)
|
|
207
|
-
else:
|
|
204
|
+
else: # model endpoint without any serving function - close the window "manually"
|
|
208
205
|
try:
|
|
209
206
|
time_window = _get_monitoring_time_window_from_controller_run(project, db)
|
|
210
207
|
except mlrun.errors.MLRunNotFoundError:
|
|
211
|
-
logger.
|
|
212
|
-
"Not bumping model endpoint last request time - the monitoring controller isn't deployed yet"
|
|
208
|
+
logger.warn(
|
|
209
|
+
"Not bumping model endpoint last request time - the monitoring controller isn't deployed yet.\n"
|
|
210
|
+
"Call `project.enable_model_monitoring()` first."
|
|
213
211
|
)
|
|
214
212
|
return
|
|
215
213
|
|
|
@@ -302,7 +300,7 @@ def get_invocations_fqn(project: str) -> str:
|
|
|
302
300
|
project=project,
|
|
303
301
|
app=mm_constants.SpecialApps.MLRUN_INFRA,
|
|
304
302
|
name=mm_constants.PredictionsQueryConstants.INVOCATIONS,
|
|
305
|
-
type=ModelEndpointMonitoringMetricType.METRIC,
|
|
303
|
+
type=mm_constants.ModelEndpointMonitoringMetricType.METRIC,
|
|
306
304
|
)
|
|
307
305
|
|
|
308
306
|
|
|
@@ -316,7 +314,7 @@ def get_invocations_metric(project: str) -> ModelEndpointMonitoringMetric:
|
|
|
316
314
|
return ModelEndpointMonitoringMetric(
|
|
317
315
|
project=project,
|
|
318
316
|
app=mm_constants.SpecialApps.MLRUN_INFRA,
|
|
319
|
-
type=ModelEndpointMonitoringMetricType.METRIC,
|
|
317
|
+
type=mm_constants.ModelEndpointMonitoringMetricType.METRIC,
|
|
320
318
|
name=mm_constants.PredictionsQueryConstants.INVOCATIONS,
|
|
321
319
|
full_name=get_invocations_fqn(project),
|
|
322
320
|
)
|
mlrun/model_monitoring/writer.py
CHANGED
|
@@ -160,7 +160,9 @@ class ModelMonitoringWriter(StepToDict):
|
|
|
160
160
|
event_kind = f"{event_kind}_detected"
|
|
161
161
|
else:
|
|
162
162
|
event_kind = f"{event_kind}_suspected"
|
|
163
|
-
return alert_objects.EventKind(
|
|
163
|
+
return alert_objects.EventKind(
|
|
164
|
+
value=mlrun.utils.helpers.normalize_name(event_kind)
|
|
165
|
+
)
|
|
164
166
|
|
|
165
167
|
@staticmethod
|
|
166
168
|
def _reconstruct_event(event: _RawEvent) -> tuple[_AppResultEvent, WriterEventKind]:
|
mlrun/projects/operations.py
CHANGED
|
@@ -15,10 +15,13 @@
|
|
|
15
15
|
import warnings
|
|
16
16
|
from typing import Optional, Union
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
import mlrun_pipelines.common.models
|
|
19
|
+
import mlrun_pipelines.models
|
|
19
20
|
|
|
20
21
|
import mlrun
|
|
21
22
|
import mlrun.common.constants as mlrun_constants
|
|
23
|
+
import mlrun.common.schemas.function
|
|
24
|
+
import mlrun.common.schemas.workflow
|
|
22
25
|
from mlrun.utils import hub_prefix
|
|
23
26
|
|
|
24
27
|
from .pipelines import enrich_function_object, pipeline_context
|
|
@@ -49,7 +52,7 @@ def _get_engine_and_function(function, project=None):
|
|
|
49
52
|
function = enrich_function_object(project, function, copy_function=False)
|
|
50
53
|
|
|
51
54
|
if not pipeline_context.workflow:
|
|
52
|
-
return
|
|
55
|
+
return mlrun.common.schemas.workflow.EngineType.LOCAL, function
|
|
53
56
|
|
|
54
57
|
return pipeline_context.workflow.engine, function
|
|
55
58
|
|
|
@@ -78,7 +81,7 @@ def run_function(
|
|
|
78
81
|
returns: Optional[list[Union[str, dict[str, str]]]] = None,
|
|
79
82
|
builder_env: Optional[list] = None,
|
|
80
83
|
reset_on_run: Optional[bool] = None,
|
|
81
|
-
) -> Union[mlrun.model.RunObject, PipelineNodeWrapper]:
|
|
84
|
+
) -> Union[mlrun.model.RunObject, mlrun_pipelines.models.PipelineNodeWrapper]:
|
|
82
85
|
"""Run a local or remote task as part of a local/kubeflow pipeline
|
|
83
86
|
|
|
84
87
|
run_function() allow you to execute a function locally, on a remote cluster, or as part of an automated workflow
|
|
@@ -186,7 +189,7 @@ def run_function(
|
|
|
186
189
|
)
|
|
187
190
|
task.spec.verbose = task.spec.verbose or verbose
|
|
188
191
|
|
|
189
|
-
if engine ==
|
|
192
|
+
if engine == mlrun.common.schemas.workflow.EngineType.KFP:
|
|
190
193
|
if schedule:
|
|
191
194
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
192
195
|
"Scheduling jobs is not supported when running a workflow with the kfp engine."
|
|
@@ -266,7 +269,7 @@ def build_function(
|
|
|
266
269
|
overwrite_build_params: bool = False,
|
|
267
270
|
extra_args: str = None,
|
|
268
271
|
force_build: bool = False,
|
|
269
|
-
) -> Union[BuildStatus, PipelineNodeWrapper]:
|
|
272
|
+
) -> Union[BuildStatus, mlrun_pipelines.models.PipelineNodeWrapper]:
|
|
270
273
|
"""deploy ML function, build container with its dependencies
|
|
271
274
|
|
|
272
275
|
:param function: Name of the function (in the project) or function object
|
|
@@ -302,7 +305,7 @@ def build_function(
|
|
|
302
305
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
303
306
|
"Cannot build use deploy_function()"
|
|
304
307
|
)
|
|
305
|
-
if engine ==
|
|
308
|
+
if engine == mlrun.common.schemas.workflow.EngineType.KFP:
|
|
306
309
|
if overwrite_build_params:
|
|
307
310
|
function.spec.build.commands = None
|
|
308
311
|
if requirements or requirements_file:
|
|
@@ -375,7 +378,7 @@ def deploy_function(
|
|
|
375
378
|
builder_env: dict = None,
|
|
376
379
|
project_object=None,
|
|
377
380
|
mock: bool = None,
|
|
378
|
-
) -> Union[DeployStatus, PipelineNodeWrapper]:
|
|
381
|
+
) -> Union[DeployStatus, mlrun_pipelines.models.PipelineNodeWrapper]:
|
|
379
382
|
"""deploy real-time (nuclio based) functions
|
|
380
383
|
|
|
381
384
|
:param function: name of the function (in the project) or function object
|
|
@@ -392,7 +395,7 @@ def deploy_function(
|
|
|
392
395
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
393
396
|
"deploy is used with real-time functions, for other kinds use build_function()"
|
|
394
397
|
)
|
|
395
|
-
if engine ==
|
|
398
|
+
if engine == mlrun.common.schemas.workflow.EngineType.KFP:
|
|
396
399
|
return function.deploy_step(models=models, env=env, tag=tag, verbose=verbose)
|
|
397
400
|
else:
|
|
398
401
|
if env:
|
mlrun/projects/pipelines.py
CHANGED
|
@@ -27,6 +27,8 @@ import mlrun_pipelines.utils
|
|
|
27
27
|
import mlrun
|
|
28
28
|
import mlrun.common.runtimes.constants
|
|
29
29
|
import mlrun.common.schemas
|
|
30
|
+
import mlrun.common.schemas.function
|
|
31
|
+
import mlrun.common.schemas.workflow
|
|
30
32
|
import mlrun.utils.notifications
|
|
31
33
|
from mlrun.errors import err_to_str
|
|
32
34
|
from mlrun.utils import (
|
|
@@ -44,21 +46,21 @@ from ..runtimes.pod import AutoMountType
|
|
|
44
46
|
|
|
45
47
|
def get_workflow_engine(engine_kind, local=False):
|
|
46
48
|
if pipeline_context.is_run_local(local):
|
|
47
|
-
if engine_kind ==
|
|
49
|
+
if engine_kind == mlrun.common.schemas.workflow.EngineType.KFP:
|
|
48
50
|
logger.warning(
|
|
49
51
|
"Running kubeflow pipeline locally, note some ops may not run locally!"
|
|
50
52
|
)
|
|
51
|
-
elif engine_kind ==
|
|
53
|
+
elif engine_kind == mlrun.common.schemas.workflow.EngineType.REMOTE:
|
|
52
54
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
53
55
|
"Cannot run a remote pipeline locally using `kind='remote'` and `local=True`. "
|
|
54
56
|
"in order to run a local pipeline remotely, please use `engine='remote:local'` instead"
|
|
55
57
|
)
|
|
56
58
|
return _LocalRunner
|
|
57
|
-
if not engine_kind or engine_kind ==
|
|
59
|
+
if not engine_kind or engine_kind == mlrun.common.schemas.workflow.EngineType.KFP:
|
|
58
60
|
return _KFPRunner
|
|
59
|
-
if engine_kind ==
|
|
61
|
+
if engine_kind == mlrun.common.schemas.workflow.EngineType.LOCAL:
|
|
60
62
|
return _LocalRunner
|
|
61
|
-
if engine_kind ==
|
|
63
|
+
if engine_kind == mlrun.common.schemas.workflow.EngineType.REMOTE:
|
|
62
64
|
return _RemoteRunner
|
|
63
65
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
64
66
|
f"Provided workflow engine is not supported. engine_kind={engine_kind}"
|
|
@@ -80,6 +82,7 @@ class WorkflowSpec(mlrun.model.ModelObj):
|
|
|
80
82
|
schedule: typing.Union[str, mlrun.common.schemas.ScheduleCronTrigger] = None,
|
|
81
83
|
cleanup_ttl: typing.Optional[int] = None,
|
|
82
84
|
image: typing.Optional[str] = None,
|
|
85
|
+
workflow_runner_node_selector: typing.Optional[dict[str, str]] = None,
|
|
83
86
|
):
|
|
84
87
|
self.engine = engine
|
|
85
88
|
self.code = code
|
|
@@ -93,6 +96,7 @@ class WorkflowSpec(mlrun.model.ModelObj):
|
|
|
93
96
|
self._tmp_path = None
|
|
94
97
|
self.schedule = schedule
|
|
95
98
|
self.image = image
|
|
99
|
+
self.workflow_runner_node_selector = workflow_runner_node_selector
|
|
96
100
|
|
|
97
101
|
def get_source_file(self, context=""):
|
|
98
102
|
if not self.code and not self.path:
|
|
@@ -311,7 +315,11 @@ def get_db_function(project, key) -> mlrun.runtimes.BaseRuntime:
|
|
|
311
315
|
|
|
312
316
|
|
|
313
317
|
def enrich_function_object(
|
|
314
|
-
project
|
|
318
|
+
project: mlrun.common.schemas.Project,
|
|
319
|
+
function: mlrun.runtimes.BaseRuntime,
|
|
320
|
+
decorator: typing.Callable = None,
|
|
321
|
+
copy_function: bool = True,
|
|
322
|
+
try_auto_mount: bool = True,
|
|
315
323
|
) -> mlrun.runtimes.BaseRuntime:
|
|
316
324
|
if hasattr(function, "_enriched"):
|
|
317
325
|
return function
|
|
@@ -352,7 +360,6 @@ def enrich_function_object(
|
|
|
352
360
|
f.enrich_runtime_spec(
|
|
353
361
|
project.spec.default_function_node_selector,
|
|
354
362
|
)
|
|
355
|
-
|
|
356
363
|
if try_auto_mount:
|
|
357
364
|
if (
|
|
358
365
|
decorator and AutoMountType.is_auto_modifier(decorator)
|
|
@@ -452,7 +459,12 @@ class _PipelineRunner(abc.ABC):
|
|
|
452
459
|
|
|
453
460
|
@staticmethod
|
|
454
461
|
@abc.abstractmethod
|
|
455
|
-
def wait_for_completion(
|
|
462
|
+
def wait_for_completion(
|
|
463
|
+
run: "_PipelineRunStatus",
|
|
464
|
+
project: typing.Optional["mlrun.projects.MlrunProject"] = None,
|
|
465
|
+
timeout: typing.Optional[int] = None,
|
|
466
|
+
expected_statuses: list[str] = None,
|
|
467
|
+
):
|
|
456
468
|
pass
|
|
457
469
|
|
|
458
470
|
@staticmethod
|
|
@@ -581,13 +593,13 @@ class _KFPRunner(_PipelineRunner):
|
|
|
581
593
|
logger.warning(
|
|
582
594
|
"Setting notifications on kfp pipeline runner uses old notification behavior. "
|
|
583
595
|
"Notifications will only be sent if you wait for pipeline completion. "
|
|
584
|
-
"
|
|
596
|
+
"Some of the features (like setting message or severity level) are not supported."
|
|
585
597
|
)
|
|
586
598
|
# for start message, fallback to old notification behavior
|
|
587
599
|
for notification in notifications or []:
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
)
|
|
600
|
+
params = notification.params
|
|
601
|
+
params.update(notification.secret_params)
|
|
602
|
+
project.notifiers.add_notification(notification.kind, params)
|
|
591
603
|
|
|
592
604
|
run_id = _run_pipeline(
|
|
593
605
|
workflow_handler,
|
|
@@ -625,12 +637,19 @@ class _KFPRunner(_PipelineRunner):
|
|
|
625
637
|
return _PipelineRunStatus(run_id, cls, project=project, workflow=workflow_spec)
|
|
626
638
|
|
|
627
639
|
@staticmethod
|
|
628
|
-
def wait_for_completion(
|
|
640
|
+
def wait_for_completion(
|
|
641
|
+
run: "_PipelineRunStatus",
|
|
642
|
+
project: typing.Optional["mlrun.projects.MlrunProject"] = None,
|
|
643
|
+
timeout: typing.Optional[int] = None,
|
|
644
|
+
expected_statuses: list[str] = None,
|
|
645
|
+
):
|
|
646
|
+
project_name = project.metadata.name if project else ""
|
|
629
647
|
logger.info(
|
|
630
|
-
"Waiting for pipeline run completion",
|
|
648
|
+
"Waiting for pipeline run completion",
|
|
649
|
+
run_id=run.run_id,
|
|
650
|
+
project=project_name,
|
|
631
651
|
)
|
|
632
652
|
timeout = timeout or 60 * 60
|
|
633
|
-
project_name = project.metadata.name if project else ""
|
|
634
653
|
run_info = wait_for_pipeline_completion(
|
|
635
654
|
run.run_id,
|
|
636
655
|
timeout=timeout,
|
|
@@ -1062,7 +1081,7 @@ def load_and_run(
|
|
|
1062
1081
|
# extract "start" notification if exists
|
|
1063
1082
|
start_notifications = [
|
|
1064
1083
|
notification
|
|
1065
|
-
for notification in context.get_notifications()
|
|
1084
|
+
for notification in context.get_notifications(unmask_secret_params=True)
|
|
1066
1085
|
if "running" in notification.when
|
|
1067
1086
|
]
|
|
1068
1087
|
|