mlrun 1.8.0rc29__py3-none-any.whl → 1.8.0rc31__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/__init__.py +2 -34
- mlrun/api/schemas/__init__.py +1 -6
- mlrun/artifacts/document.py +3 -3
- mlrun/artifacts/manager.py +1 -0
- mlrun/artifacts/model.py +3 -3
- mlrun/common/model_monitoring/helpers.py +16 -7
- mlrun/common/runtimes/constants.py +5 -0
- mlrun/common/schemas/__init__.py +0 -2
- mlrun/common/schemas/model_monitoring/__init__.py +0 -2
- mlrun/common/schemas/model_monitoring/constants.py +4 -7
- mlrun/common/schemas/model_monitoring/grafana.py +17 -11
- mlrun/config.py +9 -36
- mlrun/datastore/datastore_profile.py +1 -1
- mlrun/datastore/sources.py +14 -13
- mlrun/datastore/storeytargets.py +20 -3
- mlrun/db/httpdb.py +4 -30
- mlrun/k8s_utils.py +2 -5
- mlrun/launcher/base.py +16 -0
- mlrun/model_monitoring/api.py +1 -2
- mlrun/model_monitoring/applications/_application_steps.py +23 -37
- mlrun/model_monitoring/applications/base.py +55 -40
- mlrun/model_monitoring/applications/context.py +0 -3
- mlrun/model_monitoring/applications/results.py +16 -16
- mlrun/model_monitoring/controller.py +35 -31
- mlrun/model_monitoring/db/tsdb/__init__.py +9 -5
- mlrun/model_monitoring/db/tsdb/base.py +60 -39
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +122 -53
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +140 -14
- mlrun/model_monitoring/helpers.py +124 -16
- mlrun/model_monitoring/stream_processing.py +6 -21
- mlrun/projects/pipelines.py +11 -3
- mlrun/projects/project.py +104 -115
- mlrun/run.py +2 -2
- mlrun/runtimes/nuclio/function.py +4 -2
- mlrun/serving/routers.py +3 -4
- mlrun/serving/server.py +10 -8
- mlrun/serving/states.py +12 -2
- mlrun/serving/v2_serving.py +25 -20
- mlrun/utils/async_http.py +32 -19
- mlrun/utils/helpers.py +5 -2
- mlrun/utils/logger.py +14 -10
- mlrun/utils/notifications/notification_pusher.py +25 -0
- mlrun/utils/regex.py +1 -0
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.8.0rc29.dist-info → mlrun-1.8.0rc31.dist-info}/METADATA +4 -4
- {mlrun-1.8.0rc29.dist-info → mlrun-1.8.0rc31.dist-info}/RECORD +50 -50
- {mlrun-1.8.0rc29.dist-info → mlrun-1.8.0rc31.dist-info}/LICENSE +0 -0
- {mlrun-1.8.0rc29.dist-info → mlrun-1.8.0rc31.dist-info}/WHEEL +0 -0
- {mlrun-1.8.0rc29.dist-info → mlrun-1.8.0rc31.dist-info}/entry_points.txt +0 -0
- {mlrun-1.8.0rc29.dist-info → mlrun-1.8.0rc31.dist-info}/top_level.txt +0 -0
mlrun/projects/project.py
CHANGED
|
@@ -29,7 +29,6 @@ import zipfile
|
|
|
29
29
|
from copy import deepcopy
|
|
30
30
|
from os import environ, makedirs, path
|
|
31
31
|
from typing import Callable, Optional, Union, cast
|
|
32
|
-
from urllib.parse import urlparse
|
|
33
32
|
|
|
34
33
|
import deprecated
|
|
35
34
|
import dotenv
|
|
@@ -71,6 +70,7 @@ from mlrun.datastore.datastore_profile import (
|
|
|
71
70
|
from mlrun.datastore.vectorstore import VectorStoreCollection
|
|
72
71
|
from mlrun.model_monitoring.helpers import (
|
|
73
72
|
filter_results_by_regex,
|
|
73
|
+
get_alert_name_from_result_fqn,
|
|
74
74
|
get_result_instance_fqn,
|
|
75
75
|
)
|
|
76
76
|
from mlrun.runtimes.nuclio.function import RemoteRuntime
|
|
@@ -1254,8 +1254,14 @@ class MlrunProject(ModelObj):
|
|
|
1254
1254
|
mlrun.utils.helpers.validate_builder_source(source, pull_at_runtime, workdir)
|
|
1255
1255
|
|
|
1256
1256
|
self.spec.load_source_on_run = pull_at_runtime
|
|
1257
|
+
|
|
1258
|
+
source_has_changed = source != self.spec.source
|
|
1257
1259
|
self.spec.source = source or self.spec.source
|
|
1258
1260
|
|
|
1261
|
+
# new source should not relay on old workdir
|
|
1262
|
+
if source_has_changed:
|
|
1263
|
+
self.spec.workdir = workdir
|
|
1264
|
+
|
|
1259
1265
|
if self.spec.source.startswith("git://"):
|
|
1260
1266
|
source, reference, branch = resolve_git_reference_from_source(source)
|
|
1261
1267
|
if not branch and not reference:
|
|
@@ -1264,7 +1270,6 @@ class MlrunProject(ModelObj):
|
|
|
1264
1270
|
"'git://<url>/org/repo.git#<branch-name or refs/heads/..>'"
|
|
1265
1271
|
)
|
|
1266
1272
|
|
|
1267
|
-
self.spec.workdir = workdir or self.spec.workdir
|
|
1268
1273
|
try:
|
|
1269
1274
|
# reset function objects (to recalculate build attributes)
|
|
1270
1275
|
self.sync_functions()
|
|
@@ -2137,7 +2142,8 @@ class MlrunProject(ModelObj):
|
|
|
2137
2142
|
reset_policy: mlrun.common.schemas.alert.ResetPolicy = mlrun.common.schemas.alert.ResetPolicy.AUTO,
|
|
2138
2143
|
) -> list[mlrun.alerts.alert.AlertConfig]:
|
|
2139
2144
|
"""
|
|
2140
|
-
:param name: AlertConfig name
|
|
2145
|
+
:param name: The name of the AlertConfig template. It will be combined with mep_id, app-name
|
|
2146
|
+
and result name to generate a unique name.
|
|
2141
2147
|
:param summary: Summary of the alert, will be sent in the generated notifications
|
|
2142
2148
|
:param endpoints: The endpoints from which metrics will be retrieved to configure the alerts.
|
|
2143
2149
|
This `ModelEndpointList` object obtained via the `list_model_endpoints`
|
|
@@ -2198,10 +2204,11 @@ class MlrunProject(ModelObj):
|
|
|
2198
2204
|
)
|
|
2199
2205
|
alert_result_names = list(set(specific_result_names + matching_results))
|
|
2200
2206
|
for result_fqn in alert_result_names:
|
|
2207
|
+
result_fqn_name = get_alert_name_from_result_fqn(result_fqn)
|
|
2201
2208
|
alerts.append(
|
|
2202
2209
|
mlrun.alerts.alert.AlertConfig(
|
|
2203
2210
|
project=self.name,
|
|
2204
|
-
name=name,
|
|
2211
|
+
name=f"{name}--{result_fqn_name}",
|
|
2205
2212
|
summary=summary,
|
|
2206
2213
|
severity=severity,
|
|
2207
2214
|
entities=alert_constants.EventEntities(
|
|
@@ -2217,6 +2224,12 @@ class MlrunProject(ModelObj):
|
|
|
2217
2224
|
reset_policy=reset_policy,
|
|
2218
2225
|
)
|
|
2219
2226
|
)
|
|
2227
|
+
if not alerts:
|
|
2228
|
+
warnings.warn(
|
|
2229
|
+
"No alert config has been created. "
|
|
2230
|
+
"Try specifying a result name explicitly or verifying that results are available"
|
|
2231
|
+
)
|
|
2232
|
+
|
|
2220
2233
|
return alerts
|
|
2221
2234
|
|
|
2222
2235
|
def set_model_monitoring_function(
|
|
@@ -3456,12 +3469,13 @@ class MlrunProject(ModelObj):
|
|
|
3456
3469
|
|
|
3457
3470
|
arguments = arguments or {}
|
|
3458
3471
|
need_repo = self.spec._need_repo()
|
|
3459
|
-
if
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3472
|
+
if not dirty:
|
|
3473
|
+
if self.spec.repo and self.spec.repo.is_dirty():
|
|
3474
|
+
msg = "You seem to have uncommitted git changes, use .push()"
|
|
3475
|
+
if not need_repo:
|
|
3476
|
+
logger.warning("WARNING!, " + msg)
|
|
3477
|
+
else:
|
|
3478
|
+
raise ProjectError(msg + " or dirty=True")
|
|
3465
3479
|
|
|
3466
3480
|
if need_repo and self.spec.repo and not self.spec.source:
|
|
3467
3481
|
raise ProjectError(
|
|
@@ -3659,50 +3673,77 @@ class MlrunProject(ModelObj):
|
|
|
3659
3673
|
|
|
3660
3674
|
def set_model_monitoring_credentials(
|
|
3661
3675
|
self,
|
|
3662
|
-
access_key: Optional[str] = None,
|
|
3663
|
-
stream_path: Optional[str] = None, # Deprecated
|
|
3664
|
-
tsdb_connection: Optional[str] = None, # Deprecated
|
|
3665
|
-
replace_creds: bool = False,
|
|
3666
3676
|
*,
|
|
3667
|
-
|
|
3668
|
-
|
|
3669
|
-
|
|
3677
|
+
tsdb_profile_name: str,
|
|
3678
|
+
stream_profile_name: str,
|
|
3679
|
+
replace_creds: bool = False,
|
|
3680
|
+
) -> None:
|
|
3670
3681
|
"""
|
|
3671
|
-
Set the credentials that will be used by the project's model monitoring
|
|
3672
|
-
|
|
3673
|
-
|
|
3674
|
-
|
|
3675
|
-
:param access_key: Model monitoring access key for managing user permissions.
|
|
3676
|
-
|
|
3677
|
-
* None - will be set from the system configuration.
|
|
3678
|
-
* v3io - for v3io endpoint store, pass `v3io` and the system will generate the
|
|
3679
|
-
exact path.
|
|
3680
|
-
:param stream_path: (Deprecated) This argument is deprecated. Use ``stream_profile_name`` instead.
|
|
3681
|
-
Path to the model monitoring stream. By default, None. Options:
|
|
3682
|
-
|
|
3683
|
-
* ``"v3io"`` - for v3io stream, pass ``"v3io"`` and the system will generate
|
|
3684
|
-
the exact path.
|
|
3685
|
-
* Kafka - for Kafka stream, provide the full connection string without acustom
|
|
3686
|
-
topic, for example ``"kafka://<some_kafka_broker>:<port>"``.
|
|
3687
|
-
:param tsdb_connection: (Deprecated) Connection string to the time series database. By default, None.
|
|
3688
|
-
Options:
|
|
3689
|
-
|
|
3690
|
-
* v3io - for v3io stream, pass ``"v3io"`` and the system will generate the
|
|
3691
|
-
exact path.
|
|
3692
|
-
* TDEngine - for TDEngine tsdb, provide the full websocket connection URL,
|
|
3693
|
-
for example ``"taosws://<username>:<password>@<host>:<port>"``.
|
|
3694
|
-
:param replace_creds: If True, will override the existing credentials.
|
|
3695
|
-
Please keep in mind that if you already enabled model monitoring on
|
|
3696
|
-
your project this action can cause data loose and will require redeploying
|
|
3697
|
-
all model monitoring functions & model monitoring infra
|
|
3698
|
-
& tracked model server.
|
|
3699
|
-
:param stream_profile_name: The datastore profile name of the stream to be used in model monitoring.
|
|
3700
|
-
The supported profiles are:
|
|
3682
|
+
Set the credentials that will be used by the project's model monitoring infrastructure functions.
|
|
3683
|
+
Please note that you have to set the credentials before deploying any model monitoring application
|
|
3684
|
+
or a tracked serving function.
|
|
3701
3685
|
|
|
3702
|
-
|
|
3703
|
-
|
|
3686
|
+
For example, the full flow for enabling model monitoring infrastructure with **TDEngine** and **Kafka**, is:
|
|
3687
|
+
|
|
3688
|
+
.. code-block:: python
|
|
3689
|
+
|
|
3690
|
+
import mlrun
|
|
3691
|
+
from mlrun.datastore.datastore_profile import (
|
|
3692
|
+
DatastoreProfileKafkaSource,
|
|
3693
|
+
TDEngineDatastoreProfile,
|
|
3694
|
+
)
|
|
3695
|
+
|
|
3696
|
+
project = mlrun.get_or_create_project("mm-infra-setup")
|
|
3697
|
+
|
|
3698
|
+
# Create and register TSDB profile
|
|
3699
|
+
tsdb_profile = TDEngineDatastoreProfile(
|
|
3700
|
+
name="my-tdengine",
|
|
3701
|
+
host="<tdengine-server-ip-address>",
|
|
3702
|
+
port=6041,
|
|
3703
|
+
user="username",
|
|
3704
|
+
password="<tdengine-password>",
|
|
3705
|
+
)
|
|
3706
|
+
project.register_datastore_profile(tsdb_profile)
|
|
3707
|
+
|
|
3708
|
+
# Create and register stream profile
|
|
3709
|
+
stream_profile = DatastoreProfileKafkaSource(
|
|
3710
|
+
name="my-kafka",
|
|
3711
|
+
brokers=["<kafka-broker-ip-address>:9094"],
|
|
3712
|
+
topics=[], # Keep the topics list empty
|
|
3713
|
+
## SASL is supported
|
|
3714
|
+
# sasl_user="user1",
|
|
3715
|
+
# sasl_pass="<kafka-sasl-password>",
|
|
3716
|
+
)
|
|
3717
|
+
project.register_datastore_profile(stream_profile)
|
|
3718
|
+
|
|
3719
|
+
# Set model monitoring credentials and enable the infrastructure
|
|
3720
|
+
project.set_model_monitoring_credentials(
|
|
3721
|
+
tsdb_profile_name=tsdb_profile.name,
|
|
3722
|
+
stream_profile_name=stream_profile.name,
|
|
3723
|
+
)
|
|
3724
|
+
project.enable_model_monitoring()
|
|
3725
|
+
|
|
3726
|
+
Note that you will need to change the profiles if you want to use **V3IO** TSDB and stream:
|
|
3727
|
+
|
|
3728
|
+
.. code-block:: python
|
|
3729
|
+
|
|
3730
|
+
from mlrun.datastore.datastore_profile import DatastoreProfileV3io
|
|
3731
|
+
|
|
3732
|
+
# Create and register TSDB profile
|
|
3733
|
+
tsdb_profile = DatastoreProfileV3io(
|
|
3734
|
+
name="my-v3io-tsdb",
|
|
3735
|
+
)
|
|
3736
|
+
project.register_datastore_profile(tsdb_profile)
|
|
3737
|
+
|
|
3738
|
+
# Create and register stream profile
|
|
3739
|
+
stream_profile = DatastoreProfileV3io(
|
|
3740
|
+
name="my-v3io-stream",
|
|
3741
|
+
v3io_access_key=mlrun.mlconf.get_v3io_access_key(),
|
|
3742
|
+
)
|
|
3743
|
+
project.register_datastore_profile(stream_profile)
|
|
3744
|
+
|
|
3745
|
+
In the V3IO datastore, you must provide an explicit access key to the stream, but not to the TSDB.
|
|
3704
3746
|
|
|
3705
|
-
You need to register one of them, and pass the profile's name.
|
|
3706
3747
|
:param tsdb_profile_name: The datastore profile name of the time-series database to be used in model
|
|
3707
3748
|
monitoring. The supported profiles are:
|
|
3708
3749
|
|
|
@@ -3710,76 +3751,24 @@ class MlrunProject(ModelObj):
|
|
|
3710
3751
|
* :py:class:`~mlrun.datastore.datastore_profile.TDEngineDatastoreProfile`
|
|
3711
3752
|
|
|
3712
3753
|
You need to register one of them, and pass the profile's name.
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
if tsdb_connection:
|
|
3717
|
-
warnings.warn(
|
|
3718
|
-
"The `tsdb_connection` argument is deprecated and will be removed in MLRun version 1.8.0. "
|
|
3719
|
-
"Use `tsdb_profile_name` instead.",
|
|
3720
|
-
FutureWarning,
|
|
3721
|
-
)
|
|
3722
|
-
if tsdb_profile_name:
|
|
3723
|
-
raise mlrun.errors.MLRunValueError(
|
|
3724
|
-
"If you set `tsdb_profile_name`, you must not pass `tsdb_connection`."
|
|
3725
|
-
)
|
|
3726
|
-
if tsdb_connection == "v3io":
|
|
3727
|
-
tsdb_profile = mlrun.datastore.datastore_profile.DatastoreProfileV3io(
|
|
3728
|
-
name=mm_constants.DefaultProfileName.TSDB
|
|
3729
|
-
)
|
|
3730
|
-
else:
|
|
3731
|
-
parsed_url = urlparse(tsdb_connection)
|
|
3732
|
-
if parsed_url.scheme != "taosws":
|
|
3733
|
-
raise mlrun.errors.MLRunValueError(
|
|
3734
|
-
f"Unsupported `tsdb_connection`: '{tsdb_connection}'."
|
|
3735
|
-
)
|
|
3736
|
-
tsdb_profile = (
|
|
3737
|
-
mlrun.datastore.datastore_profile.TDEngineDatastoreProfile(
|
|
3738
|
-
name=mm_constants.DefaultProfileName.TSDB,
|
|
3739
|
-
user=parsed_url.username,
|
|
3740
|
-
password=parsed_url.password,
|
|
3741
|
-
host=parsed_url.hostname,
|
|
3742
|
-
port=parsed_url.port,
|
|
3743
|
-
)
|
|
3744
|
-
)
|
|
3754
|
+
:param stream_profile_name: The datastore profile name of the stream to be used in model monitoring.
|
|
3755
|
+
The supported profiles are:
|
|
3745
3756
|
|
|
3746
|
-
|
|
3747
|
-
|
|
3757
|
+
* :py:class:`~mlrun.datastore.datastore_profile.DatastoreProfileV3io`
|
|
3758
|
+
* :py:class:`~mlrun.datastore.datastore_profile.DatastoreProfileKafkaSource`
|
|
3748
3759
|
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3756
|
-
|
|
3757
|
-
"If you set `stream_profile_name`, you must not pass `stream_path`."
|
|
3758
|
-
)
|
|
3759
|
-
if stream_path == "v3io":
|
|
3760
|
-
stream_profile = mlrun.datastore.datastore_profile.DatastoreProfileV3io(
|
|
3761
|
-
name=mm_constants.DefaultProfileName.STREAM
|
|
3762
|
-
)
|
|
3763
|
-
else:
|
|
3764
|
-
parsed_stream = urlparse(stream_path)
|
|
3765
|
-
if parsed_stream.scheme != "kafka":
|
|
3766
|
-
raise mlrun.errors.MLRunValueError(
|
|
3767
|
-
f"Unsupported `stream_path`: '{stream_path}'."
|
|
3768
|
-
)
|
|
3769
|
-
stream_profile = (
|
|
3770
|
-
mlrun.datastore.datastore_profile.DatastoreProfileKafkaSource(
|
|
3771
|
-
name=mm_constants.DefaultProfileName.STREAM,
|
|
3772
|
-
brokers=[parsed_stream.netloc],
|
|
3773
|
-
topics=[],
|
|
3774
|
-
)
|
|
3775
|
-
)
|
|
3776
|
-
self.register_datastore_profile(stream_profile)
|
|
3777
|
-
stream_profile_name = stream_profile.name
|
|
3760
|
+
You need to register one of them, and pass the profile's name.
|
|
3761
|
+
:param replace_creds: If ``True`` - override the existing credentials.
|
|
3762
|
+
Please keep in mind that if you have already enabled model monitoring
|
|
3763
|
+
on your project, replacing the credentials can cause data loss, and will
|
|
3764
|
+
require redeploying all the model monitoring functions, model monitoring
|
|
3765
|
+
infrastructure, and tracked model servers.
|
|
3766
|
+
"""
|
|
3767
|
+
db = mlrun.db.get_run_db(secrets=self._secrets)
|
|
3778
3768
|
|
|
3779
3769
|
db.set_model_monitoring_credentials(
|
|
3780
3770
|
project=self.name,
|
|
3781
3771
|
credentials={
|
|
3782
|
-
"access_key": access_key,
|
|
3783
3772
|
"tsdb_profile_name": tsdb_profile_name,
|
|
3784
3773
|
"stream_profile_name": stream_profile_name,
|
|
3785
3774
|
},
|
|
@@ -4120,7 +4109,7 @@ class MlrunProject(ModelObj):
|
|
|
4120
4109
|
|
|
4121
4110
|
:param image: target image name/path. If not specified the project's existing `default_image` name will be
|
|
4122
4111
|
used. If not set, the `mlconf.default_project_image_name` value will be used
|
|
4123
|
-
:param set_as_default: set `image` to be the project's default image (default
|
|
4112
|
+
:param set_as_default: set `image` to be the project's default image (default True)
|
|
4124
4113
|
:param with_mlrun: add the current mlrun package to the container build
|
|
4125
4114
|
:param base_image: base image name/path (commands and source code will be added to it) defaults to
|
|
4126
4115
|
mlrun.mlconf.default_base_image
|
mlrun/run.py
CHANGED
|
@@ -911,7 +911,7 @@ def _run_pipeline(
|
|
|
911
911
|
|
|
912
912
|
def retry_pipeline(
|
|
913
913
|
run_id: str,
|
|
914
|
-
project:
|
|
914
|
+
project: str,
|
|
915
915
|
namespace: Optional[str] = None,
|
|
916
916
|
) -> str:
|
|
917
917
|
"""Retry a pipeline run.
|
|
@@ -920,7 +920,7 @@ def retry_pipeline(
|
|
|
920
920
|
retryable state, a new run is created as a clone of the original run.
|
|
921
921
|
|
|
922
922
|
:param run_id: ID of the pipeline run to retry.
|
|
923
|
-
:param project:
|
|
923
|
+
:param project: name of the project associated with the pipeline run.
|
|
924
924
|
:param namespace: Optional; Kubernetes namespace to use if not the default.
|
|
925
925
|
|
|
926
926
|
:returns: ID of the retried pipeline run or the ID of a cloned run if the original run is not retryable.
|
|
@@ -369,8 +369,9 @@ class RemoteRuntime(KubeResource):
|
|
|
369
369
|
)
|
|
370
370
|
"""
|
|
371
371
|
self.spec.build.source = source
|
|
372
|
-
# update handler in function_handler
|
|
373
|
-
|
|
372
|
+
# update handler in function_handler if needed
|
|
373
|
+
if handler:
|
|
374
|
+
self.spec.function_handler = handler
|
|
374
375
|
if workdir:
|
|
375
376
|
self.spec.workdir = workdir
|
|
376
377
|
if runtime:
|
|
@@ -1016,6 +1017,7 @@ class RemoteRuntime(KubeResource):
|
|
|
1016
1017
|
):
|
|
1017
1018
|
"""
|
|
1018
1019
|
Add a sidecar container to the function pod
|
|
1020
|
+
|
|
1019
1021
|
:param name: Sidecar container name.
|
|
1020
1022
|
:param image: Sidecar container image.
|
|
1021
1023
|
:param ports: Sidecar container ports to expose. Can be a single port or a list of ports.
|
mlrun/serving/routers.py
CHANGED
|
@@ -596,7 +596,6 @@ class VotingEnsemble(ParallelRun):
|
|
|
596
596
|
self.vote_type = vote_type
|
|
597
597
|
self.vote_flag = True if self.vote_type is not None else False
|
|
598
598
|
self.weights = weights
|
|
599
|
-
self.version = kwargs.get("version", "v1")
|
|
600
599
|
self.log_router = True
|
|
601
600
|
self.prediction_col_name = prediction_col_name or "prediction"
|
|
602
601
|
self.format_response_with_col_name_flag = format_response_with_col_name_flag
|
|
@@ -927,14 +926,14 @@ class VotingEnsemble(ParallelRun):
|
|
|
927
926
|
"model_name": self.name,
|
|
928
927
|
"outputs": votes,
|
|
929
928
|
}
|
|
930
|
-
if self.
|
|
931
|
-
response_body["
|
|
929
|
+
if self.model_endpoint_uid:
|
|
930
|
+
response_body["model_endpoint_uid"] = self.model_endpoint_uid
|
|
932
931
|
response.body = response_body
|
|
933
932
|
elif name == self.name and event.method == "GET" and not subpath:
|
|
934
933
|
response = copy.copy(event)
|
|
935
934
|
response_body = {
|
|
936
935
|
"name": self.name,
|
|
937
|
-
"
|
|
936
|
+
"model_endpoint_uid": self.model_endpoint_uid or "",
|
|
938
937
|
"inputs": [],
|
|
939
938
|
"outputs": [],
|
|
940
939
|
}
|
mlrun/serving/server.py
CHANGED
|
@@ -65,7 +65,7 @@ class _StreamContext:
|
|
|
65
65
|
self.hostname = socket.gethostname()
|
|
66
66
|
self.function_uri = function_uri
|
|
67
67
|
self.output_stream = None
|
|
68
|
-
|
|
68
|
+
stream_uri = None
|
|
69
69
|
log_stream = parameters.get(FileTargetKind.LOG_STREAM, "")
|
|
70
70
|
|
|
71
71
|
if (enabled or log_stream) and function_uri:
|
|
@@ -78,17 +78,19 @@ class _StreamContext:
|
|
|
78
78
|
|
|
79
79
|
if log_stream == DUMMY_STREAM:
|
|
80
80
|
# Dummy stream used for testing, see tests/serving/test_serving.py
|
|
81
|
-
|
|
81
|
+
stream_uri = DUMMY_STREAM
|
|
82
82
|
elif not stream_args.get("mock"): # if not a mock: `context.is_mock = True`
|
|
83
|
-
|
|
84
|
-
project=project
|
|
85
|
-
)
|
|
83
|
+
stream_uri = mlrun.model_monitoring.get_stream_path(project=project)
|
|
86
84
|
|
|
87
85
|
if log_stream:
|
|
88
86
|
# Update the stream path to the log stream value
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
87
|
+
stream_uri = log_stream.format(project=project)
|
|
88
|
+
self.output_stream = get_stream_pusher(stream_uri, **stream_args)
|
|
89
|
+
else:
|
|
90
|
+
# Get the output stream from the profile
|
|
91
|
+
self.output_stream = mlrun.model_monitoring.helpers.get_output_stream(
|
|
92
|
+
project=project, mock=stream_args.get("mock", False)
|
|
93
|
+
)
|
|
92
94
|
|
|
93
95
|
|
|
94
96
|
class GraphServer(ModelObj):
|
mlrun/serving/states.py
CHANGED
|
@@ -652,6 +652,14 @@ class TaskStep(BaseStep):
|
|
|
652
652
|
if isinstance(self.endpoint_type, schemas.EndpointType)
|
|
653
653
|
else self.endpoint_type
|
|
654
654
|
)
|
|
655
|
+
self.model_endpoint_creation_strategy = (
|
|
656
|
+
self.model_endpoint_creation_strategy.value
|
|
657
|
+
if isinstance(
|
|
658
|
+
self.model_endpoint_creation_strategy,
|
|
659
|
+
schemas.ModelEndpointCreationStrategy,
|
|
660
|
+
)
|
|
661
|
+
else self.model_endpoint_creation_strategy
|
|
662
|
+
)
|
|
655
663
|
return super().to_dict(fields, exclude, strip)
|
|
656
664
|
|
|
657
665
|
|
|
@@ -755,9 +763,11 @@ class RouterStep(TaskStep):
|
|
|
755
763
|
self._routes: ObjectDict = None
|
|
756
764
|
self.routes = routes
|
|
757
765
|
self.endpoint_type = schemas.EndpointType.ROUTER
|
|
766
|
+
if isinstance(class_name, type):
|
|
767
|
+
class_name = class_name.__name__
|
|
758
768
|
self.model_endpoint_creation_strategy = (
|
|
759
769
|
schemas.ModelEndpointCreationStrategy.INPLACE
|
|
760
|
-
if class_name and "
|
|
770
|
+
if class_name and "VotingEnsemble" in class_name
|
|
761
771
|
else schemas.ModelEndpointCreationStrategy.SKIP
|
|
762
772
|
)
|
|
763
773
|
|
|
@@ -802,8 +812,8 @@ class RouterStep(TaskStep):
|
|
|
802
812
|
* **archive**:
|
|
803
813
|
1. If model endpoints with the same name exist, preserve them.
|
|
804
814
|
2. Create a new model endpoint with the same name and set it to `latest`.
|
|
805
|
-
|
|
806
815
|
"""
|
|
816
|
+
|
|
807
817
|
if len(self.routes.keys()) >= MAX_MODELS_PER_ROUTER and key not in self.routes:
|
|
808
818
|
raise mlrun.errors.MLRunModelLimitExceededError(
|
|
809
819
|
f"Router cannot support more than {MAX_MODELS_PER_ROUTER} model endpoints. "
|
mlrun/serving/v2_serving.py
CHANGED
|
@@ -95,9 +95,6 @@ class V2ModelServer(StepToDict):
|
|
|
95
95
|
:param kwargs: extra arguments (can be accessed using self.get_param(key))
|
|
96
96
|
"""
|
|
97
97
|
self.name = name
|
|
98
|
-
self.version = ""
|
|
99
|
-
if name and ":" in name:
|
|
100
|
-
self.version = name.split(":", 1)[-1]
|
|
101
98
|
self.context = context
|
|
102
99
|
self.ready = False
|
|
103
100
|
self.error = ""
|
|
@@ -118,6 +115,7 @@ class V2ModelServer(StepToDict):
|
|
|
118
115
|
self.shard_by_endpoint = shard_by_endpoint
|
|
119
116
|
self._model_logger = None
|
|
120
117
|
self.initialized = False
|
|
118
|
+
self.output_schema = []
|
|
121
119
|
|
|
122
120
|
def _load_and_update_state(self):
|
|
123
121
|
try:
|
|
@@ -178,17 +176,15 @@ class V2ModelServer(StepToDict):
|
|
|
178
176
|
"Model endpoint creation task name not provided",
|
|
179
177
|
)
|
|
180
178
|
try:
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
.
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
function_tag=server.function_tag or "latest",
|
|
188
|
-
tsdb_metrics=False,
|
|
189
|
-
)
|
|
190
|
-
.metadata.uid
|
|
179
|
+
model_endpoint = mlrun.get_run_db().get_model_endpoint(
|
|
180
|
+
project=server.project,
|
|
181
|
+
name=self.name,
|
|
182
|
+
function_name=server.function_name,
|
|
183
|
+
function_tag=server.function_tag or "latest",
|
|
184
|
+
tsdb_metrics=False,
|
|
191
185
|
)
|
|
186
|
+
self.model_endpoint_uid = model_endpoint.metadata.uid
|
|
187
|
+
self.output_schema = model_endpoint.spec.label_names
|
|
192
188
|
except mlrun.errors.MLRunNotFoundError:
|
|
193
189
|
logger.info(
|
|
194
190
|
"Model endpoint not found for this step; monitoring for this model will not be performed",
|
|
@@ -325,8 +321,8 @@ class V2ModelServer(StepToDict):
|
|
|
325
321
|
"outputs": outputs,
|
|
326
322
|
"timestamp": start.isoformat(sep=" ", timespec="microseconds"),
|
|
327
323
|
}
|
|
328
|
-
if self.
|
|
329
|
-
response["
|
|
324
|
+
if self.model_endpoint_uid:
|
|
325
|
+
response["model_endpoint_uid"] = self.model_endpoint_uid
|
|
330
326
|
elif op == "ready" and event.method == "GET":
|
|
331
327
|
# get model health operation
|
|
332
328
|
setattr(event, "terminated", True)
|
|
@@ -352,7 +348,7 @@ class V2ModelServer(StepToDict):
|
|
|
352
348
|
setattr(event, "terminated", True)
|
|
353
349
|
event_body = {
|
|
354
350
|
"name": self.name.split(":")[0],
|
|
355
|
-
"
|
|
351
|
+
"model_endpoint_uid": self.model_endpoint_uid or "",
|
|
356
352
|
"inputs": [],
|
|
357
353
|
"outputs": [],
|
|
358
354
|
}
|
|
@@ -386,8 +382,8 @@ class V2ModelServer(StepToDict):
|
|
|
386
382
|
"model_name": self.name,
|
|
387
383
|
"outputs": outputs,
|
|
388
384
|
}
|
|
389
|
-
if self.
|
|
390
|
-
response["
|
|
385
|
+
if self.model_endpoint_uid:
|
|
386
|
+
response["model_endpoint_uid"] = self.model_endpoint_uid
|
|
391
387
|
|
|
392
388
|
elif hasattr(self, "op_" + op):
|
|
393
389
|
# custom operation (child methods starting with "op_")
|
|
@@ -510,7 +506,6 @@ class _ModelLogPusher:
|
|
|
510
506
|
self.verbose = context.verbose
|
|
511
507
|
self.hostname = context.stream.hostname
|
|
512
508
|
self.function_uri = context.stream.function_uri
|
|
513
|
-
self.stream_path = context.stream.stream_uri
|
|
514
509
|
self.sampling_percentage = float(context.get_param("sampling_percentage", 100))
|
|
515
510
|
self.output_stream = output_stream or context.stream.output_stream
|
|
516
511
|
self._worker = context.worker_id
|
|
@@ -520,7 +515,6 @@ class _ModelLogPusher:
|
|
|
520
515
|
"class": self.model.__class__.__name__,
|
|
521
516
|
"worker": self._worker,
|
|
522
517
|
"model": self.model.name,
|
|
523
|
-
"version": self.model.version,
|
|
524
518
|
"host": self.hostname,
|
|
525
519
|
"function_uri": self.function_uri,
|
|
526
520
|
"endpoint_id": self.model.model_endpoint_uid,
|
|
@@ -571,6 +565,17 @@ class _ModelLogPusher:
|
|
|
571
565
|
resp["outputs"] = [
|
|
572
566
|
resp["outputs"][i] for i in sampled_requests_indices
|
|
573
567
|
]
|
|
568
|
+
if self.model.output_schema and len(self.model.output_schema) != len(
|
|
569
|
+
resp["outputs"][0]
|
|
570
|
+
):
|
|
571
|
+
logger.info(
|
|
572
|
+
"The number of outputs returned by the model does not match the number of outputs "
|
|
573
|
+
"specified in the model endpoint.",
|
|
574
|
+
model_endpoint=self.model.name,
|
|
575
|
+
model_endpoint_id=self.model.model_endpoint_uid,
|
|
576
|
+
output_len=len(resp["outputs"][0]),
|
|
577
|
+
schema_len=len(self.model.output_schema),
|
|
578
|
+
)
|
|
574
579
|
|
|
575
580
|
data = self.base_data()
|
|
576
581
|
data["request"] = request
|