mlrun 1.9.0rc3__py3-none-any.whl → 1.9.0rc5__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.

Files changed (52) hide show
  1. mlrun/__main__.py +13 -3
  2. mlrun/artifacts/base.py +5 -5
  3. mlrun/artifacts/dataset.py +1 -1
  4. mlrun/artifacts/model.py +1 -1
  5. mlrun/artifacts/plots.py +2 -2
  6. mlrun/common/constants.py +7 -0
  7. mlrun/common/runtimes/constants.py +1 -1
  8. mlrun/common/schemas/artifact.py +1 -1
  9. mlrun/common/schemas/model_monitoring/model_endpoints.py +32 -8
  10. mlrun/common/schemas/pipeline.py +1 -1
  11. mlrun/common/schemas/project.py +1 -1
  12. mlrun/common/schemas/runs.py +1 -1
  13. mlrun/config.py +5 -11
  14. mlrun/datastore/datastore.py +1 -1
  15. mlrun/datastore/datastore_profile.py +2 -2
  16. mlrun/datastore/sources.py +3 -3
  17. mlrun/datastore/targets.py +4 -4
  18. mlrun/datastore/utils.py +2 -2
  19. mlrun/db/base.py +9 -7
  20. mlrun/db/httpdb.py +48 -27
  21. mlrun/db/nopdb.py +3 -1
  22. mlrun/execution.py +1 -1
  23. mlrun/frameworks/_common/model_handler.py +2 -2
  24. mlrun/launcher/client.py +1 -1
  25. mlrun/model_monitoring/api.py +4 -4
  26. mlrun/model_monitoring/applications/_application_steps.py +3 -1
  27. mlrun/model_monitoring/applications/evidently/base.py +59 -71
  28. mlrun/model_monitoring/controller.py +26 -11
  29. mlrun/model_monitoring/db/tsdb/base.py +3 -1
  30. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connection.py +213 -0
  31. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +27 -49
  32. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +48 -35
  33. mlrun/model_monitoring/tracking_policy.py +1 -1
  34. mlrun/model_monitoring/writer.py +1 -1
  35. mlrun/projects/operations.py +3 -3
  36. mlrun/projects/project.py +37 -22
  37. mlrun/render.py +5 -9
  38. mlrun/run.py +1 -1
  39. mlrun/runtimes/base.py +5 -5
  40. mlrun/runtimes/kubejob.py +2 -2
  41. mlrun/runtimes/nuclio/function.py +3 -3
  42. mlrun/runtimes/nuclio/serving.py +4 -4
  43. mlrun/runtimes/utils.py +25 -8
  44. mlrun/utils/helpers.py +3 -2
  45. mlrun/utils/notifications/notification/webhook.py +18 -2
  46. mlrun/utils/version/version.json +2 -2
  47. {mlrun-1.9.0rc3.dist-info → mlrun-1.9.0rc5.dist-info}/METADATA +9 -13
  48. {mlrun-1.9.0rc3.dist-info → mlrun-1.9.0rc5.dist-info}/RECORD +52 -51
  49. {mlrun-1.9.0rc3.dist-info → mlrun-1.9.0rc5.dist-info}/WHEEL +1 -1
  50. {mlrun-1.9.0rc3.dist-info → mlrun-1.9.0rc5.dist-info}/entry_points.txt +0 -0
  51. {mlrun-1.9.0rc3.dist-info → mlrun-1.9.0rc5.dist-info}/licenses/LICENSE +0 -0
  52. {mlrun-1.9.0rc3.dist-info → mlrun-1.9.0rc5.dist-info}/top_level.txt +0 -0
mlrun/db/httpdb.py CHANGED
@@ -21,7 +21,7 @@ import typing
21
21
  import warnings
22
22
  from copy import deepcopy
23
23
  from datetime import datetime, timedelta
24
- from os import path, remove
24
+ from os import environ, path, remove
25
25
  from typing import Literal, Optional, Union
