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.
Files changed (35) hide show
  1. mlrun/common/constants.py +2 -0
  2. mlrun/common/formatters/artifact.py +1 -0
  3. mlrun/common/schemas/__init__.py +1 -0
  4. mlrun/common/schemas/model_monitoring/__init__.py +1 -0
  5. mlrun/common/schemas/model_monitoring/constants.py +33 -6
  6. mlrun/common/schemas/serving.py +3 -0
  7. mlrun/common/schemas/workflow.py +1 -0
  8. mlrun/config.py +15 -5
  9. mlrun/datastore/datastore.py +4 -4
  10. mlrun/datastore/datastore_profile.py +26 -0
  11. mlrun/datastore/model_provider/huggingface_provider.py +183 -0
  12. mlrun/datastore/model_provider/model_provider.py +6 -1
  13. mlrun/datastore/model_provider/openai_provider.py +24 -12
  14. mlrun/datastore/utils.py +6 -0
  15. mlrun/db/base.py +1 -0
  16. mlrun/db/httpdb.py +4 -0
  17. mlrun/model_monitoring/api.py +5 -3
  18. mlrun/model_monitoring/applications/base.py +107 -28
  19. mlrun/model_monitoring/applications/results.py +4 -7
  20. mlrun/model_monitoring/controller.py +175 -121
  21. mlrun/model_monitoring/stream_processing.py +29 -2
  22. mlrun/projects/project.py +7 -2
  23. mlrun/run.py +3 -1
  24. mlrun/serving/server.py +98 -11
  25. mlrun/serving/states.py +8 -19
  26. mlrun/serving/system_steps.py +20 -10
  27. mlrun/utils/helpers.py +6 -1
  28. mlrun/utils/logger.py +3 -1
  29. mlrun/utils/version/version.json +2 -2
  30. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc18.dist-info}/METADATA +2 -2
  31. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc18.dist-info}/RECORD +35 -34
  32. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc18.dist-info}/WHEEL +0 -0
  33. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc18.dist-info}/entry_points.txt +0 -0
  34. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.0rc18.dist-info}/licenses/LICENSE +0 -0
  35. {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[str, list[tuple]] = defaultdict(list)
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
- allow_unordered_data: bool = False,
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.__class__.__name__
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
- allow_unordered_data=allow_unordered_data,
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
- allow_unordered_data: bool,
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 allow_unordered_data:
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
- allow_unordered_data: bool,
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
- allow_unordered_data=allow_unordered_data,
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
- * ``allow_unordered_data``, ``bool``
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 not ``None``, the class name is used.
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
- if not class_handler:
672
- class_name = cls.__name__
673
- else:
674
- class_name = handler_to_class.split(".")[-1].split("::")[-1]
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
- allow_unordered_data: bool = False,
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 not ``None``, the class name is used.
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 allow_unordered_data: Relevant only when writing outputs to the database. When ``False``, and the
781
- requested ``start`` time precedes the ``end`` time of a previous run that also
782
- wrote to the database - an error is raised.
783
- If ``True``, when the previously described situation occurs, the relevant time
784
- window is cut so that it starts at the earliest possible time after ``start``.
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["allow_unordered_data"] = allow_unordered_data
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.utils.v3io_clients
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
- pat = re.compile(mm_constants.RESULT_NAME_PATTERN)
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
- f"Attribute name must comply with the regex `{mm_constants.RESULT_NAME_PATTERN}`"
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