mlrun 1.7.0rc3__py3-none-any.whl → 1.7.0rc5__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/artifacts/manager.py +6 -1
- mlrun/common/constants.py +2 -0
- mlrun/common/model_monitoring/helpers.py +12 -6
- mlrun/common/schemas/__init__.py +11 -0
- mlrun/common/schemas/api_gateway.py +85 -0
- mlrun/common/schemas/auth.py +2 -2
- mlrun/common/schemas/client_spec.py +1 -0
- mlrun/common/schemas/common.py +40 -0
- mlrun/common/schemas/model_monitoring/constants.py +4 -1
- mlrun/common/schemas/project.py +2 -0
- mlrun/config.py +31 -17
- mlrun/datastore/azure_blob.py +22 -9
- mlrun/datastore/base.py +15 -25
- mlrun/datastore/datastore.py +19 -8
- mlrun/datastore/datastore_profile.py +47 -5
- mlrun/datastore/google_cloud_storage.py +10 -6
- mlrun/datastore/hdfs.py +51 -0
- mlrun/datastore/redis.py +4 -0
- mlrun/datastore/s3.py +4 -0
- mlrun/datastore/sources.py +29 -43
- mlrun/datastore/targets.py +59 -53
- mlrun/datastore/utils.py +2 -49
- mlrun/datastore/v3io.py +4 -0
- mlrun/db/base.py +50 -0
- mlrun/db/httpdb.py +121 -50
- mlrun/db/nopdb.py +13 -0
- mlrun/execution.py +3 -3
- mlrun/feature_store/feature_vector.py +2 -2
- mlrun/frameworks/tf_keras/callbacks/logging_callback.py +3 -3
- mlrun/frameworks/tf_keras/model_handler.py +7 -7
- mlrun/k8s_utils.py +10 -5
- mlrun/kfpops.py +19 -10
- mlrun/model.py +5 -0
- mlrun/model_monitoring/api.py +3 -3
- mlrun/model_monitoring/application.py +1 -1
- mlrun/model_monitoring/applications/__init__.py +13 -0
- mlrun/model_monitoring/applications/histogram_data_drift.py +218 -0
- mlrun/model_monitoring/batch.py +9 -111
- mlrun/model_monitoring/controller.py +73 -55
- mlrun/model_monitoring/controller_handler.py +13 -5
- mlrun/model_monitoring/features_drift_table.py +62 -53
- mlrun/model_monitoring/helpers.py +30 -21
- mlrun/model_monitoring/metrics/__init__.py +13 -0
- mlrun/model_monitoring/metrics/histogram_distance.py +127 -0
- mlrun/model_monitoring/stores/kv_model_endpoint_store.py +14 -14
- mlrun/model_monitoring/stores/sql_model_endpoint_store.py +0 -1
- mlrun/package/packagers/pandas_packagers.py +3 -3
- mlrun/package/utils/_archiver.py +3 -1
- mlrun/platforms/iguazio.py +8 -65
- mlrun/projects/pipelines.py +21 -11
- mlrun/projects/project.py +180 -42
- mlrun/run.py +1 -1
- mlrun/runtimes/base.py +25 -2
- mlrun/runtimes/kubejob.py +5 -3
- mlrun/runtimes/local.py +2 -2
- mlrun/runtimes/mpijob/abstract.py +6 -6
- mlrun/runtimes/nuclio/__init__.py +1 -0
- mlrun/runtimes/nuclio/api_gateway.py +300 -0
- mlrun/runtimes/nuclio/function.py +9 -9
- mlrun/runtimes/nuclio/serving.py +3 -3
- mlrun/runtimes/pod.py +3 -3
- mlrun/runtimes/sparkjob/spark3job.py +3 -3
- mlrun/serving/remote.py +4 -2
- mlrun/serving/server.py +2 -8
- mlrun/utils/async_http.py +3 -3
- mlrun/utils/helpers.py +27 -5
- mlrun/utils/http.py +3 -3
- mlrun/utils/logger.py +2 -2
- mlrun/utils/notifications/notification_pusher.py +6 -6
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.0rc3.dist-info → mlrun-1.7.0rc5.dist-info}/METADATA +13 -16
- {mlrun-1.7.0rc3.dist-info → mlrun-1.7.0rc5.dist-info}/RECORD +76 -68
- {mlrun-1.7.0rc3.dist-info → mlrun-1.7.0rc5.dist-info}/WHEEL +1 -1
- {mlrun-1.7.0rc3.dist-info → mlrun-1.7.0rc5.dist-info}/LICENSE +0 -0
- {mlrun-1.7.0rc3.dist-info → mlrun-1.7.0rc5.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.0rc3.dist-info → mlrun-1.7.0rc5.dist-info}/top_level.txt +0 -0
mlrun/platforms/iguazio.py
CHANGED
|
@@ -16,19 +16,15 @@ import json
|
|
|
16
16
|
import os
|
|
17
17
|
import urllib
|
|
18
18
|
from collections import namedtuple
|
|
19
|
-
from datetime import datetime
|
|
20
|
-
from http import HTTPStatus
|
|
21
19
|
from urllib.parse import urlparse
|
|
22
20
|
|
|
23
21
|
import kfp.dsl
|
|
24
22
|
import requests
|
|
25
23
|
import semver
|
|
26
|
-
import urllib3
|
|
27
24
|
import v3io
|
|
28
25
|
|
|
29
26
|
import mlrun.errors
|
|
30
27
|
from mlrun.config import config as mlconf
|
|
31
|
-
from mlrun.errors import err_to_str
|
|
32
28
|
from mlrun.utils import dict_to_json
|
|
33
29
|
|
|
34
30
|
_cached_control_session = None
|
|
@@ -488,25 +484,6 @@ class V3ioStreamClient:
|
|
|
488
484
|
return response.output.records
|
|
489
485
|
|
|
490
486
|
|
|
491
|
-
def create_control_session(url, username, password):
|
|
492
|
-
# for systems without production cert - silence no cert verification WARN
|
|
493
|
-
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
494
|
-
if not username or not password:
|
|
495
|
-
raise ValueError("cannot create session key, missing username or password")
|
|
496
|
-
|
|
497
|
-
session = requests.Session()
|
|
498
|
-
session.auth = (username, password)
|
|
499
|
-
try:
|
|
500
|
-
auth = session.post(f"{url}/api/sessions", verify=False)
|
|
501
|
-
except OSError as exc:
|
|
502
|
-
raise OSError(f"error: cannot connect to {url}: {err_to_str(exc)}")
|
|
503
|
-
|
|
504
|
-
if not auth.ok:
|
|
505
|
-
raise OSError(f"failed to create session: {url}, {auth.text}")
|
|
506
|
-
|
|
507
|
-
return auth.json()["data"]["id"]
|
|
508
|
-
|
|
509
|
-
|
|
510
487
|
def is_iguazio_endpoint(endpoint_url: str) -> bool:
|
|
511
488
|
# TODO: find a better heuristic
|
|
512
489
|
return ".default-tenant." in endpoint_url
|
|
@@ -533,21 +510,6 @@ def is_iguazio_session_cookie(session_cookie: str) -> bool:
|
|
|
533
510
|
return False
|
|
534
511
|
|
|
535
512
|
|
|
536
|
-
def is_iguazio_system_2_10_or_above(dashboard_url):
|
|
537
|
-
# for systems without production cert - silence no cert verification WARN
|
|
538
|
-
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
|
|
539
|
-
response = requests.get(f"{dashboard_url}/api/external_versions", verify=False)
|
|
540
|
-
|
|
541
|
-
if not response.ok:
|
|
542
|
-
if response.status_code == HTTPStatus.NOT_FOUND.value:
|
|
543
|
-
# in iguazio systems prior to 2.10 this endpoint didn't exist, so the api returns 404 cause endpoint not
|
|
544
|
-
# found
|
|
545
|
-
return False
|
|
546
|
-
response.raise_for_status()
|
|
547
|
-
|
|
548
|
-
return True
|
|
549
|
-
|
|
550
|
-
|
|
551
513
|
# we assign the control session or access key to the password since this is iguazio auth scheme
|
|
552
514
|
# (requests should be sent with username:control_session/access_key as auth header)
|
|
553
515
|
def add_or_refresh_credentials(
|
|
@@ -577,33 +539,12 @@ def add_or_refresh_credentials(
|
|
|
577
539
|
# (ideally if we could identify we're in enterprise we would have verify here that token and username have value)
|
|
578
540
|
if not is_iguazio_endpoint(api_url):
|
|
579
541
|
return "", "", token
|
|
580
|
-
iguazio_dashboard_url = "https://dashboard" + api_url[api_url.find(".") :]
|
|
581
|
-
|
|
582
|
-
# in 2.8 mlrun api is protected with control session, from 2.10 it's protected with access key
|
|
583
|
-
is_access_key_auth = is_iguazio_system_2_10_or_above(iguazio_dashboard_url)
|
|
584
|
-
if is_access_key_auth:
|
|
585
|
-
if not username or not token:
|
|
586
|
-
raise ValueError(
|
|
587
|
-
"username and access key required to authenticate against iguazio system"
|
|
588
|
-
)
|
|
589
|
-
return username, token, ""
|
|
590
542
|
|
|
591
|
-
if not username or not
|
|
592
|
-
raise ValueError(
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
if _cached_control_session:
|
|
597
|
-
if (
|
|
598
|
-
_cached_control_session[2] == username
|
|
599
|
-
and _cached_control_session[3] == password
|
|
600
|
-
and (now - _cached_control_session[1]).seconds < 20 * 60 * 60
|
|
601
|
-
):
|
|
602
|
-
return _cached_control_session[2], _cached_control_session[0], ""
|
|
603
|
-
|
|
604
|
-
control_session = create_control_session(iguazio_dashboard_url, username, password)
|
|
605
|
-
_cached_control_session = (control_session, now, username, password)
|
|
606
|
-
return username, control_session, ""
|
|
543
|
+
if not username or not token:
|
|
544
|
+
raise ValueError(
|
|
545
|
+
"username and access key required to authenticate against iguazio system"
|
|
546
|
+
)
|
|
547
|
+
return username, token, ""
|
|
607
548
|
|
|
608
549
|
|
|
609
550
|
def parse_path(url, suffix="/"):
|
|
@@ -611,7 +552,9 @@ def parse_path(url, suffix="/"):
|
|
|
611
552
|
parsed_url = urlparse(url)
|
|
612
553
|
if parsed_url.netloc:
|
|
613
554
|
scheme = parsed_url.scheme.lower()
|
|
614
|
-
if scheme == "
|
|
555
|
+
if scheme == "s3":
|
|
556
|
+
prefix = "s3"
|
|
557
|
+
elif scheme == "v3ios":
|
|
615
558
|
prefix = "https"
|
|
616
559
|
elif scheme == "v3io":
|
|
617
560
|
prefix = "http"
|
mlrun/projects/pipelines.py
CHANGED
|
@@ -69,16 +69,16 @@ class WorkflowSpec(mlrun.model.ModelObj):
|
|
|
69
69
|
|
|
70
70
|
def __init__(
|
|
71
71
|
self,
|
|
72
|
-
engine=None,
|
|
73
|
-
code=None,
|
|
74
|
-
path=None,
|
|
75
|
-
args=None,
|
|
76
|
-
name=None,
|
|
77
|
-
handler=None,
|
|
78
|
-
args_schema: dict = None,
|
|
72
|
+
engine: typing.Optional[str] = None,
|
|
73
|
+
code: typing.Optional[str] = None,
|
|
74
|
+
path: typing.Optional[str] = None,
|
|
75
|
+
args: typing.Optional[dict] = None,
|
|
76
|
+
name: typing.Optional[str] = None,
|
|
77
|
+
handler: typing.Optional[str] = None,
|
|
78
|
+
args_schema: typing.Optional[dict] = None,
|
|
79
79
|
schedule: typing.Union[str, mlrun.common.schemas.ScheduleCronTrigger] = None,
|
|
80
|
-
cleanup_ttl: int = None,
|
|
81
|
-
image: str = None,
|
|
80
|
+
cleanup_ttl: typing.Optional[int] = None,
|
|
81
|
+
image: typing.Optional[str] = None,
|
|
82
82
|
):
|
|
83
83
|
self.engine = engine
|
|
84
84
|
self.code = code
|
|
@@ -401,6 +401,9 @@ def enrich_function_object(
|
|
|
401
401
|
else:
|
|
402
402
|
f.spec.build.source = project.spec.source
|
|
403
403
|
f.spec.build.load_source_on_run = project.spec.load_source_on_run
|
|
404
|
+
f.spec.build.source_code_target_dir = (
|
|
405
|
+
project.spec.build.source_code_target_dir
|
|
406
|
+
)
|
|
404
407
|
f.spec.workdir = project.spec.workdir or project.spec.subpath
|
|
405
408
|
f.prepare_image_for_deploy()
|
|
406
409
|
|
|
@@ -862,6 +865,11 @@ class _RemoteRunner(_PipelineRunner):
|
|
|
862
865
|
)
|
|
863
866
|
return
|
|
864
867
|
|
|
868
|
+
logger.debug(
|
|
869
|
+
"Workflow submitted, waiting for pipeline run to start",
|
|
870
|
+
workflow_name=workflow_response.name,
|
|
871
|
+
)
|
|
872
|
+
|
|
865
873
|
# Getting workflow id from run:
|
|
866
874
|
response = retry_until_successful(
|
|
867
875
|
1,
|
|
@@ -988,6 +996,7 @@ def load_and_run(
|
|
|
988
996
|
cleanup_ttl: int = None,
|
|
989
997
|
load_only: bool = False,
|
|
990
998
|
wait_for_completion: bool = False,
|
|
999
|
+
project_context: str = None,
|
|
991
1000
|
):
|
|
992
1001
|
"""
|
|
993
1002
|
Auxiliary function that the RemoteRunner run once or run every schedule.
|
|
@@ -1018,10 +1027,11 @@ def load_and_run(
|
|
|
1018
1027
|
workflow and all its resources are deleted)
|
|
1019
1028
|
:param load_only: for just loading the project, inner use.
|
|
1020
1029
|
:param wait_for_completion: wait for workflow completion before returning
|
|
1030
|
+
:param project_context: project context path (used for loading the project)
|
|
1021
1031
|
"""
|
|
1022
1032
|
try:
|
|
1023
1033
|
project = mlrun.load_project(
|
|
1024
|
-
context=f"./{project_name}",
|
|
1034
|
+
context=project_context or f"./{project_name}",
|
|
1025
1035
|
url=url,
|
|
1026
1036
|
name=project_name,
|
|
1027
1037
|
init_git=init_git,
|
|
@@ -1053,7 +1063,7 @@ def load_and_run(
|
|
|
1053
1063
|
|
|
1054
1064
|
raise error
|
|
1055
1065
|
|
|
1056
|
-
context.logger.info(f"Loaded project {project.name}
|
|
1066
|
+
context.logger.info(f"Loaded project {project.name} successfully")
|
|
1057
1067
|
|
|
1058
1068
|
if load_only:
|
|
1059
1069
|
return
|
mlrun/projects/project.py
CHANGED
|
@@ -24,7 +24,7 @@ import typing
|
|
|
24
24
|
import uuid
|
|
25
25
|
import warnings
|
|
26
26
|
import zipfile
|
|
27
|
-
from os import environ, makedirs, path
|
|
27
|
+
from os import environ, makedirs, path
|
|
28
28
|
from typing import Callable, Optional, Union
|
|
29
29
|
|
|
30
30
|
import dotenv
|
|
@@ -36,12 +36,12 @@ import requests
|
|
|
36
36
|
import yaml
|
|
37
37
|
|
|
38
38
|
import mlrun.common.helpers
|
|
39
|
-
import mlrun.common.schemas.model_monitoring
|
|
40
39
|
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
41
40
|
import mlrun.db
|
|
42
41
|
import mlrun.errors
|
|
43
42
|
import mlrun.k8s_utils
|
|
44
43
|
import mlrun.runtimes
|
|
44
|
+
import mlrun.runtimes.nuclio.api_gateway
|
|
45
45
|
import mlrun.runtimes.pod
|
|
46
46
|
import mlrun.runtimes.utils
|
|
47
47
|
import mlrun.utils.regex
|
|
@@ -617,9 +617,14 @@ def _load_project_dir(context, name="", subpath=""):
|
|
|
617
617
|
# If there is a setup script do not force having project.yaml file
|
|
618
618
|
project = MlrunProject()
|
|
619
619
|
else:
|
|
620
|
-
|
|
621
|
-
|
|
620
|
+
message = "Project or function YAML not found in path"
|
|
621
|
+
logger.error(
|
|
622
|
+
message,
|
|
623
|
+
context=context,
|
|
624
|
+
name=name,
|
|
625
|
+
subpath=subpath,
|
|
622
626
|
)
|
|
627
|
+
raise mlrun.errors.MLRunNotFoundError(message)
|
|
623
628
|
|
|
624
629
|
project.spec.context = context
|
|
625
630
|
project.metadata.name = name or project.metadata.name
|
|
@@ -1247,20 +1252,20 @@ class MlrunProject(ModelObj):
|
|
|
1247
1252
|
self,
|
|
1248
1253
|
name,
|
|
1249
1254
|
workflow_path: str,
|
|
1250
|
-
embed=False,
|
|
1251
|
-
engine=None,
|
|
1255
|
+
embed: bool = False,
|
|
1256
|
+
engine: Optional[str] = None,
|
|
1252
1257
|
args_schema: list[EntrypointParam] = None,
|
|
1253
|
-
handler=None,
|
|
1258
|
+
handler: Optional[str] = None,
|
|
1254
1259
|
schedule: typing.Union[str, mlrun.common.schemas.ScheduleCronTrigger] = None,
|
|
1255
|
-
ttl=None,
|
|
1256
|
-
image: str = None,
|
|
1260
|
+
ttl: Optional[int] = None,
|
|
1261
|
+
image: Optional[str] = None,
|
|
1257
1262
|
**args,
|
|
1258
1263
|
):
|
|
1259
1264
|
"""Add or update a workflow, specify a name and the code path
|
|
1260
1265
|
|
|
1261
1266
|
:param name: Name of the workflow
|
|
1262
1267
|
:param workflow_path: URL (remote) / Path (absolute or relative to the project code path i.e.
|
|
1263
|
-
|
|
1268
|
+
<project.spec.get_code_path()>/<workflow_path>) for the workflow file.
|
|
1264
1269
|
:param embed: Add the workflow code into the project.yaml
|
|
1265
1270
|
:param engine: Workflow processing engine ("kfp", "local", "remote" or "remote:local")
|
|
1266
1271
|
:param args_schema: List of arg schema definitions (:py:class`~mlrun.model.EntrypointParam`)
|
|
@@ -1803,10 +1808,13 @@ class MlrunProject(ModelObj):
|
|
|
1803
1808
|
) -> mlrun.runtimes.BaseRuntime:
|
|
1804
1809
|
"""
|
|
1805
1810
|
Update or add a monitoring function to the project.
|
|
1811
|
+
Note: to deploy the function after linking it to the project,
|
|
1812
|
+
call `fn.deploy()` where `fn` is the object returned by this method.
|
|
1806
1813
|
|
|
1807
1814
|
examples::
|
|
1808
|
-
project.set_model_monitoring_function(
|
|
1809
|
-
|
|
1815
|
+
project.set_model_monitoring_function(
|
|
1816
|
+
name="myApp", application_class="MyApp", image="mlrun/mlrun"
|
|
1817
|
+
)
|
|
1810
1818
|
|
|
1811
1819
|
:param func: Function object or spec/code url, None refers to current Notebook
|
|
1812
1820
|
:param name: Name of the function (under the project), can be specified with a tag to support
|
|
@@ -1821,11 +1829,16 @@ class MlrunProject(ModelObj):
|
|
|
1821
1829
|
will be enriched with the tag value. (i.e. 'function-name:tag')
|
|
1822
1830
|
:param requirements: A list of python packages
|
|
1823
1831
|
:param requirements_file: Path to a python requirements file
|
|
1824
|
-
:param application_class: Name or an Instance of a class that
|
|
1832
|
+
:param application_class: Name or an Instance of a class that implements the monitoring application.
|
|
1825
1833
|
:param application_kwargs: Additional keyword arguments to be passed to the
|
|
1826
1834
|
monitoring application's constructor.
|
|
1827
1835
|
"""
|
|
1828
1836
|
|
|
1837
|
+
if name in mm_constants.MonitoringFunctionNames.all():
|
|
1838
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
1839
|
+
f"Application name can not be on of the following name : "
|
|
1840
|
+
f"{mm_constants.MonitoringFunctionNames.all()}"
|
|
1841
|
+
)
|
|
1829
1842
|
function_object: RemoteRuntime = None
|
|
1830
1843
|
(
|
|
1831
1844
|
resolved_function_name,
|
|
@@ -1987,27 +2000,76 @@ class MlrunProject(ModelObj):
|
|
|
1987
2000
|
self,
|
|
1988
2001
|
default_controller_image: str = "mlrun/mlrun",
|
|
1989
2002
|
base_period: int = 10,
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
+
image: str = "mlrun/mlrun",
|
|
2004
|
+
deploy_histogram_data_drift_app: bool = True,
|
|
2005
|
+
) -> None:
|
|
2006
|
+
"""
|
|
2007
|
+
Deploy model monitoring application controller, writer and stream functions.
|
|
2008
|
+
While the main goal of the controller function is to handle the monitoring processing and triggering
|
|
2009
|
+
applications, the goal of the model monitoring writer function is to write all the monitoring
|
|
2010
|
+
application results to the databases.
|
|
2011
|
+
The stream function goal is to monitor the log of the data stream. It is triggered when a new log entry
|
|
2012
|
+
is detected. It processes the new events into statistics that are then written to statistics databases.
|
|
2013
|
+
|
|
2014
|
+
|
|
2015
|
+
:param default_controller_image: Deprecated.
|
|
2016
|
+
:param base_period: The time period in minutes in which the model monitoring controller
|
|
2017
|
+
function is triggered. By default, the base period is 10 minutes.
|
|
2018
|
+
:param image: The image of the model monitoring controller, writer, monitoring
|
|
2019
|
+
stream & histogram data drift functions, which are real time nuclio
|
|
2020
|
+
functions. By default, the image is mlrun/mlrun.
|
|
2021
|
+
:param deploy_histogram_data_drift_app: If true, deploy the default histogram-based data drift application.
|
|
2022
|
+
|
|
2003
2023
|
:returns: model monitoring controller job as a dictionary.
|
|
2004
2024
|
"""
|
|
2025
|
+
if default_controller_image != "mlrun/mlrun":
|
|
2026
|
+
# TODO: Remove this in 1.9.0
|
|
2027
|
+
warnings.warn(
|
|
2028
|
+
"'default_controller_image' is deprecated and will be removed in 1.9.0, "
|
|
2029
|
+
"use 'image' instead",
|
|
2030
|
+
FutureWarning,
|
|
2031
|
+
)
|
|
2032
|
+
image = default_controller_image
|
|
2005
2033
|
db = mlrun.db.get_run_db(secrets=self._secrets)
|
|
2006
|
-
|
|
2034
|
+
db.enable_model_monitoring(
|
|
2007
2035
|
project=self.name,
|
|
2008
|
-
|
|
2036
|
+
image=image,
|
|
2009
2037
|
base_period=base_period,
|
|
2010
2038
|
)
|
|
2039
|
+
if deploy_histogram_data_drift_app:
|
|
2040
|
+
fn = self.set_model_monitoring_function(
|
|
2041
|
+
func=str(
|
|
2042
|
+
pathlib.Path(__file__).parent.parent
|
|
2043
|
+
/ "model_monitoring/applications/histogram_data_drift.py"
|
|
2044
|
+
),
|
|
2045
|
+
name=mm_constants.MLRUN_HISTOGRAM_DATA_DRIFT_APP_NAME,
|
|
2046
|
+
application_class="HistogramDataDriftApplication",
|
|
2047
|
+
image=image,
|
|
2048
|
+
)
|
|
2049
|
+
fn.deploy()
|
|
2050
|
+
|
|
2051
|
+
def update_model_monitoring_controller(
|
|
2052
|
+
self,
|
|
2053
|
+
base_period: int = 10,
|
|
2054
|
+
image: str = "mlrun/mlrun",
|
|
2055
|
+
) -> None:
|
|
2056
|
+
"""
|
|
2057
|
+
Redeploy model monitoring application controller functions.
|
|
2058
|
+
|
|
2059
|
+
|
|
2060
|
+
:param base_period: The time period in minutes in which the model monitoring controller function
|
|
2061
|
+
is triggered. By default, the base period is 10 minutes.
|
|
2062
|
+
:param image: The image of the model monitoring controller, writer & monitoring
|
|
2063
|
+
stream functions, which are real time nuclio functions.
|
|
2064
|
+
By default, the image is mlrun/mlrun.
|
|
2065
|
+
:returns: model monitoring controller job as a dictionary.
|
|
2066
|
+
"""
|
|
2067
|
+
db = mlrun.db.get_run_db(secrets=self._secrets)
|
|
2068
|
+
db.update_model_monitoring_controller(
|
|
2069
|
+
project=self.name,
|
|
2070
|
+
base_period=base_period,
|
|
2071
|
+
image=image,
|
|
2072
|
+
)
|
|
2011
2073
|
|
|
2012
2074
|
def disable_model_monitoring(self):
|
|
2013
2075
|
db = mlrun.db.get_run_db(secrets=self._secrets)
|
|
@@ -2015,6 +2077,14 @@ class MlrunProject(ModelObj):
|
|
|
2015
2077
|
project=self.name,
|
|
2016
2078
|
name=mm_constants.MonitoringFunctionNames.APPLICATION_CONTROLLER,
|
|
2017
2079
|
)
|
|
2080
|
+
db.delete_function(
|
|
2081
|
+
project=self.name,
|
|
2082
|
+
name=mm_constants.MonitoringFunctionNames.WRITER,
|
|
2083
|
+
)
|
|
2084
|
+
db.delete_function(
|
|
2085
|
+
project=self.name,
|
|
2086
|
+
name=mm_constants.MonitoringFunctionNames.STREAM,
|
|
2087
|
+
)
|
|
2018
2088
|
|
|
2019
2089
|
def set_function(
|
|
2020
2090
|
self,
|
|
@@ -2448,6 +2518,16 @@ class MlrunProject(ModelObj):
|
|
|
2448
2518
|
f = self.spec._function_definitions.get(name)
|
|
2449
2519
|
if not f:
|
|
2450
2520
|
raise ValueError(f"function named {name} not found")
|
|
2521
|
+
# If this function is already available locally, don't recreate it unless always=True
|
|
2522
|
+
if (
|
|
2523
|
+
isinstance(
|
|
2524
|
+
self.spec._function_objects.get(name, None),
|
|
2525
|
+
mlrun.runtimes.base.BaseRuntime,
|
|
2526
|
+
)
|
|
2527
|
+
and not always
|
|
2528
|
+
):
|
|
2529
|
+
funcs[name] = self.spec._function_objects[name]
|
|
2530
|
+
continue
|
|
2451
2531
|
if hasattr(f, "to_dict"):
|
|
2452
2532
|
name, func = _init_function_from_obj(f, self, name)
|
|
2453
2533
|
else:
|
|
@@ -2787,7 +2867,7 @@ class MlrunProject(ModelObj):
|
|
|
2787
2867
|
def export(self, filepath=None, include_files: str = None):
|
|
2788
2868
|
"""save the project object into a yaml file or zip archive (default to project.yaml)
|
|
2789
2869
|
|
|
2790
|
-
By default the project object is exported to a yaml file, when the filepath suffix is '.zip'
|
|
2870
|
+
By default, the project object is exported to a yaml file, when the filepath suffix is '.zip'
|
|
2791
2871
|
the project context dir (code files) are also copied into the zip, the archive path can include
|
|
2792
2872
|
DataItem urls (for remote object storage, e.g. s3://<bucket>/<path>).
|
|
2793
2873
|
|
|
@@ -2812,19 +2892,19 @@ class MlrunProject(ModelObj):
|
|
|
2812
2892
|
|
|
2813
2893
|
if archive_code:
|
|
2814
2894
|
files_filter = include_files or "**"
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2895
|
+
with tempfile.NamedTemporaryFile(suffix=".zip") as f:
|
|
2896
|
+
remote_file = "://" in filepath
|
|
2897
|
+
fpath = f.name if remote_file else filepath
|
|
2898
|
+
with zipfile.ZipFile(fpath, "w") as zipf:
|
|
2899
|
+
for file_path in glob.iglob(
|
|
2900
|
+
f"{project_dir}/{files_filter}", recursive=True
|
|
2901
|
+
):
|
|
2902
|
+
write_path = pathlib.Path(file_path)
|
|
2903
|
+
zipf.write(
|
|
2904
|
+
write_path, arcname=write_path.relative_to(project_dir)
|
|
2905
|
+
)
|
|
2906
|
+
if remote_file:
|
|
2907
|
+
mlrun.get_dataitem(filepath).upload(zipf.filename)
|
|
2828
2908
|
|
|
2829
2909
|
def set_model_monitoring_credentials(
|
|
2830
2910
|
self,
|
|
@@ -3546,6 +3626,64 @@ class MlrunProject(ModelObj):
|
|
|
3546
3626
|
"""
|
|
3547
3627
|
self.spec.remove_custom_packager(packager=packager)
|
|
3548
3628
|
|
|
3629
|
+
def store_api_gateway(
|
|
3630
|
+
self, api_gateway: mlrun.runtimes.nuclio.api_gateway.APIGateway
|
|
3631
|
+
) -> mlrun.runtimes.nuclio.api_gateway.APIGateway:
|
|
3632
|
+
"""
|
|
3633
|
+
Creates or updates a Nuclio API Gateway using the provided APIGateway object.
|
|
3634
|
+
|
|
3635
|
+
This method interacts with the MLRun service to create/update a Nuclio API Gateway based on the provided
|
|
3636
|
+
APIGateway object. Once done, it returns the updated APIGateway object containing all fields propagated
|
|
3637
|
+
on MLRun and Nuclio sides, such as the 'host' attribute.
|
|
3638
|
+
Nuclio docs here: https://docs.nuclio.io/en/latest/reference/api-gateway/http.html
|
|
3639
|
+
|
|
3640
|
+
:param api_gateway: An instance of :py:class:`~mlrun.runtimes.nuclio.APIGateway` representing the configuration
|
|
3641
|
+
of the API Gateway to be created
|
|
3642
|
+
|
|
3643
|
+
@return: An instance of :py:class:`~mlrun.runtimes.nuclio.APIGateway` with all fields populated based on the
|
|
3644
|
+
information retrieved from the Nuclio API
|
|
3645
|
+
"""
|
|
3646
|
+
|
|
3647
|
+
api_gateway_json = mlrun.db.get_run_db().store_api_gateway(
|
|
3648
|
+
api_gateway=api_gateway,
|
|
3649
|
+
project=self.name,
|
|
3650
|
+
)
|
|
3651
|
+
|
|
3652
|
+
if api_gateway_json:
|
|
3653
|
+
# fill in all the fields in the user's api_gateway object
|
|
3654
|
+
api_gateway = mlrun.runtimes.nuclio.api_gateway.APIGateway.from_scheme(
|
|
3655
|
+
api_gateway_json
|
|
3656
|
+
)
|
|
3657
|
+
return api_gateway
|
|
3658
|
+
|
|
3659
|
+
def list_api_gateways(self) -> list[mlrun.runtimes.nuclio.api_gateway.APIGateway]:
|
|
3660
|
+
"""
|
|
3661
|
+
Retrieves a list of Nuclio API gateways associated with the project.
|
|
3662
|
+
|
|
3663
|
+
@return: List of :py:class:`~mlrun.runtimes.nuclio.api_gateway.APIGateway` objects representing
|
|
3664
|
+
the Nuclio API gateways associated with the project.
|
|
3665
|
+
"""
|
|
3666
|
+
gateways_list = mlrun.db.get_run_db().list_api_gateways(self.name)
|
|
3667
|
+
return [
|
|
3668
|
+
mlrun.runtimes.nuclio.api_gateway.APIGateway.from_scheme(gateway_dict)
|
|
3669
|
+
for gateway_dict in gateways_list.api_gateways.values()
|
|
3670
|
+
]
|
|
3671
|
+
|
|
3672
|
+
def get_api_gateway(
|
|
3673
|
+
self,
|
|
3674
|
+
name: str,
|
|
3675
|
+
) -> mlrun.runtimes.nuclio.api_gateway.APIGateway:
|
|
3676
|
+
"""
|
|
3677
|
+
Retrieves an API gateway by name instance.
|
|
3678
|
+
|
|
3679
|
+
:param name: The name of the API gateway to retrieve.
|
|
3680
|
+
|
|
3681
|
+
Returns:
|
|
3682
|
+
mlrun.runtimes.nuclio.APIGateway: An instance of APIGateway.
|
|
3683
|
+
"""
|
|
3684
|
+
|
|
3685
|
+
return mlrun.db.get_run_db().get_api_gateway(name=name, project=self.name)
|
|
3686
|
+
|
|
3549
3687
|
def _run_authenticated_git_action(
|
|
3550
3688
|
self,
|
|
3551
3689
|
action: Callable,
|
mlrun/run.py
CHANGED
|
@@ -628,7 +628,7 @@ def code_to_function(
|
|
|
628
628
|
- spark: run distributed Spark job using Spark Kubernetes Operator
|
|
629
629
|
- remote-spark: run distributed Spark job on remote Spark service
|
|
630
630
|
|
|
631
|
-
Learn more about
|
|
631
|
+
Learn more about [Kinds of function (runtimes)](../concepts/functions-overview.html).
|
|
632
632
|
|
|
633
633
|
:param name: function name, typically best to use hyphen-case
|
|
634
634
|
:param project: project used to namespace the function, defaults to 'default'
|
mlrun/runtimes/base.py
CHANGED
|
@@ -15,6 +15,7 @@ import enum
|
|
|
15
15
|
import http
|
|
16
16
|
import re
|
|
17
17
|
import typing
|
|
18
|
+
import warnings
|
|
18
19
|
from base64 import b64encode
|
|
19
20
|
from os import environ
|
|
20
21
|
from typing import Callable, Optional, Union
|
|
@@ -125,7 +126,7 @@ class FunctionSpec(ModelObj):
|
|
|
125
126
|
self.allow_empty_resources = None
|
|
126
127
|
# The build.source is cloned/extracted to the specified clone_target_dir
|
|
127
128
|
# if a relative path is specified, it will be enriched with a temp dir path
|
|
128
|
-
self.
|
|
129
|
+
self._clone_target_dir = clone_target_dir or None
|
|
129
130
|
|
|
130
131
|
@property
|
|
131
132
|
def build(self) -> ImageBuilder:
|
|
@@ -135,6 +136,28 @@ class FunctionSpec(ModelObj):
|
|
|
135
136
|
def build(self, build):
|
|
136
137
|
self._build = self._verify_dict(build, "build", ImageBuilder)
|
|
137
138
|
|
|
139
|
+
@property
|
|
140
|
+
def clone_target_dir(self):
|
|
141
|
+
# TODO: remove this property in 1.9.0
|
|
142
|
+
if self.build.source_code_target_dir:
|
|
143
|
+
warnings.warn(
|
|
144
|
+
"The clone_target_dir attribute is deprecated in 1.6.2 and will be removed in 1.9.0. "
|
|
145
|
+
"Use spec.build.source_code_target_dir instead.",
|
|
146
|
+
FutureWarning,
|
|
147
|
+
)
|
|
148
|
+
return self.build.source_code_target_dir
|
|
149
|
+
|
|
150
|
+
@clone_target_dir.setter
|
|
151
|
+
def clone_target_dir(self, clone_target_dir):
|
|
152
|
+
# TODO: remove this property in 1.9.0
|
|
153
|
+
if clone_target_dir:
|
|
154
|
+
warnings.warn(
|
|
155
|
+
"The clone_target_dir attribute is deprecated in 1.6.2 and will be removed in 1.9.0. "
|
|
156
|
+
"Use spec.build.source_code_target_dir instead.",
|
|
157
|
+
FutureWarning,
|
|
158
|
+
)
|
|
159
|
+
self.build.source_code_target_dir = clone_target_dir
|
|
160
|
+
|
|
138
161
|
def enrich_function_preemption_spec(self):
|
|
139
162
|
pass
|
|
140
163
|
|
|
@@ -851,7 +874,7 @@ class BaseRuntime(ModelObj):
|
|
|
851
874
|
data = dict_to_json(struct)
|
|
852
875
|
stores = store_manager.set(secrets)
|
|
853
876
|
target = target or "function.yaml"
|
|
854
|
-
datastore, subpath = stores.get_or_create_store(target)
|
|
877
|
+
datastore, subpath, url = stores.get_or_create_store(target)
|
|
855
878
|
datastore.put(subpath, data)
|
|
856
879
|
logger.info(f"function spec saved to path: {target}")
|
|
857
880
|
return self
|
mlrun/runtimes/kubejob.py
CHANGED
|
@@ -73,7 +73,7 @@ class KubejobRuntime(KubeResource):
|
|
|
73
73
|
if workdir:
|
|
74
74
|
self.spec.workdir = workdir
|
|
75
75
|
if target_dir:
|
|
76
|
-
self.spec.
|
|
76
|
+
self.spec.build.source_code_target_dir = target_dir
|
|
77
77
|
|
|
78
78
|
self.spec.build.load_source_on_run = pull_at_runtime
|
|
79
79
|
if (
|
|
@@ -232,8 +232,10 @@ class KubejobRuntime(KubeResource):
|
|
|
232
232
|
self.spec.build.base_image = self.spec.build.base_image or get_in(
|
|
233
233
|
data, "data.spec.build.base_image"
|
|
234
234
|
)
|
|
235
|
-
#
|
|
236
|
-
self.spec.
|
|
235
|
+
# Get the source target dir in case it was enriched due to loading source
|
|
236
|
+
self.spec.build.source_code_target_dir = get_in(
|
|
237
|
+
data, "data.spec.build.source_code_target_dir"
|
|
238
|
+
) or get_in(data, "data.spec.clone_target_dir")
|
|
237
239
|
ready = data.get("ready", False)
|
|
238
240
|
if not ready:
|
|
239
241
|
logger.info(
|
mlrun/runtimes/local.py
CHANGED
|
@@ -218,7 +218,7 @@ class LocalRuntime(BaseRuntime, ParallelRunner):
|
|
|
218
218
|
if workdir:
|
|
219
219
|
self.spec.workdir = workdir
|
|
220
220
|
if target_dir:
|
|
221
|
-
self.spec.
|
|
221
|
+
self.spec.build.source_code_target_dir = target_dir
|
|
222
222
|
|
|
223
223
|
def is_deployed(self):
|
|
224
224
|
return True
|
|
@@ -240,7 +240,7 @@ class LocalRuntime(BaseRuntime, ParallelRunner):
|
|
|
240
240
|
if self.spec.build.source and not hasattr(self, "_is_run_local"):
|
|
241
241
|
target_dir = extract_source(
|
|
242
242
|
self.spec.build.source,
|
|
243
|
-
self.spec.
|
|
243
|
+
self.spec.build.source_code_target_dir,
|
|
244
244
|
secrets=execution._secrets_manager,
|
|
245
245
|
)
|
|
246
246
|
if workdir and not workdir.startswith("/"):
|
|
@@ -195,13 +195,13 @@ class AbstractMPIJobRuntime(KubejobRuntime, abc.ABC):
|
|
|
195
195
|
if steps_per_sample is not None:
|
|
196
196
|
horovod_autotune_settings["autotune-steps-per-sample"] = steps_per_sample
|
|
197
197
|
if bayes_opt_max_samples is not None:
|
|
198
|
-
horovod_autotune_settings[
|
|
199
|
-
|
|
200
|
-
|
|
198
|
+
horovod_autotune_settings["autotune-bayes-opt-max-samples"] = (
|
|
199
|
+
bayes_opt_max_samples
|
|
200
|
+
)
|
|
201
201
|
if gaussian_process_noise is not None:
|
|
202
|
-
horovod_autotune_settings[
|
|
203
|
-
|
|
204
|
-
|
|
202
|
+
horovod_autotune_settings["autotune-gaussian-process-noise"] = (
|
|
203
|
+
gaussian_process_noise
|
|
204
|
+
)
|
|
205
205
|
|
|
206
206
|
self.set_envs(horovod_autotune_settings)
|
|
207
207
|
|