26
26
  from urllib.parse import urlparse
27
27
 
@@ -129,7 +129,9 @@ class HTTPRunDB(RunDBInterface):
129
129
  self._wait_for_background_task_terminal_state_retry_interval = 3
130
130
  self._wait_for_project_deletion_interval = 3
131
131
  self.client_version = version.Version().get()["version"]
132
- self.python_version = str(version.Version().get_python_version())
132
+ self.python_version = environ.get("MLRUN_PYTHON_VERSION") or str(
133
+ version.Version().get_python_version()
134
+ )
133
135
 
134
136
  self._enrich_and_validate(url)
135
137
 
@@ -945,7 +947,7 @@ class HTTPRunDB(RunDBInterface):
945
947
  or just `"label"` for key existence.
946
948
  - A comma-separated string formatted as `"label1=value1,label2"` to match entities with
947
949
  the specified key-value pairs or key existence.
948
- :param state: Deprecated - List only runs whose state is specified (will be removed in 1.9.0)
950
+ :param state: Deprecated - List only runs whose state is specified (will be removed in 1.10.0)
949
951
  :param states: List only runs whose state is one of the provided states.
950
952
  :param sort: Whether to sort the result according to their start time. Otherwise, results will be
951
953
  returned by their internal order in the DB (order will not be guaranteed).
@@ -1276,8 +1278,8 @@ class HTTPRunDB(RunDBInterface):
1276
1278
  :param producer_uri: Return artifacts produced by the requested producer URI. Producer URI usually
1277
1279
  points to a run and is used to filter artifacts by the run that produced them when the artifact producer id
1278
1280
  is a workflow id (artifact was created as part of a workflow).
1279
- :param format_: The format in which to return the artifacts. Default is 'full'.
1280
- :param limit: Maximum number of artifacts to return.
1281
+ :param format_: The format in which to return the artifacts. Default is 'full'.
1282
+ :param limit: Deprecated - Maximum number of artifacts to return (will be removed in 1.11.0).
1281
1283
  :param partition_by: Field to group results by. When `partition_by` is specified, the `partition_sort_by`
1282
1284
  parameter must be provided as well.
1283
1285
  :param rows_per_partition: How many top rows (per sorting defined by `partition_sort_by` and `partition_order`)
@@ -2221,18 +2223,20 @@ class HTTPRunDB(RunDBInterface):
2221
2223
  elif pipe_file.endswith(".zip"):
2222
2224
  headers = {"content-type": "application/zip"}
2223
2225
  else:
2224
- raise ValueError("pipeline file must be .yaml or .zip")
2226
+ raise ValueError("'pipeline' file must be .yaml or .zip")
2225
2227
  if arguments:
2226
2228
  if not isinstance(arguments, dict):
2227
- raise ValueError("arguments must be dict type")
2229
+ raise ValueError("'arguments' must be dict type")
2228
2230
  headers[mlrun.common.schemas.HeaderNames.pipeline_arguments] = str(
2229
2231
  arguments
2230
2232
  )
2231
2233
 
2232
2234
  if not path.isfile(pipe_file):
2233
- raise OSError(f"file {pipe_file} doesnt exist")
2235
+ raise OSError(f"File {pipe_file} doesnt exist")
2234
2236
  with open(pipe_file, "rb") as fp:
2235
2237
  data = fp.read()
2238
+ if not data:
2239
+ raise ValueError("The compiled pipe file is empty")
2236
2240
  if not isinstance(pipeline, str):
2237
2241
  remove(pipe_file)
2238
2242
 
@@ -3584,7 +3588,7 @@ class HTTPRunDB(RunDBInterface):
3584
3588
  params = {
3585
3589
  "type": type,
3586
3590
  "endpoint-id": endpoint_ids,
3587
- "events_format": events_format,
3591
+ "events-format": events_format,
3588
3592
  }
