mlrun 1.8.0rc18__py3-none-any.whl → 1.8.0rc19__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/common/schemas/artifact.py +6 -0
- mlrun/common/schemas/model_monitoring/__init__.py +1 -0
- mlrun/common/schemas/model_monitoring/constants.py +11 -0
- mlrun/common/schemas/model_monitoring/model_endpoints.py +2 -2
- mlrun/config.py +2 -2
- mlrun/db/base.py +9 -0
- mlrun/db/httpdb.py +42 -0
- mlrun/model_monitoring/db/tsdb/base.py +116 -8
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +23 -11
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +23 -4
- mlrun/model_monitoring/helpers.py +2 -2
- mlrun/projects/project.py +12 -7
- mlrun/utils/helpers.py +134 -0
- mlrun/utils/notifications/notification/webhook.py +3 -0
- mlrun/utils/notifications/notification_pusher.py +4 -132
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.8.0rc18.dist-info → mlrun-1.8.0rc19.dist-info}/METADATA +1 -1
- {mlrun-1.8.0rc18.dist-info → mlrun-1.8.0rc19.dist-info}/RECORD +22 -22
- {mlrun-1.8.0rc18.dist-info → mlrun-1.8.0rc19.dist-info}/LICENSE +0 -0
- {mlrun-1.8.0rc18.dist-info → mlrun-1.8.0rc19.dist-info}/WHEEL +0 -0
- {mlrun-1.8.0rc18.dist-info → mlrun-1.8.0rc19.dist-info}/entry_points.txt +0 -0
- {mlrun-1.8.0rc18.dist-info → mlrun-1.8.0rc19.dist-info}/top_level.txt +0 -0
mlrun/common/schemas/artifact.py
CHANGED
|
@@ -51,6 +51,12 @@ class ArtifactCategories(mlrun.common.types.StrEnum):
|
|
|
51
51
|
True,
|
|
52
52
|
)
|
|
53
53
|
|
|
54
|
+
@classmethod
|
|
55
|
+
def from_kind(cls, kind: str) -> "ArtifactCategories":
|
|
56
|
+
if kind in [cls.model.value, cls.dataset.value, cls.document.value]:
|
|
57
|
+
return cls(kind)
|
|
58
|
+
return cls.other
|
|
59
|
+
|
|
54
60
|
|
|
55
61
|
class ArtifactIdentifier(pydantic.v1.BaseModel):
|
|
56
62
|
# artifact kind
|
|
@@ -248,6 +248,12 @@ class ProjectSecretKeys:
|
|
|
248
248
|
]
|
|
249
249
|
|
|
250
250
|
|
|
251
|
+
class GetEventsFormat(MonitoringStrEnum):
|
|
252
|
+
SINGLE = "single"
|
|
253
|
+
SEPARATION = "separation"
|
|
254
|
+
INTERSECTION = "intersection"
|
|
255
|
+
|
|
256
|
+
|
|
251
257
|
class ModelEndpointTargetSchemas(MonitoringStrEnum):
|
|
252
258
|
V3IO = "v3io"
|
|
253
259
|
MYSQL = "mysql"
|
|
@@ -448,3 +454,8 @@ FQN_REGEX = re.compile(FQN_PATTERN)
|
|
|
448
454
|
PROJECT_PATTERN = r"^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$"
|
|
449
455
|
|
|
450
456
|
MODEL_ENDPOINT_ID_PATTERN = r"^[a-zA-Z0-9_-]+$"
|
|
457
|
+
|
|
458
|
+
INTERSECT_DICT_KEYS = {
|
|
459
|
+
ModelEndpointMonitoringMetricType.METRIC: "intersect_metrics",
|
|
460
|
+
ModelEndpointMonitoringMetricType.RESULT: "intersect_results",
|
|
461
|
+
}
|
|
@@ -259,12 +259,12 @@ class ModelEndpointMonitoringMetric(BaseModel):
|
|
|
259
259
|
|
|
260
260
|
def __init__(self, **kwargs):
|
|
261
261
|
super().__init__(**kwargs)
|
|
262
|
-
self.full_name =
|
|
262
|
+
self.full_name = compose_full_name(
|
|
263
263
|
project=self.project, app=self.app, name=self.name, type=self.type
|
|
264
264
|
)
|
|
265
265
|
|
|
266
266
|
|
|
267
|
-
def
|
|
267
|
+
def compose_full_name(
|
|
268
268
|
*,
|
|
269
269
|
project: str,
|
|
270
270
|
app: str,
|
mlrun/config.py
CHANGED
|
@@ -83,8 +83,8 @@ default_config = {
|
|
|
83
83
|
"images_to_enrich_registry": "^mlrun/*,python:3.9",
|
|
84
84
|
"kfp_url": "",
|
|
85
85
|
"kfp_ttl": "14400", # KFP ttl in sec, after that completed PODs will be deleted
|
|
86
|
-
"kfp_image": "mlrun/mlrun-kfp", # image to use for KFP runner
|
|
87
|
-
"dask_kfp_image": "mlrun/ml-base", # image to use for dask KFP runner
|
|
86
|
+
"kfp_image": "mlrun/mlrun-kfp", # image to use for KFP runner
|
|
87
|
+
"dask_kfp_image": "mlrun/ml-base", # image to use for dask KFP runner
|
|
88
88
|
"igz_version": "", # the version of the iguazio system the API is running on
|
|
89
89
|
"iguazio_api_url": "", # the url to iguazio api
|
|
90
90
|
"spark_app_image": "", # image to use for spark operator app runtime
|
mlrun/db/base.py
CHANGED
|
@@ -337,6 +337,15 @@ class RunDBInterface(ABC):
|
|
|
337
337
|
) -> list[mm_endpoints.ModelEndpointMonitoringMetric]:
|
|
338
338
|
pass
|
|
339
339
|
|
|
340
|
+
def get_metrics_by_multiple_endpoints(
|
|
341
|
+
self,
|
|
342
|
+
project: str,
|
|
343
|
+
endpoint_ids: Union[str, list[str]],
|
|
344
|
+
type: Literal["results", "metrics", "all"] = "all",
|
|
345
|
+
events_format: mm_constants.GetEventsFormat = mm_constants.GetEventsFormat.SEPARATION,
|
|
346
|
+
) -> dict[str, list[mm_endpoints.ModelEndpointMonitoringMetric]]:
|
|
347
|
+
pass
|
|
348
|
+
|
|
340
349
|
@abstractmethod
|
|
341
350
|
def delete_project(
|
|
342
351
|
self,
|
mlrun/db/httpdb.py
CHANGED
|
@@ -3524,6 +3524,48 @@ class HTTPRunDB(RunDBInterface):
|
|
|
3524
3524
|
list[mm_endpoints.ModelEndpointMonitoringMetric], monitoring_metrics
|
|
3525
3525
|
)
|
|
3526
3526
|
|
|
3527
|
+
def get_metrics_by_multiple_endpoints(
|
|
3528
|
+
self,
|
|
3529
|
+
project: str,
|
|
3530
|
+
endpoint_ids: Union[str, list[str]],
|
|
3531
|
+
type: Literal["results", "metrics", "all"] = "all",
|
|
3532
|
+
events_format: mm_constants.GetEventsFormat = mm_constants.GetEventsFormat.SEPARATION,
|
|
3533
|
+
) -> dict[str, list[mm_endpoints.ModelEndpointMonitoringMetric]]:
|
|
3534
|
+
"""Get application metrics/results by endpoint id and project.
|
|
3535
|
+
|
|
3536
|
+
:param project: The name of the project.
|
|
3537
|
+
:param endpoint_ids: The unique id of the model endpoint. Can be a single id or a list of ids.
|
|
3538
|
+
:param type: The type of the metrics to return. "all" means "results" and "metrics".
|
|
3539
|
+
:param events_format: response format:
|
|
3540
|
+
|
|
3541
|
+
separation: {"mep_id1":[...], "mep_id2":[...]}
|
|
3542
|
+
intersection {"intersect_metrics":[], "intersect_results":[]}
|
|
3543
|
+
:return: A dictionary of application metrics and/or results for the model endpoints formatted by events_format.
|
|
3544
|
+
"""
|
|
3545
|
+
path = f"projects/{project}/model-endpoints/metrics"
|
|
3546
|
+
params = {
|
|
3547
|
+
"type": type,
|
|
3548
|
+
"endpoint-id": endpoint_ids,
|
|
3549
|
+
"events_format": events_format,
|
|
3550
|
+
}
|
|
3551
|
+
error_message = (
|
|
3552
|
+
f"Failed to get model monitoring metrics,"
|
|
3553
|
+
f" endpoint_ids: {endpoint_ids}, project: {project}"
|
|
3554
|
+
)
|
|
3555
|
+
response = self.api_call(
|
|
3556
|
+
mlrun.common.types.HTTPMethod.GET,
|
|
3557
|
+
path,
|
|
3558
|
+
error_message,
|
|
3559
|
+
params=params,
|
|
3560
|
+
)
|
|
3561
|
+
monitoring_metrics_by_endpoint = response.json()
|
|
3562
|
+
parsed_metrics_by_endpoint = {}
|
|
3563
|
+
for endpoint, metrics in monitoring_metrics_by_endpoint.items():
|
|
3564
|
+
parsed_metrics_by_endpoint[endpoint] = parse_obj_as(
|
|
3565
|
+
list[mm_endpoints.ModelEndpointMonitoringMetric], metrics
|
|
3566
|
+
)
|
|
3567
|
+
return parsed_metrics_by_endpoint
|
|
3568
|
+
|
|
3527
3569
|
def create_user_secrets(
|
|
3528
3570
|
self,
|
|
3529
3571
|
user: str,
|
|
@@ -234,14 +234,14 @@ class TSDBConnector(ABC):
|
|
|
234
234
|
@abstractmethod
|
|
235
235
|
def get_metrics_metadata(
|
|
236
236
|
self,
|
|
237
|
-
endpoint_id: str,
|
|
237
|
+
endpoint_id: typing.Union[str, list[str]],
|
|
238
238
|
start: typing.Optional[datetime] = None,
|
|
239
239
|
end: typing.Optional[datetime] = None,
|
|
240
240
|
) -> pd.DataFrame:
|
|
241
241
|
"""
|
|
242
|
-
Fetches distinct metrics metadata from the metrics TSDB table for a specified model
|
|
242
|
+
Fetches distinct metrics metadata from the metrics TSDB table for a specified model endpoints.
|
|
243
243
|
|
|
244
|
-
:param endpoint_id: The model endpoint identifier.
|
|
244
|
+
:param endpoint_id: The model endpoint identifier. Can be a single id or a list of ids.
|
|
245
245
|
:param start: The start time of the query.
|
|
246
246
|
:param end: The end time of the query.
|
|
247
247
|
|
|
@@ -252,14 +252,14 @@ class TSDBConnector(ABC):
|
|
|
252
252
|
@abstractmethod
|
|
253
253
|
def get_results_metadata(
|
|
254
254
|
self,
|
|
255
|
-
endpoint_id: str,
|
|
255
|
+
endpoint_id: typing.Union[str, list[str]],
|
|
256
256
|
start: typing.Optional[datetime] = None,
|
|
257
257
|
end: typing.Optional[datetime] = None,
|
|
258
258
|
) -> pd.DataFrame:
|
|
259
259
|
"""
|
|
260
|
-
Fetches distinct results metadata from the app-results TSDB table for a specified model
|
|
260
|
+
Fetches distinct results metadata from the app-results TSDB table for a specified model endpoints.
|
|
261
261
|
|
|
262
|
-
:param endpoint_id: The model endpoint identifier.
|
|
262
|
+
:param endpoint_id: The model endpoint identifier. Can be a single id or a list of ids.
|
|
263
263
|
:param start: The start time of the query.
|
|
264
264
|
:param end: The end time of the query.
|
|
265
265
|
|
|
@@ -341,7 +341,7 @@ class TSDBConnector(ABC):
|
|
|
341
341
|
logger.debug("No metrics", missing_metrics=metrics_without_data.keys())
|
|
342
342
|
grouped = []
|
|
343
343
|
for (app_name, name), sub_df in grouped:
|
|
344
|
-
full_name =
|
|
344
|
+
full_name = mm_schemas.model_endpoints.compose_full_name(
|
|
345
345
|
project=project,
|
|
346
346
|
app=app_name,
|
|
347
347
|
name=name,
|
|
@@ -410,7 +410,7 @@ class TSDBConnector(ABC):
|
|
|
410
410
|
result_kind = mlrun.model_monitoring.db.tsdb.helpers._get_result_kind(
|
|
411
411
|
sub_df
|
|
412
412
|
)
|
|
413
|
-
full_name =
|
|
413
|
+
full_name = mm_schemas.model_endpoints.compose_full_name(
|
|
414
414
|
project=project, app=app_name, name=name
|
|
415
415
|
)
|
|
416
416
|
try:
|
|
@@ -467,6 +467,7 @@ class TSDBConnector(ABC):
|
|
|
467
467
|
|
|
468
468
|
:return: A list of mm metrics objects.
|
|
469
469
|
"""
|
|
470
|
+
|
|
470
471
|
return list(
|
|
471
472
|
map(
|
|
472
473
|
lambda record: mm_schemas.ModelEndpointMonitoringMetric(
|
|
@@ -481,6 +482,113 @@ class TSDBConnector(ABC):
|
|
|
481
482
|
)
|
|
482
483
|
)
|
|
483
484
|
|
|
485
|
+
@staticmethod
|
|
486
|
+
def df_to_metrics_grouped_dict(
|
|
487
|
+
*,
|
|
488
|
+
df: pd.DataFrame,
|
|
489
|
+
project: str,
|
|
490
|
+
type: str,
|
|
491
|
+
) -> dict[str, list[mm_schemas.ModelEndpointMonitoringMetric]]:
|
|
492
|
+
"""
|
|
493
|
+
Parse a DataFrame of metrics from the TSDB into a grouped mm metrics objects by endpoint_id.
|
|
494
|
+
|
|
495
|
+
:param df: The DataFrame to parse.
|
|
496
|
+
:param project: The project name.
|
|
497
|
+
:param type: The type of the metrics (either "result" or "metric").
|
|
498
|
+
|
|
499
|
+
:return: A grouped dict of mm metrics/results, using model_endpoints_ids as keys.
|
|
500
|
+
"""
|
|
501
|
+
|
|
502
|
+
if df.empty:
|
|
503
|
+
return {}
|
|
504
|
+
|
|
505
|
+
grouped_by_fields = [mm_schemas.WriterEvent.APPLICATION_NAME]
|
|
506
|
+
if type == "result":
|
|
507
|
+
name_column = mm_schemas.ResultData.RESULT_NAME
|
|
508
|
+
grouped_by_fields.append(mm_schemas.ResultData.RESULT_KIND)
|
|
509
|
+
else:
|
|
510
|
+
name_column = mm_schemas.MetricData.METRIC_NAME
|
|
511
|
+
|
|
512
|
+
grouped_by_fields.append(name_column)
|
|
513
|
+
# groupby has different behavior for category columns
|
|
514
|
+
df["endpoint_id"] = df["endpoint_id"].astype(str)
|
|
515
|
+
grouped_by_df = df.groupby("endpoint_id")
|
|
516
|
+
grouped_dict = grouped_by_df.apply(
|
|
517
|
+
lambda group: list(
|
|
518
|
+
map(
|
|
519
|
+
lambda record: mm_schemas.ModelEndpointMonitoringMetric(
|
|
520
|
+
project=project,
|
|
521
|
+
type=type,
|
|
522
|
+
app=record.get(mm_schemas.WriterEvent.APPLICATION_NAME),
|
|
523
|
+
name=record.get(name_column),
|
|
524
|
+
**{"kind": record.get(mm_schemas.ResultData.RESULT_KIND)}
|
|
525
|
+
if type == "result"
|
|
526
|
+
else {},
|
|
527
|
+
),
|
|
528
|
+
group[grouped_by_fields].to_dict(orient="records"),
|
|
529
|
+
)
|
|
530
|
+
)
|
|
531
|
+
).to_dict()
|
|
532
|
+
return grouped_dict
|
|
533
|
+
|
|
534
|
+
@staticmethod
|
|
535
|
+
def df_to_events_intersection_dict(
|
|
536
|
+
*,
|
|
537
|
+
df: pd.DataFrame,
|
|
538
|
+
project: str,
|
|
539
|
+
type: typing.Union[str, mm_schemas.ModelEndpointMonitoringMetricType],
|
|
540
|
+
) -> dict[str, list[mm_schemas.ModelEndpointMonitoringMetric]]:
|
|
541
|
+
"""
|
|
542
|
+
Parse a DataFrame of metrics from the TSDB into a dict of intersection metrics/results by name and application
|
|
543
|
+
(and kind in results).
|
|
544
|
+
|
|
545
|
+
:param df: The DataFrame to parse.
|
|
546
|
+
:param project: The project name.
|
|
547
|
+
:param type: The type of the metrics (either "result" or "metric").
|
|
548
|
+
|
|
549
|
+
:return: A dictionary where the key is event type (as defined by `INTERSECT_DICT_KEYS`),
|
|
550
|
+
and the value is a list containing the intersect metrics or results across all endpoint IDs.
|
|
551
|
+
|
|
552
|
+
For example:
|
|
553
|
+
{
|
|
554
|
+
"intersect_metrics": [...]
|
|
555
|
+
}
|
|
556
|
+
"""
|
|
557
|
+
dict_key = mm_schemas.INTERSECT_DICT_KEYS[type]
|
|
558
|
+
metrics = []
|
|
559
|
+
if df.empty:
|
|
560
|
+
return {dict_key: []}
|
|
561
|
+
|
|
562
|
+
columns_to_zip = [mm_schemas.WriterEvent.APPLICATION_NAME]
|
|
563
|
+
|
|
564
|
+
if type == "result":
|
|
565
|
+
name_column = mm_schemas.ResultData.RESULT_NAME
|
|
566
|
+
columns_to_zip.append(mm_schemas.ResultData.RESULT_KIND)
|
|
567
|
+
else:
|
|
568
|
+
name_column = mm_schemas.MetricData.METRIC_NAME
|
|
569
|
+
columns_to_zip.insert(1, name_column)
|
|
570
|
+
|
|
571
|
+
# groupby has different behavior for category columns
|
|
572
|
+
df["endpoint_id"] = df["endpoint_id"].astype(str)
|
|
573
|
+
df["event_values"] = list(zip(*[df[col] for col in columns_to_zip]))
|
|
574
|
+
grouped_by_event_values = df.groupby("endpoint_id")["event_values"].apply(set)
|
|
575
|
+
common_event_values_combinations = set.intersection(*grouped_by_event_values)
|
|
576
|
+
result_kind = None
|
|
577
|
+
for data in common_event_values_combinations:
|
|
578
|
+
application_name, event_name = data[0], data[1]
|
|
579
|
+
if len(data) > 2: # in result case
|
|
580
|
+
result_kind = data[2]
|
|
581
|
+
metrics.append(
|
|
582
|
+
mm_schemas.ModelEndpointMonitoringMetric(
|
|
583
|
+
project=project,
|
|
584
|
+
type=type,
|
|
585
|
+
app=application_name,
|
|
586
|
+
name=event_name,
|
|
587
|
+
kind=result_kind,
|
|
588
|
+
)
|
|
589
|
+
)
|
|
590
|
+
return {dict_key: metrics}
|
|
591
|
+
|
|
484
592
|
@staticmethod
|
|
485
593
|
def _get_start_end(
|
|
486
594
|
start: typing.Union[datetime, None],
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
import typing
|
|
16
|
-
from datetime import datetime, timedelta
|
|
16
|
+
from datetime import datetime, timedelta
|
|
17
17
|
|
|
18
18
|
import pandas as pd
|
|
19
19
|
import taosws
|
|
@@ -164,6 +164,17 @@ class TDEngineConnector(TSDBConnector):
|
|
|
164
164
|
def _convert_to_datetime(val: typing.Union[str, datetime]) -> datetime:
|
|
165
165
|
return datetime.fromisoformat(val) if isinstance(val, str) else val
|
|
166
166
|
|
|
167
|
+
@staticmethod
|
|
168
|
+
def _get_endpoint_filter(endpoint_id: typing.Union[str, list[str]]):
|
|
169
|
+
if isinstance(endpoint_id, str):
|
|
170
|
+
return f"endpoint_id='{endpoint_id}'"
|
|
171
|
+
elif isinstance(endpoint_id, list):
|
|
172
|
+
return f"endpoint_id IN({str(endpoint_id)[1:-1]}) "
|
|
173
|
+
else:
|
|
174
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
175
|
+
"Invalid 'endpoint_id' filter: must be a string or a list."
|
|
176
|
+
)
|
|
177
|
+
|
|
167
178
|
def apply_monitoring_stream_steps(self, graph, **kwarg):
|
|
168
179
|
"""
|
|
169
180
|
Apply TSDB steps on the provided monitoring graph. Throughout these steps, the graph stores live data of
|
|
@@ -542,12 +553,11 @@ class TDEngineConnector(TSDBConnector):
|
|
|
542
553
|
},
|
|
543
554
|
inplace=True,
|
|
544
555
|
)
|
|
545
|
-
df[mm_schemas.EventFieldType.LAST_REQUEST] =
|
|
546
|
-
mm_schemas.EventFieldType.LAST_REQUEST
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
).astimezone(tz=timezone.utc)
|
|
556
|
+
df[mm_schemas.EventFieldType.LAST_REQUEST] = pd.to_datetime(
|
|
557
|
+
df[mm_schemas.EventFieldType.LAST_REQUEST],
|
|
558
|
+
errors="coerce",
|
|
559
|
+
format="ISO8601",
|
|
560
|
+
utc=True,
|
|
551
561
|
)
|
|
552
562
|
return df
|
|
553
563
|
|
|
@@ -588,7 +598,7 @@ class TDEngineConnector(TSDBConnector):
|
|
|
588
598
|
|
|
589
599
|
def get_metrics_metadata(
|
|
590
600
|
self,
|
|
591
|
-
endpoint_id: str,
|
|
601
|
+
endpoint_id: typing.Union[str, list[str]],
|
|
592
602
|
start: typing.Optional[datetime] = None,
|
|
593
603
|
end: typing.Optional[datetime] = None,
|
|
594
604
|
) -> pd.DataFrame:
|
|
@@ -602,11 +612,12 @@ class TDEngineConnector(TSDBConnector):
|
|
|
602
612
|
mm_schemas.MetricData.METRIC_NAME,
|
|
603
613
|
mm_schemas.EventFieldType.ENDPOINT_ID,
|
|
604
614
|
],
|
|
605
|
-
filter_query=
|
|
615
|
+
filter_query=self._get_endpoint_filter(endpoint_id=endpoint_id),
|
|
606
616
|
timestamp_column=mm_schemas.WriterEvent.END_INFER_TIME,
|
|
607
617
|
group_by=[
|
|
608
618
|
mm_schemas.WriterEvent.APPLICATION_NAME,
|
|
609
619
|
mm_schemas.MetricData.METRIC_NAME,
|
|
620
|
+
mm_schemas.EventFieldType.ENDPOINT_ID,
|
|
610
621
|
],
|
|
611
622
|
agg_funcs=["last"],
|
|
612
623
|
)
|
|
@@ -624,7 +635,7 @@ class TDEngineConnector(TSDBConnector):
|
|
|
624
635
|
|
|
625
636
|
def get_results_metadata(
|
|
626
637
|
self,
|
|
627
|
-
endpoint_id: str,
|
|
638
|
+
endpoint_id: typing.Union[str, list[str]],
|
|
628
639
|
start: typing.Optional[datetime] = None,
|
|
629
640
|
end: typing.Optional[datetime] = None,
|
|
630
641
|
) -> pd.DataFrame:
|
|
@@ -639,11 +650,12 @@ class TDEngineConnector(TSDBConnector):
|
|
|
639
650
|
mm_schemas.ResultData.RESULT_KIND,
|
|
640
651
|
mm_schemas.EventFieldType.ENDPOINT_ID,
|
|
641
652
|
],
|
|
642
|
-
filter_query=
|
|
653
|
+
filter_query=self._get_endpoint_filter(endpoint_id=endpoint_id),
|
|
643
654
|
timestamp_column=mm_schemas.WriterEvent.END_INFER_TIME,
|
|
644
655
|
group_by=[
|
|
645
656
|
mm_schemas.WriterEvent.APPLICATION_NAME,
|
|
646
657
|
mm_schemas.ResultData.RESULT_NAME,
|
|
658
|
+
mm_schemas.EventFieldType.ENDPOINT_ID,
|
|
647
659
|
],
|
|
648
660
|
agg_funcs=["last"],
|
|
649
661
|
)
|
|
@@ -33,6 +33,8 @@ _TSDB_BE = "tsdb"
|
|
|
33
33
|
_TSDB_RATE = "1/s"
|
|
34
34
|
_CONTAINER = "users"
|
|
35
35
|
|
|
36
|
+
V3IO_MEPS_LIMIT = 50 # TODO remove limitation after fixing ML-8886
|
|
37
|
+
|
|
36
38
|
|
|
37
39
|
def _is_no_schema_error(exc: v3io_frames.Error) -> bool:
|
|
38
40
|
"""
|
|
@@ -577,6 +579,21 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
577
579
|
token=v3io_access_key,
|
|
578
580
|
)
|
|
579
581
|
|
|
582
|
+
@staticmethod
|
|
583
|
+
def _get_endpoint_filter(endpoint_id: Union[str, list[str]]):
|
|
584
|
+
if isinstance(endpoint_id, str):
|
|
585
|
+
return f"endpoint_id=='{endpoint_id}'"
|
|
586
|
+
elif isinstance(endpoint_id, list):
|
|
587
|
+
if len(endpoint_id) > V3IO_MEPS_LIMIT:
|
|
588
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
589
|
+
f"Filtering more than {V3IO_MEPS_LIMIT} model endpoints in the V3IO connector is not supported."
|
|
590
|
+
)
|
|
591
|
+
return f"endpoint_id IN({str(endpoint_id)[1:-1]}) "
|
|
592
|
+
else:
|
|
593
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
594
|
+
f"Invalid 'endpoint_id' filter: must be a string or a list, endpoint_id: {endpoint_id}"
|
|
595
|
+
)
|
|
596
|
+
|
|
580
597
|
def read_metrics_data(
|
|
581
598
|
self,
|
|
582
599
|
*,
|
|
@@ -813,17 +830,18 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
813
830
|
|
|
814
831
|
def get_metrics_metadata(
|
|
815
832
|
self,
|
|
816
|
-
endpoint_id: str,
|
|
833
|
+
endpoint_id: Union[str, list[str]],
|
|
817
834
|
start: Optional[datetime] = None,
|
|
818
835
|
end: Optional[datetime] = None,
|
|
819
836
|
) -> pd.DataFrame:
|
|
820
837
|
start, end = self._get_start_end(start, end)
|
|
838
|
+
filter_query = self._get_endpoint_filter(endpoint_id=endpoint_id)
|
|
821
839
|
df = self._get_records(
|
|
822
840
|
table=mm_schemas.V3IOTSDBTables.METRICS,
|
|
823
841
|
start=start,
|
|
824
842
|
end=end,
|
|
825
843
|
columns=[mm_schemas.MetricData.METRIC_VALUE],
|
|
826
|
-
filter_query=
|
|
844
|
+
filter_query=filter_query,
|
|
827
845
|
agg_funcs=["last"],
|
|
828
846
|
)
|
|
829
847
|
if not df.empty:
|
|
@@ -834,11 +852,12 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
834
852
|
|
|
835
853
|
def get_results_metadata(
|
|
836
854
|
self,
|
|
837
|
-
endpoint_id: str,
|
|
855
|
+
endpoint_id: Union[str, list[str]],
|
|
838
856
|
start: Optional[datetime] = None,
|
|
839
857
|
end: Optional[datetime] = None,
|
|
840
858
|
) -> pd.DataFrame:
|
|
841
859
|
start, end = self._get_start_end(start, end)
|
|
860
|
+
filter_query = self._get_endpoint_filter(endpoint_id=endpoint_id)
|
|
842
861
|
df = self._get_records(
|
|
843
862
|
table=mm_schemas.V3IOTSDBTables.APP_RESULTS,
|
|
844
863
|
start=start,
|
|
@@ -846,7 +865,7 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
846
865
|
columns=[
|
|
847
866
|
mm_schemas.ResultData.RESULT_KIND,
|
|
848
867
|
],
|
|
849
|
-
filter_query=
|
|
868
|
+
filter_query=filter_query,
|
|
850
869
|
agg_funcs=["last"],
|
|
851
870
|
)
|
|
852
871
|
if not df.empty:
|
|
@@ -32,7 +32,7 @@ import mlrun.utils.helpers
|
|
|
32
32
|
from mlrun.common.schemas import ModelEndpoint
|
|
33
33
|
from mlrun.common.schemas.model_monitoring.model_endpoints import (
|
|
34
34
|
ModelEndpointMonitoringMetric,
|
|
35
|
-
|
|
35
|
+
compose_full_name,
|
|
36
36
|
)
|
|
37
37
|
from mlrun.utils import logger
|
|
38
38
|
|
|
@@ -450,7 +450,7 @@ def get_default_result_instance_fqn(model_endpoint_id: str) -> str:
|
|
|
450
450
|
|
|
451
451
|
|
|
452
452
|
def get_invocations_fqn(project: str) -> str:
|
|
453
|
-
return
|
|
453
|
+
return compose_full_name(
|
|
454
454
|
project=project,
|
|
455
455
|
app=mm_constants.SpecialApps.MLRUN_INFRA,
|
|
456
456
|
name=mm_constants.PredictionsQueryConstants.INVOCATIONS,
|
mlrun/projects/project.py
CHANGED
|
@@ -2137,18 +2137,23 @@ class MlrunProject(ModelObj):
|
|
|
2137
2137
|
db = mlrun.db.get_run_db(secrets=self._secrets)
|
|
2138
2138
|
matching_results = []
|
|
2139
2139
|
alerts = []
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2140
|
+
endpoint_ids = [endpoint.metadata.uid for endpoint in endpoints.endpoints]
|
|
2141
|
+
# using separation to group by endpoint IDs:
|
|
2142
|
+
# {"mep_id1": [...], "mep_id2": [...]}
|
|
2143
|
+
results_by_endpoint = db.get_metrics_by_multiple_endpoints(
|
|
2144
|
+
project=self.name,
|
|
2145
|
+
endpoint_ids=endpoint_ids,
|
|
2146
|
+
type="results",
|
|
2147
|
+
events_format=mm_constants.GetEventsFormat.SEPARATION,
|
|
2148
|
+
)
|
|
2149
|
+
for endpoint_uid, results in results_by_endpoint.items():
|
|
2145
2150
|
results_fqn_by_endpoint = [
|
|
2146
2151
|
get_result_instance_fqn(
|
|
2147
|
-
model_endpoint_id=
|
|
2152
|
+
model_endpoint_id=endpoint_uid,
|
|
2148
2153
|
app_name=result.app,
|
|
2149
2154
|
result_name=result.name,
|
|
2150
2155
|
)
|
|
2151
|
-
for result in
|
|
2156
|
+
for result in results
|
|
2152
2157
|
]
|
|
2153
2158
|
matching_results += filter_results_by_regex(
|
|
2154
2159
|
existing_result_names=results_fqn_by_endpoint,
|
mlrun/utils/helpers.py
CHANGED
|
@@ -23,6 +23,7 @@ import os
|
|
|
23
23
|
import re
|
|
24
24
|
import string
|
|
25
25
|
import sys
|
|
26
|
+
import traceback
|
|
26
27
|
import typing
|
|
27
28
|
import uuid
|
|
28
29
|
import warnings
|
|
@@ -44,11 +45,16 @@ from pandas import Timedelta, Timestamp
|
|
|
44
45
|
from yaml.representer import RepresenterError
|
|
45
46
|
|
|
46
47
|
import mlrun
|
|
48
|
+
import mlrun.common.constants as mlrun_constants
|
|
47
49
|
import mlrun.common.helpers
|
|
50
|
+
import mlrun.common.runtimes.constants as runtimes_constants
|
|
48
51
|
import mlrun.common.schemas
|
|
49
52
|
import mlrun.errors
|
|
50
53
|
import mlrun.utils.regex
|
|
51
54
|
import mlrun.utils.version.version
|
|
55
|
+
import mlrun_pipelines.common.constants
|
|
56
|
+
import mlrun_pipelines.models
|
|
57
|
+
import mlrun_pipelines.utils
|
|
52
58
|
from mlrun.common.constants import MYSQL_MEDIUMBLOB_SIZE_BYTES
|
|
53
59
|
from mlrun.config import config
|
|
54
60
|
from mlrun_pipelines.models import PipelineRun
|
|
@@ -1904,3 +1910,131 @@ def join_urls(base_url: Optional[str], path: Optional[str]) -> str:
|
|
|
1904
1910
|
if base_url is None:
|
|
1905
1911
|
base_url = ""
|
|
1906
1912
|
return f"{base_url.rstrip('/')}/{path.lstrip('/')}" if path else base_url
|
|
1913
|
+
|
|
1914
|
+
|
|
1915
|
+
class Workflow:
|
|
1916
|
+
@staticmethod
|
|
1917
|
+
def get_workflow_steps(workflow_id: str, project: str) -> list:
|
|
1918
|
+
steps = []
|
|
1919
|
+
db = mlrun.get_run_db()
|
|
1920
|
+
|
|
1921
|
+
def _add_run_step(_step: mlrun_pipelines.models.PipelineStep):
|
|
1922
|
+
try:
|
|
1923
|
+
_run = db.list_runs(
|
|
1924
|
+
project=project,
|
|
1925
|
+
labels=f"{mlrun_constants.MLRunInternalLabels.runner_pod}={_step.node_name}",
|
|
1926
|
+
)[0]
|
|
1927
|
+
except IndexError:
|
|
1928
|
+
_run = {
|
|
1929
|
+
"metadata": {
|
|
1930
|
+
"name": _step.display_name,
|
|
1931
|
+
"project": project,
|
|
1932
|
+
},
|
|
1933
|
+
}
|
|
1934
|
+
_run["step_kind"] = _step.step_type
|
|
1935
|
+
if _step.skipped:
|
|
1936
|
+
_run.setdefault("status", {})["state"] = (
|
|
1937
|
+
runtimes_constants.RunStates.skipped
|
|
1938
|
+
)
|
|
1939
|
+
steps.append(_run)
|
|
1940
|
+
|
|
1941
|
+
def _add_deploy_function_step(_step: mlrun_pipelines.models.PipelineStep):
|
|
1942
|
+
project, name, hash_key = Workflow._extract_function_uri(
|
|
1943
|
+
_step.get_annotation("mlrun/function-uri")
|
|
1944
|
+
)
|
|
1945
|
+
if name:
|
|
1946
|
+
try:
|
|
1947
|
+
function = db.get_function(
|
|
1948
|
+
project=project, name=name, hash_key=hash_key
|
|
1949
|
+
)
|
|
1950
|
+
except mlrun.errors.MLRunNotFoundError:
|
|
1951
|
+
# If the function is not found (if build failed for example), we will create a dummy
|
|
1952
|
+
# function object for the notification to display the function name
|
|
1953
|
+
function = {
|
|
1954
|
+
"metadata": {
|
|
1955
|
+
"name": name,
|
|
1956
|
+
"project": project,
|
|
1957
|
+
"hash_key": hash_key,
|
|
1958
|
+
},
|
|
1959
|
+
}
|
|
1960
|
+
pod_phase = _step.phase
|
|
1961
|
+
if _step.skipped:
|
|
1962
|
+
state = mlrun.common.schemas.FunctionState.skipped
|
|
1963
|
+
else:
|
|
1964
|
+
state = runtimes_constants.PodPhases.pod_phase_to_run_state(
|
|
1965
|
+
pod_phase
|
|
1966
|
+
)
|
|
1967
|
+
function["status"] = {"state": state}
|
|
1968
|
+
if isinstance(function["metadata"].get("updated"), datetime.datetime):
|
|
1969
|
+
function["metadata"]["updated"] = function["metadata"][
|
|
1970
|
+
"updated"
|
|
1971
|
+
].isoformat()
|
|
1972
|
+
function["step_kind"] = _step.step_type
|
|
1973
|
+
steps.append(function)
|
|
1974
|
+
|
|
1975
|
+
step_methods = {
|
|
1976
|
+
mlrun_pipelines.common.constants.PipelineRunType.run: _add_run_step,
|
|
1977
|
+
mlrun_pipelines.common.constants.PipelineRunType.build: _add_deploy_function_step,
|
|
1978
|
+
mlrun_pipelines.common.constants.PipelineRunType.deploy: _add_deploy_function_step,
|
|
1979
|
+
}
|
|
1980
|
+
|
|
1981
|
+
if not workflow_id:
|
|
1982
|
+
return steps
|
|
1983
|
+
|
|
1984
|
+
workflow_manifest = Workflow._get_workflow_manifest(workflow_id)
|
|
1985
|
+
if not workflow_manifest:
|
|
1986
|
+
return steps
|
|
1987
|
+
|
|
1988
|
+
try:
|
|
1989
|
+
for step in workflow_manifest.get_steps():
|
|
1990
|
+
step_method = step_methods.get(step.step_type)
|
|
1991
|
+
if step_method:
|
|
1992
|
+
step_method(step)
|
|
1993
|
+
return steps
|
|
1994
|
+
except Exception:
|
|
1995
|
+
# If we fail to read the pipeline steps, we will return the list of runs that have the same workflow id
|
|
1996
|
+
logger.warning(
|
|
1997
|
+
"Failed to extract workflow steps from workflow manifest, "
|
|
1998
|
+
"returning all runs with the workflow id label",
|
|
1999
|
+
workflow_id=workflow_id,
|
|
2000
|
+
traceback=traceback.format_exc(),
|
|
2001
|
+
)
|
|
2002
|
+
return db.list_runs(
|
|
2003
|
+
project=project,
|
|
2004
|
+
labels=f"workflow={workflow_id}",
|
|
2005
|
+
)
|
|
2006
|
+
|
|
2007
|
+
@staticmethod
|
|
2008
|
+
def _extract_function_uri(function_uri: str) -> tuple[str, str, str]:
|
|
2009
|
+
"""
|
|
2010
|
+
Extract the project, name, and hash key from a function uri.
|
|
2011
|
+
Examples:
|
|
2012
|
+
- "project/name@hash_key" returns project, name, hash_key
|
|
2013
|
+
- "project/name returns" project, name, ""
|
|
2014
|
+
"""
|
|
2015
|
+
project, name, hash_key = None, None, None
|
|
2016
|
+
hashed_pattern = r"^(.+)/(.+)@(.+)$"
|
|
2017
|
+
pattern = r"^(.+)/(.+)$"
|
|
2018
|
+
match = re.match(hashed_pattern, function_uri)
|
|
2019
|
+
if match:
|
|
2020
|
+
project, name, hash_key = match.groups()
|
|
2021
|
+
else:
|
|
2022
|
+
match = re.match(pattern, function_uri)
|
|
2023
|
+
if match:
|
|
2024
|
+
project, name = match.groups()
|
|
2025
|
+
hash_key = ""
|
|
2026
|
+
return project, name, hash_key
|
|
2027
|
+
|
|
2028
|
+
@staticmethod
|
|
2029
|
+
def _get_workflow_manifest(
|
|
2030
|
+
workflow_id: str,
|
|
2031
|
+
) -> typing.Optional[mlrun_pipelines.models.PipelineManifest]:
|
|
2032
|
+
kfp_client = mlrun_pipelines.utils.get_client(mlrun.mlconf.kfp_url)
|
|
2033
|
+
|
|
2034
|
+
# arbitrary timeout of 5 seconds, the workflow should be done by now
|
|
2035
|
+
kfp_run = kfp_client.wait_for_run_completion(workflow_id, 5)
|
|
2036
|
+
if not kfp_run:
|
|
2037
|
+
return None
|
|
2038
|
+
|
|
2039
|
+
kfp_run = mlrun_pipelines.models.PipelineRun(kfp_run)
|
|
2040
|
+
return kfp_run.workflow_manifest()
|
|
@@ -118,6 +118,9 @@ class WebhookNotification(NotificationBase):
|
|
|
118
118
|
|
|
119
119
|
if isinstance(override_body, dict):
|
|
120
120
|
for key, value in override_body.items():
|
|
121
|
+
if not isinstance(value, str):
|
|
122
|
+
# If the value is not a string, we don't want to parse it
|
|
123
|
+
continue
|
|
121
124
|
if re.search(r"{{\s*runs\s*}}", value):
|
|
122
125
|
str_parsed_runs = parse_runs()
|
|
123
126
|
override_body[key] = re.sub(
|
|
@@ -15,7 +15,6 @@
|
|
|
15
15
|
import asyncio
|
|
16
16
|
import datetime
|
|
17
17
|
import os
|
|
18
|
-
import re
|
|
19
18
|
import traceback
|
|
20
19
|
import typing
|
|
21
20
|
from concurrent.futures import ThreadPoolExecutor
|
|
@@ -31,11 +30,7 @@ import mlrun.model
|
|
|
31
30
|
import mlrun.utils.helpers
|
|
32
31
|
import mlrun.utils.notifications.notification as notification_module
|
|
33
32
|
import mlrun.utils.notifications.notification.base as base
|
|
34
|
-
import
|
|
35
|
-
import mlrun_pipelines.common.ops
|
|
36
|
-
import mlrun_pipelines.models
|
|
37
|
-
import mlrun_pipelines.utils
|
|
38
|
-
from mlrun.utils import logger
|
|
33
|
+
from mlrun.utils import Workflow, logger
|
|
39
34
|
from mlrun.utils.condition_evaluator import evaluate_condition_in_separate_process
|
|
40
35
|
|
|
41
36
|
|
|
@@ -283,7 +278,9 @@ class NotificationPusher(_NotificationPusherBase):
|
|
|
283
278
|
custom_message = (
|
|
284
279
|
f" (workflow: {run.metadata.labels['workflow']}){custom_message}"
|
|
285
280
|
)
|
|
286
|
-
|
|
281
|
+
project = run.metadata.project
|
|
282
|
+
workflow_id = run.status.results.get("workflow_id", None)
|
|
283
|
+
runs.extend(Workflow.get_workflow_steps(workflow_id, project))
|
|
287
284
|
|
|
288
285
|
message = (
|
|
289
286
|
self.messages.get(run.state(), "").format(resource=resource)
|
|
@@ -442,131 +439,6 @@ class NotificationPusher(_NotificationPusherBase):
|
|
|
442
439
|
mask_params=False,
|
|
443
440
|
)
|
|
444
441
|
|
|
445
|
-
def get_workflow_steps(self, run: mlrun.model.RunObject) -> list:
|
|
446
|
-
steps = []
|
|
447
|
-
db = mlrun.get_run_db()
|
|
448
|
-
|
|
449
|
-
def _add_run_step(_step: mlrun_pipelines.models.PipelineStep):
|
|
450
|
-
try:
|
|
451
|
-
_run = db.list_runs(
|
|
452
|
-
project=run.metadata.project,
|
|
453
|
-
labels=f"{mlrun_constants.MLRunInternalLabels.runner_pod}={_step.node_name}",
|
|
454
|
-
)[0]
|
|
455
|
-
except IndexError:
|
|
456
|
-
_run = {
|
|
457
|
-
"metadata": {
|
|
458
|
-
"name": _step.display_name,
|
|
459
|
-
"project": run.metadata.project,
|
|
460
|
-
},
|
|
461
|
-
}
|
|
462
|
-
_run["step_kind"] = _step.step_type
|
|
463
|
-
if _step.skipped:
|
|
464
|
-
_run.setdefault("status", {})["state"] = (
|
|
465
|
-
runtimes_constants.RunStates.skipped
|
|
466
|
-
)
|
|
467
|
-
steps.append(_run)
|
|
468
|
-
|
|
469
|
-
def _add_deploy_function_step(_step: mlrun_pipelines.models.PipelineStep):
|
|
470
|
-
project, name, hash_key = self._extract_function_uri(
|
|
471
|
-
_step.get_annotation("mlrun/function-uri")
|
|
472
|
-
)
|
|
473
|
-
if name:
|
|
474
|
-
try:
|
|
475
|
-
function = db.get_function(
|
|
476
|
-
project=project, name=name, hash_key=hash_key
|
|
477
|
-
)
|
|
478
|
-
except mlrun.errors.MLRunNotFoundError:
|
|
479
|
-
# If the function is not found (if build failed for example), we will create a dummy
|
|
480
|
-
# function object for the notification to display the function name
|
|
481
|
-
function = {
|
|
482
|
-
"metadata": {
|
|
483
|
-
"name": name,
|
|
484
|
-
"project": project,
|
|
485
|
-
"hash_key": hash_key,
|
|
486
|
-
},
|
|
487
|
-
}
|
|
488
|
-
pod_phase = _step.phase
|
|
489
|
-
if _step.skipped:
|
|
490
|
-
state = mlrun.common.schemas.FunctionState.skipped
|
|
491
|
-
else:
|
|
492
|
-
state = runtimes_constants.PodPhases.pod_phase_to_run_state(
|
|
493
|
-
pod_phase
|
|
494
|
-
)
|
|
495
|
-
function["status"] = {"state": state}
|
|
496
|
-
if isinstance(function["metadata"].get("updated"), datetime.datetime):
|
|
497
|
-
function["metadata"]["updated"] = function["metadata"][
|
|
498
|
-
"updated"
|
|
499
|
-
].isoformat()
|
|
500
|
-
function["step_kind"] = _step.step_type
|
|
501
|
-
steps.append(function)
|
|
502
|
-
|
|
503
|
-
step_methods = {
|
|
504
|
-
mlrun_pipelines.common.constants.PipelineRunType.run: _add_run_step,
|
|
505
|
-
mlrun_pipelines.common.constants.PipelineRunType.build: _add_deploy_function_step,
|
|
506
|
-
mlrun_pipelines.common.constants.PipelineRunType.deploy: _add_deploy_function_step,
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
workflow_id = run.status.results.get("workflow_id", None)
|
|
510
|
-
if not workflow_id:
|
|
511
|
-
return steps
|
|
512
|
-
|
|
513
|
-
workflow_manifest = self._get_workflow_manifest(workflow_id)
|
|
514
|
-
if not workflow_manifest:
|
|
515
|
-
return steps
|
|
516
|
-
|
|
517
|
-
try:
|
|
518
|
-
for step in workflow_manifest.get_steps():
|
|
519
|
-
step_method = step_methods.get(step.step_type)
|
|
520
|
-
if step_method:
|
|
521
|
-
step_method(step)
|
|
522
|
-
return steps
|
|
523
|
-
except Exception:
|
|
524
|
-
# If we fail to read the pipeline steps, we will return the list of runs that have the same workflow id
|
|
525
|
-
logger.warning(
|
|
526
|
-
"Failed to extract workflow steps from workflow manifest, "
|
|
527
|
-
"returning all runs with the workflow id label",
|
|
528
|
-
workflow_id=workflow_id,
|
|
529
|
-
traceback=traceback.format_exc(),
|
|
530
|
-
)
|
|
531
|
-
return db.list_runs(
|
|
532
|
-
project=run.metadata.project,
|
|
533
|
-
labels=f"workflow={workflow_id}",
|
|
534
|
-
)
|
|
535
|
-
|
|
536
|
-
@staticmethod
|
|
537
|
-
def _get_workflow_manifest(
|
|
538
|
-
workflow_id: str,
|
|
539
|
-
) -> typing.Optional[mlrun_pipelines.models.PipelineManifest]:
|
|
540
|
-
kfp_client = mlrun_pipelines.utils.get_client(mlrun.mlconf.kfp_url)
|
|
541
|
-
|
|
542
|
-
# arbitrary timeout of 5 seconds, the workflow should be done by now
|
|
543
|
-
kfp_run = kfp_client.wait_for_run_completion(workflow_id, 5)
|
|
544
|
-
if not kfp_run:
|
|
545
|
-
return None
|
|
546
|
-
|
|
547
|
-
kfp_run = mlrun_pipelines.models.PipelineRun(kfp_run)
|
|
548
|
-
return kfp_run.workflow_manifest()
|
|
549
|
-
|
|
550
|
-
def _extract_function_uri(self, function_uri: str) -> tuple[str, str, str]:
|
|
551
|
-
"""
|
|
552
|
-
Extract the project, name, and hash key from a function uri.
|
|
553
|
-
Examples:
|
|
554
|
-
- "project/name@hash_key" returns project, name, hash_key
|
|
555
|
-
- "project/name returns" project, name, ""
|
|
556
|
-
"""
|
|
557
|
-
project, name, hash_key = None, None, None
|
|
558
|
-
hashed_pattern = r"^(.+)/(.+)@(.+)$"
|
|
559
|
-
pattern = r"^(.+)/(.+)$"
|
|
560
|
-
match = re.match(hashed_pattern, function_uri)
|
|
561
|
-
if match:
|
|
562
|
-
project, name, hash_key = match.groups()
|
|
563
|
-
else:
|
|
564
|
-
match = re.match(pattern, function_uri)
|
|
565
|
-
if match:
|
|
566
|
-
project, name = match.groups()
|
|
567
|
-
hash_key = ""
|
|
568
|
-
return project, name, hash_key
|
|
569
|
-
|
|
570
442
|
|
|
571
443
|
class CustomNotificationPusher(_NotificationPusherBase):
|
|
572
444
|
def __init__(self, notification_types: typing.Optional[list[str]] = None):
|
mlrun/utils/version/version.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
mlrun/__init__.py,sha256=7vuMpUiigXXDrghLRq680LKWy1faC0kQyGCZb_7cwyE,7473
|
|
2
2
|
mlrun/__main__.py,sha256=o65gXHhmFA9GV_n2mqmAO80nW3MAwo_s7j80IKgCzRE,45949
|
|
3
|
-
mlrun/config.py,sha256=
|
|
3
|
+
mlrun/config.py,sha256=zHp7wtbaewFbHYsA_SLxwuwSDYwrcL6A3knWSPcRtAA,70547
|
|
4
4
|
mlrun/errors.py,sha256=5raKb1PXQpTcIvWQ4sr1qn2IS7P_GT_FydBJ0dXkVuc,8097
|
|
5
5
|
mlrun/execution.py,sha256=Up9U6xonTElRIaesF9Vej2JK1Isk2AZNK9ke0XcF5Dg,49030
|
|
6
6
|
mlrun/features.py,sha256=ReBaNGsBYXqcbgI012n-SO_j6oHIbk_Vpv0CGPXbUmo,15842
|
|
@@ -42,7 +42,7 @@ mlrun/common/runtimes/constants.py,sha256=Mok3m9Rv182TTMp7uYNfWalm9Xcz86yva-4fTx
|
|
|
42
42
|
mlrun/common/schemas/__init__.py,sha256=PBuIAhXSkVEVxxKcv5hR_xvTwNAUqxOXHVPugOoWTyM,5386
|
|
43
43
|
mlrun/common/schemas/alert.py,sha256=G9lFTXFYDor-RVLpJxMorIPlLWr_-GYCFKRN9DkKwXs,10124
|
|
44
44
|
mlrun/common/schemas/api_gateway.py,sha256=3a0QxECLmoDkD5IiOKtXJL-uiWB26Hg55WMA3nULYuI,7127
|
|
45
|
-
mlrun/common/schemas/artifact.py,sha256=
|
|
45
|
+
mlrun/common/schemas/artifact.py,sha256=f0NPsoZmA-WD9RtN-dcKFW6KuV0PPQB25A2psF7LbP8,4013
|
|
46
46
|
mlrun/common/schemas/auth.py,sha256=AGbBNvQq_vcvhX_NLqbT-QPHL4BAJMB3xwBXW7cFvpo,6761
|
|
47
47
|
mlrun/common/schemas/background_task.py,sha256=ofWRAQGGEkXEu79Dbw7tT_5GPolR09Lc3Ebg2r0fT24,1728
|
|
48
48
|
mlrun/common/schemas/client_spec.py,sha256=CCdAMwRS2DFKddxNSulWcRDwp3mrE7dDdKeAD6djxLo,2856
|
|
@@ -71,10 +71,10 @@ mlrun/common/schemas/schedule.py,sha256=LTWdZ4FvKDGkmmfyqKoBQ36VFqnnyIYLnq1I6qrT
|
|
|
71
71
|
mlrun/common/schemas/secret.py,sha256=CCxFYiPwJtDxwg2VVJH9nUG9cAZ2a34IjeuaWv-BYlc,1487
|
|
72
72
|
mlrun/common/schemas/tag.py,sha256=HRZi5QZ4vVGaCr2AMk9eJgcNiAIXmH4YDc8a4fvF770,893
|
|
73
73
|
mlrun/common/schemas/workflow.py,sha256=rwYzDJYxpE9k4kC88j_eUCmqK4ZsWV_h-_nli7Fs7Ow,2078
|
|
74
|
-
mlrun/common/schemas/model_monitoring/__init__.py,sha256=
|
|
75
|
-
mlrun/common/schemas/model_monitoring/constants.py,sha256=
|
|
74
|
+
mlrun/common/schemas/model_monitoring/__init__.py,sha256=jz0fvdn8BEecgUCKhiSNH6QtFhSW4O19Ql9KXo0AxOg,1900
|
|
75
|
+
mlrun/common/schemas/model_monitoring/constants.py,sha256=KHpZiTruqr1iQ4XfEz8Ptj9ytCBuvwOXp30YTt5YQws,11834
|
|
76
76
|
mlrun/common/schemas/model_monitoring/grafana.py,sha256=Rq10KKOyyUYr7qOQFZfwGZtUim0LY9O0LQ5uc9jmIVQ,1562
|
|
77
|
-
mlrun/common/schemas/model_monitoring/model_endpoints.py,sha256=
|
|
77
|
+
mlrun/common/schemas/model_monitoring/model_endpoints.py,sha256=j60-_puybc4yLlmoWkZ04m6PuY4p5yfzVJpPo0_n1sY,11725
|
|
78
78
|
mlrun/data_types/__init__.py,sha256=unRo9GGwCmj0hBKBRsXJ2P4BzpQaddlQTvIrVQaKluI,984
|
|
79
79
|
mlrun/data_types/data_types.py,sha256=0_oKLC6-sXL2_nnaDMP_HSXB3fD1nJAG4J2Jq6sGNNw,4998
|
|
80
80
|
mlrun/data_types/infer.py,sha256=KdaRgWcqvLkuLjXrMuDr3ik6WY7JP5wJO0Yii_Vl5kw,6173
|
|
@@ -107,9 +107,9 @@ mlrun/datastore/wasbfs/__init__.py,sha256=s5Ul-0kAhYqFjKDR2X0O2vDGDbLQQduElb32Ev
|
|
|
107
107
|
mlrun/datastore/wasbfs/fs.py,sha256=ge8NK__5vTcFT-krI155_8RDUywQw4SIRX6BWATXy9Q,6299
|
|
108
108
|
mlrun/db/__init__.py,sha256=WqJ4x8lqJ7ZoKbhEyFqkYADd9P6E3citckx9e9ZLcIU,1163
|
|
109
109
|
mlrun/db/auth_utils.py,sha256=hpg8D2r82oN0BWabuWN04BTNZ7jYMAF242YSUpK7LFM,5211
|
|
110
|
-
mlrun/db/base.py,sha256=
|
|
110
|
+
mlrun/db/base.py,sha256=aFtDl4J_yeEovso7uvnnn9KLYMnIRiS52qM1uemdG8k,30218
|
|
111
111
|
mlrun/db/factory.py,sha256=yP2vVmveUE7LYTCHbS6lQIxP9rW--zdISWuPd_I3d_4,2111
|
|
112
|
-
mlrun/db/httpdb.py,sha256=
|
|
112
|
+
mlrun/db/httpdb.py,sha256=VXp3ETu5fl_-6lEF_bsUerKESuRXbAFxLYvduH6DlKs,228070
|
|
113
113
|
mlrun/db/nopdb.py,sha256=v285LHP_Onfuo8KRF078IAPHIXTeEhQsU58QoY7x-b0,26673
|
|
114
114
|
mlrun/feature_store/__init__.py,sha256=AVnY2AFUNc2dKxLLUMx2K3Wo1eGviv0brDcYlDnmtf4,1506
|
|
115
115
|
mlrun/feature_store/api.py,sha256=qkojZpzqGAn3r9ww0ynBRKOs8ji8URaK4DSYD4SE-CE,50395
|
|
@@ -219,7 +219,7 @@ mlrun/model_monitoring/__init__.py,sha256=ELy7njEtZnz09Dc6PGZSFFEGtnwI15bJNWM3Pj
|
|
|
219
219
|
mlrun/model_monitoring/api.py,sha256=nH5aEUkmUEJF0CurrWJxmxVv1tQed2yzCLhQByG1L00,28561
|
|
220
220
|
mlrun/model_monitoring/controller.py,sha256=dBfZQswF67vqeUFnmgsm9jU_5sOs9dLwMPEiYHG-Kk8,19786
|
|
221
221
|
mlrun/model_monitoring/features_drift_table.py,sha256=c6GpKtpOJbuT1u5uMWDL_S-6N4YPOmlktWMqPme3KFY,25308
|
|
222
|
-
mlrun/model_monitoring/helpers.py,sha256=
|
|
222
|
+
mlrun/model_monitoring/helpers.py,sha256=6L-IO4EUAYoAf74snGNiGDln_p77OIdxVdrf392VBzY,17936
|
|
223
223
|
mlrun/model_monitoring/stream_processing.py,sha256=ltCVgo_b3yay16CUbqeGkRfzCHZSn14lVeBng5m9keY,31738
|
|
224
224
|
mlrun/model_monitoring/tracking_policy.py,sha256=PBIGrUYWrwcE5gwXupBIVzOb0QRRwPJsgQm_yLGQxB4,5595
|
|
225
225
|
mlrun/model_monitoring/writer.py,sha256=vbL7bqTyNu8q4bNcebX72sUMybVDAoTWg-CXq4fov3Y,8429
|
|
@@ -234,15 +234,15 @@ mlrun/model_monitoring/db/__init__.py,sha256=r47xPGZpIfMuv8J3PQCZTSqVPMhUta4sSJC
|
|
|
234
234
|
mlrun/model_monitoring/db/_schedules.py,sha256=NTO1rbSyhW1JidpBDSN39ZBD0ctp5pbJFYQwxKRIRrs,5821
|
|
235
235
|
mlrun/model_monitoring/db/_stats.py,sha256=VVMWLMqG3Us3ozBkLaokJF22Ewv8WKmVE1-OvS_g9vA,6943
|
|
236
236
|
mlrun/model_monitoring/db/tsdb/__init__.py,sha256=_ejhGA-bi5-6kEMEVYUN5TLxND8hTj4gMG7WrYIkpxM,4585
|
|
237
|
-
mlrun/model_monitoring/db/tsdb/base.py,sha256=
|
|
237
|
+
mlrun/model_monitoring/db/tsdb/base.py,sha256=JjLBzZXE4ZxtBmihVXjUYZ2HKmgqX03ZhUynXp4948o,25372
|
|
238
238
|
mlrun/model_monitoring/db/tsdb/helpers.py,sha256=0oUXc4aUkYtP2SGP6jTb3uPPKImIUsVsrb9otX9a7O4,1189
|
|
239
239
|
mlrun/model_monitoring/db/tsdb/tdengine/__init__.py,sha256=vgBdsKaXUURKqIf3M0y4sRatmSVA4CQiJs7J5dcVBkQ,620
|
|
240
240
|
mlrun/model_monitoring/db/tsdb/tdengine/schemas.py,sha256=6de8P0CJMqe7PGttoZNt9UtrbBcJnpIp82hk_MbtepA,12477
|
|
241
241
|
mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py,sha256=Uadj0UvAmln2MxDWod-kAzau1uNlqZh981rPhbUH_5M,2857
|
|
242
|
-
mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py,sha256=
|
|
242
|
+
mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py,sha256=IEpJknjqx_LYcZjIccPuujOfEruXsRm8c8YzYaWWNEQ,30175
|
|
243
243
|
mlrun/model_monitoring/db/tsdb/v3io/__init__.py,sha256=aL3bfmQsUQ-sbvKGdNihFj8gLCK3mSys0qDcXtYOwgc,616
|
|
244
244
|
mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py,sha256=_-zo9relCDtjGgievxAcAP9gVN9nDWs8BzGtFwTjb9M,6284
|
|
245
|
-
mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py,sha256=
|
|
245
|
+
mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py,sha256=9j_TWS_3OXTwjiYw9jb92z8URHUXDW7hCgwZnpb2P8c,36834
|
|
246
246
|
mlrun/model_monitoring/metrics/__init__.py,sha256=6CsTXAxeLbbf8yfCADTaxmiavqwrLEdYFJ-qc5kgDAY,569
|
|
247
247
|
mlrun/model_monitoring/metrics/histogram_distance.py,sha256=E9_WIl2vd6qNvoHVHoFcnuQk3ekbFWOdi8aU7sHrfk4,4724
|
|
248
248
|
mlrun/package/__init__.py,sha256=v7VDyK9kDOOuDvFo4oiGV2fx-vM1KL7fdN9pGLakhUQ,7008
|
|
@@ -267,7 +267,7 @@ mlrun/platforms/iguazio.py,sha256=6VBTq8eQ3mzT96tzjYhAtcMQ2VjF4x8LpIPW5DAcX2Q,13
|
|
|
267
267
|
mlrun/projects/__init__.py,sha256=0Krf0WIKfnZa71WthYOg0SoaTodGg3sV_hK3f_OlTPI,1220
|
|
268
268
|
mlrun/projects/operations.py,sha256=VXUlMrouFTls-I-bMhdN5pPfQ34TR7bFQ-NUSWNvl84,20029
|
|
269
269
|
mlrun/projects/pipelines.py,sha256=vZpyiERUzwPMS7NCC5ghI0KB_DItIddr7MMWGTwLawY,47437
|
|
270
|
-
mlrun/projects/project.py,sha256=
|
|
270
|
+
mlrun/projects/project.py,sha256=WKIgBy1nqHxQ4d2AHId6wptnGeYzfZTY61K8pOQUrvI,228386
|
|
271
271
|
mlrun/runtimes/__init__.py,sha256=J9Sy2HiyMlztNv6VUurMzF5H2XzttNil8nRsWDsqLyg,8923
|
|
272
272
|
mlrun/runtimes/base.py,sha256=Yt2l7srrXjK783cunBEKH0yQxQZRH8lkedXNOXuLbbo,37841
|
|
273
273
|
mlrun/runtimes/daskjob.py,sha256=JwuGvOiPsxEDHHMMUS4Oie4hLlYYIZwihAl6DjroTY0,19521
|
|
@@ -318,7 +318,7 @@ mlrun/utils/azure_vault.py,sha256=IEFizrDGDbAaoWwDr1WoA88S_EZ0T--vjYtY-i0cvYQ,34
|
|
|
318
318
|
mlrun/utils/clones.py,sha256=y3zC9QS7z5mLuvyQ6vFd6sJnikbgtDwrBvieQq0sovY,7359
|
|
319
319
|
mlrun/utils/condition_evaluator.py,sha256=-nGfRmZzivn01rHTroiGY4rqEv8T1irMyhzxEei-sKc,1897
|
|
320
320
|
mlrun/utils/db.py,sha256=blQgkWMfFH9lcN4sgJQcPQgEETz2Dl_zwbVA0SslpFg,2186
|
|
321
|
-
mlrun/utils/helpers.py,sha256=
|
|
321
|
+
mlrun/utils/helpers.py,sha256=aPMmd5dEm7PIbx7maXtNGhNKzEHp8JK8uSmOgTtoDbc,69549
|
|
322
322
|
mlrun/utils/http.py,sha256=t6FrXQstZm9xVVjxqIGiLzrwZNCR4CSienSOuVgNIcI,8706
|
|
323
323
|
mlrun/utils/logger.py,sha256=_v4UTv1-STzC2c6aAWAa0NNl9STQoBYbR3OHgAiL41s,14606
|
|
324
324
|
mlrun/utils/regex.py,sha256=IQqwPna6Z8J31xkTUduYbGk48GkQBUJFZSuxAWm1pzU,5162
|
|
@@ -327,7 +327,7 @@ mlrun/utils/singleton.py,sha256=p1Y-X0mPSs_At092GS-pZCA8CTR62HOqPU07_ZH6-To,869
|
|
|
327
327
|
mlrun/utils/v3io_clients.py,sha256=0aCFiQFBmgdSeLzJr_nEP6SG-zyieSgH8RdtcUq4dc0,1294
|
|
328
328
|
mlrun/utils/vault.py,sha256=xUiKL17dCXjwQJ33YRzQj0oadUXATlFWPzKKYAESoQk,10447
|
|
329
329
|
mlrun/utils/notifications/__init__.py,sha256=eUzQDBxSQmMZASRY-YAnYS6tL5801P0wEjycp3Dvoe0,990
|
|
330
|
-
mlrun/utils/notifications/notification_pusher.py,sha256=
|
|
330
|
+
mlrun/utils/notifications/notification_pusher.py,sha256=WN7RMfaZtCoG3bNfMjub1phbvJ2Xw8lKFiq2GStnKGw,24132
|
|
331
331
|
mlrun/utils/notifications/notification/__init__.py,sha256=9Rfy6Jm8n0LaEDO1VAQb6kIbr7_uVuQhK1pS_abELIY,2581
|
|
332
332
|
mlrun/utils/notifications/notification/base.py,sha256=VOgrzRakRfjYYBqvkc0cgEC5pl7KMidP7u-TL4HpGCY,5280
|
|
333
333
|
mlrun/utils/notifications/notification/console.py,sha256=ICbIhOf9fEBJky_3j9TFiKAewDGyDHJr9l4VeT7G2sc,2745
|
|
@@ -335,13 +335,13 @@ mlrun/utils/notifications/notification/git.py,sha256=t2lqRrPRBO4awf_uhxJreH9Cpcb
|
|
|
335
335
|
mlrun/utils/notifications/notification/ipython.py,sha256=9uZvI1uOLFaNuAsfJPXmL3l6dOzFoWdBK5GYNYFAfks,2282
|
|
336
336
|
mlrun/utils/notifications/notification/mail.py,sha256=ZyJ3eqd8simxffQmXzqd3bgbAqp1vij7C6aRJ9h2mgs,6012
|
|
337
337
|
mlrun/utils/notifications/notification/slack.py,sha256=NKV4RFiY3gLsS8uPppgniPLyag8zJ9O1VhixoXkM7kw,7108
|
|
338
|
-
mlrun/utils/notifications/notification/webhook.py,sha256=
|
|
338
|
+
mlrun/utils/notifications/notification/webhook.py,sha256=NeyIMSBojjjTJaUHmPbxMByp34GxYkl1-16NqzU27fU,4943
|
|
339
339
|
mlrun/utils/version/__init__.py,sha256=7kkrB7hEZ3cLXoWj1kPoDwo4MaswsI2JVOBpbKgPAgc,614
|
|
340
|
-
mlrun/utils/version/version.json,sha256=
|
|
340
|
+
mlrun/utils/version/version.json,sha256=9e_fs0UZTGdzgD6yjggI41O7Hv5n_tbuYDMtJsSNspY,89
|
|
341
341
|
mlrun/utils/version/version.py,sha256=eEW0tqIAkU9Xifxv8Z9_qsYnNhn3YH7NRAfM-pPLt1g,1878
|
|
342
|
-
mlrun-1.8.
|
|
343
|
-
mlrun-1.8.
|
|
344
|
-
mlrun-1.8.
|
|
345
|
-
mlrun-1.8.
|
|
346
|
-
mlrun-1.8.
|
|
347
|
-
mlrun-1.8.
|
|
342
|
+
mlrun-1.8.0rc19.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
343
|
+
mlrun-1.8.0rc19.dist-info/METADATA,sha256=A2YOQIMx1P8pckWVn0bKsr8oEpcWxVbhkXhPxTplJMM,24885
|
|
344
|
+
mlrun-1.8.0rc19.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
345
|
+
mlrun-1.8.0rc19.dist-info/entry_points.txt,sha256=1Owd16eAclD5pfRCoJpYC2ZJSyGNTtUr0nCELMioMmU,46
|
|
346
|
+
mlrun-1.8.0rc19.dist-info/top_level.txt,sha256=NObLzw3maSF9wVrgSeYBv-fgnHkAJ1kEkh12DLdd5KM,6
|
|
347
|
+
mlrun-1.8.0rc19.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|