mlrun 1.10.0rc18__py3-none-any.whl → 1.10.0rc20__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 +21 -2
- mlrun/common/constants.py +1 -0
- mlrun/common/schemas/function.py +10 -0
- mlrun/common/schemas/model_monitoring/constants.py +4 -11
- mlrun/common/schemas/model_monitoring/model_endpoints.py +2 -0
- mlrun/datastore/__init__.py +9 -1
- mlrun/datastore/model_provider/huggingface_provider.py +114 -26
- mlrun/datastore/model_provider/model_provider.py +144 -70
- mlrun/datastore/model_provider/openai_provider.py +95 -37
- mlrun/db/base.py +0 -19
- mlrun/db/httpdb.py +10 -46
- mlrun/db/nopdb.py +0 -10
- mlrun/launcher/base.py +13 -6
- mlrun/model_monitoring/api.py +43 -22
- mlrun/model_monitoring/applications/base.py +1 -1
- mlrun/model_monitoring/controller.py +112 -38
- mlrun/model_monitoring/db/_schedules.py +13 -9
- mlrun/model_monitoring/stream_processing.py +16 -12
- mlrun/platforms/__init__.py +3 -2
- mlrun/projects/project.py +2 -2
- mlrun/run.py +1 -1
- mlrun/runtimes/base.py +5 -2
- mlrun/runtimes/daskjob.py +1 -0
- mlrun/runtimes/nuclio/application/application.py +84 -5
- mlrun/runtimes/nuclio/function.py +3 -1
- mlrun/serving/server.py +24 -0
- mlrun/serving/states.py +80 -30
- mlrun/serving/system_steps.py +60 -36
- mlrun/utils/helpers.py +37 -13
- mlrun/utils/notifications/notification_pusher.py +1 -1
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.10.0rc18.dist-info → mlrun-1.10.0rc20.dist-info}/METADATA +4 -4
- {mlrun-1.10.0rc18.dist-info → mlrun-1.10.0rc20.dist-info}/RECORD +37 -38
- mlrun/api/schemas/__init__.py +0 -259
- {mlrun-1.10.0rc18.dist-info → mlrun-1.10.0rc20.dist-info}/WHEEL +0 -0
- {mlrun-1.10.0rc18.dist-info → mlrun-1.10.0rc20.dist-info}/entry_points.txt +0 -0
- {mlrun-1.10.0rc18.dist-info → mlrun-1.10.0rc20.dist-info}/licenses/LICENSE +0 -0
- {mlrun-1.10.0rc18.dist-info → mlrun-1.10.0rc20.dist-info}/top_level.txt +0 -0
|
@@ -11,33 +11,37 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
-
|
|
14
|
+
import collections
|
|
15
15
|
import concurrent.futures
|
|
16
16
|
import datetime
|
|
17
17
|
import json
|
|
18
18
|
import os
|
|
19
19
|
import traceback
|
|
20
|
+
import warnings
|
|
20
21
|
from collections.abc import Iterator
|
|
21
22
|
from contextlib import AbstractContextManager
|
|
22
23
|
from types import TracebackType
|
|
23
|
-
from typing import Any, NamedTuple, Optional, Union, cast
|
|
24
|
+
from typing import Any, Final, NamedTuple, Optional, Union, cast
|
|
24
25
|
|
|
25
26
|
import nuclio_sdk
|
|
27
|
+
import numpy as np
|
|
26
28
|
import pandas as pd
|
|
27
29
|
|
|
28
30
|
import mlrun
|
|
29
31
|
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
32
|
+
import mlrun.feature_store as fstore
|
|
30
33
|
import mlrun.model_monitoring
|
|
31
34
|
import mlrun.model_monitoring.db._schedules as schedules
|
|
32
35
|
import mlrun.model_monitoring.helpers
|
|
33
36
|
import mlrun.platforms.iguazio
|
|
37
|
+
from mlrun.common.schemas import EndpointType
|
|
34
38
|
from mlrun.common.schemas.model_monitoring.constants import (
|
|
35
39
|
ControllerEvent,
|
|
36
40
|
ControllerEventEndpointPolicy,
|
|
37
41
|
)
|
|
38
42
|
from mlrun.errors import err_to_str
|
|
39
43
|
from mlrun.model_monitoring.helpers import batch_dict2timedelta
|
|
40
|
-
from mlrun.utils import logger
|
|
44
|
+
from mlrun.utils import datetime_now, logger
|
|
41
45
|
|
|
42
46
|
_SECONDS_IN_DAY = int(datetime.timedelta(days=1).total_seconds())
|
|
43
47
|
_SECONDS_IN_MINUTE = 60
|
|
@@ -49,14 +53,16 @@ class _Interval(NamedTuple):
|
|
|
49
53
|
|
|
50
54
|
|
|
51
55
|
class _BatchWindow:
|
|
56
|
+
TIMESTAMP_RESOLUTION_MICRO: Final = 1e-6 # 0.000001 seconds or 1 microsecond
|
|
57
|
+
|
|
52
58
|
def __init__(
|
|
53
59
|
self,
|
|
54
60
|
*,
|
|
55
61
|
schedules_file: schedules.ModelMonitoringSchedulesFileEndpoint,
|
|
56
62
|
application: str,
|
|
57
63
|
timedelta_seconds: int,
|
|
58
|
-
last_updated:
|
|
59
|
-
first_request:
|
|
64
|
+
last_updated: float,
|
|
65
|
+
first_request: float,
|
|
60
66
|
endpoint_mode: mm_constants.EndpointMode = mm_constants.EndpointMode.REAL_TIME,
|
|
61
67
|
) -> None:
|
|
62
68
|
"""
|
|
@@ -73,15 +79,17 @@ class _BatchWindow:
|
|
|
73
79
|
self._endpoint_mode = endpoint_mode
|
|
74
80
|
self._start = self._get_last_analyzed()
|
|
75
81
|
|
|
76
|
-
def _get_saved_last_analyzed(
|
|
77
|
-
|
|
82
|
+
def _get_saved_last_analyzed(
|
|
83
|
+
self,
|
|
84
|
+
) -> Optional[float]:
|
|
85
|
+
return self._db.get_application_time(self._application)
|
|
78
86
|
|
|
79
|
-
def _update_last_analyzed(self, last_analyzed:
|
|
87
|
+
def _update_last_analyzed(self, last_analyzed: float) -> None:
|
|
80
88
|
self._db.update_application_time(
|
|
81
89
|
application=self._application, timestamp=last_analyzed
|
|
82
90
|
)
|
|
83
91
|
|
|
84
|
-
def _get_initial_last_analyzed(self) ->
|
|
92
|
+
def _get_initial_last_analyzed(self) -> float:
|
|
85
93
|
if self._endpoint_mode == mm_constants.EndpointMode.BATCH:
|
|
86
94
|
logger.info(
|
|
87
95
|
"No last analyzed time was found for this endpoint and application, as this is "
|
|
@@ -107,7 +115,7 @@ class _BatchWindow:
|
|
|
107
115
|
self._stop - first_period_in_seconds,
|
|
108
116
|
)
|
|
109
117
|
|
|
110
|
-
def _get_last_analyzed(self) ->
|
|
118
|
+
def _get_last_analyzed(self) -> float:
|
|
111
119
|
saved_last_analyzed = self._get_saved_last_analyzed()
|
|
112
120
|
if saved_last_analyzed is not None:
|
|
113
121
|
if self._endpoint_mode == mm_constants.EndpointMode.BATCH:
|
|
@@ -127,13 +135,16 @@ class _BatchWindow:
|
|
|
127
135
|
# Iterate timestamp from start until timestamp <= stop - step
|
|
128
136
|
# so that the last interval will end at (timestamp + step) <= stop.
|
|
129
137
|
# Add 1 to stop - step to get <= and not <.
|
|
130
|
-
for timestamp in
|
|
138
|
+
for timestamp in np.arange(
|
|
139
|
+
self._start, self._stop - self._step + 1, self._step
|
|
140
|
+
):
|
|
131
141
|
entered = True
|
|
132
142
|
start_time = datetime.datetime.fromtimestamp(
|
|
133
143
|
timestamp, tz=datetime.timezone.utc
|
|
134
144
|
)
|
|
135
145
|
end_time = datetime.datetime.fromtimestamp(
|
|
136
|
-
timestamp + self._step,
|
|
146
|
+
timestamp - self.TIMESTAMP_RESOLUTION_MICRO + self._step,
|
|
147
|
+
tz=datetime.timezone.utc,
|
|
137
148
|
)
|
|
138
149
|
yield _Interval(start_time, end_time)
|
|
139
150
|
|
|
@@ -149,7 +160,7 @@ class _BatchWindow:
|
|
|
149
160
|
# If the endpoint is a batch endpoint, we need to update the last analyzed time
|
|
150
161
|
# to the end of the batch time.
|
|
151
162
|
if last_analyzed:
|
|
152
|
-
if last_analyzed < self._stop:
|
|
163
|
+
if last_analyzed - self.TIMESTAMP_RESOLUTION_MICRO < self._stop:
|
|
153
164
|
# If the last analyzed time is earlier than the stop time,
|
|
154
165
|
# yield the final partial interval from last_analyzed to stop
|
|
155
166
|
yield _Interval(
|
|
@@ -223,7 +234,7 @@ class _BatchWindowGenerator(AbstractContextManager):
|
|
|
223
234
|
def get_application_list(self) -> set[str]:
|
|
224
235
|
return self._schedules_file.get_application_list()
|
|
225
236
|
|
|
226
|
-
def get_min_last_analyzed(self) -> Optional[
|
|
237
|
+
def get_min_last_analyzed(self) -> Optional[float]:
|
|
227
238
|
return self._schedules_file.get_min_timestamp()
|
|
228
239
|
|
|
229
240
|
@classmethod
|
|
@@ -231,22 +242,29 @@ class _BatchWindowGenerator(AbstractContextManager):
|
|
|
231
242
|
cls,
|
|
232
243
|
last_request: datetime.datetime,
|
|
233
244
|
endpoint_mode: mm_constants.EndpointMode,
|
|
234
|
-
|
|
245
|
+
not_old_batch_endpoint: bool,
|
|
246
|
+
) -> float:
|
|
235
247
|
"""
|
|
236
248
|
Get the last updated time of a model endpoint.
|
|
237
249
|
"""
|
|
238
250
|
|
|
239
251
|
if endpoint_mode == mm_constants.EndpointMode.REAL_TIME:
|
|
240
|
-
last_updated =
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
float,
|
|
244
|
-
mlrun.mlconf.model_endpoint_monitoring.parquet_batching_timeout_secs,
|
|
245
|
-
)
|
|
252
|
+
last_updated = last_request.timestamp() - cast(
|
|
253
|
+
float,
|
|
254
|
+
mlrun.mlconf.model_endpoint_monitoring.parquet_batching_timeout_secs,
|
|
246
255
|
)
|
|
256
|
+
if not not_old_batch_endpoint:
|
|
257
|
+
# If the endpoint does not have a stream, `last_updated` should be
|
|
258
|
+
# the minimum between the current time and the last updated time.
|
|
259
|
+
# This compensates for the bumping mechanism - see
|
|
260
|
+
# `update_model_endpoint_last_request`.
|
|
261
|
+
last_updated = min(datetime_now().timestamp(), last_updated)
|
|
262
|
+
logger.debug(
|
|
263
|
+
"The endpoint does not have a stream", last_updated=last_updated
|
|
264
|
+
)
|
|
247
265
|
|
|
248
266
|
return last_updated
|
|
249
|
-
return
|
|
267
|
+
return last_request.timestamp()
|
|
250
268
|
|
|
251
269
|
def get_intervals(
|
|
252
270
|
self,
|
|
@@ -255,6 +273,7 @@ class _BatchWindowGenerator(AbstractContextManager):
|
|
|
255
273
|
first_request: datetime.datetime,
|
|
256
274
|
last_request: datetime.datetime,
|
|
257
275
|
endpoint_mode: mm_constants.EndpointMode,
|
|
276
|
+
not_old_batch_endpoint: bool,
|
|
258
277
|
) -> Iterator[_Interval]:
|
|
259
278
|
"""
|
|
260
279
|
Get the batch window for a specific endpoint and application.
|
|
@@ -266,8 +285,10 @@ class _BatchWindowGenerator(AbstractContextManager):
|
|
|
266
285
|
schedules_file=self._schedules_file,
|
|
267
286
|
application=application,
|
|
268
287
|
timedelta_seconds=self._timedelta,
|
|
269
|
-
last_updated=self._get_last_updated_time(
|
|
270
|
-
|
|
288
|
+
last_updated=self._get_last_updated_time(
|
|
289
|
+
last_request, endpoint_mode, not_old_batch_endpoint
|
|
290
|
+
),
|
|
291
|
+
first_request=first_request.timestamp(),
|
|
271
292
|
endpoint_mode=endpoint_mode,
|
|
272
293
|
)
|
|
273
294
|
yield from self.batch_window.get_intervals()
|
|
@@ -291,6 +312,8 @@ class MonitoringApplicationController:
|
|
|
291
312
|
Note that the MonitoringApplicationController object requires access keys along with valid project configurations.
|
|
292
313
|
"""
|
|
293
314
|
|
|
315
|
+
_MAX_FEATURE_SET_PER_WORKER = 1000
|
|
316
|
+
|
|
294
317
|
def __init__(self) -> None:
|
|
295
318
|
"""Initialize Monitoring Application Controller"""
|
|
296
319
|
self.project = cast(str, mlrun.mlconf.active_project)
|
|
@@ -324,6 +347,9 @@ class MonitoringApplicationController:
|
|
|
324
347
|
mlrun.platforms.iguazio.KafkaOutputStream,
|
|
325
348
|
],
|
|
326
349
|
] = {}
|
|
350
|
+
self.feature_sets: collections.OrderedDict[
|
|
351
|
+
str, mlrun.feature_store.FeatureSet
|
|
352
|
+
] = collections.OrderedDict()
|
|
327
353
|
self.tsdb_connector = mlrun.model_monitoring.get_tsdb_connector(
|
|
328
354
|
project=self.project
|
|
329
355
|
)
|
|
@@ -433,15 +459,14 @@ class MonitoringApplicationController:
|
|
|
433
459
|
base_period_minutes, current_min_last_analyzed, current_time
|
|
434
460
|
)
|
|
435
461
|
and (
|
|
436
|
-
|
|
437
|
-
!= last_timestamp_sent
|
|
462
|
+
endpoint.status.last_request.timestamp() != last_timestamp_sent
|
|
438
463
|
or current_min_last_analyzed != last_analyzed_sent
|
|
439
464
|
)
|
|
440
465
|
):
|
|
441
466
|
# Write to schedule chief file the last_request, min_last_analyzed we pushed event to stream
|
|
442
467
|
schedules_file.update_endpoint_timestamps(
|
|
443
468
|
endpoint_uid=endpoint.metadata.uid,
|
|
444
|
-
last_request=
|
|
469
|
+
last_request=endpoint.status.last_request.timestamp(),
|
|
445
470
|
last_analyzed=current_min_last_analyzed,
|
|
446
471
|
)
|
|
447
472
|
return True
|
|
@@ -460,13 +485,14 @@ class MonitoringApplicationController:
|
|
|
460
485
|
last_request=endpoint.status.last_request,
|
|
461
486
|
first_request=endpoint.status.first_request,
|
|
462
487
|
endpoint_type=endpoint.metadata.endpoint_type,
|
|
488
|
+
feature_set_uri=endpoint.spec.monitoring_feature_set_uri,
|
|
463
489
|
)
|
|
464
490
|
return False
|
|
465
491
|
|
|
466
492
|
@staticmethod
|
|
467
493
|
def _should_send_nop_event(
|
|
468
494
|
base_period_minutes: int,
|
|
469
|
-
min_last_analyzed:
|
|
495
|
+
min_last_analyzed: float,
|
|
470
496
|
current_time: datetime.datetime,
|
|
471
497
|
):
|
|
472
498
|
if min_last_analyzed:
|
|
@@ -515,7 +541,7 @@ class MonitoringApplicationController:
|
|
|
515
541
|
try:
|
|
516
542
|
project_name = event[ControllerEvent.PROJECT]
|
|
517
543
|
endpoint_id = event[ControllerEvent.ENDPOINT_ID]
|
|
518
|
-
|
|
544
|
+
not_old_batch_endpoint = True
|
|
519
545
|
if (
|
|
520
546
|
event[ControllerEvent.KIND]
|
|
521
547
|
== mm_constants.ControllerEventKind.BATCH_COMPLETE
|
|
@@ -572,6 +598,10 @@ class MonitoringApplicationController:
|
|
|
572
598
|
|
|
573
599
|
endpoint_mode = mm_constants.EndpointMode.REAL_TIME
|
|
574
600
|
|
|
601
|
+
not_old_batch_endpoint = (
|
|
602
|
+
event[ControllerEvent.ENDPOINT_TYPE] != EndpointType.BATCH_EP
|
|
603
|
+
)
|
|
604
|
+
|
|
575
605
|
logger.info(
|
|
576
606
|
"Starting to analyze", timestamp=last_stream_timestamp.isoformat()
|
|
577
607
|
)
|
|
@@ -590,16 +620,49 @@ class MonitoringApplicationController:
|
|
|
590
620
|
first_request=first_request,
|
|
591
621
|
last_request=last_stream_timestamp,
|
|
592
622
|
endpoint_mode=endpoint_mode,
|
|
623
|
+
not_old_batch_endpoint=not_old_batch_endpoint,
|
|
593
624
|
):
|
|
594
625
|
data_in_window = False
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
626
|
+
if not_old_batch_endpoint:
|
|
627
|
+
# Serving endpoint - get the relevant window data from the TSDB
|
|
628
|
+
prediction_metric = self.tsdb_connector.read_predictions(
|
|
629
|
+
start=start_infer_time,
|
|
630
|
+
end=end_infer_time,
|
|
631
|
+
endpoint_id=endpoint_id,
|
|
632
|
+
)
|
|
633
|
+
if prediction_metric.data:
|
|
634
|
+
data_in_window = True
|
|
635
|
+
else:
|
|
636
|
+
# Old batch endpoint - get the relevant window data from the parquet target
|
|
637
|
+
warnings.warn(
|
|
638
|
+
"Analyzing batch model endpoints with real time processing events is "
|
|
639
|
+
"deprecated in 1.10.0 and will be removed in 1.12.0. "
|
|
640
|
+
"Instead, use job-based serving to invoke and analyze offline batch model"
|
|
641
|
+
"endpoints.",
|
|
642
|
+
# TODO: Remove this in 1.12.0
|
|
643
|
+
FutureWarning,
|
|
644
|
+
)
|
|
645
|
+
|
|
646
|
+
if endpoint_id not in self.feature_sets:
|
|
647
|
+
self.feature_sets[endpoint_id] = fstore.get_feature_set(
|
|
648
|
+
event[ControllerEvent.FEATURE_SET_URI]
|
|
649
|
+
)
|
|
650
|
+
self.feature_sets.move_to_end(endpoint_id, last=False)
|
|
651
|
+
if (
|
|
652
|
+
len(self.feature_sets)
|
|
653
|
+
> self._MAX_FEATURE_SET_PER_WORKER
|
|
654
|
+
):
|
|
655
|
+
self.feature_sets.popitem(last=True)
|
|
656
|
+
m_fs = self.feature_sets.get(endpoint_id)
|
|
657
|
+
|
|
658
|
+
df = m_fs.to_dataframe(
|
|
659
|
+
start_time=start_infer_time,
|
|
660
|
+
end_time=end_infer_time,
|
|
661
|
+
time_column=mm_constants.EventFieldType.TIMESTAMP,
|
|
662
|
+
storage_options=self.storage_options,
|
|
663
|
+
)
|
|
664
|
+
if len(df) > 0:
|
|
665
|
+
data_in_window = True
|
|
603
666
|
|
|
604
667
|
if not data_in_window:
|
|
605
668
|
logger.info(
|
|
@@ -616,7 +679,10 @@ class MonitoringApplicationController:
|
|
|
616
679
|
endpoint_id=endpoint_id,
|
|
617
680
|
)
|
|
618
681
|
self._push_to_applications(
|
|
619
|
-
start_infer_time=start_infer_time
|
|
682
|
+
start_infer_time=start_infer_time
|
|
683
|
+
- datetime.timedelta(
|
|
684
|
+
batch_window_generator.batch_window.TIMESTAMP_RESOLUTION_MICRO
|
|
685
|
+
), # We subtract a microsecond to ensure that the apps will retrieve start time data.
|
|
620
686
|
end_infer_time=end_infer_time,
|
|
621
687
|
endpoint_id=endpoint_id,
|
|
622
688
|
endpoint_name=endpoint_name,
|
|
@@ -653,6 +719,9 @@ class MonitoringApplicationController:
|
|
|
653
719
|
ControllerEvent.ENDPOINT_TYPE: event[
|
|
654
720
|
ControllerEvent.ENDPOINT_TYPE
|
|
655
721
|
],
|
|
722
|
+
ControllerEvent.FEATURE_SET_URI: event[
|
|
723
|
+
ControllerEvent.FEATURE_SET_URI
|
|
724
|
+
],
|
|
656
725
|
ControllerEvent.FIRST_REQUEST: event[
|
|
657
726
|
ControllerEvent.FIRST_REQUEST
|
|
658
727
|
],
|
|
@@ -842,6 +911,7 @@ class MonitoringApplicationController:
|
|
|
842
911
|
sep=" ", timespec="microseconds"
|
|
843
912
|
),
|
|
844
913
|
endpoint_type=endpoint.metadata.endpoint_type,
|
|
914
|
+
feature_set_uri=endpoint.spec.monitoring_feature_set_uri,
|
|
845
915
|
endpoint_policy=json.dumps(policy),
|
|
846
916
|
)
|
|
847
917
|
policy[ControllerEventEndpointPolicy.ENDPOINT_UPDATED] = (
|
|
@@ -859,6 +929,7 @@ class MonitoringApplicationController:
|
|
|
859
929
|
sep=" ", timespec="microseconds"
|
|
860
930
|
),
|
|
861
931
|
endpoint_type=endpoint.metadata.endpoint_type.value,
|
|
932
|
+
feature_set_uri=endpoint.spec.monitoring_feature_set_uri,
|
|
862
933
|
endpoint_policy=policy,
|
|
863
934
|
)
|
|
864
935
|
|
|
@@ -871,6 +942,7 @@ class MonitoringApplicationController:
|
|
|
871
942
|
timestamp: str,
|
|
872
943
|
first_request: str,
|
|
873
944
|
endpoint_type: int,
|
|
945
|
+
feature_set_uri: str,
|
|
874
946
|
endpoint_policy: dict[str, Any],
|
|
875
947
|
) -> None:
|
|
876
948
|
"""
|
|
@@ -883,6 +955,7 @@ class MonitoringApplicationController:
|
|
|
883
955
|
:param endpoint_id: endpoint id string
|
|
884
956
|
:param endpoint_name: the endpoint name string
|
|
885
957
|
:param endpoint_type: Enum of the endpoint type
|
|
958
|
+
:param feature_set_uri: the feature set uri string
|
|
886
959
|
"""
|
|
887
960
|
event = {
|
|
888
961
|
ControllerEvent.KIND.value: kind,
|
|
@@ -892,6 +965,7 @@ class MonitoringApplicationController:
|
|
|
892
965
|
ControllerEvent.TIMESTAMP.value: timestamp,
|
|
893
966
|
ControllerEvent.FIRST_REQUEST.value: first_request,
|
|
894
967
|
ControllerEvent.ENDPOINT_TYPE.value: endpoint_type,
|
|
968
|
+
ControllerEvent.FEATURE_SET_URI.value: feature_set_uri,
|
|
895
969
|
ControllerEvent.ENDPOINT_POLICY.value: endpoint_policy,
|
|
896
970
|
}
|
|
897
971
|
logger.info(
|
|
@@ -162,19 +162,19 @@ class ModelMonitoringSchedulesFileEndpoint(ModelMonitoringSchedulesFileBase):
|
|
|
162
162
|
endpoint_id=model_endpoint.metadata.uid,
|
|
163
163
|
)
|
|
164
164
|
|
|
165
|
-
def get_application_time(self, application: str) -> Optional[
|
|
165
|
+
def get_application_time(self, application: str) -> Optional[float]:
|
|
166
166
|
self._check_open_schedules()
|
|
167
167
|
return self._schedules.get(application)
|
|
168
168
|
|
|
169
|
-
def update_application_time(self, application: str, timestamp:
|
|
169
|
+
def update_application_time(self, application: str, timestamp: float) -> None:
|
|
170
170
|
self._check_open_schedules()
|
|
171
|
-
self._schedules[application] = timestamp
|
|
171
|
+
self._schedules[application] = float(timestamp)
|
|
172
172
|
|
|
173
173
|
def get_application_list(self) -> set[str]:
|
|
174
174
|
self._check_open_schedules()
|
|
175
175
|
return set(self._schedules.keys())
|
|
176
176
|
|
|
177
|
-
def get_min_timestamp(self) -> Optional[
|
|
177
|
+
def get_min_timestamp(self) -> Optional[float]:
|
|
178
178
|
self._check_open_schedules()
|
|
179
179
|
return min(self._schedules.values(), default=None)
|
|
180
180
|
|
|
@@ -198,7 +198,7 @@ class ModelMonitoringSchedulesFileChief(ModelMonitoringSchedulesFileBase):
|
|
|
198
198
|
project=self._project
|
|
199
199
|
)
|
|
200
200
|
|
|
201
|
-
def get_endpoint_last_request(self, endpoint_uid: str) -> Optional[
|
|
201
|
+
def get_endpoint_last_request(self, endpoint_uid: str) -> Optional[float]:
|
|
202
202
|
self._check_open_schedules()
|
|
203
203
|
if endpoint_uid in self._schedules:
|
|
204
204
|
return self._schedules[endpoint_uid].get(
|
|
@@ -208,15 +208,19 @@ class ModelMonitoringSchedulesFileChief(ModelMonitoringSchedulesFileBase):
|
|
|
208
208
|
return None
|
|
209
209
|
|
|
210
210
|
def update_endpoint_timestamps(
|
|
211
|
-
self, endpoint_uid: str, last_request:
|
|
211
|
+
self, endpoint_uid: str, last_request: float, last_analyzed: float
|
|
212
212
|
) -> None:
|
|
213
213
|
self._check_open_schedules()
|
|
214
214
|
self._schedules[endpoint_uid] = {
|
|
215
|
-
schemas.model_monitoring.constants.ScheduleChiefFields.LAST_REQUEST:
|
|
216
|
-
|
|
215
|
+
schemas.model_monitoring.constants.ScheduleChiefFields.LAST_REQUEST: float(
|
|
216
|
+
last_request
|
|
217
|
+
),
|
|
218
|
+
schemas.model_monitoring.constants.ScheduleChiefFields.LAST_ANALYZED: float(
|
|
219
|
+
last_analyzed
|
|
220
|
+
),
|
|
217
221
|
}
|
|
218
222
|
|
|
219
|
-
def get_endpoint_last_analyzed(self, endpoint_uid: str) -> Optional[
|
|
223
|
+
def get_endpoint_last_analyzed(self, endpoint_uid: str) -> Optional[float]:
|
|
220
224
|
self._check_open_schedules()
|
|
221
225
|
if endpoint_uid in self._schedules:
|
|
222
226
|
return self._schedules[endpoint_uid].get(
|
|
@@ -396,6 +396,8 @@ class ProcessEndpointEvent(mlrun.feature_store.steps.MapClass):
|
|
|
396
396
|
request_id = event.get("request", {}).get("id") or event.get("resp", {}).get(
|
|
397
397
|
"id"
|
|
398
398
|
)
|
|
399
|
+
feature_names = event.get("request", {}).get("input_schema")
|
|
400
|
+
labels_names = event.get("resp", {}).get("output_schema")
|
|
399
401
|
latency = event.get("microsec")
|
|
400
402
|
features = event.get("request", {}).get("inputs")
|
|
401
403
|
predictions = event.get("resp", {}).get("outputs")
|
|
@@ -496,6 +498,8 @@ class ProcessEndpointEvent(mlrun.feature_store.steps.MapClass):
|
|
|
496
498
|
),
|
|
497
499
|
EventFieldType.EFFECTIVE_SAMPLE_COUNT: effective_sample_count,
|
|
498
500
|
EventFieldType.ESTIMATED_PREDICTION_COUNT: estimated_prediction_count,
|
|
501
|
+
EventFieldType.FEATURE_NAMES: feature_names,
|
|
502
|
+
EventFieldType.LABEL_NAMES: labels_names,
|
|
499
503
|
}
|
|
500
504
|
)
|
|
501
505
|
|
|
@@ -602,19 +606,19 @@ class MapFeatureNames(mlrun.feature_store.steps.MapClass):
|
|
|
602
606
|
self.endpoint_type = {}
|
|
603
607
|
|
|
604
608
|
def _infer_feature_names_from_data(self, event):
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
609
|
+
endpoint_id = event[EventFieldType.ENDPOINT_ID]
|
|
610
|
+
if endpoint_id in self.feature_names and len(
|
|
611
|
+
self.feature_names[endpoint_id]
|
|
612
|
+
) >= len(event[EventFieldType.FEATURES]):
|
|
613
|
+
return self.feature_names[endpoint_id]
|
|
610
614
|
return None
|
|
611
615
|
|
|
612
616
|
def _infer_label_columns_from_data(self, event):
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
617
|
+
endpoint_id = event[EventFieldType.ENDPOINT_ID]
|
|
618
|
+
if endpoint_id in self.label_columns and len(
|
|
619
|
+
self.label_columns[endpoint_id]
|
|
620
|
+
) >= len(event[EventFieldType.PREDICTION]):
|
|
621
|
+
return self.label_columns[endpoint_id]
|
|
618
622
|
return None
|
|
619
623
|
|
|
620
624
|
def do(self, event: dict):
|
|
@@ -659,7 +663,7 @@ class MapFeatureNames(mlrun.feature_store.steps.MapClass):
|
|
|
659
663
|
"Feature names are not initialized, they will be automatically generated",
|
|
660
664
|
endpoint_id=endpoint_id,
|
|
661
665
|
)
|
|
662
|
-
feature_names = [
|
|
666
|
+
feature_names = event.get(EventFieldType.FEATURE_NAMES) or [
|
|
663
667
|
f"f{i}" for i, _ in enumerate(event[EventFieldType.FEATURES])
|
|
664
668
|
]
|
|
665
669
|
|
|
@@ -682,7 +686,7 @@ class MapFeatureNames(mlrun.feature_store.steps.MapClass):
|
|
|
682
686
|
"label column names are not initialized, they will be automatically generated",
|
|
683
687
|
endpoint_id=endpoint_id,
|
|
684
688
|
)
|
|
685
|
-
label_columns = [
|
|
689
|
+
label_columns = event.get(EventFieldType.LABEL_NAMES) or [
|
|
686
690
|
f"p{i}" for i, _ in enumerate(event[EventFieldType.PREDICTION])
|
|
687
691
|
]
|
|
688
692
|
attributes_to_update[EventFieldType.LABEL_NAMES] = label_columns
|
mlrun/platforms/__init__.py
CHANGED
|
@@ -25,6 +25,7 @@ from .iguazio import (
|
|
|
25
25
|
)
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
# TODO: Remove in 1.11.0
|
|
28
29
|
class _DeprecationHelper:
|
|
29
30
|
"""A helper class to deprecate old schemas"""
|
|
30
31
|
|
|
@@ -48,12 +49,12 @@ class _DeprecationHelper:
|
|
|
48
49
|
def _warn(self):
|
|
49
50
|
warnings.warn(
|
|
50
51
|
f"mlrun.platforms.{self._new_target} is deprecated since version {self._version}, "
|
|
51
|
-
f"and will be removed in 1.
|
|
52
|
+
f"and will be removed in 1.11.0. Use mlrun.runtimes.mounts.{self._new_target} instead.",
|
|
52
53
|
FutureWarning,
|
|
53
54
|
)
|
|
54
55
|
|
|
55
56
|
|
|
56
|
-
# TODO: Remove in 1.
|
|
57
|
+
# TODO: Remove in 1.11.0
|
|
57
58
|
# For backwards compatibility
|
|
58
59
|
VolumeMount = _DeprecationHelper("VolumeMount")
|
|
59
60
|
auto_mount = _DeprecationHelper("auto_mount")
|
mlrun/projects/project.py
CHANGED
|
@@ -3939,8 +3939,8 @@ class MlrunProject(ModelObj):
|
|
|
3939
3939
|
:param start: The start time to filter by.Corresponding to the `created` field.
|
|
3940
3940
|
:param end: The end time to filter by. Corresponding to the `created` field.
|
|
3941
3941
|
:param top_level: If true will return only routers and endpoint that are NOT children of any router.
|
|
3942
|
-
:param mode: Specifies the mode of the model endpoint. Can be "real-time", "batch", or
|
|
3943
|
-
to None.
|
|
3942
|
+
:param mode: Specifies the mode of the model endpoint. Can be "real-time" (0), "batch" (1), or
|
|
3943
|
+
both if set to None.
|
|
3944
3944
|
:param uids: If passed will return a list `ModelEndpoint` object with uid in uids.
|
|
3945
3945
|
:param tsdb_metrics: When True, the time series metrics will be added to the output
|
|
3946
3946
|
of the resulting.
|
mlrun/run.py
CHANGED
|
@@ -365,7 +365,7 @@ def import_function(url="", secrets=None, db="", project=None, new_name=None):
|
|
|
365
365
|
def import_function_to_dict(url, secrets=None):
|
|
366
366
|
"""Load function spec from local/remote YAML file"""
|
|
367
367
|
obj = get_object(url, secrets)
|
|
368
|
-
runtime = yaml.
|
|
368
|
+
runtime = yaml.safe_load(obj)
|
|
369
369
|
remote = "://" in url
|
|
370
370
|
|
|
371
371
|
code = get_in(runtime, "spec.build.functionSourceCode")
|
mlrun/runtimes/base.py
CHANGED
|
@@ -447,14 +447,17 @@ class BaseRuntime(ModelObj):
|
|
|
447
447
|
:return: Dictionary with all the variables that could be parsed
|
|
448
448
|
"""
|
|
449
449
|
runtime_env = {
|
|
450
|
-
|
|
450
|
+
mlrun_constants.MLRUN_ACTIVE_PROJECT: self.metadata.project
|
|
451
|
+
or config.active_project
|
|
451
452
|
}
|
|
452
453
|
if runobj:
|
|
453
454
|
runtime_env["MLRUN_EXEC_CONFIG"] = runobj.to_json(
|
|
454
455
|
exclude_notifications_params=True
|
|
455
456
|
)
|
|
456
457
|
if runobj.metadata.project:
|
|
457
|
-
runtime_env[
|
|
458
|
+
runtime_env[mlrun_constants.MLRUN_ACTIVE_PROJECT] = (
|
|
459
|
+
runobj.metadata.project
|
|
460
|
+
)
|
|
458
461
|
if runobj.spec.verbose:
|
|
459
462
|
runtime_env["MLRUN_LOG_LEVEL"] = "DEBUG"
|
|
460
463
|
if config.httpdb.api_url:
|