3589
3593
  error_message = (
3590
3594
  f"Failed to get model monitoring metrics,"
@@ -3720,7 +3724,7 @@ class HTTPRunDB(RunDBInterface):
3720
3724
  path=path,
3721
3725
  body=model_endpoint.json(),
3722
3726
  params={
3723
- "creation_strategy": creation_strategy,
3727
+ "creation-strategy": creation_strategy,
3724
3728
  },
3725
3729
  )
3726
3730
  return mlrun.common.schemas.ModelEndpoint(**response.json())
@@ -3750,9 +3754,9 @@ class HTTPRunDB(RunDBInterface):
3750
3754
  method=mlrun.common.types.HTTPMethod.DELETE,
3751
3755
  path=path,
3752
3756
  params={
3753
- "function_name": function_name,
3754
- "function_tag": function_tag,
3755
- "endpoint_id": endpoint_id,
3757
+ "function-name": function_name,
3758
+ "function-tag": function_tag,
3759
+ "endpoint-id": endpoint_id,
3756
3760
  },
3757
3761
  )
3758
3762
 
@@ -3767,7 +3771,8 @@ class HTTPRunDB(RunDBInterface):
3767
3771
  labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
3768
3772
  start: Optional[datetime] = None,
3769
3773
  end: Optional[datetime] = None,
3770
- tsdb_metrics: bool = True,
3774
+ tsdb_metrics: bool = False,
3775
+ metric_list: Optional[list[str]] = None,
3771
3776
  top_level: bool = False,
3772
3777
  uids: Optional[list[str]] = None,
3773
3778
  latest_only: bool = False,
@@ -3785,6 +3790,9 @@ class HTTPRunDB(RunDBInterface):
3785
3790
  :param start: The start time to filter by.Corresponding to the `created` field.
3786
3791
  :param end: The end time to filter by. Corresponding to the `created` field.
3787
3792
  :param tsdb_metrics: Whether to include metrics from the time series DB.
3793
+ :param metric_list: List of metrics to include from the time series DB. Defaults to all metrics.
3794
+ If tsdb_metrics=False, this parameter will be ignored and no tsdb metrics
3795
+ will be included.
3788
3796
  :param top_level: Whether to return only top level model endpoints.
3789
3797
  :param uids: A list of unique ids to filter by.
3790
3798
  :param latest_only: Whether to return only the latest model endpoint version.
@@ -3799,17 +3807,18 @@ class HTTPRunDB(RunDBInterface):
3799
3807
  path=path,
3800
3808
  params={
3801
3809
  "name": names,
3802
- "model_name": model_name,
3803
- "model_tag": model_tag,
3804
- "function_name": function_name,
3805
- "function_tag": function_tag,
3810
+ "model-name": model_name,
3811
+ "model-tag": model_tag,
3812
+ "function-name": function_name,
3813
+ "function-tag": function_tag,
3806
3814
  "label": labels,
3807
3815
  "start": datetime_to_iso(start),
3808
3816
  "end": datetime_to_iso(end),
3809
- "tsdb_metrics": tsdb_metrics,
3817
+ "tsdb-metrics": tsdb_metrics,
3818
+ "metric": metric_list,
3810
3819
  "top-level": top_level,
3811
3820
  "uid": uids,
3812
- "latest_only": latest_only,
3821
+ "latest-only": latest_only,
3813
3822
  },
3814
3823
  )
3815
3824
 
@@ -3823,6 +3832,7 @@ class HTTPRunDB(RunDBInterface):
3823
3832
  function_tag: Optional[str] = None,
3824
3833
  endpoint_id: Optional[str] = None,
3825
3834
  tsdb_metrics: bool = True,
3835
+ metric_list: Optional[list[str]] = None,
3826
3836
  feature_analysis: bool = False,
3827
3837
  ) -> mlrun.common.schemas.ModelEndpoint:
