mlrun 1.8.0rc15__py3-none-any.whl → 1.8.0rc17__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 +9 -4
- mlrun/common/model_monitoring/helpers.py +2 -20
- mlrun/common/schemas/client_spec.py +1 -0
- mlrun/common/schemas/model_monitoring/constants.py +3 -0
- mlrun/config.py +0 -4
- mlrun/datastore/datastore_profile.py +31 -2
- mlrun/db/base.py +1 -1
- mlrun/db/httpdb.py +3 -2
- mlrun/db/nopdb.py +1 -1
- mlrun/execution.py +6 -0
- mlrun/model_monitoring/__init__.py +0 -2
- mlrun/model_monitoring/applications/base.py +23 -15
- mlrun/model_monitoring/applications/context.py +3 -8
- mlrun/model_monitoring/db/tsdb/__init__.py +12 -0
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +9 -2
- mlrun/model_monitoring/helpers.py +60 -16
- mlrun/model_monitoring/writer.py +0 -53
- mlrun/projects/pipelines.py +1 -1
- mlrun/projects/project.py +24 -2
- mlrun/secrets.py +1 -1
- mlrun/serving/routers.py +10 -86
- mlrun/serving/states.py +9 -1
- mlrun/serving/v2_serving.py +13 -120
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.8.0rc15.dist-info → mlrun-1.8.0rc17.dist-info}/METADATA +12 -10
- {mlrun-1.8.0rc15.dist-info → mlrun-1.8.0rc17.dist-info}/RECORD +30 -30
- {mlrun-1.8.0rc15.dist-info → mlrun-1.8.0rc17.dist-info}/LICENSE +0 -0
- {mlrun-1.8.0rc15.dist-info → mlrun-1.8.0rc17.dist-info}/WHEEL +0 -0
- {mlrun-1.8.0rc15.dist-info → mlrun-1.8.0rc17.dist-info}/entry_points.txt +0 -0
- {mlrun-1.8.0rc15.dist-info → mlrun-1.8.0rc17.dist-info}/top_level.txt +0 -0
mlrun/artifacts/document.py
CHANGED
|
@@ -47,6 +47,7 @@ class DocumentLoaderSpec(ModelObj):
|
|
|
47
47
|
self,
|
|
48
48
|
loader_class_name: str = "langchain_community.document_loaders.TextLoader",
|
|
49
49
|
src_name: str = "file_path",
|
|
50
|
+
download_object: bool = False,
|
|
50
51
|
kwargs: Optional[dict] = None,
|
|
51
52
|
):
|
|
52
53
|
"""
|
|
@@ -56,7 +57,9 @@ class DocumentLoaderSpec(ModelObj):
|
|
|
56
57
|
loader_class_name (str): The name of the loader class to use.
|
|
57
58
|
src_name (str): The source name for the document.
|
|
58
59
|
kwargs (Optional[dict]): Additional keyword arguments to pass to the loader class.
|
|
59
|
-
|
|
60
|
+
download_object (bool, optional): If True, the file will be downloaded before launching
|
|
61
|
+
the loader. If False, the loader accepts a link that should not be downloaded.
|
|
62
|
+
Defaults to False.
|
|
60
63
|
Example:
|
|
61
64
|
>>> # Create a loader specification for PDF documents
|
|
62
65
|
>>> loader_spec = DocumentLoaderSpec(
|
|
@@ -72,6 +75,7 @@ class DocumentLoaderSpec(ModelObj):
|
|
|
72
75
|
"""
|
|
73
76
|
self.loader_class_name = loader_class_name
|
|
74
77
|
self.src_name = src_name
|
|
78
|
+
self.download_object = download_object
|
|
75
79
|
self.kwargs = kwargs
|
|
76
80
|
|
|
77
81
|
def make_loader(self, src_path):
|
|
@@ -251,6 +255,9 @@ class DocumentArtifact(Artifact):
|
|
|
251
255
|
"collections",
|
|
252
256
|
"original_source",
|
|
253
257
|
]
|
|
258
|
+
_exclude_fields_from_uid_hash = ArtifactSpec._exclude_fields_from_uid_hash + [
|
|
259
|
+
"collections",
|
|
260
|
+
]
|
|
254
261
|
|
|
255
262
|
def __init__(
|
|
256
263
|
self,
|
|
@@ -270,7 +277,6 @@ class DocumentArtifact(Artifact):
|
|
|
270
277
|
METADATA_SOURCE_KEY = "source"
|
|
271
278
|
METADATA_ORIGINAL_SOURCE_KEY = "original_source"
|
|
272
279
|
METADATA_CHUNK_KEY = "mlrun_chunk"
|
|
273
|
-
METADATA_ARTIFACT_URI_KEY = "mlrun_object_uri"
|
|
274
280
|
METADATA_ARTIFACT_TARGET_PATH_KEY = "mlrun_target_path"
|
|
275
281
|
METADATA_ARTIFACT_TAG = "mlrun_tag"
|
|
276
282
|
METADATA_ARTIFACT_KEY = "mlrun_key"
|
|
@@ -321,7 +327,7 @@ class DocumentArtifact(Artifact):
|
|
|
321
327
|
"""
|
|
322
328
|
|
|
323
329
|
loader_spec = DocumentLoaderSpec.from_dict(self.spec.document_loader)
|
|
324
|
-
if self.get_target_path():
|
|
330
|
+
if loader_spec.download_object and self.get_target_path():
|
|
325
331
|
with tempfile.NamedTemporaryFile() as tmp_file:
|
|
326
332
|
mlrun.datastore.store_manager.object(
|
|
327
333
|
url=self.get_target_path()
|
|
@@ -348,7 +354,6 @@ class DocumentArtifact(Artifact):
|
|
|
348
354
|
|
|
349
355
|
metadata[self.METADATA_ORIGINAL_SOURCE_KEY] = self.spec.original_source
|
|
350
356
|
metadata[self.METADATA_SOURCE_KEY] = self.get_source()
|
|
351
|
-
metadata[self.METADATA_ARTIFACT_URI_KEY] = self.uri
|
|
352
357
|
metadata[self.METADATA_ARTIFACT_TAG] = self.tag or "latest"
|
|
353
358
|
metadata[self.METADATA_ARTIFACT_KEY] = self.key
|
|
354
359
|
metadata[self.METADATA_ARTIFACT_PROJECT] = self.metadata.project
|
|
@@ -38,12 +38,10 @@ def parse_model_endpoint_store_prefix(store_prefix: str):
|
|
|
38
38
|
|
|
39
39
|
def parse_monitoring_stream_path(
|
|
40
40
|
stream_uri: str, project: str, function_name: typing.Optional[str] = None
|
|
41
|
-
):
|
|
41
|
+
) -> str:
|
|
42
42
|
if stream_uri.startswith("kafka://"):
|
|
43
43
|
if "?topic" in stream_uri:
|
|
44
|
-
raise mlrun.errors.
|
|
45
|
-
"Custom kafka topic is not allowed"
|
|
46
|
-
)
|
|
44
|
+
raise mlrun.errors.MLRunValueError("Custom kafka topic is not allowed")
|
|
47
45
|
# Add topic to stream kafka uri
|
|
48
46
|
if (
|
|
49
47
|
function_name is None
|
|
@@ -53,22 +51,6 @@ def parse_monitoring_stream_path(
|
|
|
53
51
|
else:
|
|
54
52
|
stream_uri += f"?topic=monitoring_stream_{project}_{function_name}"
|
|
55
53
|
|
|
56
|
-
elif stream_uri.startswith("v3io://") and mlrun.mlconf.is_ce_mode():
|
|
57
|
-
# V3IO is not supported in CE mode, generating a default http stream path
|
|
58
|
-
if function_name is None:
|
|
59
|
-
stream_uri = (
|
|
60
|
-
mlrun.mlconf.model_endpoint_monitoring.default_http_sink.format(
|
|
61
|
-
project=project, namespace=mlrun.mlconf.namespace
|
|
62
|
-
)
|
|
63
|
-
)
|
|
64
|
-
else:
|
|
65
|
-
stream_uri = (
|
|
66
|
-
mlrun.mlconf.model_endpoint_monitoring.default_http_sink_app.format(
|
|
67
|
-
project=project,
|
|
68
|
-
application_name=function_name,
|
|
69
|
-
namespace=mlrun.mlconf.namespace,
|
|
70
|
-
)
|
|
71
|
-
)
|
|
72
54
|
return stream_uri
|
|
73
55
|
|
|
74
56
|
|
|
@@ -75,6 +75,7 @@ class ModelEndpointCreationStrategy(MonitoringStrEnum):
|
|
|
75
75
|
INPLACE = "inplace"
|
|
76
76
|
ARCHIVE = "archive"
|
|
77
77
|
OVERWRITE = "overwrite"
|
|
78
|
+
SKIP = "skip"
|
|
78
79
|
|
|
79
80
|
|
|
80
81
|
class EventFieldType:
|
|
@@ -236,6 +237,8 @@ class ProjectSecretKeys:
|
|
|
236
237
|
ACCESS_KEY = "MODEL_MONITORING_ACCESS_KEY"
|
|
237
238
|
STREAM_PATH = "STREAM_PATH"
|
|
238
239
|
TSDB_CONNECTION = "TSDB_CONNECTION"
|
|
240
|
+
TSDB_PROFILE_NAME = "TSDB_PROFILE_NAME"
|
|
241
|
+
STREAM_PROFILE_NAME = "STREAM_PROFILE_NAME"
|
|
239
242
|
|
|
240
243
|
@classmethod
|
|
241
244
|
def mandatory_secrets(cls):
|
mlrun/config.py
CHANGED
|
@@ -603,10 +603,6 @@ default_config = {
|
|
|
603
603
|
# Offline storage path can be either relative or a full path. This path is used for general offline data
|
|
604
604
|
# storage such as the parquet file which is generated from the monitoring stream function for the drift analysis
|
|
605
605
|
"offline_storage_path": "model-endpoints/{kind}",
|
|
606
|
-
# Default http path that points to the monitoring stream nuclio function. Will be used as a stream path
|
|
607
|
-
# when the user is working in CE environment and has not provided any stream path.
|
|
608
|
-
"default_http_sink": "http://nuclio-{project}-model-monitoring-stream.{namespace}.svc.cluster.local:8080",
|
|
609
|
-
"default_http_sink_app": "http://nuclio-{project}-{application_name}.{namespace}.svc.cluster.local:8080",
|
|
610
606
|
"parquet_batching_max_events": 10_000,
|
|
611
607
|
"parquet_batching_timeout_secs": timedelta(minutes=1).total_seconds(),
|
|
612
608
|
# See mlrun.model_monitoring.db.tsdb.ObjectTSDBFactory for available options
|
|
@@ -11,7 +11,7 @@
|
|
|
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
|
-
|
|
14
|
+
|
|
15
15
|
import ast
|
|
16
16
|
import base64
|
|
17
17
|
import json
|
|
@@ -549,6 +549,35 @@ class DatastoreProfile2Json(pydantic.v1.BaseModel):
|
|
|
549
549
|
|
|
550
550
|
|
|
551
551
|
def datastore_profile_read(url, project_name="", secrets: typing.Optional[dict] = None):
|
|
552
|
+
"""
|
|
553
|
+
Read and retrieve a datastore profile from a given URL.
|
|
554
|
+
|
|
555
|
+
This function retrieves a datastore profile either from temporary client storage,
|
|
556
|
+
or from the MLRun database. It handles both client-side and server-side profile formats
|
|
557
|
+
and performs necessary conversions.
|
|
558
|
+
|
|
559
|
+
Args:
|
|
560
|
+
url (str): A URL with 'ds' scheme pointing to the datastore profile
|
|
561
|
+
(e.g., 'ds://profile-name').
|
|
562
|
+
project_name (str, optional): The project name where the profile is stored.
|
|
563
|
+
Defaults to MLRun's default project.
|
|
564
|
+
secrets (dict, optional): Dictionary containing secrets needed for profile retrieval.
|
|
565
|
+
|
|
566
|
+
Returns:
|
|
567
|
+
DatastoreProfile: The retrieved datastore profile object.
|
|
568
|
+
|
|
569
|
+
Raises:
|
|
570
|
+
MLRunInvalidArgumentError: In the following cases:
|
|
571
|
+
- If the URL scheme is not 'ds'
|
|
572
|
+
- If the profile cannot be retrieved from either server or local environment
|
|
573
|
+
|
|
574
|
+
Note:
|
|
575
|
+
When running from a client environment (outside MLRun pods), private profile information
|
|
576
|
+
is not accessible. In this case, use register_temporary_client_datastore_profile() to
|
|
577
|
+
register the profile with credentials for your local session. When running inside MLRun
|
|
578
|
+
pods, the private information is automatically available and no temporary registration is needed.
|
|
579
|
+
"""
|
|
580
|
+
|
|
552
581
|
parsed_url = urlparse(url)
|
|
553
582
|
if parsed_url.scheme.lower() != "ds":
|
|
554
583
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
@@ -580,7 +609,7 @@ def datastore_profile_read(url, project_name="", secrets: typing.Optional[dict]
|
|
|
580
609
|
)
|
|
581
610
|
private_body = get_secret_or_env(project_ds_name_private, secret_provider=secrets)
|
|
582
611
|
if not public_profile or not private_body:
|
|
583
|
-
raise mlrun.errors.
|
|
612
|
+
raise mlrun.errors.MLRunNotFoundError(
|
|
584
613
|
f"Unable to retrieve the datastore profile '{url}' from either the server or local environment. "
|
|
585
614
|
"Make sure the profile is registered correctly, or if running in a local environment, "
|
|
586
615
|
"use register_temporary_client_datastore_profile() to provide credentials locally."
|
mlrun/db/base.py
CHANGED
mlrun/db/httpdb.py
CHANGED
|
@@ -580,6 +580,7 @@ class HTTPRunDB(RunDBInterface):
|
|
|
580
580
|
or config.feature_store.default_targets
|
|
581
581
|
)
|
|
582
582
|
config.alerts.mode = server_cfg.get("alerts_mode") or config.alerts.mode
|
|
583
|
+
config.system_id = server_cfg.get("system_id") or config.system_id
|
|
583
584
|
|
|
584
585
|
except Exception as exc:
|
|
585
586
|
logger.warning(
|
|
@@ -773,7 +774,7 @@ class HTTPRunDB(RunDBInterface):
|
|
|
773
774
|
|
|
774
775
|
response = self.api_call(
|
|
775
776
|
"POST",
|
|
776
|
-
path=f"projects/{project}/runs/{uid}/
|
|
777
|
+
path=f"projects/{project}/runs/{uid}/push-notifications",
|
|
777
778
|
error="Failed push notifications",
|
|
778
779
|
timeout=timeout,
|
|
779
780
|
)
|
|
@@ -4017,7 +4018,7 @@ class HTTPRunDB(RunDBInterface):
|
|
|
4017
4018
|
def set_model_monitoring_credentials(
|
|
4018
4019
|
self,
|
|
4019
4020
|
project: str,
|
|
4020
|
-
credentials: dict[str, str],
|
|
4021
|
+
credentials: dict[str, Optional[str]],
|
|
4021
4022
|
replace_creds: bool,
|
|
4022
4023
|
) -> None:
|
|
4023
4024
|
"""
|
mlrun/db/nopdb.py
CHANGED
mlrun/execution.py
CHANGED
|
@@ -884,6 +884,7 @@ class MLClientCtx:
|
|
|
884
884
|
upload: Optional[bool] = False,
|
|
885
885
|
labels: Optional[dict[str, str]] = None,
|
|
886
886
|
target_path: Optional[str] = None,
|
|
887
|
+
db_key: Optional[str] = None,
|
|
887
888
|
**kwargs,
|
|
888
889
|
) -> DocumentArtifact:
|
|
889
890
|
"""
|
|
@@ -914,6 +915,8 @@ class MLClientCtx:
|
|
|
914
915
|
:param upload: Whether to upload the artifact
|
|
915
916
|
:param labels: Key-value labels
|
|
916
917
|
:param target_path: Path to the local file
|
|
918
|
+
:param db_key: The key to use in the artifact DB table, by default its run name + '_' + key
|
|
919
|
+
db_key=False will not register it in the artifacts table
|
|
917
920
|
:param kwargs: Additional keyword arguments
|
|
918
921
|
:return: DocumentArtifact object
|
|
919
922
|
|
|
@@ -943,6 +946,9 @@ class MLClientCtx:
|
|
|
943
946
|
tag=tag,
|
|
944
947
|
upload=upload,
|
|
945
948
|
labels=labels,
|
|
949
|
+
local_path=local_path,
|
|
950
|
+
target_path=target_path,
|
|
951
|
+
db_key=db_key,
|
|
946
952
|
)
|
|
947
953
|
self._update_run()
|
|
948
954
|
return item
|
|
@@ -93,7 +93,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
93
93
|
context: "mlrun.MLClientCtx",
|
|
94
94
|
sample_data: Optional[pd.DataFrame] = None,
|
|
95
95
|
reference_data: Optional[pd.DataFrame] = None,
|
|
96
|
-
|
|
96
|
+
endpoints: Optional[list[tuple[str, str]]] = None,
|
|
97
97
|
start: Optional[datetime] = None,
|
|
98
98
|
end: Optional[datetime] = None,
|
|
99
99
|
):
|
|
@@ -121,12 +121,13 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
121
121
|
)
|
|
122
122
|
return self.do_tracking(monitoring_context)
|
|
123
123
|
|
|
124
|
-
if
|
|
124
|
+
if endpoints is not None:
|
|
125
125
|
start, end = self._validate_times(start, end)
|
|
126
|
-
for endpoint_name in
|
|
126
|
+
for endpoint_name, endpoint_id in endpoints:
|
|
127
127
|
result = call_do_tracking(
|
|
128
128
|
event={
|
|
129
129
|
mm_constants.ApplicationEvent.ENDPOINT_NAME: endpoint_name,
|
|
130
|
+
mm_constants.ApplicationEvent.ENDPOINT_ID: endpoint_id,
|
|
130
131
|
mm_constants.ApplicationEvent.START_INFER_TIME: start,
|
|
131
132
|
mm_constants.ApplicationEvent.END_INFER_TIME: end,
|
|
132
133
|
}
|
|
@@ -199,7 +200,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
199
200
|
with_repo: Optional[bool] = False,
|
|
200
201
|
requirements: Optional[Union[str, list[str]]] = None,
|
|
201
202
|
requirements_file: str = "",
|
|
202
|
-
|
|
203
|
+
endpoints: Optional[list[tuple[str, str]]] = None,
|
|
203
204
|
start: Optional[datetime] = None,
|
|
204
205
|
end: Optional[datetime] = None,
|
|
205
206
|
) -> "mlrun.RunObject":
|
|
@@ -208,20 +209,23 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
208
209
|
:py:meth:`~mlrun.model_monitoring.applications.ModelMonitoringApplicationBase.do_tracking`
|
|
209
210
|
model monitoring logic as a :py:class:`~mlrun.runtimes.KubejobRuntime`, which is an MLRun function.
|
|
210
211
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
:param
|
|
215
|
-
:param
|
|
212
|
+
This method has default values for all of its arguments. You should be change them when you want to pass
|
|
213
|
+
data to the application.
|
|
214
|
+
|
|
215
|
+
:param func_path: The path to the function. If ``None``, the current notebook is used.
|
|
216
|
+
:param func_name: The name of the function. If not ``None``, the class name is used.
|
|
217
|
+
:param tag: Tag for the function.
|
|
218
|
+
:param run_local: Whether to run the function locally or remotely.
|
|
219
|
+
:param sample_data: Pandas data-frame as the current dataset.
|
|
216
220
|
When set, it replaces the data read from the model endpoint's offline source.
|
|
217
|
-
:param reference_data:
|
|
221
|
+
:param reference_data: Pandas data-frame of the reference dataset.
|
|
218
222
|
When set, its statistics override the model endpoint's feature statistics.
|
|
219
223
|
:param image: Docker image to run the job on.
|
|
220
224
|
:param with_repo: Whether to clone the current repo to the build source.
|
|
221
225
|
:param requirements: List of Python requirements to be installed in the image.
|
|
222
226
|
:param requirements_file: Path to a Python requirements file to be installed in the image.
|
|
223
|
-
:param
|
|
224
|
-
you have to provide also the start and end times of the data to analyze.
|
|
227
|
+
:param endpoints: A list of tuples of the model endpoint (name, uid) to get the data from.
|
|
228
|
+
If provided, you have to provide also the start and end times of the data to analyze.
|
|
225
229
|
:param start: The start time of the sample data.
|
|
226
230
|
:param end: The end time of the sample data.
|
|
227
231
|
|
|
@@ -249,12 +253,16 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
249
253
|
),
|
|
250
254
|
)
|
|
251
255
|
|
|
252
|
-
params: dict[str, Union[list[str], datetime]] = {}
|
|
253
|
-
if
|
|
256
|
+
params: dict[str, Union[list[tuple[str, str]], datetime]] = {}
|
|
257
|
+
if endpoints:
|
|
254
258
|
start, end = cls._validate_times(start, end)
|
|
255
|
-
params["
|
|
259
|
+
params["endpoints"] = endpoints
|
|
256
260
|
params["start"] = start
|
|
257
261
|
params["end"] = end
|
|
262
|
+
elif start or end:
|
|
263
|
+
raise mlrun.errors.MLRunValueError(
|
|
264
|
+
"Custom start or end times are supported only with endpoints data"
|
|
265
|
+
)
|
|
258
266
|
|
|
259
267
|
inputs: dict[str, str] = {}
|
|
260
268
|
for data, identifier in [
|
|
@@ -190,18 +190,13 @@ class MonitoringApplicationContext:
|
|
|
190
190
|
)
|
|
191
191
|
|
|
192
192
|
def _get_default_labels(self) -> dict[str, str]:
|
|
193
|
-
|
|
193
|
+
return {
|
|
194
194
|
mlrun_constants.MLRunInternalLabels.runner_pod: socket.gethostname(),
|
|
195
195
|
mlrun_constants.MLRunInternalLabels.producer_type: "model-monitoring-app",
|
|
196
196
|
mlrun_constants.MLRunInternalLabels.app_name: self.application_name,
|
|
197
|
+
mlrun_constants.MLRunInternalLabels.endpoint_id: self.endpoint_id,
|
|
198
|
+
mlrun_constants.MLRunInternalLabels.endpoint_name: self.endpoint_name,
|
|
197
199
|
}
|
|
198
|
-
for key, value in [
|
|
199
|
-
(mlrun_constants.MLRunInternalLabels.endpoint_id, self.endpoint_id),
|
|
200
|
-
(mlrun_constants.MLRunInternalLabels.endpoint_name, self.endpoint_name),
|
|
201
|
-
]:
|
|
202
|
-
if value:
|
|
203
|
-
labels[key] = value
|
|
204
|
-
return labels
|
|
205
200
|
|
|
206
201
|
def _add_default_labels(self, labels: Optional[dict[str, str]]) -> dict[str, str]:
|
|
207
202
|
"""Add the default labels to logged artifacts labels"""
|
|
@@ -16,7 +16,9 @@ import enum
|
|
|
16
16
|
import typing
|
|
17
17
|
|
|
18
18
|
import mlrun.common.schemas.secret
|
|
19
|
+
import mlrun.datastore.datastore_profile
|
|
19
20
|
import mlrun.errors
|
|
21
|
+
import mlrun.model_monitoring.helpers
|
|
20
22
|
|
|
21
23
|
from .base import TSDBConnector
|
|
22
24
|
|
|
@@ -80,6 +82,13 @@ def get_tsdb_connector(
|
|
|
80
82
|
or the provided TSDB connection is invalid.
|
|
81
83
|
"""
|
|
82
84
|
|
|
85
|
+
try:
|
|
86
|
+
profile = mlrun.model_monitoring.helpers._get_tsdb_profile(
|
|
87
|
+
project=project, secret_provider=secret_provider
|
|
88
|
+
)
|
|
89
|
+
except mlrun.errors.MLRunNotFoundError:
|
|
90
|
+
profile = None
|
|
91
|
+
|
|
83
92
|
tsdb_connection_string = (
|
|
84
93
|
tsdb_connection_string
|
|
85
94
|
or mlrun.model_monitoring.helpers.get_tsdb_connection_string(
|
|
@@ -92,6 +101,9 @@ def get_tsdb_connector(
|
|
|
92
101
|
kwargs["connection_string"] = tsdb_connection_string
|
|
93
102
|
elif tsdb_connection_string and tsdb_connection_string == "v3io":
|
|
94
103
|
tsdb_connector_type = mlrun.common.schemas.model_monitoring.TSDBTarget.V3IO_TSDB
|
|
104
|
+
elif isinstance(profile, mlrun.datastore.datastore_profile.DatastoreProfileV3io):
|
|
105
|
+
tsdb_connector_type = mlrun.common.schemas.model_monitoring.TSDBTarget.V3IO_TSDB
|
|
106
|
+
kwargs["v3io_access_key"] = profile.v3io_access_key
|
|
95
107
|
else:
|
|
96
108
|
raise mlrun.errors.MLRunInvalidMMStoreTypeError(
|
|
97
109
|
"You must provide a valid tsdb store connection by using "
|
|
@@ -58,6 +58,7 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
58
58
|
project: str,
|
|
59
59
|
container: str = _CONTAINER,
|
|
60
60
|
v3io_framesd: Optional[str] = None,
|
|
61
|
+
v3io_access_key: str = "",
|
|
61
62
|
create_table: bool = False,
|
|
62
63
|
) -> None:
|
|
63
64
|
super().__init__(project=project)
|
|
@@ -65,6 +66,7 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
65
66
|
self.container = container
|
|
66
67
|
|
|
67
68
|
self.v3io_framesd = v3io_framesd or mlrun.mlconf.v3io_framesd
|
|
69
|
+
self._v3io_access_key = v3io_access_key
|
|
68
70
|
self._frames_client: Optional[v3io_frames.client.ClientBase] = None
|
|
69
71
|
self._init_tables_path()
|
|
70
72
|
self._create_table = create_table
|
|
@@ -72,7 +74,9 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
72
74
|
@property
|
|
73
75
|
def frames_client(self) -> v3io_frames.client.ClientBase:
|
|
74
76
|
if not self._frames_client:
|
|
75
|
-
self._frames_client = self._get_v3io_frames_client(
|
|
77
|
+
self._frames_client = self._get_v3io_frames_client(
|
|
78
|
+
self.container, v3io_access_key=self._v3io_access_key
|
|
79
|
+
)
|
|
76
80
|
if self._create_table:
|
|
77
81
|
self.create_tables()
|
|
78
82
|
return self._frames_client
|
|
@@ -564,10 +568,13 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
564
568
|
return source_directory
|
|
565
569
|
|
|
566
570
|
@staticmethod
|
|
567
|
-
def _get_v3io_frames_client(
|
|
571
|
+
def _get_v3io_frames_client(
|
|
572
|
+
v3io_container: str, v3io_access_key: str = ""
|
|
573
|
+
) -> v3io_frames.client.ClientBase:
|
|
568
574
|
return mlrun.utils.v3io_clients.get_frames_client(
|
|
569
575
|
address=mlrun.mlconf.v3io_framesd,
|
|
570
576
|
container=v3io_container,
|
|
577
|
+
token=v3io_access_key,
|
|
571
578
|
)
|
|
572
579
|
|
|
573
580
|
def read_metrics_data(
|
|
@@ -13,25 +13,20 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
import datetime
|
|
16
|
+
import functools
|
|
16
17
|
import os
|
|
17
|
-
import
|
|
18
|
+
from fnmatch import fnmatchcase
|
|
19
|
+
from typing import TYPE_CHECKING, Callable, Optional, TypedDict, cast
|
|
18
20
|
|
|
19
21
|
import numpy as np
|
|
20
22
|
import pandas as pd
|
|
21
23
|
|
|
22
|
-
if typing.TYPE_CHECKING:
|
|
23
|
-
from mlrun.datastore import DataItem
|
|
24
|
-
from mlrun.db.base import RunDBInterface
|
|
25
|
-
from mlrun.projects import MlrunProject
|
|
26
|
-
|
|
27
|
-
from fnmatch import fnmatchcase
|
|
28
|
-
from typing import Optional
|
|
29
|
-
|
|
30
24
|
import mlrun
|
|
31
25
|
import mlrun.artifacts
|
|
32
26
|
import mlrun.common.model_monitoring.helpers
|
|
33
27
|
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
34
28
|
import mlrun.data_types.infer
|
|
29
|
+
import mlrun.datastore.datastore_profile
|
|
35
30
|
import mlrun.model_monitoring
|
|
36
31
|
import mlrun.utils.helpers
|
|
37
32
|
from mlrun.common.schemas import ModelEndpoint
|
|
@@ -41,8 +36,13 @@ from mlrun.common.schemas.model_monitoring.model_endpoints import (
|
|
|
41
36
|
)
|
|
42
37
|
from mlrun.utils import logger
|
|
43
38
|
|
|
39
|
+
if TYPE_CHECKING:
|
|
40
|
+
from mlrun.datastore import DataItem
|
|
41
|
+
from mlrun.db.base import RunDBInterface
|
|
42
|
+
from mlrun.projects import MlrunProject
|
|
43
|
+
|
|
44
44
|
|
|
45
|
-
class _BatchDict(
|
|
45
|
+
class _BatchDict(TypedDict):
|
|
46
46
|
minutes: int
|
|
47
47
|
hours: int
|
|
48
48
|
days: int
|
|
@@ -115,20 +115,30 @@ def filter_results_by_regex(
|
|
|
115
115
|
def get_stream_path(
|
|
116
116
|
project: str,
|
|
117
117
|
function_name: str = mm_constants.MonitoringFunctionNames.STREAM,
|
|
118
|
-
stream_uri:
|
|
118
|
+
stream_uri: Optional[str] = None,
|
|
119
|
+
secret_provider: Optional[Callable[[str], str]] = None,
|
|
119
120
|
) -> str:
|
|
120
121
|
"""
|
|
121
122
|
Get stream path from the project secret. If wasn't set, take it from the system configurations
|
|
122
123
|
|
|
123
124
|
:param project: Project name.
|
|
124
125
|
:param function_name: Application name. Default is model_monitoring_stream.
|
|
125
|
-
:param stream_uri: Stream URI. If provided, it will be used instead of the one from the project secret.
|
|
126
|
-
|
|
126
|
+
:param stream_uri: Stream URI. If provided, it will be used instead of the one from the project's secret.
|
|
127
|
+
:param secret_provider: Optional secret provider to get the connection string secret.
|
|
128
|
+
If not set, the env vars are used.
|
|
127
129
|
:return: Monitoring stream path to the relevant application.
|
|
128
130
|
"""
|
|
129
131
|
|
|
132
|
+
try:
|
|
133
|
+
profile = _get_stream_profile(project=project, secret_provider=secret_provider)
|
|
134
|
+
except mlrun.errors.MLRunNotFoundError:
|
|
135
|
+
profile = None
|
|
136
|
+
|
|
137
|
+
if isinstance(profile, mlrun.datastore.datastore_profile.DatastoreProfileV3io):
|
|
138
|
+
stream_uri = "v3io"
|
|
139
|
+
|
|
130
140
|
stream_uri = stream_uri or mlrun.get_secret_or_env(
|
|
131
|
-
mm_constants.ProjectSecretKeys.STREAM_PATH
|
|
141
|
+
key=mm_constants.ProjectSecretKeys.STREAM_PATH, secret_provider=secret_provider
|
|
132
142
|
)
|
|
133
143
|
|
|
134
144
|
if not stream_uri or stream_uri == "v3io":
|
|
@@ -230,7 +240,7 @@ def get_monitoring_drift_measures_data(project: str, endpoint_id: str) -> "DataI
|
|
|
230
240
|
|
|
231
241
|
|
|
232
242
|
def get_tsdb_connection_string(
|
|
233
|
-
secret_provider:
|
|
243
|
+
secret_provider: Optional[Callable[[str], str]] = None,
|
|
234
244
|
) -> str:
|
|
235
245
|
"""Get TSDB connection string from the project secret. If wasn't set, take it from the system
|
|
236
246
|
configurations.
|
|
@@ -244,6 +254,40 @@ def get_tsdb_connection_string(
|
|
|
244
254
|
)
|
|
245
255
|
|
|
246
256
|
|
|
257
|
+
def _get_profile(
|
|
258
|
+
project: str,
|
|
259
|
+
secret_provider: Optional[Callable[[str], str]],
|
|
260
|
+
profile_name_key: str,
|
|
261
|
+
) -> mlrun.datastore.datastore_profile.DatastoreProfile:
|
|
262
|
+
"""
|
|
263
|
+
Get the datastore profile from the project name and secret provider, where the profile's name
|
|
264
|
+
is saved as a secret named `profile_name_key`.
|
|
265
|
+
|
|
266
|
+
:param project: The project name.
|
|
267
|
+
:param secret_provider: Secret provider to get the secrets from, or `None` for env vars.
|
|
268
|
+
:param profile_name_key: The profile name key in the secret store.
|
|
269
|
+
:return: Datastore profile.
|
|
270
|
+
"""
|
|
271
|
+
profile_name = mlrun.get_secret_or_env(
|
|
272
|
+
key=profile_name_key, secret_provider=secret_provider
|
|
273
|
+
)
|
|
274
|
+
if not profile_name:
|
|
275
|
+
raise mlrun.errors.MLRunNotFoundError(
|
|
276
|
+
f"Not found `{profile_name_key}` profile name"
|
|
277
|
+
)
|
|
278
|
+
return mlrun.datastore.datastore_profile.datastore_profile_read(
|
|
279
|
+
url=f"ds://{profile_name}", project_name=project, secrets=secret_provider
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
_get_tsdb_profile = functools.partial(
|
|
284
|
+
_get_profile, profile_name_key=mm_constants.ProjectSecretKeys.TSDB_PROFILE_NAME
|
|
285
|
+
)
|
|
286
|
+
_get_stream_profile = functools.partial(
|
|
287
|
+
_get_profile, profile_name_key=mm_constants.ProjectSecretKeys.STREAM_PROFILE_NAME
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
|
|
247
291
|
def batch_dict2timedelta(batch_dict: _BatchDict) -> datetime.timedelta:
|
|
248
292
|
"""
|
|
249
293
|
Convert a batch dictionary to timedelta.
|
|
@@ -431,7 +475,7 @@ def get_invocations_metric(project: str) -> ModelEndpointMonitoringMetric:
|
|
|
431
475
|
|
|
432
476
|
|
|
433
477
|
def _get_monitoring_schedules_folder_path(project: str) -> str:
|
|
434
|
-
return
|
|
478
|
+
return cast(
|
|
435
479
|
str,
|
|
436
480
|
mlrun.mlconf.get_model_monitoring_file_target_path(
|
|
437
481
|
project=project, kind=mm_constants.FileTargetKind.MONITORING_SCHEDULES
|