mlrun 1.10.0rc16__py3-none-any.whl → 1.10.0rc18__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.
- mlrun/common/constants.py +2 -0
- mlrun/common/formatters/artifact.py +1 -0
- mlrun/common/schemas/__init__.py +1 -0
- mlrun/common/schemas/model_monitoring/__init__.py +1 -0
- mlrun/common/schemas/model_monitoring/constants.py +33 -6
- mlrun/common/schemas/serving.py +3 -0
- mlrun/common/schemas/workflow.py +1 -0
- mlrun/config.py +15 -5
- mlrun/datastore/datastore.py +4 -4
- mlrun/datastore/datastore_profile.py +26 -0
- mlrun/datastore/model_provider/huggingface_provider.py +183 -0
- mlrun/datastore/model_provider/model_provider.py +6 -1
- mlrun/datastore/model_provider/openai_provider.py +24 -12
- mlrun/datastore/utils.py +6 -0
- mlrun/db/base.py +1 -0
- mlrun/db/httpdb.py +4 -0
- mlrun/model_monitoring/api.py +5 -3
- mlrun/model_monitoring/applications/base.py +107 -28
- mlrun/model_monitoring/applications/results.py +4 -7
- mlrun/model_monitoring/controller.py +175 -121
- mlrun/model_monitoring/stream_processing.py +29 -2
- mlrun/projects/project.py +7 -2
- mlrun/run.py +3 -1
- mlrun/serving/server.py +98 -11
- mlrun/serving/states.py +8 -19
- mlrun/serving/system_steps.py +20 -10
- mlrun/utils/helpers.py +6 -1
- mlrun/utils/logger.py +3 -1
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc18.dist-info}/METADATA +2 -2
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc18.dist-info}/RECORD +35 -34
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc18.dist-info}/WHEEL +0 -0
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc18.dist-info}/entry_points.txt +0 -0
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc18.dist-info}/licenses/LICENSE +0 -0
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc18.dist-info}/top_level.txt +0 -0
|
@@ -25,6 +25,7 @@ import pandas as pd
|
|
|
25
25
|
|
|
26
26
|
import mlrun
|
|
27
27
|
import mlrun.common.constants as mlrun_constants
|
|
28
|
+
import mlrun.common.helpers
|
|
28
29
|
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
29
30
|
import mlrun.datastore.datastore_profile as ds_profile
|
|
30
31
|
import mlrun.errors
|
|
@@ -33,6 +34,7 @@ import mlrun.model_monitoring.applications.context as mm_context
|
|
|
33
34
|
import mlrun.model_monitoring.applications.results as mm_results
|
|
34
35
|
import mlrun.model_monitoring.db._schedules as mm_schedules
|
|
35
36
|
import mlrun.model_monitoring.helpers as mm_helpers
|
|
37
|
+
import mlrun.utils
|
|
36
38
|
from mlrun.serving.utils import MonitoringApplicationToDict
|
|
37
39
|
from mlrun.utils import logger
|
|
38
40
|
|
|
@@ -194,7 +196,25 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
194
196
|
Optional[mm_schedules.ModelMonitoringSchedulesFileApplication],
|
|
195
197
|
]
|
|
196
198
|
]:
|
|
197
|
-
endpoints_output: dict[
|
|
199
|
+
endpoints_output: dict[
|
|
200
|
+
str,
|
|
201
|
+
list[
|
|
202
|
+
tuple[
|
|
203
|
+
mm_context.MonitoringApplicationContext,
|
|
204
|
+
Union[
|
|
205
|
+
mm_results.ModelMonitoringApplicationResult,
|
|
206
|
+
mm_results.ModelMonitoringApplicationMetric,
|
|
207
|
+
list[
|
|
208
|
+
Union[
|
|
209
|
+
mm_results.ModelMonitoringApplicationResult,
|
|
210
|
+
mm_results.ModelMonitoringApplicationMetric,
|
|
211
|
+
mm_results._ModelMonitoringApplicationStats,
|
|
212
|
+
]
|
|
213
|
+
],
|
|
214
|
+
],
|
|
215
|
+
]
|
|
216
|
+
],
|
|
217
|
+
] = defaultdict(list)
|
|
198
218
|
application_schedules = nullcontext()
|
|
199
219
|
if write_output:
|
|
200
220
|
cls._check_writer_is_up(project)
|
|
@@ -220,11 +240,21 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
220
240
|
profile=stream_profile,
|
|
221
241
|
)
|
|
222
242
|
for endpoint_id, outputs in endpoints_output.items():
|
|
243
|
+
writer_events = []
|
|
244
|
+
for ctx, res in outputs:
|
|
245
|
+
if isinstance(res, list):
|
|
246
|
+
writer_events.extend(
|
|
247
|
+
_serialize_context_and_result(
|
|
248
|
+
context=ctx, result=sub_res
|
|
249
|
+
)
|
|
250
|
+
for sub_res in res
|
|
251
|
+
)
|
|
252
|
+
else:
|
|
253
|
+
writer_events.append(
|
|
254
|
+
_serialize_context_and_result(context=ctx, result=res)
|
|
255
|
+
)
|
|
223
256
|
writer_stream.push(
|
|
224
|
-
|
|
225
|
-
_serialize_context_and_result(context=ctx, result=res)
|
|
226
|
-
for ctx, res in outputs
|
|
227
|
-
],
|
|
257
|
+
writer_events,
|
|
228
258
|
partition_key=endpoint_id,
|
|
229
259
|
)
|
|
230
260
|
logger.debug(
|
|
@@ -238,6 +268,14 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
238
268
|
)
|
|
239
269
|
application_schedules.__exit__(None, None, None)
|
|
240
270
|
|
|
271
|
+
@classmethod
|
|
272
|
+
def _get_application_name(cls, context: "mlrun.MLClientCtx") -> str:
|
|
273
|
+
"""Get the application name from the context via the function URI"""
|
|
274
|
+
_, application_name, _, _ = mlrun.common.helpers.parse_versioned_object_uri(
|
|
275
|
+
context.to_dict().get("spec", {}).get("function", "")
|
|
276
|
+
)
|
|
277
|
+
return application_name
|
|
278
|
+
|
|
241
279
|
def _handler(
|
|
242
280
|
self,
|
|
243
281
|
context: "mlrun.MLClientCtx",
|
|
@@ -250,7 +288,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
250
288
|
end: Optional[str] = None,
|
|
251
289
|
base_period: Optional[int] = None,
|
|
252
290
|
write_output: bool = False,
|
|
253
|
-
|
|
291
|
+
fail_on_overlap: bool = True,
|
|
254
292
|
stream_profile: Optional[ds_profile.DatastoreProfile] = None,
|
|
255
293
|
):
|
|
256
294
|
"""
|
|
@@ -271,7 +309,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
271
309
|
"working with endpoints, without any custom data-frame input"
|
|
272
310
|
)
|
|
273
311
|
|
|
274
|
-
application_name = self.
|
|
312
|
+
application_name = self._get_application_name(context)
|
|
275
313
|
|
|
276
314
|
feature_stats = (
|
|
277
315
|
mm_api.get_sample_set_statistics(reference_data)
|
|
@@ -320,7 +358,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
320
358
|
application_schedules=application_schedules,
|
|
321
359
|
endpoint_id=endpoint_id,
|
|
322
360
|
application_name=application_name,
|
|
323
|
-
|
|
361
|
+
fail_on_overlap=fail_on_overlap,
|
|
324
362
|
):
|
|
325
363
|
result = call_do_tracking(
|
|
326
364
|
event={
|
|
@@ -443,7 +481,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
443
481
|
end_dt: datetime,
|
|
444
482
|
base_period: Optional[int],
|
|
445
483
|
application_name: str,
|
|
446
|
-
|
|
484
|
+
fail_on_overlap: bool,
|
|
447
485
|
) -> datetime:
|
|
448
486
|
"""Make sure that the (app, endpoint) pair doesn't write output before the last analyzed window"""
|
|
449
487
|
if application_schedules:
|
|
@@ -452,7 +490,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
452
490
|
)
|
|
453
491
|
if last_analyzed:
|
|
454
492
|
if start_dt < last_analyzed:
|
|
455
|
-
if
|
|
493
|
+
if not fail_on_overlap:
|
|
456
494
|
if last_analyzed < end_dt and base_period is None:
|
|
457
495
|
logger.warn(
|
|
458
496
|
"Setting the start time to last_analyzed since the original start time precedes "
|
|
@@ -499,7 +537,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
499
537
|
],
|
|
500
538
|
endpoint_id: str,
|
|
501
539
|
application_name: str,
|
|
502
|
-
|
|
540
|
+
fail_on_overlap: bool,
|
|
503
541
|
) -> Iterator[tuple[Optional[datetime], Optional[datetime]]]:
|
|
504
542
|
if start is None or end is None:
|
|
505
543
|
# A single window based on the `sample_data` input - see `_handler`.
|
|
@@ -516,7 +554,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
516
554
|
end_dt=end_dt,
|
|
517
555
|
base_period=base_period,
|
|
518
556
|
application_name=application_name,
|
|
519
|
-
|
|
557
|
+
fail_on_overlap=fail_on_overlap,
|
|
520
558
|
)
|
|
521
559
|
|
|
522
560
|
if base_period is None:
|
|
@@ -589,6 +627,42 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
589
627
|
"""
|
|
590
628
|
return f"{handler_to_class}::{cls._handler.__name__}"
|
|
591
629
|
|
|
630
|
+
@classmethod
|
|
631
|
+
def _determine_job_name(
|
|
632
|
+
cls,
|
|
633
|
+
*,
|
|
634
|
+
func_name: Optional[str],
|
|
635
|
+
class_handler: Optional[str],
|
|
636
|
+
handler_to_class: str,
|
|
637
|
+
) -> str:
|
|
638
|
+
"""
|
|
639
|
+
Determine the batch app's job name. This name is used also as the application name,
|
|
640
|
+
which is retrieved in `_get_application_name`.
|
|
641
|
+
"""
|
|
642
|
+
if func_name:
|
|
643
|
+
job_name = func_name
|
|
644
|
+
else:
|
|
645
|
+
if not class_handler:
|
|
646
|
+
class_name = cls.__name__
|
|
647
|
+
else:
|
|
648
|
+
class_name = handler_to_class.split(".")[-1].split("::")[0]
|
|
649
|
+
|
|
650
|
+
job_name = mlrun.utils.normalize_name(class_name, verbose=False)
|
|
651
|
+
|
|
652
|
+
if not mm_constants.APP_NAME_REGEX.fullmatch(job_name):
|
|
653
|
+
raise mlrun.errors.MLRunValueError(
|
|
654
|
+
"The function name does not comply with the required pattern "
|
|
655
|
+
f"`{mm_constants.APP_NAME_REGEX.pattern}`. "
|
|
656
|
+
"Please choose another `func_name`."
|
|
657
|
+
)
|
|
658
|
+
if not job_name.endswith(mm_constants._RESERVED_EVALUATE_FUNCTION_SUFFIX):
|
|
659
|
+
job_name += mm_constants._RESERVED_EVALUATE_FUNCTION_SUFFIX
|
|
660
|
+
mlrun.utils.logger.info(
|
|
661
|
+
'Changing function name - adding `"-batch"` suffix', func_name=job_name
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
return job_name
|
|
665
|
+
|
|
592
666
|
@classmethod
|
|
593
667
|
def to_job(
|
|
594
668
|
cls,
|
|
@@ -628,7 +702,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
628
702
|
* ``end``, ``datetime``
|
|
629
703
|
* ``base_period``, ``int``
|
|
630
704
|
* ``write_output``, ``bool``
|
|
631
|
-
* ``
|
|
705
|
+
* ``fail_on_overlap``, ``bool``
|
|
632
706
|
|
|
633
707
|
For Git sources, add the source archive to the returned job and change the handler:
|
|
634
708
|
|
|
@@ -647,7 +721,10 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
647
721
|
:py:class:`~mlrun.model_monitoring.applications.ModelMonitoringApplicationBase`,
|
|
648
722
|
is used.
|
|
649
723
|
:param func_path: The path to the function. If ``None``, the current notebook is used.
|
|
650
|
-
:param func_name: The name of the function. If
|
|
724
|
+
:param func_name: The name of the function. If ``None``, the normalized class name is used
|
|
725
|
+
(:py:meth:`mlrun.utils.helpers.normalize_name`).
|
|
726
|
+
A ``"-batch"`` suffix is guaranteed to be added if not already there.
|
|
727
|
+
The function name is also used as the application name to use for the results.
|
|
651
728
|
:param tag: Tag for the function.
|
|
652
729
|
:param image: Docker image to run the job on (when running remotely).
|
|
653
730
|
:param with_repo: Whether to clone the current repo to the build source.
|
|
@@ -668,12 +745,11 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
668
745
|
handler_to_class = class_handler or cls.__name__
|
|
669
746
|
handler = cls.get_job_handler(handler_to_class)
|
|
670
747
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
job_name = func_name if func_name else class_name
|
|
748
|
+
job_name = cls._determine_job_name(
|
|
749
|
+
func_name=func_name,
|
|
750
|
+
class_handler=class_handler,
|
|
751
|
+
handler_to_class=handler_to_class,
|
|
752
|
+
)
|
|
677
753
|
|
|
678
754
|
job = cast(
|
|
679
755
|
mlrun.runtimes.KubejobRuntime,
|
|
@@ -712,7 +788,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
712
788
|
end: Optional[datetime] = None,
|
|
713
789
|
base_period: Optional[int] = None,
|
|
714
790
|
write_output: bool = False,
|
|
715
|
-
|
|
791
|
+
fail_on_overlap: bool = True,
|
|
716
792
|
stream_profile: Optional[ds_profile.DatastoreProfile] = None,
|
|
717
793
|
) -> "mlrun.RunObject":
|
|
718
794
|
"""
|
|
@@ -724,7 +800,10 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
724
800
|
data to the application.
|
|
725
801
|
|
|
726
802
|
:param func_path: The path to the function. If ``None``, the current notebook is used.
|
|
727
|
-
:param func_name: The name of the function. If
|
|
803
|
+
:param func_name: The name of the function. If ``None``, the normalized class name is used
|
|
804
|
+
(:py:meth:`mlrun.utils.helpers.normalize_name`).
|
|
805
|
+
A ``"-batch"`` suffix is guaranteed to be added if not already there.
|
|
806
|
+
The function name is also used as the application name to use for the results.
|
|
728
807
|
:param tag: Tag for the function.
|
|
729
808
|
:param run_local: Whether to run the function locally or remotely.
|
|
730
809
|
:param auto_build: Whether to auto build the function.
|
|
@@ -777,11 +856,11 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
777
856
|
:param write_output: Whether to write the results and metrics to the time-series DB. Can be ``True`` only
|
|
778
857
|
if ``endpoints`` are passed.
|
|
779
858
|
Note: the model monitoring infrastructure must be up for the writing to work.
|
|
780
|
-
:param
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
859
|
+
:param fail_on_overlap: Relevant only when ``write_output=True``. When ``True``, and the
|
|
860
|
+
requested ``start`` time precedes the ``end`` time of a previous run that also
|
|
861
|
+
wrote to the database - an error is raised.
|
|
862
|
+
If ``False``, when the previously described situation occurs, the relevant time
|
|
863
|
+
window is cut so that it starts at the earliest possible time after ``start``.
|
|
785
864
|
:param stream_profile: The stream datastore profile. It should be provided only when running locally and
|
|
786
865
|
writing the outputs to the database (i.e., when both ``run_local`` and
|
|
787
866
|
``write_output`` are set to ``True``).
|
|
@@ -821,7 +900,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
821
900
|
params["end"] = end.isoformat() if isinstance(end, datetime) else end
|
|
822
901
|
params["base_period"] = base_period
|
|
823
902
|
params["write_output"] = write_output
|
|
824
|
-
params["
|
|
903
|
+
params["fail_on_overlap"] = fail_on_overlap
|
|
825
904
|
if stream_profile:
|
|
826
905
|
if not run_local:
|
|
827
906
|
raise mlrun.errors.MLRunValueError(
|
|
@@ -14,16 +14,13 @@
|
|
|
14
14
|
|
|
15
15
|
import dataclasses
|
|
16
16
|
import json
|
|
17
|
-
import re
|
|
18
17
|
from abc import ABC, abstractmethod
|
|
19
18
|
|
|
20
19
|
from pydantic.v1 import validator
|
|
21
20
|
from pydantic.v1.dataclasses import dataclass
|
|
22
21
|
|
|
23
|
-
import mlrun.common.helpers
|
|
24
|
-
import mlrun.common.model_monitoring.helpers
|
|
25
22
|
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
26
|
-
import mlrun.
|
|
23
|
+
import mlrun.errors
|
|
27
24
|
from mlrun.utils import logger
|
|
28
25
|
|
|
29
26
|
_RESULT_EXTRA_DATA_MAX_SIZE = 998
|
|
@@ -33,10 +30,10 @@ class _ModelMonitoringApplicationDataRes(ABC):
|
|
|
33
30
|
name: str
|
|
34
31
|
|
|
35
32
|
def __post_init__(self):
|
|
36
|
-
|
|
37
|
-
if not re.fullmatch(pat, self.name):
|
|
33
|
+
if not mm_constants.RESULT_NAME_REGEX.fullmatch(self.name):
|
|
38
34
|
raise mlrun.errors.MLRunValueError(
|
|
39
|
-
|
|
35
|
+
"The application result or metric name must comply with the regex "
|
|
36
|
+
f"`{mm_constants.RESULT_NAME_REGEX.pattern}`"
|
|
40
37
|
)
|
|
41
38
|
|
|
42
39
|
@abstractmethod
|