mlrun 1.7.2__py3-none-any.whl → 1.8.0rc1__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 (222) hide show
  1. mlrun/__init__.py +14 -12
  2. mlrun/__main__.py +3 -3
  3. mlrun/alerts/alert.py +19 -12
  4. mlrun/artifacts/__init__.py +0 -2
  5. mlrun/artifacts/base.py +34 -11
  6. mlrun/artifacts/dataset.py +16 -16
  7. mlrun/artifacts/manager.py +13 -13
  8. mlrun/artifacts/model.py +66 -53
  9. mlrun/common/constants.py +6 -0
  10. mlrun/common/formatters/__init__.py +1 -0
  11. mlrun/common/formatters/feature_set.py +1 -0
  12. mlrun/common/formatters/function.py +1 -0
  13. mlrun/common/formatters/model_endpoint.py +30 -0
  14. mlrun/common/formatters/pipeline.py +1 -2
  15. mlrun/common/model_monitoring/__init__.py +0 -3
  16. mlrun/common/model_monitoring/helpers.py +1 -1
  17. mlrun/common/runtimes/constants.py +1 -2
  18. mlrun/common/schemas/__init__.py +4 -2
  19. mlrun/common/schemas/artifact.py +0 -6
  20. mlrun/common/schemas/common.py +50 -0
  21. mlrun/common/schemas/model_monitoring/__init__.py +8 -1
  22. mlrun/common/schemas/model_monitoring/constants.py +62 -12
  23. mlrun/common/schemas/model_monitoring/model_endpoint_v2.py +149 -0
  24. mlrun/common/schemas/model_monitoring/model_endpoints.py +21 -5
  25. mlrun/common/schemas/partition.py +122 -0
  26. mlrun/config.py +43 -15
  27. mlrun/data_types/__init__.py +0 -2
  28. mlrun/data_types/data_types.py +0 -1
  29. mlrun/data_types/infer.py +3 -1
  30. mlrun/data_types/spark.py +4 -4
  31. mlrun/data_types/to_pandas.py +2 -11
  32. mlrun/datastore/__init__.py +0 -2
  33. mlrun/datastore/alibaba_oss.py +4 -1
  34. mlrun/datastore/azure_blob.py +4 -1
  35. mlrun/datastore/base.py +12 -4
  36. mlrun/datastore/datastore.py +9 -3
  37. mlrun/datastore/datastore_profile.py +1 -1
  38. mlrun/datastore/dbfs_store.py +4 -1
  39. mlrun/datastore/filestore.py +4 -1
  40. mlrun/datastore/google_cloud_storage.py +4 -1
  41. mlrun/datastore/hdfs.py +4 -1
  42. mlrun/datastore/inmem.py +4 -1
  43. mlrun/datastore/redis.py +4 -1
  44. mlrun/datastore/s3.py +4 -1
  45. mlrun/datastore/sources.py +51 -49
  46. mlrun/datastore/store_resources.py +0 -2
  47. mlrun/datastore/targets.py +22 -23
  48. mlrun/datastore/utils.py +2 -2
  49. mlrun/datastore/v3io.py +4 -1
  50. mlrun/datastore/wasbfs/fs.py +13 -12
  51. mlrun/db/base.py +126 -62
  52. mlrun/db/factory.py +3 -0
  53. mlrun/db/httpdb.py +767 -231
  54. mlrun/db/nopdb.py +126 -57
  55. mlrun/errors.py +2 -2
  56. mlrun/execution.py +55 -29
  57. mlrun/feature_store/__init__.py +0 -2
  58. mlrun/feature_store/api.py +40 -40
  59. mlrun/feature_store/common.py +9 -9
  60. mlrun/feature_store/feature_set.py +20 -18
  61. mlrun/feature_store/feature_vector.py +27 -24
  62. mlrun/feature_store/retrieval/base.py +14 -9
  63. mlrun/feature_store/retrieval/job.py +2 -1
  64. mlrun/feature_store/steps.py +2 -2
  65. mlrun/features.py +30 -13
  66. mlrun/frameworks/__init__.py +1 -2
  67. mlrun/frameworks/_common/__init__.py +1 -2
  68. mlrun/frameworks/_common/artifacts_library.py +2 -2
  69. mlrun/frameworks/_common/mlrun_interface.py +10 -6
  70. mlrun/frameworks/_common/model_handler.py +29 -27
  71. mlrun/frameworks/_common/producer.py +3 -1
  72. mlrun/frameworks/_dl_common/__init__.py +1 -2
  73. mlrun/frameworks/_dl_common/loggers/__init__.py +1 -2
  74. mlrun/frameworks/_dl_common/loggers/mlrun_logger.py +4 -4
  75. mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +3 -3
  76. mlrun/frameworks/_ml_common/__init__.py +1 -2
  77. mlrun/frameworks/_ml_common/loggers/__init__.py +1 -2
  78. mlrun/frameworks/_ml_common/model_handler.py +21 -21
  79. mlrun/frameworks/_ml_common/plans/__init__.py +1 -2
  80. mlrun/frameworks/_ml_common/plans/confusion_matrix_plan.py +3 -1
  81. mlrun/frameworks/_ml_common/plans/dataset_plan.py +3 -3
  82. mlrun/frameworks/_ml_common/plans/roc_curve_plan.py +4 -4
  83. mlrun/frameworks/auto_mlrun/__init__.py +1 -2
  84. mlrun/frameworks/auto_mlrun/auto_mlrun.py +22 -15
  85. mlrun/frameworks/huggingface/__init__.py +1 -2
  86. mlrun/frameworks/huggingface/model_server.py +9 -9
  87. mlrun/frameworks/lgbm/__init__.py +47 -44
  88. mlrun/frameworks/lgbm/callbacks/__init__.py +1 -2
  89. mlrun/frameworks/lgbm/callbacks/logging_callback.py +4 -2
  90. mlrun/frameworks/lgbm/callbacks/mlrun_logging_callback.py +4 -2
  91. mlrun/frameworks/lgbm/mlrun_interfaces/__init__.py +1 -2
  92. mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +5 -5
  93. mlrun/frameworks/lgbm/model_handler.py +15 -11
  94. mlrun/frameworks/lgbm/model_server.py +11 -7
  95. mlrun/frameworks/lgbm/utils.py +2 -2
  96. mlrun/frameworks/onnx/__init__.py +1 -2
  97. mlrun/frameworks/onnx/dataset.py +3 -3
  98. mlrun/frameworks/onnx/mlrun_interface.py +2 -2
  99. mlrun/frameworks/onnx/model_handler.py +7 -5
  100. mlrun/frameworks/onnx/model_server.py +8 -6
  101. mlrun/frameworks/parallel_coordinates.py +11 -11
  102. mlrun/frameworks/pytorch/__init__.py +22 -23
  103. mlrun/frameworks/pytorch/callbacks/__init__.py +1 -2
  104. mlrun/frameworks/pytorch/callbacks/callback.py +2 -1
  105. mlrun/frameworks/pytorch/callbacks/logging_callback.py +15 -8
  106. mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +19 -12
  107. mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +22 -15
  108. mlrun/frameworks/pytorch/callbacks_handler.py +36 -30
  109. mlrun/frameworks/pytorch/mlrun_interface.py +17 -17
  110. mlrun/frameworks/pytorch/model_handler.py +21 -17
  111. mlrun/frameworks/pytorch/model_server.py +13 -9
  112. mlrun/frameworks/sklearn/__init__.py +19 -18
  113. mlrun/frameworks/sklearn/estimator.py +2 -2
  114. mlrun/frameworks/sklearn/metric.py +3 -3
  115. mlrun/frameworks/sklearn/metrics_library.py +8 -6
  116. mlrun/frameworks/sklearn/mlrun_interface.py +3 -2
  117. mlrun/frameworks/sklearn/model_handler.py +4 -3
  118. mlrun/frameworks/tf_keras/__init__.py +11 -12
  119. mlrun/frameworks/tf_keras/callbacks/__init__.py +1 -2
  120. mlrun/frameworks/tf_keras/callbacks/logging_callback.py +17 -14
  121. mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +15 -12
  122. mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +21 -18
  123. mlrun/frameworks/tf_keras/model_handler.py +17 -13
  124. mlrun/frameworks/tf_keras/model_server.py +12 -8
  125. mlrun/frameworks/xgboost/__init__.py +19 -18
  126. mlrun/frameworks/xgboost/model_handler.py +13 -9
  127. mlrun/launcher/base.py +3 -4
  128. mlrun/launcher/local.py +1 -1
  129. mlrun/launcher/remote.py +1 -1
  130. mlrun/lists.py +4 -3
  131. mlrun/model.py +108 -44
  132. mlrun/model_monitoring/__init__.py +1 -2
  133. mlrun/model_monitoring/api.py +6 -6
  134. mlrun/model_monitoring/applications/_application_steps.py +13 -15
  135. mlrun/model_monitoring/applications/histogram_data_drift.py +41 -15
  136. mlrun/model_monitoring/applications/results.py +55 -3
  137. mlrun/model_monitoring/controller.py +185 -223
  138. mlrun/model_monitoring/db/_schedules.py +156 -0
  139. mlrun/model_monitoring/db/_stats.py +189 -0
  140. mlrun/model_monitoring/db/stores/__init__.py +1 -1
  141. mlrun/model_monitoring/db/stores/base/store.py +6 -65
  142. mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +0 -25
  143. mlrun/model_monitoring/db/stores/sqldb/models/base.py +0 -97
  144. mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +2 -58
  145. mlrun/model_monitoring/db/stores/sqldb/models/sqlite.py +0 -15
  146. mlrun/model_monitoring/db/stores/sqldb/sql_store.py +6 -257
  147. mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +9 -271
  148. mlrun/model_monitoring/db/tsdb/base.py +74 -22
  149. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +66 -35
  150. mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +33 -0
  151. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +284 -51
  152. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +1 -0
  153. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +35 -17
  154. mlrun/model_monitoring/helpers.py +97 -1
  155. mlrun/model_monitoring/model_endpoint.py +4 -2
  156. mlrun/model_monitoring/stream_processing.py +2 -2
  157. mlrun/model_monitoring/tracking_policy.py +10 -3
  158. mlrun/model_monitoring/writer.py +47 -26
  159. mlrun/package/__init__.py +3 -6
  160. mlrun/package/context_handler.py +1 -1
  161. mlrun/package/packager.py +12 -9
  162. mlrun/package/packagers/__init__.py +0 -2
  163. mlrun/package/packagers/default_packager.py +14 -11
  164. mlrun/package/packagers/numpy_packagers.py +16 -7
  165. mlrun/package/packagers/pandas_packagers.py +18 -18
  166. mlrun/package/packagers/python_standard_library_packagers.py +25 -11
  167. mlrun/package/packagers_manager.py +31 -14
  168. mlrun/package/utils/__init__.py +0 -3
  169. mlrun/package/utils/_pickler.py +6 -6
  170. mlrun/platforms/__init__.py +3 -3
  171. mlrun/platforms/iguazio.py +4 -1
  172. mlrun/projects/__init__.py +1 -6
  173. mlrun/projects/operations.py +27 -27
  174. mlrun/projects/pipelines.py +85 -215
  175. mlrun/projects/project.py +444 -158
  176. mlrun/run.py +9 -9
  177. mlrun/runtimes/__init__.py +1 -3
  178. mlrun/runtimes/base.py +13 -10
  179. mlrun/runtimes/daskjob.py +9 -9
  180. mlrun/runtimes/generators.py +2 -1
  181. mlrun/runtimes/kubejob.py +4 -5
  182. mlrun/runtimes/mpijob/__init__.py +0 -2
  183. mlrun/runtimes/mpijob/abstract.py +7 -6
  184. mlrun/runtimes/nuclio/api_gateway.py +7 -7
  185. mlrun/runtimes/nuclio/application/application.py +11 -11
  186. mlrun/runtimes/nuclio/function.py +14 -14
  187. mlrun/runtimes/nuclio/serving.py +9 -9
  188. mlrun/runtimes/pod.py +74 -29
  189. mlrun/runtimes/remotesparkjob.py +3 -2
  190. mlrun/runtimes/sparkjob/__init__.py +0 -2
  191. mlrun/runtimes/sparkjob/spark3job.py +21 -11
  192. mlrun/runtimes/utils.py +6 -5
  193. mlrun/serving/merger.py +6 -4
  194. mlrun/serving/remote.py +18 -17
  195. mlrun/serving/routers.py +27 -27
  196. mlrun/serving/server.py +1 -1
  197. mlrun/serving/states.py +76 -71
  198. mlrun/serving/utils.py +13 -2
  199. mlrun/serving/v1_serving.py +3 -2
  200. mlrun/serving/v2_serving.py +4 -4
  201. mlrun/track/__init__.py +1 -1
  202. mlrun/track/tracker.py +2 -2
  203. mlrun/track/trackers/mlflow_tracker.py +6 -5
  204. mlrun/utils/async_http.py +1 -1
  205. mlrun/utils/helpers.py +72 -28
  206. mlrun/utils/logger.py +104 -2
  207. mlrun/utils/notifications/notification/base.py +23 -4
  208. mlrun/utils/notifications/notification/console.py +1 -1
  209. mlrun/utils/notifications/notification/git.py +6 -6
  210. mlrun/utils/notifications/notification/ipython.py +5 -4
  211. mlrun/utils/notifications/notification/slack.py +1 -1
  212. mlrun/utils/notifications/notification/webhook.py +13 -17
  213. mlrun/utils/notifications/notification_pusher.py +23 -19
  214. mlrun/utils/regex.py +1 -1
  215. mlrun/utils/version/version.json +2 -2
  216. {mlrun-1.7.2.dist-info → mlrun-1.8.0rc1.dist-info}/METADATA +187 -199
  217. mlrun-1.8.0rc1.dist-info/RECORD +356 -0
  218. {mlrun-1.7.2.dist-info → mlrun-1.8.0rc1.dist-info}/WHEEL +1 -1
  219. mlrun-1.7.2.dist-info/RECORD +0 -351
  220. {mlrun-1.7.2.dist-info → mlrun-1.8.0rc1.dist-info}/LICENSE +0 -0
  221. {mlrun-1.7.2.dist-info → mlrun-1.8.0rc1.dist-info}/entry_points.txt +0 -0
  222. {mlrun-1.7.2.dist-info → mlrun-1.8.0rc1.dist-info}/top_level.txt +0 -0
