mlrun 1.7.2rc3__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.
- mlrun/__init__.py +14 -12
- mlrun/__main__.py +3 -3
- mlrun/alerts/alert.py +19 -12
- mlrun/artifacts/__init__.py +0 -2
- mlrun/artifacts/base.py +34 -11
- mlrun/artifacts/dataset.py +16 -16
- mlrun/artifacts/manager.py +13 -13
- mlrun/artifacts/model.py +66 -53
- mlrun/common/constants.py +6 -0
- mlrun/common/formatters/__init__.py +1 -0
- mlrun/common/formatters/feature_set.py +1 -0
- mlrun/common/formatters/function.py +1 -0
- mlrun/common/formatters/model_endpoint.py +30 -0
- mlrun/common/formatters/pipeline.py +1 -2
- mlrun/common/model_monitoring/__init__.py +0 -3
- mlrun/common/model_monitoring/helpers.py +1 -1
- mlrun/common/runtimes/constants.py +1 -2
- mlrun/common/schemas/__init__.py +4 -2
- mlrun/common/schemas/artifact.py +0 -6
- mlrun/common/schemas/common.py +50 -0
- mlrun/common/schemas/model_monitoring/__init__.py +8 -1
- mlrun/common/schemas/model_monitoring/constants.py +62 -12
- mlrun/common/schemas/model_monitoring/model_endpoint_v2.py +149 -0
- mlrun/common/schemas/model_monitoring/model_endpoints.py +21 -5
- mlrun/common/schemas/partition.py +122 -0
- mlrun/config.py +43 -15
- mlrun/data_types/__init__.py +0 -2
- mlrun/data_types/data_types.py +0 -1
- mlrun/data_types/infer.py +3 -1
- mlrun/data_types/spark.py +4 -4
- mlrun/data_types/to_pandas.py +2 -11
- mlrun/datastore/__init__.py +0 -2
- mlrun/datastore/alibaba_oss.py +4 -1
- mlrun/datastore/azure_blob.py +4 -1
- mlrun/datastore/base.py +12 -4
- mlrun/datastore/datastore.py +9 -3
- mlrun/datastore/datastore_profile.py +1 -1
- mlrun/datastore/dbfs_store.py +4 -1
- mlrun/datastore/filestore.py +4 -1
- mlrun/datastore/google_cloud_storage.py +4 -1
- mlrun/datastore/hdfs.py +4 -1
- mlrun/datastore/inmem.py +4 -1
- mlrun/datastore/redis.py +4 -1
- mlrun/datastore/s3.py +4 -1
- mlrun/datastore/sources.py +51 -49
- mlrun/datastore/store_resources.py +0 -2
- mlrun/datastore/targets.py +22 -23
- mlrun/datastore/utils.py +2 -2
- mlrun/datastore/v3io.py +4 -1
- mlrun/datastore/wasbfs/fs.py +13 -12
- mlrun/db/base.py +126 -62
- mlrun/db/factory.py +3 -0
- mlrun/db/httpdb.py +767 -231
- mlrun/db/nopdb.py +126 -57
- mlrun/errors.py +2 -2
- mlrun/execution.py +55 -29
- mlrun/feature_store/__init__.py +0 -2
- mlrun/feature_store/api.py +40 -40
- mlrun/feature_store/common.py +9 -9
- mlrun/feature_store/feature_set.py +20 -18
- mlrun/feature_store/feature_vector.py +27 -24
- mlrun/feature_store/retrieval/base.py +14 -9
- mlrun/feature_store/retrieval/job.py +2 -1
- mlrun/feature_store/steps.py +2 -2
- mlrun/features.py +30 -13
- mlrun/frameworks/__init__.py +1 -2
- mlrun/frameworks/_common/__init__.py +1 -2
- mlrun/frameworks/_common/artifacts_library.py +2 -2
- mlrun/frameworks/_common/mlrun_interface.py +10 -6
- mlrun/frameworks/_common/model_handler.py +29 -27
- mlrun/frameworks/_common/producer.py +3 -1
- mlrun/frameworks/_dl_common/__init__.py +1 -2
- mlrun/frameworks/_dl_common/loggers/__init__.py +1 -2
- mlrun/frameworks/_dl_common/loggers/mlrun_logger.py +4 -4
- mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +3 -3
- mlrun/frameworks/_ml_common/__init__.py +1 -2
- mlrun/frameworks/_ml_common/loggers/__init__.py +1 -2
- mlrun/frameworks/_ml_common/model_handler.py +21 -21
- mlrun/frameworks/_ml_common/plans/__init__.py +1 -2
- mlrun/frameworks/_ml_common/plans/confusion_matrix_plan.py +3 -1
- mlrun/frameworks/_ml_common/plans/dataset_plan.py +3 -3
- mlrun/frameworks/_ml_common/plans/roc_curve_plan.py +4 -4
- mlrun/frameworks/auto_mlrun/__init__.py +1 -2
- mlrun/frameworks/auto_mlrun/auto_mlrun.py +22 -15
- mlrun/frameworks/huggingface/__init__.py +1 -2
- mlrun/frameworks/huggingface/model_server.py +9 -9
- mlrun/frameworks/lgbm/__init__.py +47 -44
- mlrun/frameworks/lgbm/callbacks/__init__.py +1 -2
- mlrun/frameworks/lgbm/callbacks/logging_callback.py +4 -2
- mlrun/frameworks/lgbm/callbacks/mlrun_logging_callback.py +4 -2
- mlrun/frameworks/lgbm/mlrun_interfaces/__init__.py +1 -2
- mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +5 -5
- mlrun/frameworks/lgbm/model_handler.py +15 -11
- mlrun/frameworks/lgbm/model_server.py +11 -7
- mlrun/frameworks/lgbm/utils.py +2 -2
- mlrun/frameworks/onnx/__init__.py +1 -2
- mlrun/frameworks/onnx/dataset.py +3 -3
- mlrun/frameworks/onnx/mlrun_interface.py +2 -2
- mlrun/frameworks/onnx/model_handler.py +7 -5
- mlrun/frameworks/onnx/model_server.py +8 -6
- mlrun/frameworks/parallel_coordinates.py +11 -11
- mlrun/frameworks/pytorch/__init__.py +22 -23
- mlrun/frameworks/pytorch/callbacks/__init__.py +1 -2
- mlrun/frameworks/pytorch/callbacks/callback.py +2 -1
- mlrun/frameworks/pytorch/callbacks/logging_callback.py +15 -8
- mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +19 -12
- mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +22 -15
- mlrun/frameworks/pytorch/callbacks_handler.py +36 -30
- mlrun/frameworks/pytorch/mlrun_interface.py +17 -17
- mlrun/frameworks/pytorch/model_handler.py +21 -17
- mlrun/frameworks/pytorch/model_server.py +13 -9
- mlrun/frameworks/sklearn/__init__.py +19 -18
- mlrun/frameworks/sklearn/estimator.py +2 -2
- mlrun/frameworks/sklearn/metric.py +3 -3
- mlrun/frameworks/sklearn/metrics_library.py +8 -6
- mlrun/frameworks/sklearn/mlrun_interface.py +3 -2
- mlrun/frameworks/sklearn/model_handler.py +4 -3
- mlrun/frameworks/tf_keras/__init__.py +11 -12
- mlrun/frameworks/tf_keras/callbacks/__init__.py +1 -2
- mlrun/frameworks/tf_keras/callbacks/logging_callback.py +17 -14
- mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +15 -12
- mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +21 -18
- mlrun/frameworks/tf_keras/model_handler.py +17 -13
- mlrun/frameworks/tf_keras/model_server.py +12 -8
- mlrun/frameworks/xgboost/__init__.py +19 -18
- mlrun/frameworks/xgboost/model_handler.py +13 -9
- mlrun/launcher/base.py +3 -4
- mlrun/launcher/local.py +1 -1
- mlrun/launcher/remote.py +1 -1
- mlrun/lists.py +4 -3
- mlrun/model.py +108 -44
- mlrun/model_monitoring/__init__.py +1 -2
- mlrun/model_monitoring/api.py +6 -6
- mlrun/model_monitoring/applications/_application_steps.py +13 -15
- mlrun/model_monitoring/applications/histogram_data_drift.py +41 -15
- mlrun/model_monitoring/applications/results.py +55 -3
- mlrun/model_monitoring/controller.py +185 -223
- mlrun/model_monitoring/db/_schedules.py +156 -0
- mlrun/model_monitoring/db/_stats.py +189 -0
- mlrun/model_monitoring/db/stores/__init__.py +1 -1
- mlrun/model_monitoring/db/stores/base/store.py +6 -65
- mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +0 -25
- mlrun/model_monitoring/db/stores/sqldb/models/base.py +0 -97
- mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +2 -58
- mlrun/model_monitoring/db/stores/sqldb/models/sqlite.py +0 -15
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +6 -257
- mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +9 -271
- mlrun/model_monitoring/db/tsdb/base.py +74 -22
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +66 -35
- mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +33 -0
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +284 -51
- mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +1 -0
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +35 -17
- mlrun/model_monitoring/helpers.py +97 -1
- mlrun/model_monitoring/model_endpoint.py +4 -2
- mlrun/model_monitoring/stream_processing.py +2 -2
- mlrun/model_monitoring/tracking_policy.py +10 -3
- mlrun/model_monitoring/writer.py +47 -26
- mlrun/package/__init__.py +3 -6
- mlrun/package/context_handler.py +1 -1
- mlrun/package/packager.py +12 -9
- mlrun/package/packagers/__init__.py +0 -2
- mlrun/package/packagers/default_packager.py +14 -11
- mlrun/package/packagers/numpy_packagers.py +16 -7
- mlrun/package/packagers/pandas_packagers.py +18 -18
- mlrun/package/packagers/python_standard_library_packagers.py +25 -11
- mlrun/package/packagers_manager.py +31 -14
- mlrun/package/utils/__init__.py +0 -3
- mlrun/package/utils/_pickler.py +6 -6
- mlrun/platforms/__init__.py +3 -3
- mlrun/platforms/iguazio.py +4 -1
- mlrun/projects/__init__.py +1 -6
- mlrun/projects/operations.py +27 -27
- mlrun/projects/pipelines.py +85 -215
- mlrun/projects/project.py +444 -158
- mlrun/run.py +9 -9
- mlrun/runtimes/__init__.py +1 -3
- mlrun/runtimes/base.py +13 -10
- mlrun/runtimes/daskjob.py +9 -9
- mlrun/runtimes/generators.py +2 -1
- mlrun/runtimes/kubejob.py +4 -5
- mlrun/runtimes/mpijob/__init__.py +0 -2
- mlrun/runtimes/mpijob/abstract.py +7 -6
- mlrun/runtimes/nuclio/api_gateway.py +7 -7
- mlrun/runtimes/nuclio/application/application.py +11 -11
- mlrun/runtimes/nuclio/function.py +14 -13
- mlrun/runtimes/nuclio/serving.py +9 -9
- mlrun/runtimes/pod.py +74 -29
- mlrun/runtimes/remotesparkjob.py +3 -2
- mlrun/runtimes/sparkjob/__init__.py +0 -2
- mlrun/runtimes/sparkjob/spark3job.py +21 -11
- mlrun/runtimes/utils.py +6 -5
- mlrun/serving/merger.py +6 -4
- mlrun/serving/remote.py +18 -17
- mlrun/serving/routers.py +27 -27
- mlrun/serving/server.py +1 -1
- mlrun/serving/states.py +76 -71
- mlrun/serving/utils.py +13 -2
- mlrun/serving/v1_serving.py +3 -2
- mlrun/serving/v2_serving.py +4 -4
- mlrun/track/__init__.py +1 -1
- mlrun/track/tracker.py +2 -2
- mlrun/track/trackers/mlflow_tracker.py +6 -5
- mlrun/utils/async_http.py +1 -1
- mlrun/utils/helpers.py +72 -28
- mlrun/utils/logger.py +104 -2
- mlrun/utils/notifications/notification/base.py +23 -4
- mlrun/utils/notifications/notification/console.py +1 -1
- mlrun/utils/notifications/notification/git.py +6 -6
- mlrun/utils/notifications/notification/ipython.py +5 -4
- mlrun/utils/notifications/notification/slack.py +1 -1
- mlrun/utils/notifications/notification/webhook.py +13 -17
- mlrun/utils/notifications/notification_pusher.py +23 -19
- mlrun/utils/regex.py +1 -1
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0rc1.dist-info}/METADATA +186 -186
- mlrun-1.8.0rc1.dist-info/RECORD +356 -0
- {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0rc1.dist-info}/WHEEL +1 -1
- mlrun-1.7.2rc3.dist-info/RECORD +0 -351
- {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0rc1.dist-info}/LICENSE +0 -0
- {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0rc1.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.2rc3.dist-info → mlrun-1.8.0rc1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,156 @@
|
|
|
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
|
+
import json
|
|
16
|
+
from contextlib import AbstractContextManager
|
|
17
|
+
from types import TracebackType
|
|
18
|
+
from typing import Final, Optional
|
|
19
|
+
|
|
20
|
+
import botocore.exceptions
|
|
21
|
+
|
|
22
|
+
import mlrun.common.schemas
|
|
23
|
+
import mlrun.errors
|
|
24
|
+
import mlrun.model_monitoring.helpers
|
|
25
|
+
from mlrun.utils import logger
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ModelMonitoringSchedulesFile(AbstractContextManager):
|
|
29
|
+
DEFAULT_SCHEDULES: Final = {}
|
|
30
|
+
INITIAL_CONTENT = json.dumps(DEFAULT_SCHEDULES)
|
|
31
|
+
ENCODING = "utf-8"
|
|
32
|
+
|
|
33
|
+
def __init__(self, project: str, endpoint_id: str) -> None:
|
|
34
|
+
"""
|
|
35
|
+
Initialize applications monitoring schedules file object.
|
|
36
|
+
The JSON file stores a dictionary of registered application name as key and Unix timestamp as value.
|
|
37
|
+
When working with the schedules data, use this class as a context manager to read and write the data.
|
|
38
|
+
|
|
39
|
+
:param project: The project name.
|
|
40
|
+
:param endpoint_id: The endpoint ID.
|
|
41
|
+
"""
|
|
42
|
+
# `self._item` is the persistent version of the monitoring schedules.
|
|
43
|
+
self._item = mlrun.model_monitoring.helpers.get_monitoring_schedules_data(
|
|
44
|
+
project=project, endpoint_id=endpoint_id
|
|
45
|
+
)
|
|
46
|
+
self._path = self._item.url
|
|
47
|
+
self._fs = self._item.store.filesystem
|
|
48
|
+
# `self._schedules` is an in-memory copy of the DB for all the applications for
|
|
49
|
+
# the same model endpoint.
|
|
50
|
+
self._schedules: dict[str, int] = self.DEFAULT_SCHEDULES.copy()
|
|
51
|
+
# Does `self._schedules` hold the content of `self._item`?
|
|
52
|
+
self._open_schedules = False
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
def from_model_endpoint(
|
|
56
|
+
cls, model_endpoint: mlrun.common.schemas.ModelEndpoint
|
|
57
|
+
) -> "ModelMonitoringSchedulesFile":
|
|
58
|
+
return cls(
|
|
59
|
+
project=model_endpoint.metadata.project,
|
|
60
|
+
endpoint_id=model_endpoint.metadata.uid,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
def create(self) -> None:
|
|
64
|
+
"""Create a schedules file with initial content - an empty dictionary"""
|
|
65
|
+
logger.debug("Creating model monitoring schedules file", path=self._item.url)
|
|
66
|
+
self._item.put(self.INITIAL_CONTENT)
|
|
67
|
+
|
|
68
|
+
def delete(self) -> None:
|
|
69
|
+
"""Delete schedules file if it exists"""
|
|
70
|
+
if (
|
|
71
|
+
self._fs is None # In-memory store
|
|
72
|
+
or self._fs.exists(self._path)
|
|
73
|
+
):
|
|
74
|
+
logger.debug(
|
|
75
|
+
"Deleting model monitoring schedules file", path=self._item.url
|
|
76
|
+
)
|
|
77
|
+
self._item.delete()
|
|
78
|
+
else:
|
|
79
|
+
logger.debug(
|
|
80
|
+
"Model monitoring schedules file does not exist, nothing to delete",
|
|
81
|
+
path=self._item.url,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def _open(self) -> None:
|
|
85
|
+
try:
|
|
86
|
+
content = self._item.get()
|
|
87
|
+
except (
|
|
88
|
+
mlrun.errors.MLRunNotFoundError,
|
|
89
|
+
# Different errors are raised for S3 or local storage, see ML-8042
|
|
90
|
+
botocore.exceptions.ClientError,
|
|
91
|
+
FileNotFoundError,
|
|
92
|
+
) as err:
|
|
93
|
+
if (
|
|
94
|
+
isinstance(err, botocore.exceptions.ClientError)
|
|
95
|
+
# Add a log only to "NoSuchKey" errors codes - equivalent to `FileNotFoundError`
|
|
96
|
+
and err.response["Error"]["Code"] != "NoSuchKey"
|
|
97
|
+
):
|
|
98
|
+
raise
|
|
99
|
+
|
|
100
|
+
logger.exception(
|
|
101
|
+
"The schedules file was not found. It should have been created "
|
|
102
|
+
"as a part of the model endpoint's creation",
|
|
103
|
+
path=self._path,
|
|
104
|
+
)
|
|
105
|
+
raise
|
|
106
|
+
|
|
107
|
+
if isinstance(content, bytes):
|
|
108
|
+
content = content.decode(encoding=self.ENCODING)
|
|
109
|
+
self._schedules = json.loads(content)
|
|
110
|
+
self._open_schedules = True
|
|
111
|
+
|
|
112
|
+
def _close(self) -> None:
|
|
113
|
+
self._item.put(json.dumps(self._schedules))
|
|
114
|
+
self._schedules = self.DEFAULT_SCHEDULES
|
|
115
|
+
self._open_schedules = False
|
|
116
|
+
|
|
117
|
+
def __enter__(self) -> "ModelMonitoringSchedulesFile":
|
|
118
|
+
self._open()
|
|
119
|
+
return super().__enter__()
|
|
120
|
+
|
|
121
|
+
def __exit__(
|
|
122
|
+
self,
|
|
123
|
+
exc_type: Optional[type[BaseException]],
|
|
124
|
+
exc_value: Optional[BaseException],
|
|
125
|
+
traceback: Optional[TracebackType],
|
|
126
|
+
) -> Optional[bool]:
|
|
127
|
+
self._close()
|
|
128
|
+
|
|
129
|
+
def _check_open_schedules(self) -> None:
|
|
130
|
+
if not self._open_schedules:
|
|
131
|
+
raise mlrun.errors.MLRunValueError(
|
|
132
|
+
"Open the schedules file as a context manager first"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
def get_application_time(self, application: str) -> Optional[int]:
|
|
136
|
+
self._check_open_schedules()
|
|
137
|
+
return self._schedules.get(application)
|
|
138
|
+
|
|
139
|
+
def update_application_time(self, application: str, timestamp: int) -> None:
|
|
140
|
+
self._check_open_schedules()
|
|
141
|
+
self._schedules[application] = timestamp
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def delete_model_monitoring_schedules_folder(project: str) -> None:
|
|
145
|
+
"""Delete the model monitoring schedules folder of the project"""
|
|
146
|
+
folder = mlrun.model_monitoring.helpers._get_monitoring_schedules_folder_path(
|
|
147
|
+
project
|
|
148
|
+
)
|
|
149
|
+
fs = mlrun.datastore.store_manager.object(folder).store.filesystem
|
|
150
|
+
if fs and fs.exists(folder):
|
|
151
|
+
logger.debug("Deleting model monitoring schedules folder", folder=folder)
|
|
152
|
+
fs.rm(folder, recursive=True)
|
|
153
|
+
elif fs is None: # In-memory store
|
|
154
|
+
raise mlrun.errors.MLRunValueError(
|
|
155
|
+
"Cannot delete a folder without a file-system"
|
|
156
|
+
)
|
|
@@ -0,0 +1,189 @@
|
|
|
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
|
+
import abc
|
|
15
|
+
import json
|
|
16
|
+
from abc import abstractmethod
|
|
17
|
+
from datetime import datetime, timezone
|
|
18
|
+
from typing import cast
|
|
19
|
+
|
|
20
|
+
import botocore.exceptions
|
|
21
|
+
import fsspec
|
|
22
|
+
|
|
23
|
+
import mlrun.datastore.base
|
|
24
|
+
from mlrun.common.schemas.model_monitoring.constants import StatsKind
|
|
25
|
+
from mlrun.model_monitoring.helpers import (
|
|
26
|
+
get_monitoring_current_stats_data,
|
|
27
|
+
get_monitoring_drift_measures_data,
|
|
28
|
+
get_monitoring_stats_directory_path,
|
|
29
|
+
)
|
|
30
|
+
from mlrun.utils import logger
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ModelMonitoringStatsFile(abc.ABC):
|
|
34
|
+
"""
|
|
35
|
+
Abstract class
|
|
36
|
+
Initialize applications monitoring stats file object.
|
|
37
|
+
The JSON file stores a dictionary of registered application name as key and Unix timestamp as value.
|
|
38
|
+
When working with the schedules data, use this class as a context manager to read and write the data.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(self, item: mlrun.datastore.base.DataItem, file_type: str):
|
|
42
|
+
self._path = item.url
|
|
43
|
+
self._item = item
|
|
44
|
+
self._file_type = file_type
|
|
45
|
+
self._fs = cast(fsspec.AbstractFileSystem, self._item.store.filesystem)
|
|
46
|
+
|
|
47
|
+
def create(self) -> None:
|
|
48
|
+
"""Create a json file with initial content - an empty dictionary"""
|
|
49
|
+
logger.debug(
|
|
50
|
+
f"Creating model monitoring {self._file_type} file", path=self._item.url
|
|
51
|
+
)
|
|
52
|
+
self._item.put(
|
|
53
|
+
json.dumps(
|
|
54
|
+
{
|
|
55
|
+
"data": dict(),
|
|
56
|
+
"timestamp": mlrun.utils.datetime_now().isoformat(
|
|
57
|
+
sep=" ", timespec="microseconds"
|
|
58
|
+
),
|
|
59
|
+
}
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
def delete(self) -> None:
|
|
64
|
+
"""Delete json file if it exists"""
|
|
65
|
+
if self._fs.exists(self._path):
|
|
66
|
+
logger.debug(
|
|
67
|
+
f"Deleting model monitoring {self._file_type} file", path=self._item.url
|
|
68
|
+
)
|
|
69
|
+
self._item.delete()
|
|
70
|
+
else:
|
|
71
|
+
logger.debug(
|
|
72
|
+
f"Model monitoring {self._file_type} file does not exist, nothing to delete",
|
|
73
|
+
path=self._item.url,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def read(self) -> tuple[dict, datetime]:
|
|
77
|
+
"""
|
|
78
|
+
Read the stats data and timestamp saved in file
|
|
79
|
+
:return: tuple[dict, str] dictionary with stats data and timestamp saved in file
|
|
80
|
+
"""
|
|
81
|
+
try:
|
|
82
|
+
content = json.loads(self._item.get().decode())
|
|
83
|
+
timestamp = content.get("timestamp")
|
|
84
|
+
if timestamp is not None:
|
|
85
|
+
timestamp = datetime.fromisoformat(timestamp).astimezone(
|
|
86
|
+
tz=timezone.utc
|
|
87
|
+
)
|
|
88
|
+
return content.get("data"), timestamp
|
|
89
|
+
except (
|
|
90
|
+
mlrun.errors.MLRunNotFoundError,
|
|
91
|
+
# Different errors are raised for S3 or local storage, see ML-8042
|
|
92
|
+
botocore.exceptions.ClientError,
|
|
93
|
+
FileNotFoundError,
|
|
94
|
+
) as err:
|
|
95
|
+
if (
|
|
96
|
+
isinstance(err, botocore.exceptions.ClientError)
|
|
97
|
+
# Add a log only to "NoSuchKey" errors codes - equivalent to `FileNotFoundError`
|
|
98
|
+
and err.response["Error"]["Code"] != "NoSuchKey"
|
|
99
|
+
):
|
|
100
|
+
raise
|
|
101
|
+
|
|
102
|
+
logger.exception(
|
|
103
|
+
"The Stats file was not found. It should have been created "
|
|
104
|
+
"as a part of the model endpoint's creation",
|
|
105
|
+
path=self._path,
|
|
106
|
+
error=err,
|
|
107
|
+
)
|
|
108
|
+
raise
|
|
109
|
+
|
|
110
|
+
def write(self, stats: dict, timestamp: datetime) -> None:
|
|
111
|
+
"""
|
|
112
|
+
Write stats data to file overwrite the existing file
|
|
113
|
+
:param stats: dictionary with the stats data
|
|
114
|
+
:param timestamp: datetime object with the timestamp of last entry point for the stats calculation
|
|
115
|
+
"""
|
|
116
|
+
content = {
|
|
117
|
+
"data": stats,
|
|
118
|
+
"timestamp": timestamp.isoformat(sep=" ", timespec="microseconds"),
|
|
119
|
+
}
|
|
120
|
+
self._item.put(json.dumps(content))
|
|
121
|
+
|
|
122
|
+
@classmethod
|
|
123
|
+
@abstractmethod
|
|
124
|
+
def from_model_endpoint(
|
|
125
|
+
cls, model_endpoint: mlrun.common.schemas.ModelEndpoint
|
|
126
|
+
) -> "ModelMonitoringStatsFile":
|
|
127
|
+
"""
|
|
128
|
+
Return ModelMonitoringStatsFile child object using ModelEndpoint metadata
|
|
129
|
+
:param model_endpoint: The current model endpoint to get a stats object for
|
|
130
|
+
:return: ModelMonitoringStatsFile child object instance
|
|
131
|
+
"""
|
|
132
|
+
pass
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class ModelMonitoringCurrentStatsFile(ModelMonitoringStatsFile):
|
|
136
|
+
def __init__(self, project: str, endpoint_id: str) -> None:
|
|
137
|
+
"""
|
|
138
|
+
Initialize File object specific for current stats.
|
|
139
|
+
:param project: (str) Project name
|
|
140
|
+
:param endpoint_id: (str) Endpoint name
|
|
141
|
+
"""
|
|
142
|
+
super().__init__(
|
|
143
|
+
get_monitoring_current_stats_data(project, endpoint_id),
|
|
144
|
+
StatsKind.CURRENT_STATS.value,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
@classmethod
|
|
148
|
+
def from_model_endpoint(
|
|
149
|
+
cls, model_endpoint: mlrun.common.schemas.ModelEndpoint
|
|
150
|
+
) -> "ModelMonitoringCurrentStatsFile":
|
|
151
|
+
return cls(
|
|
152
|
+
project=model_endpoint.metadata.project,
|
|
153
|
+
endpoint_id=model_endpoint.metadata.uid,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class ModelMonitoringDriftMeasuresFile(ModelMonitoringStatsFile):
|
|
158
|
+
def __init__(self, project: str, endpoint_id: str) -> None:
|
|
159
|
+
"""
|
|
160
|
+
Initialize File object specific for drift measures.
|
|
161
|
+
:param project: (str) Project name
|
|
162
|
+
:param endpoint_id: (str) Endpoint name
|
|
163
|
+
"""
|
|
164
|
+
super().__init__(
|
|
165
|
+
get_monitoring_drift_measures_data(project, endpoint_id),
|
|
166
|
+
StatsKind.DRIFT_MEASURES.value,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
@classmethod
|
|
170
|
+
def from_model_endpoint(
|
|
171
|
+
cls, model_endpoint: mlrun.common.schemas.ModelEndpoint
|
|
172
|
+
) -> "ModelMonitoringDriftMeasuresFile":
|
|
173
|
+
return cls(
|
|
174
|
+
project=model_endpoint.metadata.project,
|
|
175
|
+
endpoint_id=model_endpoint.metadata.uid,
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def delete_model_monitoring_stats_folder(project: str) -> None:
|
|
180
|
+
"""Delete the model monitoring schedules folder of the project"""
|
|
181
|
+
folder = get_monitoring_stats_directory_path(project)
|
|
182
|
+
fs = mlrun.datastore.store_manager.object(folder).store.filesystem
|
|
183
|
+
if fs and fs.exists(folder):
|
|
184
|
+
logger.debug("Deleting model monitoring stats folder", folder=folder)
|
|
185
|
+
fs.rm(folder, recursive=True)
|
|
186
|
+
elif fs is None: # In-memory store
|
|
187
|
+
raise mlrun.errors.MLRunValueError(
|
|
188
|
+
"Cannot delete a folder without a file-system"
|
|
189
|
+
)
|
|
@@ -70,7 +70,7 @@ class ObjectStoreFactory(enum.Enum):
|
|
|
70
70
|
|
|
71
71
|
def get_model_endpoint_store(
|
|
72
72
|
project: str,
|
|
73
|
-
access_key: str = None,
|
|
73
|
+
access_key: typing.Optional[str] = None,
|
|
74
74
|
secret_provider: typing.Optional[typing.Callable[[str], str]] = None,
|
|
75
75
|
) -> StoreBase:
|
|
76
76
|
# Leaving here for backwards compatibility
|
|
@@ -90,12 +90,12 @@ class StoreBase(ABC):
|
|
|
90
90
|
@abstractmethod
|
|
91
91
|
def list_model_endpoints(
|
|
92
92
|
self,
|
|
93
|
-
model: str = None,
|
|
94
|
-
function: str = None,
|
|
95
|
-
labels: list[str] = None,
|
|
96
|
-
top_level: bool = None,
|
|
97
|
-
uids: list = None,
|
|
98
|
-
include_stats: bool = None,
|
|
93
|
+
model: typing.Optional[str] = None,
|
|
94
|
+
function: typing.Optional[str] = None,
|
|
95
|
+
labels: typing.Optional[list[str]] = None,
|
|
96
|
+
top_level: typing.Optional[bool] = None,
|
|
97
|
+
uids: typing.Optional[list] = None,
|
|
98
|
+
include_stats: typing.Optional[bool] = None,
|
|
99
99
|
) -> list[dict[str, typing.Any]]:
|
|
100
100
|
"""
|
|
101
101
|
Returns a list of model endpoint dictionaries, supports filtering by model, function, labels or top level.
|
|
@@ -115,65 +115,6 @@ class StoreBase(ABC):
|
|
|
115
115
|
"""
|
|
116
116
|
pass
|
|
117
117
|
|
|
118
|
-
@abstractmethod
|
|
119
|
-
def write_application_event(
|
|
120
|
-
self,
|
|
121
|
-
event: dict[str, typing.Any],
|
|
122
|
-
kind: mm_schemas.WriterEventKind = mm_schemas.WriterEventKind.RESULT,
|
|
123
|
-
) -> None:
|
|
124
|
-
"""
|
|
125
|
-
Write a new event in the target table.
|
|
126
|
-
|
|
127
|
-
:param event: An event dictionary that represents the application result, should be corresponded to the
|
|
128
|
-
schema defined in the :py:class:`~mlrun.common.schemas.model_monitoring.constants.WriterEvent`
|
|
129
|
-
object.
|
|
130
|
-
:param kind: The type of the event, can be either "result" or "metric".
|
|
131
|
-
"""
|
|
132
|
-
|
|
133
|
-
@abstractmethod
|
|
134
|
-
def get_last_analyzed(self, endpoint_id: str, application_name: str) -> int:
|
|
135
|
-
"""
|
|
136
|
-
Get the last analyzed time for the provided model endpoint and application.
|
|
137
|
-
|
|
138
|
-
:param endpoint_id: The unique id of the model endpoint.
|
|
139
|
-
:param application_name: Registered application name.
|
|
140
|
-
|
|
141
|
-
:return: Timestamp as a Unix time.
|
|
142
|
-
:raise: MLRunNotFoundError if last analyzed value is not found.
|
|
143
|
-
"""
|
|
144
|
-
pass
|
|
145
|
-
|
|
146
|
-
@abstractmethod
|
|
147
|
-
def update_last_analyzed(
|
|
148
|
-
self,
|
|
149
|
-
endpoint_id: str,
|
|
150
|
-
application_name: str,
|
|
151
|
-
last_analyzed: int,
|
|
152
|
-
):
|
|
153
|
-
"""
|
|
154
|
-
Update the last analyzed time for the provided model endpoint and application.
|
|
155
|
-
|
|
156
|
-
:param endpoint_id: The unique id of the model endpoint.
|
|
157
|
-
:param application_name: Registered application name.
|
|
158
|
-
:param last_analyzed: Timestamp as a Unix time that represents the last analyzed time of a certain
|
|
159
|
-
application and model endpoint.
|
|
160
|
-
|
|
161
|
-
"""
|
|
162
|
-
pass
|
|
163
|
-
|
|
164
|
-
@abstractmethod
|
|
165
|
-
def get_model_endpoint_metrics(
|
|
166
|
-
self, endpoint_id: str, type: mm_schemas.ModelEndpointMonitoringMetricType
|
|
167
|
-
) -> list[mm_schemas.ModelEndpointMonitoringMetric]:
|
|
168
|
-
"""
|
|
169
|
-
Get the model monitoring results and metrics of the requested model endpoint.
|
|
170
|
-
|
|
171
|
-
:param: endpoint_id: The model endpoint identifier.
|
|
172
|
-
:param: type: The type of the requested metrics ("result" or "metric").
|
|
173
|
-
|
|
174
|
-
:return: A list of the available metrics.
|
|
175
|
-
"""
|
|
176
|
-
|
|
177
118
|
@staticmethod
|
|
178
119
|
def _validate_labels(
|
|
179
120
|
endpoint_dict: dict,
|
|
@@ -15,14 +15,8 @@
|
|
|
15
15
|
from functools import partial
|
|
16
16
|
from typing import Optional, TypeVar, Union
|
|
17
17
|
|
|
18
|
-
from .mysql import ApplicationMetricsTable as MySQLApplicationMetricsTable
|
|
19
|
-
from .mysql import ApplicationResultTable as MySQLApplicationResultTable
|
|
20
18
|
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
19
|
from .sqlite import ModelEndpointsTable as SQLiteModelEndpointsTable
|
|
25
|
-
from .sqlite import MonitoringSchedulesTable as SQLiteMonitoringSchedulesTable
|
|
26
20
|
|
|
27
21
|
MySQLTableType = TypeVar("MySQLTableType")
|
|
28
22
|
SQLiteTableType = TypeVar("SQLiteTableType")
|
|
@@ -50,22 +44,3 @@ _get_model_endpoints_table = partial(
|
|
|
50
44
|
mysql_table=MySQLModelEndpointsTable,
|
|
51
45
|
sqlite_table=SQLiteModelEndpointsTable,
|
|
52
46
|
)
|
|
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
|
-
)
|
|
@@ -13,11 +13,9 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
from sqlalchemy import (
|
|
16
|
-
DATETIME,
|
|
17
16
|
TIMESTAMP, # TODO: migrate to DATETIME, see ML-6921
|
|
18
17
|
Boolean,
|
|
19
18
|
Column,
|
|
20
|
-
Float,
|
|
21
19
|
Integer,
|
|
22
20
|
String,
|
|
23
21
|
Text,
|
|
@@ -25,11 +23,6 @@ from sqlalchemy import (
|
|
|
25
23
|
|
|
26
24
|
from mlrun.common.schemas.model_monitoring import (
|
|
27
25
|
EventFieldType,
|
|
28
|
-
FileTargetKind,
|
|
29
|
-
MetricData,
|
|
30
|
-
ResultData,
|
|
31
|
-
SchedulingKeys,
|
|
32
|
-
WriterEvent,
|
|
33
26
|
)
|
|
34
27
|
from mlrun.utils.db import BaseModel
|
|
35
28
|
|
|
@@ -98,93 +91,3 @@ class ModelEndpointsBaseTable(BaseModel):
|
|
|
98
91
|
EventFieldType.LAST_REQUEST,
|
|
99
92
|
TIMESTAMP(timezone=True), # TODO: migrate to DATETIME, see ML-6921
|
|
100
93
|
)
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
class ApplicationResultBaseTable(BaseModel):
|
|
104
|
-
__tablename__ = FileTargetKind.APP_RESULTS
|
|
105
|
-
|
|
106
|
-
uid = Column(EventFieldType.UID, String(120), primary_key=True)
|
|
107
|
-
|
|
108
|
-
application_name = Column(
|
|
109
|
-
WriterEvent.APPLICATION_NAME,
|
|
110
|
-
String(40),
|
|
111
|
-
nullable=True,
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
endpoint_id = Column(
|
|
115
|
-
WriterEvent.ENDPOINT_ID,
|
|
116
|
-
String(40),
|
|
117
|
-
nullable=True,
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
result_name = Column(
|
|
121
|
-
ResultData.RESULT_NAME,
|
|
122
|
-
String(40),
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
start_infer_time = Column(
|
|
126
|
-
WriterEvent.START_INFER_TIME,
|
|
127
|
-
DATETIME(timezone=True),
|
|
128
|
-
)
|
|
129
|
-
end_infer_time = Column(
|
|
130
|
-
WriterEvent.END_INFER_TIME,
|
|
131
|
-
DATETIME(timezone=True),
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
result_status = Column(ResultData.RESULT_STATUS, String(10))
|
|
135
|
-
result_kind = Column(ResultData.RESULT_KIND, String(40))
|
|
136
|
-
result_value = Column(ResultData.RESULT_VALUE, Float)
|
|
137
|
-
result_extra_data = Column(ResultData.RESULT_EXTRA_DATA, Text)
|
|
138
|
-
current_stats = Column(ResultData.CURRENT_STATS, Text)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
class ApplicationMetricsBaseTable(BaseModel):
|
|
142
|
-
__tablename__ = FileTargetKind.APP_METRICS
|
|
143
|
-
|
|
144
|
-
uid = Column(EventFieldType.UID, String(120), primary_key=True)
|
|
145
|
-
application_name = Column(
|
|
146
|
-
WriterEvent.APPLICATION_NAME,
|
|
147
|
-
String(40),
|
|
148
|
-
nullable=True,
|
|
149
|
-
)
|
|
150
|
-
endpoint_id = Column(
|
|
151
|
-
WriterEvent.ENDPOINT_ID,
|
|
152
|
-
String(40),
|
|
153
|
-
nullable=True,
|
|
154
|
-
)
|
|
155
|
-
start_infer_time = Column(
|
|
156
|
-
WriterEvent.START_INFER_TIME,
|
|
157
|
-
DATETIME(timezone=True),
|
|
158
|
-
)
|
|
159
|
-
end_infer_time = Column(
|
|
160
|
-
WriterEvent.END_INFER_TIME,
|
|
161
|
-
DATETIME(timezone=True),
|
|
162
|
-
)
|
|
163
|
-
metric_name = Column(
|
|
164
|
-
MetricData.METRIC_NAME,
|
|
165
|
-
String(40),
|
|
166
|
-
)
|
|
167
|
-
metric_value = Column(MetricData.METRIC_VALUE, Float)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
class MonitoringSchedulesBaseTable(BaseModel):
|
|
171
|
-
__tablename__ = FileTargetKind.MONITORING_SCHEDULES
|
|
172
|
-
|
|
173
|
-
uid = Column(SchedulingKeys.UID, String(32), primary_key=True)
|
|
174
|
-
|
|
175
|
-
application_name = Column(
|
|
176
|
-
SchedulingKeys.APPLICATION_NAME,
|
|
177
|
-
String(40),
|
|
178
|
-
nullable=False,
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
endpoint_id = Column(
|
|
182
|
-
SchedulingKeys.ENDPOINT_ID,
|
|
183
|
-
String(40),
|
|
184
|
-
nullable=False,
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
last_analyzed = Column(
|
|
188
|
-
SchedulingKeys.LAST_ANALYZED,
|
|
189
|
-
Integer,
|
|
190
|
-
)
|
|
@@ -13,20 +13,15 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
import sqlalchemy.dialects.mysql
|
|
16
|
-
from sqlalchemy import Column
|
|
17
|
-
from sqlalchemy.ext.declarative import declarative_base
|
|
16
|
+
from sqlalchemy import Column
|
|
17
|
+
from sqlalchemy.ext.declarative import declarative_base
|
|
18
18
|
|
|
19
19
|
from mlrun.common.schemas.model_monitoring import (
|
|
20
20
|
EventFieldType,
|
|
21
|
-
ResultData,
|
|
22
|
-
WriterEvent,
|
|
23
21
|
)
|
|
24
22
|
|
|
25
23
|
from .base import (
|
|
26
|
-
ApplicationMetricsBaseTable,
|
|
27
|
-
ApplicationResultBaseTable,
|
|
28
24
|
ModelEndpointsBaseTable,
|
|
29
|
-
MonitoringSchedulesBaseTable,
|
|
30
25
|
)
|
|
31
26
|
|
|
32
27
|
Base = declarative_base()
|
|
@@ -50,54 +45,3 @@ class ModelEndpointsTable(Base, ModelEndpointsBaseTable):
|
|
|
50
45
|
# TODO: migrate to DATETIME, see ML-6921
|
|
51
46
|
sqlalchemy.dialects.mysql.TIMESTAMP(fsp=3, timezone=True),
|
|
52
47
|
)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
class _ApplicationResultOrMetric:
|
|
56
|
-
"""
|
|
57
|
-
This class sets common columns of `ApplicationResultTable` and `ApplicationMetricsTable`
|
|
58
|
-
to the correct values in MySQL.
|
|
59
|
-
Note: This class must come before the base tables in the inheritance order to override
|
|
60
|
-
the relevant columns.
|
|
61
|
-
"""
|
|
62
|
-
|
|
63
|
-
start_infer_time = Column(
|
|
64
|
-
WriterEvent.START_INFER_TIME,
|
|
65
|
-
sqlalchemy.dialects.mysql.DATETIME(fsp=3, timezone=True),
|
|
66
|
-
)
|
|
67
|
-
end_infer_time = Column(
|
|
68
|
-
WriterEvent.END_INFER_TIME,
|
|
69
|
-
sqlalchemy.dialects.mysql.DATETIME(fsp=3, timezone=True),
|
|
70
|
-
)
|
|
71
|
-
|
|
72
|
-
@declared_attr
|
|
73
|
-
def endpoint_id(self):
|
|
74
|
-
return Column(
|
|
75
|
-
String(40),
|
|
76
|
-
ForeignKey(f"{EventFieldType.MODEL_ENDPOINTS}.{EventFieldType.UID}"),
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
class ApplicationResultTable(
|
|
81
|
-
Base, _ApplicationResultOrMetric, ApplicationResultBaseTable
|
|
82
|
-
):
|
|
83
|
-
result_extra_data = Column(
|
|
84
|
-
ResultData.RESULT_EXTRA_DATA, sqlalchemy.dialects.mysql.MEDIUMTEXT
|
|
85
|
-
)
|
|
86
|
-
current_stats = Column(
|
|
87
|
-
ResultData.CURRENT_STATS, sqlalchemy.dialects.mysql.MEDIUMTEXT
|
|
88
|
-
)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
class ApplicationMetricsTable(
|
|
92
|
-
Base, _ApplicationResultOrMetric, ApplicationMetricsBaseTable
|
|
93
|
-
):
|
|
94
|
-
pass
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
class MonitoringSchedulesTable(Base, MonitoringSchedulesBaseTable):
|
|
98
|
-
@declared_attr
|
|
99
|
-
def endpoint_id(self):
|
|
100
|
-
return Column(
|
|
101
|
-
String(40),
|
|
102
|
-
ForeignKey(f"{EventFieldType.MODEL_ENDPOINTS}.{EventFieldType.UID}"),
|
|
103
|
-
)
|
|
@@ -15,10 +15,7 @@
|
|
|
15
15
|
from sqlalchemy.ext.declarative import declarative_base
|
|
16
16
|
|
|
17
17
|
from .base import (
|
|
18
|
-
ApplicationMetricsBaseTable,
|
|
19
|
-
ApplicationResultBaseTable,
|
|
20
18
|
ModelEndpointsBaseTable,
|
|
21
|
-
MonitoringSchedulesBaseTable,
|
|
22
19
|
)
|
|
23
20
|
|
|
24
21
|
Base = declarative_base()
|
|
@@ -26,15 +23,3 @@ Base = declarative_base()
|
|
|
26
23
|
|
|
27
24
|
class ModelEndpointsTable(Base, ModelEndpointsBaseTable):
|
|
28
25
|
pass
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class ApplicationResultTable(Base, ApplicationResultBaseTable):
|
|
32
|
-
pass
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class ApplicationMetricsTable(Base, ApplicationMetricsBaseTable):
|
|
36
|
-
pass
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class MonitoringSchedulesTable(Base, MonitoringSchedulesBaseTable):
|
|
40
|
-
pass
|