3828
3838
  """
@@ -3834,6 +3844,9 @@ class HTTPRunDB(RunDBInterface):
3834
3844
  :param function_tag: The tag of the function
3835
3845
  :param endpoint_id: The id of the endpoint
3836
3846
  :param tsdb_metrics: Whether to include metrics from the time series DB.
3847
+ :param metric_list: List of metrics to include from the time series DB. Defaults to all metrics.
3848
+ If tsdb_metrics=False, this parameter will be ignored and no tsdb metrics
3849
+ will be included.
3837
3850
  :param feature_analysis: Whether to include feature analysis data (feature_stats,
3838
3851
  current_stats & drift_measures).
3839
3852
 
@@ -3847,11 +3860,12 @@ class HTTPRunDB(RunDBInterface):
3847
3860
  method=mlrun.common.types.HTTPMethod.GET,
3848
3861
  path=path,
3849
3862
  params={
3850
- "function_name": function_name,
3851
- "function_tag": function_tag,
3852
- "endpoint_id": endpoint_id,
3853
- "tsdb_metrics": tsdb_metrics,
3854
- "feature_analysis": feature_analysis,
3863
+ "function-name": function_name,
3864
+ "function-tag": function_tag,
3865
+ "endpoint-id": endpoint_id,
3866
+ "tsdb-metrics": tsdb_metrics,
3867
+ "metric": metric_list,
3868
+ "feature-analysis": feature_analysis,
3855
3869
  },
3856
3870
  )
3857
3871
 
@@ -5089,6 +5103,13 @@ class HTTPRunDB(RunDBInterface):
5089
5103
  project = project or config.default_project
5090
5104
  labels = self._parse_labels(labels)
5091
5105
 
5106
+ if limit:
5107
+ # TODO: Remove this in 1.11.0
5108
+ warnings.warn(
5109
+ "'limit' is deprecated and will be removed in 1.11.0. Use 'page' and 'page_size' instead.",
5110
+ FutureWarning,
5111
+ )
5112
+
5092
5113
  params = {
5093
5114
  "name": name,
5094
5115
  "tag": tag,
@@ -5225,9 +5246,9 @@ class HTTPRunDB(RunDBInterface):
5225
5246
  )
5226
5247
 
5227
5248
  if state:
5228
- # TODO: Remove this in 1.9.0
5249
+ # TODO: Remove this in 1.10.0
5229
5250
  warnings.warn(
5230
- "'state' is deprecated and will be removed in 1.9.0. Use 'states' instead.",
5251
+ "'state' is deprecated in 1.7.0 and will be removed in 1.10.0. Use 'states' instead.",
5231
5252
  FutureWarning,
5232
5253
  )
5233
5254
 
mlrun/db/nopdb.py CHANGED
@@ -631,7 +631,8 @@ class NopDB(RunDBInterface):
631
631
  labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
632
632
  start: Optional[datetime.datetime] = None,
633
633
  end: Optional[datetime.datetime] = None,
634
- tsdb_metrics: bool = True,
634
+ tsdb_metrics: bool = False,
635
+ metric_list: Optional[list[str]] = None,
635
636
  top_level: bool = False,
636
637
  uids: Optional[list[str]] = None,
637
638
  latest_only: bool = False,
@@ -646,6 +647,7 @@ class NopDB(RunDBInterface):
646
647
  function_tag: Optional[str] = None,
647
648
  endpoint_id: Optional[str] = None,
648
649
  tsdb_metrics: bool = True,
650
+ metric_list: Optional[list[str]] = None,
649
651
  feature_analysis: bool = False,
650
652
  ) -> mlrun.common.schemas.ModelEndpoint:
651
653
  pass
mlrun/execution.py CHANGED
@@ -976,7 +976,7 @@ class MLClientCtx:
976
976
  def get_cached_artifact(self, key):
977
977
  """Return a logged artifact from cache (for potential updates)"""
978
978
  warnings.warn(
979
- "get_cached_artifact is deprecated in 1.8.0 and will be removed in 1.10.0. Use get_artifact instead.",
979
+ "get_cached_artifact is deprecated in 1.8.0 and will be removed in 1.11.0. Use get_artifact instead.",
980
980
  FutureWarning,
981
981
  )
982
982
  return self.get_artifact(key)
@@ -690,10 +690,10 @@ class ModelHandler(ABC, Generic[CommonTypes.ModelType, CommonTypes.IOSampleType]
690
690
  }
691
691
  self._registered_artifacts = {}
692
692
 
693
- # Get the model artifact. If the model was logged during this run, use the cached artifact, otherwise use the
693
+ # Get the model artifact. If the model was logged during this run, use the artifact, otherwise use the
694
694
  # user's given model path:
695
695
  model_artifact = (
696
- self._context.get_cached_artifact(self._model_name)
696
+ self._context.get_artifact(self._model_name)
697
697
  if self._is_logged
698
698
  else self._model_path
699
699
  )
mlrun/launcher/client.py CHANGED
@@ -72,7 +72,7 @@ class ClientBaseLauncher(launcher.BaseLauncher, abc.ABC):
72
72
  ):
73
73
  run.metadata.labels[mlrun_constants.MLRunInternalLabels.kind] = runtime.kind
74
74
  mlrun.runtimes.utils.enrich_run_labels(
75
- run.metadata.labels, [mlrun.common.runtimes.constants.RunLabels.owner]
75
+ run.metadata.labels, [mlrun_constants.MLRunInternalLabels.owner]
76
76
  )
77
77
  if run.spec.output_path:
78
78
  run.spec.output_path = run.spec.output_path.replace(
@@ -181,7 +181,7 @@ def record_results(
181
181
  if drift_threshold is not None or possible_drift_threshold is not None:
182
182
  warnings.warn(
183
183
  "Custom drift threshold arguments are deprecated since version "
184
- "1.7.0 and have no effect. They will be removed in version 1.9.0.\n"
184
+ "1.7.0 and have no effect. They will be removed in version 1.10.0.\n"
185
185
  "To enable the default histogram data drift application, run:\n"
186
186
  "`project.enable_model_monitoring()`.",
187
187
  FutureWarning,
@@ -189,7 +189,7 @@ def record_results(
189
189
  if trigger_monitoring_job is not False:
190
190
  warnings.warn(
191
191
  "`trigger_monitoring_job` argument is deprecated since version "
192
- "1.7.0 and has no effect. It will be removed in version 1.9.0.\n"
192
+ "1.7.0 and has no effect. It will be removed in version 1.10.0.\n"
193
193
  "To enable the default histogram data drift application, run:\n"
194
194
  "`project.enable_model_monitoring()`.",
195
195
  FutureWarning,
@@ -197,13 +197,13 @@ def record_results(
197
197
  if artifacts_tag != "":
198
198
  warnings.warn(
199
199
  "`artifacts_tag` argument is deprecated since version "
200
- "1.7.0 and has no effect. It will be removed in version 1.9.0.",
200
+ "1.7.0 and has no effect. It will be removed in version 1.10.0.",
201
201
  FutureWarning,
202
202
  )
203
203
  if default_batch_image != "mlrun/mlrun":
204
204
  warnings.warn(
205
205
  "`default_batch_image` argument is deprecated since version "
206
- "1.7.0 and has no effect. It will be removed in version 1.9.0.",
206
+ "1.7.0 and has no effect. It will be removed in version 1.10.0.",
207
207
  FutureWarning,
208
208
  )
209
209
 
@@ -96,7 +96,9 @@ class _PushToMonitoringWriter(StepToDict):
96
96
  logger.debug(
97
97
  "Pushing data to output stream", writer_event=str(writer_event)
98
98
  )
99
- self.output_stream.push([writer_event])
99
+ self.output_stream.push(
100
+ [writer_event], partition_key=application_context.endpoint_id
101
+ )
100
102
  logger.debug("Pushed data to output stream successfully")
101
103
 
102
104
  def _lazy_init(self):
@@ -14,19 +14,18 @@
14
14
 
15
15
  import json
16
16
  import posixpath
17
- import uuid
18
17
  import warnings
19
18
  from abc import ABC
19
+ from tempfile import NamedTemporaryFile
20
+ from typing import Optional
20
21
 
21
- import pandas as pd
22
22
  import semver
23
- from evidently.ui.storage.local.base import METADATA_PATH, FSLocation
24
23
 
25
24
  import mlrun.model_monitoring.applications.base as mm_base
26
25
  import mlrun.model_monitoring.applications.context as mm_context
27
- from mlrun.errors import MLRunIncompatibleVersionError
26
+ from mlrun.errors import MLRunIncompatibleVersionError, MLRunValueError
28
27
 
29
- SUPPORTED_EVIDENTLY_VERSION = semver.Version.parse("0.6.0")
28
+ SUPPORTED_EVIDENTLY_VERSION = semver.Version.parse("0.7.5")
30
29
 
31
30
 
32
31
  def _check_evidently_version(*, cur: semver.Version, ref: semver.Version) -> None:
@@ -60,36 +59,66 @@ except ModuleNotFoundError:
60
59
 
61
60
 
62
61
  if _HAS_EVIDENTLY:
63
- from evidently.suite.base_suite import Display
64
- from evidently.ui.type_aliases import STR_UUID
65
- from evidently.ui.workspace import Workspace
66
- from evidently.utils.dashboard import TemplateParams, file_html_template
62
+ from evidently.core.report import Snapshot
63
+ from evidently.legacy.ui.storage.local.base import METADATA_PATH, FSLocation
64
+ from evidently.ui.workspace import (
65
+ STR_UUID,
66
+ CloudWorkspace,
67
+ Project,
68
+ Workspace,
69
+ WorkspaceBase,
70
+ )
67
71
 
68
72
 
69
73
  class EvidentlyModelMonitoringApplicationBase(
70
74
  mm_base.ModelMonitoringApplicationBase, ABC
71
75
  ):
72
76
  def __init__(
73
- self, evidently_workspace_path: str, evidently_project_id: "STR_UUID"
77
+ self,
78
+ evidently_project_id: "STR_UUID",
79
+ evidently_workspace_path: Optional[str] = None,
80
+ cloud_workspace: bool = False,
74
81
  ) -> None:
75
82
  """
