mlrun 1.8.0rc4__py3-none-any.whl → 1.8.0rc7__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 +5 -3
- mlrun/alerts/alert.py +129 -2
- mlrun/artifacts/__init__.py +1 -1
- mlrun/artifacts/base.py +12 -1
- mlrun/artifacts/document.py +59 -38
- 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 +80 -1
- mlrun/common/schemas/artifact.py +4 -0
- 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/vectorstore.py +67 -59
- mlrun/db/base.py +29 -19
- mlrun/db/factory.py +0 -3
- mlrun/db/httpdb.py +224 -161
- mlrun/db/nopdb.py +36 -17
- mlrun/execution.py +46 -32
- mlrun/feature_store/api.py +1 -0
- mlrun/model.py +7 -0
- mlrun/model_monitoring/__init__.py +3 -2
- mlrun/model_monitoring/api.py +55 -53
- mlrun/model_monitoring/applications/_application_steps.py +4 -2
- mlrun/model_monitoring/applications/base.py +165 -6
- mlrun/model_monitoring/applications/context.py +88 -37
- mlrun/model_monitoring/applications/evidently_base.py +0 -1
- mlrun/model_monitoring/applications/histogram_data_drift.py +3 -7
- 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 +79 -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 +171 -74
- mlrun/run.py +40 -0
- mlrun/runtimes/nuclio/function.py +7 -6
- mlrun/runtimes/nuclio/serving.py +9 -2
- mlrun/serving/routers.py +158 -145
- mlrun/serving/server.py +6 -0
- mlrun/serving/states.py +21 -7
- mlrun/serving/v2_serving.py +70 -61
- mlrun/utils/helpers.py +14 -30
- mlrun/utils/notifications/notification/mail.py +36 -9
- mlrun/utils/notifications/notification_pusher.py +43 -18
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.8.0rc4.dist-info → mlrun-1.8.0rc7.dist-info}/METADATA +5 -4
- {mlrun-1.8.0rc4.dist-info → mlrun-1.8.0rc7.dist-info}/RECORD +62 -75
- 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.0rc4.dist-info → mlrun-1.8.0rc7.dist-info}/LICENSE +0 -0
- {mlrun-1.8.0rc4.dist-info → mlrun-1.8.0rc7.dist-info}/WHEEL +0 -0
- {mlrun-1.8.0rc4.dist-info → mlrun-1.8.0rc7.dist-info}/entry_points.txt +0 -0
- {mlrun-1.8.0rc4.dist-info → mlrun-1.8.0rc7.dist-info}/top_level.txt +0 -0
|
@@ -55,12 +55,6 @@ from .grafana import (
|
|
|
55
55
|
GrafanaTable,
|
|
56
56
|
GrafanaTimeSeriesTarget,
|
|
57
57
|
)
|
|
58
|
-
from .model_endpoint_v2 import (
|
|
59
|
-
ModelEndpointV2,
|
|
60
|
-
ModelEndpointV2Metadata,
|
|
61
|
-
ModelEndpointV2Spec,
|
|
62
|
-
ModelEndpointV2Status,
|
|
63
|
-
)
|
|
64
58
|
from .model_endpoints import (
|
|
65
59
|
Features,
|
|
66
60
|
FeatureValues,
|
|
@@ -41,6 +41,7 @@ class ModelEndpointSchema(MonitoringStrEnum):
|
|
|
41
41
|
|
|
42
42
|
# spec
|
|
43
43
|
FUNCTION_NAME = "function_name"
|
|
44
|
+
FUNCTION_TAG = "function_tag"
|
|
44
45
|
FUNCTION_UID = "function_uid"
|
|
45
46
|
MODEL_NAME = "model_name"
|
|
46
47
|
MODEL_TAG = "model_tag"
|
|
@@ -48,23 +49,23 @@ class ModelEndpointSchema(MonitoringStrEnum):
|
|
|
48
49
|
MODEL_UID = "model_uid"
|
|
49
50
|
FEATURE_NAMES = "feature_names"
|
|
50
51
|
LABEL_NAMES = "label_names"
|
|
51
|
-
|
|
52
|
-
# status
|
|
53
|
-
STATE = "state"
|
|
54
|
-
MONITORING_MODE = "monitoring_mode"
|
|
52
|
+
FEATURE_STATS = "feature_stats"
|
|
55
53
|
MONITORING_FEATURE_SET_URI = "monitoring_feature_set_uri"
|
|
56
54
|
CHILDREN = "children"
|
|
57
55
|
CHILDREN_UIDS = "children_uids"
|
|
58
|
-
FIRST_REQUEST = "first_request"
|
|
59
56
|
FUNCTION_URI = "function_uri"
|
|
60
57
|
MODEL_URI = "model_uri"
|
|
61
58
|
|
|
59
|
+
# status
|
|
60
|
+
STATE = "state"
|
|
61
|
+
MONITORING_MODE = "monitoring_mode"
|
|
62
|
+
FIRST_REQUEST = "first_request"
|
|
63
|
+
|
|
62
64
|
# status - operative
|
|
63
65
|
LAST_REQUEST = "last_request"
|
|
64
|
-
|
|
66
|
+
RESULT_STATUS = "result_status"
|
|
65
67
|
AVG_LATENCY = "avg_latency"
|
|
66
68
|
ERROR_COUNT = "error_count"
|
|
67
|
-
FEATURE_STATS = "feature_stats"
|
|
68
69
|
CURRENT_STATS = "current_stats"
|
|
69
70
|
DRIFT_MEASURES = "drift_measures"
|
|
70
71
|
|
|
@@ -80,6 +81,7 @@ class EventFieldType:
|
|
|
80
81
|
TIMESTAMP = "timestamp"
|
|
81
82
|
# `endpoint_id` is deprecated as a field in the model endpoint schema since 1.3.1, replaced by `uid`.
|
|
82
83
|
ENDPOINT_ID = "endpoint_id"
|
|
84
|
+
ENDPOINT_NAME = "endpoint_name"
|
|
83
85
|
UID = "uid"
|
|
84
86
|
ENDPOINT_TYPE = "endpoint_type"
|
|
85
87
|
REQUEST_ID = "request_id"
|
|
@@ -148,10 +150,12 @@ class ApplicationEvent:
|
|
|
148
150
|
START_INFER_TIME = "start_infer_time"
|
|
149
151
|
END_INFER_TIME = "end_infer_time"
|
|
150
152
|
ENDPOINT_ID = "endpoint_id"
|
|
153
|
+
ENDPOINT_NAME = "endpoint_name"
|
|
151
154
|
OUTPUT_STREAM_URI = "output_stream_uri"
|
|
152
155
|
|
|
153
156
|
|
|
154
157
|
class WriterEvent(MonitoringStrEnum):
|
|
158
|
+
ENDPOINT_NAME = "endpoint_name"
|
|
155
159
|
APPLICATION_NAME = "application_name"
|
|
156
160
|
ENDPOINT_ID = "endpoint_id"
|
|
157
161
|
START_INFER_TIME = "start_infer_time"
|
|
@@ -222,7 +226,6 @@ class TSDBTarget(MonitoringStrEnum):
|
|
|
222
226
|
|
|
223
227
|
|
|
224
228
|
class ProjectSecretKeys:
|
|
225
|
-
ENDPOINT_STORE_CONNECTION = "MODEL_MONITORING_ENDPOINT_STORE_CONNECTION"
|
|
226
229
|
ACCESS_KEY = "MODEL_MONITORING_ACCESS_KEY"
|
|
227
230
|
STREAM_PATH = "STREAM_PATH"
|
|
228
231
|
TSDB_CONNECTION = "TSDB_CONNECTION"
|
|
@@ -230,7 +233,6 @@ class ProjectSecretKeys:
|
|
|
230
233
|
@classmethod
|
|
231
234
|
def mandatory_secrets(cls):
|
|
232
235
|
return [
|
|
233
|
-
cls.ENDPOINT_STORE_CONNECTION,
|
|
234
236
|
cls.STREAM_PATH,
|
|
235
237
|
cls.TSDB_CONNECTION,
|
|
236
238
|
]
|
|
@@ -11,27 +11,22 @@
|
|
|
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
|
-
import enum
|
|
14
|
+
import abc
|
|
16
15
|
import json
|
|
17
16
|
from datetime import datetime
|
|
18
17
|
from typing import Any, NamedTuple, Optional, TypeVar
|
|
19
18
|
|
|
20
|
-
from pydantic.v1 import BaseModel,
|
|
19
|
+
from pydantic.v1 import BaseModel, Field, constr
|
|
21
20
|
|
|
22
21
|
# TODO: remove the unused import below after `mlrun.datastore` and `mlrun.utils` usage is removed.
|
|
23
22
|
# At the moment `make lint` fails if this is removed.
|
|
24
|
-
import
|
|
25
|
-
|
|
26
|
-
from ..object import ObjectKind, ObjectSpec, ObjectStatus
|
|
23
|
+
from ..object import ObjectKind, ObjectMetadata, ObjectSpec, ObjectStatus
|
|
24
|
+
from . import ModelEndpointSchema
|
|
27
25
|
from .constants import (
|
|
28
26
|
FQN_REGEX,
|
|
29
27
|
MODEL_ENDPOINT_ID_PATTERN,
|
|
30
28
|
PROJECT_PATTERN,
|
|
31
29
|
EndpointType,
|
|
32
|
-
EventFieldType,
|
|
33
|
-
EventKeyMetrics,
|
|
34
|
-
EventLiveStats,
|
|
35
30
|
ModelEndpointMonitoringMetricType,
|
|
36
31
|
ModelMonitoringMode,
|
|
37
32
|
ResultKindApp,
|
|
@@ -47,81 +42,6 @@ class ModelMonitoringStoreKinds:
|
|
|
47
42
|
EVENTS = "events"
|
|
48
43
|
|
|
49
44
|
|
|
50
|
-
class ModelEndpointMetadata(BaseModel):
|
|
51
|
-
project: constr(regex=PROJECT_PATTERN)
|
|
52
|
-
uid: constr(regex=MODEL_ENDPOINT_ID_PATTERN)
|
|
53
|
-
labels: Optional[dict] = {}
|
|
54
|
-
|
|
55
|
-
class Config:
|
|
56
|
-
extra = Extra.allow
|
|
57
|
-
|
|
58
|
-
@classmethod
|
|
59
|
-
def from_flat_dict(
|
|
60
|
-
cls, endpoint_dict: dict, json_parse_values: Optional[list] = None
|
|
61
|
-
):
|
|
62
|
-
"""Create a `ModelEndpointMetadata` object from an endpoint dictionary
|
|
63
|
-
|
|
64
|
-
:param endpoint_dict: Model endpoint dictionary.
|
|
65
|
-
:param json_parse_values: List of dictionary keys with a JSON string value that will be parsed into a
|
|
66
|
-
dictionary using json.loads().
|
|
67
|
-
"""
|
|
68
|
-
if json_parse_values is None:
|
|
69
|
-
json_parse_values = [EventFieldType.LABELS]
|
|
70
|
-
|
|
71
|
-
return _mapping_attributes(
|
|
72
|
-
model_class=cls,
|
|
73
|
-
flattened_dictionary=endpoint_dict,
|
|
74
|
-
json_parse_values=json_parse_values,
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
class ModelEndpointSpec(ObjectSpec):
|
|
79
|
-
function_uri: Optional[str] = "" # <project_name>/<function_name>:<tag>
|
|
80
|
-
model: Optional[str] = "" # <model_name>:<version>
|
|
81
|
-
model_class: Optional[str] = ""
|
|
82
|
-
model_uri: Optional[str] = ""
|
|
83
|
-
feature_names: Optional[list[str]] = []
|
|
84
|
-
label_names: Optional[list[str]] = []
|
|
85
|
-
stream_path: Optional[str] = ""
|
|
86
|
-
algorithm: Optional[str] = ""
|
|
87
|
-
monitor_configuration: Optional[dict] = {}
|
|
88
|
-
active: Optional[bool] = True
|
|
89
|
-
monitoring_mode: Optional[ModelMonitoringMode] = ModelMonitoringMode.disabled.value
|
|
90
|
-
|
|
91
|
-
@classmethod
|
|
92
|
-
def from_flat_dict(
|
|
93
|
-
cls, endpoint_dict: dict, json_parse_values: Optional[list] = None
|
|
94
|
-
):
|
|
95
|
-
"""Create a `ModelEndpointSpec` object from an endpoint dictionary
|
|
96
|
-
|
|
97
|
-
:param endpoint_dict: Model endpoint dictionary.
|
|
98
|
-
:param json_parse_values: List of dictionary keys with a JSON string value that will be parsed into a
|
|
99
|
-
dictionary using json.loads().
|
|
100
|
-
"""
|
|
101
|
-
if json_parse_values is None:
|
|
102
|
-
json_parse_values = [
|
|
103
|
-
EventFieldType.FEATURE_NAMES,
|
|
104
|
-
EventFieldType.LABEL_NAMES,
|
|
105
|
-
EventFieldType.MONITOR_CONFIGURATION,
|
|
106
|
-
]
|
|
107
|
-
return _mapping_attributes(
|
|
108
|
-
model_class=cls,
|
|
109
|
-
flattened_dictionary=endpoint_dict,
|
|
110
|
-
json_parse_values=json_parse_values,
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
@validator("model_uri")
|
|
114
|
-
@classmethod
|
|
115
|
-
def validate_model_uri(cls, model_uri):
|
|
116
|
-
"""Validate that the model uri includes the required prefix"""
|
|
117
|
-
prefix, uri = mlrun.datastore.parse_store_uri(model_uri)
|
|
118
|
-
if prefix and prefix != mlrun.utils.helpers.StorePrefix.Model:
|
|
119
|
-
return mlrun.datastore.get_store_uri(
|
|
120
|
-
mlrun.utils.helpers.StorePrefix.Model, uri
|
|
121
|
-
)
|
|
122
|
-
return model_uri
|
|
123
|
-
|
|
124
|
-
|
|
125
45
|
class Histogram(BaseModel):
|
|
126
46
|
buckets: list[float]
|
|
127
47
|
counts: list[int]
|
|
@@ -167,50 +87,24 @@ class Features(BaseModel):
|
|
|
167
87
|
)
|
|
168
88
|
|
|
169
89
|
|
|
170
|
-
class
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
last_request: Optional[str] = ""
|
|
175
|
-
error_count: Optional[int] = 0
|
|
176
|
-
drift_status: Optional[str] = ""
|
|
177
|
-
drift_measures: Optional[dict] = {}
|
|
178
|
-
metrics: Optional[dict[str, dict[str, Any]]] = {
|
|
179
|
-
EventKeyMetrics.GENERIC: {
|
|
180
|
-
EventLiveStats.LATENCY_AVG_1H: 0,
|
|
181
|
-
EventLiveStats.PREDICTIONS_PER_SECOND: 0,
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
features: Optional[list[Features]] = []
|
|
185
|
-
children: Optional[list[str]] = []
|
|
186
|
-
children_uids: Optional[list[str]] = []
|
|
187
|
-
endpoint_type: Optional[EndpointType] = EndpointType.NODE_EP
|
|
188
|
-
monitoring_feature_set_uri: Optional[str] = ""
|
|
189
|
-
state: Optional[str] = ""
|
|
190
|
-
|
|
191
|
-
class Config:
|
|
192
|
-
extra = Extra.allow
|
|
90
|
+
class ModelEndpointParser(abc.ABC, BaseModel):
|
|
91
|
+
@classmethod
|
|
92
|
+
def json_parse_values(cls) -> list[str]:
|
|
93
|
+
return []
|
|
193
94
|
|
|
194
95
|
@classmethod
|
|
195
96
|
def from_flat_dict(
|
|
196
97
|
cls, endpoint_dict: dict, json_parse_values: Optional[list] = None
|
|
197
|
-
):
|
|
198
|
-
"""Create a `
|
|
98
|
+
) -> "ModelEndpointParser":
|
|
99
|
+
"""Create a `ModelEndpointParser` object from an endpoint dictionary
|
|
199
100
|
|
|
200
101
|
:param endpoint_dict: Model endpoint dictionary.
|
|
201
102
|
:param json_parse_values: List of dictionary keys with a JSON string value that will be parsed into a
|
|
202
103
|
dictionary using json.loads().
|
|
203
104
|
"""
|
|
204
105
|
if json_parse_values is None:
|
|
205
|
-
json_parse_values =
|
|
206
|
-
|
|
207
|
-
EventFieldType.CURRENT_STATS,
|
|
208
|
-
EventFieldType.DRIFT_MEASURES,
|
|
209
|
-
EventFieldType.METRICS,
|
|
210
|
-
EventFieldType.CHILDREN,
|
|
211
|
-
EventFieldType.CHILDREN_UIDS,
|
|
212
|
-
EventFieldType.ENDPOINT_TYPE,
|
|
213
|
-
]
|
|
106
|
+
json_parse_values = cls.json_parse_values()
|
|
107
|
+
|
|
214
108
|
return _mapping_attributes(
|
|
215
109
|
model_class=cls,
|
|
216
110
|
flattened_dictionary=endpoint_dict,
|
|
@@ -218,16 +112,53 @@ class ModelEndpointStatus(ObjectStatus):
|
|
|
218
112
|
)
|
|
219
113
|
|
|
220
114
|
|
|
115
|
+
class ModelEndpointMetadata(ObjectMetadata, ModelEndpointParser):
|
|
116
|
+
project: constr(regex=PROJECT_PATTERN)
|
|
117
|
+
endpoint_type: EndpointType = EndpointType.NODE_EP
|
|
118
|
+
uid: Optional[constr(regex=MODEL_ENDPOINT_ID_PATTERN)]
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class ModelEndpointSpec(ObjectSpec, ModelEndpointParser):
|
|
122
|
+
model_uid: Optional[str] = ""
|
|
123
|
+
model_name: Optional[str] = ""
|
|
124
|
+
model_tag: Optional[str] = ""
|
|
125
|
+
model_class: Optional[str] = ""
|
|
126
|
+
function_name: Optional[str] = ""
|
|
127
|
+
function_tag: Optional[str] = ""
|
|
128
|
+
function_uid: Optional[str] = ""
|
|
129
|
+
feature_names: Optional[list[str]] = []
|
|
130
|
+
label_names: Optional[list[str]] = []
|
|
131
|
+
feature_stats: Optional[dict] = {}
|
|
132
|
+
function_uri: Optional[str] = "" # <project_name>/<function_hash>
|
|
133
|
+
model_uri: Optional[str] = ""
|
|
134
|
+
children: Optional[list[str]] = []
|
|
135
|
+
children_uids: Optional[list[str]] = []
|
|
136
|
+
monitoring_feature_set_uri: Optional[str] = ""
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class ModelEndpointStatus(ObjectStatus, ModelEndpointParser):
|
|
140
|
+
state: Optional[str] = "unknown" # will be updated according to the function state
|
|
141
|
+
first_request: Optional[datetime] = None
|
|
142
|
+
monitoring_mode: Optional[ModelMonitoringMode] = ModelMonitoringMode.disabled
|
|
143
|
+
|
|
144
|
+
# operative
|
|
145
|
+
last_request: Optional[datetime] = None
|
|
146
|
+
result_status: Optional[int] = -1
|
|
147
|
+
avg_latency: Optional[float] = None
|
|
148
|
+
error_count: Optional[int] = 0
|
|
149
|
+
current_stats: Optional[dict] = {}
|
|
150
|
+
current_stats_timestamp: Optional[datetime] = None
|
|
151
|
+
drift_measures: Optional[dict] = {}
|
|
152
|
+
drift_measures_timestamp: Optional[datetime] = None
|
|
153
|
+
|
|
154
|
+
|
|
221
155
|
class ModelEndpoint(BaseModel):
|
|
222
156
|
kind: ObjectKind = Field(ObjectKind.model_endpoint, const=True)
|
|
223
157
|
metadata: ModelEndpointMetadata
|
|
224
|
-
spec: ModelEndpointSpec
|
|
225
|
-
status: ModelEndpointStatus
|
|
158
|
+
spec: ModelEndpointSpec
|
|
159
|
+
status: ModelEndpointStatus
|
|
226
160
|
|
|
227
|
-
|
|
228
|
-
extra = Extra.allow
|
|
229
|
-
|
|
230
|
-
def flat_dict(self):
|
|
161
|
+
def flat_dict(self) -> dict[str, Any]:
|
|
231
162
|
"""Generate a flattened `ModelEndpoint` dictionary. The flattened dictionary result is important for storing
|
|
232
163
|
the model endpoint object in the database.
|
|
233
164
|
|
|
@@ -235,35 +166,24 @@ class ModelEndpoint(BaseModel):
|
|
|
235
166
|
"""
|
|
236
167
|
# Convert the ModelEndpoint object into a dictionary using BaseModel dict() function
|
|
237
168
|
# In addition, remove the BaseModel kind as it is not required by the DB schema
|
|
238
|
-
model_endpoint_dictionary = self.dict(exclude={"kind"})
|
|
239
169
|
|
|
170
|
+
model_endpoint_dictionary = self.dict(exclude={"kind"})
|
|
171
|
+
exclude = {
|
|
172
|
+
"tag",
|
|
173
|
+
ModelEndpointSchema.FEATURE_STATS,
|
|
174
|
+
ModelEndpointSchema.CURRENT_STATS,
|
|
175
|
+
ModelEndpointSchema.DRIFT_MEASURES,
|
|
176
|
+
ModelEndpointSchema.FUNCTION_URI,
|
|
177
|
+
ModelEndpointSchema.MODEL_URI,
|
|
178
|
+
}
|
|
240
179
|
# Initialize a flattened dictionary that will be filled with the model endpoint dictionary attributes
|
|
241
180
|
flatten_dict = {}
|
|
242
181
|
for k_object in model_endpoint_dictionary:
|
|
243
182
|
for key in model_endpoint_dictionary[k_object]:
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
# for matching the database required format
|
|
249
|
-
if not isinstance(current_value, (str, bool, int)) or isinstance(
|
|
250
|
-
current_value, enum.IntEnum
|
|
251
|
-
):
|
|
252
|
-
flatten_dict[key] = json.dumps(current_value)
|
|
253
|
-
else:
|
|
254
|
-
flatten_dict[key] = current_value
|
|
255
|
-
|
|
256
|
-
if EventFieldType.METRICS not in flatten_dict:
|
|
257
|
-
# Initialize metrics dictionary
|
|
258
|
-
flatten_dict[EventFieldType.METRICS] = {
|
|
259
|
-
EventKeyMetrics.GENERIC: {
|
|
260
|
-
EventLiveStats.LATENCY_AVG_1H: 0,
|
|
261
|
-
EventLiveStats.PREDICTIONS_PER_SECOND: 0,
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
# Remove the features from the dictionary as this field will be filled only within the feature analysis process
|
|
266
|
-
flatten_dict.pop(EventFieldType.FEATURES, None)
|
|
183
|
+
if key not in exclude:
|
|
184
|
+
# Extract the value of the current field
|
|
185
|
+
flatten_dict[key] = model_endpoint_dictionary[k_object][key]
|
|
186
|
+
|
|
267
187
|
return flatten_dict
|
|
268
188
|
|
|
269
189
|
@classmethod
|
|
@@ -280,9 +200,17 @@ class ModelEndpoint(BaseModel):
|
|
|
280
200
|
status=ModelEndpointStatus.from_flat_dict(endpoint_dict=endpoint_dict),
|
|
281
201
|
)
|
|
282
202
|
|
|
203
|
+
def get(self, field, default=None):
|
|
204
|
+
return (
|
|
205
|
+
getattr(self.metadata, field, None)
|
|
206
|
+
or getattr(self.spec, field, None)
|
|
207
|
+
or getattr(self.status, field, None)
|
|
208
|
+
or default
|
|
209
|
+
)
|
|
210
|
+
|
|
283
211
|
|
|
284
212
|
class ModelEndpointList(BaseModel):
|
|
285
|
-
endpoints: list[ModelEndpoint]
|
|
213
|
+
endpoints: list[ModelEndpoint]
|
|
286
214
|
|
|
287
215
|
|
|
288
216
|
class ModelEndpointMonitoringMetric(BaseModel):
|
|
@@ -132,8 +132,14 @@ class SetNotificationRequest(pydantic.v1.BaseModel):
|
|
|
132
132
|
notifications: list[Notification] = None
|
|
133
133
|
|
|
134
134
|
|
|
135
|
+
class NotificationSummary(pydantic.v1.BaseModel):
|
|
136
|
+
failed: int = 0
|
|
137
|
+
succeeded: int = 0
|
|
138
|
+
|
|
139
|
+
|
|
135
140
|
class NotificationState(pydantic.v1.BaseModel):
|
|
136
141
|
kind: str
|
|
137
142
|
err: Optional[
|
|
138
143
|
str
|
|
139
144
|
] # empty error means that the notifications were sent successfully
|
|
145
|
+
summary: NotificationSummary
|
mlrun/common/schemas/project.py
CHANGED
|
@@ -159,6 +159,9 @@ class ProjectSummary(pydantic.v1.BaseModel):
|
|
|
159
159
|
pipelines_failed_recent_count: typing.Optional[int] = None
|
|
160
160
|
pipelines_running_count: typing.Optional[int] = None
|
|
161
161
|
updated: typing.Optional[datetime.datetime] = None
|
|
162
|
+
endpoint_alerts_count: int = 0
|
|
163
|
+
job_alerts_count: int = 0
|
|
164
|
+
other_alerts_count: int = 0
|
|
162
165
|
|
|
163
166
|
|
|
164
167
|
class IguazioProject(pydantic.v1.BaseModel):
|
mlrun/config.py
CHANGED
|
@@ -136,7 +136,7 @@ default_config = {
|
|
|
136
136
|
},
|
|
137
137
|
},
|
|
138
138
|
"object_retentions": {
|
|
139
|
-
"
|
|
139
|
+
"alert_activations": 14 * 7, # days
|
|
140
140
|
},
|
|
141
141
|
# A safety margin to account for delays
|
|
142
142
|
# This ensures that extra partitions are available beyond the specified retention period
|
|
@@ -232,6 +232,7 @@ default_config = {
|
|
|
232
232
|
"delete_function": "900",
|
|
233
233
|
},
|
|
234
234
|
"runtimes": {"dask": "600"},
|
|
235
|
+
"push_notifications": "60",
|
|
235
236
|
},
|
|
236
237
|
},
|
|
237
238
|
"function": {
|
|
@@ -607,8 +608,6 @@ default_config = {
|
|
|
607
608
|
"default_http_sink_app": "http://nuclio-{project}-{application_name}.{namespace}.svc.cluster.local:8080",
|
|
608
609
|
"parquet_batching_max_events": 10_000,
|
|
609
610
|
"parquet_batching_timeout_secs": timedelta(minutes=1).total_seconds(),
|
|
610
|
-
# See mlrun.model_monitoring.db.stores.ObjectStoreFactory for available options
|
|
611
|
-
"endpoint_store_connection": "",
|
|
612
611
|
# See mlrun.model_monitoring.db.tsdb.ObjectTSDBFactory for available options
|
|
613
612
|
"tsdb_connection": "",
|
|
614
613
|
# See mlrun.common.schemas.model_monitoring.constants.StreamKind for available options
|
|
@@ -81,22 +81,62 @@ class DatastoreProfileBasic(DatastoreProfile):
|
|
|
81
81
|
private: typing.Optional[str] = None
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
class
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
84
|
+
class ConfigProfile(DatastoreProfile):
|
|
85
|
+
"""
|
|
86
|
+
A profile class for managing configuration data with nested public and private attributes.
|
|
87
|
+
This class extends DatastoreProfile to handle configuration settings, separating them into
|
|
88
|
+
public and private dictionaries. Both dictionaries support nested structures, and the class
|
|
89
|
+
provides functionality to merge these attributes when needed.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
public (Optional[dict]): Dictionary containing public configuration settings,
|
|
93
|
+
supporting nested structures
|
|
94
|
+
private (Optional[dict]): Dictionary containing private/sensitive configuration settings,
|
|
95
|
+
supporting nested structures
|
|
96
|
+
|
|
97
|
+
Example:
|
|
98
|
+
>>> public = {
|
|
99
|
+
"database": {
|
|
100
|
+
"host": "localhost",
|
|
101
|
+
"port": 5432
|
|
102
|
+
},
|
|
103
|
+
"api_version": "v1"
|
|
104
|
+
}
|
|
105
|
+
>>> private = {
|
|
106
|
+
"database": {
|
|
107
|
+
"password": "secret123",
|
|
108
|
+
"username": "admin"
|
|
109
|
+
},
|
|
110
|
+
"api_key": "xyz789"
|
|
111
|
+
}
|
|
112
|
+
>>> config = ConfigProfile("myconfig", public=public, private=private)
|
|
113
|
+
|
|
114
|
+
# When attributes() is called, it merges public and private:
|
|
115
|
+
# {
|
|
116
|
+
# "database": {
|
|
117
|
+
# "host": "localhost",
|
|
118
|
+
# "port": 5432,
|
|
119
|
+
# "password": "secret123",
|
|
120
|
+
# "username": "admin"
|
|
121
|
+
# },
|
|
122
|
+
# "api_version": "v1",
|
|
123
|
+
# "api_key": "xyz789"
|
|
124
|
+
# }
|
|
125
|
+
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
type = "config"
|
|
129
|
+
_private_attributes = "private"
|
|
130
|
+
public: typing.Optional[dict] = None
|
|
131
|
+
private: typing.Optional[dict] = None
|
|
132
|
+
|
|
133
|
+
def attributes(self):
|
|
134
|
+
res = {}
|
|
135
|
+
if self.public:
|
|
136
|
+
res = merge(res, self.public)
|
|
137
|
+
if self.private:
|
|
138
|
+
res = merge(res, self.private)
|
|
139
|
+
return res
|
|
100
140
|
|
|
101
141
|
|
|
102
142
|
class DatastoreProfileKafkaTarget(DatastoreProfile):
|
|
@@ -494,7 +534,7 @@ class DatastoreProfile2Json(pydantic.v1.BaseModel):
|
|
|
494
534
|
"gcs": DatastoreProfileGCS,
|
|
495
535
|
"az": DatastoreProfileAzureBlob,
|
|
496
536
|
"hdfs": DatastoreProfileHdfs,
|
|
497
|
-
"
|
|
537
|
+
"config": ConfigProfile,
|
|
498
538
|
}
|
|
499
539
|
if datastore_type in ds_profile_factory:
|
|
500
540
|
return ds_profile_factory[datastore_type].parse_obj(decoded_dict)
|
mlrun/datastore/sources.py
CHANGED
|
@@ -951,8 +951,7 @@ class OnlineSource(BaseSourceDriver):
|
|
|
951
951
|
is_explicit_ack_supported(context)
|
|
952
952
|
and mlrun.mlconf.is_explicit_ack_enabled()
|
|
953
953
|
)
|
|
954
|
-
|
|
955
|
-
src_class = storey.SyncEmitSource(
|
|
954
|
+
src_class = storey.AsyncEmitSource(
|
|
956
955
|
context=context,
|
|
957
956
|
key_field=self.key_field or key_field,
|
|
958
957
|
full_event=True,
|