mlrun 1.7.0rc4__py3-none-any.whl → 1.7.0rc6__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/base.py +2 -1
- mlrun/artifacts/plots.py +9 -5
- mlrun/common/constants.py +1 -0
- mlrun/common/schemas/__init__.py +10 -0
- mlrun/common/schemas/api_gateway.py +85 -0
- mlrun/common/schemas/auth.py +2 -2
- mlrun/config.py +19 -4
- mlrun/datastore/sources.py +5 -4
- mlrun/datastore/targets.py +16 -20
- mlrun/db/base.py +16 -0
- mlrun/db/factory.py +1 -1
- mlrun/db/httpdb.py +50 -8
- mlrun/db/nopdb.py +13 -0
- mlrun/launcher/__init__.py +1 -1
- mlrun/launcher/base.py +1 -1
- mlrun/launcher/client.py +1 -1
- mlrun/launcher/factory.py +1 -1
- mlrun/launcher/local.py +1 -1
- mlrun/launcher/remote.py +1 -1
- mlrun/model_monitoring/api.py +6 -12
- mlrun/model_monitoring/application.py +21 -21
- mlrun/model_monitoring/applications/histogram_data_drift.py +130 -40
- mlrun/model_monitoring/batch.py +1 -42
- mlrun/model_monitoring/controller.py +1 -8
- mlrun/model_monitoring/features_drift_table.py +34 -22
- mlrun/model_monitoring/helpers.py +45 -4
- mlrun/model_monitoring/stream_processing.py +2 -0
- mlrun/projects/project.py +229 -16
- mlrun/run.py +70 -74
- mlrun/runtimes/__init__.py +35 -0
- mlrun/runtimes/base.py +15 -11
- mlrun/runtimes/nuclio/__init__.py +1 -0
- mlrun/runtimes/nuclio/api_gateway.py +300 -0
- mlrun/runtimes/nuclio/application/__init__.py +15 -0
- mlrun/runtimes/nuclio/application/application.py +283 -0
- mlrun/runtimes/nuclio/application/reverse_proxy.go +87 -0
- mlrun/runtimes/nuclio/function.py +50 -1
- mlrun/runtimes/pod.py +1 -1
- mlrun/serving/states.py +7 -19
- mlrun/utils/logger.py +2 -2
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.0rc4.dist-info → mlrun-1.7.0rc6.dist-info}/METADATA +1 -1
- {mlrun-1.7.0rc4.dist-info → mlrun-1.7.0rc6.dist-info}/RECORD +47 -42
- {mlrun-1.7.0rc4.dist-info → mlrun-1.7.0rc6.dist-info}/WHEEL +1 -1
- {mlrun-1.7.0rc4.dist-info → mlrun-1.7.0rc6.dist-info}/LICENSE +0 -0
- {mlrun-1.7.0rc4.dist-info → mlrun-1.7.0rc6.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.0rc4.dist-info → mlrun-1.7.0rc6.dist-info}/top_level.txt +0 -0
mlrun/projects/project.py
CHANGED
|
@@ -41,6 +41,7 @@ import mlrun.db
|
|
|
41
41
|
import mlrun.errors
|
|
42
42
|
import mlrun.k8s_utils
|
|
43
43
|
import mlrun.runtimes
|
|
44
|
+
import mlrun.runtimes.nuclio.api_gateway
|
|
44
45
|
import mlrun.runtimes.pod
|
|
45
46
|
import mlrun.runtimes.utils
|
|
46
47
|
import mlrun.utils.regex
|
|
@@ -1374,14 +1375,7 @@ class MlrunProject(ModelObj):
|
|
|
1374
1375
|
artifact_path = mlrun.utils.helpers.template_artifact_path(
|
|
1375
1376
|
self.spec.artifact_path or mlrun.mlconf.artifact_path, self.metadata.name
|
|
1376
1377
|
)
|
|
1377
|
-
|
|
1378
|
-
# we need to maintain the different trees that generated them
|
|
1379
|
-
producer = ArtifactProducer(
|
|
1380
|
-
"project",
|
|
1381
|
-
self.metadata.name,
|
|
1382
|
-
self.metadata.name,
|
|
1383
|
-
tag=self._get_hexsha() or str(uuid.uuid4()),
|
|
1384
|
-
)
|
|
1378
|
+
project_tag = self._get_project_tag()
|
|
1385
1379
|
for artifact_dict in self.spec.artifacts:
|
|
1386
1380
|
if _is_imported_artifact(artifact_dict):
|
|
1387
1381
|
import_from = artifact_dict["import_from"]
|
|
@@ -1401,6 +1395,15 @@ class MlrunProject(ModelObj):
|
|
|
1401
1395
|
artifact.src_path = path.join(
|
|
1402
1396
|
self.spec.get_code_path(), artifact.src_path
|
|
1403
1397
|
)
|
|
1398
|
+
producer = self._resolve_artifact_producer(artifact, project_tag)
|
|
1399
|
+
# log the artifact only if it doesn't already exist
|
|
1400
|
+
if (
|
|
1401
|
+
producer.name != self.metadata.name
|
|
1402
|
+
and self._resolve_existing_artifact(
|
|
1403
|
+
artifact,
|
|
1404
|
+
)
|
|
1405
|
+
):
|
|
1406
|
+
continue
|
|
1404
1407
|
artifact_manager.log_artifact(
|
|
1405
1408
|
producer, artifact, artifact_path=artifact_path
|
|
1406
1409
|
)
|
|
@@ -1497,12 +1500,20 @@ class MlrunProject(ModelObj):
|
|
|
1497
1500
|
artifact_path = mlrun.utils.helpers.template_artifact_path(
|
|
1498
1501
|
artifact_path, self.metadata.name
|
|
1499
1502
|
)
|
|
1500
|
-
producer =
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
self.
|
|
1504
|
-
|
|
1505
|
-
|
|
1503
|
+
producer = self._resolve_artifact_producer(item)
|
|
1504
|
+
if producer.name != self.metadata.name:
|
|
1505
|
+
# the artifact producer is retained, log it only if it doesn't already exist
|
|
1506
|
+
if existing_artifact := self._resolve_existing_artifact(
|
|
1507
|
+
item,
|
|
1508
|
+
tag,
|
|
1509
|
+
):
|
|
1510
|
+
artifact_key = item if isinstance(item, str) else item.key
|
|
1511
|
+
logger.info(
|
|
1512
|
+
"Artifact already exists, skipping logging",
|
|
1513
|
+
key=artifact_key,
|
|
1514
|
+
tag=tag,
|
|
1515
|
+
)
|
|
1516
|
+
return existing_artifact
|
|
1506
1517
|
item = am.log_artifact(
|
|
1507
1518
|
producer,
|
|
1508
1519
|
item,
|
|
@@ -2402,13 +2413,47 @@ class MlrunProject(ModelObj):
|
|
|
2402
2413
|
clone_zip(url, self.spec.context, self._secrets)
|
|
2403
2414
|
|
|
2404
2415
|
def create_remote(self, url, name="origin", branch=None):
|
|
2405
|
-
"""
|
|
2416
|
+
"""Create remote for the project git
|
|
2417
|
+
|
|
2418
|
+
This method creates a new remote repository associated with the project's Git repository.
|
|
2419
|
+
If a remote with the specified name already exists, it will not be overwritten.
|
|
2420
|
+
|
|
2421
|
+
If you wish to update the URL of an existing remote, use the `set_remote` method instead.
|
|
2406
2422
|
|
|
2407
2423
|
:param url: remote git url
|
|
2408
2424
|
:param name: name for the remote (default is 'origin')
|
|
2409
2425
|
:param branch: Git branch to use as source
|
|
2410
2426
|
"""
|
|
2427
|
+
self.set_remote(url, name=name, branch=branch, overwrite=False)
|
|
2428
|
+
|
|
2429
|
+
def set_remote(self, url, name="origin", branch=None, overwrite=True):
|
|
2430
|
+
"""Create or update a remote for the project git repository.
|
|
2431
|
+
|
|
2432
|
+
This method allows you to manage remote repositories associated with the project.
|
|
2433
|
+
It checks if a remote with the specified name already exists.
|
|
2434
|
+
|
|
2435
|
+
If a remote with the same name does not exist, it will be created.
|
|
2436
|
+
If a remote with the same name already exists,
|
|
2437
|
+
the behavior depends on the value of the 'overwrite' flag.
|
|
2438
|
+
|
|
2439
|
+
:param url: remote git url
|
|
2440
|
+
:param name: name for the remote (default is 'origin')
|
|
2441
|
+
:param branch: Git branch to use as source
|
|
2442
|
+
:param overwrite: if True (default), updates the existing remote with the given URL if it already exists.
|
|
2443
|
+
if False, raises an error when attempting to create a remote with a name that already exists.
|
|
2444
|
+
:raises MLRunConflictError: If a remote with the same name already exists and overwrite
|
|
2445
|
+
is set to False.
|
|
2446
|
+
"""
|
|
2411
2447
|
self._ensure_git_repo()
|
|
2448
|
+
if self._remote_exists(name):
|
|
2449
|
+
if overwrite:
|
|
2450
|
+
self.spec.repo.delete_remote(name)
|
|
2451
|
+
else:
|
|
2452
|
+
raise mlrun.errors.MLRunConflictError(
|
|
2453
|
+
f"Remote '{name}' already exists in the project, "
|
|
2454
|
+
f"each remote in the project must have a unique name."
|
|
2455
|
+
"Use 'set_remote' with 'override=True' inorder to update the remote, or choose a different name."
|
|
2456
|
+
)
|
|
2412
2457
|
self.spec.repo.create_remote(name, url=url)
|
|
2413
2458
|
url = url.replace("https://", "git://")
|
|
2414
2459
|
if not branch:
|
|
@@ -2421,6 +2466,22 @@ class MlrunProject(ModelObj):
|
|
|
2421
2466
|
self.spec._source = self.spec.source or url
|
|
2422
2467
|
self.spec.origin_url = self.spec.origin_url or url
|
|
2423
2468
|
|
|
2469
|
+
def remove_remote(self, name):
|
|
2470
|
+
"""Remove a remote from the project's Git repository.
|
|
2471
|
+
|
|
2472
|
+
This method removes the remote repository associated with the specified name from the project's Git repository.
|
|
2473
|
+
|
|
2474
|
+
:param name: Name of the remote to remove.
|
|
2475
|
+
"""
|
|
2476
|
+
if self._remote_exists(name):
|
|
2477
|
+
self.spec.repo.delete_remote(name)
|
|
2478
|
+
else:
|
|
2479
|
+
logger.warning(f"The remote '{name}' does not exist. Nothing to remove.")
|
|
2480
|
+
|
|
2481
|
+
def _remote_exists(self, name):
|
|
2482
|
+
"""Check if a remote with the given name already exists"""
|
|
2483
|
+
return any(remote.name == name for remote in self.spec.repo.remotes)
|
|
2484
|
+
|
|
2424
2485
|
def _ensure_git_repo(self):
|
|
2425
2486
|
if self.spec.repo:
|
|
2426
2487
|
return
|
|
@@ -3332,7 +3393,12 @@ class MlrunProject(ModelObj):
|
|
|
3332
3393
|
artifact = db.read_artifact(
|
|
3333
3394
|
key, tag, iter=iter, project=self.metadata.name, tree=tree
|
|
3334
3395
|
)
|
|
3335
|
-
|
|
3396
|
+
|
|
3397
|
+
# in tests, if an artifact is not found, the db returns None
|
|
3398
|
+
# in real usage, the db should raise an exception
|
|
3399
|
+
if artifact:
|
|
3400
|
+
return dict_to_artifact(artifact)
|
|
3401
|
+
return None
|
|
3336
3402
|
|
|
3337
3403
|
def list_artifacts(
|
|
3338
3404
|
self,
|
|
@@ -3625,6 +3691,64 @@ class MlrunProject(ModelObj):
|
|
|
3625
3691
|
"""
|
|
3626
3692
|
self.spec.remove_custom_packager(packager=packager)
|
|
3627
3693
|
|
|
3694
|
+
def store_api_gateway(
|
|
3695
|
+
self, api_gateway: mlrun.runtimes.nuclio.api_gateway.APIGateway
|
|
3696
|
+
) -> mlrun.runtimes.nuclio.api_gateway.APIGateway:
|
|
3697
|
+
"""
|
|
3698
|
+
Creates or updates a Nuclio API Gateway using the provided APIGateway object.
|
|
3699
|
+
|
|
3700
|
+
This method interacts with the MLRun service to create/update a Nuclio API Gateway based on the provided
|
|
3701
|
+
APIGateway object. Once done, it returns the updated APIGateway object containing all fields propagated
|
|
3702
|
+
on MLRun and Nuclio sides, such as the 'host' attribute.
|
|
3703
|
+
Nuclio docs here: https://docs.nuclio.io/en/latest/reference/api-gateway/http.html
|
|
3704
|
+
|
|
3705
|
+
:param api_gateway: An instance of :py:class:`~mlrun.runtimes.nuclio.APIGateway` representing the configuration
|
|
3706
|
+
of the API Gateway to be created
|
|
3707
|
+
|
|
3708
|
+
@return: An instance of :py:class:`~mlrun.runtimes.nuclio.APIGateway` with all fields populated based on the
|
|
3709
|
+
information retrieved from the Nuclio API
|
|
3710
|
+
"""
|
|
3711
|
+
|
|
3712
|
+
api_gateway_json = mlrun.db.get_run_db().store_api_gateway(
|
|
3713
|
+
api_gateway=api_gateway,
|
|
3714
|
+
project=self.name,
|
|
3715
|
+
)
|
|
3716
|
+
|
|
3717
|
+
if api_gateway_json:
|
|
3718
|
+
# fill in all the fields in the user's api_gateway object
|
|
3719
|
+
api_gateway = mlrun.runtimes.nuclio.api_gateway.APIGateway.from_scheme(
|
|
3720
|
+
api_gateway_json
|
|
3721
|
+
)
|
|
3722
|
+
return api_gateway
|
|
3723
|
+
|
|
3724
|
+
def list_api_gateways(self) -> list[mlrun.runtimes.nuclio.api_gateway.APIGateway]:
|
|
3725
|
+
"""
|
|
3726
|
+
Retrieves a list of Nuclio API gateways associated with the project.
|
|
3727
|
+
|
|
3728
|
+
@return: List of :py:class:`~mlrun.runtimes.nuclio.api_gateway.APIGateway` objects representing
|
|
3729
|
+
the Nuclio API gateways associated with the project.
|
|
3730
|
+
"""
|
|
3731
|
+
gateways_list = mlrun.db.get_run_db().list_api_gateways(self.name)
|
|
3732
|
+
return [
|
|
3733
|
+
mlrun.runtimes.nuclio.api_gateway.APIGateway.from_scheme(gateway_dict)
|
|
3734
|
+
for gateway_dict in gateways_list.api_gateways.values()
|
|
3735
|
+
]
|
|
3736
|
+
|
|
3737
|
+
def get_api_gateway(
|
|
3738
|
+
self,
|
|
3739
|
+
name: str,
|
|
3740
|
+
) -> mlrun.runtimes.nuclio.api_gateway.APIGateway:
|
|
3741
|
+
"""
|
|
3742
|
+
Retrieves an API gateway by name instance.
|
|
3743
|
+
|
|
3744
|
+
:param name: The name of the API gateway to retrieve.
|
|
3745
|
+
|
|
3746
|
+
Returns:
|
|
3747
|
+
mlrun.runtimes.nuclio.APIGateway: An instance of APIGateway.
|
|
3748
|
+
"""
|
|
3749
|
+
|
|
3750
|
+
return mlrun.db.get_run_db().get_api_gateway(name=name, project=self.name)
|
|
3751
|
+
|
|
3628
3752
|
def _run_authenticated_git_action(
|
|
3629
3753
|
self,
|
|
3630
3754
|
action: Callable,
|
|
@@ -3702,6 +3826,83 @@ class MlrunProject(ModelObj):
|
|
|
3702
3826
|
f"<project.spec.get_code_path()>/<{param_name}>)."
|
|
3703
3827
|
)
|
|
3704
3828
|
|
|
3829
|
+
def _resolve_artifact_producer(
|
|
3830
|
+
self,
|
|
3831
|
+
artifact: typing.Union[str, Artifact],
|
|
3832
|
+
project_producer_tag: str = None,
|
|
3833
|
+
) -> typing.Optional[ArtifactProducer]:
|
|
3834
|
+
"""
|
|
3835
|
+
Resolve the artifact producer of the given artifact.
|
|
3836
|
+
If the artifact's producer is a run, the artifact is registered with the original producer.
|
|
3837
|
+
Otherwise, the artifact is registered with the current project as the producer.
|
|
3838
|
+
|
|
3839
|
+
:param artifact: The artifact to resolve its producer.
|
|
3840
|
+
:param project_producer_tag: The tag to use for the project as the producer. If not provided, a tag will be
|
|
3841
|
+
generated for the project.
|
|
3842
|
+
:return: A tuple of the resolved producer and the resolved artifact.
|
|
3843
|
+
"""
|
|
3844
|
+
|
|
3845
|
+
if not isinstance(artifact, str) and artifact.producer:
|
|
3846
|
+
# if the artifact was imported from a yaml file, the producer can be a dict
|
|
3847
|
+
if isinstance(artifact.spec.producer, ArtifactProducer):
|
|
3848
|
+
producer_dict = artifact.spec.producer.get_meta()
|
|
3849
|
+
else:
|
|
3850
|
+
producer_dict = artifact.spec.producer
|
|
3851
|
+
|
|
3852
|
+
if producer_dict.get("kind", "") == "run":
|
|
3853
|
+
return ArtifactProducer(
|
|
3854
|
+
name=producer_dict.get("name", ""),
|
|
3855
|
+
kind=producer_dict.get("kind", ""),
|
|
3856
|
+
project=producer_dict.get("project", ""),
|
|
3857
|
+
tag=producer_dict.get("tag", ""),
|
|
3858
|
+
)
|
|
3859
|
+
|
|
3860
|
+
# do not retain the artifact's producer, replace it with the project as the producer
|
|
3861
|
+
project_producer_tag = project_producer_tag or self._get_project_tag()
|
|
3862
|
+
return ArtifactProducer(
|
|
3863
|
+
kind="project",
|
|
3864
|
+
name=self.metadata.name,
|
|
3865
|
+
project=self.metadata.name,
|
|
3866
|
+
tag=project_producer_tag,
|
|
3867
|
+
)
|
|
3868
|
+
|
|
3869
|
+
def _resolve_existing_artifact(
|
|
3870
|
+
self,
|
|
3871
|
+
item: typing.Union[str, Artifact],
|
|
3872
|
+
tag: str = None,
|
|
3873
|
+
) -> typing.Optional[Artifact]:
|
|
3874
|
+
"""
|
|
3875
|
+
Check if there is and existing artifact with the given item and tag.
|
|
3876
|
+
If there is, return the existing artifact. Otherwise, return None.
|
|
3877
|
+
|
|
3878
|
+
:param item: The item (or key) to check if there is an existing artifact for.
|
|
3879
|
+
:param tag: The tag to check if there is an existing artifact for.
|
|
3880
|
+
:return: The existing artifact if there is one, otherwise None.
|
|
3881
|
+
"""
|
|
3882
|
+
try:
|
|
3883
|
+
if isinstance(item, str):
|
|
3884
|
+
existing_artifact = self.get_artifact(key=item, tag=tag)
|
|
3885
|
+
else:
|
|
3886
|
+
existing_artifact = self.get_artifact(
|
|
3887
|
+
key=item.key,
|
|
3888
|
+
tag=item.tag,
|
|
3889
|
+
iter=item.iter,
|
|
3890
|
+
tree=item.tree,
|
|
3891
|
+
)
|
|
3892
|
+
if existing_artifact is not None:
|
|
3893
|
+
return existing_artifact.from_dict(existing_artifact)
|
|
3894
|
+
except mlrun.errors.MLRunNotFoundError:
|
|
3895
|
+
logger.debug(
|
|
3896
|
+
"No existing artifact was found",
|
|
3897
|
+
key=item if isinstance(item, str) else item.key,
|
|
3898
|
+
tag=tag if isinstance(item, str) else item.tag,
|
|
3899
|
+
tree=None if isinstance(item, str) else item.tree,
|
|
3900
|
+
)
|
|
3901
|
+
return None
|
|
3902
|
+
|
|
3903
|
+
def _get_project_tag(self):
|
|
3904
|
+
return self._get_hexsha() or str(uuid.uuid4())
|
|
3905
|
+
|
|
3705
3906
|
|
|
3706
3907
|
def _set_as_current_default_project(project: MlrunProject):
|
|
3707
3908
|
mlrun.mlconf.default_project = project.metadata.name
|
|
@@ -3787,6 +3988,18 @@ def _init_function_from_dict(
|
|
|
3787
3988
|
tag=tag,
|
|
3788
3989
|
)
|
|
3789
3990
|
|
|
3991
|
+
elif image and kind in mlrun.runtimes.RuntimeKinds.nuclio_runtimes():
|
|
3992
|
+
func = new_function(
|
|
3993
|
+
name,
|
|
3994
|
+
command=relative_url,
|
|
3995
|
+
image=image,
|
|
3996
|
+
kind=kind,
|
|
3997
|
+
handler=handler,
|
|
3998
|
+
tag=tag,
|
|
3999
|
+
)
|
|
4000
|
+
if kind != mlrun.runtimes.RuntimeKinds.application:
|
|
4001
|
+
logger.info("Function code not specified, setting entry point to image")
|
|
4002
|
+
func.from_image(image)
|
|
3790
4003
|
else:
|
|
3791
4004
|
raise ValueError(f"Unsupported function url:handler {url}:{handler} or no spec")
|
|
3792
4005
|
|
mlrun/run.py
CHANGED
|
@@ -34,7 +34,6 @@ import mlrun.common.schemas
|
|
|
34
34
|
import mlrun.errors
|
|
35
35
|
import mlrun.utils.helpers
|
|
36
36
|
from mlrun.kfpops import format_summary_from_kfp_run, show_kfp_run
|
|
37
|
-
from mlrun.runtimes.nuclio.serving import serving_subkind
|
|
38
37
|
|
|
39
38
|
from .common.helpers import parse_versioned_object_uri
|
|
40
39
|
from .config import config as mlconf
|
|
@@ -58,6 +57,7 @@ from .runtimes import (
|
|
|
58
57
|
)
|
|
59
58
|
from .runtimes.databricks_job.databricks_runtime import DatabricksRuntime
|
|
60
59
|
from .runtimes.funcdoc import update_function_entry_points
|
|
60
|
+
from .runtimes.nuclio.application import ApplicationRuntime
|
|
61
61
|
from .runtimes.utils import add_code_metadata, global_context
|
|
62
62
|
from .utils import (
|
|
63
63
|
extend_hub_uri_if_needed,
|
|
@@ -425,19 +425,19 @@ def import_function_to_dict(url, secrets=None):
|
|
|
425
425
|
|
|
426
426
|
|
|
427
427
|
def new_function(
|
|
428
|
-
name: str = "",
|
|
429
|
-
project: str = "",
|
|
430
|
-
tag: str = "",
|
|
431
|
-
kind: str = "",
|
|
432
|
-
command: str = "",
|
|
433
|
-
image: str = "",
|
|
434
|
-
args: list = None,
|
|
435
|
-
runtime=None,
|
|
436
|
-
mode=None,
|
|
437
|
-
handler: str = None,
|
|
438
|
-
source: str = None,
|
|
428
|
+
name: Optional[str] = "",
|
|
429
|
+
project: Optional[str] = "",
|
|
430
|
+
tag: Optional[str] = "",
|
|
431
|
+
kind: Optional[str] = "",
|
|
432
|
+
command: Optional[str] = "",
|
|
433
|
+
image: Optional[str] = "",
|
|
434
|
+
args: Optional[list] = None,
|
|
435
|
+
runtime: Optional[Union[mlrun.runtimes.BaseRuntime, dict]] = None,
|
|
436
|
+
mode: Optional[str] = None,
|
|
437
|
+
handler: Optional[str] = None,
|
|
438
|
+
source: Optional[str] = None,
|
|
439
439
|
requirements: Union[str, list[str]] = None,
|
|
440
|
-
kfp=None,
|
|
440
|
+
kfp: Optional[bool] = None,
|
|
441
441
|
requirements_file: str = "",
|
|
442
442
|
):
|
|
443
443
|
"""Create a new ML function from base properties
|
|
@@ -535,9 +535,9 @@ def new_function(
|
|
|
535
535
|
if source:
|
|
536
536
|
runner.spec.build.source = source
|
|
537
537
|
if handler:
|
|
538
|
-
if kind
|
|
538
|
+
if kind in [RuntimeKinds.serving, RuntimeKinds.application]:
|
|
539
539
|
raise MLRunInvalidArgumentError(
|
|
540
|
-
"
|
|
540
|
+
f"Handler is not supported for {kind} runtime"
|
|
541
541
|
)
|
|
542
542
|
elif kind in RuntimeKinds.nuclio_runtimes():
|
|
543
543
|
runner.spec.function_handler = handler
|
|
@@ -575,22 +575,22 @@ def _process_runtime(command, runtime, kind):
|
|
|
575
575
|
|
|
576
576
|
|
|
577
577
|
def code_to_function(
|
|
578
|
-
name: str = "",
|
|
579
|
-
project: str = "",
|
|
580
|
-
tag: str = "",
|
|
581
|
-
filename: str = "",
|
|
582
|
-
handler: str = "",
|
|
583
|
-
kind: str = "",
|
|
584
|
-
image: str = None,
|
|
585
|
-
code_output: str = "",
|
|
578
|
+
name: Optional[str] = "",
|
|
579
|
+
project: Optional[str] = "",
|
|
580
|
+
tag: Optional[str] = "",
|
|
581
|
+
filename: Optional[str] = "",
|
|
582
|
+
handler: Optional[str] = "",
|
|
583
|
+
kind: Optional[str] = "",
|
|
584
|
+
image: Optional[str] = None,
|
|
585
|
+
code_output: Optional[str] = "",
|
|
586
586
|
embed_code: bool = True,
|
|
587
|
-
description: str = "",
|
|
588
|
-
requirements: Union[str, list[str]] = None,
|
|
589
|
-
categories: list[str] = None,
|
|
590
|
-
labels: dict[str, str] = None,
|
|
591
|
-
with_doc: bool = True,
|
|
592
|
-
ignored_tags=None,
|
|
593
|
-
requirements_file: str = "",
|
|
587
|
+
description: Optional[str] = "",
|
|
588
|
+
requirements: Optional[Union[str, list[str]]] = None,
|
|
589
|
+
categories: Optional[list[str]] = None,
|
|
590
|
+
labels: Optional[dict[str, str]] = None,
|
|
591
|
+
with_doc: Optional[bool] = True,
|
|
592
|
+
ignored_tags: Optional[str] = None,
|
|
593
|
+
requirements_file: Optional[str] = "",
|
|
594
594
|
) -> Union[
|
|
595
595
|
MpiRuntimeV1Alpha1,
|
|
596
596
|
MpiRuntimeV1,
|
|
@@ -602,6 +602,7 @@ def code_to_function(
|
|
|
602
602
|
Spark3Runtime,
|
|
603
603
|
RemoteSparkRuntime,
|
|
604
604
|
DatabricksRuntime,
|
|
605
|
+
ApplicationRuntime,
|
|
605
606
|
]:
|
|
606
607
|
"""Convenience function to insert code and configure an mlrun runtime.
|
|
607
608
|
|
|
@@ -628,7 +629,7 @@ def code_to_function(
|
|
|
628
629
|
- spark: run distributed Spark job using Spark Kubernetes Operator
|
|
629
630
|
- remote-spark: run distributed Spark job on remote Spark service
|
|
630
631
|
|
|
631
|
-
Learn more about
|
|
632
|
+
Learn more about [Kinds of function (runtimes)](../concepts/functions-overview.html).
|
|
632
633
|
|
|
633
634
|
:param name: function name, typically best to use hyphen-case
|
|
634
635
|
:param project: project used to namespace the function, defaults to 'default'
|
|
@@ -718,35 +719,34 @@ def code_to_function(
|
|
|
718
719
|
fn.metadata.categories = categories
|
|
719
720
|
fn.metadata.labels = labels or fn.metadata.labels
|
|
720
721
|
|
|
721
|
-
def resolve_nuclio_subkind(kind):
|
|
722
|
-
is_nuclio = kind.startswith("nuclio")
|
|
723
|
-
subkind = kind[kind.find(":") + 1 :] if is_nuclio and ":" in kind else None
|
|
724
|
-
if kind == RuntimeKinds.serving:
|
|
725
|
-
is_nuclio = True
|
|
726
|
-
subkind = serving_subkind
|
|
727
|
-
return is_nuclio, subkind
|
|
728
|
-
|
|
729
722
|
if (
|
|
730
723
|
not embed_code
|
|
731
724
|
and not code_output
|
|
732
725
|
and (not filename or filename.endswith(".ipynb"))
|
|
733
726
|
):
|
|
734
727
|
raise ValueError(
|
|
735
|
-
"
|
|
728
|
+
"A valid code file must be specified "
|
|
736
729
|
"when not using the embed_code option"
|
|
737
730
|
)
|
|
738
731
|
|
|
739
732
|
if kind == RuntimeKinds.databricks and not embed_code:
|
|
740
|
-
raise ValueError("
|
|
733
|
+
raise ValueError("Databricks tasks only support embed_code=True")
|
|
741
734
|
|
|
742
|
-
|
|
735
|
+
if kind == RuntimeKinds.application:
|
|
736
|
+
if handler:
|
|
737
|
+
raise MLRunInvalidArgumentError(
|
|
738
|
+
"Handler is not supported for application runtime"
|
|
739
|
+
)
|
|
740
|
+
filename, handler = ApplicationRuntime.get_filename_and_handler()
|
|
741
|
+
|
|
742
|
+
is_nuclio, sub_kind = RuntimeKinds.resolve_nuclio_sub_kind(kind)
|
|
743
743
|
code_origin = add_name(add_code_metadata(filename), name)
|
|
744
744
|
|
|
745
745
|
name, spec, code = nuclio.build_file(
|
|
746
746
|
filename,
|
|
747
747
|
name=name,
|
|
748
748
|
handler=handler or "handler",
|
|
749
|
-
kind=
|
|
749
|
+
kind=sub_kind,
|
|
750
750
|
ignored_tags=ignored_tags,
|
|
751
751
|
)
|
|
752
752
|
spec["spec"]["env"].append(
|
|
@@ -759,14 +759,14 @@ def code_to_function(
|
|
|
759
759
|
if not kind and spec_kind not in ["", "Function"]:
|
|
760
760
|
kind = spec_kind.lower()
|
|
761
761
|
|
|
762
|
-
# if its a nuclio
|
|
763
|
-
is_nuclio,
|
|
762
|
+
# if its a nuclio sub kind, redo nb parsing
|
|
763
|
+
is_nuclio, sub_kind = RuntimeKinds.resolve_nuclio_sub_kind(kind)
|
|
764
764
|
if is_nuclio:
|
|
765
765
|
name, spec, code = nuclio.build_file(
|
|
766
766
|
filename,
|
|
767
767
|
name=name,
|
|
768
768
|
handler=handler or "handler",
|
|
769
|
-
kind=
|
|
769
|
+
kind=sub_kind,
|
|
770
770
|
ignored_tags=ignored_tags,
|
|
771
771
|
)
|
|
772
772
|
|
|
@@ -780,33 +780,29 @@ def code_to_function(
|
|
|
780
780
|
raise ValueError("code_output option is only used with notebooks")
|
|
781
781
|
|
|
782
782
|
if is_nuclio:
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
else
|
|
786
|
-
|
|
787
|
-
r.spec.function_kind = subkind
|
|
788
|
-
# default_handler is only used in :mlrun subkind, determine the handler to invoke in function.run()
|
|
789
|
-
r.spec.default_handler = handler if subkind == "mlrun" else ""
|
|
790
|
-
r.spec.function_handler = (
|
|
783
|
+
runtime = RuntimeKinds.resolve_nuclio_runtime(kind, sub_kind)
|
|
784
|
+
# default_handler is only used in :mlrun sub kind, determine the handler to invoke in function.run()
|
|
785
|
+
runtime.spec.default_handler = handler if sub_kind == "mlrun" else ""
|
|
786
|
+
runtime.spec.function_handler = (
|
|
791
787
|
handler if handler and ":" in handler else get_in(spec, "spec.handler")
|
|
792
788
|
)
|
|
793
789
|
if not embed_code:
|
|
794
|
-
|
|
790
|
+
runtime.spec.source = filename
|
|
795
791
|
nuclio_runtime = get_in(spec, "spec.runtime")
|
|
796
792
|
if nuclio_runtime and not nuclio_runtime.startswith("py"):
|
|
797
|
-
|
|
793
|
+
runtime.spec.nuclio_runtime = nuclio_runtime
|
|
798
794
|
if not name:
|
|
799
|
-
raise ValueError("
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
update_common(
|
|
804
|
-
return
|
|
795
|
+
raise ValueError("Missing required parameter: name")
|
|
796
|
+
runtime.metadata.name = name
|
|
797
|
+
runtime.spec.build.code_origin = code_origin
|
|
798
|
+
runtime.spec.build.origin_filename = filename or (name + ".ipynb")
|
|
799
|
+
update_common(runtime, spec)
|
|
800
|
+
return runtime
|
|
805
801
|
|
|
806
802
|
if kind is None or kind in ["", "Function"]:
|
|
807
803
|
raise ValueError("please specify the function kind")
|
|
808
804
|
elif kind in RuntimeKinds.all():
|
|
809
|
-
|
|
805
|
+
runtime = get_runtime_class(kind)()
|
|
810
806
|
else:
|
|
811
807
|
raise ValueError(f"unsupported runtime ({kind})")
|
|
812
808
|
|
|
@@ -815,10 +811,10 @@ def code_to_function(
|
|
|
815
811
|
if not name:
|
|
816
812
|
raise ValueError("name must be specified")
|
|
817
813
|
h = get_in(spec, "spec.handler", "").split(":")
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
build =
|
|
814
|
+
runtime.handler = h[0] if len(h) <= 1 else h[1]
|
|
815
|
+
runtime.metadata = get_in(spec, "spec.metadata")
|
|
816
|
+
runtime.metadata.name = name
|
|
817
|
+
build = runtime.spec.build
|
|
822
818
|
build.code_origin = code_origin
|
|
823
819
|
build.origin_filename = filename or (name + ".ipynb")
|
|
824
820
|
build.extra = get_in(spec, "spec.build.extra")
|
|
@@ -826,18 +822,18 @@ def code_to_function(
|
|
|
826
822
|
build.builder_env = get_in(spec, "spec.build.builder_env")
|
|
827
823
|
if not embed_code:
|
|
828
824
|
if code_output:
|
|
829
|
-
|
|
825
|
+
runtime.spec.command = code_output
|
|
830
826
|
else:
|
|
831
|
-
|
|
827
|
+
runtime.spec.command = filename
|
|
832
828
|
|
|
833
829
|
build.image = get_in(spec, "spec.build.image")
|
|
834
|
-
update_common(
|
|
835
|
-
|
|
830
|
+
update_common(runtime, spec)
|
|
831
|
+
runtime.prepare_image_for_deploy()
|
|
836
832
|
|
|
837
833
|
if with_doc:
|
|
838
|
-
update_function_entry_points(
|
|
839
|
-
|
|
840
|
-
return
|
|
834
|
+
update_function_entry_points(runtime, code)
|
|
835
|
+
runtime.spec.default_handler = handler
|
|
836
|
+
return runtime
|
|
841
837
|
|
|
842
838
|
|
|
843
839
|
def _run_pipeline(
|
mlrun/runtimes/__init__.py
CHANGED
|
@@ -43,6 +43,8 @@ from .nuclio import (
|
|
|
43
43
|
new_v2_model_server,
|
|
44
44
|
nuclio_init_hook,
|
|
45
45
|
)
|
|
46
|
+
from .nuclio.application import ApplicationRuntime
|
|
47
|
+
from .nuclio.serving import serving_subkind
|
|
46
48
|
from .remotesparkjob import RemoteSparkRuntime
|
|
47
49
|
from .sparkjob import Spark3Runtime
|
|
48
50
|
|
|
@@ -101,6 +103,7 @@ class RuntimeKinds:
|
|
|
101
103
|
local = "local"
|
|
102
104
|
handler = "handler"
|
|
103
105
|
databricks = "databricks"
|
|
106
|
+
application = "application"
|
|
104
107
|
|
|
105
108
|
@staticmethod
|
|
106
109
|
def all():
|
|
@@ -115,6 +118,7 @@ class RuntimeKinds:
|
|
|
115
118
|
RuntimeKinds.mpijob,
|
|
116
119
|
RuntimeKinds.local,
|
|
117
120
|
RuntimeKinds.databricks,
|
|
121
|
+
RuntimeKinds.application,
|
|
118
122
|
]
|
|
119
123
|
|
|
120
124
|
@staticmethod
|
|
@@ -147,6 +151,7 @@ class RuntimeKinds:
|
|
|
147
151
|
RuntimeKinds.remote,
|
|
148
152
|
RuntimeKinds.nuclio,
|
|
149
153
|
RuntimeKinds.serving,
|
|
154
|
+
RuntimeKinds.application,
|
|
150
155
|
]
|
|
151
156
|
|
|
152
157
|
@staticmethod
|
|
@@ -211,6 +216,35 @@ class RuntimeKinds:
|
|
|
211
216
|
# both spark and remote spark uses different mechanism for assigning images
|
|
212
217
|
return kind not in [RuntimeKinds.spark, RuntimeKinds.remotespark]
|
|
213
218
|
|
|
219
|
+
@staticmethod
|
|
220
|
+
def resolve_nuclio_runtime(kind: str, sub_kind: str):
|
|
221
|
+
kind = kind.split(":")[0]
|
|
222
|
+
if kind not in RuntimeKinds.nuclio_runtimes():
|
|
223
|
+
raise ValueError(
|
|
224
|
+
f"Kind {kind} is not a nuclio runtime, available runtimes are {RuntimeKinds.nuclio_runtimes()}"
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
if sub_kind == serving_subkind:
|
|
228
|
+
return ServingRuntime()
|
|
229
|
+
|
|
230
|
+
if kind == RuntimeKinds.application:
|
|
231
|
+
return ApplicationRuntime()
|
|
232
|
+
|
|
233
|
+
runtime = RemoteRuntime()
|
|
234
|
+
runtime.spec.function_kind = sub_kind
|
|
235
|
+
return runtime
|
|
236
|
+
|
|
237
|
+
@staticmethod
|
|
238
|
+
def resolve_nuclio_sub_kind(kind):
|
|
239
|
+
is_nuclio = kind.startswith("nuclio")
|
|
240
|
+
sub_kind = kind[kind.find(":") + 1 :] if is_nuclio and ":" in kind else None
|
|
241
|
+
if kind == RuntimeKinds.serving:
|
|
242
|
+
is_nuclio = True
|
|
243
|
+
sub_kind = serving_subkind
|
|
244
|
+
elif kind == RuntimeKinds.application:
|
|
245
|
+
is_nuclio = True
|
|
246
|
+
return is_nuclio, sub_kind
|
|
247
|
+
|
|
214
248
|
|
|
215
249
|
def get_runtime_class(kind: str):
|
|
216
250
|
if kind == RuntimeKinds.mpijob:
|
|
@@ -228,6 +262,7 @@ def get_runtime_class(kind: str):
|
|
|
228
262
|
RuntimeKinds.local: LocalRuntime,
|
|
229
263
|
RuntimeKinds.remotespark: RemoteSparkRuntime,
|
|
230
264
|
RuntimeKinds.databricks: DatabricksRuntime,
|
|
265
|
+
RuntimeKinds.application: ApplicationRuntime,
|
|
231
266
|
}
|
|
232
267
|
|
|
233
268
|
return kind_runtime_map[kind]
|