76
- A class for integrating Evidently for mlrun model monitoring within a monitoring application.
77
- Note: evidently is not installed by default in the mlrun/mlrun image.
78
- It must be installed separately to use this class.
83
+ A class for integrating Evidently for MLRun model monitoring within a monitoring application.
84
+
85
+ .. note::
86
+
87
+ The ``evidently`` package is not installed by default in the mlrun/mlrun image.
88
+ It must be installed separately to use this class.
79
89
 
80
- :param evidently_workspace_path: (str) The path to the Evidently workspace.
81
90
  :param evidently_project_id: (str) The ID of the Evidently project.
91
+ :param evidently_workspace_path: (str) The path to the Evidently workspace.
92
+ :param cloud_workspace: (bool) Whether the workspace is an Evidently Cloud workspace.
82
93
  """
83
-
84
- # TODO : more then one project (mep -> project)
85
94
  if not _HAS_EVIDENTLY:
86
95
  raise ModuleNotFoundError("Evidently is not installed - the app cannot run")
87
- self._log_location(evidently_workspace_path)
88
- self.evidently_workspace = Workspace.create(evidently_workspace_path)
96
+ self.evidently_workspace_path = evidently_workspace_path
97
+ if cloud_workspace:
98
+ self.get_workspace = self.get_cloud_workspace
99
+ self.evidently_workspace = self.get_workspace()
89
100
  self.evidently_project_id = evidently_project_id
90
- self.evidently_project = self.evidently_workspace.get_project(
91
- evidently_project_id
92
- )
101
+ self.evidently_project = self.load_project()
102
+
103
+ def load_project(self) -> Project:
104
+ """Load the Evidently project."""
105
+ return self.evidently_workspace.get_project(self.evidently_project_id)
106
+
107
+ def get_workspace(self) -> WorkspaceBase:
108
+ """Get the Evidently workspace. Override this method for customize access to the workspace."""
109
+ if self.evidently_workspace_path:
110
+ self._log_location(self.evidently_workspace_path)
111
+ return Workspace.create(self.evidently_workspace_path)
112
+ else:
113
+ raise MLRunValueError(
114
+ "A local workspace could not be created as `evidently_workspace_path` is not set.\n"
115
+ "If you intend to use a cloud workspace, please use `cloud_workspace=True` and set the "
116
+ "`EVIDENTLY_API_KEY` environment variable. In other cases, override this method."
117
+ )
118
+
119
+ def get_cloud_workspace(self) -> CloudWorkspace:
120
+ """Load the Evidently cloud workspace according to the `EVIDENTLY_API_KEY` environment variable."""
121
+ return CloudWorkspace()
93
122
 
94
123
  @staticmethod
95
124
  def _log_location(evidently_workspace_path):
@@ -128,7 +157,7 @@ class EvidentlyModelMonitoringApplicationBase(
128
157
  @staticmethod
129
158
  def log_evidently_object(
130
159
  monitoring_context: mm_context.MonitoringApplicationContext,
131
- evidently_object: "Display",
160
+ evidently_object: "Snapshot",
132
161
  artifact_name: str,
133
162
  unique_per_endpoint: bool = True,
134
163
  ) -> None:
@@ -141,56 +170,15 @@ class EvidentlyModelMonitoringApplicationBase(
141
170
  This method should be called on special occasions only.
142
171
 
143
172
  :param monitoring_context: (MonitoringApplicationContext) The monitoring context to process.
144
- :param evidently_object: (Display) The Evidently display to log, e.g. a report or a test suite object.
145
- :param artifact_name: (str) The name for the logged artifact.
146
- :param unique_per_endpoint: by default ``True``, we will log different artifact for each model endpoint,
147
- set to ``False`` without changing item key will cause artifact override.
148
- """
149
- evidently_object_html = evidently_object.get_html()
150
- monitoring_context.log_artifact(
151
- artifact_name,
152
- body=evidently_object_html.encode("utf-8"),
153
- format="html",
154
- unique_per_endpoint=unique_per_endpoint,
155
- )
156
-
157
- def log_project_dashboard(
158
- self,
159
- monitoring_context: mm_context.MonitoringApplicationContext,
160
- timestamp_start: pd.Timestamp,
161
- timestamp_end: pd.Timestamp,
162
- artifact_name: str = "dashboard",
163
- unique_per_endpoint: bool = True,
164
- ) -> None:
165
- """
166
- Logs an Evidently project dashboard.
167
-
168
- .. caution::
169
-
170
- Logging Evidently dashboards in every model monitoring window may cause scale issues.
171
- This method should be called on special occasions only.
172
-
173
- :param monitoring_context: (MonitoringApplicationContext) The monitoring context to process.
174
- :param timestamp_start: (pd.Timestamp) The start timestamp for the dashboard data.
175
- :param timestamp_end: (pd.Timestamp) The end timestamp for the dashboard data.
173
+ :param evidently_object: (Snapshot) The Evidently run to log, e.g. a report run.
176
174
  :param artifact_name: (str) The name for the logged artifact.
177
175
  :param unique_per_endpoint: by default ``True``, we will log different artifact for each model endpoint,
178
176
  set to ``False`` without changing item key will cause artifact override.
179
177
  """
