mlrun 1.7.0rc4__py3-none-any.whl → 1.7.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.

Files changed (200) hide show
  1. mlrun/__init__.py +11 -1
  2. mlrun/__main__.py +25 -111
  3. mlrun/{datastore/helpers.py → alerts/__init__.py} +2 -5
  4. mlrun/alerts/alert.py +144 -0
  5. mlrun/api/schemas/__init__.py +4 -3
  6. mlrun/artifacts/__init__.py +8 -3
  7. mlrun/artifacts/base.py +38 -254
  8. mlrun/artifacts/dataset.py +9 -190
  9. mlrun/artifacts/manager.py +41 -47
  10. mlrun/artifacts/model.py +30 -158
  11. mlrun/artifacts/plots.py +23 -380
  12. mlrun/common/constants.py +68 -0
  13. mlrun/common/formatters/__init__.py +19 -0
  14. mlrun/{model_monitoring/stores/models/sqlite.py → common/formatters/artifact.py} +6 -8
  15. mlrun/common/formatters/base.py +78 -0
  16. mlrun/common/formatters/function.py +41 -0
  17. mlrun/common/formatters/pipeline.py +53 -0
  18. mlrun/common/formatters/project.py +51 -0
  19. mlrun/{runtimes → common/runtimes}/constants.py +32 -4
  20. mlrun/common/schemas/__init__.py +25 -4
  21. mlrun/common/schemas/alert.py +203 -0
  22. mlrun/common/schemas/api_gateway.py +148 -0
  23. mlrun/common/schemas/artifact.py +15 -5
  24. mlrun/common/schemas/auth.py +8 -2
  25. mlrun/common/schemas/client_spec.py +2 -0
  26. mlrun/common/schemas/frontend_spec.py +1 -0
  27. mlrun/common/schemas/function.py +4 -0
  28. mlrun/common/schemas/hub.py +7 -9
  29. mlrun/common/schemas/model_monitoring/__init__.py +19 -3
  30. mlrun/common/schemas/model_monitoring/constants.py +96 -26
  31. mlrun/common/schemas/model_monitoring/grafana.py +9 -5
  32. mlrun/common/schemas/model_monitoring/model_endpoints.py +86 -2
  33. mlrun/{runtimes/mpijob/v1alpha1.py → common/schemas/pagination.py} +10 -13
  34. mlrun/common/schemas/pipeline.py +0 -9
  35. mlrun/common/schemas/project.py +22 -21
  36. mlrun/common/types.py +7 -1
  37. mlrun/config.py +87 -19
  38. mlrun/data_types/data_types.py +4 -0
  39. mlrun/data_types/to_pandas.py +9 -9
  40. mlrun/datastore/__init__.py +5 -8
  41. mlrun/datastore/alibaba_oss.py +130 -0
  42. mlrun/datastore/azure_blob.py +4 -5
  43. mlrun/datastore/base.py +69 -30
  44. mlrun/datastore/datastore.py +10 -2
  45. mlrun/datastore/datastore_profile.py +90 -6
  46. mlrun/datastore/google_cloud_storage.py +1 -1
  47. mlrun/datastore/hdfs.py +5 -0
  48. mlrun/datastore/inmem.py +2 -2
  49. mlrun/datastore/redis.py +2 -2
  50. mlrun/datastore/s3.py +5 -0
  51. mlrun/datastore/snowflake_utils.py +43 -0
  52. mlrun/datastore/sources.py +172 -44
  53. mlrun/datastore/store_resources.py +7 -7
  54. mlrun/datastore/targets.py +285 -41
  55. mlrun/datastore/utils.py +68 -5
  56. mlrun/datastore/v3io.py +27 -50
  57. mlrun/db/auth_utils.py +152 -0
  58. mlrun/db/base.py +149 -14
  59. mlrun/db/factory.py +1 -1
  60. mlrun/db/httpdb.py +608 -178
  61. mlrun/db/nopdb.py +191 -7
  62. mlrun/errors.py +11 -0
  63. mlrun/execution.py +37 -20
  64. mlrun/feature_store/__init__.py +0 -2
  65. mlrun/feature_store/api.py +21 -52
  66. mlrun/feature_store/feature_set.py +48 -23
  67. mlrun/feature_store/feature_vector.py +2 -1
  68. mlrun/feature_store/ingestion.py +7 -6
  69. mlrun/feature_store/retrieval/base.py +9 -4
  70. mlrun/feature_store/retrieval/conversion.py +9 -9
  71. mlrun/feature_store/retrieval/dask_merger.py +2 -0
  72. mlrun/feature_store/retrieval/job.py +9 -3
  73. mlrun/feature_store/retrieval/local_merger.py +2 -0
  74. mlrun/feature_store/retrieval/spark_merger.py +34 -24
  75. mlrun/feature_store/steps.py +30 -19
  76. mlrun/features.py +4 -13
  77. mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +7 -12
  78. mlrun/frameworks/auto_mlrun/auto_mlrun.py +2 -2
  79. mlrun/frameworks/lgbm/__init__.py +1 -1
  80. mlrun/frameworks/lgbm/callbacks/callback.py +2 -4
  81. mlrun/frameworks/lgbm/model_handler.py +1 -1
  82. mlrun/frameworks/parallel_coordinates.py +2 -1
  83. mlrun/frameworks/pytorch/__init__.py +2 -2
  84. mlrun/frameworks/sklearn/__init__.py +1 -1
  85. mlrun/frameworks/tf_keras/__init__.py +5 -2
  86. mlrun/frameworks/tf_keras/callbacks/logging_callback.py +1 -1
  87. mlrun/frameworks/tf_keras/mlrun_interface.py +2 -2
  88. mlrun/frameworks/xgboost/__init__.py +1 -1
  89. mlrun/k8s_utils.py +10 -11
  90. mlrun/launcher/__init__.py +1 -1
  91. mlrun/launcher/base.py +6 -5
  92. mlrun/launcher/client.py +8 -6
  93. mlrun/launcher/factory.py +1 -1
  94. mlrun/launcher/local.py +9 -3
  95. mlrun/launcher/remote.py +9 -3
  96. mlrun/lists.py +6 -2
  97. mlrun/model.py +58 -19
  98. mlrun/model_monitoring/__init__.py +1 -1
  99. mlrun/model_monitoring/api.py +127 -301
  100. mlrun/model_monitoring/application.py +5 -296
  101. mlrun/model_monitoring/applications/__init__.py +11 -0
  102. mlrun/model_monitoring/applications/_application_steps.py +157 -0
  103. mlrun/model_monitoring/applications/base.py +282 -0
  104. mlrun/model_monitoring/applications/context.py +214 -0
  105. mlrun/model_monitoring/applications/evidently_base.py +211 -0
  106. mlrun/model_monitoring/applications/histogram_data_drift.py +224 -93
  107. mlrun/model_monitoring/applications/results.py +99 -0
  108. mlrun/model_monitoring/controller.py +30 -36
  109. mlrun/model_monitoring/db/__init__.py +18 -0
  110. mlrun/model_monitoring/{stores → db/stores}/__init__.py +43 -36
  111. mlrun/model_monitoring/db/stores/base/__init__.py +15 -0
  112. mlrun/model_monitoring/{stores/model_endpoint_store.py → db/stores/base/store.py} +58 -32
  113. mlrun/model_monitoring/db/stores/sqldb/__init__.py +13 -0
  114. mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +71 -0
  115. mlrun/model_monitoring/{stores → db/stores/sqldb}/models/base.py +109 -5
  116. mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +88 -0
  117. mlrun/model_monitoring/{stores/models/mysql.py → db/stores/sqldb/models/sqlite.py} +19 -13
  118. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +684 -0
  119. mlrun/model_monitoring/db/stores/v3io_kv/__init__.py +13 -0
  120. mlrun/model_monitoring/{stores/kv_model_endpoint_store.py → db/stores/v3io_kv/kv_store.py} +302 -155
  121. mlrun/model_monitoring/db/tsdb/__init__.py +100 -0
  122. mlrun/model_monitoring/db/tsdb/base.py +329 -0
  123. mlrun/model_monitoring/db/tsdb/helpers.py +30 -0
  124. mlrun/model_monitoring/db/tsdb/tdengine/__init__.py +15 -0
  125. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +240 -0
  126. mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +45 -0
  127. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +397 -0
  128. mlrun/model_monitoring/db/tsdb/v3io/__init__.py +15 -0
  129. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +117 -0
  130. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +630 -0
  131. mlrun/model_monitoring/evidently_application.py +6 -118
  132. mlrun/model_monitoring/features_drift_table.py +34 -22
  133. mlrun/model_monitoring/helpers.py +100 -7
  134. mlrun/model_monitoring/model_endpoint.py +3 -2
  135. mlrun/model_monitoring/stream_processing.py +93 -228
  136. mlrun/model_monitoring/tracking_policy.py +7 -1
  137. mlrun/model_monitoring/writer.py +152 -124
  138. mlrun/package/packagers_manager.py +1 -0
  139. mlrun/package/utils/_formatter.py +2 -2
  140. mlrun/platforms/__init__.py +11 -10
  141. mlrun/platforms/iguazio.py +21 -202
  142. mlrun/projects/operations.py +30 -16
  143. mlrun/projects/pipelines.py +92 -99
  144. mlrun/projects/project.py +757 -268
  145. mlrun/render.py +15 -14
  146. mlrun/run.py +160 -162
  147. mlrun/runtimes/__init__.py +55 -3
  148. mlrun/runtimes/base.py +33 -19
  149. mlrun/runtimes/databricks_job/databricks_wrapper.py +1 -1
  150. mlrun/runtimes/funcdoc.py +0 -28
  151. mlrun/runtimes/kubejob.py +28 -122
  152. mlrun/runtimes/local.py +5 -2
  153. mlrun/runtimes/mpijob/__init__.py +0 -20
  154. mlrun/runtimes/mpijob/abstract.py +8 -8
  155. mlrun/runtimes/mpijob/v1.py +1 -1
  156. mlrun/runtimes/nuclio/__init__.py +1 -0
  157. mlrun/runtimes/nuclio/api_gateway.py +709 -0
  158. mlrun/runtimes/nuclio/application/__init__.py +15 -0
  159. mlrun/runtimes/nuclio/application/application.py +523 -0
  160. mlrun/runtimes/nuclio/application/reverse_proxy.go +95 -0
  161. mlrun/runtimes/nuclio/function.py +98 -58
  162. mlrun/runtimes/nuclio/serving.py +36 -42
  163. mlrun/runtimes/pod.py +196 -45
  164. mlrun/runtimes/remotesparkjob.py +1 -1
  165. mlrun/runtimes/sparkjob/spark3job.py +1 -1
  166. mlrun/runtimes/utils.py +6 -73
  167. mlrun/secrets.py +6 -2
  168. mlrun/serving/remote.py +2 -3
  169. mlrun/serving/routers.py +7 -4
  170. mlrun/serving/server.py +7 -8
  171. mlrun/serving/states.py +73 -43
  172. mlrun/serving/v2_serving.py +8 -7
  173. mlrun/track/tracker.py +2 -1
  174. mlrun/utils/async_http.py +25 -5
  175. mlrun/utils/helpers.py +141 -75
  176. mlrun/utils/http.py +1 -1
  177. mlrun/utils/logger.py +39 -7
  178. mlrun/utils/notifications/notification/__init__.py +14 -9
  179. mlrun/utils/notifications/notification/base.py +12 -0
  180. mlrun/utils/notifications/notification/console.py +2 -0
  181. mlrun/utils/notifications/notification/git.py +3 -1
  182. mlrun/utils/notifications/notification/ipython.py +2 -0
  183. mlrun/utils/notifications/notification/slack.py +101 -21
  184. mlrun/utils/notifications/notification/webhook.py +11 -1
  185. mlrun/utils/notifications/notification_pusher.py +147 -16
  186. mlrun/utils/retryer.py +3 -2
  187. mlrun/utils/v3io_clients.py +0 -1
  188. mlrun/utils/version/version.json +2 -2
  189. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.0rc20.dist-info}/METADATA +33 -18
  190. mlrun-1.7.0rc20.dist-info/RECORD +353 -0
  191. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.0rc20.dist-info}/WHEEL +1 -1
  192. mlrun/kfpops.py +0 -868
  193. mlrun/model_monitoring/batch.py +0 -974
  194. mlrun/model_monitoring/stores/models/__init__.py +0 -27
  195. mlrun/model_monitoring/stores/sql_model_endpoint_store.py +0 -382
  196. mlrun/platforms/other.py +0 -305
  197. mlrun-1.7.0rc4.dist-info/RECORD +0 -321
  198. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.0rc20.dist-info}/LICENSE +0 -0
  199. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.0rc20.dist-info}/entry_points.txt +0 -0
  200. {mlrun-1.7.0rc4.dist-info → mlrun-1.7.0rc20.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,99 @@
1
+ # Copyright 2023 Iguazio
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ import dataclasses
16
+ import json
17
+ import re
18
+ from abc import ABC, abstractmethod
19
+
20
+ import mlrun.common.helpers
21
+ import mlrun.common.model_monitoring.helpers
22
+ import mlrun.common.schemas.model_monitoring.constants as mm_constant
23
+ import mlrun.utils.v3io_clients
24
+
25
+
26
+ class _ModelMonitoringApplicationDataRes(ABC):
27
+ name: str
28
+
29
+ def __post_init__(self):
30
+ pat = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*")
31
+ if not re.fullmatch(pat, self.name):
32
+ raise mlrun.errors.MLRunInvalidArgumentError(
33
+ "Attribute name must be of the format [a-zA-Z_][a-zA-Z0-9_]*"
34
+ )
35
+
36
+ @abstractmethod
37
+ def to_dict(self):
38
+ raise NotImplementedError
39
+
40
+
41
+ @dataclasses.dataclass
42
+ class ModelMonitoringApplicationResult(_ModelMonitoringApplicationDataRes):
43
+ """
44
+ Class representing the result of a custom model monitoring application.
45
+
46
+ :param name: (str) Name of the application result. This name must be
47
+ unique for each metric in a single application
48
+ (name must be of the format [a-zA-Z_][a-zA-Z0-9_]*).
49
+ :param value: (float) Value of the application result.
50
+ :param kind: (ResultKindApp) Kind of application result.
51
+ :param status: (ResultStatusApp) Status of the application result.
52
+ :param extra_data: (dict) Extra data associated with the application result.
53
+ """
54
+
55
+ name: str
56
+ value: float
57
+ kind: mm_constant.ResultKindApp
58
+ status: mm_constant.ResultStatusApp
59
+ extra_data: dict = dataclasses.field(default_factory=dict)
60
+
61
+ def to_dict(self):
62
+ """
63
+ Convert the object to a dictionary format suitable for writing.
64
+
65
+ :returns: (dict) Dictionary representation of the result.
66
+ """
67
+ return {
68
+ mm_constant.ResultData.RESULT_NAME: self.name,
69
+ mm_constant.ResultData.RESULT_VALUE: self.value,
70
+ mm_constant.ResultData.RESULT_KIND: self.kind.value,
71
+ mm_constant.ResultData.RESULT_STATUS: self.status.value,
72
+ mm_constant.ResultData.RESULT_EXTRA_DATA: json.dumps(self.extra_data),
73
+ }
74
+
75
+
76
+ @dataclasses.dataclass
77
+ class ModelMonitoringApplicationMetric(_ModelMonitoringApplicationDataRes):
78
+ """
79
+ Class representing a single metric of a custom model monitoring application.
80
+
81
+ :param name: (str) Name of the application metric. This name must be
82
+ unique for each metric in a single application
83
+ (name must be of the format [a-zA-Z_][a-zA-Z0-9_]*).
84
+ :param value: (float) Value of the application metric.
85
+ """
86
+
87
+ name: str
88
+ value: float
89
+
90
+ def to_dict(self):
91
+ """
92
+ Convert the object to a dictionary format suitable for writing.
93
+
94
+ :returns: (dict) Dictionary representation of the result.
95
+ """
96
+ return {
97
+ mm_constant.MetricData.METRIC_NAME: self.name,
98
+ mm_constant.MetricData.METRIC_VALUE: self.value,
99
+ }
@@ -21,25 +21,24 @@ from collections.abc import Iterator
21
21
  from typing import Any, NamedTuple, Optional, Union, cast
22
22
 
23
23
  import nuclio
24
- from v3io.dataplane.response import HttpResponseError
25
24
 
26
25
  import mlrun
27
26
  import mlrun.common.schemas.model_monitoring.constants as mm_constants
28
27
  import mlrun.data_types.infer
29
28
  import mlrun.feature_store as fstore
29
+ import mlrun.model_monitoring.db.stores
30
30
  from mlrun.common.model_monitoring.helpers import FeatureStats, pad_features_hist
31
31
  from mlrun.datastore import get_stream_pusher
32
32
  from mlrun.datastore.targets import ParquetTarget
33
33
  from mlrun.errors import err_to_str
34
- from mlrun.model_monitoring.batch import calculate_inputs_statistics
35
34
  from mlrun.model_monitoring.helpers import (
36
35
  _BatchDict,
37
36
  batch_dict2timedelta,
37
+ calculate_inputs_statistics,
38
38
  get_monitoring_parquet_path,
39
39
  get_stream_path,
40
40
  )
41
- from mlrun.utils import create_logger, datetime_now, logger
42
- from mlrun.utils.v3io_clients import get_v3io_client
41
+ from mlrun.utils import datetime_now, logger
43
42
 
44
43
 
45
44
  class _Interval(NamedTuple):
@@ -48,8 +47,6 @@ class _Interval(NamedTuple):
48
47
 
49
48
 
50
49
  class _BatchWindow:
51
- V3IO_CONTAINER_FORMAT = "users/pipelines/{project}/monitoring-schedules/functions"
52
-
53
50
  def __init__(
54
51
  self,
55
52
  project: str,
@@ -65,27 +62,22 @@ class _BatchWindow:
65
62
  All the time values are in seconds.
66
63
  The start and stop time are in seconds since the epoch.
67
64
  """
65
+ self.project = project
68
66
  self._endpoint = endpoint
69
67
  self._application = application
70
68
  self._first_request = first_request
71
- self._kv_storage = get_v3io_client(
72
- endpoint=mlrun.mlconf.v3io_api,
73
- # Avoid noisy warning logs before the KV table is created
74
- logger=create_logger(name="v3io_client", level="error"),
75
- ).kv
76
- self._v3io_container = self.V3IO_CONTAINER_FORMAT.format(project=project)
77
69
  self._stop = last_updated
78
70
  self._step = timedelta_seconds
71
+ self._db = mlrun.model_monitoring.get_store_object(project=self.project)
79
72
  self._start = self._get_last_analyzed()
80
73
 
81
74
  def _get_last_analyzed(self) -> Optional[int]:
82
75
  try:
83
- data = self._kv_storage.get(
84
- container=self._v3io_container,
85
- table_path=self._endpoint,
86
- key=self._application,
76
+ last_analyzed = self._db.get_last_analyzed(
77
+ endpoint_id=self._endpoint,
78
+ application_name=self._application,
87
79
  )
88
- except HttpResponseError as err:
80
+ except mlrun.errors.MLRunNotFoundError:
89
81
  logger.info(
90
82
  "No last analyzed time was found for this endpoint and "
91
83
  "application, as this is probably the first time this "
@@ -96,7 +88,7 @@ class _BatchWindow:
96
88
  first_request=self._first_request,
97
89
  last_updated=self._stop,
98
90
  )
99
- logger.debug("Error while getting last analyzed time", err=err)
91
+
100
92
  if self._first_request and self._stop:
101
93
  # TODO : Change the timedelta according to the policy.
102
94
  first_period_in_seconds = max(
@@ -108,7 +100,6 @@ class _BatchWindow:
108
100
  )
109
101
  return self._first_request
110
102
 
111
- last_analyzed = data.output.item[mm_constants.SchedulingKeys.LAST_ANALYZED]
112
103
  logger.info(
113
104
  "Got the last analyzed time for this endpoint and application",
114
105
  endpoint=self._endpoint,
@@ -124,11 +115,11 @@ class _BatchWindow:
124
115
  application=self._application,
125
116
  last_analyzed=last_analyzed,
126
117
  )
127
- self._kv_storage.put(
128
- container=self._v3io_container,
129
- table_path=self._endpoint,
130
- key=self._application,
131
- attributes={mm_constants.SchedulingKeys.LAST_ANALYZED: last_analyzed},
118
+
119
+ self._db.update_last_analyzed(
120
+ endpoint_id=self._endpoint,
121
+ application_name=self._application,
122
+ last_analyzed=last_analyzed,
132
123
  )
133
124
 
134
125
  def get_intervals(
@@ -301,7 +292,7 @@ class MonitoringApplicationController:
301
292
  f"Initializing {self.__class__.__name__}", project=project
302
293
  )
303
294
 
304
- self.db = mlrun.model_monitoring.get_model_endpoint_store(project=project)
295
+ self.db = mlrun.model_monitoring.get_store_object(project=project)
305
296
 
306
297
  self._batch_window_generator = _BatchWindowGenerator(
307
298
  batch_dict=json.loads(
@@ -359,7 +350,12 @@ class MonitoringApplicationController:
359
350
  {
360
351
  app.metadata.name
361
352
  for app in monitoring_functions
362
- if app.status.state == "ready"
353
+ if (
354
+ app.status.state == "ready"
355
+ # workaround for the default app, as its `status.state` is `None`
356
+ or app.metadata.name
357
+ == mm_constants.HistogramDataDriftApplicationConstants.NAME
358
+ )
363
359
  }
364
360
  )
365
361
  if not applications_names:
@@ -367,6 +363,10 @@ class MonitoringApplicationController:
367
363
  "No monitoring functions found", project=self.project
368
364
  )
369
365
  return
366
+ self.context.logger.info(
367
+ "Starting to iterate over the applications",
368
+ applications=applications_names,
369
+ )
370
370
 
371
371
  except Exception as e:
372
372
  self.context.logger.error(
@@ -445,13 +445,6 @@ class MonitoringApplicationController:
445
445
  m_fs = fstore.get_feature_set(
446
446
  endpoint[mm_constants.EventFieldType.FEATURE_SET_URI]
447
447
  )
448
- labels = endpoint[mm_constants.EventFieldType.LABEL_NAMES]
449
- if labels:
450
- if isinstance(labels, str):
451
- labels = json.loads(labels)
452
- for label in labels:
453
- if label not in list(m_fs.spec.features.keys()):
454
- m_fs.add_feature(fstore.Feature(name=label, value_type="float"))
455
448
 
456
449
  for application in applications_names:
457
450
  batch_window = batch_window_generator.get_batch_window(
@@ -464,6 +457,7 @@ class MonitoringApplicationController:
464
457
  )
465
458
 
466
459
  for start_infer_time, end_infer_time in batch_window.get_intervals():
460
+ # start - TODO : delete in 1.9.0 (V1 app deprecation)
467
461
  try:
468
462
  # Get application sample data
469
463
  offline_response = cls._get_sample_df(
@@ -509,10 +503,9 @@ class MonitoringApplicationController:
509
503
 
510
504
  # Get the current stats:
511
505
  current_stats = calculate_inputs_statistics(
512
- sample_set_statistics=feature_stats,
513
- inputs=df,
506
+ sample_set_statistics=feature_stats, inputs=df
514
507
  )
515
-
508
+ # end - TODO : delete in 1.9.0 (V1 app deprecation)
516
509
  cls._push_to_applications(
517
510
  current_stats=current_stats,
518
511
  feature_stats=feature_stats,
@@ -621,6 +614,7 @@ class MonitoringApplicationController:
621
614
  project=project,
622
615
  function_name=mm_constants.MonitoringFunctionNames.WRITER,
623
616
  ),
617
+ mm_constants.ApplicationEvent.MLRUN_CONTEXT: {}, # TODO : for future use by ad-hoc batch infer
624
618
  }
625
619
  for app_name in applications_names:
626
620
  data.update({mm_constants.ApplicationEvent.APPLICATION_NAME: app_name})
@@ -0,0 +1,18 @@
1
+ # Copyright 2024 Iguazio
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from .stores import ObjectStoreFactory, get_store_object
16
+ from .stores.base import StoreBase
17
+ from .tsdb import get_tsdb_connector
18
+ from .tsdb.base import TSDBConnector
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Iguazio
1
+ # Copyright 2024 Iguazio
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -12,64 +12,56 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- # flake8: noqa - this is until we take care of the F401 violations with respect to __all__ & sphinx
16
-
17
15
  import enum
18
16
  import typing
17
+ import warnings
19
18
 
20
19
  import mlrun.common.schemas.secret
21
20
  import mlrun.errors
22
21
 
23
- from .model_endpoint_store import ModelEndpointStore
22
+ from .base import StoreBase
24
23
 
25
24
 
26
- class ModelEndpointStoreType(enum.Enum):
27
- """Enum class to handle the different store type values for saving a model endpoint record."""
25
+ class ObjectStoreFactory(enum.Enum):
26
+ """Enum class to handle the different store type values for saving model monitoring records."""
28
27
 
29
28
  v3io_nosql = "v3io-nosql"
30
29
  SQL = "sql"
31
30
 
32
- def to_endpoint_store(
31
+ def to_object_store(
33
32
  self,
34
33
  project: str,
35
34
  access_key: str = None,
36
- endpoint_store_connection: str = None,
37
35
  secret_provider: typing.Callable = None,
38
- ) -> ModelEndpointStore:
36
+ ) -> StoreBase:
39
37
  """
40
- Return a ModelEndpointStore object based on the provided enum value.
41
-
42
- :param project: The name of the project.
43
- :param access_key: Access key with permission to the DB table. Note that if access key is None
44
- and the endpoint target is from type KV then the access key will be
45
- retrieved from the environment variable.
46
- :param endpoint_store_connection: A valid connection string for model endpoint target. Contains several
47
- key-value pairs that required for the database connection.
48
- e.g. A root user with password 1234, tries to connect a schema called
49
- mlrun within a local MySQL DB instance:
50
- 'mysql+pymysql://root:1234@localhost:3306/mlrun'.
38
+ Return a StoreBase object based on the provided enum value.
39
+
40
+ :param project: The name of the project.
41
+ :param access_key: Access key with permission to the DB table. Note that if access key is None
42
+ and the endpoint target is from type KV then the access key will be
43
+ retrieved from the environment variable.
51
44
  :param secret_provider: An optional secret provider to get the connection string secret.
52
45
 
53
- :return: `ModelEndpointStore` object.
46
+ :return: `StoreBase` object.
54
47
 
55
48
  """
56
49
 
57
- if self.value == ModelEndpointStoreType.v3io_nosql.value:
58
- from .kv_model_endpoint_store import KVModelEndpointStore
50
+ if self == self.v3io_nosql:
51
+ from mlrun.model_monitoring.db.stores.v3io_kv.kv_store import KVStoreBase
59
52
 
60
53
  # Get V3IO access key from env
61
54
  access_key = access_key or mlrun.mlconf.get_v3io_access_key()
62
55
 
63
- return KVModelEndpointStore(project=project, access_key=access_key)
56
+ return KVStoreBase(project=project, access_key=access_key)
64
57
 
65
58
  # Assuming SQL store target if store type is not KV.
66
59
  # Update these lines once there are more than two store target types.
67
60
 
68
- from .sql_model_endpoint_store import SQLModelEndpointStore
61
+ from mlrun.model_monitoring.db.stores.sqldb.sql_store import SQLStoreBase
69
62
 
70
- return SQLModelEndpointStore(
63
+ return SQLStoreBase(
71
64
  project=project,
72
- sql_connection_string=endpoint_store_connection,
73
65
  secret_provider=secret_provider,
74
66
  )
75
67
 
@@ -88,7 +80,24 @@ def get_model_endpoint_store(
88
80
  project: str,
89
81
  access_key: str = None,
90
82
  secret_provider: typing.Callable = None,
91
- ) -> ModelEndpointStore:
83
+ ) -> StoreBase:
84
+ # Leaving here for backwards compatibility
85
+ warnings.warn(
86
+ "The 'get_model_endpoint_store' function is deprecated and will be removed in 1.9.0. "
87
+ "Please use `get_store_object` instead.",
88
+ # TODO: remove in 1.9.0
89
+ FutureWarning,
90
+ )
91
+ return get_store_object(
92
+ project=project, access_key=access_key, secret_provider=secret_provider
93
+ )
94
+
95
+
96
+ def get_store_object(
97
+ project: str,
98
+ access_key: str = None,
99
+ secret_provider: typing.Callable = None,
100
+ ) -> StoreBase:
92
101
  """
93
102
  Getting the DB target type based on mlrun.config.model_endpoint_monitoring.store_type.
94
103
 
@@ -96,16 +105,14 @@ def get_model_endpoint_store(
96
105
  :param access_key: Access key with permission to the DB table.
97
106
  :param secret_provider: An optional secret provider to get the connection string secret.
98
107
 
99
- :return: `ModelEndpointStore` object. Using this object, the user can apply different operations on the
100
- model endpoint record such as write, update, get and delete.
108
+ :return: `StoreBase` object. Using this object, the user can apply different operations on the
109
+ model monitoring record such as write, update, get and delete a model endpoint.
101
110
  """
102
111
 
103
- # Get store type value from ModelEndpointStoreType enum class
104
- model_endpoint_store_type = ModelEndpointStoreType(
105
- mlrun.mlconf.model_endpoint_monitoring.store_type
106
- )
112
+ # Get store type value from ObjectStoreFactory enum class
113
+ store_type = ObjectStoreFactory(mlrun.mlconf.model_endpoint_monitoring.store_type)
107
114
 
108
- # Convert into model endpoint store target object
109
- return model_endpoint_store_type.to_endpoint_store(
115
+ # Convert into store target object
116
+ return store_type.to_object_store(
110
117
  project=project, access_key=access_key, secret_provider=secret_provider
111
118
  )
@@ -0,0 +1,15 @@
1
+ # Copyright 2024 Iguazio
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from .store import StoreBase
@@ -1,4 +1,4 @@
1
- # Copyright 2023 Iguazio
1
+ # Copyright 2024 Iguazio
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -11,22 +11,23 @@
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
- #
15
14
 
16
15
  import typing
17
16
  from abc import ABC, abstractmethod
18
17
 
18
+ import mlrun.common.schemas.model_monitoring as mm_schemas
19
+
19
20
 
20
- class ModelEndpointStore(ABC):
21
+ class StoreBase(ABC):
21
22
  """
22
- An abstract class to handle the model endpoint in the DB target.
23
+ An abstract class to handle the store object in the DB target.
23
24
  """
24
25
 
25
26
  def __init__(self, project: str):
26
27
  """
27
- Initialize a new model endpoint target.
28
+ Initialize a new store target.
28
29
 
29
- :param project: The name of the project.
30
+ :param project: The name of the project.
30
31
  """
31
32
  self.project = project
32
33
 
@@ -63,12 +64,10 @@ class ModelEndpointStore(ABC):
63
64
  pass
64
65
 
65
66
  @abstractmethod
66
- def delete_model_endpoints_resources(self, endpoints: list[dict[str, typing.Any]]):
67
+ def delete_model_endpoints_resources(self):
67
68
  """
68
69
  Delete all model endpoints resources.
69
70
 
70
- :param endpoints: A list of model endpoints flattened dictionaries.
71
-
72
71
  """
73
72
  pass
74
73
 
@@ -113,33 +112,60 @@ class ModelEndpointStore(ABC):
113
112
  pass
114
113
 
115
114
  @abstractmethod
116
- def get_endpoint_real_time_metrics(
115
+ def write_application_event(
117
116
  self,
118
- endpoint_id: str,
119
- metrics: list[str],
120
- start: str = "now-1h",
121
- end: str = "now",
122
- access_key: str = None,
123
- ) -> dict[str, list[tuple[str, float]]]:
117
+ event: dict[str, typing.Any],
118
+ kind: mm_schemas.WriterEventKind = mm_schemas.WriterEventKind.RESULT,
119
+ ) -> None:
124
120
  """
125
- Getting metrics from the time series DB. There are pre-defined metrics for model endpoints such as
126
- `predictions_per_second` and `latency_avg_5m` but also custom metrics defined by the user.
121
+ Write a new event in the target table.
122
+
123
+ :param event: An event dictionary that represents the application result, should be corresponded to the
124
+ schema defined in the :py:class:`~mlrun.common.schemas.model_monitoring.constants.WriterEvent`
125
+ object.
126
+ :param kind: The type of the event, can be either "result" or "metric".
127
+ """
128
+
129
+ @abstractmethod
130
+ def get_last_analyzed(self, endpoint_id: str, application_name: str) -> int:
131
+ """
132
+ Get the last analyzed time for the provided model endpoint and application.
127
133
 
128
134
  :param endpoint_id: The unique id of the model endpoint.
129
- :param metrics: A list of real-time metrics to return for the model endpoint.
130
- :param start: The start time of the metrics. Can be represented by a string containing an RFC 3339
131
- time, a Unix timestamp in milliseconds, a relative time (`'now'` or
132
- `'now-[0-9]+[mhd]'`, where `m` = minutes, `h` = hours, and `'d'` = days), or 0 for the
133
- earliest time.
134
- :param end: The end time of the metrics. Can be represented by a string containing an RFC 3339
135
- time, a Unix timestamp in milliseconds, a relative time (`'now'` or
136
- `'now-[0-9]+[mhd]'`, where `m` = minutes, `h` = hours, and `'d'` = days), or 0 for the
137
- earliest time.
138
- :param access_key: V3IO access key that will be used for generating Frames client object. If not
139
- provided, the access key will be retrieved from the environment variables.
140
-
141
- :return: A dictionary of metrics in which the key is a metric name and the value is a list of tuples that
142
- includes timestamps and the values.
135
+ :param application_name: Registered application name.
136
+
137
+ :return: Timestamp as a Unix time.
138
+ :raise: MLRunNotFoundError if last analyzed value is not found.
139
+ """
140
+ pass
141
+
142
+ @abstractmethod
143
+ def update_last_analyzed(
144
+ self,
145
+ endpoint_id: str,
146
+ application_name: str,
147
+ last_analyzed: int,
148
+ ):
143
149
  """
150
+ Update the last analyzed time for the provided model endpoint and application.
144
151
 
152
+ :param endpoint_id: The unique id of the model endpoint.
153
+ :param application_name: Registered application name.
154
+ :param last_analyzed: Timestamp as a Unix time that represents the last analyzed time of a certain
155
+ application and model endpoint.
156
+
157
+ """
145
158
  pass
159
+
160
+ @abstractmethod
161
+ def get_model_endpoint_metrics(
162
+ self, endpoint_id: str, type: mm_schemas.ModelEndpointMonitoringMetricType
163
+ ) -> list[mm_schemas.ModelEndpointMonitoringMetric]:
164
+ """
165
+ Get the model monitoring results and metrics of the requested model endpoint.
166
+
167
+ :param: endpoint_id: The model endpoint identifier.
168
+ :param: type: The type of the requested metrics ("result" or "metric").
169
+
170
+ :return: A list of the available metrics.
171
+ """
@@ -0,0 +1,13 @@
1
+ # Copyright 2024 Iguazio
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
@@ -0,0 +1,71 @@
1
+ # Copyright 2023 Iguazio
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from functools import partial
16
+ from typing import Optional, TypeVar, Union
17
+
18
+ from .mysql import ApplicationMetricsTable as MySQLApplicationMetricsTable
19
+ from .mysql import ApplicationResultTable as MySQLApplicationResultTable
20
+ from .mysql import ModelEndpointsTable as MySQLModelEndpointsTable
21
+ from .mysql import MonitoringSchedulesTable as MySQLMonitoringSchedulesTable
22
+ from .sqlite import ApplicationMetricsTable as SQLiteApplicationMetricsTable
23
+ from .sqlite import ApplicationResultTable as SQLiteApplicationResultTable
24
+ from .sqlite import ModelEndpointsTable as SQLiteModelEndpointsTable
25
+ from .sqlite import MonitoringSchedulesTable as SQLiteMonitoringSchedulesTable
26
+
27
+ MySQLTableType = TypeVar("MySQLTableType")
28
+ SQLiteTableType = TypeVar("SQLiteTableType")
29
+
30
+ _MYSQL_SCHEME = "mysql:"
31
+
32
+
33
+ def _get_sql_table(
34
+ *,
35
+ mysql_table: MySQLTableType,
36
+ sqlite_table: SQLiteTableType,
37
+ connection_string: Optional[str] = None,
38
+ ) -> Union[MySQLTableType, SQLiteTableType]:
39
+ """
40
+ Return a SQLAlchemy table for MySQL or SQLite according to the connection string.
41
+ Note: this function should not be directly used in other modules.
42
+ """
43
+ if connection_string and _MYSQL_SCHEME in connection_string:
44
+ return mysql_table
45
+ return sqlite_table
46
+
47
+
48
+ _get_model_endpoints_table = partial(
49
+ _get_sql_table,
50
+ mysql_table=MySQLModelEndpointsTable,
51
+ sqlite_table=SQLiteModelEndpointsTable,
52
+ )
53
+
54
+
55
+ _get_application_result_table = partial(
56
+ _get_sql_table,
57
+ mysql_table=MySQLApplicationResultTable,
58
+ sqlite_table=SQLiteApplicationResultTable,
59
+ )
60
+
61
+ _get_application_metrics_table = partial(
62
+ _get_sql_table,
63
+ mysql_table=MySQLApplicationMetricsTable,
64
+ sqlite_table=SQLiteApplicationMetricsTable,
65
+ )
66
+
67
+ _get_monitoring_schedules_table = partial(
68
+ _get_sql_table,
69
+ mysql_table=MySQLMonitoringSchedulesTable,
70
+ sqlite_table=SQLiteMonitoringSchedulesTable,
71
+ )