@@ -15,11 +15,10 @@
15
15
 
16
16
  import typing
17
17
 
18
+ import mlrun.common.types
18
19
  import mlrun_pipelines.common.ops
19
20
  import mlrun_pipelines.models
20
21
 
21
- import mlrun.common.types
22
-
23
22
  from .base import ObjectFormat
24
23
 
25
24
 
@@ -11,8 +11,5 @@
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
-
16
- # flake8: noqa - this is until we take care of the F401 violations with respect to __all__ & sphinx
17
14
 
18
15
  from .helpers import create_model_endpoint_uid
@@ -65,7 +65,7 @@ def parse_model_endpoint_store_prefix(store_prefix: str):
65
65
 
66
66
 
67
67
  def parse_monitoring_stream_path(
68
- stream_uri: str, project: str, function_name: str = None
68
+ stream_uri: str, project: str, function_name: typing.Optional[str] = None
69
69
  ):
70
70
  if stream_uri.startswith("kafka://"):
71
71
  if "?topic" in stream_uri:
@@ -15,9 +15,8 @@
15
15
  import enum
16
16
  import typing
17
17
 
18
- import mlrun_pipelines.common.models
19
-
20
18
  import mlrun.common.constants as mlrun_constants
19
+ import mlrun_pipelines.common.models
21
20
 