180
-
181
- dashboard_info = self.evidently_project.build_dashboard_info(
182
- timestamp_start, timestamp_end
183
- )
184
- template_params = TemplateParams(
185
- dashboard_id="pd_" + str(uuid.uuid4()).replace("-", ""),
186
- dashboard_info=dashboard_info,
187
- additional_graphs={},
188
- )
189
-
190
- dashboard_html = file_html_template(params=template_params)
191
- monitoring_context.log_artifact(
192
- artifact_name,
193
- body=dashboard_html.encode("utf-8"),
194
- format="html",
195
- unique_per_endpoint=unique_per_endpoint,
196
- )
178
+ with NamedTemporaryFile(suffix=".html") as file:
179
+ evidently_object.save_html(filename=file.name)
180
+ monitoring_context.log_artifact(
181
+ artifact_name,
182
+ local_path=file.name,
183
+ unique_per_endpoint=unique_per_endpoint,
184
+ )
@@ -25,6 +25,7 @@ from types import TracebackType
25
25
  from typing import Any, NamedTuple, Optional, Union, cast
26
26
 
27
27
  import nuclio_sdk
28
+ import pandas as pd
28
29
 
29
30
  import mlrun
30
31
  import mlrun.common.schemas.model_monitoring.constants as mm_constants
