mlrun 1.8.0rc21__py3-none-any.whl → 1.8.0rc24__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 +37 -3
- mlrun/artifacts/document.py +40 -11
- mlrun/common/schemas/client_spec.py +0 -1
- mlrun/common/schemas/model_monitoring/constants.py +26 -9
- mlrun/config.py +39 -6
- mlrun/datastore/datastore_profile.py +58 -16
- mlrun/datastore/sources.py +7 -1
- mlrun/datastore/vectorstore.py +17 -1
- mlrun/db/base.py +3 -0
- mlrun/db/httpdb.py +0 -8
- mlrun/db/nopdb.py +3 -0
- mlrun/errors.py +4 -0
- mlrun/execution.py +1 -0
- mlrun/model_monitoring/controller.py +266 -103
- mlrun/model_monitoring/db/tsdb/__init__.py +11 -23
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +5 -2
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +2 -2
- mlrun/model_monitoring/helpers.py +15 -9
- mlrun/model_monitoring/stream_processing.py +72 -2
- mlrun/projects/project.py +95 -32
- mlrun/runtimes/nuclio/serving.py +1 -1
- mlrun/serving/server.py +11 -3
- mlrun/serving/states.py +33 -8
- mlrun/utils/notifications/notification_pusher.py +11 -2
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.8.0rc21.dist-info → mlrun-1.8.0rc24.dist-info}/METADATA +14 -2
- {mlrun-1.8.0rc21.dist-info → mlrun-1.8.0rc24.dist-info}/RECORD +31 -31
- {mlrun-1.8.0rc21.dist-info → mlrun-1.8.0rc24.dist-info}/WHEEL +1 -1
- {mlrun-1.8.0rc21.dist-info → mlrun-1.8.0rc24.dist-info}/LICENSE +0 -0
- {mlrun-1.8.0rc21.dist-info → mlrun-1.8.0rc24.dist-info}/entry_points.txt +0 -0
- {mlrun-1.8.0rc21.dist-info → mlrun-1.8.0rc24.dist-info}/top_level.txt +0 -0
|
@@ -29,11 +29,14 @@ import mlrun.model_monitoring.db
|
|
|
29
29
|
import mlrun.serving.states
|
|
30
30
|
import mlrun.utils
|
|
31
31
|
from mlrun.common.schemas.model_monitoring.constants import (
|
|
32
|
+
ControllerEvent,
|
|
33
|
+
ControllerEventKind,
|
|
32
34
|
EndpointType,
|
|
33
35
|
EventFieldType,
|
|
34
36
|
FileTargetKind,
|
|
35
37
|
ProjectSecretKeys,
|
|
36
38
|
)
|
|
39
|
+
from mlrun.datastore import parse_kafka_url
|
|
37
40
|
from mlrun.model_monitoring.db import TSDBConnector
|
|
38
41
|
from mlrun.utils import logger
|
|
39
42
|
|
|
@@ -88,7 +91,9 @@ class EventStreamProcessor:
|
|
|
88
91
|
self.v3io_framesd = v3io_framesd or mlrun.mlconf.v3io_framesd
|
|
89
92
|
self.v3io_api = v3io_api or mlrun.mlconf.v3io_api
|
|
90
93
|
|
|
91
|
-
self.v3io_access_key = v3io_access_key or
|
|
94
|
+
self.v3io_access_key = v3io_access_key or mlrun.get_secret_or_env(
|
|
95
|
+
"V3IO_ACCESS_KEY"
|
|
96
|
+
)
|
|
92
97
|
self.model_monitoring_access_key = (
|
|
93
98
|
model_monitoring_access_key
|
|
94
99
|
or os.environ.get(ProjectSecretKeys.ACCESS_KEY)
|
|
@@ -118,6 +123,7 @@ class EventStreamProcessor:
|
|
|
118
123
|
self,
|
|
119
124
|
fn: mlrun.runtimes.ServingRuntime,
|
|
120
125
|
tsdb_connector: TSDBConnector,
|
|
126
|
+
controller_stream_uri: str,
|
|
121
127
|
) -> None:
|
|
122
128
|
"""
|
|
123
129
|
Apply monitoring serving graph to a given serving function. The following serving graph includes about 4 main
|
|
@@ -146,6 +152,8 @@ class EventStreamProcessor:
|
|
|
146
152
|
|
|
147
153
|
:param fn: A serving function.
|
|
148
154
|
:param tsdb_connector: Time series database connector.
|
|
155
|
+
:param controller_stream_uri: The controller stream URI. Runs on server api pod so needed to be provided as
|
|
156
|
+
input
|
|
149
157
|
"""
|
|
150
158
|
|
|
151
159
|
graph = typing.cast(
|
|
@@ -209,6 +217,20 @@ class EventStreamProcessor:
|
|
|
209
217
|
)
|
|
210
218
|
|
|
211
219
|
apply_map_feature_names()
|
|
220
|
+
# split the graph between event with error vs valid event
|
|
221
|
+
graph.add_step(
|
|
222
|
+
"storey.Filter",
|
|
223
|
+
"FilterNOP",
|
|
224
|
+
after="MapFeatureNames",
|
|
225
|
+
_fn="(event.get('kind', " ") != 'nop_event')",
|
|
226
|
+
)
|
|
227
|
+
graph.add_step(
|
|
228
|
+
"storey.Filter",
|
|
229
|
+
"ForwardNOP",
|
|
230
|
+
after="MapFeatureNames",
|
|
231
|
+
_fn="(event.get('kind', " ") == 'nop_event')",
|
|
232
|
+
)
|
|
233
|
+
|
|
212
234
|
tsdb_connector.apply_monitoring_stream_steps(
|
|
213
235
|
graph=graph,
|
|
214
236
|
aggregate_windows=self.aggregate_windows,
|
|
@@ -221,7 +243,7 @@ class EventStreamProcessor:
|
|
|
221
243
|
graph.add_step(
|
|
222
244
|
"ProcessBeforeParquet",
|
|
223
245
|
name="ProcessBeforeParquet",
|
|
224
|
-
after="
|
|
246
|
+
after="FilterNOP",
|
|
225
247
|
_fn="(event)",
|
|
226
248
|
)
|
|
227
249
|
|
|
@@ -248,6 +270,44 @@ class EventStreamProcessor:
|
|
|
248
270
|
|
|
249
271
|
apply_parquet_target()
|
|
250
272
|
|
|
273
|
+
# controller branch
|
|
274
|
+
def apply_push_controller_stream(stream_uri: str):
|
|
275
|
+
if stream_uri.startswith("v3io://"):
|
|
276
|
+
graph.add_step(
|
|
277
|
+
">>",
|
|
278
|
+
"controller_stream_v3io",
|
|
279
|
+
path=stream_uri,
|
|
280
|
+
sharding_func=ControllerEvent.ENDPOINT_ID,
|
|
281
|
+
access_key=self.v3io_access_key,
|
|
282
|
+
after="ForwardNOP",
|
|
283
|
+
)
|
|
284
|
+
elif stream_uri.startswith("kafka://"):
|
|
285
|
+
topic, brokers = parse_kafka_url(stream_uri)
|
|
286
|
+
logger.info(
|
|
287
|
+
"Controller stream uri for kafka",
|
|
288
|
+
stream_uri=stream_uri,
|
|
289
|
+
topic=topic,
|
|
290
|
+
brokers=brokers,
|
|
291
|
+
)
|
|
292
|
+
if isinstance(brokers, list):
|
|
293
|
+
path = f"kafka://{brokers[0]}/{topic}"
|
|
294
|
+
elif isinstance(brokers, str):
|
|
295
|
+
path = f"kafka://{brokers}/{topic}"
|
|
296
|
+
else:
|
|
297
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
298
|
+
"Brokers must be a list or str check controller stream uri"
|
|
299
|
+
)
|
|
300
|
+
graph.add_step(
|
|
301
|
+
">>",
|
|
302
|
+
"controller_stream_kafka",
|
|
303
|
+
path=path,
|
|
304
|
+
kafka_brokers=brokers,
|
|
305
|
+
_sharding_func="kafka_sharding_func", # TODO: remove this when storey handle str key
|
|
306
|
+
after="ForwardNOP",
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
apply_push_controller_stream(controller_stream_uri)
|
|
310
|
+
|
|
251
311
|
|
|
252
312
|
class ProcessBeforeParquet(mlrun.feature_store.steps.MapClass):
|
|
253
313
|
def __init__(self, **kwargs):
|
|
@@ -321,6 +381,9 @@ class ProcessEndpointEvent(mlrun.feature_store.steps.MapClass):
|
|
|
321
381
|
|
|
322
382
|
def do(self, full_event):
|
|
323
383
|
event = full_event.body
|
|
384
|
+
if event.get(ControllerEvent.KIND, "") == ControllerEventKind.NOP_EVENT:
|
|
385
|
+
logger.info("Skipped nop event inside of ProcessEndpointEvent", event=event)
|
|
386
|
+
return storey.Event(body=[event])
|
|
324
387
|
# Getting model version and function uri from event
|
|
325
388
|
# and use them for retrieving the endpoint_id
|
|
326
389
|
function_uri = full_event.body.get(EventFieldType.FUNCTION_URI)
|
|
@@ -589,6 +652,9 @@ class MapFeatureNames(mlrun.feature_store.steps.MapClass):
|
|
|
589
652
|
return None
|
|
590
653
|
|
|
591
654
|
def do(self, event: dict):
|
|
655
|
+
if event.get(ControllerEvent.KIND, "") == ControllerEventKind.NOP_EVENT:
|
|
656
|
+
logger.info("Skipped nop event inside of MapFeatureNames", event=event)
|
|
657
|
+
return event
|
|
592
658
|
endpoint_id = event[EventFieldType.ENDPOINT_ID]
|
|
593
659
|
|
|
594
660
|
feature_values = event[EventFieldType.FEATURES]
|
|
@@ -827,3 +893,7 @@ def update_monitoring_feature_set(
|
|
|
827
893
|
)
|
|
828
894
|
|
|
829
895
|
monitoring_feature_set.save()
|
|
896
|
+
|
|
897
|
+
|
|
898
|
+
def kafka_sharding_func(event):
|
|
899
|
+
return event.body[ControllerEvent.ENDPOINT_ID].encode("UTF-8")
|
mlrun/projects/project.py
CHANGED
|
@@ -29,6 +29,7 @@ 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
|
|
32
33
|
|
|
33
34
|
import dotenv
|
|
34
35
|
import git
|
|
@@ -1964,17 +1965,17 @@ class MlrunProject(ModelObj):
|
|
|
1964
1965
|
... )
|
|
1965
1966
|
|
|
1966
1967
|
"""
|
|
1968
|
+
document_loader_spec = document_loader_spec or DocumentLoaderSpec()
|
|
1967
1969
|
if not document_loader_spec.download_object and upload:
|
|
1968
1970
|
raise ValueError(
|
|
1969
|
-
"
|
|
1970
|
-
"Either set download_object=True or set upload=False"
|
|
1971
|
+
"The document loader is configured to not support downloads but the upload flag is set to True."
|
|
1972
|
+
"Either set loader.download_object=True or set upload=False"
|
|
1971
1973
|
)
|
|
1972
1974
|
doc_artifact = DocumentArtifact(
|
|
1973
1975
|
key=key,
|
|
1974
1976
|
original_source=local_path or target_path,
|
|
1975
|
-
document_loader_spec=document_loader_spec
|
|
1976
|
-
|
|
1977
|
-
else DocumentLoaderSpec(),
|
|
1977
|
+
document_loader_spec=document_loader_spec,
|
|
1978
|
+
collections=kwargs.pop("collections", None),
|
|
1978
1979
|
**kwargs,
|
|
1979
1980
|
)
|
|
1980
1981
|
return self.log_artifact(
|
|
@@ -3608,9 +3609,12 @@ class MlrunProject(ModelObj):
|
|
|
3608
3609
|
def set_model_monitoring_credentials(
|
|
3609
3610
|
self,
|
|
3610
3611
|
access_key: Optional[str] = None,
|
|
3611
|
-
stream_path: Optional[str] = None,
|
|
3612
|
-
tsdb_connection: Optional[str] = None,
|
|
3612
|
+
stream_path: Optional[str] = None, # Deprecated
|
|
3613
|
+
tsdb_connection: Optional[str] = None, # Deprecated
|
|
3613
3614
|
replace_creds: bool = False,
|
|
3615
|
+
*,
|
|
3616
|
+
stream_profile_name: Optional[str] = None,
|
|
3617
|
+
tsdb_profile_name: Optional[str] = None,
|
|
3614
3618
|
):
|
|
3615
3619
|
"""
|
|
3616
3620
|
Set the credentials that will be used by the project's model monitoring
|
|
@@ -3622,50 +3626,109 @@ class MlrunProject(ModelObj):
|
|
|
3622
3626
|
* None - will be set from the system configuration.
|
|
3623
3627
|
* v3io - for v3io endpoint store, pass `v3io` and the system will generate the
|
|
3624
3628
|
exact path.
|
|
3625
|
-
:param stream_path:
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
* v3io - for v3io stream, pass
|
|
3629
|
-
path.
|
|
3630
|
-
* Kafka - for Kafka stream, provide the full connection string without
|
|
3631
|
-
topic, for example kafka://<some_kafka_broker>:<port
|
|
3632
|
-
:param tsdb_connection: Connection string to the time series database. By default, None.
|
|
3629
|
+
:param stream_path: (Deprecated) This argument is deprecated. Use ``stream_profile_name`` instead.
|
|
3630
|
+
Path to the model monitoring stream. By default, None. Options:
|
|
3631
|
+
|
|
3632
|
+
* ``"v3io"`` - for v3io stream, pass ``"v3io"`` and the system will generate
|
|
3633
|
+
the exact path.
|
|
3634
|
+
* Kafka - for Kafka stream, provide the full connection string without acustom
|
|
3635
|
+
topic, for example ``"kafka://<some_kafka_broker>:<port>"``.
|
|
3636
|
+
:param tsdb_connection: (Deprecated) Connection string to the time series database. By default, None.
|
|
3633
3637
|
Options:
|
|
3634
3638
|
|
|
3635
|
-
*
|
|
3636
|
-
|
|
3637
|
-
path.
|
|
3639
|
+
* v3io - for v3io stream, pass ``"v3io"`` and the system will generate the
|
|
3640
|
+
exact path.
|
|
3638
3641
|
* TDEngine - for TDEngine tsdb, provide the full websocket connection URL,
|
|
3639
|
-
for example taosws://<username>:<password>@<host>:<port
|
|
3642
|
+
for example ``"taosws://<username>:<password>@<host>:<port>"``.
|
|
3640
3643
|
:param replace_creds: If True, will override the existing credentials.
|
|
3641
3644
|
Please keep in mind that if you already enabled model monitoring on
|
|
3642
3645
|
your project this action can cause data loose and will require redeploying
|
|
3643
3646
|
all model monitoring functions & model monitoring infra
|
|
3644
3647
|
& tracked model server.
|
|
3648
|
+
:param stream_profile_name: The datastore profile name of the stream to be used in model monitoring.
|
|
3649
|
+
The supported profiles are:
|
|
3650
|
+
|
|
3651
|
+
* :py:class:`~mlrun.datastore.datastore_profile.DatastoreProfileV3io`
|
|
3652
|
+
* :py:class:`~mlrun.datastore.datastore_profile.DatastoreProfileKafkaSource`
|
|
3653
|
+
|
|
3654
|
+
You need to register one of them, and pass the profile's name.
|
|
3655
|
+
:param tsdb_profile_name: The datastore profile name of the time-series database to be used in model
|
|
3656
|
+
monitoring. The supported profiles are:
|
|
3657
|
+
|
|
3658
|
+
* :py:class:`~mlrun.datastore.datastore_profile.DatastoreProfileV3io`
|
|
3659
|
+
* :py:class:`~mlrun.datastore.datastore_profile.TDEngineDatastoreProfile`
|
|
3660
|
+
|
|
3661
|
+
You need to register one of them, and pass the profile's name.
|
|
3645
3662
|
"""
|
|
3646
3663
|
db = mlrun.db.get_run_db(secrets=self._secrets)
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3664
|
+
|
|
3665
|
+
if tsdb_connection:
|
|
3666
|
+
warnings.warn(
|
|
3667
|
+
"The `tsdb_connection` argument is deprecated and will be removed in MLRun version 1.8.0. "
|
|
3668
|
+
"Use `tsdb_profile_name` instead.",
|
|
3669
|
+
FutureWarning,
|
|
3650
3670
|
)
|
|
3671
|
+
if tsdb_profile_name:
|
|
3672
|
+
raise mlrun.errors.MLRunValueError(
|
|
3673
|
+
"If you set `tsdb_profile_name`, you must not pass `tsdb_connection`."
|
|
3674
|
+
)
|
|
3675
|
+
if tsdb_connection == "v3io":
|
|
3676
|
+
tsdb_profile = mlrun.datastore.datastore_profile.DatastoreProfileV3io(
|
|
3677
|
+
name=mm_constants.DefaultProfileName.TSDB
|
|
3678
|
+
)
|
|
3679
|
+
else:
|
|
3680
|
+
parsed_url = urlparse(tsdb_connection)
|
|
3681
|
+
if parsed_url.scheme != "taosws":
|
|
3682
|
+
raise mlrun.errors.MLRunValueError(
|
|
3683
|
+
f"Unsupported `tsdb_connection`: '{tsdb_connection}'."
|
|
3684
|
+
)
|
|
3685
|
+
tsdb_profile = (
|
|
3686
|
+
mlrun.datastore.datastore_profile.TDEngineDatastoreProfile(
|
|
3687
|
+
name=mm_constants.DefaultProfileName.TSDB,
|
|
3688
|
+
user=parsed_url.username,
|
|
3689
|
+
password=parsed_url.password,
|
|
3690
|
+
host=parsed_url.hostname,
|
|
3691
|
+
port=parsed_url.port,
|
|
3692
|
+
)
|
|
3693
|
+
)
|
|
3694
|
+
|
|
3651
3695
|
self.register_datastore_profile(tsdb_profile)
|
|
3652
3696
|
tsdb_profile_name = tsdb_profile.name
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
|
|
3657
|
-
|
|
3697
|
+
|
|
3698
|
+
if stream_path:
|
|
3699
|
+
warnings.warn(
|
|
3700
|
+
"The `stream_path` argument is deprecated and will be removed in MLRun version 1.8.0. "
|
|
3701
|
+
"Use `stream_profile_name` instead.",
|
|
3702
|
+
FutureWarning,
|
|
3658
3703
|
)
|
|
3704
|
+
if stream_profile_name:
|
|
3705
|
+
raise mlrun.errors.MLRunValueError(
|
|
3706
|
+
"If you set `stream_profile_name`, you must not pass `stream_path`."
|
|
3707
|
+
)
|
|
3708
|
+
if stream_path == "v3io":
|
|
3709
|
+
stream_profile = mlrun.datastore.datastore_profile.DatastoreProfileV3io(
|
|
3710
|
+
name=mm_constants.DefaultProfileName.STREAM
|
|
3711
|
+
)
|
|
3712
|
+
else:
|
|
3713
|
+
parsed_stream = urlparse(stream_path)
|
|
3714
|
+
if parsed_stream.scheme != "kafka":
|
|
3715
|
+
raise mlrun.errors.MLRunValueError(
|
|
3716
|
+
f"Unsupported `stream_path`: '{stream_path}'."
|
|
3717
|
+
)
|
|
3718
|
+
stream_profile = (
|
|
3719
|
+
mlrun.datastore.datastore_profile.DatastoreProfileKafkaSource(
|
|
3720
|
+
name=mm_constants.DefaultProfileName.STREAM,
|
|
3721
|
+
brokers=[parsed_stream.netloc],
|
|
3722
|
+
topics=[],
|
|
3723
|
+
)
|
|
3724
|
+
)
|
|
3659
3725
|
self.register_datastore_profile(stream_profile)
|
|
3660
3726
|
stream_profile_name = stream_profile.name
|
|
3661
|
-
|
|
3662
|
-
stream_profile_name = None
|
|
3727
|
+
|
|
3663
3728
|
db.set_model_monitoring_credentials(
|
|
3664
3729
|
project=self.name,
|
|
3665
3730
|
credentials={
|
|
3666
3731
|
"access_key": access_key,
|
|
3667
|
-
"stream_path": stream_path,
|
|
3668
|
-
"tsdb_connection": tsdb_connection,
|
|
3669
3732
|
"tsdb_profile_name": tsdb_profile_name,
|
|
3670
3733
|
"stream_profile_name": stream_profile_name,
|
|
3671
3734
|
},
|
|
@@ -3676,7 +3739,7 @@ class MlrunProject(ModelObj):
|
|
|
3676
3739
|
"Model monitoring credentials were set successfully. "
|
|
3677
3740
|
"Please keep in mind that if you already had model monitoring functions "
|
|
3678
3741
|
"/ model monitoring infra / tracked model server "
|
|
3679
|
-
"deployed on your project, you will need to redeploy them."
|
|
3742
|
+
"deployed on your project, you will need to redeploy them. "
|
|
3680
3743
|
"For redeploying the model monitoring infra, please use `enable_model_monitoring` API "
|
|
3681
3744
|
"and set `rebuild_images=True`"
|
|
3682
3745
|
)
|
mlrun/runtimes/nuclio/serving.py
CHANGED
|
@@ -688,7 +688,7 @@ class ServingRuntime(RemoteRuntime):
|
|
|
688
688
|
"project": self.metadata.project,
|
|
689
689
|
"version": "v2",
|
|
690
690
|
"parameters": self.spec.parameters,
|
|
691
|
-
"graph": self.spec.graph.to_dict() if self.spec.graph else {},
|
|
691
|
+
"graph": self.spec.graph.to_dict(strip=True) if self.spec.graph else {},
|
|
692
692
|
"load_mode": self.spec.load_mode,
|
|
693
693
|
"functions": function_name_uri_map,
|
|
694
694
|
"graph_initializer": self.spec.graph_initializer,
|
mlrun/serving/server.py
CHANGED
|
@@ -44,6 +44,8 @@ from ..utils import get_caller_globals
|
|
|
44
44
|
from .states import RootFlowStep, RouterStep, get_function, graph_root_setter
|
|
45
45
|
from .utils import event_id_key, event_path_key
|
|
46
46
|
|
|
47
|
+
DUMMY_STREAM = "dummy://"
|
|
48
|
+
|
|
47
49
|
|
|
48
50
|
class _StreamContext:
|
|
49
51
|
"""Handles the stream context for the events stream process. Includes the configuration for the output stream
|
|
@@ -72,14 +74,20 @@ class _StreamContext:
|
|
|
72
74
|
function_uri, config.default_project
|
|
73
75
|
)
|
|
74
76
|
|
|
75
|
-
|
|
77
|
+
stream_args = parameters.get("stream_args", {})
|
|
78
|
+
|
|
79
|
+
if log_stream == DUMMY_STREAM:
|
|
80
|
+
# Dummy stream used for testing, see tests/serving/test_serving.py
|
|
81
|
+
self.stream_uri = DUMMY_STREAM
|
|
82
|
+
elif not stream_args.get("mock"): # if not a mock: `context.is_mock = True`
|
|
83
|
+
self.stream_uri = mlrun.model_monitoring.get_stream_path(
|
|
84
|
+
project=project
|
|
85
|
+
)
|
|
76
86
|
|
|
77
87
|
if log_stream:
|
|
78
88
|
# Update the stream path to the log stream value
|
|
79
89
|
self.stream_uri = log_stream.format(project=project)
|
|
80
90
|
|
|
81
|
-
stream_args = parameters.get("stream_args", {})
|
|
82
|
-
|
|
83
91
|
self.output_stream = get_stream_pusher(self.stream_uri, **stream_args)
|
|
84
92
|
|
|
85
93
|
|
mlrun/serving/states.py
CHANGED
|
@@ -31,6 +31,7 @@ import storey.utils
|
|
|
31
31
|
|
|
32
32
|
import mlrun
|
|
33
33
|
import mlrun.common.schemas as schemas
|
|
34
|
+
from mlrun.utils import logger
|
|
34
35
|
|
|
35
36
|
from ..config import config
|
|
36
37
|
from ..datastore import get_stream_pusher
|
|
@@ -49,6 +50,8 @@ path_splitter = "/"
|
|
|
49
50
|
previous_step = "$prev"
|
|
50
51
|
queue_class_names = [">>", "$queue"]
|
|
51
52
|
|
|
53
|
+
MAX_MODELS_PER_ROUTER = 5000
|
|
54
|
+
|
|
52
55
|
|
|
53
56
|
class GraphError(Exception):
|
|
54
57
|
"""error in graph topology or configuration"""
|
|
@@ -86,8 +89,10 @@ _task_step_fields = [
|
|
|
86
89
|
"endpoint_type",
|
|
87
90
|
]
|
|
88
91
|
|
|
89
|
-
|
|
90
|
-
|
|
92
|
+
_default_fields_to_strip_from_step = [
|
|
93
|
+
"model_endpoint_creation_strategy",
|
|
94
|
+
"endpoint_type",
|
|
95
|
+
]
|
|
91
96
|
|
|
92
97
|
|
|
93
98
|
def new_remote_endpoint(
|
|
@@ -110,6 +115,7 @@ class BaseStep(ModelObj):
|
|
|
110
115
|
kind = "BaseStep"
|
|
111
116
|
default_shape = "ellipse"
|
|
112
117
|
_dict_fields = ["kind", "comment", "after", "on_error"]
|
|
118
|
+
_default_fields_to_strip = _default_fields_to_strip_from_step
|
|
113
119
|
|
|
114
120
|
def __init__(
|
|
115
121
|
self,
|
|
@@ -625,6 +631,19 @@ class TaskStep(BaseStep):
|
|
|
625
631
|
raise exc
|
|
626
632
|
return event
|
|
627
633
|
|
|
634
|
+
def to_dict(
|
|
635
|
+
self,
|
|
636
|
+
fields: Optional[list] = None,
|
|
637
|
+
exclude: Optional[list] = None,
|
|
638
|
+
strip: bool = False,
|
|
639
|
+
) -> dict:
|
|
640
|
+
self.endpoint_type = (
|
|
641
|
+
self.endpoint_type.value
|
|
642
|
+
if isinstance(self.endpoint_type, schemas.EndpointType)
|
|
643
|
+
else self.endpoint_type
|
|
644
|
+
)
|
|
645
|
+
return super().to_dict(fields, exclude, strip)
|
|
646
|
+
|
|
628
647
|
|
|
629
648
|
class MonitoringApplicationStep(TaskStep):
|
|
630
649
|
"""monitoring application execution step, runs users class code"""
|
|
@@ -755,7 +774,7 @@ class RouterStep(TaskStep):
|
|
|
755
774
|
creation_strategy: schemas.ModelEndpointCreationStrategy = schemas.ModelEndpointCreationStrategy.INPLACE,
|
|
756
775
|
**class_args,
|
|
757
776
|
):
|
|
758
|
-
"""add child route step or class to the router
|
|
777
|
+
"""add child route step or class to the router, if key exists it will be updated
|
|
759
778
|
|
|
760
779
|
:param key: unique name (and route path) for the child step
|
|
761
780
|
:param route: child step object (Task, ..)
|
|
@@ -775,7 +794,13 @@ class RouterStep(TaskStep):
|
|
|
775
794
|
2. Create a new model endpoint with the same name and set it to `latest`.
|
|
776
795
|
|
|
777
796
|
"""
|
|
778
|
-
|
|
797
|
+
if len(self.routes.keys()) >= MAX_MODELS_PER_ROUTER and key not in self.routes:
|
|
798
|
+
raise mlrun.errors.MLRunModelLimitExceededError(
|
|
799
|
+
f"Router cannot support more than {MAX_MODELS_PER_ROUTER} model endpoints. "
|
|
800
|
+
f"To add a new route, edit an existing one by passing the same key."
|
|
801
|
+
)
|
|
802
|
+
if key in self.routes:
|
|
803
|
+
logger.info(f"Model {key} already exists, updating it.")
|
|
779
804
|
if not route and not class_name and not handler:
|
|
780
805
|
raise MLRunInvalidArgumentError("route or class_name must be specified")
|
|
781
806
|
if not route:
|
|
@@ -790,10 +815,6 @@ class RouterStep(TaskStep):
|
|
|
790
815
|
)
|
|
791
816
|
route.function = function or route.function
|
|
792
817
|
|
|
793
|
-
if len(self._routes) >= MAX_ALLOWED_STEPS:
|
|
794
|
-
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
795
|
-
f"Cannot create the serving graph: the maximum number of steps is {MAX_ALLOWED_STEPS}"
|
|
796
|
-
)
|
|
797
818
|
route = self._routes.update(key, route)
|
|
798
819
|
route.set_parent(self)
|
|
799
820
|
return route
|
|
@@ -806,6 +827,10 @@ class RouterStep(TaskStep):
|
|
|
806
827
|
del self._routes[key]
|
|
807
828
|
|
|
808
829
|
def init_object(self, context, namespace, mode="sync", reset=False, **extra_kwargs):
|
|
830
|
+
if not self.routes:
|
|
831
|
+
raise mlrun.errors.MLRunRuntimeError(
|
|
832
|
+
"You have to add models to the router step before initializing it"
|
|
833
|
+
)
|
|
809
834
|
if not self._is_local_function(context):
|
|
810
835
|
return
|
|
811
836
|
|
|
@@ -412,8 +412,17 @@ class NotificationPusher(_NotificationPusherBase):
|
|
|
412
412
|
sent_time: typing.Optional[datetime.datetime] = None,
|
|
413
413
|
reason: typing.Optional[str] = None,
|
|
414
414
|
):
|
|
415
|
-
if
|
|
416
|
-
|
|
415
|
+
# Skip update the notification state if the following conditions are met:
|
|
416
|
+
# 1. the run is not in a terminal state
|
|
417
|
+
# 2. the when contains only one state (which is the current state)
|
|
418
|
+
# Skip updating because currently each notification has only one row in the db, even if it has multiple when.
|
|
419
|
+
# This means that if the notification is updated to sent for running state for example, it will not send for
|
|
420
|
+
# The terminal state
|
|
421
|
+
# TODO: Change this behavior after implementing ML-8723
|
|
422
|
+
if (
|
|
423
|
+
run_state not in runtimes_constants.RunStates.terminal_states()
|
|
424
|
+
and len(notification.when) > 1
|
|
425
|
+
):
|
|
417
426
|
logger.debug(
|
|
418
427
|
"Skip updating notification status - run not in terminal state",
|
|
419
428
|
run_uid=run_uid,
|
mlrun/utils/version/version.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: mlrun
|
|
3
|
-
Version: 1.8.
|
|
3
|
+
Version: 1.8.0rc24
|
|
4
4
|
Summary: Tracking and config of machine learning runs
|
|
5
5
|
Home-page: https://github.com/mlrun/mlrun
|
|
6
6
|
Author: Yaron Haviv
|
|
@@ -225,6 +225,18 @@ Requires-Dist: taos-ws-py==0.3.2; extra == "complete-api"
|
|
|
225
225
|
Requires-Dist: taoswswrap~=0.3.0; extra == "complete-api"
|
|
226
226
|
Requires-Dist: timelength~=1.1; extra == "complete-api"
|
|
227
227
|
Requires-Dist: uvicorn~=0.32.1; extra == "complete-api"
|
|
228
|
+
Dynamic: author
|
|
229
|
+
Dynamic: author-email
|
|
230
|
+
Dynamic: classifier
|
|
231
|
+
Dynamic: description
|
|
232
|
+
Dynamic: description-content-type
|
|
233
|
+
Dynamic: home-page
|
|
234
|
+
Dynamic: keywords
|
|
235
|
+
Dynamic: license
|
|
236
|
+
Dynamic: provides-extra
|
|
237
|
+
Dynamic: requires-dist
|
|
238
|
+
Dynamic: requires-python
|
|
239
|
+
Dynamic: summary
|
|
228
240
|
|
|
229
241
|
<a id="top"></a>
|
|
230
242
|
[](https://github.com/mlrun/mlrun/actions/workflows/build.yaml?query=branch%3Adevelopment)
|