mlrun 1.7.0rc7__py3-none-any.whl → 1.7.0rc11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mlrun might be problematic. Click here for more details.
- mlrun/__init__.py +1 -0
- mlrun/__main__.py +2 -0
- mlrun/artifacts/model.py +29 -25
- mlrun/common/schemas/__init__.py +4 -0
- mlrun/common/schemas/alert.py +122 -0
- mlrun/common/schemas/api_gateway.py +8 -1
- mlrun/common/schemas/auth.py +4 -0
- mlrun/common/schemas/client_spec.py +1 -0
- mlrun/common/schemas/hub.py +7 -9
- mlrun/common/schemas/model_monitoring/constants.py +4 -2
- mlrun/{datastore/helpers.py → common/schemas/pagination.py} +11 -3
- mlrun/common/schemas/project.py +15 -10
- mlrun/config.py +35 -13
- mlrun/datastore/__init__.py +3 -7
- mlrun/datastore/base.py +6 -5
- mlrun/datastore/datastore_profile.py +19 -1
- mlrun/datastore/snowflake_utils.py +43 -0
- mlrun/datastore/sources.py +18 -30
- mlrun/datastore/targets.py +140 -12
- mlrun/datastore/utils.py +10 -5
- mlrun/datastore/v3io.py +27 -50
- mlrun/db/base.py +88 -2
- mlrun/db/httpdb.py +314 -41
- mlrun/db/nopdb.py +142 -0
- mlrun/execution.py +21 -14
- mlrun/feature_store/api.py +9 -5
- mlrun/feature_store/feature_set.py +39 -23
- mlrun/feature_store/feature_vector.py +2 -1
- mlrun/feature_store/retrieval/spark_merger.py +27 -23
- mlrun/feature_store/steps.py +30 -19
- mlrun/features.py +4 -13
- mlrun/frameworks/auto_mlrun/auto_mlrun.py +2 -2
- mlrun/frameworks/lgbm/__init__.py +1 -1
- mlrun/frameworks/lgbm/callbacks/callback.py +2 -4
- mlrun/frameworks/lgbm/model_handler.py +1 -1
- mlrun/frameworks/pytorch/__init__.py +2 -2
- mlrun/frameworks/sklearn/__init__.py +1 -1
- mlrun/frameworks/tf_keras/__init__.py +1 -1
- mlrun/frameworks/tf_keras/callbacks/logging_callback.py +1 -1
- mlrun/frameworks/tf_keras/mlrun_interface.py +2 -2
- mlrun/frameworks/xgboost/__init__.py +1 -1
- mlrun/kfpops.py +2 -5
- mlrun/launcher/base.py +1 -1
- mlrun/launcher/client.py +2 -2
- mlrun/model.py +2 -2
- mlrun/model_monitoring/application.py +11 -2
- mlrun/model_monitoring/applications/histogram_data_drift.py +3 -3
- mlrun/model_monitoring/controller.py +2 -3
- mlrun/model_monitoring/helpers.py +3 -1
- mlrun/model_monitoring/stream_processing.py +0 -1
- mlrun/model_monitoring/writer.py +32 -0
- mlrun/package/packagers_manager.py +1 -0
- mlrun/platforms/__init__.py +1 -1
- mlrun/platforms/other.py +1 -1
- mlrun/projects/operations.py +11 -4
- mlrun/projects/pipelines.py +1 -1
- mlrun/projects/project.py +180 -73
- mlrun/run.py +77 -41
- mlrun/runtimes/__init__.py +16 -0
- mlrun/runtimes/base.py +4 -1
- mlrun/runtimes/kubejob.py +26 -121
- mlrun/runtimes/mpijob/abstract.py +8 -8
- mlrun/runtimes/nuclio/api_gateway.py +58 -8
- mlrun/runtimes/nuclio/application/application.py +79 -1
- mlrun/runtimes/nuclio/application/reverse_proxy.go +9 -1
- mlrun/runtimes/nuclio/function.py +20 -13
- mlrun/runtimes/nuclio/serving.py +11 -10
- mlrun/runtimes/pod.py +148 -3
- mlrun/runtimes/utils.py +0 -28
- mlrun/secrets.py +6 -2
- mlrun/serving/remote.py +2 -3
- mlrun/serving/routers.py +7 -4
- mlrun/serving/server.py +1 -1
- mlrun/serving/states.py +14 -38
- mlrun/serving/v2_serving.py +8 -7
- mlrun/utils/helpers.py +1 -1
- mlrun/utils/http.py +1 -1
- mlrun/utils/notifications/notification/base.py +12 -0
- mlrun/utils/notifications/notification/console.py +2 -0
- mlrun/utils/notifications/notification/git.py +3 -1
- mlrun/utils/notifications/notification/ipython.py +2 -0
- mlrun/utils/notifications/notification/slack.py +41 -13
- mlrun/utils/notifications/notification/webhook.py +11 -1
- mlrun/utils/retryer.py +3 -2
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.0rc7.dist-info → mlrun-1.7.0rc11.dist-info}/METADATA +15 -15
- {mlrun-1.7.0rc7.dist-info → mlrun-1.7.0rc11.dist-info}/RECORD +91 -89
- {mlrun-1.7.0rc7.dist-info → mlrun-1.7.0rc11.dist-info}/LICENSE +0 -0
- {mlrun-1.7.0rc7.dist-info → mlrun-1.7.0rc11.dist-info}/WHEEL +0 -0
- {mlrun-1.7.0rc7.dist-info → mlrun-1.7.0rc11.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.0rc7.dist-info → mlrun-1.7.0rc11.dist-info}/top_level.txt +0 -0
mlrun/__init__.py
CHANGED
mlrun/__main__.py
CHANGED
mlrun/artifacts/model.py
CHANGED
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
+
|
|
14
15
|
import tempfile
|
|
15
16
|
from os import path
|
|
16
|
-
from typing import Any
|
|
17
|
+
from typing import Any, Optional
|
|
17
18
|
|
|
18
19
|
import pandas as pd
|
|
19
20
|
import yaml
|
|
@@ -69,8 +70,8 @@ class ModelArtifactSpec(ArtifactSpec):
|
|
|
69
70
|
model_file=None,
|
|
70
71
|
metrics=None,
|
|
71
72
|
paraemeters=None,
|
|
72
|
-
inputs: list[Feature] = None,
|
|
73
|
-
outputs: list[Feature] = None,
|
|
73
|
+
inputs: Optional[list[Feature]] = None,
|
|
74
|
+
outputs: Optional[list[Feature]] = None,
|
|
74
75
|
framework=None,
|
|
75
76
|
algorithm=None,
|
|
76
77
|
feature_vector=None,
|
|
@@ -92,8 +93,8 @@ class ModelArtifactSpec(ArtifactSpec):
|
|
|
92
93
|
self.model_file = model_file
|
|
93
94
|
self.metrics = metrics or {}
|
|
94
95
|
self.parameters = paraemeters or {}
|
|
95
|
-
self.inputs
|
|
96
|
-
self.outputs
|
|
96
|
+
self.inputs = inputs or []
|
|
97
|
+
self.outputs = outputs or []
|
|
97
98
|
self.framework = framework
|
|
98
99
|
self.algorithm = algorithm
|
|
99
100
|
self.feature_vector = feature_vector
|
|
@@ -102,21 +103,21 @@ class ModelArtifactSpec(ArtifactSpec):
|
|
|
102
103
|
self.model_target_file = model_target_file
|
|
103
104
|
|
|
104
105
|
@property
|
|
105
|
-
def inputs(self) ->
|
|
106
|
+
def inputs(self) -> ObjectList:
|
|
106
107
|
"""input feature list"""
|
|
107
108
|
return self._inputs
|
|
108
109
|
|
|
109
110
|
@inputs.setter
|
|
110
|
-
def inputs(self, inputs: list[Feature]):
|
|
111
|
+
def inputs(self, inputs: list[Feature]) -> None:
|
|
111
112
|
self._inputs = ObjectList.from_list(Feature, inputs)
|
|
112
113
|
|
|
113
114
|
@property
|
|
114
|
-
def outputs(self) ->
|
|
115
|
+
def outputs(self) -> ObjectList:
|
|
115
116
|
"""output feature list"""
|
|
116
117
|
return self._outputs
|
|
117
118
|
|
|
118
119
|
@outputs.setter
|
|
119
|
-
def outputs(self, outputs: list[Feature]):
|
|
120
|
+
def outputs(self, outputs: list[Feature]) -> None:
|
|
120
121
|
self._outputs = ObjectList.from_list(Feature, outputs)
|
|
121
122
|
|
|
122
123
|
|
|
@@ -176,22 +177,22 @@ class ModelArtifact(Artifact):
|
|
|
176
177
|
self._spec = self._verify_dict(spec, "spec", ModelArtifactSpec)
|
|
177
178
|
|
|
178
179
|
@property
|
|
179
|
-
def inputs(self) ->
|
|
180
|
+
def inputs(self) -> ObjectList:
|
|
180
181
|
"""input feature list"""
|
|
181
182
|
return self.spec.inputs
|
|
182
183
|
|
|
183
184
|
@inputs.setter
|
|
184
|
-
def inputs(self, inputs: list[Feature]):
|
|
185
|
+
def inputs(self, inputs: list[Feature]) -> None:
|
|
185
186
|
"""input feature list"""
|
|
186
187
|
self.spec.inputs = inputs
|
|
187
188
|
|
|
188
189
|
@property
|
|
189
|
-
def outputs(self) ->
|
|
190
|
+
def outputs(self) -> ObjectList:
|
|
190
191
|
"""input feature list"""
|
|
191
192
|
return self.spec.outputs
|
|
192
193
|
|
|
193
194
|
@outputs.setter
|
|
194
|
-
def outputs(self, outputs: list[Feature]):
|
|
195
|
+
def outputs(self, outputs: list[Feature]) -> None:
|
|
195
196
|
"""input feature list"""
|
|
196
197
|
self.spec.outputs = outputs
|
|
197
198
|
|
|
@@ -445,14 +446,14 @@ class LegacyModelArtifact(LegacyArtifact):
|
|
|
445
446
|
**kwargs,
|
|
446
447
|
):
|
|
447
448
|
super().__init__(key, body, format=format, target_path=target_path, **kwargs)
|
|
448
|
-
self._inputs: ObjectList = None
|
|
449
|
-
self._outputs: ObjectList = None
|
|
449
|
+
self._inputs: Optional[ObjectList] = None
|
|
450
|
+
self._outputs: Optional[ObjectList] = None
|
|
450
451
|
|
|
451
452
|
self.model_file = model_file
|
|
452
453
|
self.parameters = parameters or {}
|
|
453
454
|
self.metrics = metrics or {}
|
|
454
|
-
self.inputs
|
|
455
|
-
self.outputs
|
|
455
|
+
self.inputs = inputs or []
|
|
456
|
+
self.outputs = outputs or []
|
|
456
457
|
self.extra_data = extra_data or {}
|
|
457
458
|
self.framework = framework
|
|
458
459
|
self.algorithm = algorithm
|
|
@@ -462,21 +463,21 @@ class LegacyModelArtifact(LegacyArtifact):
|
|
|
462
463
|
self.model_target_file = model_target_file
|
|
463
464
|
|
|
464
465
|
@property
|
|
465
|
-
def inputs(self) ->
|
|
466
|
+
def inputs(self) -> Optional[ObjectList]:
|
|
466
467
|
"""input feature list"""
|
|
467
468
|
return self._inputs
|
|
468
469
|
|
|
469
470
|
@inputs.setter
|
|
470
|
-
def inputs(self, inputs: list[Feature]):
|
|
471
|
+
def inputs(self, inputs: list[Feature]) -> None:
|
|
471
472
|
self._inputs = ObjectList.from_list(Feature, inputs)
|
|
472
473
|
|
|
473
474
|
@property
|
|
474
|
-
def outputs(self) ->
|
|
475
|
+
def outputs(self) -> Optional[ObjectList]:
|
|
475
476
|
"""output feature list"""
|
|
476
477
|
return self._outputs
|
|
477
478
|
|
|
478
479
|
@outputs.setter
|
|
479
|
-
def outputs(self, outputs: list[Feature]):
|
|
480
|
+
def outputs(self, outputs: list[Feature]) -> None:
|
|
480
481
|
self._outputs = ObjectList.from_list(Feature, outputs)
|
|
481
482
|
|
|
482
483
|
def infer_from_df(self, df, label_columns=None, with_stats=True, num_bins=None):
|
|
@@ -552,9 +553,9 @@ def get_model(model_dir, suffix=""):
|
|
|
552
553
|
|
|
553
554
|
example::
|
|
554
555
|
|
|
555
|
-
model_file, model_artifact, extra_data = get_model(models_path, suffix=
|
|
556
|
+
model_file, model_artifact, extra_data = get_model(models_path, suffix=".pkl")
|
|
556
557
|
model = load(open(model_file, "rb"))
|
|
557
|
-
categories = extra_data[
|
|
558
|
+
categories = extra_data["categories"].as_df()
|
|
558
559
|
|
|
559
560
|
:param model_dir: model dir or artifact path (store://..) or DataItem
|
|
560
561
|
:param suffix: model filename suffix (when using a dir)
|
|
@@ -663,8 +664,11 @@ def update_model(
|
|
|
663
664
|
|
|
664
665
|
example::
|
|
665
666
|
|
|
666
|
-
update_model(
|
|
667
|
-
|
|
667
|
+
update_model(
|
|
668
|
+
model_path,
|
|
669
|
+
metrics={"speed": 100},
|
|
670
|
+
extra_data={"my_data": b"some text", "file": "s3://mybucket/.."},
|
|
671
|
+
)
|
|
668
672
|
|
|
669
673
|
:param model_artifact: model artifact object or path (store://..) or DataItem
|
|
670
674
|
:param parameters: parameters dict
|
mlrun/common/schemas/__init__.py
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
#
|
|
15
15
|
# flake8: noqa - this is until we take care of the F401 violations with respect to __all__ & sphinx
|
|
16
16
|
|
|
17
|
+
from .alert import AlertActiveState, AlertConfig, Event
|
|
17
18
|
from .api_gateway import (
|
|
18
19
|
APIGateway,
|
|
19
20
|
APIGatewayAuthenticationMode,
|
|
@@ -21,6 +22,7 @@ from .api_gateway import (
|
|
|
21
22
|
APIGatewayMetadata,
|
|
22
23
|
APIGatewaysOutput,
|
|
23
24
|
APIGatewaySpec,
|
|
25
|
+
APIGatewayState,
|
|
24
26
|
APIGatewayStatus,
|
|
25
27
|
APIGatewayUpstream,
|
|
26
28
|
)
|
|
@@ -151,12 +153,14 @@ from .notification import (
|
|
|
151
153
|
SetNotificationRequest,
|
|
152
154
|
)
|
|
153
155
|
from .object import ObjectKind, ObjectMetadata, ObjectSpec, ObjectStatus
|
|
156
|
+
from .pagination import PaginationInfo
|
|
154
157
|
from .pipeline import PipelinesFormat, PipelinesOutput, PipelinesPagination
|
|
155
158
|
from .project import (
|
|
156
159
|
IguazioProject,
|
|
157
160
|
Project,
|
|
158
161
|
ProjectDesiredState,
|
|
159
162
|
ProjectMetadata,
|
|
163
|
+
ProjectOutput,
|
|
160
164
|
ProjectOwner,
|
|
161
165
|
ProjectsFormat,
|
|
162
166
|
ProjectsOutput,
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Copyright 2023 Iguazio
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
#
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
from typing import Annotated, Optional, Union
|
|
17
|
+
|
|
18
|
+
import pydantic
|
|
19
|
+
|
|
20
|
+
from mlrun.common.schemas.notification import Notification
|
|
21
|
+
from mlrun.common.types import StrEnum
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class EventEntityKind(StrEnum):
|
|
25
|
+
MODEL = "model"
|
|
26
|
+
JOB = "job"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class EventEntity(pydantic.BaseModel):
|
|
30
|
+
kind: EventEntityKind
|
|
31
|
+
project: str
|
|
32
|
+
id: str
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class EventKind(StrEnum):
|
|
36
|
+
DRIFT_DETECTED = "drift_detected"
|
|
37
|
+
DRIFT_SUSPECTED = "drift_suspected"
|
|
38
|
+
FAILED = "failed"
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
_event_kind_entity_map = {
|
|
42
|
+
EventKind.DRIFT_SUSPECTED: [EventEntityKind.MODEL],
|
|
43
|
+
EventKind.DRIFT_DETECTED: [EventEntityKind.MODEL],
|
|
44
|
+
EventKind.FAILED: [EventEntityKind.JOB],
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class Event(pydantic.BaseModel):
|
|
49
|
+
kind: EventKind
|
|
50
|
+
timestamp: Union[str, datetime] = None # occurrence time
|
|
51
|
+
entity: EventEntity
|
|
52
|
+
value: Optional[Union[float, str]] = None
|
|
53
|
+
|
|
54
|
+
def is_valid(self):
|
|
55
|
+
return self.entity.kind in _event_kind_entity_map[self.kind]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class AlertActiveState(StrEnum):
|
|
59
|
+
ACTIVE = "active"
|
|
60
|
+
INACTIVE = "inactive"
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class AlertSeverity(StrEnum):
|
|
64
|
+
LOW = "low"
|
|
65
|
+
MEDIUM = "medium"
|
|
66
|
+
HIGH = "high"
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
# what should trigger the alert. must be either event (at least 1), or prometheus query
|
|
70
|
+
class AlertTrigger(pydantic.BaseModel):
|
|
71
|
+
events: list[EventKind] = []
|
|
72
|
+
prometheus_alert: str = None
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class AlertCriteria(pydantic.BaseModel):
|
|
76
|
+
count: Annotated[
|
|
77
|
+
int,
|
|
78
|
+
pydantic.Field(
|
|
79
|
+
description="Number of events to wait until notification is sent"
|
|
80
|
+
),
|
|
81
|
+
] = 0
|
|
82
|
+
period: Annotated[
|
|
83
|
+
str,
|
|
84
|
+
pydantic.Field(
|
|
85
|
+
description="Time period during which event occurred. e.g. 1d, 3h, 5m, 15s"
|
|
86
|
+
),
|
|
87
|
+
] = None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class ResetPolicy(StrEnum):
|
|
91
|
+
MANUAL = "manual"
|
|
92
|
+
AUTO = "auto"
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class AlertConfig(pydantic.BaseModel):
|
|
96
|
+
project: str
|
|
97
|
+
id: int = None
|
|
98
|
+
name: str
|
|
99
|
+
description: Optional[str] = ""
|
|
100
|
+
summary: Annotated[
|
|
101
|
+
str,
|
|
102
|
+
pydantic.Field(
|
|
103
|
+
description=(
|
|
104
|
+
"String to be sent in the notifications generated."
|
|
105
|
+
"e.g. 'Model {{ $project }}/{{ $entity }} is drifting.'"
|
|
106
|
+
)
|
|
107
|
+
),
|
|
108
|
+
]
|
|
109
|
+
created: Union[str, datetime] = None
|
|
110
|
+
severity: AlertSeverity
|
|
111
|
+
entity: EventEntity
|
|
112
|
+
trigger: AlertTrigger
|
|
113
|
+
criteria: Optional[AlertCriteria]
|
|
114
|
+
reset_policy: ResetPolicy = ResetPolicy.MANUAL
|
|
115
|
+
notifications: pydantic.conlist(Notification, min_items=1)
|
|
116
|
+
state: AlertActiveState = AlertActiveState.INACTIVE
|
|
117
|
+
count: Optional[int] = 0
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class AlertsModes(StrEnum):
|
|
121
|
+
enabled = "enabled"
|
|
122
|
+
disabled = "disabled"
|
|
@@ -36,6 +36,13 @@ class APIGatewayAuthenticationMode(mlrun.common.types.StrEnum):
|
|
|
36
36
|
)
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
class APIGatewayState(mlrun.common.types.StrEnum):
|
|
40
|
+
none = ""
|
|
41
|
+
ready = "ready"
|
|
42
|
+
error = "error"
|
|
43
|
+
waiting_for_provisioning = "waitingForProvisioning"
|
|
44
|
+
|
|
45
|
+
|
|
39
46
|
class _APIGatewayBaseModel(pydantic.BaseModel):
|
|
40
47
|
class Config:
|
|
41
48
|
extra = pydantic.Extra.allow
|
|
@@ -72,7 +79,7 @@ class APIGatewaySpec(_APIGatewayBaseModel):
|
|
|
72
79
|
|
|
73
80
|
class APIGatewayStatus(_APIGatewayBaseModel):
|
|
74
81
|
name: Optional[str]
|
|
75
|
-
state: Optional[
|
|
82
|
+
state: Optional[APIGatewayState]
|
|
76
83
|
|
|
77
84
|
|
|
78
85
|
class APIGateway(_APIGatewayBaseModel):
|
mlrun/common/schemas/auth.py
CHANGED
|
@@ -58,6 +58,8 @@ class AuthorizationResourceTypes(mlrun.common.types.StrEnum):
|
|
|
58
58
|
pipeline = "pipeline"
|
|
59
59
|
hub_source = "hub-source"
|
|
60
60
|
workflow = "workflow"
|
|
61
|
+
alert = "alert"
|
|
62
|
+
event = "event"
|
|
61
63
|
datastore_profile = "datastore-profile"
|
|
62
64
|
api_gateway = "api-gateway"
|
|
63
65
|
|
|
@@ -83,6 +85,8 @@ class AuthorizationResourceTypes(mlrun.common.types.StrEnum):
|
|
|
83
85
|
AuthorizationResourceTypes.schedule: "/projects/{project_name}/schedules/{resource_name}",
|
|
84
86
|
AuthorizationResourceTypes.secret: "/projects/{project_name}/secrets/{resource_name}",
|
|
85
87
|
AuthorizationResourceTypes.run: "/projects/{project_name}/runs/{resource_name}",
|
|
88
|
+
AuthorizationResourceTypes.event: "/projects/{project_name}/events/{resource_name}",
|
|
89
|
+
AuthorizationResourceTypes.alert: "/projects/{project_name}/alerts/{resource_name}",
|
|
86
90
|
# runtime resource doesn't have an identifier, we don't need any auth granularity behind project level
|
|
87
91
|
AuthorizationResourceTypes.runtime_resource: "/projects/{project_name}/runtime-resources",
|
|
88
92
|
AuthorizationResourceTypes.model_endpoint: "/projects/{project_name}/model-endpoints/{resource_name}",
|
mlrun/common/schemas/hub.py
CHANGED
|
@@ -59,28 +59,26 @@ class HubSource(BaseModel):
|
|
|
59
59
|
return f"{self.spec.path}/{self.spec.object_type}/{self.spec.channel}/{relative_path}"
|
|
60
60
|
|
|
61
61
|
def get_catalog_uri(self):
|
|
62
|
-
return self.get_full_uri(mlrun.
|
|
62
|
+
return self.get_full_uri(mlrun.mlconf.hub.catalog_filename)
|
|
63
63
|
|
|
64
64
|
@classmethod
|
|
65
65
|
def generate_default_source(cls):
|
|
66
|
-
if not mlrun.
|
|
66
|
+
if not mlrun.mlconf.hub.default_source.create:
|
|
67
67
|
return None
|
|
68
68
|
|
|
69
69
|
now = datetime.now(timezone.utc)
|
|
70
70
|
hub_metadata = HubObjectMetadata(
|
|
71
|
-
name=mlrun.
|
|
72
|
-
description=mlrun.
|
|
71
|
+
name=mlrun.mlconf.hub.default_source.name,
|
|
72
|
+
description=mlrun.mlconf.hub.default_source.description,
|
|
73
73
|
created=now,
|
|
74
74
|
updated=now,
|
|
75
75
|
)
|
|
76
76
|
return cls(
|
|
77
77
|
metadata=hub_metadata,
|
|
78
78
|
spec=HubSourceSpec(
|
|
79
|
-
path=mlrun.
|
|
80
|
-
channel=mlrun.
|
|
81
|
-
object_type=HubSourceType(
|
|
82
|
-
mlrun.config.config.hub.default_source.object_type
|
|
83
|
-
),
|
|
79
|
+
path=mlrun.mlconf.hub.default_source.url,
|
|
80
|
+
channel=mlrun.mlconf.hub.default_source.channel,
|
|
81
|
+
object_type=HubSourceType(mlrun.mlconf.hub.default_source.object_type),
|
|
84
82
|
),
|
|
85
83
|
status=ObjectStatus(state="created"),
|
|
86
84
|
)
|
|
@@ -151,7 +151,7 @@ class ProjectSecretKeys:
|
|
|
151
151
|
ENDPOINT_STORE_CONNECTION = "MODEL_MONITORING_ENDPOINT_STORE_CONNECTION"
|
|
152
152
|
ACCESS_KEY = "MODEL_MONITORING_ACCESS_KEY"
|
|
153
153
|
PIPELINES_ACCESS_KEY = "MODEL_MONITORING_PIPELINES_ACCESS_KEY"
|
|
154
|
-
|
|
154
|
+
KAFKA_BROKERS = "KAFKA_BROKERS"
|
|
155
155
|
STREAM_PATH = "STREAM_PATH"
|
|
156
156
|
|
|
157
157
|
|
|
@@ -308,4 +308,6 @@ class ControllerPolicy:
|
|
|
308
308
|
BASE_PERIOD = "base_period"
|
|
309
309
|
|
|
310
310
|
|
|
311
|
-
|
|
311
|
+
class HistogramDataDriftApplicationConstants:
|
|
312
|
+
NAME = "histogram-data-drift"
|
|
313
|
+
GENERAL_RESULT_NAME = "general_drift"
|
|
@@ -11,8 +11,16 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
-
#
|
|
15
14
|
|
|
15
|
+
import typing
|
|
16
|
+
|
|
17
|
+
import pydantic
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class PaginationInfo(pydantic.BaseModel):
|
|
21
|
+
class Config:
|
|
22
|
+
allow_population_by_field_name = True
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
|
|
24
|
+
page: typing.Optional[int]
|
|
25
|
+
page_size: typing.Optional[int] = pydantic.Field(alias="page-size")
|
|
26
|
+
page_token: typing.Optional[str] = pydantic.Field(alias="page-token")
|
mlrun/common/schemas/project.py
CHANGED
|
@@ -120,17 +120,22 @@ class IguazioProject(pydantic.BaseModel):
|
|
|
120
120
|
data: dict
|
|
121
121
|
|
|
122
122
|
|
|
123
|
+
# The format query param controls the project type used:
|
|
124
|
+
# full - Project
|
|
125
|
+
# name_only - str
|
|
126
|
+
# summary - ProjectSummary
|
|
127
|
+
# leader - currently only IguazioProject supported
|
|
128
|
+
# The way pydantic handles typing.Union is that it takes the object and tries to coerce it to be the types of the
|
|
129
|
+
# union by the definition order. Therefore we can't currently add generic dict for all leader formats, but we need
|
|
130
|
+
# to add a specific classes for them. it's frustrating but couldn't find other workaround, see:
|
|
131
|
+
# https://github.com/samuelcolvin/pydantic/issues/1423, https://github.com/samuelcolvin/pydantic/issues/619
|
|
132
|
+
ProjectOutput = typing.TypeVar(
|
|
133
|
+
"ProjectOutput", Project, str, ProjectSummary, IguazioProject
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
123
137
|
class ProjectsOutput(pydantic.BaseModel):
|
|
124
|
-
|
|
125
|
-
# full - Project
|
|
126
|
-
# name_only - str
|
|
127
|
-
# summary - ProjectSummary
|
|
128
|
-
# leader - currently only IguazioProject supported
|
|
129
|
-
# The way pydantic handles typing.Union is that it takes the object and tries to coerce it to be the types of the
|
|
130
|
-
# union by the definition order. Therefore we can't currently add generic dict for all leader formats, but we need
|
|
131
|
-
# to add a specific classes for them. it's frustrating but couldn't find other workaround, see:
|
|
132
|
-
# https://github.com/samuelcolvin/pydantic/issues/1423, https://github.com/samuelcolvin/pydantic/issues/619
|
|
133
|
-
projects: list[typing.Union[Project, str, ProjectSummary, IguazioProject]]
|
|
138
|
+
projects: list[ProjectOutput]
|
|
134
139
|
|
|
135
140
|
|
|
136
141
|
class ProjectSummariesOutput(pydantic.BaseModel):
|
mlrun/config.py
CHANGED
|
@@ -240,6 +240,7 @@ default_config = {
|
|
|
240
240
|
"remote": "mlrun/mlrun",
|
|
241
241
|
"dask": "mlrun/ml-base",
|
|
242
242
|
"mpijob": "mlrun/mlrun",
|
|
243
|
+
"application": "python:3.9-slim",
|
|
243
244
|
},
|
|
244
245
|
# see enrich_function_preemption_spec for more info,
|
|
245
246
|
# and mlrun.common.schemas.function.PreemptionModes for available options
|
|
@@ -481,10 +482,13 @@ default_config = {
|
|
|
481
482
|
# if set to true, will log a warning for trying to use run db functionality while in nop db mode
|
|
482
483
|
"verbose": True,
|
|
483
484
|
},
|
|
484
|
-
"
|
|
485
|
-
"
|
|
486
|
-
"
|
|
487
|
-
|
|
485
|
+
"pagination": {
|
|
486
|
+
"default_page_size": 20,
|
|
487
|
+
"pagination_cache": {
|
|
488
|
+
"interval": 60,
|
|
489
|
+
"ttl": 3600,
|
|
490
|
+
"max_size": 10000,
|
|
491
|
+
},
|
|
488
492
|
},
|
|
489
493
|
},
|
|
490
494
|
"model_endpoint_monitoring": {
|
|
@@ -544,10 +548,11 @@ default_config = {
|
|
|
544
548
|
},
|
|
545
549
|
"feature_store": {
|
|
546
550
|
"data_prefixes": {
|
|
547
|
-
"default": "v3io:///projects/{project}/FeatureStore/{name}/
|
|
548
|
-
"nosql": "v3io:///projects/{project}/FeatureStore/{name}/
|
|
551
|
+
"default": "v3io:///projects/{project}/FeatureStore/{name}/nosql",
|
|
552
|
+
"nosql": "v3io:///projects/{project}/FeatureStore/{name}/nosql",
|
|
549
553
|
# "authority" is optional and generalizes [userinfo "@"] host [":" port]
|
|
550
|
-
"redisnosql": "redis://{authority}/projects/{project}/FeatureStore/{name}/
|
|
554
|
+
"redisnosql": "redis://{authority}/projects/{project}/FeatureStore/{name}/nosql",
|
|
555
|
+
"dsnosql": "ds://{ds_profile_name}/projects/{project}/FeatureStore/{name}/nosql",
|
|
551
556
|
},
|
|
552
557
|
"default_targets": "parquet,nosql",
|
|
553
558
|
"default_job_image": "mlrun/mlrun",
|
|
@@ -683,6 +688,10 @@ default_config = {
|
|
|
683
688
|
"access_key": "",
|
|
684
689
|
},
|
|
685
690
|
"grafana_url": "",
|
|
691
|
+
"alerts": {
|
|
692
|
+
# supported modes: "enabled", "disabled".
|
|
693
|
+
"mode": "disabled"
|
|
694
|
+
},
|
|
686
695
|
}
|
|
687
696
|
|
|
688
697
|
_is_running_as_api = None
|
|
@@ -1073,7 +1082,7 @@ class Config:
|
|
|
1073
1082
|
target: str = "online",
|
|
1074
1083
|
artifact_path: str = None,
|
|
1075
1084
|
function_name: str = None,
|
|
1076
|
-
) -> str:
|
|
1085
|
+
) -> typing.Union[str, list[str]]:
|
|
1077
1086
|
"""Get the full path from the configuration based on the provided project and kind.
|
|
1078
1087
|
|
|
1079
1088
|
:param project: Project name.
|
|
@@ -1089,7 +1098,8 @@ class Config:
|
|
|
1089
1098
|
relative artifact path will be taken from the global MLRun artifact path.
|
|
1090
1099
|
:param function_name: Application name, None for model_monitoring_stream.
|
|
1091
1100
|
|
|
1092
|
-
:return: Full configured path for the provided kind.
|
|
1101
|
+
:return: Full configured path for the provided kind. Can be either a single path
|
|
1102
|
+
or a list of paths in the case of the online model monitoring stream path.
|
|
1093
1103
|
"""
|
|
1094
1104
|
|
|
1095
1105
|
if target != "offline":
|
|
@@ -1111,10 +1121,22 @@ class Config:
|
|
|
1111
1121
|
if function_name is None
|
|
1112
1122
|
else f"{kind}-{function_name.lower()}",
|
|
1113
1123
|
)
|
|
1114
|
-
return mlrun.
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1124
|
+
elif kind == "stream": # return list for mlrun<1.6.3 BC
|
|
1125
|
+
return [
|
|
1126
|
+
mlrun.mlconf.model_endpoint_monitoring.store_prefixes.default.format(
|
|
1127
|
+
project=project,
|
|
1128
|
+
kind=kind,
|
|
1129
|
+
), # old stream uri (pipelines) for BC ML-6043
|
|
1130
|
+
mlrun.mlconf.model_endpoint_monitoring.store_prefixes.user_space.format(
|
|
1131
|
+
project=project,
|
|
1132
|
+
kind=kind,
|
|
1133
|
+
), # new stream uri (projects)
|
|
1134
|
+
]
|
|
1135
|
+
else:
|
|
1136
|
+
return mlrun.mlconf.model_endpoint_monitoring.store_prefixes.default.format(
|
|
1137
|
+
project=project,
|
|
1138
|
+
kind=kind,
|
|
1139
|
+
)
|
|
1118
1140
|
|
|
1119
1141
|
# Get the current offline path from the configuration
|
|
1120
1142
|
file_path = mlrun.mlconf.model_endpoint_monitoring.offline_storage_path.format(
|
mlrun/datastore/__init__.py
CHANGED
|
@@ -107,13 +107,9 @@ def get_stream_pusher(stream_path: str, **kwargs):
|
|
|
107
107
|
:param stream_path: path/url of stream
|
|
108
108
|
"""
|
|
109
109
|
|
|
110
|
-
if stream_path.startswith("kafka://") or "
|
|
111
|
-
topic,
|
|
112
|
-
|
|
113
|
-
)
|
|
114
|
-
return KafkaOutputStream(
|
|
115
|
-
topic, bootstrap_servers, kwargs.get("kafka_producer_options")
|
|
116
|
-
)
|
|
110
|
+
if stream_path.startswith("kafka://") or "kafka_brokers" in kwargs:
|
|
111
|
+
topic, brokers = parse_kafka_url(stream_path, kwargs.get("kafka_brokers"))
|
|
112
|
+
return KafkaOutputStream(topic, brokers, kwargs.get("kafka_producer_options"))
|
|
117
113
|
elif stream_path.startswith("http://") or stream_path.startswith("https://"):
|
|
118
114
|
return HTTPOutputStream(stream_path=stream_path)
|
|
119
115
|
elif "://" not in stream_path:
|
mlrun/datastore/base.py
CHANGED
|
@@ -389,14 +389,15 @@ class DataItem:
|
|
|
389
389
|
|
|
390
390
|
|
|
391
391
|
# reading run results using DataItem (run.artifact())
|
|
392
|
-
train_run = train_iris_func.run(
|
|
393
|
-
|
|
392
|
+
train_run = train_iris_func.run(
|
|
393
|
+
inputs={"dataset": dataset}, params={"label_column": "label"}
|
|
394
|
+
)
|
|
394
395
|
|
|
395
|
-
train_run.artifact(
|
|
396
|
-
test_set = train_run.artifact(
|
|
396
|
+
train_run.artifact("confusion-matrix").show()
|
|
397
|
+
test_set = train_run.artifact("test_set").as_df()
|
|
397
398
|
|
|
398
399
|
# create and use DataItem from uri
|
|
399
|
-
data = mlrun.get_dataitem(
|
|
400
|
+
data = mlrun.get_dataitem("http://xyz/data.json").get()
|
|
400
401
|
"""
|
|
401
402
|
|
|
402
403
|
def __init__(
|
|
@@ -16,6 +16,7 @@ import ast
|
|
|
16
16
|
import base64
|
|
17
17
|
import json
|
|
18
18
|
import typing
|
|
19
|
+
import warnings
|
|
19
20
|
from urllib.parse import ParseResult, urlparse, urlunparse
|
|
20
21
|
|
|
21
22
|
import pydantic
|
|
@@ -68,6 +69,9 @@ class TemporaryClientDatastoreProfiles(metaclass=mlrun.utils.singleton.Singleton
|
|
|
68
69
|
def get(self, key):
|
|
69
70
|
return self._data.get(key, None)
|
|
70
71
|
|
|
72
|
+
def remove(self, key):
|
|
73
|
+
self._data.pop(key, None)
|
|
74
|
+
|
|
71
75
|
|
|
72
76
|
class DatastoreProfileBasic(DatastoreProfile):
|
|
73
77
|
type: str = pydantic.Field("basic")
|
|
@@ -80,12 +84,22 @@ class DatastoreProfileKafkaTarget(DatastoreProfile):
|
|
|
80
84
|
type: str = pydantic.Field("kafka_target")
|
|
81
85
|
_private_attributes = "kwargs_private"
|
|
82
86
|
bootstrap_servers: str
|
|
87
|
+
brokers: str
|
|
83
88
|
topic: str
|
|
84
89
|
kwargs_public: typing.Optional[dict]
|
|
85
90
|
kwargs_private: typing.Optional[dict]
|
|
86
91
|
|
|
92
|
+
def __pydantic_post_init__(self):
|
|
93
|
+
if self.bootstrap_servers:
|
|
94
|
+
warnings.warn(
|
|
95
|
+
"'bootstrap_servers' parameter is deprecated in 1.7.0 and will be removed in 1.9.0, "
|
|
96
|
+
"use 'brokers' instead.",
|
|
97
|
+
# TODO: Remove this in 1.9.0
|
|
98
|
+
FutureWarning,
|
|
99
|
+
)
|
|
100
|
+
|
|
87
101
|
def attributes(self):
|
|
88
|
-
attributes = {"
|
|
102
|
+
attributes = {"brokers": self.brokers or self.bootstrap_servers}
|
|
89
103
|
if self.kwargs_public:
|
|
90
104
|
attributes = merge(attributes, self.kwargs_public)
|
|
91
105
|
if self.kwargs_private:
|
|
@@ -460,3 +474,7 @@ def register_temporary_client_datastore_profile(profile: DatastoreProfile):
|
|
|
460
474
|
It's beneficial for testing purposes.
|
|
461
475
|
"""
|
|
462
476
|
TemporaryClientDatastoreProfiles().add(profile)
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def remove_temporary_client_datastore_profile(profile_name: str):
|
|
480
|
+
TemporaryClientDatastoreProfiles().remove(profile_name)
|