@@ -673,7 +674,15 @@ class MonitoringApplicationController:
673
674
  """
674
675
  logger.info("Starting monitoring controller chief")
675
676
  applications_names = []
676
- endpoints = self.project_obj.list_model_endpoints(tsdb_metrics=True).endpoints
677
+ endpoints = self.project_obj.list_model_endpoints(tsdb_metrics=False).endpoints
678
+ last_request_dict = self.tsdb_connector.get_last_request(
679
+ endpoint_ids=[mep.metadata.uid for mep in endpoints]
680
+ )
681
+ if isinstance(last_request_dict, pd.DataFrame):
682
+ last_request_dict = last_request_dict.set_index(
683
+ mm_constants.EventFieldType.ENDPOINT_ID
684
+ )[mm_constants.ModelEndpointSchema.LAST_REQUEST].to_dict()
685
+
677
686
  if not endpoints:
678
687
  logger.info("No model endpoints found", project=self.project)
679
688
  return
@@ -719,16 +728,22 @@ class MonitoringApplicationController:
719
728
  with schedules.ModelMonitoringSchedulesFileChief(
720
729
  self.project
721
730
  ) as schedule_file:
722
- futures = {
723
- pool.submit(
724
- self.endpoint_to_regular_event,
725
- endpoint,
726
- policy,
727
- set(applications_names),
728
- schedule_file,
729
- ): endpoint
730
- for endpoint in endpoints
731
- }
731
+ for endpoint in endpoints:
732
+ last_request = last_request_dict.get(endpoint.metadata.uid, None)
733
+ if isinstance(last_request, float):
734
+ last_request = pd.to_datetime(last_request, unit="s", utc=True)
735
+ endpoint.status.last_request = (
736
+ last_request or endpoint.status.last_request
737
+ )
738
+ futures = {
739
+ pool.submit(
740
+ self.endpoint_to_regular_event,
741
+ endpoint,
742
+ policy,
743
+ set(applications_names),
744
+ schedule_file,
745
+ ): endpoint
746
+ }
732
747
  for future in concurrent.futures.as_completed(futures):
733
748
  if future.exception():
734
749
  exception = future.exception()
@@ -82,7 +82,8 @@ class TSDBConnector(ABC):
82
82
 
83
83
  @abstractmethod
84
84
  def delete_tsdb_records(
85
- self, endpoint_ids: list[str], delete_timeout: Optional[int] = None
85
+ self,
86
+ endpoint_ids: list[str],
86
87
  ) -> None:
87
88
  """
88
89
  Delete model endpoint records from the TSDB connector.
@@ -332,6 +333,7 @@ class TSDBConnector(ABC):
332
333
  model_endpoint_objects: list[mlrun.common.schemas.ModelEndpoint],
333
334
  project: str,
334
335
  run_in_threadpool: Callable,
336
+ metric_list: Optional[list[str]] = None,
335
337
  ) -> list[mlrun.common.schemas.ModelEndpoint]:
336
338
  raise NotImplementedError()
337
339