mlrun 1.7.2rc4__py3-none-any.whl → 1.8.0__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 +26 -22
- mlrun/__main__.py +15 -16
- mlrun/alerts/alert.py +150 -15
- mlrun/api/schemas/__init__.py +1 -9
- mlrun/artifacts/__init__.py +2 -3
- mlrun/artifacts/base.py +62 -19
- mlrun/artifacts/dataset.py +17 -17
- mlrun/artifacts/document.py +454 -0
- mlrun/artifacts/manager.py +28 -18
- mlrun/artifacts/model.py +91 -59
- mlrun/artifacts/plots.py +2 -2
- mlrun/common/constants.py +8 -0
- mlrun/common/formatters/__init__.py +1 -0
- mlrun/common/formatters/artifact.py +1 -1
- mlrun/common/formatters/feature_set.py +2 -0
- mlrun/common/formatters/function.py +1 -0
- mlrun/{model_monitoring/db/stores/v3io_kv/__init__.py → common/formatters/model_endpoint.py} +17 -0
- mlrun/common/formatters/pipeline.py +1 -2
- mlrun/common/formatters/project.py +9 -0
- mlrun/common/model_monitoring/__init__.py +0 -5
- mlrun/common/model_monitoring/helpers.py +12 -62
- mlrun/common/runtimes/constants.py +25 -4
- mlrun/common/schemas/__init__.py +9 -5
- mlrun/common/schemas/alert.py +114 -19
- mlrun/common/schemas/api_gateway.py +3 -3
- mlrun/common/schemas/artifact.py +22 -9
- mlrun/common/schemas/auth.py +8 -4
- mlrun/common/schemas/background_task.py +7 -7
- mlrun/common/schemas/client_spec.py +4 -4
- mlrun/common/schemas/clusterization_spec.py +2 -2
- mlrun/common/schemas/common.py +53 -3
- mlrun/common/schemas/constants.py +15 -0
- mlrun/common/schemas/datastore_profile.py +1 -1
- mlrun/common/schemas/feature_store.py +9 -9
- mlrun/common/schemas/frontend_spec.py +4 -4
- mlrun/common/schemas/function.py +10 -10
- mlrun/common/schemas/hub.py +1 -1
- mlrun/common/schemas/k8s.py +3 -3
- mlrun/common/schemas/memory_reports.py +3 -3
- mlrun/common/schemas/model_monitoring/__init__.py +4 -8
- mlrun/common/schemas/model_monitoring/constants.py +127 -46
- mlrun/common/schemas/model_monitoring/grafana.py +18 -12
- mlrun/common/schemas/model_monitoring/model_endpoints.py +154 -160
- mlrun/common/schemas/notification.py +24 -3
- mlrun/common/schemas/object.py +1 -1
- mlrun/common/schemas/pagination.py +4 -4
- mlrun/common/schemas/partition.py +142 -0
- mlrun/common/schemas/pipeline.py +3 -3
- mlrun/common/schemas/project.py +26 -18
- mlrun/common/schemas/runs.py +3 -3
- mlrun/common/schemas/runtime_resource.py +5 -5
- mlrun/common/schemas/schedule.py +1 -1
- mlrun/common/schemas/secret.py +1 -1
- mlrun/{model_monitoring/db/stores/sqldb/__init__.py → common/schemas/serving.py} +10 -1
- mlrun/common/schemas/tag.py +3 -3
- mlrun/common/schemas/workflow.py +6 -5
- mlrun/common/types.py +1 -0
- mlrun/config.py +157 -89
- mlrun/data_types/__init__.py +5 -3
- mlrun/data_types/infer.py +13 -3
- mlrun/data_types/spark.py +2 -1
- mlrun/datastore/__init__.py +59 -18
- mlrun/datastore/alibaba_oss.py +4 -1
- mlrun/datastore/azure_blob.py +4 -1
- mlrun/datastore/base.py +19 -24
- mlrun/datastore/datastore.py +10 -4
- mlrun/datastore/datastore_profile.py +178 -45
- mlrun/datastore/dbfs_store.py +4 -1
- mlrun/datastore/filestore.py +4 -1
- mlrun/datastore/google_cloud_storage.py +4 -1
- mlrun/datastore/hdfs.py +4 -1
- mlrun/datastore/inmem.py +4 -1
- mlrun/datastore/redis.py +4 -1
- mlrun/datastore/s3.py +14 -3
- mlrun/datastore/sources.py +89 -92
- mlrun/datastore/store_resources.py +7 -4
- mlrun/datastore/storeytargets.py +51 -16
- mlrun/datastore/targets.py +38 -31
- mlrun/datastore/utils.py +87 -4
- mlrun/datastore/v3io.py +4 -1
- mlrun/datastore/vectorstore.py +291 -0
- mlrun/datastore/wasbfs/fs.py +13 -12
- mlrun/db/base.py +286 -100
- mlrun/db/httpdb.py +1562 -490
- mlrun/db/nopdb.py +250 -83
- mlrun/errors.py +6 -2
- mlrun/execution.py +194 -50
- mlrun/feature_store/__init__.py +2 -10
- mlrun/feature_store/api.py +20 -458
- mlrun/feature_store/common.py +9 -9
- mlrun/feature_store/feature_set.py +20 -18
- mlrun/feature_store/feature_vector.py +105 -479
- mlrun/feature_store/feature_vector_utils.py +466 -0
- mlrun/feature_store/retrieval/base.py +15 -11
- mlrun/feature_store/retrieval/job.py +2 -1
- mlrun/feature_store/retrieval/storey_merger.py +1 -1
- mlrun/feature_store/steps.py +3 -3
- mlrun/features.py +30 -13
- mlrun/frameworks/__init__.py +1 -2
- mlrun/frameworks/_common/__init__.py +1 -2
- mlrun/frameworks/_common/artifacts_library.py +2 -2
- mlrun/frameworks/_common/mlrun_interface.py +10 -6
- mlrun/frameworks/_common/model_handler.py +31 -31
- mlrun/frameworks/_common/producer.py +3 -1
- mlrun/frameworks/_dl_common/__init__.py +1 -2
- mlrun/frameworks/_dl_common/loggers/__init__.py +1 -2
- mlrun/frameworks/_dl_common/loggers/mlrun_logger.py +4 -4
- mlrun/frameworks/_dl_common/loggers/tensorboard_logger.py +3 -3
- mlrun/frameworks/_ml_common/__init__.py +1 -2
- mlrun/frameworks/_ml_common/loggers/__init__.py +1 -2
- mlrun/frameworks/_ml_common/model_handler.py +21 -21
- mlrun/frameworks/_ml_common/plans/__init__.py +1 -2
- mlrun/frameworks/_ml_common/plans/confusion_matrix_plan.py +3 -1
- mlrun/frameworks/_ml_common/plans/dataset_plan.py +3 -3
- mlrun/frameworks/_ml_common/plans/roc_curve_plan.py +4 -4
- mlrun/frameworks/auto_mlrun/__init__.py +1 -2
- mlrun/frameworks/auto_mlrun/auto_mlrun.py +22 -15
- mlrun/frameworks/huggingface/__init__.py +1 -2
- mlrun/frameworks/huggingface/model_server.py +9 -9
- mlrun/frameworks/lgbm/__init__.py +47 -44
- mlrun/frameworks/lgbm/callbacks/__init__.py +1 -2
- mlrun/frameworks/lgbm/callbacks/logging_callback.py +4 -2
- mlrun/frameworks/lgbm/callbacks/mlrun_logging_callback.py +4 -2
- mlrun/frameworks/lgbm/mlrun_interfaces/__init__.py +1 -2
- mlrun/frameworks/lgbm/mlrun_interfaces/mlrun_interface.py +5 -5
- mlrun/frameworks/lgbm/model_handler.py +15 -11
- mlrun/frameworks/lgbm/model_server.py +11 -7
- mlrun/frameworks/lgbm/utils.py +2 -2
- mlrun/frameworks/onnx/__init__.py +1 -2
- mlrun/frameworks/onnx/dataset.py +3 -3
- mlrun/frameworks/onnx/mlrun_interface.py +2 -2
- mlrun/frameworks/onnx/model_handler.py +7 -5
- mlrun/frameworks/onnx/model_server.py +8 -6
- mlrun/frameworks/parallel_coordinates.py +11 -11
- mlrun/frameworks/pytorch/__init__.py +22 -23
- mlrun/frameworks/pytorch/callbacks/__init__.py +1 -2
- mlrun/frameworks/pytorch/callbacks/callback.py +2 -1
- mlrun/frameworks/pytorch/callbacks/logging_callback.py +15 -8
- mlrun/frameworks/pytorch/callbacks/mlrun_logging_callback.py +19 -12
- mlrun/frameworks/pytorch/callbacks/tensorboard_logging_callback.py +22 -15
- mlrun/frameworks/pytorch/callbacks_handler.py +36 -30
- mlrun/frameworks/pytorch/mlrun_interface.py +17 -17
- mlrun/frameworks/pytorch/model_handler.py +21 -17
- mlrun/frameworks/pytorch/model_server.py +13 -9
- mlrun/frameworks/sklearn/__init__.py +19 -18
- mlrun/frameworks/sklearn/estimator.py +2 -2
- mlrun/frameworks/sklearn/metric.py +3 -3
- mlrun/frameworks/sklearn/metrics_library.py +8 -6
- mlrun/frameworks/sklearn/mlrun_interface.py +3 -2
- mlrun/frameworks/sklearn/model_handler.py +4 -3
- mlrun/frameworks/tf_keras/__init__.py +11 -12
- mlrun/frameworks/tf_keras/callbacks/__init__.py +1 -2
- mlrun/frameworks/tf_keras/callbacks/logging_callback.py +17 -14
- mlrun/frameworks/tf_keras/callbacks/mlrun_logging_callback.py +15 -12
- mlrun/frameworks/tf_keras/callbacks/tensorboard_logging_callback.py +21 -18
- mlrun/frameworks/tf_keras/model_handler.py +17 -13
- mlrun/frameworks/tf_keras/model_server.py +12 -8
- mlrun/frameworks/xgboost/__init__.py +19 -18
- mlrun/frameworks/xgboost/model_handler.py +13 -9
- mlrun/k8s_utils.py +2 -5
- mlrun/launcher/base.py +3 -4
- mlrun/launcher/client.py +2 -2
- mlrun/launcher/local.py +6 -2
- mlrun/launcher/remote.py +1 -1
- mlrun/lists.py +8 -4
- mlrun/model.py +132 -46
- mlrun/model_monitoring/__init__.py +3 -5
- mlrun/model_monitoring/api.py +113 -98
- mlrun/model_monitoring/applications/__init__.py +0 -5
- mlrun/model_monitoring/applications/_application_steps.py +81 -50
- mlrun/model_monitoring/applications/base.py +467 -14
- mlrun/model_monitoring/applications/context.py +212 -134
- mlrun/model_monitoring/{db/stores/base → applications/evidently}/__init__.py +6 -2
- mlrun/model_monitoring/applications/evidently/base.py +146 -0
- mlrun/model_monitoring/applications/histogram_data_drift.py +89 -56
- mlrun/model_monitoring/applications/results.py +67 -15
- mlrun/model_monitoring/controller.py +701 -315
- mlrun/model_monitoring/db/__init__.py +0 -2
- mlrun/model_monitoring/db/_schedules.py +242 -0
- mlrun/model_monitoring/db/_stats.py +189 -0
- mlrun/model_monitoring/db/tsdb/__init__.py +33 -22
- mlrun/model_monitoring/db/tsdb/base.py +243 -49
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +76 -36
- mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py +33 -0
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connection.py +213 -0
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +534 -88
- mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +1 -0
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +436 -106
- mlrun/model_monitoring/helpers.py +356 -114
- mlrun/model_monitoring/stream_processing.py +190 -345
- mlrun/model_monitoring/tracking_policy.py +11 -4
- mlrun/model_monitoring/writer.py +49 -90
- mlrun/package/__init__.py +3 -6
- mlrun/package/context_handler.py +2 -2
- mlrun/package/packager.py +12 -9
- mlrun/package/packagers/__init__.py +0 -2
- mlrun/package/packagers/default_packager.py +14 -11
- mlrun/package/packagers/numpy_packagers.py +16 -7
- mlrun/package/packagers/pandas_packagers.py +18 -18
- mlrun/package/packagers/python_standard_library_packagers.py +25 -11
- mlrun/package/packagers_manager.py +35 -32
- mlrun/package/utils/__init__.py +0 -3
- mlrun/package/utils/_pickler.py +6 -6
- mlrun/platforms/__init__.py +47 -16
- mlrun/platforms/iguazio.py +4 -1
- mlrun/projects/operations.py +30 -30
- mlrun/projects/pipelines.py +116 -47
- mlrun/projects/project.py +1292 -329
- mlrun/render.py +5 -9
- mlrun/run.py +57 -14
- mlrun/runtimes/__init__.py +1 -3
- mlrun/runtimes/base.py +30 -22
- mlrun/runtimes/daskjob.py +9 -9
- mlrun/runtimes/databricks_job/databricks_runtime.py +6 -5
- mlrun/runtimes/function_reference.py +5 -2
- mlrun/runtimes/generators.py +3 -2
- mlrun/runtimes/kubejob.py +6 -7
- mlrun/runtimes/mounts.py +574 -0
- mlrun/runtimes/mpijob/__init__.py +0 -2
- mlrun/runtimes/mpijob/abstract.py +7 -6
- mlrun/runtimes/nuclio/api_gateway.py +7 -7
- mlrun/runtimes/nuclio/application/application.py +11 -13
- mlrun/runtimes/nuclio/application/reverse_proxy.go +66 -64
- mlrun/runtimes/nuclio/function.py +127 -70
- mlrun/runtimes/nuclio/serving.py +105 -37
- mlrun/runtimes/pod.py +159 -54
- mlrun/runtimes/remotesparkjob.py +3 -2
- mlrun/runtimes/sparkjob/__init__.py +0 -2
- mlrun/runtimes/sparkjob/spark3job.py +22 -12
- mlrun/runtimes/utils.py +7 -6
- mlrun/secrets.py +2 -2
- mlrun/serving/__init__.py +8 -0
- mlrun/serving/merger.py +7 -5
- mlrun/serving/remote.py +35 -22
- mlrun/serving/routers.py +186 -240
- mlrun/serving/server.py +41 -10
- mlrun/serving/states.py +432 -118
- mlrun/serving/utils.py +13 -2
- mlrun/serving/v1_serving.py +3 -2
- mlrun/serving/v2_serving.py +161 -203
- mlrun/track/__init__.py +1 -1
- mlrun/track/tracker.py +2 -2
- mlrun/track/trackers/mlflow_tracker.py +6 -5
- mlrun/utils/async_http.py +35 -22
- mlrun/utils/clones.py +7 -4
- mlrun/utils/helpers.py +511 -58
- mlrun/utils/logger.py +119 -13
- mlrun/utils/notifications/notification/__init__.py +22 -19
- mlrun/utils/notifications/notification/base.py +39 -15
- mlrun/utils/notifications/notification/console.py +6 -6
- mlrun/utils/notifications/notification/git.py +11 -11
- mlrun/utils/notifications/notification/ipython.py +10 -9
- mlrun/utils/notifications/notification/mail.py +176 -0
- mlrun/utils/notifications/notification/slack.py +16 -8
- mlrun/utils/notifications/notification/webhook.py +24 -8
- mlrun/utils/notifications/notification_pusher.py +191 -200
- mlrun/utils/regex.py +12 -2
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.7.2rc4.dist-info → mlrun-1.8.0.dist-info}/METADATA +69 -54
- mlrun-1.8.0.dist-info/RECORD +351 -0
- {mlrun-1.7.2rc4.dist-info → mlrun-1.8.0.dist-info}/WHEEL +1 -1
- mlrun/model_monitoring/applications/evidently_base.py +0 -137
- mlrun/model_monitoring/db/stores/__init__.py +0 -136
- mlrun/model_monitoring/db/stores/base/store.py +0 -213
- mlrun/model_monitoring/db/stores/sqldb/models/__init__.py +0 -71
- mlrun/model_monitoring/db/stores/sqldb/models/base.py +0 -190
- mlrun/model_monitoring/db/stores/sqldb/models/mysql.py +0 -103
- mlrun/model_monitoring/db/stores/sqldb/models/sqlite.py +0 -40
- mlrun/model_monitoring/db/stores/sqldb/sql_store.py +0 -659
- mlrun/model_monitoring/db/stores/v3io_kv/kv_store.py +0 -726
- mlrun/model_monitoring/model_endpoint.py +0 -118
- mlrun-1.7.2rc4.dist-info/RECORD +0 -351
- {mlrun-1.7.2rc4.dist-info → mlrun-1.8.0.dist-info}/entry_points.txt +0 -0
- {mlrun-1.7.2rc4.dist-info → mlrun-1.8.0.dist-info/licenses}/LICENSE +0 -0
- {mlrun-1.7.2rc4.dist-info → mlrun-1.8.0.dist-info}/top_level.txt +0 -0
mlrun/serving/states.py
CHANGED
|
@@ -25,11 +25,20 @@ import pathlib
|
|
|
25
25
|
import traceback
|
|
26
26
|
from copy import copy, deepcopy
|
|
27
27
|
from inspect import getfullargspec, signature
|
|
28
|
-
from typing import Any, Union
|
|
28
|
+
from typing import Any, Optional, Union, cast
|
|
29
29
|
|
|
30
30
|
import storey.utils
|
|
31
31
|
|
|
32
32
|
import mlrun
|
|
33
|
+
import mlrun.common.schemas as schemas
|
|
34
|
+
from mlrun.datastore.datastore_profile import (
|
|
35
|
+
DatastoreProfileKafkaSource,
|
|
36
|
+
DatastoreProfileKafkaTarget,
|
|
37
|
+
DatastoreProfileV3io,
|
|
38
|
+
datastore_profile_read,
|
|
39
|
+
)
|
|
40
|
+
from mlrun.datastore.storeytargets import KafkaStoreyTarget, StreamStoreyTarget
|
|
41
|
+
from mlrun.utils import logger
|
|
33
42
|
|
|
34
43
|
from ..config import config
|
|
35
44
|
from ..datastore import get_stream_pusher
|
|
@@ -48,6 +57,8 @@ path_splitter = "/"
|
|
|
48
57
|
previous_step = "$prev"
|
|
49
58
|
queue_class_names = [">>", "$queue"]
|
|
50
59
|
|
|
60
|
+
MAX_MODELS_PER_ROUTER = 5000
|
|
61
|
+
|
|
51
62
|
|
|
52
63
|
class GraphError(Exception):
|
|
53
64
|
"""error in graph topology or configuration"""
|
|
@@ -81,30 +92,44 @@ _task_step_fields = [
|
|
|
81
92
|
"responder",
|
|
82
93
|
"input_path",
|
|
83
94
|
"result_path",
|
|
95
|
+
"model_endpoint_creation_strategy",
|
|
96
|
+
"endpoint_type",
|
|
84
97
|
]
|
|
85
98
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def new_model_endpoint(class_name, model_path, handler=None, **class_args):
|
|
91
|
-
class_args = deepcopy(class_args)
|
|
92
|
-
class_args["model_path"] = model_path
|
|
93
|
-
return TaskStep(class_name, class_args, handler=handler)
|
|
99
|
+
_default_fields_to_strip_from_step = [
|
|
100
|
+
"model_endpoint_creation_strategy",
|
|
101
|
+
"endpoint_type",
|
|
102
|
+
]
|
|
94
103
|
|
|
95
104
|
|
|
96
|
-
def new_remote_endpoint(
|
|
105
|
+
def new_remote_endpoint(
|
|
106
|
+
url: str,
|
|
107
|
+
creation_strategy: schemas.ModelEndpointCreationStrategy,
|
|
108
|
+
endpoint_type: schemas.EndpointType,
|
|
109
|
+
**class_args,
|
|
110
|
+
):
|
|
97
111
|
class_args = deepcopy(class_args)
|
|
98
112
|
class_args["url"] = url
|
|
99
|
-
return TaskStep(
|
|
113
|
+
return TaskStep(
|
|
114
|
+
"$remote",
|
|
115
|
+
class_args=class_args,
|
|
116
|
+
model_endpoint_creation_strategy=creation_strategy,
|
|
117
|
+
endpoint_type=endpoint_type,
|
|
118
|
+
)
|
|
100
119
|
|
|
101
120
|
|
|
102
121
|
class BaseStep(ModelObj):
|
|
103
122
|
kind = "BaseStep"
|
|
104
123
|
default_shape = "ellipse"
|
|
105
124
|
_dict_fields = ["kind", "comment", "after", "on_error"]
|
|
125
|
+
_default_fields_to_strip = _default_fields_to_strip_from_step
|
|
106
126
|
|
|
107
|
-
def __init__(
|
|
127
|
+
def __init__(
|
|
128
|
+
self,
|
|
129
|
+
name: Optional[str] = None,
|
|
130
|
+
after: Optional[list] = None,
|
|
131
|
+
shape: Optional[str] = None,
|
|
132
|
+
):
|
|
108
133
|
self.name = name
|
|
109
134
|
self._parent = None
|
|
110
135
|
self.comment = None
|
|
@@ -114,6 +139,9 @@ class BaseStep(ModelObj):
|
|
|
114
139
|
self.shape = shape
|
|
115
140
|
self.on_error = None
|
|
116
141
|
self._on_error_handler = None
|
|
142
|
+
self.model_endpoint_creation_strategy = (
|
|
143
|
+
schemas.ModelEndpointCreationStrategy.SKIP
|
|
144
|
+
)
|
|
117
145
|
|
|
118
146
|
def get_shape(self):
|
|
119
147
|
"""graphviz shape"""
|
|
@@ -154,14 +182,14 @@ class BaseStep(ModelObj):
|
|
|
154
182
|
|
|
155
183
|
def error_handler(
|
|
156
184
|
self,
|
|
157
|
-
name: str = None,
|
|
185
|
+
name: Optional[str] = None,
|
|
158
186
|
class_name=None,
|
|
159
187
|
handler=None,
|
|
160
188
|
before=None,
|
|
161
189
|
function=None,
|
|
162
|
-
full_event: bool = None,
|
|
163
|
-
input_path: str = None,
|
|
164
|
-
result_path: str = None,
|
|
190
|
+
full_event: Optional[bool] = None,
|
|
191
|
+
input_path: Optional[str] = None,
|
|
192
|
+
result_path: Optional[str] = None,
|
|
165
193
|
**class_args,
|
|
166
194
|
):
|
|
167
195
|
"""set error handler on a step or the entire graph (to be executed on failure/raise)
|
|
@@ -297,13 +325,16 @@ class BaseStep(ModelObj):
|
|
|
297
325
|
def to(
|
|
298
326
|
self,
|
|
299
327
|
class_name: Union[str, StepToDict] = None,
|
|
300
|
-
name: str = None,
|
|
301
|
-
handler: str = None,
|
|
302
|
-
graph_shape: str = None,
|
|
303
|
-
function: str = None,
|
|
304
|
-
full_event: bool = None,
|
|
305
|
-
input_path: str = None,
|
|
306
|
-
result_path: str = None,
|
|
328
|
+
name: Optional[str] = None,
|
|
329
|
+
handler: Optional[str] = None,
|
|
330
|
+
graph_shape: Optional[str] = None,
|
|
331
|
+
function: Optional[str] = None,
|
|
332
|
+
full_event: Optional[bool] = None,
|
|
333
|
+
input_path: Optional[str] = None,
|
|
334
|
+
result_path: Optional[str] = None,
|
|
335
|
+
model_endpoint_creation_strategy: Optional[
|
|
336
|
+
schemas.ModelEndpointCreationStrategy
|
|
337
|
+
] = None,
|
|
307
338
|
**class_args,
|
|
308
339
|
):
|
|
309
340
|
"""add a step right after this step and return the new step
|
|
@@ -331,6 +362,23 @@ class BaseStep(ModelObj):
|
|
|
331
362
|
this require that the event body will behave like a dict, example:
|
|
332
363
|
event: {"x": 5} , result_path="y" means the output of the step will be written
|
|
333
364
|
to event["y"] resulting in {"x": 5, "y": <result>}
|
|
365
|
+
:param model_endpoint_creation_strategy: Strategy for creating or updating the model endpoint:
|
|
366
|
+
|
|
367
|
+
* **overwrite**:
|
|
368
|
+
|
|
369
|
+
1. If model endpoints with the same name exist, delete the `latest` one.
|
|
370
|
+
2. Create a new model endpoint entry and set it as `latest`.
|
|
371
|
+
|
|
372
|
+
* **inplace** (default):
|
|
373
|
+
|
|
374
|
+
1. If model endpoints with the same name exist, update the `latest` entry.
|
|
375
|
+
2. Otherwise, create a new entry.
|
|
376
|
+
|
|
377
|
+
* **archive**:
|
|
378
|
+
|
|
379
|
+
1. If model endpoints with the same name exist, preserve them.
|
|
380
|
+
2. Create a new model endpoint with the same name and set it to `latest`.
|
|
381
|
+
|
|
334
382
|
:param class_args: class init arguments
|
|
335
383
|
"""
|
|
336
384
|
if hasattr(self, "steps"):
|
|
@@ -352,6 +400,7 @@ class BaseStep(ModelObj):
|
|
|
352
400
|
input_path=input_path,
|
|
353
401
|
result_path=result_path,
|
|
354
402
|
class_args=class_args,
|
|
403
|
+
model_endpoint_creation_strategy=model_endpoint_creation_strategy,
|
|
355
404
|
)
|
|
356
405
|
step = parent._steps.update(name, step)
|
|
357
406
|
step.set_parent(parent)
|
|
@@ -366,15 +415,18 @@ class BaseStep(ModelObj):
|
|
|
366
415
|
steps: list[Union[str, StepToDict, dict[str, Any]]],
|
|
367
416
|
force: bool = False,
|
|
368
417
|
):
|
|
369
|
-
"""
|
|
418
|
+
"""
|
|
419
|
+
Set list of steps as downstream from this step, in the order specified. This will overwrite any existing
|
|
370
420
|
downstream steps.
|
|
371
421
|
|
|
372
422
|
:param steps: list of steps to follow this one
|
|
373
423
|
:param force: whether to overwrite existing downstream steps. If False, this method will fail if any downstream
|
|
374
|
-
|
|
424
|
+
steps have already been defined. Defaults to False.
|
|
425
|
+
|
|
375
426
|
:return: the last step added to the flow
|
|
376
427
|
|
|
377
|
-
example
|
|
428
|
+
example::
|
|
429
|
+
|
|
378
430
|
The below code sets the downstream nodes of step1 by using a list of steps (provided to `set_flow()`) and a
|
|
379
431
|
single step (provided to `to()`), resulting in the graph (step1 -> step2 -> step3 -> step4).
|
|
380
432
|
Notice that using `force=True` is required in case step1 already had downstream nodes (e.g. if the existing
|
|
@@ -404,16 +456,20 @@ class TaskStep(BaseStep):
|
|
|
404
456
|
|
|
405
457
|
def __init__(
|
|
406
458
|
self,
|
|
407
|
-
class_name: Union[str, type] = None,
|
|
408
|
-
class_args: dict = None,
|
|
409
|
-
handler: str = None,
|
|
410
|
-
name: str = None,
|
|
411
|
-
after: list = None,
|
|
412
|
-
full_event: bool = None,
|
|
413
|
-
function: str = None,
|
|
414
|
-
responder: bool = None,
|
|
415
|
-
input_path: str = None,
|
|
416
|
-
result_path: str = None,
|
|
459
|
+
class_name: Optional[Union[str, type]] = None,
|
|
460
|
+
class_args: Optional[dict] = None,
|
|
461
|
+
handler: Optional[str] = None,
|
|
462
|
+
name: Optional[str] = None,
|
|
463
|
+
after: Optional[list] = None,
|
|
464
|
+
full_event: Optional[bool] = None,
|
|
465
|
+
function: Optional[str] = None,
|
|
466
|
+
responder: Optional[bool] = None,
|
|
467
|
+
input_path: Optional[str] = None,
|
|
468
|
+
result_path: Optional[str] = None,
|
|
469
|
+
model_endpoint_creation_strategy: Optional[
|
|
470
|
+
schemas.ModelEndpointCreationStrategy
|
|
471
|
+
] = schemas.ModelEndpointCreationStrategy.SKIP,
|
|
472
|
+
endpoint_type: Optional[schemas.EndpointType] = schemas.EndpointType.NODE_EP,
|
|
417
473
|
):
|
|
418
474
|
super().__init__(name, after)
|
|
419
475
|
self.class_name = class_name
|
|
@@ -433,6 +489,8 @@ class TaskStep(BaseStep):
|
|
|
433
489
|
self.on_error = None
|
|
434
490
|
self._inject_context = False
|
|
435
491
|
self._call_with_event = False
|
|
492
|
+
self.model_endpoint_creation_strategy = model_endpoint_creation_strategy
|
|
493
|
+
self.endpoint_type = endpoint_type
|
|
436
494
|
|
|
437
495
|
def init_object(self, context, namespace, mode="sync", reset=False, **extra_kwargs):
|
|
438
496
|
self.context = context
|
|
@@ -549,9 +607,11 @@ class TaskStep(BaseStep):
|
|
|
549
607
|
|
|
550
608
|
def _post_init(self, mode="sync"):
|
|
551
609
|
if self._object and hasattr(self._object, "post_init"):
|
|
552
|
-
self._object.post_init(
|
|
553
|
-
|
|
554
|
-
|
|
610
|
+
self._object.post_init(
|
|
611
|
+
mode,
|
|
612
|
+
creation_strategy=self.model_endpoint_creation_strategy,
|
|
613
|
+
endpoint_type=self.endpoint_type,
|
|
614
|
+
)
|
|
555
615
|
|
|
556
616
|
def respond(self):
|
|
557
617
|
"""mark this step as the responder.
|
|
@@ -598,6 +658,27 @@ class TaskStep(BaseStep):
|
|
|
598
658
|
raise exc
|
|
599
659
|
return event
|
|
600
660
|
|
|
661
|
+
def to_dict(
|
|
662
|
+
self,
|
|
663
|
+
fields: Optional[list] = None,
|
|
664
|
+
exclude: Optional[list] = None,
|
|
665
|
+
strip: bool = False,
|
|
666
|
+
) -> dict:
|
|
667
|
+
self.endpoint_type = (
|
|
668
|
+
self.endpoint_type.value
|
|
669
|
+
if isinstance(self.endpoint_type, schemas.EndpointType)
|
|
670
|
+
else self.endpoint_type
|
|
671
|
+
)
|
|
672
|
+
self.model_endpoint_creation_strategy = (
|
|
673
|
+
self.model_endpoint_creation_strategy.value
|
|
674
|
+
if isinstance(
|
|
675
|
+
self.model_endpoint_creation_strategy,
|
|
676
|
+
schemas.ModelEndpointCreationStrategy,
|
|
677
|
+
)
|
|
678
|
+
else self.model_endpoint_creation_strategy
|
|
679
|
+
)
|
|
680
|
+
return super().to_dict(fields, exclude, strip)
|
|
681
|
+
|
|
601
682
|
|
|
602
683
|
class MonitoringApplicationStep(TaskStep):
|
|
603
684
|
"""monitoring application execution step, runs users class code"""
|
|
@@ -607,16 +688,16 @@ class MonitoringApplicationStep(TaskStep):
|
|
|
607
688
|
|
|
608
689
|
def __init__(
|
|
609
690
|
self,
|
|
610
|
-
class_name: Union[str, type] = None,
|
|
611
|
-
class_args: dict = None,
|
|
612
|
-
handler: str = None,
|
|
613
|
-
name: str = None,
|
|
614
|
-
after: list = None,
|
|
615
|
-
full_event: bool = None,
|
|
616
|
-
function: str = None,
|
|
617
|
-
responder: bool = None,
|
|
618
|
-
input_path: str = None,
|
|
619
|
-
result_path: str = None,
|
|
691
|
+
class_name: Optional[Union[str, type]] = None,
|
|
692
|
+
class_args: Optional[dict] = None,
|
|
693
|
+
handler: Optional[str] = None,
|
|
694
|
+
name: Optional[str] = None,
|
|
695
|
+
after: Optional[list] = None,
|
|
696
|
+
full_event: Optional[bool] = None,
|
|
697
|
+
function: Optional[str] = None,
|
|
698
|
+
responder: Optional[bool] = None,
|
|
699
|
+
input_path: Optional[str] = None,
|
|
700
|
+
result_path: Optional[str] = None,
|
|
620
701
|
):
|
|
621
702
|
super().__init__(
|
|
622
703
|
class_name=class_name,
|
|
@@ -641,16 +722,16 @@ class ErrorStep(TaskStep):
|
|
|
641
722
|
|
|
642
723
|
def __init__(
|
|
643
724
|
self,
|
|
644
|
-
class_name: Union[str, type] = None,
|
|
645
|
-
class_args: dict = None,
|
|
646
|
-
handler: str = None,
|
|
647
|
-
name: str = None,
|
|
648
|
-
after: list = None,
|
|
649
|
-
full_event: bool = None,
|
|
650
|
-
function: str = None,
|
|
651
|
-
responder: bool = None,
|
|
652
|
-
input_path: str = None,
|
|
653
|
-
result_path: str = None,
|
|
725
|
+
class_name: Optional[Union[str, type]] = None,
|
|
726
|
+
class_args: Optional[dict] = None,
|
|
727
|
+
handler: Optional[str] = None,
|
|
728
|
+
name: Optional[str] = None,
|
|
729
|
+
after: Optional[list] = None,
|
|
730
|
+
full_event: Optional[bool] = None,
|
|
731
|
+
function: Optional[str] = None,
|
|
732
|
+
responder: Optional[bool] = None,
|
|
733
|
+
input_path: Optional[str] = None,
|
|
734
|
+
result_path: Optional[str] = None,
|
|
654
735
|
):
|
|
655
736
|
super().__init__(
|
|
656
737
|
class_name=class_name,
|
|
@@ -673,31 +754,39 @@ class RouterStep(TaskStep):
|
|
|
673
754
|
|
|
674
755
|
kind = "router"
|
|
675
756
|
default_shape = "doubleoctagon"
|
|
676
|
-
_dict_fields = _task_step_fields + ["routes"]
|
|
757
|
+
_dict_fields = _task_step_fields + ["routes", "name"]
|
|
677
758
|
_default_class = "mlrun.serving.ModelRouter"
|
|
678
759
|
|
|
679
760
|
def __init__(
|
|
680
761
|
self,
|
|
681
|
-
class_name: Union[str, type] = None,
|
|
682
|
-
class_args: dict = None,
|
|
683
|
-
handler: str = None,
|
|
684
|
-
routes: list = None,
|
|
685
|
-
name: str = None,
|
|
686
|
-
function: str = None,
|
|
687
|
-
input_path: str = None,
|
|
688
|
-
result_path: str = None,
|
|
762
|
+
class_name: Optional[Union[str, type]] = None,
|
|
763
|
+
class_args: Optional[dict] = None,
|
|
764
|
+
handler: Optional[str] = None,
|
|
765
|
+
routes: Optional[list] = None,
|
|
766
|
+
name: Optional[str] = None,
|
|
767
|
+
function: Optional[str] = None,
|
|
768
|
+
input_path: Optional[str] = None,
|
|
769
|
+
result_path: Optional[str] = None,
|
|
689
770
|
):
|
|
690
771
|
super().__init__(
|
|
691
772
|
class_name,
|
|
692
773
|
class_args,
|
|
693
774
|
handler,
|
|
694
|
-
name=name,
|
|
775
|
+
name=get_name(name, class_name or RouterStep.kind),
|
|
695
776
|
function=function,
|
|
696
777
|
input_path=input_path,
|
|
697
778
|
result_path=result_path,
|
|
698
779
|
)
|
|
699
780
|
self._routes: ObjectDict = None
|
|
700
781
|
self.routes = routes
|
|
782
|
+
self.endpoint_type = schemas.EndpointType.ROUTER
|
|
783
|
+
if isinstance(class_name, type):
|
|
784
|
+
class_name = class_name.__name__
|
|
785
|
+
self.model_endpoint_creation_strategy = (
|
|
786
|
+
schemas.ModelEndpointCreationStrategy.INPLACE
|
|
787
|
+
if class_name and "VotingEnsemble" in class_name
|
|
788
|
+
else schemas.ModelEndpointCreationStrategy.SKIP
|
|
789
|
+
)
|
|
701
790
|
|
|
702
791
|
def get_children(self):
|
|
703
792
|
"""get child steps (routes)"""
|
|
@@ -719,9 +808,10 @@ class RouterStep(TaskStep):
|
|
|
719
808
|
class_name=None,
|
|
720
809
|
handler=None,
|
|
721
810
|
function=None,
|
|
811
|
+
creation_strategy: schemas.ModelEndpointCreationStrategy = schemas.ModelEndpointCreationStrategy.INPLACE,
|
|
722
812
|
**class_args,
|
|
723
813
|
):
|
|
724
|
-
"""add child route step or class to the router
|
|
814
|
+
"""add child route step or class to the router, if key exists it will be updated
|
|
725
815
|
|
|
726
816
|
:param key: unique name (and route path) for the child step
|
|
727
817
|
:param route: child step object (Task, ..)
|
|
@@ -729,18 +819,46 @@ class RouterStep(TaskStep):
|
|
|
729
819
|
:param class_args: class init arguments
|
|
730
820
|
:param handler: class handler to invoke on run/event
|
|
731
821
|
:param function: function this step should run in
|
|
822
|
+
:param creation_strategy: Strategy for creating or updating the model endpoint:
|
|
823
|
+
|
|
824
|
+
* **overwrite**:
|
|
825
|
+
|
|
826
|
+
1. If model endpoints with the same name exist, delete the `latest` one.
|
|
827
|
+
2. Create a new model endpoint entry and set it as `latest`.
|
|
828
|
+
|
|
829
|
+
* **inplace** (default):
|
|
830
|
+
|
|
831
|
+
1. If model endpoints with the same name exist, update the `latest` entry.
|
|
832
|
+
2. Otherwise, create a new entry.
|
|
833
|
+
|
|
834
|
+
* **archive**:
|
|
835
|
+
|
|
836
|
+
1. If model endpoints with the same name exist, preserve them.
|
|
837
|
+
2. Create a new model endpoint with the same name and set it to `latest`.
|
|
838
|
+
|
|
732
839
|
"""
|
|
733
840
|
|
|
841
|
+
if len(self.routes.keys()) >= MAX_MODELS_PER_ROUTER and key not in self.routes:
|
|
842
|
+
raise mlrun.errors.MLRunModelLimitExceededError(
|
|
843
|
+
f"Router cannot support more than {MAX_MODELS_PER_ROUTER} model endpoints. "
|
|
844
|
+
f"To add a new route, edit an existing one by passing the same key."
|
|
845
|
+
)
|
|
846
|
+
if key in self.routes:
|
|
847
|
+
logger.info(f"Model {key} already exists, updating it.")
|
|
734
848
|
if not route and not class_name and not handler:
|
|
735
849
|
raise MLRunInvalidArgumentError("route or class_name must be specified")
|
|
736
850
|
if not route:
|
|
737
|
-
route = TaskStep(
|
|
851
|
+
route = TaskStep(
|
|
852
|
+
class_name,
|
|
853
|
+
class_args,
|
|
854
|
+
handler=handler,
|
|
855
|
+
model_endpoint_creation_strategy=creation_strategy,
|
|
856
|
+
endpoint_type=schemas.EndpointType.LEAF_EP
|
|
857
|
+
if self.class_name and "serving.VotingEnsemble" in self.class_name
|
|
858
|
+
else schemas.EndpointType.NODE_EP,
|
|
859
|
+
)
|
|
738
860
|
route.function = function or route.function
|
|
739
861
|
|
|
740
|
-
if len(self._routes) >= MAX_ALLOWED_STEPS:
|
|
741
|
-
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
742
|
-
f"Cannot create the serving graph: the maximum number of steps is {MAX_ALLOWED_STEPS}"
|
|
743
|
-
)
|
|
744
862
|
route = self._routes.update(key, route)
|
|
745
863
|
route.set_parent(self)
|
|
746
864
|
return route
|
|
@@ -753,6 +871,10 @@ class RouterStep(TaskStep):
|
|
|
753
871
|
del self._routes[key]
|
|
754
872
|
|
|
755
873
|
def init_object(self, context, namespace, mode="sync", reset=False, **extra_kwargs):
|
|
874
|
+
if not self.routes:
|
|
875
|
+
raise mlrun.errors.MLRunRuntimeError(
|
|
876
|
+
"You have to add models to the router step before initializing it"
|
|
877
|
+
)
|
|
756
878
|
if not self._is_local_function(context):
|
|
757
879
|
return
|
|
758
880
|
|
|
@@ -798,7 +920,119 @@ class RouterStep(TaskStep):
|
|
|
798
920
|
)
|
|
799
921
|
|
|
800
922
|
|
|
801
|
-
class
|
|
923
|
+
class Model(storey.ParallelExecutionRunnable):
|
|
924
|
+
def load(self) -> None:
|
|
925
|
+
"""Override to load model if needed."""
|
|
926
|
+
pass
|
|
927
|
+
|
|
928
|
+
def init(self):
|
|
929
|
+
self.load()
|
|
930
|
+
|
|
931
|
+
def predict(self, body: Any) -> Any:
|
|
932
|
+
"""Override to implement prediction logic. If the logic requires asyncio, override predict_async() instead."""
|
|
933
|
+
return body
|
|
934
|
+
|
|
935
|
+
async def predict_async(self, body: Any) -> Any:
|
|
936
|
+
"""Override to implement prediction logic if the logic requires asyncio."""
|
|
937
|
+
return body
|
|
938
|
+
|
|
939
|
+
def run(self, body: Any, path: str) -> Any:
|
|
940
|
+
return self.predict(body)
|
|
941
|
+
|
|
942
|
+
async def run_async(self, body: Any, path: str) -> Any:
|
|
943
|
+
return self.predict(body)
|
|
944
|
+
|
|
945
|
+
|
|
946
|
+
class ModelSelector:
|
|
947
|
+
"""Used to select which models to run on each event."""
|
|
948
|
+
|
|
949
|
+
def select(
|
|
950
|
+
self, event, available_models: list[Model]
|
|
951
|
+
) -> Union[list[str], list[Model]]:
|
|
952
|
+
"""
|
|
953
|
+
Given an event, returns a list of model names or a list of model objects to run on the event.
|
|
954
|
+
If None is returned, all models will be run.
|
|
955
|
+
|
|
956
|
+
:param event: The full event
|
|
957
|
+
:param available_models: List of available models
|
|
958
|
+
"""
|
|
959
|
+
pass
|
|
960
|
+
|
|
961
|
+
|
|
962
|
+
class ModelRunner(storey.ParallelExecution):
|
|
963
|
+
"""
|
|
964
|
+
Runs multiple Models on each event. See ModelRunnerStep.
|
|
965
|
+
|
|
966
|
+
:param model_selector: ModelSelector instance whose select() method will be used to select models to run on each
|
|
967
|
+
event. Optional. If not passed, all models will be run.
|
|
968
|
+
"""
|
|
969
|
+
|
|
970
|
+
def __init__(self, *args, model_selector: Optional[ModelSelector] = None, **kwargs):
|
|
971
|
+
super().__init__(*args, **kwargs)
|
|
972
|
+
self.model_selector = model_selector or ModelSelector()
|
|
973
|
+
|
|
974
|
+
def select_runnables(self, event):
|
|
975
|
+
models = cast(list[Model], self.runnables)
|
|
976
|
+
return self.model_selector.select(event, models)
|
|
977
|
+
|
|
978
|
+
|
|
979
|
+
class ModelRunnerStep(TaskStep, StepToDict):
|
|
980
|
+
"""
|
|
981
|
+
Runs multiple Models on each event.
|
|
982
|
+
|
|
983
|
+
example::
|
|
984
|
+
|
|
985
|
+
model_runner_step = ModelRunnerStep(name="my_model_runner")
|
|
986
|
+
model_runner_step.add_model(MyModel(name="my_model"))
|
|
987
|
+
graph.to(model_runner_step)
|
|
988
|
+
|
|
989
|
+
:param model_selector: ModelSelector instance whose select() method will be used to select models to run on each
|
|
990
|
+
event. Optional. If not passed, all models will be run.
|
|
991
|
+
"""
|
|
992
|
+
|
|
993
|
+
kind = "model_runner"
|
|
994
|
+
|
|
995
|
+
def __init__(
|
|
996
|
+
self,
|
|
997
|
+
*args,
|
|
998
|
+
model_selector: Optional[Union[str, ModelSelector]] = None,
|
|
999
|
+
**kwargs,
|
|
1000
|
+
):
|
|
1001
|
+
super().__init__(
|
|
1002
|
+
*args,
|
|
1003
|
+
class_name="mlrun.serving.ModelRunner",
|
|
1004
|
+
class_args=dict(model_selector=model_selector),
|
|
1005
|
+
**kwargs,
|
|
1006
|
+
)
|
|
1007
|
+
|
|
1008
|
+
def add_model(self, model: Union[str, Model], **model_parameters) -> None:
|
|
1009
|
+
"""
|
|
1010
|
+
Add a Model to this ModelRunner.
|
|
1011
|
+
|
|
1012
|
+
:param model: Model class name or object
|
|
1013
|
+
:param model_parameters: Parameters for model instantiation
|
|
1014
|
+
"""
|
|
1015
|
+
models = self.class_args.get("models", [])
|
|
1016
|
+
models.append((model, model_parameters))
|
|
1017
|
+
self.class_args["models"] = models
|
|
1018
|
+
|
|
1019
|
+
def init_object(self, context, namespace, mode="sync", reset=False, **extra_kwargs):
|
|
1020
|
+
model_selector = self.class_args.get("model_selector")
|
|
1021
|
+
models = self.class_args.get("models")
|
|
1022
|
+
if isinstance(model_selector, str):
|
|
1023
|
+
model_selector = get_class(model_selector, namespace)()
|
|
1024
|
+
model_objects = []
|
|
1025
|
+
for model, model_params in models:
|
|
1026
|
+
if not isinstance(model, Model):
|
|
1027
|
+
model = get_class(model, namespace)(**model_params)
|
|
1028
|
+
model_objects.append(model)
|
|
1029
|
+
self._async_object = ModelRunner(
|
|
1030
|
+
model_selector=model_selector,
|
|
1031
|
+
runnables=model_objects,
|
|
1032
|
+
)
|
|
1033
|
+
|
|
1034
|
+
|
|
1035
|
+
class QueueStep(BaseStep, StepToDict):
|
|
802
1036
|
"""queue step, implement an async queue or represent a stream"""
|
|
803
1037
|
|
|
804
1038
|
kind = "queue"
|
|
@@ -813,12 +1047,12 @@ class QueueStep(BaseStep):
|
|
|
813
1047
|
|
|
814
1048
|
def __init__(
|
|
815
1049
|
self,
|
|
816
|
-
name: str = None,
|
|
817
|
-
path: str = None,
|
|
818
|
-
after: list = None,
|
|
819
|
-
shards: int = None,
|
|
820
|
-
retention_in_hours: int = None,
|
|
821
|
-
trigger_args: dict = None,
|
|
1050
|
+
name: Optional[str] = None,
|
|
1051
|
+
path: Optional[str] = None,
|
|
1052
|
+
after: Optional[list] = None,
|
|
1053
|
+
shards: Optional[int] = None,
|
|
1054
|
+
retention_in_hours: Optional[int] = None,
|
|
1055
|
+
trigger_args: Optional[dict] = None,
|
|
822
1056
|
**options,
|
|
823
1057
|
):
|
|
824
1058
|
super().__init__(name, after)
|
|
@@ -850,13 +1084,16 @@ class QueueStep(BaseStep):
|
|
|
850
1084
|
def to(
|
|
851
1085
|
self,
|
|
852
1086
|
class_name: Union[str, StepToDict] = None,
|
|
853
|
-
name: str = None,
|
|
854
|
-
handler: str = None,
|
|
855
|
-
graph_shape: str = None,
|
|
856
|
-
function: str = None,
|
|
857
|
-
full_event: bool = None,
|
|
858
|
-
input_path: str = None,
|
|
859
|
-
result_path: str = None,
|
|
1087
|
+
name: Optional[str] = None,
|
|
1088
|
+
handler: Optional[str] = None,
|
|
1089
|
+
graph_shape: Optional[str] = None,
|
|
1090
|
+
function: Optional[str] = None,
|
|
1091
|
+
full_event: Optional[bool] = None,
|
|
1092
|
+
input_path: Optional[str] = None,
|
|
1093
|
+
result_path: Optional[str] = None,
|
|
1094
|
+
model_endpoint_creation_strategy: Optional[
|
|
1095
|
+
schemas.ModelEndpointCreationStrategy
|
|
1096
|
+
] = None,
|
|
860
1097
|
**class_args,
|
|
861
1098
|
):
|
|
862
1099
|
if not function:
|
|
@@ -873,6 +1110,7 @@ class QueueStep(BaseStep):
|
|
|
873
1110
|
full_event,
|
|
874
1111
|
input_path,
|
|
875
1112
|
result_path,
|
|
1113
|
+
model_endpoint_creation_strategy,
|
|
876
1114
|
**class_args,
|
|
877
1115
|
)
|
|
878
1116
|
|
|
@@ -905,7 +1143,7 @@ class FlowStep(BaseStep):
|
|
|
905
1143
|
self,
|
|
906
1144
|
name=None,
|
|
907
1145
|
steps=None,
|
|
908
|
-
after: list = None,
|
|
1146
|
+
after: Optional[list] = None,
|
|
909
1147
|
engine=None,
|
|
910
1148
|
final_step=None,
|
|
911
1149
|
):
|
|
@@ -948,9 +1186,12 @@ class FlowStep(BaseStep):
|
|
|
948
1186
|
before=None,
|
|
949
1187
|
graph_shape=None,
|
|
950
1188
|
function=None,
|
|
951
|
-
full_event: bool = None,
|
|
952
|
-
input_path: str = None,
|
|
953
|
-
result_path: str = None,
|
|
1189
|
+
full_event: Optional[bool] = None,
|
|
1190
|
+
input_path: Optional[str] = None,
|
|
1191
|
+
result_path: Optional[str] = None,
|
|
1192
|
+
model_endpoint_creation_strategy: Optional[
|
|
1193
|
+
schemas.ModelEndpointCreationStrategy
|
|
1194
|
+
] = None,
|
|
954
1195
|
**class_args,
|
|
955
1196
|
):
|
|
956
1197
|
"""add task, queue or router step/class to the flow
|
|
@@ -982,6 +1223,23 @@ class FlowStep(BaseStep):
|
|
|
982
1223
|
this require that the event body will behave like a dict, example:
|
|
983
1224
|
event: {"x": 5} , result_path="y" means the output of the step will be written
|
|
984
1225
|
to event["y"] resulting in {"x": 5, "y": <result>}
|
|
1226
|
+
:param model_endpoint_creation_strategy: Strategy for creating or updating the model endpoint:
|
|
1227
|
+
|
|
1228
|
+
* **overwrite**:
|
|
1229
|
+
|
|
1230
|
+
1. If model endpoints with the same name exist, delete the `latest` one.
|
|
1231
|
+
2. Create a new model endpoint entry and set it as `latest`.
|
|
1232
|
+
|
|
1233
|
+
* **inplace** (default):
|
|
1234
|
+
|
|
1235
|
+
1. If model endpoints with the same name exist, update the `latest` entry.
|
|
1236
|
+
2. Otherwise, create a new entry.
|
|
1237
|
+
|
|
1238
|
+
* **archive**:
|
|
1239
|
+
|
|
1240
|
+
1. If model endpoints with the same name exist, preserve them.
|
|
1241
|
+
2. Create a new model endpoint with the same name and set it to `latest`.
|
|
1242
|
+
|
|
985
1243
|
:param class_args: class init arguments
|
|
986
1244
|
"""
|
|
987
1245
|
|
|
@@ -994,6 +1252,7 @@ class FlowStep(BaseStep):
|
|
|
994
1252
|
full_event=full_event,
|
|
995
1253
|
input_path=input_path,
|
|
996
1254
|
result_path=result_path,
|
|
1255
|
+
model_endpoint_creation_strategy=model_endpoint_creation_strategy,
|
|
997
1256
|
class_args=class_args,
|
|
998
1257
|
)
|
|
999
1258
|
|
|
@@ -1037,7 +1296,7 @@ class FlowStep(BaseStep):
|
|
|
1037
1296
|
self._last_added = step
|
|
1038
1297
|
return step
|
|
1039
1298
|
|
|
1040
|
-
def clear_children(self, steps: list = None):
|
|
1299
|
+
def clear_children(self, steps: Optional[list] = None):
|
|
1041
1300
|
"""remove some or all of the states, empty/None for all"""
|
|
1042
1301
|
if not steps:
|
|
1043
1302
|
steps = self._steps.keys()
|
|
@@ -1282,11 +1541,19 @@ class FlowStep(BaseStep):
|
|
|
1282
1541
|
if self._controller:
|
|
1283
1542
|
# async flow (using storey)
|
|
1284
1543
|
event._awaitable_result = None
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1544
|
+
if self.context.is_mock:
|
|
1545
|
+
resp = self._controller.emit(
|
|
1546
|
+
event, return_awaitable_result=self._wait_for_result
|
|
1547
|
+
)
|
|
1548
|
+
if self._wait_for_result and resp:
|
|
1549
|
+
return resp.await_result()
|
|
1550
|
+
else:
|
|
1551
|
+
resp_awaitable = self._controller.emit(
|
|
1552
|
+
event, await_result=self._wait_for_result
|
|
1553
|
+
)
|
|
1554
|
+
if self._wait_for_result:
|
|
1555
|
+
return resp_awaitable
|
|
1556
|
+
return self._await_and_return_id(resp_awaitable, event)
|
|
1290
1557
|
event = copy(event)
|
|
1291
1558
|
event.body = {"id": event.id}
|
|
1292
1559
|
return event
|
|
@@ -1329,8 +1596,9 @@ class FlowStep(BaseStep):
|
|
|
1329
1596
|
|
|
1330
1597
|
if self._controller:
|
|
1331
1598
|
if hasattr(self._controller, "terminate"):
|
|
1332
|
-
self._controller.terminate()
|
|
1333
|
-
|
|
1599
|
+
return self._controller.terminate(wait=True)
|
|
1600
|
+
else:
|
|
1601
|
+
return self._controller.await_termination()
|
|
1334
1602
|
|
|
1335
1603
|
def plot(self, filename=None, format=None, source=None, targets=None, **kw):
|
|
1336
1604
|
"""plot/save graph using graphviz
|
|
@@ -1418,6 +1686,7 @@ classes_map = {
|
|
|
1418
1686
|
"queue": QueueStep,
|
|
1419
1687
|
"error_step": ErrorStep,
|
|
1420
1688
|
"monitoring_application": MonitoringApplicationStep,
|
|
1689
|
+
"model_runner": ModelRunnerStep,
|
|
1421
1690
|
}
|
|
1422
1691
|
|
|
1423
1692
|
|
|
@@ -1544,7 +1813,7 @@ def get_name(name, class_name):
|
|
|
1544
1813
|
raise MLRunInvalidArgumentError("name or class_name must be provided")
|
|
1545
1814
|
if isinstance(class_name, type):
|
|
1546
1815
|
return class_name.__name__
|
|
1547
|
-
return class_name
|
|
1816
|
+
return class_name.split(".")[-1]
|
|
1548
1817
|
|
|
1549
1818
|
|
|
1550
1819
|
def params_to_step(
|
|
@@ -1554,26 +1823,25 @@ def params_to_step(
|
|
|
1554
1823
|
graph_shape=None,
|
|
1555
1824
|
function=None,
|
|
1556
1825
|
full_event=None,
|
|
1557
|
-
input_path: str = None,
|
|
1558
|
-
result_path: str = None,
|
|
1826
|
+
input_path: Optional[str] = None,
|
|
1827
|
+
result_path: Optional[str] = None,
|
|
1559
1828
|
class_args=None,
|
|
1829
|
+
model_endpoint_creation_strategy: Optional[
|
|
1830
|
+
schemas.ModelEndpointCreationStrategy
|
|
1831
|
+
] = None,
|
|
1832
|
+
endpoint_type: Optional[schemas.EndpointType] = None,
|
|
1560
1833
|
):
|
|
1561
1834
|
"""return step object from provided params or classes/objects"""
|
|
1562
1835
|
|
|
1563
1836
|
class_args = class_args or {}
|
|
1564
1837
|
|
|
1565
|
-
if
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
step = cls.from_dict(struct)
|
|
1571
|
-
step.function = function
|
|
1572
|
-
step.full_event = full_event or step.full_event
|
|
1573
|
-
step.input_path = input_path or step.input_path
|
|
1574
|
-
step.result_path = result_path or step.result_path
|
|
1838
|
+
if isinstance(class_name, QueueStep):
|
|
1839
|
+
if not (name or class_name.name):
|
|
1840
|
+
raise MLRunInvalidArgumentError("queue name must be specified")
|
|
1841
|
+
|
|
1842
|
+
step = class_name
|
|
1575
1843
|
|
|
1576
|
-
elif class_name
|
|
1844
|
+
elif class_name in queue_class_names:
|
|
1577
1845
|
if "path" not in class_args:
|
|
1578
1846
|
raise MLRunInvalidArgumentError(
|
|
1579
1847
|
"path=<stream path or None> must be specified for queues"
|
|
@@ -1586,6 +1854,24 @@ def params_to_step(
|
|
|
1586
1854
|
class_args["full_event"] = full_event
|
|
1587
1855
|
step = QueueStep(name, **class_args)
|
|
1588
1856
|
|
|
1857
|
+
elif class_name and hasattr(class_name, "to_dict"):
|
|
1858
|
+
struct = class_name.to_dict()
|
|
1859
|
+
kind = struct.get("kind", StepKinds.task)
|
|
1860
|
+
name = (
|
|
1861
|
+
name
|
|
1862
|
+
or struct.get("name", struct.get("class_name"))
|
|
1863
|
+
or class_name.to_dict(["name"]).get("name")
|
|
1864
|
+
)
|
|
1865
|
+
cls = classes_map.get(kind, RootFlowStep)
|
|
1866
|
+
step = cls.from_dict(struct)
|
|
1867
|
+
step.function = function
|
|
1868
|
+
step.full_event = full_event or step.full_event
|
|
1869
|
+
step.input_path = input_path or step.input_path
|
|
1870
|
+
step.result_path = result_path or step.result_path
|
|
1871
|
+
if kind == StepKinds.task:
|
|
1872
|
+
step.model_endpoint_creation_strategy = model_endpoint_creation_strategy
|
|
1873
|
+
step.endpoint_type = endpoint_type
|
|
1874
|
+
|
|
1589
1875
|
elif class_name and class_name.startswith("*"):
|
|
1590
1876
|
routes = class_args.get("routes", None)
|
|
1591
1877
|
class_name = class_name[1:]
|
|
@@ -1612,6 +1898,8 @@ def params_to_step(
|
|
|
1612
1898
|
full_event=full_event,
|
|
1613
1899
|
input_path=input_path,
|
|
1614
1900
|
result_path=result_path,
|
|
1901
|
+
model_endpoint_creation_strategy=model_endpoint_creation_strategy,
|
|
1902
|
+
endpoint_type=endpoint_type,
|
|
1615
1903
|
)
|
|
1616
1904
|
else:
|
|
1617
1905
|
raise MLRunInvalidArgumentError("class_name or handler must be provided")
|
|
@@ -1650,7 +1938,29 @@ def _init_async_objects(context, steps):
|
|
|
1650
1938
|
|
|
1651
1939
|
kafka_brokers = get_kafka_brokers_from_dict(options, pop=True)
|
|
1652
1940
|
|
|
1653
|
-
if stream_path.startswith("
|
|
1941
|
+
if stream_path and stream_path.startswith("ds://"):
|
|
1942
|
+
datastore_profile = datastore_profile_read(stream_path)
|
|
1943
|
+
if isinstance(
|
|
1944
|
+
datastore_profile,
|
|
1945
|
+
(DatastoreProfileKafkaTarget, DatastoreProfileKafkaSource),
|
|
1946
|
+
):
|
|
1947
|
+
step._async_object = KafkaStoreyTarget(
|
|
1948
|
+
path=stream_path,
|
|
1949
|
+
context=context,
|
|
1950
|
+
**options,
|
|
1951
|
+
)
|
|
1952
|
+
elif isinstance(datastore_profile, DatastoreProfileV3io):
|
|
1953
|
+
step._async_object = StreamStoreyTarget(
|
|
1954
|
+
stream_path=stream_path,
|
|
1955
|
+
context=context,
|
|
1956
|
+
**options,
|
|
1957
|
+
)
|
|
1958
|
+
else:
|
|
1959
|
+
raise mlrun.errors.MLRunValueError(
|
|
1960
|
+
f"Received an unexpected stream profile type: {type(datastore_profile)}\n"
|
|
1961
|
+
"Expects `DatastoreProfileV3io` or `DatastoreProfileKafkaSource`."
|
|
1962
|
+
)
|
|
1963
|
+
elif stream_path.startswith("kafka://") or kafka_brokers:
|
|
1654
1964
|
topic, brokers = parse_kafka_url(stream_path, kafka_brokers)
|
|
1655
1965
|
|
|
1656
1966
|
kafka_producer_options = options.pop(
|
|
@@ -1703,8 +2013,12 @@ def _init_async_objects(context, steps):
|
|
|
1703
2013
|
is_explicit_ack_supported(context) and mlrun.mlconf.is_explicit_ack_enabled()
|
|
1704
2014
|
)
|
|
1705
2015
|
|
|
1706
|
-
|
|
1707
|
-
|
|
2016
|
+
if context.is_mock:
|
|
2017
|
+
source_class = storey.SyncEmitSource
|
|
2018
|
+
else:
|
|
2019
|
+
source_class = storey.AsyncEmitSource
|
|
2020
|
+
|
|
2021
|
+
default_source = source_class(
|
|
1708
2022
|
context=context,
|
|
1709
2023
|
explicit_ack=explicit_ack,
|
|
1710
2024
|
**source_args,
|