mlrun 1.8.0rc19__py3-none-any.whl → 1.8.0rc21__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/__main__.py +5 -0
- mlrun/common/runtimes/constants.py +17 -0
- mlrun/common/schemas/model_monitoring/constants.py +5 -0
- mlrun/common/schemas/model_monitoring/model_endpoints.py +2 -0
- mlrun/db/base.py +9 -0
- mlrun/db/httpdb.py +76 -1
- mlrun/db/nopdb.py +9 -0
- mlrun/frameworks/_common/model_handler.py +0 -2
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +2 -0
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +15 -19
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +27 -26
- mlrun/model_monitoring/stream_processing.py +21 -0
- mlrun/projects/pipelines.py +16 -3
- mlrun/projects/project.py +33 -1
- mlrun/runtimes/nuclio/serving.py +20 -11
- mlrun/serving/v2_serving.py +51 -36
- mlrun/utils/helpers.py +30 -2
- mlrun/utils/notifications/notification_pusher.py +55 -33
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.8.0rc19.dist-info → mlrun-1.8.0rc21.dist-info}/METADATA +1 -1
- {mlrun-1.8.0rc19.dist-info → mlrun-1.8.0rc21.dist-info}/RECORD +25 -25
- {mlrun-1.8.0rc19.dist-info → mlrun-1.8.0rc21.dist-info}/LICENSE +0 -0
- {mlrun-1.8.0rc19.dist-info → mlrun-1.8.0rc21.dist-info}/WHEEL +0 -0
- {mlrun-1.8.0rc19.dist-info → mlrun-1.8.0rc21.dist-info}/entry_points.txt +0 -0
- {mlrun-1.8.0rc19.dist-info → mlrun-1.8.0rc21.dist-info}/top_level.txt +0 -0
mlrun/__main__.py
CHANGED
|
@@ -32,6 +32,7 @@ from tabulate import tabulate
|
|
|
32
32
|
import mlrun
|
|
33
33
|
import mlrun.common.constants as mlrun_constants
|
|
34
34
|
import mlrun.common.schemas
|
|
35
|
+
import mlrun.utils.helpers
|
|
35
36
|
from mlrun.common.helpers import parse_versioned_object_uri
|
|
36
37
|
from mlrun.runtimes.mounts import auto_mount as auto_mount_modifier
|
|
37
38
|
|
|
@@ -304,6 +305,7 @@ def run(
|
|
|
304
305
|
update_in(runtime, "spec.build.code_origin", url_file)
|
|
305
306
|
elif runtime:
|
|
306
307
|
runtime = py_eval(runtime)
|
|
308
|
+
runtime = mlrun.utils.helpers.as_dict(runtime)
|
|
307
309
|
if not isinstance(runtime, dict):
|
|
308
310
|
print(f"Runtime parameter must be a dict, not {type(runtime)}")
|
|
309
311
|
exit(1)
|
|
@@ -515,6 +517,7 @@ def build(
|
|
|
515
517
|
|
|
516
518
|
if runtime:
|
|
517
519
|
runtime = py_eval(runtime)
|
|
520
|
+
runtime = mlrun.utils.helpers.as_dict(runtime)
|
|
518
521
|
if not isinstance(runtime, dict):
|
|
519
522
|
print(f"Runtime parameter must be a dict, not {type(runtime)}")
|
|
520
523
|
exit(1)
|
|
@@ -662,6 +665,8 @@ def deploy(
|
|
|
662
665
|
runtime = py_eval(spec)
|
|
663
666
|
else:
|
|
664
667
|
runtime = {}
|
|
668
|
+
|
|
669
|
+
runtime = mlrun.utils.helpers.as_dict(runtime)
|
|
665
670
|
if not isinstance(runtime, dict):
|
|
666
671
|
print(f"Runtime parameter must be a dict, not {type(runtime)}")
|
|
667
672
|
exit(1)
|
|
@@ -214,6 +214,23 @@ class RunStates:
|
|
|
214
214
|
RunStates.skipped: mlrun_pipelines.common.models.RunStatuses.skipped,
|
|
215
215
|
}[run_state]
|
|
216
216
|
|
|
217
|
+
@staticmethod
|
|
218
|
+
def pipeline_run_status_to_run_state(pipeline_run_status):
|
|
219
|
+
if pipeline_run_status not in mlrun_pipelines.common.models.RunStatuses.all():
|
|
220
|
+
raise ValueError(f"Invalid pipeline run status: {pipeline_run_status}")
|
|
221
|
+
return {
|
|
222
|
+
mlrun_pipelines.common.models.RunStatuses.succeeded: RunStates.completed,
|
|
223
|
+
mlrun_pipelines.common.models.RunStatuses.failed: RunStates.error,
|
|
224
|
+
mlrun_pipelines.common.models.RunStatuses.running: RunStates.running,
|
|
225
|
+
mlrun_pipelines.common.models.RunStatuses.pending: RunStates.pending,
|
|
226
|
+
mlrun_pipelines.common.models.RunStatuses.canceled: RunStates.aborted,
|
|
227
|
+
mlrun_pipelines.common.models.RunStatuses.canceling: RunStates.aborting,
|
|
228
|
+
mlrun_pipelines.common.models.RunStatuses.skipped: RunStates.skipped,
|
|
229
|
+
mlrun_pipelines.common.models.RunStatuses.runtime_state_unspecified: RunStates.unknown,
|
|
230
|
+
mlrun_pipelines.common.models.RunStatuses.error: RunStates.error,
|
|
231
|
+
mlrun_pipelines.common.models.RunStatuses.paused: RunStates.unknown,
|
|
232
|
+
}[pipeline_run_status]
|
|
233
|
+
|
|
217
234
|
|
|
218
235
|
# TODO: remove this class in 1.9.0 - use only MlrunInternalLabels
|
|
219
236
|
class RunLabels(enum.Enum):
|
|
@@ -61,6 +61,7 @@ class ModelEndpointSchema(MonitoringStrEnum):
|
|
|
61
61
|
STATE = "state"
|
|
62
62
|
MONITORING_MODE = "monitoring_mode"
|
|
63
63
|
FIRST_REQUEST = "first_request"
|
|
64
|
+
SAMPLING_PERCENTAGE = "sampling_percentage"
|
|
64
65
|
|
|
65
66
|
# status - operative
|
|
66
67
|
LAST_REQUEST = "last_request"
|
|
@@ -137,6 +138,10 @@ class EventFieldType:
|
|
|
137
138
|
SAMPLE_PARQUET_PATH = "sample_parquet_path"
|
|
138
139
|
TIME = "time"
|
|
139
140
|
TABLE_COLUMN = "table_column"
|
|
141
|
+
SAMPLING_PERCENTAGE = "sampling_percentage"
|
|
142
|
+
SAMPLING_RATE = "sampling_rate"
|
|
143
|
+
ESTIMATED_PREDICTION_COUNT = "estimated_prediction_count"
|
|
144
|
+
EFFECTIVE_SAMPLE_COUNT = "effective_sample_count"
|
|
140
145
|
|
|
141
146
|
|
|
142
147
|
class FeatureSetFeatures(MonitoringStrEnum):
|
|
@@ -160,6 +160,7 @@ class ModelEndpointStatus(ObjectStatus, ModelEndpointParser):
|
|
|
160
160
|
state: Optional[str] = "unknown" # will be updated according to the function state
|
|
161
161
|
first_request: Optional[datetime] = None
|
|
162
162
|
monitoring_mode: Optional[ModelMonitoringMode] = ModelMonitoringMode.disabled
|
|
163
|
+
sampling_percentage: Optional[float] = 100
|
|
163
164
|
|
|
164
165
|
# operative
|
|
165
166
|
last_request: Optional[datetime] = None
|
|
@@ -177,6 +178,7 @@ class ModelEndpointStatus(ObjectStatus, ModelEndpointParser):
|
|
|
177
178
|
"monitoring_mode",
|
|
178
179
|
"first_request",
|
|
179
180
|
"last_request",
|
|
181
|
+
"sampling_percentage",
|
|
180
182
|
]
|
|
181
183
|
|
|
182
184
|
|
mlrun/db/base.py
CHANGED
mlrun/db/httpdb.py
CHANGED
|
@@ -780,9 +780,84 @@ class HTTPRunDB(RunDBInterface):
|
|
|
780
780
|
)
|
|
781
781
|
if response.status_code == http.HTTPStatus.ACCEPTED:
|
|
782
782
|
background_task = mlrun.common.schemas.BackgroundTask(**response.json())
|
|
783
|
-
|
|
783
|
+
background_task = self._wait_for_background_task_to_reach_terminal_state(
|
|
784
784
|
background_task.metadata.name, project=project
|
|
785
785
|
)
|
|
786
|
+
if (
|
|
787
|
+
background_task.status.state
|
|
788
|
+
== mlrun.common.schemas.BackgroundTaskState.succeeded
|
|
789
|
+
):
|
|
790
|
+
logger.info(
|
|
791
|
+
"Notifications for the run have been pushed",
|
|
792
|
+
project=project,
|
|
793
|
+
run_id=uid,
|
|
794
|
+
)
|
|
795
|
+
elif (
|
|
796
|
+
background_task.status.state
|
|
797
|
+
== mlrun.common.schemas.BackgroundTaskState.failed
|
|
798
|
+
):
|
|
799
|
+
logger.error(
|
|
800
|
+
"Failed to push run notifications",
|
|
801
|
+
project=project,
|
|
802
|
+
run_id=uid,
|
|
803
|
+
error=background_task.status.error,
|
|
804
|
+
)
|
|
805
|
+
return None
|
|
806
|
+
|
|
807
|
+
def push_pipeline_notifications(
|
|
808
|
+
self,
|
|
809
|
+
pipeline_id,
|
|
810
|
+
project="",
|
|
811
|
+
notifications=None,
|
|
812
|
+
timeout=45,
|
|
813
|
+
):
|
|
814
|
+
"""
|
|
815
|
+
Push notifications for a pipeline.
|
|
816
|
+
|
|
817
|
+
:param pipeline_id: Unique ID of the pipeline(KFP).
|
|
818
|
+
:param project: Project that the run belongs to.
|
|
819
|
+
:param notifications: List of notifications to push.
|
|
820
|
+
:returns: :py:class:`~mlrun.common.schemas.BackgroundTask`.
|
|
821
|
+
"""
|
|
822
|
+
if notifications is None or type(notifications) is not list:
|
|
823
|
+
raise MLRunInvalidArgumentError(
|
|
824
|
+
"The 'notifications' parameter must be a list."
|
|
825
|
+
)
|
|
826
|
+
|
|
827
|
+
project = project or config.default_project
|
|
828
|
+
|
|
829
|
+
response = self.api_call(
|
|
830
|
+
"POST",
|
|
831
|
+
path=f"projects/{project}/pipelines/{pipeline_id}/push-notifications",
|
|
832
|
+
error="Failed push notifications",
|
|
833
|
+
body=_as_json([notification.to_dict() for notification in notifications]),
|
|
834
|
+
timeout=timeout,
|
|
835
|
+
)
|
|
836
|
+
if response.status_code == http.HTTPStatus.ACCEPTED:
|
|
837
|
+
background_task = mlrun.common.schemas.BackgroundTask(**response.json())
|
|
838
|
+
background_task = self._wait_for_background_task_to_reach_terminal_state(
|
|
839
|
+
background_task.metadata.name, project=project
|
|
840
|
+
)
|
|
841
|
+
if (
|
|
842
|
+
background_task.status.state
|
|
843
|
+
== mlrun.common.schemas.BackgroundTaskState.succeeded
|
|
844
|
+
):
|
|
845
|
+
logger.info(
|
|
846
|
+
"Pipeline notifications have been pushed",
|
|
847
|
+
project=project,
|
|
848
|
+
pipeline_id=pipeline_id,
|
|
849
|
+
)
|
|
850
|
+
elif (
|
|
851
|
+
background_task.status.state
|
|
852
|
+
== mlrun.common.schemas.BackgroundTaskState.failed
|
|
853
|
+
):
|
|
854
|
+
logger.error(
|
|
855
|
+
"Failed to push pipeline notifications",
|
|
856
|
+
project=project,
|
|
857
|
+
pipeline_id=pipeline_id,
|
|
858
|
+
error=background_task.status.error,
|
|
859
|
+
)
|
|
860
|
+
|
|
786
861
|
return None
|
|
787
862
|
|
|
788
863
|
def read_run(
|
mlrun/db/nopdb.py
CHANGED
|
@@ -84,6 +84,15 @@ class NopDB(RunDBInterface):
|
|
|
84
84
|
):
|
|
85
85
|
pass
|
|
86
86
|
|
|
87
|
+
def push_pipeline_notifications(
|
|
88
|
+
self,
|
|
89
|
+
pipeline_id,
|
|
90
|
+
project="",
|
|
91
|
+
notifications=None,
|
|
92
|
+
timeout=45,
|
|
93
|
+
):
|
|
94
|
+
pass
|
|
95
|
+
|
|
87
96
|
def list_runtime_resources(
|
|
88
97
|
self,
|
|
89
98
|
project: Optional[str] = None,
|
|
@@ -976,7 +976,6 @@ class ModelHandler(ABC, Generic[CommonTypes.ModelType, CommonTypes.IOSampleType]
|
|
|
976
976
|
custom_objects_map_json,
|
|
977
977
|
local_path=custom_objects_map_json,
|
|
978
978
|
artifact_path=self._context.artifact_path,
|
|
979
|
-
db_key=False,
|
|
980
979
|
)
|
|
981
980
|
|
|
982
981
|
# Zip the custom objects directory:
|
|
@@ -997,7 +996,6 @@ class ModelHandler(ABC, Generic[CommonTypes.ModelType, CommonTypes.IOSampleType]
|
|
|
997
996
|
custom_objects_zip,
|
|
998
997
|
local_path=custom_objects_zip,
|
|
999
998
|
artifact_path=self._context.artifact_path,
|
|
1000
|
-
db_key=False,
|
|
1001
999
|
)
|
|
1002
1000
|
|
|
1003
1001
|
return artifacts
|
|
@@ -298,6 +298,8 @@ class Predictions(TDEngineSchema):
|
|
|
298
298
|
mm_schemas.EventFieldType.TIME: _TDEngineColumn.TIMESTAMP,
|
|
299
299
|
mm_schemas.EventFieldType.LATENCY: _TDEngineColumn.FLOAT,
|
|
300
300
|
mm_schemas.EventKeyMetrics.CUSTOM_METRICS: _TDEngineColumn.BINARY_1000,
|
|
301
|
+
mm_schemas.EventFieldType.ESTIMATED_PREDICTION_COUNT: _TDEngineColumn.FLOAT,
|
|
302
|
+
mm_schemas.EventFieldType.EFFECTIVE_SAMPLE_COUNT: _TDEngineColumn.INT,
|
|
301
303
|
}
|
|
302
304
|
tags = {
|
|
303
305
|
mm_schemas.WriterEvent.ENDPOINT_ID: _TDEngineColumn.BINARY_64,
|
|
@@ -165,7 +165,7 @@ class TDEngineConnector(TSDBConnector):
|
|
|
165
165
|
return datetime.fromisoformat(val) if isinstance(val, str) else val
|
|
166
166
|
|
|
167
167
|
@staticmethod
|
|
168
|
-
def _get_endpoint_filter(endpoint_id: typing.Union[str, list[str]]):
|
|
168
|
+
def _get_endpoint_filter(endpoint_id: typing.Union[str, list[str]]) -> str:
|
|
169
169
|
if isinstance(endpoint_id, str):
|
|
170
170
|
return f"endpoint_id='{endpoint_id}'"
|
|
171
171
|
elif isinstance(endpoint_id, list):
|
|
@@ -206,6 +206,8 @@ class TDEngineConnector(TSDBConnector):
|
|
|
206
206
|
columns=[
|
|
207
207
|
mm_schemas.EventFieldType.LATENCY,
|
|
208
208
|
mm_schemas.EventKeyMetrics.CUSTOM_METRICS,
|
|
209
|
+
mm_schemas.EventFieldType.ESTIMATED_PREDICTION_COUNT,
|
|
210
|
+
mm_schemas.EventFieldType.EFFECTIVE_SAMPLE_COUNT,
|
|
209
211
|
],
|
|
210
212
|
tag_cols=[
|
|
211
213
|
mm_schemas.EventFieldType.ENDPOINT_ID,
|
|
@@ -483,7 +485,7 @@ class TDEngineConnector(TSDBConnector):
|
|
|
483
485
|
table=self.tables[mm_schemas.TDEngineSuperTables.PREDICTIONS].super_table,
|
|
484
486
|
start=start,
|
|
485
487
|
end=end,
|
|
486
|
-
columns=[mm_schemas.EventFieldType.
|
|
488
|
+
columns=[mm_schemas.EventFieldType.ESTIMATED_PREDICTION_COUNT],
|
|
487
489
|
filter_query=f"endpoint_id='{endpoint_id}'",
|
|
488
490
|
agg_funcs=agg_funcs,
|
|
489
491
|
interval=aggregation_window,
|
|
@@ -503,10 +505,10 @@ class TDEngineConnector(TSDBConnector):
|
|
|
503
505
|
df["_wend"] = pd.to_datetime(df["_wend"])
|
|
504
506
|
df.set_index("_wend", inplace=True)
|
|
505
507
|
|
|
506
|
-
|
|
507
|
-
f"{agg_funcs[0]}({mm_schemas.EventFieldType.
|
|
508
|
+
estimated_prediction_count = (
|
|
509
|
+
f"{agg_funcs[0]}({mm_schemas.EventFieldType.ESTIMATED_PREDICTION_COUNT})"
|
|
508
510
|
if agg_funcs
|
|
509
|
-
else mm_schemas.EventFieldType.
|
|
511
|
+
else mm_schemas.EventFieldType.ESTIMATED_PREDICTION_COUNT
|
|
510
512
|
)
|
|
511
513
|
|
|
512
514
|
return mm_schemas.ModelEndpointMonitoringMetricValues(
|
|
@@ -514,7 +516,7 @@ class TDEngineConnector(TSDBConnector):
|
|
|
514
516
|
values=list(
|
|
515
517
|
zip(
|
|
516
518
|
df.index,
|
|
517
|
-
df[
|
|
519
|
+
df[estimated_prediction_count],
|
|
518
520
|
)
|
|
519
521
|
), # pyright: ignore[reportArgumentType]
|
|
520
522
|
)
|
|
@@ -525,9 +527,7 @@ class TDEngineConnector(TSDBConnector):
|
|
|
525
527
|
start: typing.Optional[datetime] = None,
|
|
526
528
|
end: typing.Optional[datetime] = None,
|
|
527
529
|
) -> pd.DataFrame:
|
|
528
|
-
|
|
529
|
-
endpoint_ids if isinstance(endpoint_ids, list) else [endpoint_ids]
|
|
530
|
-
)
|
|
530
|
+
filter_query = self._get_endpoint_filter(endpoint_id=endpoint_ids)
|
|
531
531
|
start, end = self._get_start_end(start, end)
|
|
532
532
|
df = self._get_records(
|
|
533
533
|
table=self.tables[mm_schemas.TDEngineSuperTables.PREDICTIONS].super_table,
|
|
@@ -538,7 +538,7 @@ class TDEngineConnector(TSDBConnector):
|
|
|
538
538
|
mm_schemas.EventFieldType.TIME,
|
|
539
539
|
mm_schemas.EventFieldType.LATENCY,
|
|
540
540
|
],
|
|
541
|
-
filter_query=
|
|
541
|
+
filter_query=filter_query,
|
|
542
542
|
timestamp_column=mm_schemas.EventFieldType.TIME,
|
|
543
543
|
agg_funcs=["last"],
|
|
544
544
|
group_by=mm_schemas.EventFieldType.ENDPOINT_ID,
|
|
@@ -567,9 +567,7 @@ class TDEngineConnector(TSDBConnector):
|
|
|
567
567
|
start: typing.Optional[datetime] = None,
|
|
568
568
|
end: typing.Optional[datetime] = None,
|
|
569
569
|
) -> pd.DataFrame:
|
|
570
|
-
|
|
571
|
-
endpoint_ids if isinstance(endpoint_ids, list) else [endpoint_ids]
|
|
572
|
-
)
|
|
570
|
+
filter_query = self._get_endpoint_filter(endpoint_id=endpoint_ids)
|
|
573
571
|
start = start or (mlrun.utils.datetime_now() - timedelta(hours=24))
|
|
574
572
|
start, end = self._get_start_end(start, end)
|
|
575
573
|
df = self._get_records(
|
|
@@ -580,7 +578,7 @@ class TDEngineConnector(TSDBConnector):
|
|
|
580
578
|
mm_schemas.ResultData.RESULT_STATUS,
|
|
581
579
|
mm_schemas.EventFieldType.ENDPOINT_ID,
|
|
582
580
|
],
|
|
583
|
-
filter_query=
|
|
581
|
+
filter_query=filter_query,
|
|
584
582
|
timestamp_column=mm_schemas.WriterEvent.END_INFER_TIME,
|
|
585
583
|
agg_funcs=["max"],
|
|
586
584
|
group_by=mm_schemas.EventFieldType.ENDPOINT_ID,
|
|
@@ -678,9 +676,8 @@ class TDEngineConnector(TSDBConnector):
|
|
|
678
676
|
start: typing.Optional[datetime] = None,
|
|
679
677
|
end: typing.Optional[datetime] = None,
|
|
680
678
|
) -> pd.DataFrame:
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
)
|
|
679
|
+
filter_query = self._get_endpoint_filter(endpoint_id=endpoint_ids)
|
|
680
|
+
filter_query += f"AND {mm_schemas.EventFieldType.ERROR_TYPE} = '{mm_schemas.EventFieldType.INFER_ERROR}'"
|
|
684
681
|
start, end = self._get_start_end(start, end)
|
|
685
682
|
df = self._get_records(
|
|
686
683
|
table=self.tables[mm_schemas.TDEngineSuperTables.ERRORS].super_table,
|
|
@@ -691,8 +688,7 @@ class TDEngineConnector(TSDBConnector):
|
|
|
691
688
|
mm_schemas.EventFieldType.ENDPOINT_ID,
|
|
692
689
|
],
|
|
693
690
|
agg_funcs=["count"],
|
|
694
|
-
filter_query=
|
|
695
|
-
f"AND {mm_schemas.EventFieldType.ERROR_TYPE} = '{mm_schemas.EventFieldType.INFER_ERROR}'",
|
|
691
|
+
filter_query=filter_query,
|
|
696
692
|
group_by=mm_schemas.EventFieldType.ENDPOINT_ID,
|
|
697
693
|
preform_agg_columns=[mm_schemas.EventFieldType.MODEL_ERROR],
|
|
698
694
|
)
|
|
@@ -33,7 +33,7 @@ _TSDB_BE = "tsdb"
|
|
|
33
33
|
_TSDB_RATE = "1/s"
|
|
34
34
|
_CONTAINER = "users"
|
|
35
35
|
|
|
36
|
-
V3IO_MEPS_LIMIT =
|
|
36
|
+
V3IO_MEPS_LIMIT = 200
|
|
37
37
|
|
|
38
38
|
|
|
39
39
|
def _is_no_schema_error(exc: v3io_frames.Error) -> bool:
|
|
@@ -234,6 +234,8 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
234
234
|
columns=[
|
|
235
235
|
mm_schemas.EventFieldType.LATENCY,
|
|
236
236
|
mm_schemas.EventFieldType.LAST_REQUEST_TIMESTAMP,
|
|
237
|
+
mm_schemas.EventFieldType.ESTIMATED_PREDICTION_COUNT,
|
|
238
|
+
mm_schemas.EventFieldType.EFFECTIVE_SAMPLE_COUNT,
|
|
237
239
|
],
|
|
238
240
|
index_cols=[
|
|
239
241
|
mm_schemas.EventFieldType.ENDPOINT_ID,
|
|
@@ -580,14 +582,18 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
580
582
|
)
|
|
581
583
|
|
|
582
584
|
@staticmethod
|
|
583
|
-
def _get_endpoint_filter(endpoint_id: Union[str, list[str]]):
|
|
585
|
+
def _get_endpoint_filter(endpoint_id: Union[str, list[str]]) -> Optional[str]:
|
|
584
586
|
if isinstance(endpoint_id, str):
|
|
585
587
|
return f"endpoint_id=='{endpoint_id}'"
|
|
586
588
|
elif isinstance(endpoint_id, list):
|
|
587
589
|
if len(endpoint_id) > V3IO_MEPS_LIMIT:
|
|
588
|
-
|
|
589
|
-
|
|
590
|
+
logger.info(
|
|
591
|
+
"The number of endpoint ids exceeds the v3io-engine filter-expression limit, "
|
|
592
|
+
"retrieving all the model endpoints from the db.",
|
|
593
|
+
limit=V3IO_MEPS_LIMIT,
|
|
594
|
+
amount=len(endpoint_id),
|
|
590
595
|
)
|
|
596
|
+
return None
|
|
591
597
|
return f"endpoint_id IN({str(endpoint_id)[1:-1]}) "
|
|
592
598
|
else:
|
|
593
599
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
@@ -737,7 +743,7 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
737
743
|
table=mm_schemas.FileTargetKind.PREDICTIONS,
|
|
738
744
|
start=start,
|
|
739
745
|
end=end,
|
|
740
|
-
columns=[mm_schemas.EventFieldType.
|
|
746
|
+
columns=[mm_schemas.EventFieldType.ESTIMATED_PREDICTION_COUNT],
|
|
741
747
|
filter_query=f"endpoint_id=='{endpoint_id}'",
|
|
742
748
|
agg_funcs=agg_funcs,
|
|
743
749
|
sliding_window_step=aggregation_window,
|
|
@@ -751,10 +757,10 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
751
757
|
type=mm_schemas.ModelEndpointMonitoringMetricType.METRIC,
|
|
752
758
|
)
|
|
753
759
|
|
|
754
|
-
|
|
755
|
-
f"{agg_funcs[0]}({mm_schemas.EventFieldType.
|
|
760
|
+
estimated_prediction_count = (
|
|
761
|
+
f"{agg_funcs[0]}({mm_schemas.EventFieldType.ESTIMATED_PREDICTION_COUNT})"
|
|
756
762
|
if agg_funcs
|
|
757
|
-
else mm_schemas.EventFieldType.
|
|
763
|
+
else mm_schemas.EventFieldType.ESTIMATED_PREDICTION_COUNT
|
|
758
764
|
)
|
|
759
765
|
|
|
760
766
|
return mm_schemas.ModelEndpointMonitoringMetricValues(
|
|
@@ -762,7 +768,7 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
762
768
|
values=list(
|
|
763
769
|
zip(
|
|
764
770
|
df.index,
|
|
765
|
-
df[
|
|
771
|
+
df[estimated_prediction_count],
|
|
766
772
|
)
|
|
767
773
|
), # pyright: ignore[reportArgumentType]
|
|
768
774
|
)
|
|
@@ -773,15 +779,13 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
773
779
|
start: Optional[datetime] = None,
|
|
774
780
|
end: Optional[datetime] = None,
|
|
775
781
|
) -> pd.DataFrame:
|
|
776
|
-
|
|
777
|
-
endpoint_ids if isinstance(endpoint_ids, list) else [endpoint_ids]
|
|
778
|
-
)
|
|
782
|
+
filter_query = self._get_endpoint_filter(endpoint_id=endpoint_ids)
|
|
779
783
|
start, end = self._get_start_end(start, end)
|
|
780
784
|
df = self._get_records(
|
|
781
785
|
table=mm_schemas.FileTargetKind.PREDICTIONS,
|
|
782
786
|
start=start,
|
|
783
787
|
end=end,
|
|
784
|
-
filter_query=
|
|
788
|
+
filter_query=filter_query,
|
|
785
789
|
agg_funcs=["last"],
|
|
786
790
|
)
|
|
787
791
|
if not df.empty:
|
|
@@ -808,9 +812,7 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
808
812
|
start: Optional[datetime] = None,
|
|
809
813
|
end: Optional[datetime] = None,
|
|
810
814
|
) -> pd.DataFrame:
|
|
811
|
-
|
|
812
|
-
endpoint_ids if isinstance(endpoint_ids, list) else [endpoint_ids]
|
|
813
|
-
)
|
|
815
|
+
filter_query = self._get_endpoint_filter(endpoint_id=endpoint_ids)
|
|
814
816
|
start = start or (mlrun.utils.datetime_now() - timedelta(hours=24))
|
|
815
817
|
start, end = self._get_start_end(start, end)
|
|
816
818
|
df = self._get_records(
|
|
@@ -818,7 +820,7 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
818
820
|
start=start,
|
|
819
821
|
end=end,
|
|
820
822
|
columns=[mm_schemas.ResultData.RESULT_STATUS],
|
|
821
|
-
filter_query=
|
|
823
|
+
filter_query=filter_query,
|
|
822
824
|
agg_funcs=["max"],
|
|
823
825
|
group_by="endpoint_id",
|
|
824
826
|
)
|
|
@@ -883,17 +885,18 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
883
885
|
start: Optional[datetime] = None,
|
|
884
886
|
end: Optional[datetime] = None,
|
|
885
887
|
) -> pd.DataFrame:
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
888
|
+
filter_query = self._get_endpoint_filter(endpoint_id=endpoint_ids)
|
|
889
|
+
if filter_query:
|
|
890
|
+
filter_query += f"AND {mm_schemas.EventFieldType.ERROR_TYPE} == '{mm_schemas.EventFieldType.INFER_ERROR}'"
|
|
891
|
+
else:
|
|
892
|
+
filter_query = f"{mm_schemas.EventFieldType.ERROR_TYPE} == '{mm_schemas.EventFieldType.INFER_ERROR}' z"
|
|
889
893
|
start, end = self._get_start_end(start, end)
|
|
890
894
|
df = self._get_records(
|
|
891
895
|
table=mm_schemas.FileTargetKind.ERRORS,
|
|
892
896
|
start=start,
|
|
893
897
|
end=end,
|
|
894
898
|
columns=[mm_schemas.EventFieldType.ERROR_COUNT],
|
|
895
|
-
filter_query=
|
|
896
|
-
f"AND {mm_schemas.EventFieldType.ERROR_TYPE} == '{mm_schemas.EventFieldType.INFER_ERROR}'",
|
|
899
|
+
filter_query=filter_query,
|
|
897
900
|
agg_funcs=["count"],
|
|
898
901
|
)
|
|
899
902
|
if not df.empty:
|
|
@@ -912,9 +915,7 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
912
915
|
start: Optional[datetime] = None,
|
|
913
916
|
end: Optional[datetime] = None,
|
|
914
917
|
) -> pd.DataFrame:
|
|
915
|
-
|
|
916
|
-
endpoint_ids if isinstance(endpoint_ids, list) else [endpoint_ids]
|
|
917
|
-
)
|
|
918
|
+
filter_query = self._get_endpoint_filter(endpoint_id=endpoint_ids)
|
|
918
919
|
start = start or (mlrun.utils.datetime_now() - timedelta(hours=24))
|
|
919
920
|
start, end = self._get_start_end(start, end)
|
|
920
921
|
df = self._get_records(
|
|
@@ -922,7 +923,7 @@ class V3IOTSDBConnector(TSDBConnector):
|
|
|
922
923
|
start=start,
|
|
923
924
|
end=end,
|
|
924
925
|
columns=[mm_schemas.EventFieldType.LATENCY],
|
|
925
|
-
filter_query=
|
|
926
|
+
filter_query=filter_query,
|
|
926
927
|
agg_funcs=["avg"],
|
|
927
928
|
)
|
|
928
929
|
if not df.empty:
|
|
@@ -430,6 +430,10 @@ class ProcessEndpointEvent(mlrun.feature_store.steps.MapClass):
|
|
|
430
430
|
if not isinstance(feature, list):
|
|
431
431
|
feature = [feature]
|
|
432
432
|
|
|
433
|
+
effective_sample_count, estimated_prediction_count = (
|
|
434
|
+
self._get_effective_and_estimated_counts(event=event)
|
|
435
|
+
)
|
|
436
|
+
|
|
433
437
|
events.append(
|
|
434
438
|
{
|
|
435
439
|
EventFieldType.FUNCTION_URI: function_uri,
|
|
@@ -453,6 +457,8 @@ class ProcessEndpointEvent(mlrun.feature_store.steps.MapClass):
|
|
|
453
457
|
EventFieldType.ENTITIES: event.get("request", {}).get(
|
|
454
458
|
EventFieldType.ENTITIES, {}
|
|
455
459
|
),
|
|
460
|
+
EventFieldType.EFFECTIVE_SAMPLE_COUNT: effective_sample_count,
|
|
461
|
+
EventFieldType.ESTIMATED_PREDICTION_COUNT: estimated_prediction_count,
|
|
456
462
|
}
|
|
457
463
|
)
|
|
458
464
|
|
|
@@ -507,6 +513,20 @@ class ProcessEndpointEvent(mlrun.feature_store.steps.MapClass):
|
|
|
507
513
|
self.error_count[endpoint_id] += 1
|
|
508
514
|
return False
|
|
509
515
|
|
|
516
|
+
@staticmethod
|
|
517
|
+
def _get_effective_and_estimated_counts(event):
|
|
518
|
+
"""
|
|
519
|
+
Calculate the `effective_sample_count` and the `estimated_prediction_count` based on the event's
|
|
520
|
+
sampling percentage. These values will be stored in the TSDB target.
|
|
521
|
+
Note that In non-batch serving, the `effective_sample_count` is always set to 1. In addition, when the sampling
|
|
522
|
+
percentage is 100%, the `estimated_prediction_count` is equal to the `effective_sample_count`.
|
|
523
|
+
"""
|
|
524
|
+
effective_sample_count = event.get(EventFieldType.EFFECTIVE_SAMPLE_COUNT, 1)
|
|
525
|
+
estimated_prediction_count = effective_sample_count * (
|
|
526
|
+
100 / event.get(EventFieldType.SAMPLING_PERCENTAGE, 100)
|
|
527
|
+
)
|
|
528
|
+
return effective_sample_count, estimated_prediction_count
|
|
529
|
+
|
|
510
530
|
|
|
511
531
|
def is_not_none(field: typing.Any, dict_path: list[str]):
|
|
512
532
|
if field is not None:
|
|
@@ -672,6 +692,7 @@ class MapFeatureNames(mlrun.feature_store.steps.MapClass):
|
|
|
672
692
|
)
|
|
673
693
|
)
|
|
674
694
|
self.first_request[endpoint_id] = True
|
|
695
|
+
|
|
675
696
|
if attributes_to_update:
|
|
676
697
|
logger.info(
|
|
677
698
|
"Updating endpoint record",
|
mlrun/projects/pipelines.py
CHANGED
|
@@ -523,11 +523,12 @@ class _PipelineRunner(abc.ABC):
|
|
|
523
523
|
text = _PipelineRunner._generate_workflow_finished_message(
|
|
524
524
|
run.run_id, errors_counter, run._state
|
|
525
525
|
)
|
|
526
|
-
|
|
527
526
|
notifiers = notifiers or project.notifiers
|
|
528
527
|
if notifiers:
|
|
529
528
|
notifiers.push(text, "info", runs)
|
|
530
529
|
|
|
530
|
+
project.push_pipeline_notification_kfp_runner(run.run_id, run._state, text)
|
|
531
|
+
|
|
531
532
|
if raise_error:
|
|
532
533
|
raise raise_error
|
|
533
534
|
return state or run._state, errors_counter, text
|
|
@@ -620,6 +621,8 @@ class _KFPRunner(_PipelineRunner):
|
|
|
620
621
|
params.update(notification.secret_params)
|
|
621
622
|
project.notifiers.add_notification(notification.kind, params)
|
|
622
623
|
|
|
624
|
+
project.spec.notifications = notifications
|
|
625
|
+
|
|
623
626
|
run_id = _run_pipeline(
|
|
624
627
|
workflow_handler,
|
|
625
628
|
project=project.metadata.name,
|
|
@@ -647,13 +650,23 @@ class _KFPRunner(_PipelineRunner):
|
|
|
647
650
|
exc_info=err_to_str(exc),
|
|
648
651
|
)
|
|
649
652
|
|
|
650
|
-
#
|
|
651
|
-
|
|
653
|
+
# Pushing only relevant notification for the client (ipython and console)
|
|
654
|
+
project.notifiers.push_pipeline_start_message_from_client(
|
|
655
|
+
project.metadata.name, pipeline_id=run_id
|
|
656
|
+
)
|
|
657
|
+
|
|
652
658
|
if context:
|
|
653
659
|
project.notifiers.push_pipeline_start_message(
|
|
654
660
|
project.metadata.name,
|
|
655
661
|
context.uid,
|
|
656
662
|
)
|
|
663
|
+
else:
|
|
664
|
+
project.push_pipeline_notification_kfp_runner(
|
|
665
|
+
run_id,
|
|
666
|
+
mlrun_pipelines.common.models.RunStatuses.running,
|
|
667
|
+
f"Workflow {run_id} started in project {project.metadata.name}",
|
|
668
|
+
notifications,
|
|
669
|
+
)
|
|
657
670
|
pipeline_context.clear()
|
|
658
671
|
return _PipelineRunStatus(run_id, cls, project=project, workflow=workflow_spec)
|
|
659
672
|
|
mlrun/projects/project.py
CHANGED
|
@@ -83,6 +83,7 @@ from ..artifacts import (
|
|
|
83
83
|
ModelArtifact,
|
|
84
84
|
)
|
|
85
85
|
from ..artifacts.manager import ArtifactManager, dict_to_artifact, extend_artifact_path
|
|
86
|
+
from ..common.runtimes.constants import RunStates
|
|
86
87
|
from ..datastore import store_manager
|
|
87
88
|
from ..features import Feature
|
|
88
89
|
from ..model import EntrypointParam, ImageBuilder, ModelObj
|
|
@@ -851,6 +852,7 @@ class ProjectSpec(ModelObj):
|
|
|
851
852
|
build=None,
|
|
852
853
|
custom_packagers: Optional[list[tuple[str, bool]]] = None,
|
|
853
854
|
default_function_node_selector=None,
|
|
855
|
+
notifications=None,
|
|
854
856
|
):
|
|
855
857
|
self.repo = None
|
|
856
858
|
|
|
@@ -891,6 +893,7 @@ class ProjectSpec(ModelObj):
|
|
|
891
893
|
# whether it is mandatory for a run (raise exception on collection error) or not.
|
|
892
894
|
self.custom_packagers = custom_packagers or []
|
|
893
895
|
self._default_function_node_selector = default_function_node_selector or None
|
|
896
|
+
self.notifications = notifications or []
|
|
894
897
|
|
|
895
898
|
@property
|
|
896
899
|
def source(self) -> str:
|
|
@@ -1172,7 +1175,6 @@ class MlrunProject(ModelObj):
|
|
|
1172
1175
|
self._artifact_manager = None
|
|
1173
1176
|
self._notifiers = CustomNotificationPusher(
|
|
1174
1177
|
[
|
|
1175
|
-
NotificationTypes.slack,
|
|
1176
1178
|
NotificationTypes.console,
|
|
1177
1179
|
NotificationTypes.ipython,
|
|
1178
1180
|
]
|
|
@@ -2670,6 +2672,36 @@ class MlrunProject(ModelObj):
|
|
|
2670
2672
|
timeout=timeout,
|
|
2671
2673
|
)
|
|
2672
2674
|
|
|
2675
|
+
def push_pipeline_notification_kfp_runner(
|
|
2676
|
+
self,
|
|
2677
|
+
pipeline_id: str,
|
|
2678
|
+
current_run_state: mlrun_pipelines.common.models.RunStatuses,
|
|
2679
|
+
message: str,
|
|
2680
|
+
notifications: Optional[list] = None,
|
|
2681
|
+
):
|
|
2682
|
+
"""
|
|
2683
|
+
Push notifications for a pipeline run(KFP).
|
|
2684
|
+
|
|
2685
|
+
:param pipeline_id: Unique ID of the pipeline run.
|
|
2686
|
+
:param current_run_state: Current run state of the pipeline.
|
|
2687
|
+
:param message: Message to send in the notification.
|
|
2688
|
+
:param notifications: List of notifications to send.
|
|
2689
|
+
"""
|
|
2690
|
+
current_run_state = RunStates.pipeline_run_status_to_run_state(
|
|
2691
|
+
current_run_state
|
|
2692
|
+
)
|
|
2693
|
+
db = mlrun.get_run_db()
|
|
2694
|
+
notifications = notifications or self.spec.notifications
|
|
2695
|
+
notifications_to_send = []
|
|
2696
|
+
for notification in notifications:
|
|
2697
|
+
if current_run_state in notification.when:
|
|
2698
|
+
notification_copy = notification.copy()
|
|
2699
|
+
notification_copy.message = message
|
|
2700
|
+
notifications_to_send.append(notification_copy)
|
|
2701
|
+
db.push_pipeline_notifications(
|
|
2702
|
+
pipeline_id, self.metadata.name, notifications_to_send
|
|
2703
|
+
)
|
|
2704
|
+
|
|
2673
2705
|
def _instantiate_function(
|
|
2674
2706
|
self,
|
|
2675
2707
|
func: typing.Union[str, mlrun.runtimes.BaseRuntime] = None,
|
mlrun/runtimes/nuclio/serving.py
CHANGED
|
@@ -309,7 +309,7 @@ class ServingRuntime(RemoteRuntime):
|
|
|
309
309
|
self,
|
|
310
310
|
stream_path: Optional[str] = None,
|
|
311
311
|
batch: Optional[int] = None,
|
|
312
|
-
|
|
312
|
+
sampling_percentage: float = 100,
|
|
313
313
|
stream_args: Optional[dict] = None,
|
|
314
314
|
tracking_policy: Optional[Union["TrackingPolicy", dict]] = None,
|
|
315
315
|
enable_tracking: bool = True,
|
|
@@ -317,13 +317,13 @@ class ServingRuntime(RemoteRuntime):
|
|
|
317
317
|
"""Apply on your serving function to monitor a deployed model, including real-time dashboards to detect drift
|
|
318
318
|
and analyze performance.
|
|
319
319
|
|
|
320
|
-
:param stream_path:
|
|
321
|
-
|
|
322
|
-
:param batch:
|
|
323
|
-
:param
|
|
324
|
-
|
|
325
|
-
:param
|
|
326
|
-
|
|
320
|
+
:param stream_path: Path/url of the tracking stream e.g. v3io:///users/mike/mystream
|
|
321
|
+
you can use the "dummy://" path for test/simulation.
|
|
322
|
+
:param batch: Deprecated. Micro batch size (send micro batches of N records at a time).
|
|
323
|
+
:param sampling_percentage: Down sampling events that will be pushed to the monitoring stream based on
|
|
324
|
+
a specified percentage. e.g. 50 for 50%. By default, all events are pushed.
|
|
325
|
+
:param stream_args: Stream initialization parameters, e.g. shards, retention_in_hours, ..
|
|
326
|
+
:param enable_tracking: Enabled/Disable model-monitoring tracking. Default True (tracking enabled).
|
|
327
327
|
|
|
328
328
|
Example::
|
|
329
329
|
|
|
@@ -336,12 +336,21 @@ class ServingRuntime(RemoteRuntime):
|
|
|
336
336
|
# Applying model monitoring configurations
|
|
337
337
|
self.spec.track_models = enable_tracking
|
|
338
338
|
|
|
339
|
+
if not 0 < sampling_percentage <= 100:
|
|
340
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
341
|
+
"`sampling_percentage` must be greater than 0 and less or equal to 100."
|
|
342
|
+
)
|
|
343
|
+
self.spec.parameters["sampling_percentage"] = sampling_percentage
|
|
344
|
+
|
|
339
345
|
if stream_path:
|
|
340
346
|
self.spec.parameters["log_stream"] = stream_path
|
|
341
347
|
if batch:
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
348
|
+
warnings.warn(
|
|
349
|
+
"The `batch` size parameter was deprecated in version 1.8.0 and is no longer used. "
|
|
350
|
+
"It will be removed in 1.10.",
|
|
351
|
+
# TODO: Remove this in 1.10
|
|
352
|
+
FutureWarning,
|
|
353
|
+
)
|
|
345
354
|
if stream_args:
|
|
346
355
|
self.spec.parameters["stream_args"] = stream_args
|
|
347
356
|
if tracking_policy is not None:
|
mlrun/serving/v2_serving.py
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
|
+
import random
|
|
15
16
|
import threading
|
|
16
17
|
import time
|
|
17
18
|
import traceback
|
|
@@ -283,7 +284,6 @@ class V2ModelServer(StepToDict):
|
|
|
283
284
|
}
|
|
284
285
|
if self.version:
|
|
285
286
|
response["model_version"] = self.version
|
|
286
|
-
|
|
287
287
|
elif op == "ready" and event.method == "GET":
|
|
288
288
|
# get model health operation
|
|
289
289
|
setattr(event, "terminated", True)
|
|
@@ -468,13 +468,9 @@ class _ModelLogPusher:
|
|
|
468
468
|
self.hostname = context.stream.hostname
|
|
469
469
|
self.function_uri = context.stream.function_uri
|
|
470
470
|
self.stream_path = context.stream.stream_uri
|
|
471
|
-
self.
|
|
472
|
-
self.stream_sample = int(context.get_param("log_stream_sample", 1))
|
|
471
|
+
self.sampling_percentage = float(context.get_param("sampling_percentage", 100))
|
|
473
472
|
self.output_stream = output_stream or context.stream.output_stream
|
|
474
473
|
self._worker = context.worker_id
|
|
475
|
-
self._sample_iter = 0
|
|
476
|
-
self._batch_iter = 0
|
|
477
|
-
self._batch = []
|
|
478
474
|
|
|
479
475
|
def base_data(self):
|
|
480
476
|
base_data = {
|
|
@@ -485,6 +481,7 @@ class _ModelLogPusher:
|
|
|
485
481
|
"host": self.hostname,
|
|
486
482
|
"function_uri": self.function_uri,
|
|
487
483
|
"endpoint_id": self.model.model_endpoint_uid,
|
|
484
|
+
"sampling_percentage": self.sampling_percentage,
|
|
488
485
|
}
|
|
489
486
|
if getattr(self.model, "labels", None):
|
|
490
487
|
base_data["labels"] = self.model.labels
|
|
@@ -504,37 +501,55 @@ class _ModelLogPusher:
|
|
|
504
501
|
self.output_stream.push([data], partition_key=partition_key)
|
|
505
502
|
return
|
|
506
503
|
|
|
507
|
-
|
|
508
|
-
|
|
504
|
+
if self.output_stream:
|
|
505
|
+
# Ensure that the inputs are a list of lists
|
|
506
|
+
request["inputs"] = (
|
|
507
|
+
request["inputs"]
|
|
508
|
+
if not any(not isinstance(req, list) for req in request["inputs"])
|
|
509
|
+
else [request["inputs"]]
|
|
510
|
+
)
|
|
509
511
|
microsec = (now_date() - start).microseconds
|
|
510
512
|
|
|
511
|
-
if self.
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
self.
|
|
515
|
-
|
|
513
|
+
if self.sampling_percentage != 100:
|
|
514
|
+
# Randomly select a subset of the requests based on the percentage
|
|
515
|
+
num_of_inputs = len(request["inputs"])
|
|
516
|
+
sampled_requests_indices = self._pick_random_requests(
|
|
517
|
+
num_of_inputs, self.sampling_percentage
|
|
516
518
|
)
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
"
|
|
519
|
+
if not sampled_requests_indices:
|
|
520
|
+
# No events were selected for sampling
|
|
521
|
+
return
|
|
522
|
+
|
|
523
|
+
request["inputs"] = [
|
|
524
|
+
request["inputs"][i] for i in sampled_requests_indices
|
|
525
|
+
]
|
|
526
|
+
|
|
527
|
+
if resp and "outputs" in resp and isinstance(resp["outputs"], list):
|
|
528
|
+
resp["outputs"] = [
|
|
529
|
+
resp["outputs"][i] for i in sampled_requests_indices
|
|
528
530
|
]
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
data["
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
531
|
+
|
|
532
|
+
data = self.base_data()
|
|
533
|
+
data["request"] = request
|
|
534
|
+
data["op"] = op
|
|
535
|
+
data["resp"] = resp
|
|
536
|
+
data["when"] = start_str
|
|
537
|
+
data["microsec"] = microsec
|
|
538
|
+
if getattr(self.model, "metrics", None):
|
|
539
|
+
data["metrics"] = self.model.metrics
|
|
540
|
+
data["effective_sample_count"] = len(request["inputs"])
|
|
541
|
+
self.output_stream.push([data], partition_key=partition_key)
|
|
542
|
+
|
|
543
|
+
@staticmethod
|
|
544
|
+
def _pick_random_requests(num_of_reqs: int, percentage: float) -> list[int]:
|
|
545
|
+
"""
|
|
546
|
+
Randomly selects indices of requests to sample based on the given percentage
|
|
547
|
+
|
|
548
|
+
:param num_of_reqs: Number of requests to select from
|
|
549
|
+
:param percentage: Sample percentage for each request
|
|
550
|
+
:return: A list containing the indices of the selected requests
|
|
551
|
+
"""
|
|
552
|
+
|
|
553
|
+
return [
|
|
554
|
+
req for req in range(num_of_reqs) if random.random() < (percentage / 100)
|
|
555
|
+
]
|
mlrun/utils/helpers.py
CHANGED
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
|
|
15
15
|
import asyncio
|
|
16
|
+
import base64
|
|
16
17
|
import enum
|
|
17
18
|
import functools
|
|
19
|
+
import gzip
|
|
18
20
|
import hashlib
|
|
19
21
|
import inspect
|
|
20
22
|
import itertools
|
|
@@ -1709,7 +1711,14 @@ def get_serving_spec():
|
|
|
1709
1711
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
1710
1712
|
"Failed to find serving spec in env var or config file"
|
|
1711
1713
|
)
|
|
1712
|
-
|
|
1714
|
+
# Attempt to decode and decompress, or use as-is for backward compatibility
|
|
1715
|
+
try:
|
|
1716
|
+
decoded_data = base64.b64decode(data)
|
|
1717
|
+
decompressed_data = gzip.decompress(decoded_data)
|
|
1718
|
+
spec = json.loads(decompressed_data.decode("utf-8"))
|
|
1719
|
+
except (OSError, gzip.BadGzipFile, base64.binascii.Error, json.JSONDecodeError):
|
|
1720
|
+
spec = json.loads(data)
|
|
1721
|
+
|
|
1713
1722
|
return spec
|
|
1714
1723
|
|
|
1715
1724
|
|
|
@@ -1981,7 +1990,20 @@ class Workflow:
|
|
|
1981
1990
|
if not workflow_id:
|
|
1982
1991
|
return steps
|
|
1983
1992
|
|
|
1984
|
-
|
|
1993
|
+
try:
|
|
1994
|
+
workflow_manifest = Workflow._get_workflow_manifest(workflow_id)
|
|
1995
|
+
except Exception:
|
|
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
|
+
|
|
1985
2007
|
if not workflow_manifest:
|
|
1986
2008
|
return steps
|
|
1987
2009
|
|
|
@@ -2038,3 +2060,9 @@ class Workflow:
|
|
|
2038
2060
|
|
|
2039
2061
|
kfp_run = mlrun_pipelines.models.PipelineRun(kfp_run)
|
|
2040
2062
|
return kfp_run.workflow_manifest()
|
|
2063
|
+
|
|
2064
|
+
|
|
2065
|
+
def as_dict(data: typing.Union[dict, str]) -> dict:
|
|
2066
|
+
if isinstance(data, str):
|
|
2067
|
+
return json.loads(data)
|
|
2068
|
+
return data
|
|
@@ -139,15 +139,25 @@ class NotificationPusher(_NotificationPusherBase):
|
|
|
139
139
|
error=mlrun.errors.err_to_str(exc),
|
|
140
140
|
)
|
|
141
141
|
|
|
142
|
-
def _process_notification(self,
|
|
143
|
-
|
|
142
|
+
def _process_notification(self, notification_object, run):
|
|
143
|
+
notification_object.status = run.status.notifications.get(
|
|
144
|
+
notification_object.name, {}
|
|
145
|
+
).get(
|
|
144
146
|
"status",
|
|
145
147
|
mlrun.common.schemas.NotificationStatus.PENDING,
|
|
146
148
|
)
|
|
147
|
-
if self._should_notify(run,
|
|
148
|
-
self._load_notification(
|
|
149
|
+
if self._should_notify(run, notification_object):
|
|
150
|
+
notification = self._load_notification(notification_object)
|
|
151
|
+
if notification.is_async:
|
|
152
|
+
self._async_notifications.append(
|
|
153
|
+
(notification, run, notification_object)
|
|
154
|
+
)
|
|
155
|
+
else:
|
|
156
|
+
self._sync_notifications.append(
|
|
157
|
+
(notification, run, notification_object)
|
|
158
|
+
)
|
|
149
159
|
|
|
150
|
-
def push(self):
|
|
160
|
+
def push(self, sync_push_callback=None, async_push_callback=None):
|
|
151
161
|
"""
|
|
152
162
|
Asynchronously push notifications for all runs in the initialized runs list (if they should be pushed).
|
|
153
163
|
When running from a sync environment, the notifications will be pushed asynchronously however the function will
|
|
@@ -201,8 +211,9 @@ class NotificationPusher(_NotificationPusherBase):
|
|
|
201
211
|
notifications_amount=len(self._sync_notifications)
|
|
202
212
|
+ len(self._async_notifications),
|
|
203
213
|
)
|
|
204
|
-
|
|
205
|
-
|
|
214
|
+
sync_push_callback = sync_push_callback or sync_push
|
|
215
|
+
async_push_callback = async_push_callback or async_push
|
|
216
|
+
self._push(sync_push_callback, async_push_callback)
|
|
206
217
|
|
|
207
218
|
@staticmethod
|
|
208
219
|
def _should_notify(
|
|
@@ -241,24 +252,19 @@ class NotificationPusher(_NotificationPusherBase):
|
|
|
241
252
|
return False
|
|
242
253
|
|
|
243
254
|
def _load_notification(
|
|
244
|
-
self,
|
|
255
|
+
self, notification_object: mlrun.model.Notification
|
|
245
256
|
) -> base.NotificationBase:
|
|
246
257
|
name = notification_object.name
|
|
247
258
|
notification_type = notification_module.NotificationTypes(
|
|
248
259
|
notification_object.kind or notification_module.NotificationTypes.console
|
|
249
260
|
)
|
|
250
261
|
params = {}
|
|
251
|
-
params.update(notification_object.secret_params)
|
|
252
|
-
params.update(notification_object.params)
|
|
262
|
+
params.update(notification_object.secret_params or {})
|
|
263
|
+
params.update(notification_object.params or {})
|
|
253
264
|
default_params = self._default_params.get(notification_type.value, {})
|
|
254
265
|
notification = notification_type.get_notification()(
|
|
255
266
|
name, params, default_params
|
|
256
267
|
)
|
|
257
|
-
if notification.is_async:
|
|
258
|
-
self._async_notifications.append((notification, run, notification_object))
|
|
259
|
-
else:
|
|
260
|
-
self._sync_notifications.append((notification, run, notification_object))
|
|
261
|
-
|
|
262
268
|
logger.debug(
|
|
263
269
|
"Loaded notification", notification=name, type=notification_type.value
|
|
264
270
|
)
|
|
@@ -496,6 +502,14 @@ class CustomNotificationPusher(_NotificationPusherBase):
|
|
|
496
502
|
notification_type: str,
|
|
497
503
|
params: typing.Optional[dict[str, str]] = None,
|
|
498
504
|
):
|
|
505
|
+
if notification_type not in [
|
|
506
|
+
notification_module.NotificationTypes.console,
|
|
507
|
+
notification_module.NotificationTypes.ipython,
|
|
508
|
+
]:
|
|
509
|
+
# We want that only the console and ipython notifications will be notified by the client.
|
|
510
|
+
# The rest of the notifications will be notified by the BE.
|
|
511
|
+
return
|
|
512
|
+
|
|
499
513
|
if notification_type in self._async_notifications:
|
|
500
514
|
self._async_notifications[notification_type].load_notification(params)
|
|
501
515
|
elif notification_type in self._sync_notifications:
|
|
@@ -565,25 +579,9 @@ class CustomNotificationPusher(_NotificationPusherBase):
|
|
|
565
579
|
pipeline_id: typing.Optional[str] = None,
|
|
566
580
|
has_workflow_url: bool = False,
|
|
567
581
|
):
|
|
568
|
-
message =
|
|
569
|
-
|
|
570
|
-
message += f" id={pipeline_id}"
|
|
571
|
-
commit_id = (
|
|
572
|
-
commit_id or os.environ.get("GITHUB_SHA") or os.environ.get("CI_COMMIT_SHA")
|
|
582
|
+
html, message = self.generate_start_message(
|
|
583
|
+
commit_id, has_workflow_url, pipeline_id, project
|
|
573
584
|
)
|
|
574
|
-
if commit_id:
|
|
575
|
-
message += f", commit={commit_id}"
|
|
576
|
-
if has_workflow_url:
|
|
577
|
-
url = mlrun.utils.helpers.get_workflow_url(project, pipeline_id)
|
|
578
|
-
else:
|
|
579
|
-
url = mlrun.utils.helpers.get_ui_url(project)
|
|
580
|
-
html = ""
|
|
581
|
-
if url:
|
|
582
|
-
html = (
|
|
583
|
-
message
|
|
584
|
-
+ f'<div><a href="{url}" target="_blank">click here to view progress</a></div>'
|
|
585
|
-
)
|
|
586
|
-
message = message + f", check progress in {url}"
|
|
587
585
|
self.push(message, "info", custom_html=html)
|
|
588
586
|
|
|
589
587
|
def push_pipeline_run_results(
|
|
@@ -616,6 +614,30 @@ class CustomNotificationPusher(_NotificationPusherBase):
|
|
|
616
614
|
text += f", state={state}"
|
|
617
615
|
self.push(text, "info", runs=runs_list)
|
|
618
616
|
|
|
617
|
+
def generate_start_message(
|
|
618
|
+
self, commit_id=None, has_workflow_url=None, pipeline_id=None, project=None
|
|
619
|
+
):
|
|
620
|
+
message = f"Workflow started in project {project}"
|
|
621
|
+
if pipeline_id:
|
|
622
|
+
message += f" id={pipeline_id}"
|
|
623
|
+
commit_id = (
|
|
624
|
+
commit_id or os.environ.get("GITHUB_SHA") or os.environ.get("CI_COMMIT_SHA")
|
|
625
|
+
)
|
|
626
|
+
if commit_id:
|
|
627
|
+
message += f", commit={commit_id}"
|
|
628
|
+
if has_workflow_url:
|
|
629
|
+
url = mlrun.utils.helpers.get_workflow_url(project, pipeline_id)
|
|
630
|
+
else:
|
|
631
|
+
url = mlrun.utils.helpers.get_ui_url(project)
|
|
632
|
+
html = ""
|
|
633
|
+
if url:
|
|
634
|
+
html = (
|
|
635
|
+
message
|
|
636
|
+
+ f'<div><a href="{url}" target="_blank">click here to view progress</a></div>'
|
|
637
|
+
)
|
|
638
|
+
message = message + f", check progress in {url}"
|
|
639
|
+
return html, message
|
|
640
|
+
|
|
619
641
|
|
|
620
642
|
def sanitize_notification(notification_dict: dict):
|
|
621
643
|
notification_dict.pop("secret_params", None)
|
mlrun/utils/version/version.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
mlrun/__init__.py,sha256=7vuMpUiigXXDrghLRq680LKWy1faC0kQyGCZb_7cwyE,7473
|
|
2
|
-
mlrun/__main__.py,sha256=
|
|
2
|
+
mlrun/__main__.py,sha256=3CJdwbSQGpbEhnAnVN_-CkQmLOPUUXTKhMf7xIWNQrc,46138
|
|
3
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
|
|
@@ -38,7 +38,7 @@ mlrun/common/formatters/project.py,sha256=0G4lhcTAsxQCxd40dKC4894cMH8nKt03BcGyp9
|
|
|
38
38
|
mlrun/common/formatters/run.py,sha256=Gcf9lVDqxPMNfWcPX0RJasjTC_N_U0yTBkQ02jOPJ7A,1062
|
|
39
39
|
mlrun/common/model_monitoring/__init__.py,sha256=kXGBqhLN0rlAx0kTXhozGzFsIdSqW0uTSKMmsLgq_is,569
|
|
40
40
|
mlrun/common/model_monitoring/helpers.py,sha256=lV86teJYoE3MNDx4yhpbzO1KylWmvDbuNODw5yGZwgs,2943
|
|
41
|
-
mlrun/common/runtimes/constants.py,sha256=
|
|
41
|
+
mlrun/common/runtimes/constants.py,sha256=07wD1g8QjXZe1fm2hSMOxZG19aAUsEZM8WeXnyoBd6Q,12127
|
|
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
|
|
@@ -72,9 +72,9 @@ mlrun/common/schemas/secret.py,sha256=CCxFYiPwJtDxwg2VVJH9nUG9cAZ2a34IjeuaWv-BYl
|
|
|
72
72
|
mlrun/common/schemas/tag.py,sha256=HRZi5QZ4vVGaCr2AMk9eJgcNiAIXmH4YDc8a4fvF770,893
|
|
73
73
|
mlrun/common/schemas/workflow.py,sha256=rwYzDJYxpE9k4kC88j_eUCmqK4ZsWV_h-_nli7Fs7Ow,2078
|
|
74
74
|
mlrun/common/schemas/model_monitoring/__init__.py,sha256=jz0fvdn8BEecgUCKhiSNH6QtFhSW4O19Ql9KXo0AxOg,1900
|
|
75
|
-
mlrun/common/schemas/model_monitoring/constants.py,sha256=
|
|
75
|
+
mlrun/common/schemas/model_monitoring/constants.py,sha256=kok1NFXJDicmcJ30MhOqR-vxd185sotXYU0CGLdrQxA,12082
|
|
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=0gBH-KnDDbGLOkiqHtk0_iNIdW-NPVy0TKJnZ1fDcEQ,11807
|
|
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,10 +107,10 @@ 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=Ke3atYsWfwhTlo3OoLKAz9gOkmu_a18De1lNKxV29Cc,30379
|
|
111
111
|
mlrun/db/factory.py,sha256=yP2vVmveUE7LYTCHbS6lQIxP9rW--zdISWuPd_I3d_4,2111
|
|
112
|
-
mlrun/db/httpdb.py,sha256=
|
|
113
|
-
mlrun/db/nopdb.py,sha256=
|
|
112
|
+
mlrun/db/httpdb.py,sha256=n1bGlooT2OQnPmzzFyKAPJO-2pCWf5B0EHctHa1h9rQ,230865
|
|
113
|
+
mlrun/db/nopdb.py,sha256=rte_AcP2itEwi7wpA26SH-AOAzm5ntPPoqXFHHtuewE,26834
|
|
114
114
|
mlrun/feature_store/__init__.py,sha256=AVnY2AFUNc2dKxLLUMx2K3Wo1eGviv0brDcYlDnmtf4,1506
|
|
115
115
|
mlrun/feature_store/api.py,sha256=qkojZpzqGAn3r9ww0ynBRKOs8ji8URaK4DSYD4SE-CE,50395
|
|
116
116
|
mlrun/feature_store/common.py,sha256=Z7USI-d1fo0iwBMsqMBtJflJfyuiV3BLoDXQPSAoBAs,12826
|
|
@@ -130,7 +130,7 @@ mlrun/frameworks/parallel_coordinates.py,sha256=UuZ0b0ACsaaH0rDya_0YMOWwaH6zhEyD
|
|
|
130
130
|
mlrun/frameworks/_common/__init__.py,sha256=1ovfHxNW8V9ERVVZx8lPFVGBtsXHaHli7pZPR-Ixn8g,860
|
|
131
131
|
mlrun/frameworks/_common/artifacts_library.py,sha256=O0z74o3Z6k5NruTqXMLDugZ6midOmSFqNlt7WhYIRP8,8517
|
|
132
132
|
mlrun/frameworks/_common/mlrun_interface.py,sha256=gjNV_siKyJ7xZdwoH8uvRmPuuQrc8WjnhoXr3GUCkAc,21093
|
|
133
|
-
mlrun/frameworks/_common/model_handler.py,sha256=
|
|
133
|
+
mlrun/frameworks/_common/model_handler.py,sha256=xoelZloxZ1BcLX7krwWrs-TmqIn52CRoPvGsB7Hoar4,55544
|
|
134
134
|
mlrun/frameworks/_common/plan.py,sha256=Yr98b5lkCV0K0u_krnU8gZJiXj14xfrFjJ6xD6QJdn0,3444
|
|
135
135
|
mlrun/frameworks/_common/producer.py,sha256=R67XRbiz1bk0XNvuW7ybbA4v6o6Q5qzD0h3AkC-tM1A,5766
|
|
136
136
|
mlrun/frameworks/_common/utils.py,sha256=NqoKbgj6UGPMBNhpK6mkKK4GOt5ko1lDqExFhQm9oEc,9131
|
|
@@ -220,7 +220,7 @@ mlrun/model_monitoring/api.py,sha256=nH5aEUkmUEJF0CurrWJxmxVv1tQed2yzCLhQByG1L00
|
|
|
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
222
|
mlrun/model_monitoring/helpers.py,sha256=6L-IO4EUAYoAf74snGNiGDln_p77OIdxVdrf392VBzY,17936
|
|
223
|
-
mlrun/model_monitoring/stream_processing.py,sha256=
|
|
223
|
+
mlrun/model_monitoring/stream_processing.py,sha256=wAPKdySwh8kGc7k6Zqu952X5E7uAom2J_YJmPXQUAlA,32859
|
|
224
224
|
mlrun/model_monitoring/tracking_policy.py,sha256=PBIGrUYWrwcE5gwXupBIVzOb0QRRwPJsgQm_yLGQxB4,5595
|
|
225
225
|
mlrun/model_monitoring/writer.py,sha256=vbL7bqTyNu8q4bNcebX72sUMybVDAoTWg-CXq4fov3Y,8429
|
|
226
226
|
mlrun/model_monitoring/applications/__init__.py,sha256=QYvzgCutFdAkzqKPD3mvkX_3c1X4tzd-kW8ojUOE9ic,889
|
|
@@ -237,12 +237,12 @@ mlrun/model_monitoring/db/tsdb/__init__.py,sha256=_ejhGA-bi5-6kEMEVYUN5TLxND8hTj
|
|
|
237
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
|
-
mlrun/model_monitoring/db/tsdb/tdengine/schemas.py,sha256=
|
|
240
|
+
mlrun/model_monitoring/db/tsdb/tdengine/schemas.py,sha256=qfKDUZhgteL0mp2A1aP1iMmcthgUMKmZqMUidZjQktQ,12649
|
|
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=Be-DdprjCdRQz-Goxy5sRxBdHp94V7d868N_IBEQxrY,30205
|
|
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=O9j0Hnmz5LbaJO2RxKRSDJU54MOCjICap2G9tuut8JE,37044
|
|
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
|
|
@@ -266,8 +266,8 @@ mlrun/platforms/__init__.py,sha256=ZuyeHCHHUxYEoZRmaJqzFSfwhaTyUdBZXMeVp75ql1w,3
|
|
|
266
266
|
mlrun/platforms/iguazio.py,sha256=6VBTq8eQ3mzT96tzjYhAtcMQ2VjF4x8LpIPW5DAcX2Q,13749
|
|
267
267
|
mlrun/projects/__init__.py,sha256=0Krf0WIKfnZa71WthYOg0SoaTodGg3sV_hK3f_OlTPI,1220
|
|
268
268
|
mlrun/projects/operations.py,sha256=VXUlMrouFTls-I-bMhdN5pPfQ34TR7bFQ-NUSWNvl84,20029
|
|
269
|
-
mlrun/projects/pipelines.py,sha256=
|
|
270
|
-
mlrun/projects/project.py,sha256=
|
|
269
|
+
mlrun/projects/pipelines.py,sha256=UwRwK2e8T0463quQ3d-ud_swmTLkBuSVUroY4YkgzZU,47903
|
|
270
|
+
mlrun/projects/project.py,sha256=C1OcldXuyCUIucvPFQX-tJbwjBkht8Ocn4m7lTHNZeM,229691
|
|
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
|
|
@@ -291,7 +291,7 @@ mlrun/runtimes/nuclio/__init__.py,sha256=gx1kizzKv8pGT5TNloN1js1hdbxqDw3rM90sLVY
|
|
|
291
291
|
mlrun/runtimes/nuclio/api_gateway.py,sha256=vH9ClKVP4Mb24rvA67xPuAvAhX-gAv6vVtjVxyplhdc,26969
|
|
292
292
|
mlrun/runtimes/nuclio/function.py,sha256=Bff8Veg-eaqNrQ7yn20HpRhwAO4OA7FTnzXnAyoaBPU,52365
|
|
293
293
|
mlrun/runtimes/nuclio/nuclio.py,sha256=sLK8KdGO1LbftlL3HqPZlFOFTAAuxJACZCVl1c0Ha6E,2942
|
|
294
|
-
mlrun/runtimes/nuclio/serving.py,sha256=
|
|
294
|
+
mlrun/runtimes/nuclio/serving.py,sha256=NG9OOTxbOGXhyVPI8BjIcZX5CDXvyk931o5DYOSfCg0,31977
|
|
295
295
|
mlrun/runtimes/nuclio/application/__init__.py,sha256=rRs5vasy_G9IyoTpYIjYDafGoL6ifFBKgBtsXn31Atw,614
|
|
296
296
|
mlrun/runtimes/nuclio/application/application.py,sha256=HlEq4A6hbFqr3Ba3TL4m7nbmfMYI06Zb_NAKGjzkEFU,29242
|
|
297
297
|
mlrun/runtimes/nuclio/application/reverse_proxy.go,sha256=JIIYae6bXzCLf3jXuu49KWPQYoXr_FDQ2Rbo1OWKAd0,3150
|
|
@@ -306,7 +306,7 @@ mlrun/serving/serving_wrapper.py,sha256=R670-S6PX_d5ER6jiHtRvacuPyFzQH0mEf2K0sBI
|
|
|
306
306
|
mlrun/serving/states.py,sha256=g6UIeaS6B9v8k4eDMmOxyoB8Gdqm9PiNIkeuzDyTJA8,67565
|
|
307
307
|
mlrun/serving/utils.py,sha256=k2EIYDWHUGkE-IBI6T0UNT32fw-KySsccIJM_LObI00,4171
|
|
308
308
|
mlrun/serving/v1_serving.py,sha256=c6J_MtpE-Tqu00-6r4eJOCO6rUasHDal9W2eBIcrl50,11853
|
|
309
|
-
mlrun/serving/v2_serving.py,sha256=
|
|
309
|
+
mlrun/serving/v2_serving.py,sha256=Nm-mPa40JlhpAFH5JuaS4Kc38g_o70cBPGgrqo7zDRM,22525
|
|
310
310
|
mlrun/track/__init__.py,sha256=yVXbT52fXvGKRlc_ByHqIVt7-9L3DRE634RSeQwgXtU,665
|
|
311
311
|
mlrun/track/tracker.py,sha256=CyTU6Qd3_5GGEJ_hpocOj71wvV65EuFYUjaYEUKAL6Q,3575
|
|
312
312
|
mlrun/track/tracker_manager.py,sha256=IYBl99I62IC6VCCmG1yt6JoHNOQXa53C4DURJ2sWgio,5726
|
|
@@ -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=TYDhMDWGtkrMJLslanSAb1sg7kR92q3etOWpTcqCoJk,70499
|
|
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=9KXwuMGgA340TfYlMbudlJ-MEwOyBJbbJGu_iBrIhrM,25106
|
|
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
|
|
@@ -337,11 +337,11 @@ mlrun/utils/notifications/notification/mail.py,sha256=ZyJ3eqd8simxffQmXzqd3bgbAq
|
|
|
337
337
|
mlrun/utils/notifications/notification/slack.py,sha256=NKV4RFiY3gLsS8uPppgniPLyag8zJ9O1VhixoXkM7kw,7108
|
|
338
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=Uil-vG-gWmMqNXcEWVV1qzfhqKErwFyS3X-ikYkxG4A,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.0rc21.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
343
|
+
mlrun-1.8.0rc21.dist-info/METADATA,sha256=OjUYCfgyCX1v5wIDojPMNCTe-gQqOxKDodJL7n-Cj_M,24885
|
|
344
|
+
mlrun-1.8.0rc21.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
345
|
+
mlrun-1.8.0rc21.dist-info/entry_points.txt,sha256=1Owd16eAclD5pfRCoJpYC2ZJSyGNTtUr0nCELMioMmU,46
|
|
346
|
+
mlrun-1.8.0rc21.dist-info/top_level.txt,sha256=NObLzw3maSF9wVrgSeYBv-fgnHkAJ1kEkh12DLdd5KM,6
|
|
347
|
+
mlrun-1.8.0rc21.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|