mlrun 1.7.0rc28__py3-none-any.whl → 1.7.0rc55__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/alerts/alert.py +75 -8
- mlrun/artifacts/base.py +1 -0
- mlrun/artifacts/manager.py +9 -2
- mlrun/common/constants.py +4 -1
- mlrun/common/db/sql_session.py +3 -2
- mlrun/common/formatters/__init__.py +1 -0
- mlrun/common/formatters/artifact.py +1 -0
- mlrun/{model_monitoring/application.py → common/formatters/feature_set.py} +20 -6
- mlrun/common/formatters/run.py +3 -0
- mlrun/common/helpers.py +0 -1
- mlrun/common/schemas/__init__.py +3 -1
- mlrun/common/schemas/alert.py +15 -12
- mlrun/common/schemas/api_gateway.py +6 -6
- mlrun/common/schemas/auth.py +5 -0
- mlrun/common/schemas/client_spec.py +0 -1
- mlrun/common/schemas/common.py +7 -4
- mlrun/common/schemas/frontend_spec.py +7 -0
- mlrun/common/schemas/function.py +7 -0
- mlrun/common/schemas/model_monitoring/__init__.py +4 -3
- mlrun/common/schemas/model_monitoring/constants.py +41 -26
- mlrun/common/schemas/model_monitoring/model_endpoints.py +23 -47
- mlrun/common/schemas/notification.py +69 -12
- mlrun/common/schemas/project.py +45 -12
- mlrun/common/schemas/workflow.py +10 -2
- mlrun/common/types.py +1 -0
- mlrun/config.py +91 -35
- mlrun/data_types/data_types.py +6 -1
- mlrun/data_types/spark.py +2 -2
- mlrun/data_types/to_pandas.py +57 -25
- mlrun/datastore/__init__.py +1 -0
- mlrun/datastore/alibaba_oss.py +3 -2
- mlrun/datastore/azure_blob.py +125 -37
- mlrun/datastore/base.py +42 -21
- mlrun/datastore/datastore.py +4 -2
- mlrun/datastore/datastore_profile.py +1 -1
- mlrun/datastore/dbfs_store.py +3 -7
- mlrun/datastore/filestore.py +1 -3
- mlrun/datastore/google_cloud_storage.py +85 -29
- mlrun/datastore/inmem.py +4 -1
- mlrun/datastore/redis.py +1 -0
- mlrun/datastore/s3.py +25 -12
- mlrun/datastore/sources.py +76 -4
- mlrun/datastore/spark_utils.py +30 -0
- mlrun/datastore/storeytargets.py +151 -0
- mlrun/datastore/targets.py +102 -131
- mlrun/datastore/v3io.py +1 -0
- mlrun/db/base.py +15 -6
- mlrun/db/httpdb.py +57 -28
- mlrun/db/nopdb.py +29 -5
- mlrun/errors.py +20 -3
- mlrun/execution.py +46 -5
- mlrun/feature_store/api.py +25 -1
- mlrun/feature_store/common.py +6 -11
- mlrun/feature_store/feature_vector.py +3 -1
- mlrun/feature_store/retrieval/job.py +4 -1
- mlrun/feature_store/retrieval/spark_merger.py +10 -39
- mlrun/feature_store/steps.py +8 -0
- mlrun/frameworks/_common/plan.py +3 -3
- mlrun/frameworks/_ml_common/plan.py +1 -1
- mlrun/frameworks/parallel_coordinates.py +2 -3
- mlrun/frameworks/sklearn/mlrun_interface.py +13 -3
- mlrun/k8s_utils.py +48 -2
- mlrun/launcher/client.py +6 -6
- mlrun/launcher/local.py +2 -2
- mlrun/model.py +215 -34
- mlrun/model_monitoring/api.py +38 -24
- mlrun/model_monitoring/applications/__init__.py +1 -2
- mlrun/model_monitoring/applications/_application_steps.py +60 -29
- mlrun/model_monitoring/applications/base.py +2 -174
- mlrun/model_monitoring/applications/context.py +197 -70
- mlrun/model_monitoring/applications/evidently_base.py +11 -85
- mlrun/model_monitoring/applications/histogram_data_drift.py +21 -16
- mlrun/model_monitoring/applications/results.py +4 -4
- mlrun/model_monitoring/controller.py +110 -282
- mlrun/model_monitoring/db/stores/__init__.py +8 -3
- mlrun/model_monitoring/db/stores/base/store.py +3 -0
- mlrun/model_monitoring/db/stores/sqldb/models/base.py +9 -7
- mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +18 -3
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +43 -23
- mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +48 -35
- mlrun/model_monitoring/db/tsdb/__init__.py +7 -2
- mlrun/model_monitoring/db/tsdb/base.py +147 -15
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +94 -55
- mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +0 -3
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +144 -38
- mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +44 -3
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +246 -57
- mlrun/model_monitoring/helpers.py +70 -50
- mlrun/model_monitoring/stream_processing.py +96 -195
- mlrun/model_monitoring/writer.py +13 -5
- mlrun/package/packagers/default_packager.py +2 -2
- mlrun/projects/operations.py +16 -8
- mlrun/projects/pipelines.py +126 -115
- mlrun/projects/project.py +286 -129
- mlrun/render.py +3 -3
- mlrun/run.py +38 -19
- mlrun/runtimes/__init__.py +19 -8
- mlrun/runtimes/base.py +4 -1
- mlrun/runtimes/daskjob.py +1 -1
- mlrun/runtimes/funcdoc.py +1 -1
- mlrun/runtimes/kubejob.py +6 -6
- mlrun/runtimes/local.py +12 -5
- mlrun/runtimes/nuclio/api_gateway.py +68 -8
- mlrun/runtimes/nuclio/application/application.py +307 -70
- mlrun/runtimes/nuclio/function.py +63 -14
- mlrun/runtimes/nuclio/serving.py +10 -10
- mlrun/runtimes/pod.py +25 -19
- mlrun/runtimes/remotesparkjob.py +2 -5
- mlrun/runtimes/sparkjob/spark3job.py +16 -17
- mlrun/runtimes/utils.py +34 -0
- mlrun/serving/routers.py +2 -5
- mlrun/serving/server.py +37 -19
- mlrun/serving/states.py +30 -3
- mlrun/serving/v2_serving.py +44 -35
- mlrun/track/trackers/mlflow_tracker.py +5 -0
- mlrun/utils/async_http.py +1 -1
- mlrun/utils/db.py +18 -0
- mlrun/utils/helpers.py +150 -36
- mlrun/utils/http.py +1 -1
- mlrun/utils/notifications/notification/__init__.py +0 -1
- mlrun/utils/notifications/notification/webhook.py +8 -1
- mlrun/utils/notifications/notification_pusher.py +1 -1
- mlrun/utils/v3io_clients.py +2 -2
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/METADATA +153 -66
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/RECORD +131 -134
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/WHEEL +1 -1
- mlrun/feature_store/retrieval/conversion.py +0 -271
- mlrun/model_monitoring/controller_handler.py +0 -37
- mlrun/model_monitoring/evidently_application.py +0 -20
- mlrun/model_monitoring/prometheus.py +0 -216
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/LICENSE +0 -0
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.0rc28.dist-info → mlrun-1.7.0rc55.dist-info}/top_level.txt +0 -0
mlrun/projects/project.py
CHANGED
|
@@ -18,6 +18,7 @@ import glob
|
|
|
18
18
|
import http
|
|
19
19
|
import importlib.util as imputil
|
|
20
20
|
import json
|
|
21
|
+
import os
|
|
21
22
|
import pathlib
|
|
22
23
|
import shutil
|
|
23
24
|
import tempfile
|
|
@@ -25,6 +26,7 @@ import typing
|
|
|
25
26
|
import uuid
|
|
26
27
|
import warnings
|
|
27
28
|
import zipfile
|
|
29
|
+
from copy import deepcopy
|
|
28
30
|
from os import environ, makedirs, path
|
|
29
31
|
from typing import Callable, Optional, Union
|
|
30
32
|
|
|
@@ -65,13 +67,7 @@ from ..features import Feature
|
|
|
65
67
|
from ..model import EntrypointParam, ImageBuilder, ModelObj
|
|
66
68
|
from ..run import code_to_function, get_object, import_function, new_function
|
|
67
69
|
from ..secrets import SecretsStore
|
|
68
|
-
from ..utils import
|
|
69
|
-
is_ipython,
|
|
70
|
-
is_relative_path,
|
|
71
|
-
is_yaml_path,
|
|
72
|
-
logger,
|
|
73
|
-
update_in,
|
|
74
|
-
)
|
|
70
|
+
from ..utils import is_jupyter, is_relative_path, is_yaml_path, logger, update_in
|
|
75
71
|
from ..utils.clones import (
|
|
76
72
|
add_credentials_git_remote_url,
|
|
77
73
|
clone_git,
|
|
@@ -251,8 +247,7 @@ def new_project(
|
|
|
251
247
|
project.spec.description = description
|
|
252
248
|
|
|
253
249
|
if default_function_node_selector:
|
|
254
|
-
|
|
255
|
-
project.spec.default_function_node_selector[key] = val
|
|
250
|
+
project.spec.default_function_node_selector = default_function_node_selector
|
|
256
251
|
|
|
257
252
|
if parameters:
|
|
258
253
|
# Enable setting project parameters at load time, can be used to customize the project_setup
|
|
@@ -517,17 +512,24 @@ def get_or_create_project(
|
|
|
517
512
|
parameters=parameters,
|
|
518
513
|
allow_cross_project=allow_cross_project,
|
|
519
514
|
)
|
|
520
|
-
logger.info("Project loaded successfully", project_name=name)
|
|
515
|
+
logger.info("Project loaded successfully", project_name=project.name)
|
|
521
516
|
return project
|
|
522
517
|
except mlrun.errors.MLRunNotFoundError:
|
|
523
|
-
logger.debug(
|
|
518
|
+
logger.debug(
|
|
519
|
+
"Project not found in db", project_name=name, user_project=user_project
|
|
520
|
+
)
|
|
524
521
|
|
|
525
522
|
spec_path = path.join(context, subpath or "", "project.yaml")
|
|
526
523
|
load_from_path = url or path.isfile(spec_path)
|
|
527
524
|
# do not nest under "try" or else the exceptions raised below will be logged along with the "not found" message
|
|
528
525
|
if load_from_path:
|
|
529
526
|
# loads a project from archive or local project.yaml
|
|
530
|
-
logger.info(
|
|
527
|
+
logger.info(
|
|
528
|
+
"Loading project from path",
|
|
529
|
+
project_name=name,
|
|
530
|
+
user_project=user_project,
|
|
531
|
+
path=url or context,
|
|
532
|
+
)
|
|
531
533
|
project = load_project(
|
|
532
534
|
context,
|
|
533
535
|
url,
|
|
@@ -544,7 +546,7 @@ def get_or_create_project(
|
|
|
544
546
|
|
|
545
547
|
logger.info(
|
|
546
548
|
"Project loaded successfully",
|
|
547
|
-
project_name=name,
|
|
549
|
+
project_name=project.name,
|
|
548
550
|
path=url or context,
|
|
549
551
|
stored_in_db=save,
|
|
550
552
|
)
|
|
@@ -562,7 +564,9 @@ def get_or_create_project(
|
|
|
562
564
|
save=save,
|
|
563
565
|
parameters=parameters,
|
|
564
566
|
)
|
|
565
|
-
logger.info(
|
|
567
|
+
logger.info(
|
|
568
|
+
"Project created successfully", project_name=project.name, stored_in_db=save
|
|
569
|
+
)
|
|
566
570
|
return project
|
|
567
571
|
|
|
568
572
|
|
|
@@ -600,6 +604,10 @@ def _run_project_setup(
|
|
|
600
604
|
if hasattr(mod, "setup"):
|
|
601
605
|
try:
|
|
602
606
|
project = getattr(mod, "setup")(project)
|
|
607
|
+
if not project or not isinstance(project, mlrun.projects.MlrunProject):
|
|
608
|
+
raise ValueError(
|
|
609
|
+
"MLRun project_setup:setup() must return a project object"
|
|
610
|
+
)
|
|
603
611
|
except Exception as exc:
|
|
604
612
|
logger.error(
|
|
605
613
|
"Failed to run project_setup script",
|
|
@@ -610,7 +618,9 @@ def _run_project_setup(
|
|
|
610
618
|
if save:
|
|
611
619
|
project.save()
|
|
612
620
|
else:
|
|
613
|
-
logger.warn(
|
|
621
|
+
logger.warn(
|
|
622
|
+
f"skipping setup, setup() handler was not found in {path.basename(setup_file_path)}"
|
|
623
|
+
)
|
|
614
624
|
return project
|
|
615
625
|
|
|
616
626
|
|
|
@@ -698,7 +708,7 @@ def _load_project_from_db(url, secrets, user_project=False):
|
|
|
698
708
|
|
|
699
709
|
def _delete_project_from_db(project_name, secrets, deletion_strategy):
|
|
700
710
|
db = mlrun.db.get_run_db(secrets=secrets)
|
|
701
|
-
|
|
711
|
+
db.delete_project(project_name, deletion_strategy=deletion_strategy)
|
|
702
712
|
|
|
703
713
|
|
|
704
714
|
def _load_project_file(url, name="", secrets=None, allow_cross_project=None):
|
|
@@ -725,7 +735,7 @@ def _project_instance_from_struct(struct, name, allow_cross_project):
|
|
|
725
735
|
# TODO: Remove this warning in version 1.9.0 and also fix cli to support allow_cross_project
|
|
726
736
|
warnings.warn(
|
|
727
737
|
f"Project {name=} is different than specified on the context's project yaml. "
|
|
728
|
-
"This behavior is deprecated and will not be supported
|
|
738
|
+
"This behavior is deprecated and will not be supported from version 1.9.0."
|
|
729
739
|
)
|
|
730
740
|
logger.warn(error_message)
|
|
731
741
|
elif allow_cross_project:
|
|
@@ -859,7 +869,7 @@ class ProjectSpec(ModelObj):
|
|
|
859
869
|
# in a tuple where the first index is the packager module's path (str) and the second is a flag (bool) for
|
|
860
870
|
# whether it is mandatory for a run (raise exception on collection error) or not.
|
|
861
871
|
self.custom_packagers = custom_packagers or []
|
|
862
|
-
self.
|
|
872
|
+
self._default_function_node_selector = default_function_node_selector or None
|
|
863
873
|
|
|
864
874
|
@property
|
|
865
875
|
def source(self) -> str:
|
|
@@ -1034,6 +1044,14 @@ class ProjectSpec(ModelObj):
|
|
|
1034
1044
|
if key in self._artifacts:
|
|
1035
1045
|
del self._artifacts[key]
|
|
1036
1046
|
|
|
1047
|
+
@property
|
|
1048
|
+
def default_function_node_selector(self):
|
|
1049
|
+
return self._default_function_node_selector
|
|
1050
|
+
|
|
1051
|
+
@default_function_node_selector.setter
|
|
1052
|
+
def default_function_node_selector(self, node_selector: dict[str, str]):
|
|
1053
|
+
self._default_function_node_selector = deepcopy(node_selector)
|
|
1054
|
+
|
|
1037
1055
|
@property
|
|
1038
1056
|
def build(self) -> ImageBuilder:
|
|
1039
1057
|
return self._build
|
|
@@ -1534,7 +1552,7 @@ class MlrunProject(ModelObj):
|
|
|
1534
1552
|
url = path.normpath(path.join(self.spec.get_code_path(), url))
|
|
1535
1553
|
|
|
1536
1554
|
if (not in_context or check_path_in_context) and not path.isfile(url):
|
|
1537
|
-
raise
|
|
1555
|
+
raise FileNotFoundError(f"{url} not found")
|
|
1538
1556
|
|
|
1539
1557
|
return url, in_context
|
|
1540
1558
|
|
|
@@ -1542,15 +1560,15 @@ class MlrunProject(ModelObj):
|
|
|
1542
1560
|
self,
|
|
1543
1561
|
item,
|
|
1544
1562
|
body=None,
|
|
1545
|
-
tag="",
|
|
1546
|
-
local_path="",
|
|
1547
|
-
artifact_path=None,
|
|
1548
|
-
format=None,
|
|
1549
|
-
upload=None,
|
|
1550
|
-
labels=None,
|
|
1551
|
-
target_path=None,
|
|
1563
|
+
tag: str = "",
|
|
1564
|
+
local_path: str = "",
|
|
1565
|
+
artifact_path: Optional[str] = None,
|
|
1566
|
+
format: Optional[str] = None,
|
|
1567
|
+
upload: Optional[bool] = None,
|
|
1568
|
+
labels: Optional[dict[str, str]] = None,
|
|
1569
|
+
target_path: Optional[str] = None,
|
|
1552
1570
|
**kwargs,
|
|
1553
|
-
):
|
|
1571
|
+
) -> Artifact:
|
|
1554
1572
|
"""Log an output artifact and optionally upload it to datastore
|
|
1555
1573
|
|
|
1556
1574
|
If the artifact already exists with the same key and tag, it will be overwritten.
|
|
@@ -1575,7 +1593,9 @@ class MlrunProject(ModelObj):
|
|
|
1575
1593
|
:param format: artifact file format: csv, png, ..
|
|
1576
1594
|
:param tag: version tag
|
|
1577
1595
|
:param target_path: absolute target path (instead of using artifact_path + local_path)
|
|
1578
|
-
:param upload: upload to datastore
|
|
1596
|
+
:param upload: Whether to upload the artifact to the datastore. If not provided, and the `local_path`
|
|
1597
|
+
is not a directory, upload occurs by default. Directories are uploaded only when this
|
|
1598
|
+
flag is explicitly set to `True`.
|
|
1579
1599
|
:param labels: a set of key/value labels to tag the artifact with
|
|
1580
1600
|
|
|
1581
1601
|
:returns: artifact object
|
|
@@ -1649,7 +1669,7 @@ class MlrunProject(ModelObj):
|
|
|
1649
1669
|
stats=None,
|
|
1650
1670
|
target_path="",
|
|
1651
1671
|
extra_data=None,
|
|
1652
|
-
label_column: str = None,
|
|
1672
|
+
label_column: Optional[str] = None,
|
|
1653
1673
|
**kwargs,
|
|
1654
1674
|
) -> DatasetArtifact:
|
|
1655
1675
|
"""
|
|
@@ -1726,15 +1746,15 @@ class MlrunProject(ModelObj):
|
|
|
1726
1746
|
artifact_path=None,
|
|
1727
1747
|
upload=None,
|
|
1728
1748
|
labels=None,
|
|
1729
|
-
inputs: list[Feature] = None,
|
|
1730
|
-
outputs: list[Feature] = None,
|
|
1731
|
-
feature_vector: str = None,
|
|
1732
|
-
feature_weights: list = None,
|
|
1749
|
+
inputs: Optional[list[Feature]] = None,
|
|
1750
|
+
outputs: Optional[list[Feature]] = None,
|
|
1751
|
+
feature_vector: Optional[str] = None,
|
|
1752
|
+
feature_weights: Optional[list] = None,
|
|
1733
1753
|
training_set=None,
|
|
1734
1754
|
label_column=None,
|
|
1735
1755
|
extra_data=None,
|
|
1736
1756
|
**kwargs,
|
|
1737
|
-
):
|
|
1757
|
+
) -> ModelArtifact:
|
|
1738
1758
|
"""Log a model artifact and optionally upload it to datastore
|
|
1739
1759
|
|
|
1740
1760
|
If the model already exists with the same key and tag, it will be overwritten.
|
|
@@ -1930,7 +1950,6 @@ class MlrunProject(ModelObj):
|
|
|
1930
1950
|
application_class: typing.Union[
|
|
1931
1951
|
str,
|
|
1932
1952
|
mm_app.ModelMonitoringApplicationBase,
|
|
1933
|
-
mm_app.ModelMonitoringApplicationBaseV2,
|
|
1934
1953
|
] = None,
|
|
1935
1954
|
name: str = None,
|
|
1936
1955
|
image: str = None,
|
|
@@ -1998,7 +2017,6 @@ class MlrunProject(ModelObj):
|
|
|
1998
2017
|
application_class: typing.Union[
|
|
1999
2018
|
str,
|
|
2000
2019
|
mm_app.ModelMonitoringApplicationBase,
|
|
2001
|
-
mm_app.ModelMonitoringApplicationBaseV2,
|
|
2002
2020
|
] = None,
|
|
2003
2021
|
name: str = None,
|
|
2004
2022
|
image: str = None,
|
|
@@ -2056,7 +2074,6 @@ class MlrunProject(ModelObj):
|
|
|
2056
2074
|
application_class: typing.Union[
|
|
2057
2075
|
str,
|
|
2058
2076
|
mm_app.ModelMonitoringApplicationBase,
|
|
2059
|
-
mm_app.ModelMonitoringApplicationBaseV2,
|
|
2060
2077
|
None,
|
|
2061
2078
|
] = None,
|
|
2062
2079
|
name: typing.Optional[str] = None,
|
|
@@ -2145,7 +2162,8 @@ class MlrunProject(ModelObj):
|
|
|
2145
2162
|
|
|
2146
2163
|
:param default_controller_image: Deprecated.
|
|
2147
2164
|
:param base_period: The time period in minutes in which the model monitoring controller
|
|
2148
|
-
function is triggered. By default, the base period is 10 minutes
|
|
2165
|
+
function is triggered. By default, the base period is 10 minutes
|
|
2166
|
+
(which is also the minimum value for production environments).
|
|
2149
2167
|
:param image: The image of the model monitoring controller, writer, monitoring
|
|
2150
2168
|
stream & histogram data drift functions, which are real time nuclio
|
|
2151
2169
|
functions. By default, the image is mlrun/mlrun.
|
|
@@ -2164,6 +2182,12 @@ class MlrunProject(ModelObj):
|
|
|
2164
2182
|
FutureWarning,
|
|
2165
2183
|
)
|
|
2166
2184
|
image = default_controller_image
|
|
2185
|
+
if base_period < 10:
|
|
2186
|
+
logger.warn(
|
|
2187
|
+
"enable_model_monitoring: 'base_period' < 10 minutes is not supported in production environments",
|
|
2188
|
+
project=self.name,
|
|
2189
|
+
)
|
|
2190
|
+
|
|
2167
2191
|
db = mlrun.db.get_run_db(secrets=self._secrets)
|
|
2168
2192
|
db.enable_model_monitoring(
|
|
2169
2193
|
project=self.name,
|
|
@@ -2310,31 +2334,51 @@ class MlrunProject(ModelObj):
|
|
|
2310
2334
|
requirements: typing.Union[str, list[str]] = None,
|
|
2311
2335
|
requirements_file: str = "",
|
|
2312
2336
|
) -> mlrun.runtimes.BaseRuntime:
|
|
2313
|
-
"""
|
|
2337
|
+
"""
|
|
2338
|
+
| Update or add a function object to the project.
|
|
2339
|
+
| Function can be provided as an object (func) or a .py/.ipynb/.yaml URL.
|
|
2314
2340
|
|
|
2315
|
-
function
|
|
2316
|
-
|
|
2341
|
+
| Creating a function from a single file is done by specifying ``func`` and disabling ``with_repo``.
|
|
2342
|
+
| Creating a function with project source (specify ``with_repo=True``):
|
|
2343
|
+
| 1. Specify a relative ``func`` path.
|
|
2344
|
+
| 2. Specify a module ``handler`` (e.g. ``handler=package.package.func``) without ``func``.
|
|
2345
|
+
| Creating a function with non project source is done by specifying a module ``handler`` and on the
|
|
2346
|
+
returned function set the source with ``function.with_source_archive(<source>)``.
|
|
2317
2347
|
|
|
2318
|
-
|
|
2319
|
-
MLRun DB e.g. db://project/func:ver
|
|
2320
|
-
functions hub/market: e.g. hub://auto-trainer:master
|
|
2348
|
+
Support URL prefixes:
|
|
2321
2349
|
|
|
2322
|
-
|
|
2350
|
+
| Object (s3://, v3io://, ..)
|
|
2351
|
+
| MLRun DB e.g. db://project/func:ver
|
|
2352
|
+
| Functions hub/market: e.g. hub://auto-trainer:master
|
|
2353
|
+
|
|
2354
|
+
Examples::
|
|
2323
2355
|
|
|
2324
2356
|
proj.set_function(func_object)
|
|
2325
|
-
proj.set_function(
|
|
2326
|
-
"./src/mycode.py", "ingest", image="myrepo/ing:latest", with_repo=True
|
|
2327
|
-
)
|
|
2328
2357
|
proj.set_function("http://.../mynb.ipynb", "train")
|
|
2329
2358
|
proj.set_function("./func.yaml")
|
|
2330
2359
|
proj.set_function("hub://get_toy_data", "getdata")
|
|
2331
2360
|
|
|
2332
|
-
#
|
|
2361
|
+
# Create a function from a single file
|
|
2362
|
+
proj.set_function("./src/mycode.py", "ingest")
|
|
2333
2363
|
|
|
2334
|
-
#
|
|
2364
|
+
# Creating a function with project source
|
|
2365
|
+
proj.set_function(
|
|
2366
|
+
"./src/mycode.py", "ingest", image="myrepo/ing:latest", with_repo=True
|
|
2367
|
+
)
|
|
2368
|
+
proj.set_function("ingest", handler="package.package.func", with_repo=True)
|
|
2369
|
+
|
|
2370
|
+
# Creating a function with non project source
|
|
2371
|
+
func = proj.set_function(
|
|
2372
|
+
"ingest", handler="package.package.func", with_repo=False
|
|
2373
|
+
)
|
|
2374
|
+
func.with_source_archive("git://github.com/mlrun/something.git")
|
|
2375
|
+
|
|
2376
|
+
# Set function requirements
|
|
2377
|
+
|
|
2378
|
+
# By providing a list of packages
|
|
2335
2379
|
proj.set_function("my.py", requirements=["requests", "pandas"])
|
|
2336
2380
|
|
|
2337
|
-
#
|
|
2381
|
+
# By providing a path to a pip requirements file
|
|
2338
2382
|
proj.set_function("my.py", requirements="requirements.txt")
|
|
2339
2383
|
|
|
2340
2384
|
:param func: Function object or spec/code url, None refers to current Notebook
|
|
@@ -2354,7 +2398,7 @@ class MlrunProject(ModelObj):
|
|
|
2354
2398
|
:param requirements: A list of python packages
|
|
2355
2399
|
:param requirements_file: Path to a python requirements file
|
|
2356
2400
|
|
|
2357
|
-
:returns:
|
|
2401
|
+
:returns: :py:class:`~mlrun.runtimes.BaseRuntime`
|
|
2358
2402
|
"""
|
|
2359
2403
|
(
|
|
2360
2404
|
resolved_function_name,
|
|
@@ -2388,10 +2432,14 @@ class MlrunProject(ModelObj):
|
|
|
2388
2432
|
requirements: typing.Union[str, list[str]] = None,
|
|
2389
2433
|
requirements_file: str = "",
|
|
2390
2434
|
) -> tuple[str, str, mlrun.runtimes.BaseRuntime, dict]:
|
|
2391
|
-
if
|
|
2435
|
+
if (
|
|
2436
|
+
func is None
|
|
2437
|
+
and not _has_module(handler, kind)
|
|
2438
|
+
and mlrun.runtimes.RuntimeKinds.supports_from_notebook(kind)
|
|
2439
|
+
):
|
|
2392
2440
|
# if function path is not provided and it is not a module (no ".")
|
|
2393
2441
|
# use the current notebook as default
|
|
2394
|
-
if
|
|
2442
|
+
if is_jupyter:
|
|
2395
2443
|
from IPython import get_ipython
|
|
2396
2444
|
|
|
2397
2445
|
kernel = get_ipython()
|
|
@@ -2782,47 +2830,104 @@ class MlrunProject(ModelObj):
|
|
|
2782
2830
|
secrets=secrets or {},
|
|
2783
2831
|
)
|
|
2784
2832
|
|
|
2785
|
-
def sync_functions(
|
|
2786
|
-
|
|
2833
|
+
def sync_functions(
|
|
2834
|
+
self,
|
|
2835
|
+
names: list = None,
|
|
2836
|
+
always: bool = True,
|
|
2837
|
+
save: bool = False,
|
|
2838
|
+
silent: bool = False,
|
|
2839
|
+
):
|
|
2840
|
+
"""
|
|
2841
|
+
Reload function objects from specs and files.
|
|
2842
|
+
The function objects are synced against the definitions spec in `self.spec._function_definitions`.
|
|
2843
|
+
Referenced files/URLs in the function spec will be reloaded.
|
|
2844
|
+
Function definitions are parsed by the following precedence:
|
|
2845
|
+
|
|
2846
|
+
1. Contains runtime spec.
|
|
2847
|
+
2. Contains module in the project's context.
|
|
2848
|
+
3. Contains path to function definition (yaml, DB, Hub).
|
|
2849
|
+
4. Contains path to .ipynb or .py files.
|
|
2850
|
+
5. Contains a Nuclio/Serving function image / an 'Application' kind definition.
|
|
2851
|
+
|
|
2852
|
+
If function definition is already an object, some project metadata updates will apply however,
|
|
2853
|
+
it will not be reloaded.
|
|
2854
|
+
|
|
2855
|
+
:param names: Names of functions to reload, defaults to `self.spec._function_definitions.keys()`.
|
|
2856
|
+
:param always: Force reloading the functions.
|
|
2857
|
+
:param save: Whether to save the loaded functions or not.
|
|
2858
|
+
:param silent: Whether to raise an exception when a function fails to load.
|
|
2859
|
+
|
|
2860
|
+
:returns: Dictionary of function objects
|
|
2861
|
+
"""
|
|
2787
2862
|
if self._initialized and not always:
|
|
2788
2863
|
return self.spec._function_objects
|
|
2789
2864
|
|
|
2790
|
-
|
|
2865
|
+
functions = self.spec._function_objects
|
|
2791
2866
|
if not names:
|
|
2792
2867
|
names = self.spec._function_definitions.keys()
|
|
2793
|
-
|
|
2868
|
+
functions = {}
|
|
2869
|
+
|
|
2794
2870
|
origin = mlrun.runtimes.utils.add_code_metadata(self.spec.context)
|
|
2795
2871
|
for name in names:
|
|
2796
|
-
|
|
2797
|
-
if not
|
|
2798
|
-
|
|
2872
|
+
function_definition = self.spec._function_definitions.get(name)
|
|
2873
|
+
if not function_definition:
|
|
2874
|
+
if silent:
|
|
2875
|
+
logger.warn(
|
|
2876
|
+
"Function definition was not found, skipping reload", name=name
|
|
2877
|
+
)
|
|
2878
|
+
continue
|
|
2879
|
+
|
|
2880
|
+
raise ValueError(f"Function named {name} not found")
|
|
2881
|
+
|
|
2882
|
+
function_object = self.spec._function_objects.get(name, None)
|
|
2883
|
+
is_base_runtime = isinstance(
|
|
2884
|
+
function_object, mlrun.runtimes.base.BaseRuntime
|
|
2885
|
+
)
|
|
2799
2886
|
# If this function is already available locally, don't recreate it unless always=True
|
|
2800
|
-
if
|
|
2801
|
-
|
|
2802
|
-
self.spec._function_objects.get(name, None),
|
|
2803
|
-
mlrun.runtimes.base.BaseRuntime,
|
|
2804
|
-
)
|
|
2805
|
-
and not always
|
|
2806
|
-
):
|
|
2807
|
-
funcs[name] = self.spec._function_objects[name]
|
|
2887
|
+
if is_base_runtime and not always:
|
|
2888
|
+
functions[name] = function_object
|
|
2808
2889
|
continue
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2890
|
+
|
|
2891
|
+
# Reload the function
|
|
2892
|
+
if hasattr(function_definition, "to_dict"):
|
|
2893
|
+
name, func = _init_function_from_obj(function_definition, self, name)
|
|
2894
|
+
elif isinstance(function_definition, dict):
|
|
2814
2895
|
try:
|
|
2815
|
-
name, func = _init_function_from_dict(
|
|
2896
|
+
name, func = _init_function_from_dict(
|
|
2897
|
+
function_definition, self, name
|
|
2898
|
+
)
|
|
2816
2899
|
except FileNotFoundError as exc:
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2900
|
+
message = f"File {exc.filename} not found while syncing project functions."
|
|
2901
|
+
if silent:
|
|
2902
|
+
message += " Skipping function reload"
|
|
2903
|
+
logger.warn(message, name=name)
|
|
2904
|
+
continue
|
|
2905
|
+
|
|
2906
|
+
raise mlrun.errors.MLRunMissingDependencyError(message) from exc
|
|
2907
|
+
|
|
2908
|
+
except Exception as exc:
|
|
2909
|
+
if silent:
|
|
2910
|
+
logger.warn(
|
|
2911
|
+
"Failed to instantiate function",
|
|
2912
|
+
name=name,
|
|
2913
|
+
error=mlrun.utils.err_to_str(exc),
|
|
2914
|
+
)
|
|
2915
|
+
continue
|
|
2916
|
+
raise exc
|
|
2917
|
+
else:
|
|
2918
|
+
message = f"Function {name} must be an object or dict."
|
|
2919
|
+
if silent:
|
|
2920
|
+
message += " Skipping function reload"
|
|
2921
|
+
logger.warn(message, name=name)
|
|
2922
|
+
continue
|
|
2923
|
+
raise ValueError(message)
|
|
2924
|
+
|
|
2820
2925
|
func.spec.build.code_origin = origin
|
|
2821
|
-
|
|
2926
|
+
functions[name] = func
|
|
2822
2927
|
if save:
|
|
2823
2928
|
func.save(versioned=False)
|
|
2824
2929
|
|
|
2825
|
-
self.spec._function_objects =
|
|
2930
|
+
self.spec._function_objects = functions
|
|
2826
2931
|
self._initialized = True
|
|
2827
2932
|
return self.spec._function_objects
|
|
2828
2933
|
|
|
@@ -2967,6 +3072,7 @@ class MlrunProject(ModelObj):
|
|
|
2967
3072
|
source: str = None,
|
|
2968
3073
|
cleanup_ttl: int = None,
|
|
2969
3074
|
notifications: list[mlrun.model.Notification] = None,
|
|
3075
|
+
workflow_runner_node_selector: typing.Optional[dict[str, str]] = None,
|
|
2970
3076
|
) -> _PipelineRunStatus:
|
|
2971
3077
|
"""Run a workflow using kubeflow pipelines
|
|
2972
3078
|
|
|
@@ -2995,15 +3101,20 @@ class MlrunProject(ModelObj):
|
|
|
2995
3101
|
|
|
2996
3102
|
* Remote URL which is loaded dynamically to the workflow runner.
|
|
2997
3103
|
* A path to the project's context on the workflow runner's image.
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3104
|
+
Path can be absolute or relative to `project.spec.build.source_code_target_dir` if defined
|
|
3105
|
+
(enriched when building a project image with source, see `MlrunProject.build_image`).
|
|
3106
|
+
For other engines the source is used to validate that the code is up-to-date.
|
|
3107
|
+
|
|
3001
3108
|
:param cleanup_ttl:
|
|
3002
3109
|
Pipeline cleanup ttl in secs (time to wait after workflow completion, at which point the
|
|
3003
3110
|
workflow and all its resources are deleted)
|
|
3004
3111
|
:param notifications:
|
|
3005
3112
|
List of notifications to send for workflow completion
|
|
3006
|
-
|
|
3113
|
+
:param workflow_runner_node_selector:
|
|
3114
|
+
Defines the node selector for the workflow runner pod when using a remote engine.
|
|
3115
|
+
This allows you to control and specify where the workflow runner pod will be scheduled.
|
|
3116
|
+
This setting is only relevant when the engine is set to 'remote' or for scheduled workflows,
|
|
3117
|
+
and it will be ignored if the workflow is not run on a remote engine.
|
|
3007
3118
|
:returns: ~py:class:`~mlrun.projects.pipelines._PipelineRunStatus` instance
|
|
3008
3119
|
"""
|
|
3009
3120
|
|
|
@@ -3021,11 +3132,11 @@ class MlrunProject(ModelObj):
|
|
|
3021
3132
|
"Remote repo is not defined, use .create_remote() + push()"
|
|
3022
3133
|
)
|
|
3023
3134
|
|
|
3024
|
-
if engine not in ["remote"]:
|
|
3025
|
-
#
|
|
3026
|
-
self.sync_functions(always=sync)
|
|
3135
|
+
if engine not in ["remote"] and not schedule:
|
|
3136
|
+
# For remote/scheduled runs there is no need to sync functions as they can be loaded dynamically during run
|
|
3137
|
+
self.sync_functions(always=sync, silent=True)
|
|
3027
3138
|
if not self.spec._function_objects:
|
|
3028
|
-
|
|
3139
|
+
logger.warn(
|
|
3029
3140
|
"There are no functions in the project."
|
|
3030
3141
|
" Make sure you've set your functions with project.set_function()."
|
|
3031
3142
|
)
|
|
@@ -3069,6 +3180,16 @@ class MlrunProject(ModelObj):
|
|
|
3069
3180
|
)
|
|
3070
3181
|
inner_engine = get_workflow_engine(engine_kind, local).engine
|
|
3071
3182
|
workflow_spec.engine = inner_engine or workflow_engine.engine
|
|
3183
|
+
if workflow_runner_node_selector:
|
|
3184
|
+
if workflow_engine.engine == "remote":
|
|
3185
|
+
workflow_spec.workflow_runner_node_selector = (
|
|
3186
|
+
workflow_runner_node_selector
|
|
3187
|
+
)
|
|
3188
|
+
else:
|
|
3189
|
+
logger.warn(
|
|
3190
|
+
"'workflow_runner_node_selector' applies only to remote engines"
|
|
3191
|
+
" and is ignored for non-remote runs."
|
|
3192
|
+
)
|
|
3072
3193
|
|
|
3073
3194
|
run = workflow_engine.run(
|
|
3074
3195
|
self,
|
|
@@ -3205,35 +3326,41 @@ class MlrunProject(ModelObj):
|
|
|
3205
3326
|
endpoint_store_connection: Optional[str] = None,
|
|
3206
3327
|
stream_path: Optional[str] = None,
|
|
3207
3328
|
tsdb_connection: Optional[str] = None,
|
|
3329
|
+
replace_creds: bool = False,
|
|
3208
3330
|
):
|
|
3209
3331
|
"""
|
|
3210
3332
|
Set the credentials that will be used by the project's model monitoring
|
|
3211
3333
|
infrastructure functions. Important to note that you have to set the credentials before deploying any
|
|
3212
3334
|
model monitoring or serving function.
|
|
3213
3335
|
|
|
3214
|
-
:param access_key: Model
|
|
3215
|
-
:param endpoint_store_connection: Endpoint store connection string. By default, None.
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
custom topic, for example kafka://<some_kafka_broker>:<port>.
|
|
3336
|
+
:param access_key: Model monitoring access key for managing user permissions.
|
|
3337
|
+
:param endpoint_store_connection: Endpoint store connection string. By default, None. Options:
|
|
3338
|
+
|
|
3339
|
+
* None - will be set from the system configuration.
|
|
3340
|
+
* v3io - for v3io endpoint store, pass `v3io` and the system will generate the
|
|
3341
|
+
exact path.
|
|
3342
|
+
* MySQL/SQLite - for SQL endpoint store, provide the full connection string,
|
|
3343
|
+
for example: mysql+pymysql://<username>:<password>@<host>:<port>/<db_name>
|
|
3344
|
+
:param stream_path: Path to the model monitoring stream. By default, None. Options:
|
|
3345
|
+
|
|
3346
|
+
* None - will be set from the system configuration.
|
|
3347
|
+
* v3io - for v3io stream, pass `v3io` and the system will generate the exact
|
|
3348
|
+
path.
|
|
3349
|
+
* Kafka - for Kafka stream, provide the full connection string without custom
|
|
3350
|
+
topic, for example kafka://<some_kafka_broker>:<port>.
|
|
3230
3351
|
:param tsdb_connection: Connection string to the time series database. By default, None.
|
|
3231
3352
|
Options:
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3353
|
+
|
|
3354
|
+
* None - will be set from the system configuration.
|
|
3355
|
+
* v3io - for v3io stream, pass `v3io` and the system will generate the exact
|
|
3356
|
+
path.
|
|
3357
|
+
* TDEngine - for TDEngine tsdb, provide the full websocket connection URL,
|
|
3358
|
+
for example taosws://<username>:<password>@<host>:<port>.
|
|
3359
|
+
:param replace_creds: If True, will override the existing credentials.
|
|
3360
|
+
Please keep in mind that if you already enabled model monitoring on
|
|
3361
|
+
your project this action can cause data loose and will require redeploying
|
|
3362
|
+
all model monitoring functions & model monitoring infra
|
|
3363
|
+
& tracked model server.
|
|
3237
3364
|
"""
|
|
3238
3365
|
db = mlrun.db.get_run_db(secrets=self._secrets)
|
|
3239
3366
|
db.set_model_monitoring_credentials(
|
|
@@ -3244,7 +3371,17 @@ class MlrunProject(ModelObj):
|
|
|
3244
3371
|
"stream_path": stream_path,
|
|
3245
3372
|
"tsdb_connection": tsdb_connection,
|
|
3246
3373
|
},
|
|
3374
|
+
replace_creds=replace_creds,
|
|
3247
3375
|
)
|
|
3376
|
+
if replace_creds:
|
|
3377
|
+
logger.info(
|
|
3378
|
+
"Model monitoring credentials were set successfully. "
|
|
3379
|
+
"Please keep in mind that if you already had model monitoring functions "
|
|
3380
|
+
"/ model monitoring infra / tracked model server "
|
|
3381
|
+
"deployed on your project, you will need to redeploy them."
|
|
3382
|
+
"For redeploying the model monitoring infra, please use `enable_model_monitoring` API "
|
|
3383
|
+
"and set `rebuild_images=True`"
|
|
3384
|
+
)
|
|
3248
3385
|
|
|
3249
3386
|
def run_function(
|
|
3250
3387
|
self,
|
|
@@ -3325,7 +3462,8 @@ class MlrunProject(ModelObj):
|
|
|
3325
3462
|
* A dictionary of configurations to use when logging. Further info per object type and
|
|
3326
3463
|
artifact type can be given there. The artifact key must appear in the dictionary as
|
|
3327
3464
|
"key": "the_key".
|
|
3328
|
-
:param builder_env:
|
|
3465
|
+
:param builder_env: env vars dict for source archive config/credentials e.g. builder_env={"GIT_TOKEN":
|
|
3466
|
+
token}
|
|
3329
3467
|
:param reset_on_run: When True, function python modules would reload prior to code execution.
|
|
3330
3468
|
This ensures latest code changes are executed. This argument must be used in
|
|
3331
3469
|
conjunction with the local=True argument.
|
|
@@ -3765,7 +3903,7 @@ class MlrunProject(ModelObj):
|
|
|
3765
3903
|
|
|
3766
3904
|
|
|
3767
3905
|
:param name: Return only functions with a specific name.
|
|
3768
|
-
:param tag: Return function versions with specific tags.
|
|
3906
|
+
:param tag: Return function versions with specific tags. To return only tagged functions, set tag to ``"*"``.
|
|
3769
3907
|
:param labels: Return functions that have specific labels assigned to them.
|
|
3770
3908
|
:returns: List of function objects.
|
|
3771
3909
|
"""
|
|
@@ -4035,7 +4173,7 @@ class MlrunProject(ModelObj):
|
|
|
4035
4173
|
mlrun.db.get_run_db().delete_api_gateway(name=name, project=self.name)
|
|
4036
4174
|
|
|
4037
4175
|
def store_alert_config(
|
|
4038
|
-
self, alert_data: AlertConfig, alert_name=None
|
|
4176
|
+
self, alert_data: AlertConfig, alert_name: typing.Optional[str] = None
|
|
4039
4177
|
) -> AlertConfig:
|
|
4040
4178
|
"""
|
|
4041
4179
|
Create/modify an alert.
|
|
@@ -4044,9 +4182,17 @@ class MlrunProject(ModelObj):
|
|
|
4044
4182
|
:param alert_name: The name of the alert.
|
|
4045
4183
|
:return: the created/modified alert.
|
|
4046
4184
|
"""
|
|
4185
|
+
if not alert_data:
|
|
4186
|
+
raise mlrun.errors.MLRunInvalidArgumentError("Alert data must be provided")
|
|
4187
|
+
|
|
4047
4188
|
db = mlrun.db.get_run_db(secrets=self._secrets)
|
|
4048
|
-
|
|
4049
|
-
|
|
4189
|
+
alert_name = alert_name or alert_data.name
|
|
4190
|
+
if alert_data.project is not None and alert_data.project != self.metadata.name:
|
|
4191
|
+
logger.warn(
|
|
4192
|
+
"Project in alert does not match project in operation",
|
|
4193
|
+
project=alert_data.project,
|
|
4194
|
+
)
|
|
4195
|
+
alert_data.project = self.metadata.name
|
|
4050
4196
|
return db.store_alert_config(alert_name, alert_data, project=self.metadata.name)
|
|
4051
4197
|
|
|
4052
4198
|
def get_alert_config(self, alert_name: str) -> AlertConfig:
|
|
@@ -4244,6 +4390,7 @@ class MlrunProject(ModelObj):
|
|
|
4244
4390
|
kind=producer_dict.get("kind", ""),
|
|
4245
4391
|
project=producer_project,
|
|
4246
4392
|
tag=producer_tag,
|
|
4393
|
+
owner=producer_dict.get("owner", ""),
|
|
4247
4394
|
), True
|
|
4248
4395
|
|
|
4249
4396
|
# do not retain the artifact's producer, replace it with the project as the producer
|
|
@@ -4253,6 +4400,7 @@ class MlrunProject(ModelObj):
|
|
|
4253
4400
|
name=self.metadata.name,
|
|
4254
4401
|
project=self.metadata.name,
|
|
4255
4402
|
tag=project_producer_tag,
|
|
4403
|
+
owner=self._resolve_artifact_owner(),
|
|
4256
4404
|
), False
|
|
4257
4405
|
|
|
4258
4406
|
def _resolve_existing_artifact(
|
|
@@ -4292,6 +4440,9 @@ class MlrunProject(ModelObj):
|
|
|
4292
4440
|
def _get_project_tag(self):
|
|
4293
4441
|
return self._get_hexsha() or str(uuid.uuid4())
|
|
4294
4442
|
|
|
4443
|
+
def _resolve_artifact_owner(self):
|
|
4444
|
+
return os.getenv("V3IO_USERNAME") or self.spec.owner
|
|
4445
|
+
|
|
4295
4446
|
|
|
4296
4447
|
def _set_as_current_default_project(project: MlrunProject):
|
|
4297
4448
|
mlrun.mlconf.default_project = project.metadata.name
|
|
@@ -4343,18 +4494,17 @@ def _init_function_from_dict(
|
|
|
4343
4494
|
)
|
|
4344
4495
|
|
|
4345
4496
|
elif url.endswith(".py"):
|
|
4346
|
-
# when load_source_on_run is used we allow not providing image as code will be loaded pre-run. ML-4994
|
|
4347
|
-
if (
|
|
4348
|
-
not image
|
|
4349
|
-
and not project.default_image
|
|
4350
|
-
and kind != "local"
|
|
4351
|
-
and not project.spec.load_source_on_run
|
|
4352
|
-
):
|
|
4353
|
-
raise ValueError(
|
|
4354
|
-
"image must be provided with py code files which do not "
|
|
4355
|
-
"run on 'local' engine kind"
|
|
4356
|
-
)
|
|
4357
4497
|
if in_context and with_repo:
|
|
4498
|
+
# when load_source_on_run is used we allow not providing image as code will be loaded pre-run. ML-4994
|
|
4499
|
+
if (
|
|
4500
|
+
not image
|
|
4501
|
+
and not project.default_image
|
|
4502
|
+
and kind != "local"
|
|
4503
|
+
and not project.spec.load_source_on_run
|
|
4504
|
+
):
|
|
4505
|
+
raise ValueError(
|
|
4506
|
+
"image must be provided with py code files which do not run on 'local' engine kind"
|
|
4507
|
+
)
|
|
4358
4508
|
func = new_function(
|
|
4359
4509
|
name,
|
|
4360
4510
|
command=relative_url,
|
|
@@ -4376,7 +4526,6 @@ def _init_function_from_dict(
|
|
|
4376
4526
|
elif kind in mlrun.runtimes.RuntimeKinds.nuclio_runtimes():
|
|
4377
4527
|
func = new_function(
|
|
4378
4528
|
name,
|
|
4379
|
-
command=relative_url,
|
|
4380
4529
|
image=image,
|
|
4381
4530
|
kind=kind,
|
|
4382
4531
|
handler=handler,
|
|
@@ -4430,9 +4579,17 @@ def _init_function_from_obj(
|
|
|
4430
4579
|
def _has_module(handler, kind):
|
|
4431
4580
|
if not handler:
|
|
4432
4581
|
return False
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4582
|
+
|
|
4583
|
+
if (
|
|
4584
|
+
kind in mlrun.runtimes.RuntimeKinds.pure_nuclio_deployed_runtimes()
|
|
4585
|
+
and ":" in handler
|
|
4586
|
+
):
|
|
4587
|
+
return True
|
|
4588
|
+
|
|
4589
|
+
if "." in handler:
|
|
4590
|
+
return True
|
|
4591
|
+
|
|
4592
|
+
return False
|
|
4436
4593
|
|
|
4437
4594
|
|
|
4438
4595
|
def _is_imported_artifact(artifact):
|