22
21
 
23
22
  class PodPhases:
@@ -11,8 +11,6 @@
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
- # flake8: noqa - this is until we take care of the F401 violations with respect to __all__ & sphinx
16
14
 
17
15
  from .alert import (
18
16
  AlertActiveState,
@@ -149,6 +147,10 @@ from .model_monitoring import (
149
147
  ModelEndpointMetadata,
150
148
  ModelEndpointSpec,
151
149
  ModelEndpointStatus,
150
+ ModelEndpointV2,
151
+ ModelEndpointV2Metadata,
152
+ ModelEndpointV2Spec,
153
+ ModelEndpointV2Status,
152
154
  ModelMonitoringMode,
153
155
  ModelMonitoringStoreKinds,
154
156
  MonitoringFunctionNames,
@@ -47,12 +47,6 @@ class ArtifactCategories(mlrun.common.types.StrEnum):
47
47
  True,
48
48
  )
49
49
 
50
- @classmethod
51
- def from_kind(cls, kind: str) -> "ArtifactCategories":
52
- if kind in [cls.model.value, cls.dataset.value]:
53
- return cls(kind)
54
- return cls.other
55
-
56
50
 
57
51
  class ArtifactIdentifier(pydantic.BaseModel):
58
52
  # artifact kind
@@ -16,6 +16,8 @@ import typing
16
16
 
17
17
  import pydantic
18
18
 
19
+ import mlrun.errors
20
+
19
21
 
20
22
  class ImageBuilder(pydantic.BaseModel):
21
23
  functionSourceCode: typing.Optional[str] = None # noqa: N815
@@ -41,3 +43,51 @@ class ImageBuilder(pydantic.BaseModel):
41
43
 
42
44
  class Config:
43
45
  extra = pydantic.Extra.allow
46
+
47
+
48
+ class LabelsModel(pydantic.BaseModel):
49
+ """
50
+ This class accepts either a dictionary, a list, or a string for filtering by labels.
51
+
52
+ :param labels:
53
+ - If a dictionary is provided, it should be in the format {'label_name': 'value'}.
54
+ The values can also be `None`, which will result in the format 'label_name' (without a value).
55
+ This will be converted to a list of strings in the format 'label_name=value'.
56
+ - If a list is provided, all items must be strings. Each string can either
57
+ be a simple label name (e.g., 'label1') or a key-value pair in the format
58
+ 'label=value'.
59
+ - If a string is provided, it should be a comma-separated list of labels
60
+ (e.g., 'label1,label2').
61
+ - If no labels are specified, the default is an empty list.
62
+ """
63
+
64
+ labels: typing.Optional[
65
+ typing.Union[str, dict[str, typing.Optional[str]], list[str]]
66
+ ]
67
+
68
+ @pydantic.validator("labels")
69
+ @classmethod
70
+ def validate(cls, labels) -> list[str]:
71
+ if labels is None:
72
+ return []
73
+
74
+ # If labels is a string, split it by commas
75
+ if isinstance(labels, str):
76
+ return [label.strip() for label in labels.split(",") if label.strip()]
77
+
78
+ if isinstance(labels, list):
79
+ if not all(isinstance(item, str) for item in labels):
80
+ raise mlrun.errors.MLRunValueError(
81
+ "All items in the list must be strings."
82
+ )
83
+ return labels
84
+
85
+ if isinstance(labels, dict):
86
+ return [
87
+ f"{key}={value}" if value is not None else key
88
+ for key, value in labels.items()
89
+ ]
90
+
91
+ raise mlrun.errors.MLRunValueError(
92
+ "Invalid labels format. Must be a string, dictionary of strings, or a list of strings."
93
+ )
@@ -14,6 +14,7 @@
14
14
 
15
15
  from .constants import (
16
16
  V3IO_MODEL_MONITORING_DB,
17
+ ApplicationEvent,
17
18
  ControllerPolicy,
18
19
  DriftStatus,
19
20
  EndpointType,
@@ -26,6 +27,7 @@ from .constants import (
26
27
  FunctionURI,
27
28
  MetricData,
28
29
  ModelEndpointMonitoringMetricType,
30
+ ModelEndpointSchema,
29
31
  ModelEndpointTarget,
30
32
  ModelEndpointTargetSchemas,
31
33
  ModelMonitoringMode,
@@ -36,7 +38,6 @@ from .constants import (
36
38
  ResultData,
37
39
  ResultKindApp,
38
40
  ResultStatusApp,
39
- SchedulingKeys,
40
41
  SpecialApps,
41
42
  TDEngineSuperTables,
42
43
  TSDBTarget,
@@ -54,6 +55,12 @@ from .grafana import (
54
55
  GrafanaTable,
55
56
  GrafanaTimeSeriesTarget,
56
57
  )
58
+ from .model_endpoint_v2 import (
59
+ ModelEndpointV2,
60
+ ModelEndpointV2Metadata,
61
+ ModelEndpointV2Spec,
62
+ ModelEndpointV2Status,
63
+ )
57
64
  from .model_endpoints import (
58
65
  Features,
59
66
  FeatureValues,
@@ -29,6 +29,46 @@ class MonitoringStrEnum(StrEnum):
29
29
  return list(map(lambda c: c.value, cls))
30
30
 
31
31
 
32
+ class ModelEndpointSchema(MonitoringStrEnum):
33
+ # metadata
34
+ UID = "uid"
35
+ PROJECT = "project"
36
+ ENDPOINT_TYPE = "endpoint_type"
37
+ NAME = "name"
38
+ CREATED = "created"
39
+ UPDATED = "updated"
40
+ LABELS = "labels"
41
+
42
+ # spec
43
+ FUNCTION_NAME = "function_name"
44
+ FUNCTION_UID = "function_uid"
45
+ MODEL_NAME = "model_name"
46
+ MODEL_TAG = "model_tag"
47
+ MODEL_CLASS = "model_class"
48
+ MODEL_UID = "model_uid"
49
+ FEATURE_NAMES = "feature_names"
50
+ LABEL_NAMES = "label_names"
51
+
52
+ # status
53
+ STATE = "state"
54
+ MONITORING_MODE = "monitoring_mode"
55
+ MONITORING_FEATURE_SET_URI = "monitoring_feature_set_uri"
56
+ CHILDREN = "children"
57
+ CHILDREN_UIDS = "children_uids"
58
+ FIRST_REQUEST = "first_request"
59
+ FUNCTION_URI = "function_uri"
60
+ MODEL_URI = "model_uri"
61
+
62
+ # status - operative
63
+ LAST_REQUEST = "last_request"
64
+ DRIFT_STATUS = "drift_status"
65
+ AVG_LATENCY = "avg_latency"
66
+ ERROR_COUNT = "error_count"
67
+ FEATURE_STATS = "feature_stats"
68
+ CURRENT_STATS = "current_stats"
69
+ DRIFT_MEASURES = "drift_measures"
70
+
71
+
32
72
  class EventFieldType:
33
73
  FUNCTION_URI = "function_uri"
34
74
  FUNCTION = "function"
@@ -55,6 +95,8 @@ class EventFieldType:
55
95
  NAMED_PREDICTIONS = "named_predictions"
56
96
  ERROR_COUNT = "error_count"
57
97
  MODEL_ERROR = "model_error"
98
+ ERROR_TYPE = "error_type"
99
+ INFER_ERROR = "infer_error"
58
100
  ENTITIES = "entities"
59
101
  FIRST_REQUEST = "first_request"
60
102
  LAST_REQUEST = "last_request"
@@ -114,13 +156,14 @@ class WriterEvent(MonitoringStrEnum):
114
156
  ENDPOINT_ID = "endpoint_id"
115
157
  START_INFER_TIME = "start_infer_time"
116
158
  END_INFER_TIME = "end_infer_time"
117
- EVENT_KIND = "event_kind" # metric or result
159
+ EVENT_KIND = "event_kind" # metric or result or stats
118
160
  DATA = "data"
119
161
 
120
162
 
121
163
  class WriterEventKind(MonitoringStrEnum):
122
164
  METRIC = "metric"
123
165
  RESULT = "result"
166
+ STATS = "stats"
124
167
 
125
168
 
126
169
  class MetricData(MonitoringStrEnum):
@@ -134,7 +177,17 @@ class ResultData(MonitoringStrEnum):
134
177
  RESULT_KIND = "result_kind"
135
178
  RESULT_STATUS = "result_status"
136
179
  RESULT_EXTRA_DATA = "result_extra_data"
180
+
181
+
182
+ class StatsData(MonitoringStrEnum):
183
+ STATS_NAME = "stats_name"
184
+ STATS = "stats"
185
+ TIMESTAMP = "timestamp"
186
+
187
+
188
+ class StatsKind(MonitoringStrEnum):
137
189
  CURRENT_STATS = "current_stats"
190
+ DRIFT_MEASURES = "drift_measures"
138
191
 
139
192
 
140
193
  class EventLiveStats:
@@ -194,13 +247,6 @@ class ModelMonitoringStoreKinds:
194
247
  EVENTS = "events"
195
248
 
196
249
 
197
- class SchedulingKeys:
198
- LAST_ANALYZED = "last_analyzed"
199
- ENDPOINT_ID = "endpoint_id"
200
- APPLICATION_NAME = "application_name"
201
- UID = "uid"
202
-
203
-
204
250
  class FileTargetKind:
205
251
  ENDPOINTS = "endpoints"
206
252
  EVENTS = "events"
@@ -209,14 +255,13 @@ class FileTargetKind:
209
255
  PARQUET = "parquet"
210
256
  APPS_PARQUET = "apps_parquet"
211
257
  LOG_STREAM = "log_stream"
212
- APP_RESULTS = "app_results"
213
- APP_METRICS = "app_metrics"
214
258
  MONITORING_SCHEDULES = "monitoring_schedules"
215
259
  MONITORING_APPLICATION = "monitoring_application"
216
260
  ERRORS = "errors"
261
+ STATS = "stats"
217
262
 
218
263
 
219
- class ModelMonitoringMode(str, Enum):
264
+ class ModelMonitoringMode(StrEnum):
220
265
  enabled = "enabled"
221
266
  disabled = "disabled"
222
267
 
@@ -225,6 +270,11 @@ class EndpointType(IntEnum):
225
270
  NODE_EP = 1 # end point that is not a child of a router
226
271
  ROUTER = 2 # endpoint that is router
227
272
  LEAF_EP = 3 # end point that is a child of a router
273
+ BATCH_EP = 4 # endpoint that is representing an offline batch endpoint
274
+
275
+ @classmethod
276
+ def top_level_list(cls):
277
+ return [cls.NODE_EP, cls.ROUTER, cls.BATCH_EP]
228
278
 
229
279
 
230
280
  class MonitoringFunctionNames(MonitoringStrEnum):
@@ -244,6 +294,7 @@ class TDEngineSuperTables(MonitoringStrEnum):
244
294
  APP_RESULTS = "app_results"
245
295
  METRICS = "metrics"
246
296
  PREDICTIONS = "predictions"
297
+ ERRORS = "errors"
247
298
 
248
299
 
249
300
  @dataclass
@@ -364,7 +415,6 @@ class SpecialApps:
364
415
 
365
416
  _RESERVED_FUNCTION_NAMES = MonitoringFunctionNames.list() + [SpecialApps.MLRUN_INFRA]
366
417
 
367
-
368
418
  V3IO_MODEL_MONITORING_DB = "v3io"
369
419
 
370
420
 
@@ -0,0 +1,149 @@
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 abc
16
+ from typing import Optional
17
+
18
+ from pydantic import BaseModel, Field, constr
19
+
20
+ from ..object import ObjectKind, ObjectMetadata, ObjectSpec, ObjectStatus
21
+ from .constants import (
22
+ PROJECT_PATTERN,
23
+ EndpointType,
24
+ ModelMonitoringMode,
25
+ )
26
+ from .model_endpoints import _mapping_attributes
27
+
28
+ # TODO : replace ModelEndpoint
29
+
30
+
31
+ class ModelEndpointParser(abc.ABC, BaseModel):
32
+ @classmethod
33
+ def json_parse_values(cls) -> list[str]:
34
+ return []
35
+
36
+ @classmethod
37
+ def from_flat_dict(
38
+ cls, endpoint_dict: dict, json_parse_values: Optional[list] = None
39
+ ):
40
+ """Create a `ModelEndpointMetadata` object from an endpoint dictionary
41
+
42
+ :param endpoint_dict: Model endpoint dictionary.
43
+ :param json_parse_values: List of dictionary keys with a JSON string value that will be parsed into a
44
+ dictionary using json.loads().
45
+ """
46
+ if json_parse_values is None:
47
+ json_parse_values = cls.json_parse_values()
48
+
49
+ return _mapping_attributes(
50
+ model_class=cls,
51
+ flattened_dictionary=endpoint_dict,
52
+ json_parse_values=json_parse_values,
53
+ )
54
+
55
+
56
+ class ModelEndpointV2Metadata(ObjectMetadata, ModelEndpointParser):
57
+ project: constr(regex=PROJECT_PATTERN)
58
+ endpoint_type: Optional[EndpointType] = EndpointType.NODE_EP.value
59
+
60
+
61
+ class ModelEndpointV2Spec(ObjectSpec, ModelEndpointParser):
62
+ model_uid: Optional[str] = ""
63
+ model_name: Optional[str] = ""
64
+ model_tag: Optional[str] = ""
65
+ model_class: Optional[str] = ""
66
+ function_name: Optional[str] = ""
67
+ function_uid: Optional[str] = ""
68
+ feature_names: Optional[list[str]] = []
69
+ label_names: Optional[list[str]] = []
70
+
71
+
72
+ class ModelEndpointV2Status(ObjectStatus, ModelEndpointParser):
73
+ state: Optional[str] = "unknown" # will be updated according to the function state
74
+ first_request: Optional[str] = ""
75
+ children: Optional[list[str]] = []
76
+ children_uids: Optional[list[str]] = []
77
+ monitoring_feature_set_uri: Optional[str] = ""
78
+ monitoring_mode: Optional[ModelMonitoringMode] = ModelMonitoringMode.disabled.value
79
+ function_uri: Optional[str] = "" # <project_name>/<function_name>:<tag>
80
+ model_uri: Optional[str] = ""
81
+
82
+ # operative
83
+ last_request: Optional[str] = ""
84
+ drift_status: Optional[str] = ""
85
+ avg_latency: Optional[float] = None
86
+ error_count: Optional[int] = 0
87
+ feature_stats: Optional[dict] = {}
88
+ current_stats: Optional[dict] = {}
89
+ drift_measures: Optional[dict] = {}
90
+
91
+
92
+ class ModelEndpointV2(BaseModel):
93
+ kind: ObjectKind = Field(ObjectKind.model_endpoint, const=True)
94
+ metadata: ModelEndpointV2Metadata
95
+ spec: ModelEndpointV2Spec
96
+ status: ModelEndpointV2Status
97
+
98
+ def flat_dict(self, exclude: Optional[set] = None):
99
+ """Generate a flattened `ModelEndpoint` dictionary. The flattened dictionary result is important for storing
100
+ the model endpoint object in the database.
101
+
102
+ :return: Flattened `ModelEndpoint` dictionary.
103
+ """
104
+ # Convert the ModelEndpoint object into a dictionary using BaseModel dict() function
105
+ # In addition, remove the BaseModel kind as it is not required by the DB schema
106
+ if exclude:
107
+ exclude = exclude | {"kind", "tag"}
108
+ else:
109
+ exclude = {"kind", "tag"}
110
+ model_endpoint_dictionary = self.dict(exclude=exclude)
111
+
112
+ # Initialize a flattened dictionary that will be filled with the model endpoint dictionary attributes
113
+ flatten_dict = {}
114
+ for k_object in model_endpoint_dictionary:
115
+ for key in model_endpoint_dictionary[k_object]:
116
+ # Extract the value of the current field
117
+ flatten_dict[key] = model_endpoint_dictionary[k_object][key]
118
+
119
+ return flatten_dict
120
+
121
+ @classmethod
122
+ def from_flat_dict(cls, endpoint_dict: dict) -> "ModelEndpointV2":
123
+ """Create a `ModelEndpoint` object from an endpoint flattened dictionary. Because the provided dictionary
124
+ is flattened, we pass it as is to the subclasses without splitting the keys into spec, metadata, and status.
125
+
126
+ :param endpoint_dict: Model endpoint dictionary.
127
+ """
128
+
129
+ return cls(
130
+ metadata=ModelEndpointV2Metadata.from_flat_dict(
131
+ endpoint_dict=endpoint_dict
132
+ ),
133
+ spec=ModelEndpointV2Spec.from_flat_dict(endpoint_dict=endpoint_dict),
134
+ status=ModelEndpointV2Status.from_flat_dict(endpoint_dict=endpoint_dict),
135
+ )
136
+
137
+ @classmethod
138
+ def _operative_data(cls) -> set:
139
+ return {
140
+ "last_request",
141
+ "drift_status",
142
+ "avg_latency",
143
+ "error_count",
144
+ "feature_stats",
145
+ "current_stats",
146
+ "drift_measures",
147
+ "function_uri",
148
+ "model_uri",
149
+ }
@@ -56,7 +56,9 @@ class ModelEndpointMetadata(BaseModel):
56
56
  extra = Extra.allow
57
57
 
58
58
  @classmethod
59
- def from_flat_dict(cls, endpoint_dict: dict, json_parse_values: list = None):
59
+ def from_flat_dict(
60
+ cls, endpoint_dict: dict, json_parse_values: Optional[list] = None
61
+ ):
60
62
  """Create a `ModelEndpointMetadata` object from an endpoint dictionary
61
63
 
62
64
  :param endpoint_dict: Model endpoint dictionary.
@@ -87,7 +89,9 @@ class ModelEndpointSpec(ObjectSpec):
87
89
  monitoring_mode: Optional[ModelMonitoringMode] = ModelMonitoringMode.disabled.value
88
90
 
89
91
  @classmethod
90
- def from_flat_dict(cls, endpoint_dict: dict, json_parse_values: list = None):
92
+ def from_flat_dict(
93
+ cls, endpoint_dict: dict, json_parse_values: Optional[list] = None
94
+ ):
91
95
  """Create a `ModelEndpointSpec` object from an endpoint dictionary
92
96
 
93
97
  :param endpoint_dict: Model endpoint dictionary.
@@ -188,7 +192,9 @@ class ModelEndpointStatus(ObjectStatus):
188
192
  extra = Extra.allow
189
193
 
190
194
  @classmethod
191
- def from_flat_dict(cls, endpoint_dict: dict, json_parse_values: list = None):
195
+ def from_flat_dict(
196
+ cls, endpoint_dict: dict, json_parse_values: Optional[list] = None
197
+ ):
192
198
  """Create a `ModelEndpointStatus` object from an endpoint dictionary
193
199
 
194
200
  :param endpoint_dict: Model endpoint dictionary.
@@ -284,7 +290,14 @@ class ModelEndpointMonitoringMetric(BaseModel):
284
290
  app: str
285
291
  type: ModelEndpointMonitoringMetricType
286
292
  name: str
287
- full_name: str
293
+ full_name: Optional[str] = None
294
+ kind: Optional[ResultKindApp] = None
295
+
296
+ def __init__(self, **kwargs):
297
+ super().__init__(**kwargs)
298
+ self.full_name = _compose_full_name(
299
+ project=self.project, app=self.app, name=self.name, type=self.type
300
+ )
288
301
 
289
302
 
290
303
  def _compose_full_name(
@@ -315,6 +328,7 @@ class _ResultPoint(NamedTuple):
315
328
  timestamp: datetime
316
329
  value: float
317
330
  status: ResultStatusApp
331
+ extra_data: Optional[str] = ""
318
332
 
319
333
 
320
334
  class _ModelEndpointMonitoringMetricValuesBase(BaseModel):
@@ -365,8 +379,10 @@ def _mapping_attributes(
365
379
  dict_to_parse[field_key] = _json_loads_if_not_none(
366
380
  flattened_dictionary[field_key]
367
381
  )
368
- else:
382
+ elif flattened_dictionary[field_key] != "null":
369
383
  dict_to_parse[field_key] = flattened_dictionary[field_key]
384
+ else:
385
+ dict_to_parse[field_key] = None
370
386
 
371
387
  return model_class.parse_obj(dict_to_parse)
372
388
 
@@ -0,0 +1,122 @@
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 datetime import datetime, timedelta
16
+
17
+ from mlrun.common.types import StrEnum
18
+
19
+
20
+ class PartitionInterval(StrEnum):
21
+ DAY = "DAY"
22
+ MONTH = "MONTH"
23
+ YEARWEEK = "YEARWEEK"
24
+
25
+ @classmethod
26
+ def is_valid(cls, value: str) -> bool:
27
+ return value in cls._value2member_map_
28
+
29
+ @classmethod
30
+ def valid_intervals(cls) -> list:
31
+ return list(cls._value2member_map_.keys())
32
+
33
+ @classmethod
34
+ def from_function(cls, partition_function: str):
35
+ """
36
+ Returns the corresponding PartitionInterval for a given partition function,
37
+ or None if the function is not mapped.
38
+
39
+ :param partition_function: The partition function to map to an interval.
40
+ :return: PartitionInterval corresponding to the function, or None if no match is found.
41
+ """
42
+ partition_function_to_partitions_interval = {
43
+ "DAY": "DAY",
44
+ "DAYOFMONTH": "DAY",
45
+ "MONTH": "MONTH",
46
+ "YEARWEEK": "YEARWEEK",
47
+ }
48
+ interval = partition_function_to_partitions_interval.get(partition_function)
49
+ if interval and cls.is_valid(interval):
50
+ return cls[interval]
51
+ return None
52
+
53
+ def get_partition_info(
54
+ self,
55
+ start_datetime: datetime,
56
+ partition_number: int = 1,
57
+ ) -> list[tuple[str, str]]:
58
+ """
59
+ Generates partition details for a specified number of partitions starting from a given datetime.
60
+
61
+ :param start_datetime: The starting datetime used for generating partition details.
62
+ :param partition_number: The number of partitions to generate details for.
63
+
64
+ :return: A list of tuples:
65
+ - partition_name: The name for the partition.
66
+ - partition_value: The "LESS THAN" value for the next partition boundary.
67
+ """
68
+ partitioning_information_list = []
69
+ current_datetime = start_datetime
70
+
71
+ for _ in range(partition_number):
72
+ partition_name = self.get_partition_name(current_datetime)
73
+ partition_boundary_date = self.get_next_partition_time(current_datetime)
74
+ partition_value = self.get_partition_name(partition_boundary_date)
75
+ partitioning_information_list.append((partition_name, partition_value))
76
+
77
+ # Move to the next interval
78
+ current_datetime = partition_boundary_date
79
+
80
+ return partitioning_information_list
81
+
82
+ def get_next_partition_time(self, current_datetime: datetime) -> datetime:
83
+ """
84
+ Calculates the next partition boundary time based on the specified partition interval.
85
+ :param current_datetime: The current datetime from which the next interval is calculated.
86
+
87
+ :return: A datetime object representing the start of the next partition interval.
88
+ - If the interval is DAY, it advances by one day.
89
+ - If the interval is MONTH, it advances to the first day of the next month.
90
+ - If the interval is YEARWEEK, it advances by one week.
91
+ """
92
+ if self == PartitionInterval.DAY:
93
+ return current_datetime + timedelta(days=1)
94
+ elif self == PartitionInterval.MONTH:
95
+ return (current_datetime.replace(day=1) + timedelta(days=32)).replace(day=1)
96
+ elif self == PartitionInterval.YEARWEEK:
97
+ return current_datetime + timedelta(weeks=1)
98
+
99
+ def get_partition_name(self, current_datetime: datetime) -> str:
100
+ if self == PartitionInterval.DAY:
101
+ return current_datetime.strftime("%Y%m%d")
102
+ elif self == PartitionInterval.MONTH:
103
+ return current_datetime.strftime("%Y%m")
104
+ elif self == PartitionInterval.YEARWEEK:
105
+ year, week, _ = current_datetime.isocalendar()
106
+ return f"{year}{week:02d}"
107
+
108
+ def get_partition_expression(self):
109
+ if self == PartitionInterval.YEARWEEK:
110
+ return "YEARWEEK(activation_time, 1)"
111
+ else:
112
+ return f"{self}(activation_time)"
113
+
114
+ def get_number_of_partitions(self, days: int) -> int:
115
+ # Calculate the number partitions based on given number of days
116
+ if self == PartitionInterval.DAY:
117
+ return days
118
+ elif self == PartitionInterval.MONTH:
119
+ # Average number days in a month is 30.44
120
+ return int(days / 30.44)
121
+ elif self == PartitionInterval.YEARWEEK:
122
+ return int(days / 7)