mlrun 1.8.0rc5__py3-none-any.whl → 1.8.0rc9__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 +1 -0
- mlrun/artifacts/__init__.py +1 -1
- mlrun/artifacts/base.py +21 -1
- mlrun/artifacts/document.py +62 -39
- mlrun/artifacts/manager.py +12 -5
- mlrun/common/constants.py +1 -0
- mlrun/common/model_monitoring/__init__.py +0 -2
- mlrun/common/model_monitoring/helpers.py +0 -28
- mlrun/common/schemas/__init__.py +2 -4
- mlrun/common/schemas/alert.py +77 -1
- mlrun/common/schemas/client_spec.py +0 -1
- mlrun/common/schemas/model_monitoring/__init__.py +0 -6
- mlrun/common/schemas/model_monitoring/constants.py +11 -9
- mlrun/common/schemas/model_monitoring/model_endpoints.py +77 -149
- mlrun/common/schemas/notification.py +6 -0
- mlrun/common/schemas/project.py +3 -0
- mlrun/config.py +2 -3
- mlrun/datastore/datastore_profile.py +57 -17
- mlrun/datastore/sources.py +1 -2
- mlrun/datastore/store_resources.py +7 -2
- mlrun/datastore/vectorstore.py +99 -62
- mlrun/db/base.py +34 -20
- mlrun/db/httpdb.py +249 -163
- mlrun/db/nopdb.py +40 -17
- mlrun/execution.py +14 -7
- mlrun/feature_store/api.py +1 -0
- mlrun/model.py +3 -0
- mlrun/model_monitoring/__init__.py +3 -2
- mlrun/model_monitoring/api.py +64 -53
- mlrun/model_monitoring/applications/_application_steps.py +3 -1
- mlrun/model_monitoring/applications/base.py +115 -15
- mlrun/model_monitoring/applications/context.py +42 -24
- mlrun/model_monitoring/applications/histogram_data_drift.py +1 -1
- mlrun/model_monitoring/controller.py +43 -37
- mlrun/model_monitoring/db/__init__.py +0 -2
- mlrun/model_monitoring/db/tsdb/base.py +2 -1
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +2 -1
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +43 -0
- mlrun/model_monitoring/helpers.py +78 -66
- mlrun/model_monitoring/stream_processing.py +83 -270
- mlrun/model_monitoring/writer.py +1 -10
- mlrun/projects/pipelines.py +37 -1
- mlrun/projects/project.py +173 -70
- mlrun/run.py +40 -0
- mlrun/runtimes/nuclio/function.py +7 -6
- mlrun/runtimes/nuclio/serving.py +9 -4
- mlrun/serving/routers.py +158 -145
- mlrun/serving/server.py +6 -0
- mlrun/serving/states.py +21 -7
- mlrun/serving/v2_serving.py +94 -68
- mlrun/utils/helpers.py +23 -33
- mlrun/utils/notifications/notification/mail.py +17 -6
- mlrun/utils/notifications/notification_pusher.py +9 -5
- mlrun/utils/regex.py +8 -1
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.8.0rc5.dist-info → mlrun-1.8.0rc9.dist-info}/METADATA +2 -2
- {mlrun-1.8.0rc5.dist-info → mlrun-1.8.0rc9.dist-info}/RECORD +61 -74
- mlrun/common/schemas/model_monitoring/model_endpoint_v2.py +0 -149
- mlrun/model_monitoring/db/stores/__init__.py +0 -136
- mlrun/model_monitoring/db/stores/base/__init__.py +0 -15
- mlrun/model_monitoring/db/stores/base/store.py +0 -154
- mlrun/model_monitoring/db/stores/sqldb/__init__.py +0 -13
- mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +0 -46
- mlrun/model_monitoring/db/stores/sqldb/models/base.py +0 -93
- mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +0 -47
- mlrun/model_monitoring/db/stores/sqldb/models/sqlite.py +0 -25
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +0 -408
- mlrun/model_monitoring/db/stores/v3io_kv/__init__.py +0 -13
- mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +0 -464
- mlrun/model_monitoring/model_endpoint.py +0 -120
- {mlrun-1.8.0rc5.dist-info → mlrun-1.8.0rc9.dist-info}/LICENSE +0 -0
- {mlrun-1.8.0rc5.dist-info → mlrun-1.8.0rc9.dist-info}/WHEEL +0 -0
- {mlrun-1.8.0rc5.dist-info → mlrun-1.8.0rc9.dist-info}/entry_points.txt +0 -0
- {mlrun-1.8.0rc5.dist-info → mlrun-1.8.0rc9.dist-info}/top_level.txt +0 -0
|
@@ -1,464 +0,0 @@
|
|
|
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 json
|
|
16
|
-
import typing
|
|
17
|
-
from dataclasses import dataclass
|
|
18
|
-
|
|
19
|
-
import v3io.dataplane
|
|
20
|
-
import v3io.dataplane.output
|
|
21
|
-
import v3io.dataplane.response
|
|
22
|
-
from v3io.dataplane import Client as V3IOClient
|
|
23
|
-
|
|
24
|
-
import mlrun.common.model_monitoring.helpers
|
|
25
|
-
import mlrun.common.schemas.model_monitoring as mm_schemas
|
|
26
|
-
import mlrun.utils.v3io_clients
|
|
27
|
-
from mlrun.model_monitoring.db import StoreBase
|
|
28
|
-
from mlrun.utils import logger
|
|
29
|
-
|
|
30
|
-
# Fields to encode before storing in the KV table or to decode after retrieving
|
|
31
|
-
fields_to_encode_decode = [
|
|
32
|
-
mm_schemas.EventFieldType.FEATURE_STATS,
|
|
33
|
-
mm_schemas.EventFieldType.CURRENT_STATS,
|
|
34
|
-
]
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class SchemaField(typing.TypedDict):
|
|
38
|
-
name: str
|
|
39
|
-
type: str
|
|
40
|
-
nullable: bool
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@dataclass
|
|
44
|
-
class SchemaParams:
|
|
45
|
-
key: str
|
|
46
|
-
fields: list[SchemaField]
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
_EXCLUDE_SCHEMA_FILTER_EXPRESSION = '__name!=".#schema"'
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
class KVStoreBase(StoreBase):
|
|
53
|
-
type: typing.ClassVar[str] = "v3io-nosql"
|
|
54
|
-
"""
|
|
55
|
-
Handles the DB operations when the DB target is from type KV. For the KV operations, we use an instance of V3IO
|
|
56
|
-
client and usually the KV table can be found under v3io:///users/pipelines/project-name/model-endpoints/endpoints/.
|
|
57
|
-
"""
|
|
58
|
-
|
|
59
|
-
def __init__(
|
|
60
|
-
self,
|
|
61
|
-
project: str,
|
|
62
|
-
) -> None:
|
|
63
|
-
super().__init__(project=project)
|
|
64
|
-
self._client = None
|
|
65
|
-
# Get the KV table path and container
|
|
66
|
-
self.path, self.container = self._get_path_and_container()
|
|
67
|
-
|
|
68
|
-
@property
|
|
69
|
-
def client(self) -> V3IOClient:
|
|
70
|
-
if not self._client:
|
|
71
|
-
self._client = mlrun.utils.v3io_clients.get_v3io_client(
|
|
72
|
-
endpoint=mlrun.mlconf.v3io_api,
|
|
73
|
-
)
|
|
74
|
-
return self._client
|
|
75
|
-
|
|
76
|
-
def write_model_endpoint(self, endpoint: dict[str, typing.Any]):
|
|
77
|
-
"""
|
|
78
|
-
Create a new endpoint record in the KV table.
|
|
79
|
-
|
|
80
|
-
:param endpoint: model endpoint dictionary that will be written into the DB.
|
|
81
|
-
"""
|
|
82
|
-
|
|
83
|
-
for field in fields_to_encode_decode:
|
|
84
|
-
if field in endpoint:
|
|
85
|
-
# Encode to binary data
|
|
86
|
-
endpoint[field] = self._encode_field(endpoint[field])
|
|
87
|
-
|
|
88
|
-
self.client.kv.put(
|
|
89
|
-
container=self.container,
|
|
90
|
-
table_path=self.path,
|
|
91
|
-
key=endpoint[mm_schemas.EventFieldType.UID],
|
|
92
|
-
attributes=endpoint,
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
self._infer_kv_schema()
|
|
96
|
-
|
|
97
|
-
def update_model_endpoint(
|
|
98
|
-
self, endpoint_id: str, attributes: dict[str, typing.Any]
|
|
99
|
-
):
|
|
100
|
-
"""
|
|
101
|
-
Update a model endpoint record with a given attributes.
|
|
102
|
-
|
|
103
|
-
:param endpoint_id: The unique id of the model endpoint.
|
|
104
|
-
:param attributes: Dictionary of attributes that will be used for update the model endpoint. Note that the keys
|
|
105
|
-
of the attributes dictionary should exist in the KV table.
|
|
106
|
-
|
|
107
|
-
"""
|
|
108
|
-
|
|
109
|
-
for field in fields_to_encode_decode:
|
|
110
|
-
if field in attributes:
|
|
111
|
-
# Encode to binary data
|
|
112
|
-
attributes[field] = self._encode_field(attributes[field])
|
|
113
|
-
|
|
114
|
-
self.client.kv.update(
|
|
115
|
-
container=self.container,
|
|
116
|
-
table_path=self.path,
|
|
117
|
-
key=endpoint_id,
|
|
118
|
-
attributes=attributes,
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
def delete_model_endpoint(
|
|
122
|
-
self,
|
|
123
|
-
endpoint_id: str,
|
|
124
|
-
):
|
|
125
|
-
"""
|
|
126
|
-
Deletes the KV record of a given model endpoint id.
|
|
127
|
-
|
|
128
|
-
:param endpoint_id: The unique id of the model endpoint.
|
|
129
|
-
"""
|
|
130
|
-
|
|
131
|
-
self.client.kv.delete(
|
|
132
|
-
container=self.container,
|
|
133
|
-
table_path=self.path,
|
|
134
|
-
key=endpoint_id,
|
|
135
|
-
)
|
|
136
|
-
|
|
137
|
-
def get_model_endpoint(
|
|
138
|
-
self,
|
|
139
|
-
endpoint_id: str,
|
|
140
|
-
) -> dict[str, typing.Any]:
|
|
141
|
-
"""
|
|
142
|
-
Get a single model endpoint record.
|
|
143
|
-
|
|
144
|
-
:param endpoint_id: The unique id of the model endpoint.
|
|
145
|
-
|
|
146
|
-
:return: A model endpoint record as a dictionary.
|
|
147
|
-
|
|
148
|
-
:raise MLRunNotFoundError: If the endpoint was not found.
|
|
149
|
-
"""
|
|
150
|
-
|
|
151
|
-
# Getting the raw data from the KV table
|
|
152
|
-
endpoint = self.client.kv.get(
|
|
153
|
-
container=self.container,
|
|
154
|
-
table_path=self.path,
|
|
155
|
-
key=endpoint_id,
|
|
156
|
-
raise_for_status=v3io.dataplane.RaiseForStatus.never,
|
|
157
|
-
)
|
|
158
|
-
endpoint = endpoint.output.item
|
|
159
|
-
|
|
160
|
-
for field in fields_to_encode_decode:
|
|
161
|
-
if field in endpoint:
|
|
162
|
-
# Decode binary data
|
|
163
|
-
endpoint[field] = self._decode_field(endpoint[field])
|
|
164
|
-
|
|
165
|
-
if not endpoint:
|
|
166
|
-
raise mlrun.errors.MLRunNotFoundError(f"Endpoint {endpoint_id} not found")
|
|
167
|
-
|
|
168
|
-
# For backwards compatability: replace null values for `error_count` and `metrics`
|
|
169
|
-
self.validate_old_schema_fields(endpoint=endpoint)
|
|
170
|
-
|
|
171
|
-
return endpoint
|
|
172
|
-
|
|
173
|
-
def _get_path_and_container(self):
|
|
174
|
-
"""Getting path and container based on the model monitoring configurations"""
|
|
175
|
-
path = mlrun.mlconf.model_endpoint_monitoring.store_prefixes.default.format(
|
|
176
|
-
project=self.project,
|
|
177
|
-
kind=mm_schemas.ModelMonitoringStoreKinds.ENDPOINTS,
|
|
178
|
-
)
|
|
179
|
-
(
|
|
180
|
-
_,
|
|
181
|
-
container,
|
|
182
|
-
path,
|
|
183
|
-
) = mlrun.common.model_monitoring.helpers.parse_model_endpoint_store_prefix(
|
|
184
|
-
path
|
|
185
|
-
)
|
|
186
|
-
return path, container
|
|
187
|
-
|
|
188
|
-
def list_model_endpoints(
|
|
189
|
-
self,
|
|
190
|
-
model: typing.Optional[str] = None,
|
|
191
|
-
function: typing.Optional[str] = None,
|
|
192
|
-
labels: typing.Optional[list[str]] = None,
|
|
193
|
-
top_level: typing.Optional[bool] = None,
|
|
194
|
-
uids: typing.Optional[list] = None,
|
|
195
|
-
include_stats: typing.Optional[bool] = None,
|
|
196
|
-
) -> list[dict[str, typing.Any]]:
|
|
197
|
-
# # Initialize an empty model endpoints list
|
|
198
|
-
endpoint_list = []
|
|
199
|
-
|
|
200
|
-
# Retrieve the raw data from the KV table and get the endpoint ids
|
|
201
|
-
try:
|
|
202
|
-
cursor = self.client.kv.new_cursor(
|
|
203
|
-
container=self.container,
|
|
204
|
-
table_path=self.path,
|
|
205
|
-
filter_expression=self._build_kv_cursor_filter_expression(
|
|
206
|
-
self.project,
|
|
207
|
-
function,
|
|
208
|
-
model,
|
|
209
|
-
top_level,
|
|
210
|
-
),
|
|
211
|
-
raise_for_status=v3io.dataplane.RaiseForStatus.never,
|
|
212
|
-
)
|
|
213
|
-
items = cursor.all()
|
|
214
|
-
|
|
215
|
-
except Exception as exc:
|
|
216
|
-
logger.warning(
|
|
217
|
-
"Failed retrieving raw data from kv table",
|
|
218
|
-
exc=mlrun.errors.err_to_str(exc),
|
|
219
|
-
)
|
|
220
|
-
return endpoint_list
|
|
221
|
-
# Create a list of model endpoints unique ids
|
|
222
|
-
if uids is None:
|
|
223
|
-
uids = []
|
|
224
|
-
for item in items:
|
|
225
|
-
if mm_schemas.EventFieldType.UID not in item:
|
|
226
|
-
# This is kept for backwards compatibility - in old versions the key column named endpoint_id
|
|
227
|
-
uids.append(item[mm_schemas.EventFieldType.ENDPOINT_ID])
|
|
228
|
-
else:
|
|
229
|
-
uids.append(item[mm_schemas.EventFieldType.UID])
|
|
230
|
-
|
|
231
|
-
# Add each relevant model endpoint to the model endpoints list
|
|
232
|
-
for endpoint_id in uids:
|
|
233
|
-
endpoint_dict = self.get_model_endpoint(
|
|
234
|
-
endpoint_id=endpoint_id,
|
|
235
|
-
)
|
|
236
|
-
if not include_stats:
|
|
237
|
-
# Exclude these fields when listing model endpoints to avoid returning too much data (ML-6594)
|
|
238
|
-
endpoint_dict.pop(mm_schemas.EventFieldType.FEATURE_STATS)
|
|
239
|
-
endpoint_dict.pop(mm_schemas.EventFieldType.CURRENT_STATS)
|
|
240
|
-
|
|
241
|
-
if labels and not self._validate_labels(
|
|
242
|
-
endpoint_dict=endpoint_dict, labels=labels
|
|
243
|
-
):
|
|
244
|
-
continue
|
|
245
|
-
|
|
246
|
-
endpoint_list.append(endpoint_dict)
|
|
247
|
-
|
|
248
|
-
return endpoint_list
|
|
249
|
-
|
|
250
|
-
def delete_model_endpoints_resources(self):
|
|
251
|
-
"""
|
|
252
|
-
Delete all model endpoints resources in V3IO KV.
|
|
253
|
-
"""
|
|
254
|
-
logger.debug(
|
|
255
|
-
"Deleting model monitoring endpoints resources in V3IO KV",
|
|
256
|
-
project=self.project,
|
|
257
|
-
)
|
|
258
|
-
|
|
259
|
-
endpoints = self.list_model_endpoints()
|
|
260
|
-
|
|
261
|
-
# Delete model endpoint record from KV table
|
|
262
|
-
for endpoint_dict in endpoints:
|
|
263
|
-
if mm_schemas.EventFieldType.UID not in endpoint_dict:
|
|
264
|
-
# This is kept for backwards compatibility - in old versions the key column named endpoint_id
|
|
265
|
-
endpoint_id = endpoint_dict[mm_schemas.EventFieldType.ENDPOINT_ID]
|
|
266
|
-
else:
|
|
267
|
-
endpoint_id = endpoint_dict[mm_schemas.EventFieldType.UID]
|
|
268
|
-
|
|
269
|
-
logger.debug(
|
|
270
|
-
"Deleting model endpoint resources from the V3IO KV table",
|
|
271
|
-
endpoint_id=endpoint_id,
|
|
272
|
-
project=self.project,
|
|
273
|
-
)
|
|
274
|
-
|
|
275
|
-
self.delete_model_endpoint(
|
|
276
|
-
endpoint_id,
|
|
277
|
-
)
|
|
278
|
-
|
|
279
|
-
logger.debug(
|
|
280
|
-
"Successfully deleted model monitoring endpoints from the V3IO KV table",
|
|
281
|
-
project=self.project,
|
|
282
|
-
)
|
|
283
|
-
|
|
284
|
-
# Delete remain records in the KV
|
|
285
|
-
all_records = self.client.kv.new_cursor(
|
|
286
|
-
container=self.container,
|
|
287
|
-
table_path=self.path,
|
|
288
|
-
raise_for_status=v3io.dataplane.RaiseForStatus.never,
|
|
289
|
-
).all()
|
|
290
|
-
|
|
291
|
-
all_records = [r["__name"] for r in all_records]
|
|
292
|
-
|
|
293
|
-
# Cleanup KV
|
|
294
|
-
for record in all_records:
|
|
295
|
-
self.client.kv.delete(
|
|
296
|
-
container=self.container,
|
|
297
|
-
table_path=self.path,
|
|
298
|
-
key=record,
|
|
299
|
-
raise_for_status=v3io.dataplane.RaiseForStatus.never,
|
|
300
|
-
)
|
|
301
|
-
|
|
302
|
-
def _generate_tsdb_paths(self) -> tuple[str, str]:
|
|
303
|
-
"""Generate a short path to the TSDB resources and a filtered path for the frames object
|
|
304
|
-
:return: A tuple of:
|
|
305
|
-
[0] = Short path to the TSDB resources
|
|
306
|
-
[1] = Filtered path to TSDB events without schema and container
|
|
307
|
-
"""
|
|
308
|
-
# Full path for the time series DB events
|
|
309
|
-
full_path = (
|
|
310
|
-
mlrun.mlconf.model_endpoint_monitoring.store_prefixes.default.format(
|
|
311
|
-
project=self.project,
|
|
312
|
-
kind=mm_schemas.ModelMonitoringStoreKinds.EVENTS,
|
|
313
|
-
)
|
|
314
|
-
)
|
|
315
|
-
|
|
316
|
-
# Generate the main directory with the TSDB resources
|
|
317
|
-
tsdb_path = (
|
|
318
|
-
mlrun.common.model_monitoring.helpers.parse_model_endpoint_project_prefix(
|
|
319
|
-
full_path, self.project
|
|
320
|
-
)
|
|
321
|
-
)
|
|
322
|
-
|
|
323
|
-
# Generate filtered path without schema and container as required by the frames object
|
|
324
|
-
(
|
|
325
|
-
_,
|
|
326
|
-
_,
|
|
327
|
-
filtered_path,
|
|
328
|
-
) = mlrun.common.model_monitoring.helpers.parse_model_endpoint_store_prefix(
|
|
329
|
-
full_path
|
|
330
|
-
)
|
|
331
|
-
return tsdb_path, filtered_path
|
|
332
|
-
|
|
333
|
-
def _infer_kv_schema(self):
|
|
334
|
-
"""
|
|
335
|
-
Create KV schema file if not exist. This schema is being used by the Grafana dashboards.
|
|
336
|
-
"""
|
|
337
|
-
|
|
338
|
-
schema_file = self.client.kv.new_cursor(
|
|
339
|
-
container=self.container,
|
|
340
|
-
table_path=self.path,
|
|
341
|
-
filter_expression='__name==".#schema"',
|
|
342
|
-
)
|
|
343
|
-
|
|
344
|
-
if not schema_file.all():
|
|
345
|
-
logger.info("Generate a new V3IO KV schema file", kv_table_path=self.path)
|
|
346
|
-
frames_client = self._get_frames_client()
|
|
347
|
-
frames_client.execute(backend="kv", table=self.path, command="infer_schema")
|
|
348
|
-
|
|
349
|
-
def _get_frames_client(self):
|
|
350
|
-
return mlrun.utils.v3io_clients.get_frames_client(
|
|
351
|
-
address=mlrun.mlconf.v3io_framesd,
|
|
352
|
-
container=self.container,
|
|
353
|
-
)
|
|
354
|
-
|
|
355
|
-
@staticmethod
|
|
356
|
-
def _build_kv_cursor_filter_expression(
|
|
357
|
-
project: str,
|
|
358
|
-
function: typing.Optional[str] = None,
|
|
359
|
-
model: typing.Optional[str] = None,
|
|
360
|
-
top_level: bool = False,
|
|
361
|
-
) -> str:
|
|
362
|
-
"""
|
|
363
|
-
Convert the provided filters into a valid filter expression. The expected filter expression includes different
|
|
364
|
-
conditions, divided by ' AND '.
|
|
365
|
-
|
|
366
|
-
:param project: The name of the project.
|
|
367
|
-
:param model: The name of the model to filter by.
|
|
368
|
-
:param function: The name of the function to filter by.
|
|
369
|
-
:param top_level: If True will return only routers and endpoint that are NOT children of any router.
|
|
370
|
-
|
|
371
|
-
:return: A valid filter expression as a string.
|
|
372
|
-
|
|
373
|
-
:raise MLRunInvalidArgumentError: If project value is None.
|
|
374
|
-
"""
|
|
375
|
-
|
|
376
|
-
if not project:
|
|
377
|
-
raise mlrun.errors.MLRunInvalidArgumentError("project can't be empty")
|
|
378
|
-
|
|
379
|
-
# Add project filter
|
|
380
|
-
filter_expression = [f"{mm_schemas.EventFieldType.PROJECT}=='{project}'"]
|
|
381
|
-
|
|
382
|
-
# Add function and model filters
|
|
383
|
-
if function:
|
|
384
|
-
function_uri = f"{project}/{function}" if function else None
|
|
385
|
-
filter_expression.append(
|
|
386
|
-
f"{mm_schemas.EventFieldType.FUNCTION_URI}=='{function_uri}'"
|
|
387
|
-
)
|
|
388
|
-
if model:
|
|
389
|
-
model = model if ":" in model else f"{model}:latest"
|
|
390
|
-
filter_expression.append(f"{mm_schemas.EventFieldType.MODEL}=='{model}'")
|
|
391
|
-
|
|
392
|
-
# Apply top_level filter (remove endpoints that considered a child of a router)
|
|
393
|
-
if top_level:
|
|
394
|
-
filter_expression.append(
|
|
395
|
-
f"(endpoint_type=='{str(mm_schemas.EndpointType.NODE_EP.value)}' "
|
|
396
|
-
f"OR endpoint_type=='{str(mm_schemas.EndpointType.ROUTER.value)}')"
|
|
397
|
-
)
|
|
398
|
-
|
|
399
|
-
return " AND ".join(filter_expression)
|
|
400
|
-
|
|
401
|
-
@staticmethod
|
|
402
|
-
def validate_old_schema_fields(endpoint: dict):
|
|
403
|
-
"""
|
|
404
|
-
Replace default null values for `error_count` and `metrics` for users that logged a model endpoint before 1.3.0.
|
|
405
|
-
In addition, this function also validates that the key name of the endpoint unique id is `uid` and not
|
|
406
|
-
`endpoint_id` that has been used before 1.3.0.
|
|
407
|
-
|
|
408
|
-
Leaving here for backwards compatibility which related to the model endpoint schema.
|
|
409
|
-
|
|
410
|
-
:param endpoint: An endpoint flattened dictionary.
|
|
411
|
-
"""
|
|
412
|
-
|
|
413
|
-
# Validate default value for `error_count`
|
|
414
|
-
# For backwards compatibility reasons, we validate that the model endpoint includes the `error_count` key
|
|
415
|
-
if (
|
|
416
|
-
mm_schemas.EventFieldType.ERROR_COUNT in endpoint
|
|
417
|
-
and endpoint[mm_schemas.EventFieldType.ERROR_COUNT] == "null"
|
|
418
|
-
):
|
|
419
|
-
endpoint[mm_schemas.EventFieldType.ERROR_COUNT] = "0"
|
|
420
|
-
|
|
421
|
-
# Validate default value for `metrics`
|
|
422
|
-
# For backwards compatibility reasons, we validate that the model endpoint includes the `metrics` key
|
|
423
|
-
if (
|
|
424
|
-
mm_schemas.EventFieldType.METRICS in endpoint
|
|
425
|
-
and endpoint[mm_schemas.EventFieldType.METRICS] == "null"
|
|
426
|
-
):
|
|
427
|
-
endpoint[mm_schemas.EventFieldType.METRICS] = json.dumps(
|
|
428
|
-
{
|
|
429
|
-
mm_schemas.EventKeyMetrics.GENERIC: {
|
|
430
|
-
mm_schemas.EventLiveStats.LATENCY_AVG_1H: 0,
|
|
431
|
-
mm_schemas.EventLiveStats.PREDICTIONS_PER_SECOND: 0,
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
)
|
|
435
|
-
# Validate key `uid` instead of `endpoint_id`
|
|
436
|
-
# For backwards compatibility reasons, we replace the `endpoint_id` with `uid` which is the updated key name
|
|
437
|
-
if mm_schemas.EventFieldType.ENDPOINT_ID in endpoint:
|
|
438
|
-
endpoint[mm_schemas.EventFieldType.UID] = endpoint[
|
|
439
|
-
mm_schemas.EventFieldType.ENDPOINT_ID
|
|
440
|
-
]
|
|
441
|
-
|
|
442
|
-
@staticmethod
|
|
443
|
-
def _encode_field(field: typing.Union[str, bytes]) -> bytes:
|
|
444
|
-
"""Encode a provided field. Mainly used when storing data in the KV table."""
|
|
445
|
-
|
|
446
|
-
if isinstance(field, str):
|
|
447
|
-
return field.encode("ascii")
|
|
448
|
-
return field
|
|
449
|
-
|
|
450
|
-
@staticmethod
|
|
451
|
-
def _decode_field(field: typing.Union[str, bytes]) -> str:
|
|
452
|
-
"""Decode a provided field. Mainly used when retrieving data from the KV table."""
|
|
453
|
-
|
|
454
|
-
if isinstance(field, bytes):
|
|
455
|
-
return field.decode()
|
|
456
|
-
return field
|
|
457
|
-
|
|
458
|
-
@staticmethod
|
|
459
|
-
def get_v3io_monitoring_apps_container(project_name: str) -> str:
|
|
460
|
-
return f"users/pipelines/{project_name}/monitoring-apps"
|
|
461
|
-
|
|
462
|
-
@staticmethod
|
|
463
|
-
def _get_monitoring_schedules_container(project_name: str) -> str:
|
|
464
|
-
return f"users/pipelines/{project_name}/monitoring-schedules/functions"
|
|
@@ -1,120 +0,0 @@
|
|
|
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
|
-
|
|
16
|
-
from dataclasses import dataclass, field
|
|
17
|
-
from typing import Any, Optional
|
|
18
|
-
|
|
19
|
-
import mlrun.model
|
|
20
|
-
from mlrun.common.model_monitoring.helpers import FeatureStats
|
|
21
|
-
from mlrun.common.schemas.model_monitoring.constants import (
|
|
22
|
-
EndpointType,
|
|
23
|
-
EventKeyMetrics,
|
|
24
|
-
EventLiveStats,
|
|
25
|
-
ModelMonitoringMode,
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@dataclass
|
|
30
|
-
class ModelEndpointSpec(mlrun.model.ModelObj):
|
|
31
|
-
function_uri: str = "" # <project_name>/<function_name>:<tag>
|
|
32
|
-
model: str = "" # <model_name>:<version>
|
|
33
|
-
model_class: str = ""
|
|
34
|
-
model_uri: str = ""
|
|
35
|
-
feature_names: list[str] = field(default_factory=list)
|
|
36
|
-
label_names: list[str] = field(default_factory=list)
|
|
37
|
-
stream_path: str = ""
|
|
38
|
-
algorithm: str = ""
|
|
39
|
-
monitor_configuration: dict = field(default_factory=dict)
|
|
40
|
-
active: bool = True
|
|
41
|
-
monitoring_mode: ModelMonitoringMode = ModelMonitoringMode.disabled
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@dataclass
|
|
45
|
-
class ModelEndpointStatus(mlrun.model.ModelObj):
|
|
46
|
-
feature_stats: FeatureStats = field(default_factory=dict)
|
|
47
|
-
current_stats: FeatureStats = field(default_factory=dict)
|
|
48
|
-
first_request: str = ""
|
|
49
|
-
last_request: str = ""
|
|
50
|
-
error_count: int = 0
|
|
51
|
-
drift_status: str = ""
|
|
52
|
-
drift_measures: dict = field(default_factory=dict)
|
|
53
|
-
metrics: dict[str, dict[str, Any]] = field(
|
|
54
|
-
default_factory=lambda: {
|
|
55
|
-
EventKeyMetrics.GENERIC: {
|
|
56
|
-
EventLiveStats.LATENCY_AVG_1H: 0,
|
|
57
|
-
EventLiveStats.PREDICTIONS_PER_SECOND: 0,
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
)
|
|
61
|
-
features: list[dict[str, Any]] = field(default_factory=list)
|
|
62
|
-
children: list[str] = field(default_factory=list)
|
|
63
|
-
children_uids: list[str] = field(default_factory=list)
|
|
64
|
-
endpoint_type: EndpointType = EndpointType.NODE_EP
|
|
65
|
-
monitoring_feature_set_uri: str = ""
|
|
66
|
-
state: str = ""
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
class ModelEndpoint(mlrun.model.ModelObj):
|
|
70
|
-
kind = "model-endpoint"
|
|
71
|
-
_dict_fields = ["kind", "metadata", "spec", "status"]
|
|
72
|
-
|
|
73
|
-
def __init__(self):
|
|
74
|
-
self._status: ModelEndpointStatus = ModelEndpointStatus()
|
|
75
|
-
self._spec: ModelEndpointSpec = ModelEndpointSpec()
|
|
76
|
-
self._metadata: mlrun.model.VersionedObjMetadata = (
|
|
77
|
-
mlrun.model.VersionedObjMetadata()
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
@property
|
|
81
|
-
def status(self) -> ModelEndpointStatus:
|
|
82
|
-
return self._status
|
|
83
|
-
|
|
84
|
-
@status.setter
|
|
85
|
-
def status(self, status):
|
|
86
|
-
self._status = self._verify_dict(status, "status", ModelEndpointStatus)
|
|
87
|
-
|
|
88
|
-
@property
|
|
89
|
-
def spec(self) -> ModelEndpointSpec:
|
|
90
|
-
return self._spec
|
|
91
|
-
|
|
92
|
-
@spec.setter
|
|
93
|
-
def spec(self, spec):
|
|
94
|
-
self._spec = self._verify_dict(spec, "spec", ModelEndpointSpec)
|
|
95
|
-
|
|
96
|
-
@property
|
|
97
|
-
def metadata(self) -> mlrun.model.VersionedObjMetadata:
|
|
98
|
-
return self._metadata
|
|
99
|
-
|
|
100
|
-
@metadata.setter
|
|
101
|
-
def metadata(self, metadata):
|
|
102
|
-
self._metadata = self._verify_dict(
|
|
103
|
-
metadata, "metadata", mlrun.model.VersionedObjMetadata
|
|
104
|
-
)
|
|
105
|
-
|
|
106
|
-
@classmethod
|
|
107
|
-
def from_flat_dict(
|
|
108
|
-
cls, struct=None, fields=None, deprecated_fields: Optional[dict] = None
|
|
109
|
-
):
|
|
110
|
-
new_obj = cls()
|
|
111
|
-
new_obj._metadata = mlrun.model.VersionedObjMetadata().from_dict(
|
|
112
|
-
struct=struct, fields=fields, deprecated_fields=deprecated_fields
|
|
113
|
-
)
|
|
114
|
-
new_obj._status = ModelEndpointStatus().from_dict(
|
|
115
|
-
struct=struct, fields=fields, deprecated_fields=deprecated_fields
|
|
116
|
-
)
|
|
117
|
-
new_obj._spec = ModelEndpointSpec().from_dict(
|
|
118
|
-
struct=struct, fields=fields, deprecated_fields=deprecated_fields
|
|
119
|
-
)
|
|
120
|
-
return new_obj
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|