mlrun 1.8.0rc10__py3-none-any.whl → 1.8.0rc13__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/artifacts/document.py +32 -6
- mlrun/common/constants.py +1 -0
- mlrun/common/formatters/artifact.py +1 -1
- mlrun/common/schemas/__init__.py +2 -0
- mlrun/common/schemas/model_monitoring/__init__.py +1 -0
- mlrun/common/schemas/model_monitoring/constants.py +6 -0
- mlrun/common/schemas/model_monitoring/model_endpoints.py +35 -0
- mlrun/common/schemas/partition.py +23 -18
- mlrun/datastore/vectorstore.py +69 -26
- mlrun/db/base.py +14 -0
- mlrun/db/httpdb.py +48 -1
- mlrun/db/nopdb.py +13 -0
- mlrun/execution.py +43 -11
- mlrun/feature_store/steps.py +1 -1
- mlrun/model_monitoring/api.py +26 -19
- mlrun/model_monitoring/applications/_application_steps.py +1 -1
- mlrun/model_monitoring/applications/base.py +44 -7
- mlrun/model_monitoring/applications/context.py +94 -71
- mlrun/projects/pipelines.py +6 -3
- mlrun/projects/project.py +95 -17
- mlrun/runtimes/nuclio/function.py +2 -1
- mlrun/runtimes/nuclio/serving.py +33 -5
- mlrun/serving/__init__.py +8 -0
- mlrun/serving/merger.py +1 -1
- mlrun/serving/remote.py +17 -5
- mlrun/serving/routers.py +36 -87
- mlrun/serving/server.py +6 -2
- mlrun/serving/states.py +162 -13
- mlrun/serving/v2_serving.py +39 -82
- mlrun/utils/helpers.py +6 -0
- mlrun/utils/notifications/notification/base.py +1 -1
- mlrun/utils/notifications/notification/webhook.py +13 -12
- mlrun/utils/notifications/notification_pusher.py +18 -23
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.8.0rc10.dist-info → mlrun-1.8.0rc13.dist-info}/METADATA +10 -10
- {mlrun-1.8.0rc10.dist-info → mlrun-1.8.0rc13.dist-info}/RECORD +40 -40
- {mlrun-1.8.0rc10.dist-info → mlrun-1.8.0rc13.dist-info}/LICENSE +0 -0
- {mlrun-1.8.0rc10.dist-info → mlrun-1.8.0rc13.dist-info}/WHEEL +0 -0
- {mlrun-1.8.0rc10.dist-info → mlrun-1.8.0rc13.dist-info}/entry_points.txt +0 -0
- {mlrun-1.8.0rc10.dist-info → mlrun-1.8.0rc13.dist-info}/top_level.txt +0 -0
mlrun/artifacts/document.py
CHANGED
|
@@ -89,12 +89,17 @@ class MLRunLoader:
|
|
|
89
89
|
A factory class for creating instances of a dynamically defined document loader.
|
|
90
90
|
|
|
91
91
|
Args:
|
|
92
|
-
artifact_key (str): The key for the artifact to be logged.
|
|
93
|
-
|
|
92
|
+
artifact_key (str, optional): The key for the artifact to be logged. Special characters and symbols
|
|
93
|
+
not valid in artifact names will be encoded as their hexadecimal representation. The '%%' pattern
|
|
94
|
+
in the key will be replaced by the hex-encoded version of the source path. Defaults to "doc%%".
|
|
94
95
|
local_path (str): The source path of the document to be loaded.
|
|
95
96
|
loader_spec (DocumentLoaderSpec): Specification for the document loader.
|
|
96
|
-
producer (Optional[Union[MlrunProject, str, MLClientCtx]], optional): The producer of the document
|
|
97
|
+
producer (Optional[Union[MlrunProject, str, MLClientCtx]], optional): The producer of the document.
|
|
98
|
+
If not specified, will try to get the current MLRun context or project.
|
|
99
|
+
Defaults to None.
|
|
97
100
|
upload (bool, optional): Flag indicating whether to upload the document.
|
|
101
|
+
labels (Optional[Dict[str, str]], optional): Key-value labels to attach to the artifact. Defaults to None.
|
|
102
|
+
tag (str, optional): Version tag for the artifact. Defaults to "".
|
|
98
103
|
|
|
99
104
|
Returns:
|
|
100
105
|
DynamicDocumentLoader: An instance of a dynamically defined subclass of BaseLoader.
|
|
@@ -146,6 +151,8 @@ class MLRunLoader:
|
|
|
146
151
|
artifact_key="doc%%",
|
|
147
152
|
producer: Optional[Union["MlrunProject", str, "MLClientCtx"]] = None, # noqa: F821
|
|
148
153
|
upload: bool = False,
|
|
154
|
+
tag: str = "",
|
|
155
|
+
labels: Optional[dict[str, str]] = None,
|
|
149
156
|
):
|
|
150
157
|
# Dynamically import BaseLoader
|
|
151
158
|
from langchain_community.document_loaders.base import BaseLoader
|
|
@@ -158,6 +165,8 @@ class MLRunLoader:
|
|
|
158
165
|
artifact_key,
|
|
159
166
|
producer,
|
|
160
167
|
upload,
|
|
168
|
+
tag,
|
|
169
|
+
labels,
|
|
161
170
|
):
|
|
162
171
|
self.producer = producer
|
|
163
172
|
self.artifact_key = (
|
|
@@ -168,6 +177,8 @@ class MLRunLoader:
|
|
|
168
177
|
self.loader_spec = loader_spec
|
|
169
178
|
self.local_path = local_path
|
|
170
179
|
self.upload = upload
|
|
180
|
+
self.tag = tag
|
|
181
|
+
self.labels = labels
|
|
171
182
|
|
|
172
183
|
# Resolve the producer
|
|
173
184
|
if not self.producer:
|
|
@@ -181,9 +192,11 @@ class MLRunLoader:
|
|
|
181
192
|
document_loader_spec=self.loader_spec,
|
|
182
193
|
local_path=self.local_path,
|
|
183
194
|
upload=self.upload,
|
|
195
|
+
labels=self.labels,
|
|
196
|
+
tag=self.tag,
|
|
184
197
|
)
|
|
185
198
|
res = artifact.to_langchain_documents()
|
|
186
|
-
|
|
199
|
+
return res
|
|
187
200
|
|
|
188
201
|
# Return an instance of the dynamically defined subclass
|
|
189
202
|
instance = DynamicDocumentLoader(
|
|
@@ -192,6 +205,8 @@ class MLRunLoader:
|
|
|
192
205
|
loader_spec=loader_spec,
|
|
193
206
|
producer=producer,
|
|
194
207
|
upload=upload,
|
|
208
|
+
tag=tag,
|
|
209
|
+
labels=labels,
|
|
195
210
|
)
|
|
196
211
|
return instance
|
|
197
212
|
|
|
@@ -257,6 +272,9 @@ class DocumentArtifact(Artifact):
|
|
|
257
272
|
METADATA_CHUNK_KEY = "mlrun_chunk"
|
|
258
273
|
METADATA_ARTIFACT_URI_KEY = "mlrun_object_uri"
|
|
259
274
|
METADATA_ARTIFACT_TARGET_PATH_KEY = "mlrun_target_path"
|
|
275
|
+
METADATA_ARTIFACT_TAG = "mlrun_tag"
|
|
276
|
+
METADATA_ARTIFACT_KEY = "mlrun_key"
|
|
277
|
+
METADATA_ARTIFACT_PROJECT = "mlrun_project"
|
|
260
278
|
|
|
261
279
|
def __init__(
|
|
262
280
|
self,
|
|
@@ -331,6 +349,10 @@ class DocumentArtifact(Artifact):
|
|
|
331
349
|
metadata[self.METADATA_ORIGINAL_SOURCE_KEY] = self.spec.original_source
|
|
332
350
|
metadata[self.METADATA_SOURCE_KEY] = self.get_source()
|
|
333
351
|
metadata[self.METADATA_ARTIFACT_URI_KEY] = self.uri
|
|
352
|
+
metadata[self.METADATA_ARTIFACT_TAG] = self.tag or "latest"
|
|
353
|
+
metadata[self.METADATA_ARTIFACT_KEY] = self.key
|
|
354
|
+
metadata[self.METADATA_ARTIFACT_PROJECT] = self.metadata.project
|
|
355
|
+
|
|
334
356
|
if self.get_target_path():
|
|
335
357
|
metadata[self.METADATA_ARTIFACT_TARGET_PATH_KEY] = (
|
|
336
358
|
self.get_target_path()
|
|
@@ -346,7 +368,7 @@ class DocumentArtifact(Artifact):
|
|
|
346
368
|
idx = idx + 1
|
|
347
369
|
return results
|
|
348
370
|
|
|
349
|
-
def collection_add(self, collection_id: str) ->
|
|
371
|
+
def collection_add(self, collection_id: str) -> bool:
|
|
350
372
|
"""
|
|
351
373
|
Add a collection ID to the artifact's collection list.
|
|
352
374
|
|
|
@@ -361,8 +383,10 @@ class DocumentArtifact(Artifact):
|
|
|
361
383
|
"""
|
|
362
384
|
if collection_id not in self.spec.collections:
|
|
363
385
|
self.spec.collections[collection_id] = "1"
|
|
386
|
+
return True
|
|
387
|
+
return False
|
|
364
388
|
|
|
365
|
-
def collection_remove(self, collection_id: str) ->
|
|
389
|
+
def collection_remove(self, collection_id: str) -> bool:
|
|
366
390
|
"""
|
|
367
391
|
Remove a collection ID from the artifact's collection list.
|
|
368
392
|
|
|
@@ -376,3 +400,5 @@ class DocumentArtifact(Artifact):
|
|
|
376
400
|
"""
|
|
377
401
|
if collection_id in self.spec.collections:
|
|
378
402
|
self.spec.collections.pop(collection_id)
|
|
403
|
+
return True
|
|
404
|
+
return False
|
mlrun/common/constants.py
CHANGED
mlrun/common/schemas/__init__.py
CHANGED
|
@@ -146,8 +146,10 @@ from .model_monitoring import (
|
|
|
146
146
|
GrafanaTable,
|
|
147
147
|
GrafanaTimeSeriesTarget,
|
|
148
148
|
ModelEndpoint,
|
|
149
|
+
ModelEndpointCreationStrategy,
|
|
149
150
|
ModelEndpointList,
|
|
150
151
|
ModelEndpointMetadata,
|
|
152
|
+
ModelEndpointSchema,
|
|
151
153
|
ModelEndpointSpec,
|
|
152
154
|
ModelEndpointStatus,
|
|
153
155
|
ModelMonitoringMode,
|
|
@@ -71,6 +71,12 @@ class ModelEndpointSchema(MonitoringStrEnum):
|
|
|
71
71
|
DRIFT_MEASURES = "drift_measures"
|
|
72
72
|
|
|
73
73
|
|
|
74
|
+
class ModelEndpointCreationStrategy(MonitoringStrEnum):
|
|
75
|
+
INPLACE = "inplace"
|
|
76
|
+
ARCHIVE = "archive"
|
|
77
|
+
OVERWRITE = "overwrite"
|
|
78
|
+
|
|
79
|
+
|
|
74
80
|
class EventFieldType:
|
|
75
81
|
FUNCTION_URI = "function_uri"
|
|
76
82
|
FUNCTION = "function"
|
|
@@ -117,6 +117,10 @@ class ModelEndpointMetadata(ObjectMetadata, ModelEndpointParser):
|
|
|
117
117
|
endpoint_type: EndpointType = EndpointType.NODE_EP
|
|
118
118
|
uid: Optional[constr(regex=MODEL_ENDPOINT_ID_PATTERN)]
|
|
119
119
|
|
|
120
|
+
@classmethod
|
|
121
|
+
def mutable_fields(cls):
|
|
122
|
+
return ["labels"]
|
|
123
|
+
|
|
120
124
|
|
|
121
125
|
class ModelEndpointSpec(ObjectSpec, ModelEndpointParser):
|
|
122
126
|
model_uid: Optional[str] = ""
|
|
@@ -136,6 +140,21 @@ class ModelEndpointSpec(ObjectSpec, ModelEndpointParser):
|
|
|
136
140
|
children_uids: Optional[list[str]] = []
|
|
137
141
|
monitoring_feature_set_uri: Optional[str] = ""
|
|
138
142
|
|
|
143
|
+
@classmethod
|
|
144
|
+
def mutable_fields(cls):
|
|
145
|
+
return [
|
|
146
|
+
"model_uid",
|
|
147
|
+
"model_name",
|
|
148
|
+
"model_db_key",
|
|
149
|
+
"model_tag",
|
|
150
|
+
"model_class",
|
|
151
|
+
"function_uid",
|
|
152
|
+
"feature_names",
|
|
153
|
+
"label_names",
|
|
154
|
+
"children",
|
|
155
|
+
"children_uids",
|
|
156
|
+
]
|
|
157
|
+
|
|
139
158
|
|
|
140
159
|
class ModelEndpointStatus(ObjectStatus, ModelEndpointParser):
|
|
141
160
|
state: Optional[str] = "unknown" # will be updated according to the function state
|
|
@@ -152,6 +171,14 @@ class ModelEndpointStatus(ObjectStatus, ModelEndpointParser):
|
|
|
152
171
|
drift_measures: Optional[dict] = {}
|
|
153
172
|
drift_measures_timestamp: Optional[datetime] = None
|
|
154
173
|
|
|
174
|
+
@classmethod
|
|
175
|
+
def mutable_fields(cls):
|
|
176
|
+
return [
|
|
177
|
+
"monitoring_mode",
|
|
178
|
+
"first_request",
|
|
179
|
+
"last_request",
|
|
180
|
+
]
|
|
181
|
+
|
|
155
182
|
|
|
156
183
|
class ModelEndpoint(BaseModel):
|
|
157
184
|
kind: ObjectKind = Field(ObjectKind.model_endpoint, const=True)
|
|
@@ -159,6 +186,14 @@ class ModelEndpoint(BaseModel):
|
|
|
159
186
|
spec: ModelEndpointSpec
|
|
160
187
|
status: ModelEndpointStatus
|
|
161
188
|
|
|
189
|
+
@classmethod
|
|
190
|
+
def mutable_fields(cls):
|
|
191
|
+
return (
|
|
192
|
+
ModelEndpointMetadata.mutable_fields()
|
|
193
|
+
+ ModelEndpointSpec.mutable_fields()
|
|
194
|
+
+ ModelEndpointStatus.mutable_fields()
|
|
195
|
+
)
|
|
196
|
+
|
|
162
197
|
def flat_dict(self) -> dict[str, Any]:
|
|
163
198
|
"""Generate a flattened `ModelEndpoint` dictionary. The flattened dictionary result is important for storing
|
|
164
199
|
the model endpoint object in the database.
|
|
@@ -46,24 +46,23 @@ class PartitionInterval(StrEnum):
|
|
|
46
46
|
return timedelta(weeks=1)
|
|
47
47
|
|
|
48
48
|
@classmethod
|
|
49
|
-
def
|
|
49
|
+
def from_expression(cls, partition_expression: str):
|
|
50
50
|
"""
|
|
51
|
-
Returns the corresponding PartitionInterval for a given partition
|
|
51
|
+
Returns the corresponding PartitionInterval for a given partition expression,
|
|
52
52
|
or None if the function is not mapped.
|
|
53
53
|
|
|
54
|
-
:param
|
|
55
|
-
:return: PartitionInterval corresponding to the
|
|
54
|
+
:param partition_expression: The partition expression to map to an interval.
|
|
55
|
+
:return: PartitionInterval corresponding to the expression, or `month` if no match is found.
|
|
56
56
|
"""
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return cls
|
|
66
|
-
raise KeyError(f"Partition function: {partition_function} isn't supported")
|
|
57
|
+
|
|
58
|
+
# Match the provided function string to the correct interval
|
|
59
|
+
partition_expression = partition_expression.upper()
|
|
60
|
+
if "YEARWEEK" in partition_expression:
|
|
61
|
+
return cls.YEARWEEK
|
|
62
|
+
elif "DAYOFMONTH" in partition_expression:
|
|
63
|
+
return cls.DAY
|
|
64
|
+
else:
|
|
65
|
+
return cls.MONTH
|
|
67
66
|
|
|
68
67
|
def get_partition_info(
|
|
69
68
|
self,
|
|
@@ -120,11 +119,17 @@ class PartitionInterval(StrEnum):
|
|
|
120
119
|
year, week, _ = current_datetime.isocalendar()
|
|
121
120
|
return f"{year}{week:02d}"
|
|
122
121
|
|
|
123
|
-
def get_partition_expression(self):
|
|
122
|
+
def get_partition_expression(self, column_name: str):
|
|
124
123
|
if self == PartitionInterval.YEARWEEK:
|
|
125
|
-
return "YEARWEEK(
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
return f"YEARWEEK({column_name}, 1)"
|
|
125
|
+
elif self == PartitionInterval.DAY:
|
|
126
|
+
# generates value in format %Y%m%d in mysql
|
|
127
|
+
# mysql query example: `select YEAR(NOW())*10000 + MONTH(NOW())*100 + DAY(NOW());`
|
|
128
|
+
return f"YEAR({column_name}) * 10000 + MONTH({column_name}) * 100 + DAY({column_name})"
|
|
129
|
+
elif self == PartitionInterval.MONTH:
|
|
130
|
+
# generates value in format %Y%m in mysql
|
|
131
|
+
# mysql query example: `select YEAR(NOW())*100 + MONTH(NOW());`
|
|
132
|
+
return f"YEAR({column_name}) * 100 + MONTH({column_name})"
|
|
128
133
|
|
|
129
134
|
def get_number_of_partitions(self, days: int) -> int:
|
|
130
135
|
# Calculate the number partitions based on given number of days
|
mlrun/datastore/vectorstore.py
CHANGED
|
@@ -19,19 +19,42 @@ from typing import Optional, Union
|
|
|
19
19
|
from mlrun.artifacts import DocumentArtifact
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
def
|
|
23
|
-
#
|
|
24
|
-
|
|
22
|
+
def find_existing_attribute(obj, base_name="name", parent_name="collection"):
|
|
23
|
+
# Define all possible patterns
|
|
24
|
+
|
|
25
|
+
return None
|
|
26
|
+
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
def _extract_collection_name(vectorstore: "VectorStore") -> str: # noqa: F821
|
|
29
|
+
patterns = [
|
|
30
|
+
"collection.name",
|
|
31
|
+
"collection._name",
|
|
32
|
+
"_collection.name",
|
|
33
|
+
"_collection._name",
|
|
34
|
+
"collection_name",
|
|
35
|
+
"_collection_name",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
def resolve_attribute(obj, pattern):
|
|
39
|
+
if "." in pattern:
|
|
40
|
+
parts = pattern.split(".")
|
|
41
|
+
current = vectorstore
|
|
42
|
+
for part in parts:
|
|
43
|
+
if hasattr(current, part):
|
|
44
|
+
current = getattr(current, part)
|
|
45
|
+
else:
|
|
46
|
+
return None
|
|
47
|
+
return current
|
|
48
|
+
else:
|
|
49
|
+
return getattr(obj, pattern, None)
|
|
31
50
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
51
|
+
for pattern in patterns:
|
|
52
|
+
try:
|
|
53
|
+
value = resolve_attribute(vectorstore, pattern)
|
|
54
|
+
if value is not None:
|
|
55
|
+
return value
|
|
56
|
+
except (AttributeError, TypeError):
|
|
57
|
+
continue
|
|
35
58
|
|
|
36
59
|
# If we get here, we couldn't find a valid collection name
|
|
37
60
|
raise ValueError(
|
|
@@ -82,6 +105,19 @@ class VectorStoreCollection:
|
|
|
82
105
|
# Forward the attribute setting to _collection_impl
|
|
83
106
|
setattr(self._collection_impl, name, value)
|
|
84
107
|
|
|
108
|
+
def _get_mlrun_project_name(self):
|
|
109
|
+
import mlrun
|
|
110
|
+
|
|
111
|
+
if self._mlrun_context and isinstance(
|
|
112
|
+
self._mlrun_context, mlrun.projects.MlrunProject
|
|
113
|
+
):
|
|
114
|
+
return self._mlrun_context.name
|
|
115
|
+
if self._mlrun_context and isinstance(
|
|
116
|
+
self._mlrun_context, mlrun.execution.MLClientCtx
|
|
117
|
+
):
|
|
118
|
+
return self._mlrun_context.get_project_object().name
|
|
119
|
+
return None
|
|
120
|
+
|
|
85
121
|
def delete(self, *args, **kwargs):
|
|
86
122
|
self._collection_impl.delete(*args, **kwargs)
|
|
87
123
|
|
|
@@ -106,13 +142,22 @@ class VectorStoreCollection:
|
|
|
106
142
|
"""
|
|
107
143
|
if self._mlrun_context:
|
|
108
144
|
for document in documents:
|
|
109
|
-
|
|
110
|
-
DocumentArtifact.
|
|
145
|
+
mlrun_key = document.metadata.get(
|
|
146
|
+
DocumentArtifact.METADATA_ARTIFACT_KEY, None
|
|
147
|
+
)
|
|
148
|
+
mlrun_project = document.metadata.get(
|
|
149
|
+
DocumentArtifact.METADATA_ARTIFACT_PROJECT, None
|
|
111
150
|
)
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
151
|
+
|
|
152
|
+
if mlrun_key and mlrun_project == self._get_mlrun_project_name():
|
|
153
|
+
mlrun_tag = document.metadata.get(
|
|
154
|
+
DocumentArtifact.METADATA_ARTIFACT_TAG, None
|
|
155
|
+
)
|
|
156
|
+
artifact = self._mlrun_context.get_artifact(
|
|
157
|
+
key=mlrun_key, tag=mlrun_tag
|
|
158
|
+
)
|
|
159
|
+
if artifact.collection_add(self.collection_name):
|
|
160
|
+
self._mlrun_context.update_artifact(artifact)
|
|
116
161
|
|
|
117
162
|
return self._collection_impl.add_documents(documents, **kwargs)
|
|
118
163
|
|
|
@@ -159,8 +204,7 @@ class VectorStoreCollection:
|
|
|
159
204
|
)
|
|
160
205
|
for index, artifact in enumerate(artifacts):
|
|
161
206
|
documents = artifact.to_langchain_documents(splitter)
|
|
162
|
-
artifact.collection_add(self.collection_name)
|
|
163
|
-
if self._mlrun_context:
|
|
207
|
+
if artifact.collection_add(self.collection_name) and self._mlrun_context:
|
|
164
208
|
self._mlrun_context.update_artifact(artifact)
|
|
165
209
|
if user_ids:
|
|
166
210
|
num_of_documents = len(documents)
|
|
@@ -182,8 +226,8 @@ class VectorStoreCollection:
|
|
|
182
226
|
Args:
|
|
183
227
|
artifact (DocumentArtifact): The artifact from which the current object should be removed.
|
|
184
228
|
"""
|
|
185
|
-
|
|
186
|
-
if self._mlrun_context:
|
|
229
|
+
|
|
230
|
+
if artifact.collection_remove(self.collection_name) and self._mlrun_context:
|
|
187
231
|
self._mlrun_context.update_artifact(artifact)
|
|
188
232
|
|
|
189
233
|
def delete_artifacts(self, artifacts: list[DocumentArtifact]):
|
|
@@ -201,16 +245,15 @@ class VectorStoreCollection:
|
|
|
201
245
|
"""
|
|
202
246
|
store_class = self._collection_impl.__class__.__name__.lower()
|
|
203
247
|
for artifact in artifacts:
|
|
204
|
-
artifact.collection_remove(self.collection_name)
|
|
205
|
-
if self._mlrun_context:
|
|
248
|
+
if artifact.collection_remove(self.collection_name) and self._mlrun_context:
|
|
206
249
|
self._mlrun_context.update_artifact(artifact)
|
|
207
250
|
|
|
208
251
|
if store_class == "milvus":
|
|
209
252
|
expr = f"{DocumentArtifact.METADATA_SOURCE_KEY} == '{artifact.get_source()}'"
|
|
210
|
-
|
|
253
|
+
self._collection_impl.delete(expr=expr)
|
|
211
254
|
elif store_class == "chroma":
|
|
212
255
|
where = {DocumentArtifact.METADATA_SOURCE_KEY: artifact.get_source()}
|
|
213
|
-
|
|
256
|
+
self._collection_impl.delete(where=where)
|
|
214
257
|
|
|
215
258
|
elif (
|
|
216
259
|
hasattr(self._collection_impl, "delete")
|
|
@@ -222,7 +265,7 @@ class VectorStoreCollection:
|
|
|
222
265
|
DocumentArtifact.METADATA_SOURCE_KEY: artifact.get_source()
|
|
223
266
|
}
|
|
224
267
|
}
|
|
225
|
-
|
|
268
|
+
self._collection_impl.delete(filter=filter)
|
|
226
269
|
else:
|
|
227
270
|
raise NotImplementedError(
|
|
228
271
|
f"delete_artifacts() operation not supported for {store_class}"
|
mlrun/db/base.py
CHANGED
|
@@ -23,6 +23,7 @@ import mlrun.common
|
|
|
23
23
|
import mlrun.common.formatters
|
|
24
24
|
import mlrun.common.runtimes.constants
|
|
25
25
|
import mlrun.common.schemas
|
|
26
|
+
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
26
27
|
import mlrun.common.schemas.model_monitoring.model_endpoints as mm_endpoints
|
|
27
28
|
import mlrun.model_monitoring
|
|
28
29
|
|
|
@@ -58,6 +59,15 @@ class RunDBInterface(ABC):
|
|
|
58
59
|
def abort_run(self, uid, project="", iter=0, timeout=45, status_text=""):
|
|
59
60
|
pass
|
|
60
61
|
|
|
62
|
+
@abstractmethod
|
|
63
|
+
def push_run_notifications(
|
|
64
|
+
self,
|
|
65
|
+
uid,
|
|
66
|
+
project="",
|
|
67
|
+
timeout=45,
|
|
68
|
+
):
|
|
69
|
+
pass
|
|
70
|
+
|
|
61
71
|
@abstractmethod
|
|
62
72
|
def read_run(
|
|
63
73
|
self,
|
|
@@ -666,6 +676,9 @@ class RunDBInterface(ABC):
|
|
|
666
676
|
def create_model_endpoint(
|
|
667
677
|
self,
|
|
668
678
|
model_endpoint: mlrun.common.schemas.ModelEndpoint,
|
|
679
|
+
creation_strategy: Optional[
|
|
680
|
+
mm_constants.ModelEndpointCreationStrategy
|
|
681
|
+
] = mm_constants.ModelEndpointCreationStrategy.INPLACE,
|
|
669
682
|
) -> mlrun.common.schemas.ModelEndpoint:
|
|
670
683
|
pass
|
|
671
684
|
|
|
@@ -688,6 +701,7 @@ class RunDBInterface(ABC):
|
|
|
688
701
|
function_name: Optional[str] = None,
|
|
689
702
|
function_tag: Optional[str] = None,
|
|
690
703
|
model_name: Optional[str] = None,
|
|
704
|
+
model_tag: Optional[str] = None,
|
|
691
705
|
labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
|
|
692
706
|
start: Optional[datetime.datetime] = None,
|
|
693
707
|
end: Optional[datetime.datetime] = None,
|
mlrun/db/httpdb.py
CHANGED
|
@@ -35,6 +35,7 @@ import mlrun.common.constants
|
|
|
35
35
|
import mlrun.common.formatters
|
|
36
36
|
import mlrun.common.runtimes
|
|
37
37
|
import mlrun.common.schemas
|
|
38
|
+
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
38
39
|
import mlrun.common.schemas.model_monitoring.model_endpoints as mm_endpoints
|
|
39
40
|
import mlrun.common.types
|
|
40
41
|
import mlrun.platforms
|
|
@@ -755,6 +756,34 @@ class HTTPRunDB(RunDBInterface):
|
|
|
755
756
|
)
|
|
756
757
|
return None
|
|
757
758
|
|
|
759
|
+
def push_run_notifications(
|
|
760
|
+
self,
|
|
761
|
+
uid,
|
|
762
|
+
project="",
|
|
763
|
+
timeout=45,
|
|
764
|
+
):
|
|
765
|
+
"""
|
|
766
|
+
Push notifications for a run.
|
|
767
|
+
|
|
768
|
+
:param uid: Unique ID of the run.
|
|
769
|
+
:param project: Project that the run belongs to.
|
|
770
|
+
:returns: :py:class:`~mlrun.common.schemas.BackgroundTask`.
|
|
771
|
+
"""
|
|
772
|
+
project = project or config.default_project
|
|
773
|
+
|
|
774
|
+
response = self.api_call(
|
|
775
|
+
"POST",
|
|
776
|
+
path=f"projects/{project}/runs/{uid}/push_notifications",
|
|
777
|
+
error="Failed push notifications",
|
|
778
|
+
timeout=timeout,
|
|
779
|
+
)
|
|
780
|
+
if response.status_code == http.HTTPStatus.ACCEPTED:
|
|
781
|
+
background_task = mlrun.common.schemas.BackgroundTask(**response.json())
|
|
782
|
+
return self._wait_for_background_task_to_reach_terminal_state(
|
|
783
|
+
background_task.metadata.name, project=project
|
|
784
|
+
)
|
|
785
|
+
return None
|
|
786
|
+
|
|
758
787
|
def read_run(
|
|
759
788
|
self,
|
|
760
789
|
uid,
|
|
@@ -3582,12 +3611,24 @@ class HTTPRunDB(RunDBInterface):
|
|
|
3582
3611
|
def create_model_endpoint(
|
|
3583
3612
|
self,
|
|
3584
3613
|
model_endpoint: mlrun.common.schemas.ModelEndpoint,
|
|
3614
|
+
creation_strategy: Optional[
|
|
3615
|
+
mm_constants.ModelEndpointCreationStrategy
|
|
3616
|
+
] = mm_constants.ModelEndpointCreationStrategy.INPLACE,
|
|
3585
3617
|
) -> mlrun.common.schemas.ModelEndpoint:
|
|
3586
3618
|
"""
|
|
3587
3619
|
Creates a DB record with the given model_endpoint record.
|
|
3588
3620
|
|
|
3589
3621
|
:param model_endpoint: An object representing the model endpoint.
|
|
3590
|
-
|
|
3622
|
+
:param creation_strategy: Strategy for creating or updating the model endpoint:
|
|
3623
|
+
* **overwrite**:
|
|
3624
|
+
1. If model endpoints with the same name exist, delete the `latest` one.
|
|
3625
|
+
2. Create a new model endpoint entry and set it as `latest`.
|
|
3626
|
+
* **inplace** (default):
|
|
3627
|
+
1. If model endpoints with the same name exist, update the `latest` entry.
|
|
3628
|
+
2. Otherwise, create a new entry.
|
|
3629
|
+
* **archive**:
|
|
3630
|
+
1. If model endpoints with the same name exist, preserve them.
|
|
3631
|
+
2. Create a new model endpoint with the same name and set it to `latest`.
|
|
3591
3632
|
:return: The created model endpoint object.
|
|
3592
3633
|
"""
|
|
3593
3634
|
|
|
@@ -3596,6 +3637,9 @@ class HTTPRunDB(RunDBInterface):
|
|
|
3596
3637
|
method=mlrun.common.types.HTTPMethod.POST,
|
|
3597
3638
|
path=path,
|
|
3598
3639
|
body=model_endpoint.json(),
|
|
3640
|
+
params={
|
|
3641
|
+
"creation_strategy": creation_strategy,
|
|
3642
|
+
},
|
|
3599
3643
|
)
|
|
3600
3644
|
return mlrun.common.schemas.ModelEndpoint(**response.json())
|
|
3601
3645
|
|
|
@@ -3637,6 +3681,7 @@ class HTTPRunDB(RunDBInterface):
|
|
|
3637
3681
|
function_name: Optional[str] = None,
|
|
3638
3682
|
function_tag: Optional[str] = None,
|
|
3639
3683
|
model_name: Optional[str] = None,
|
|
3684
|
+
model_tag: Optional[str] = None,
|
|
3640
3685
|
labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
|
|
3641
3686
|
start: Optional[datetime] = None,
|
|
3642
3687
|
end: Optional[datetime] = None,
|
|
@@ -3653,6 +3698,7 @@ class HTTPRunDB(RunDBInterface):
|
|
|
3653
3698
|
:param function_name: The name of the function
|
|
3654
3699
|
:param function_tag: The tag of the function
|
|
3655
3700
|
:param model_name: The name of the model
|
|
3701
|
+
:param model_tag: The tag of the model
|
|
3656
3702
|
:param labels: A list of labels to filter by. (see mlrun.common.schemas.LabelsModel)
|
|
3657
3703
|
:param start: The start time to filter by.Corresponding to the `created` field.
|
|
3658
3704
|
:param end: The end time to filter by. Corresponding to the `created` field.
|
|
@@ -3671,6 +3717,7 @@ class HTTPRunDB(RunDBInterface):
|
|
|
3671
3717
|
params={
|
|
3672
3718
|
"name": name,
|
|
3673
3719
|
"model_name": model_name,
|
|
3720
|
+
"model_tag": model_tag,
|
|
3674
3721
|
"function_name": function_name,
|
|
3675
3722
|
"function_tag": function_tag,
|
|
3676
3723
|
"label": labels,
|
mlrun/db/nopdb.py
CHANGED
|
@@ -20,6 +20,7 @@ import mlrun.alerts
|
|
|
20
20
|
import mlrun.common.formatters
|
|
21
21
|
import mlrun.common.runtimes.constants
|
|
22
22
|
import mlrun.common.schemas
|
|
23
|
+
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
23
24
|
import mlrun.errors
|
|
24
25
|
import mlrun.lists
|
|
25
26
|
import mlrun.model_monitoring
|
|
@@ -75,6 +76,14 @@ class NopDB(RunDBInterface):
|
|
|
75
76
|
def abort_run(self, uid, project="", iter=0, timeout=45, status_text=""):
|
|
76
77
|
pass
|
|
77
78
|
|
|
79
|
+
def push_run_notifications(
|
|
80
|
+
self,
|
|
81
|
+
uid,
|
|
82
|
+
project="",
|
|
83
|
+
timeout=45,
|
|
84
|
+
):
|
|
85
|
+
pass
|
|
86
|
+
|
|
78
87
|
def list_runtime_resources(
|
|
79
88
|
self,
|
|
80
89
|
project: Optional[str] = None,
|
|
@@ -575,6 +584,9 @@ class NopDB(RunDBInterface):
|
|
|
575
584
|
def create_model_endpoint(
|
|
576
585
|
self,
|
|
577
586
|
model_endpoint: mlrun.common.schemas.ModelEndpoint,
|
|
587
|
+
creation_strategy: Optional[
|
|
588
|
+
mm_constants.ModelEndpointCreationStrategy
|
|
589
|
+
] = mm_constants.ModelEndpointCreationStrategy.INPLACE,
|
|
578
590
|
) -> mlrun.common.schemas.ModelEndpoint:
|
|
579
591
|
pass
|
|
580
592
|
|
|
@@ -595,6 +607,7 @@ class NopDB(RunDBInterface):
|
|
|
595
607
|
function_name: Optional[str] = None,
|
|
596
608
|
function_tag: Optional[str] = None,
|
|
597
609
|
model_name: Optional[str] = None,
|
|
610
|
+
model_tag: Optional[str] = None,
|
|
598
611
|
labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
|
|
599
612
|
start: Optional[datetime.datetime] = None,
|
|
600
613
|
end: Optional[datetime.datetime] = None,
|