mlrun 1.10.0rc17__py3-none-any.whl → 1.10.0rc19__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of mlrun might be problematic. Click here for more details.
- mlrun/__init__.py +21 -2
- mlrun/common/constants.py +1 -0
- mlrun/common/formatters/artifact.py +1 -0
- mlrun/common/schemas/model_monitoring/constants.py +14 -6
- mlrun/config.py +14 -0
- mlrun/datastore/__init__.py +9 -1
- mlrun/datastore/datastore.py +4 -4
- mlrun/datastore/datastore_profile.py +26 -0
- mlrun/datastore/model_provider/huggingface_provider.py +182 -0
- mlrun/datastore/model_provider/model_provider.py +57 -56
- mlrun/datastore/model_provider/openai_provider.py +34 -20
- mlrun/datastore/utils.py +6 -0
- mlrun/launcher/base.py +13 -0
- mlrun/model_monitoring/api.py +5 -3
- mlrun/model_monitoring/applications/base.py +107 -28
- mlrun/model_monitoring/applications/results.py +4 -7
- mlrun/run.py +4 -2
- mlrun/runtimes/base.py +5 -2
- mlrun/runtimes/daskjob.py +1 -0
- mlrun/runtimes/nuclio/application/application.py +84 -5
- mlrun/runtimes/nuclio/function.py +3 -1
- mlrun/serving/server.py +1 -0
- mlrun/serving/states.py +5 -2
- mlrun/utils/helpers.py +16 -1
- mlrun/utils/logger.py +3 -1
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc19.dist-info}/METADATA +2 -2
- {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc19.dist-info}/RECORD +32 -31
- {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc19.dist-info}/WHEEL +0 -0
- {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc19.dist-info}/entry_points.txt +0 -0
- {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc19.dist-info}/licenses/LICENSE +0 -0
- {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc19.dist-info}/top_level.txt +0 -0
|
@@ -11,13 +11,17 @@
|
|
|
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
|
+
import inspect
|
|
14
15
|
from collections.abc import Awaitable
|
|
15
|
-
from typing import Callable, Optional,
|
|
16
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional, Union
|
|
16
17
|
|
|
17
18
|
import mlrun
|
|
18
19
|
from mlrun.datastore.model_provider.model_provider import ModelProvider
|
|
20
|
+
from mlrun.datastore.utils import accepts_param
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from openai._models import BaseModel # noqa
|
|
24
|
+
from openai.types.chat.chat_completion import ChatCompletion
|
|
21
25
|
|
|
22
26
|
|
|
23
27
|
class OpenAIProvider(ModelProvider):
|
|
@@ -68,10 +72,6 @@ class OpenAIProvider(ModelProvider):
|
|
|
68
72
|
subpath = ""
|
|
69
73
|
return endpoint, subpath
|
|
70
74
|
|
|
71
|
-
@property
|
|
72
|
-
def model(self) -> Optional[str]:
|
|
73
|
-
return self.endpoint
|
|
74
|
-
|
|
75
75
|
def load_client(self) -> None:
|
|
76
76
|
"""
|
|
77
77
|
Initializes the OpenAI SDK client using the provided options.
|
|
@@ -103,8 +103,8 @@ class OpenAIProvider(ModelProvider):
|
|
|
103
103
|
return self._sanitize_options(res)
|
|
104
104
|
|
|
105
105
|
def custom_invoke(
|
|
106
|
-
self, operation: Optional[Callable
|
|
107
|
-
) ->
|
|
106
|
+
self, operation: Optional[Callable] = None, **invoke_kwargs
|
|
107
|
+
) -> Union["ChatCompletion", "BaseModel"]:
|
|
108
108
|
"""
|
|
109
109
|
OpenAI-specific implementation of `ModelProvider.custom_invoke`.
|
|
110
110
|
|
|
@@ -126,18 +126,24 @@ class OpenAIProvider(ModelProvider):
|
|
|
126
126
|
|
|
127
127
|
"""
|
|
128
128
|
invoke_kwargs = self.get_invoke_kwargs(invoke_kwargs)
|
|
129
|
+
model_kwargs = {"model": invoke_kwargs.pop("model", None) or self.model}
|
|
130
|
+
|
|
129
131
|
if operation:
|
|
130
|
-
|
|
132
|
+
if not callable(operation):
|
|
133
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
134
|
+
"OpenAI custom_invoke operation must be a callable"
|
|
135
|
+
)
|
|
136
|
+
if not accepts_param(operation, "model"):
|
|
137
|
+
model_kwargs = {}
|
|
138
|
+
return operation(**invoke_kwargs, **model_kwargs)
|
|
131
139
|
else:
|
|
132
|
-
return self.client.chat.completions.create(
|
|
133
|
-
**invoke_kwargs, model=self.model
|
|
134
|
-
)
|
|
140
|
+
return self.client.chat.completions.create(**invoke_kwargs, **model_kwargs)
|
|
135
141
|
|
|
136
142
|
async def async_custom_invoke(
|
|
137
143
|
self,
|
|
138
|
-
operation: Optional[Callable[..., Awaitable[
|
|
144
|
+
operation: Optional[Callable[..., Awaitable[Any]]] = None,
|
|
139
145
|
**invoke_kwargs,
|
|
140
|
-
) ->
|
|
146
|
+
) -> Union["ChatCompletion", "BaseModel"]:
|
|
141
147
|
"""
|
|
142
148
|
OpenAI-specific implementation of `ModelProvider.async_custom_invoke`.
|
|
143
149
|
|
|
@@ -145,25 +151,33 @@ class OpenAIProvider(ModelProvider):
|
|
|
145
151
|
`ModelProvider.async_custom_invoke`.
|
|
146
152
|
|
|
147
153
|
Example:
|
|
148
|
-
|
|
154
|
+
```python
|
|
149
155
|
result = openai_model_provider.invoke(
|
|
150
156
|
openai_model_provider.async_client.images.generate,
|
|
151
157
|
prompt="A futuristic cityscape at sunset",
|
|
152
158
|
n=1,
|
|
153
159
|
size="1024x1024",
|
|
154
160
|
)
|
|
155
|
-
|
|
161
|
+
```
|
|
162
|
+
|
|
156
163
|
:param operation: Same as ModelProvider.async_custom_invoke.
|
|
157
164
|
:param invoke_kwargs: Same as ModelProvider.async_custom_invoke.
|
|
158
165
|
:return: Same as ModelProvider.async_custom_invoke.
|
|
159
166
|
|
|
160
167
|
"""
|
|
161
168
|
invoke_kwargs = self.get_invoke_kwargs(invoke_kwargs)
|
|
169
|
+
model_kwargs = {"model": invoke_kwargs.pop("model", None) or self.model}
|
|
162
170
|
if operation:
|
|
163
|
-
|
|
171
|
+
if not inspect.iscoroutinefunction(operation):
|
|
172
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
173
|
+
"OpenAI async_custom_invoke operation must be a coroutine function"
|
|
174
|
+
)
|
|
175
|
+
if not accepts_param(operation, "model"):
|
|
176
|
+
model_kwargs = {}
|
|
177
|
+
return await operation(**invoke_kwargs, **model_kwargs)
|
|
164
178
|
else:
|
|
165
179
|
return await self.async_client.chat.completions.create(
|
|
166
|
-
**invoke_kwargs,
|
|
180
|
+
**invoke_kwargs, **model_kwargs
|
|
167
181
|
)
|
|
168
182
|
|
|
169
183
|
def invoke(
|
|
@@ -171,7 +185,7 @@ class OpenAIProvider(ModelProvider):
|
|
|
171
185
|
messages: Optional[list[dict]] = None,
|
|
172
186
|
as_str: bool = False,
|
|
173
187
|
**invoke_kwargs,
|
|
174
|
-
) ->
|
|
188
|
+
) -> Union[str, "ChatCompletion"]:
|
|
175
189
|
"""
|
|
176
190
|
OpenAI-specific implementation of `ModelProvider.invoke`.
|
|
177
191
|
Invokes an OpenAI model operation using the sync client.
|
|
@@ -200,7 +214,7 @@ class OpenAIProvider(ModelProvider):
|
|
|
200
214
|
messages: Optional[list[dict]] = None,
|
|
201
215
|
as_str: bool = False,
|
|
202
216
|
**invoke_kwargs,
|
|
203
|
-
) -> str:
|
|
217
|
+
) -> Union[str, "ChatCompletion"]:
|
|
204
218
|
"""
|
|
205
219
|
OpenAI-specific implementation of `ModelProvider.async_invoke`.
|
|
206
220
|
Invokes an OpenAI model operation using the async client.
|
mlrun/datastore/utils.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 inspect
|
|
15
16
|
import math
|
|
16
17
|
import tarfile
|
|
17
18
|
import tempfile
|
|
@@ -333,3 +334,8 @@ def parse_url(url):
|
|
|
333
334
|
if parsed_url.port:
|
|
334
335
|
endpoint += f":{parsed_url.port}"
|
|
335
336
|
return schema, endpoint, parsed_url
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def accepts_param(func: callable, param_name):
|
|
340
|
+
sig = inspect.signature(func)
|
|
341
|
+
return param_name in sig.parameters
|
mlrun/launcher/base.py
CHANGED
|
@@ -157,6 +157,19 @@ class BaseLauncher(abc.ABC):
|
|
|
157
157
|
]:
|
|
158
158
|
mlrun.utils.helpers.warn_on_deprecated_image(image)
|
|
159
159
|
|
|
160
|
+
# Raise an error if retry is configured for a runtime that doesn't support retries.
|
|
161
|
+
# For local runs, we intentionally skip this validation and allow the run to proceed, since they are typically
|
|
162
|
+
# used for debugging purposes, and in such cases we avoid blocking their execution.
|
|
163
|
+
if (
|
|
164
|
+
not mlrun.runtimes.RuntimeKinds.is_local_runtime(runtime.kind)
|
|
165
|
+
and run.spec.retry.count
|
|
166
|
+
and runtime.kind not in mlrun.runtimes.RuntimeKinds.retriable_runtimes()
|
|
167
|
+
):
|
|
168
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
169
|
+
f"Retry is not supported for {runtime.kind} runtime, supported runtimes are: "
|
|
170
|
+
f"{mlrun.runtimes.RuntimeKinds.retriable_runtimes()}"
|
|
171
|
+
)
|
|
172
|
+
|
|
160
173
|
@staticmethod
|
|
161
174
|
def _validate_output_path(
|
|
162
175
|
runtime: "mlrun.runtimes.BaseRuntime",
|
mlrun/model_monitoring/api.py
CHANGED
|
@@ -19,8 +19,6 @@ from datetime import datetime
|
|
|
19
19
|
import numpy as np
|
|
20
20
|
import pandas as pd
|
|
21
21
|
|
|
22
|
-
import mlrun.artifacts
|
|
23
|
-
import mlrun.common.helpers
|
|
24
22
|
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
25
23
|
import mlrun.datastore.base
|
|
26
24
|
import mlrun.feature_store
|
|
@@ -538,10 +536,14 @@ def _create_model_monitoring_function_base(
|
|
|
538
536
|
This function does not set the labels or mounts v3io.
|
|
539
537
|
"""
|
|
540
538
|
if name in mm_constants._RESERVED_FUNCTION_NAMES:
|
|
541
|
-
raise mlrun.errors.
|
|
539
|
+
raise mlrun.errors.MLRunValueError(
|
|
542
540
|
"An application cannot have the following names: "
|
|
543
541
|
f"{mm_constants._RESERVED_FUNCTION_NAMES}"
|
|
544
542
|
)
|
|
543
|
+
if name and name.endswith(mm_constants._RESERVED_EVALUATE_FUNCTION_SUFFIX):
|
|
544
|
+
raise mlrun.errors.MLRunValueError(
|
|
545
|
+
"Model monitoring application names cannot end with `-batch`"
|
|
546
|
+
)
|
|
545
547
|
if func is None:
|
|
546
548
|
func = ""
|
|
547
549
|
func_obj = typing.cast(
|
|
@@ -25,6 +25,7 @@ import pandas as pd
|
|
|
25
25
|
|
|
26
26
|
import mlrun
|
|
27
27
|
import mlrun.common.constants as mlrun_constants
|
|
28
|
+
import mlrun.common.helpers
|
|
28
29
|
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
29
30
|
import mlrun.datastore.datastore_profile as ds_profile
|
|
30
31
|
import mlrun.errors
|
|
@@ -33,6 +34,7 @@ import mlrun.model_monitoring.applications.context as mm_context
|
|
|
33
34
|
import mlrun.model_monitoring.applications.results as mm_results
|
|
34
35
|
import mlrun.model_monitoring.db._schedules as mm_schedules
|
|
35
36
|
import mlrun.model_monitoring.helpers as mm_helpers
|
|
37
|
+
import mlrun.utils
|
|
36
38
|
from mlrun.serving.utils import MonitoringApplicationToDict
|
|
37
39
|
from mlrun.utils import logger
|
|
38
40
|
|
|
@@ -194,7 +196,25 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
194
196
|
Optional[mm_schedules.ModelMonitoringSchedulesFileApplication],
|
|
195
197
|
]
|
|
196
198
|
]:
|
|
197
|
-
endpoints_output: dict[
|
|
199
|
+
endpoints_output: dict[
|
|
200
|
+
str,
|
|
201
|
+
list[
|
|
202
|
+
tuple[
|
|
203
|
+
mm_context.MonitoringApplicationContext,
|
|
204
|
+
Union[
|
|
205
|
+
mm_results.ModelMonitoringApplicationResult,
|
|
206
|
+
mm_results.ModelMonitoringApplicationMetric,
|
|
207
|
+
list[
|
|
208
|
+
Union[
|
|
209
|
+
mm_results.ModelMonitoringApplicationResult,
|
|
210
|
+
mm_results.ModelMonitoringApplicationMetric,
|
|
211
|
+
mm_results._ModelMonitoringApplicationStats,
|
|
212
|
+
]
|
|
213
|
+
],
|
|
214
|
+
],
|
|
215
|
+
]
|
|
216
|
+
],
|
|
217
|
+
] = defaultdict(list)
|
|
198
218
|
application_schedules = nullcontext()
|
|
199
219
|
if write_output:
|
|
200
220
|
cls._check_writer_is_up(project)
|
|
@@ -220,11 +240,21 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
220
240
|
profile=stream_profile,
|
|
221
241
|
)
|
|
222
242
|
for endpoint_id, outputs in endpoints_output.items():
|
|
243
|
+
writer_events = []
|
|
244
|
+
for ctx, res in outputs:
|
|
245
|
+
if isinstance(res, list):
|
|
246
|
+
writer_events.extend(
|
|
247
|
+
_serialize_context_and_result(
|
|
248
|
+
context=ctx, result=sub_res
|
|
249
|
+
)
|
|
250
|
+
for sub_res in res
|
|
251
|
+
)
|
|
252
|
+
else:
|
|
253
|
+
writer_events.append(
|
|
254
|
+
_serialize_context_and_result(context=ctx, result=res)
|
|
255
|
+
)
|
|
223
256
|
writer_stream.push(
|
|
224
|
-
|
|
225
|
-
_serialize_context_and_result(context=ctx, result=res)
|
|
226
|
-
for ctx, res in outputs
|
|
227
|
-
],
|
|
257
|
+
writer_events,
|
|
228
258
|
partition_key=endpoint_id,
|
|
229
259
|
)
|
|
230
260
|
logger.debug(
|
|
@@ -238,6 +268,14 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
238
268
|
)
|
|
239
269
|
application_schedules.__exit__(None, None, None)
|
|
240
270
|
|
|
271
|
+
@classmethod
|
|
272
|
+
def _get_application_name(cls, context: "mlrun.MLClientCtx") -> str:
|
|
273
|
+
"""Get the application name from the context via the function URI"""
|
|
274
|
+
_, application_name, _, _ = mlrun.common.helpers.parse_versioned_object_uri(
|
|
275
|
+
context.to_dict().get("spec", {}).get("function", "")
|
|
276
|
+
)
|
|
277
|
+
return application_name
|
|
278
|
+
|
|
241
279
|
def _handler(
|
|
242
280
|
self,
|
|
243
281
|
context: "mlrun.MLClientCtx",
|
|
@@ -250,7 +288,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
250
288
|
end: Optional[str] = None,
|
|
251
289
|
base_period: Optional[int] = None,
|
|
252
290
|
write_output: bool = False,
|
|
253
|
-
|
|
291
|
+
fail_on_overlap: bool = True,
|
|
254
292
|
stream_profile: Optional[ds_profile.DatastoreProfile] = None,
|
|
255
293
|
):
|
|
256
294
|
"""
|
|
@@ -271,7 +309,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
271
309
|
"working with endpoints, without any custom data-frame input"
|
|
272
310
|
)
|
|
273
311
|
|
|
274
|
-
application_name = self.
|
|
312
|
+
application_name = self._get_application_name(context)
|
|
275
313
|
|
|
276
314
|
feature_stats = (
|
|
277
315
|
mm_api.get_sample_set_statistics(reference_data)
|
|
@@ -320,7 +358,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
320
358
|
application_schedules=application_schedules,
|
|
321
359
|
endpoint_id=endpoint_id,
|
|
322
360
|
application_name=application_name,
|
|
323
|
-
|
|
361
|
+
fail_on_overlap=fail_on_overlap,
|
|
324
362
|
):
|
|
325
363
|
result = call_do_tracking(
|
|
326
364
|
event={
|
|
@@ -443,7 +481,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
443
481
|
end_dt: datetime,
|
|
444
482
|
base_period: Optional[int],
|
|
445
483
|
application_name: str,
|
|
446
|
-
|
|
484
|
+
fail_on_overlap: bool,
|
|
447
485
|
) -> datetime:
|
|
448
486
|
"""Make sure that the (app, endpoint) pair doesn't write output before the last analyzed window"""
|
|
449
487
|
if application_schedules:
|
|
@@ -452,7 +490,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
452
490
|
)
|
|
453
491
|
if last_analyzed:
|
|
454
492
|
if start_dt < last_analyzed:
|
|
455
|
-
if
|
|
493
|
+
if not fail_on_overlap:
|
|
456
494
|
if last_analyzed < end_dt and base_period is None:
|
|
457
495
|
logger.warn(
|
|
458
496
|
"Setting the start time to last_analyzed since the original start time precedes "
|
|
@@ -499,7 +537,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
499
537
|
],
|
|
500
538
|
endpoint_id: str,
|
|
501
539
|
application_name: str,
|
|
502
|
-
|
|
540
|
+
fail_on_overlap: bool,
|
|
503
541
|
) -> Iterator[tuple[Optional[datetime], Optional[datetime]]]:
|
|
504
542
|
if start is None or end is None:
|
|
505
543
|
# A single window based on the `sample_data` input - see `_handler`.
|
|
@@ -516,7 +554,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
516
554
|
end_dt=end_dt,
|
|
517
555
|
base_period=base_period,
|
|
518
556
|
application_name=application_name,
|
|
519
|
-
|
|
557
|
+
fail_on_overlap=fail_on_overlap,
|
|
520
558
|
)
|
|
521
559
|
|
|
522
560
|
if base_period is None:
|
|
@@ -589,6 +627,42 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
589
627
|
"""
|
|
590
628
|
return f"{handler_to_class}::{cls._handler.__name__}"
|
|
591
629
|
|
|
630
|
+
@classmethod
|
|
631
|
+
def _determine_job_name(
|
|
632
|
+
cls,
|
|
633
|
+
*,
|
|
634
|
+
func_name: Optional[str],
|
|
635
|
+
class_handler: Optional[str],
|
|
636
|
+
handler_to_class: str,
|
|
637
|
+
) -> str:
|
|
638
|
+
"""
|
|
639
|
+
Determine the batch app's job name. This name is used also as the application name,
|
|
640
|
+
which is retrieved in `_get_application_name`.
|
|
641
|
+
"""
|
|
642
|
+
if func_name:
|
|
643
|
+
job_name = func_name
|
|
644
|
+
else:
|
|
645
|
+
if not class_handler:
|
|
646
|
+
class_name = cls.__name__
|
|
647
|
+
else:
|
|
648
|
+
class_name = handler_to_class.split(".")[-1].split("::")[0]
|
|
649
|
+
|
|
650
|
+
job_name = mlrun.utils.normalize_name(class_name, verbose=False)
|
|
651
|
+
|
|
652
|
+
if not mm_constants.APP_NAME_REGEX.fullmatch(job_name):
|
|
653
|
+
raise mlrun.errors.MLRunValueError(
|
|
654
|
+
"The function name does not comply with the required pattern "
|
|
655
|
+
f"`{mm_constants.APP_NAME_REGEX.pattern}`. "
|
|
656
|
+
"Please choose another `func_name`."
|
|
657
|
+
)
|
|
658
|
+
if not job_name.endswith(mm_constants._RESERVED_EVALUATE_FUNCTION_SUFFIX):
|
|
659
|
+
job_name += mm_constants._RESERVED_EVALUATE_FUNCTION_SUFFIX
|
|
660
|
+
mlrun.utils.logger.info(
|
|
661
|
+
'Changing function name - adding `"-batch"` suffix', func_name=job_name
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
return job_name
|
|
665
|
+
|
|
592
666
|
@classmethod
|
|
593
667
|
def to_job(
|
|
594
668
|
cls,
|
|
@@ -628,7 +702,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
628
702
|
* ``end``, ``datetime``
|
|
629
703
|
* ``base_period``, ``int``
|
|
630
704
|
* ``write_output``, ``bool``
|
|
631
|
-
* ``
|
|
705
|
+
* ``fail_on_overlap``, ``bool``
|
|
632
706
|
|
|
633
707
|
For Git sources, add the source archive to the returned job and change the handler:
|
|
634
708
|
|
|
@@ -647,7 +721,10 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
647
721
|
:py:class:`~mlrun.model_monitoring.applications.ModelMonitoringApplicationBase`,
|
|
648
722
|
is used.
|
|
649
723
|
:param func_path: The path to the function. If ``None``, the current notebook is used.
|
|
650
|
-
:param func_name: The name of the function. If
|
|
724
|
+
:param func_name: The name of the function. If ``None``, the normalized class name is used
|
|
725
|
+
(:py:meth:`mlrun.utils.helpers.normalize_name`).
|
|
726
|
+
A ``"-batch"`` suffix is guaranteed to be added if not already there.
|
|
727
|
+
The function name is also used as the application name to use for the results.
|
|
651
728
|
:param tag: Tag for the function.
|
|
652
729
|
:param image: Docker image to run the job on (when running remotely).
|
|
653
730
|
:param with_repo: Whether to clone the current repo to the build source.
|
|
@@ -668,12 +745,11 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
668
745
|
handler_to_class = class_handler or cls.__name__
|
|
669
746
|
handler = cls.get_job_handler(handler_to_class)
|
|
670
747
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
job_name = func_name if func_name else class_name
|
|
748
|
+
job_name = cls._determine_job_name(
|
|
749
|
+
func_name=func_name,
|
|
750
|
+
class_handler=class_handler,
|
|
751
|
+
handler_to_class=handler_to_class,
|
|
752
|
+
)
|
|
677
753
|
|
|
678
754
|
job = cast(
|
|
679
755
|
mlrun.runtimes.KubejobRuntime,
|
|
@@ -712,7 +788,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
712
788
|
end: Optional[datetime] = None,
|
|
713
789
|
base_period: Optional[int] = None,
|
|
714
790
|
write_output: bool = False,
|
|
715
|
-
|
|
791
|
+
fail_on_overlap: bool = True,
|
|
716
792
|
stream_profile: Optional[ds_profile.DatastoreProfile] = None,
|
|
717
793
|
) -> "mlrun.RunObject":
|
|
718
794
|
"""
|
|
@@ -724,7 +800,10 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
724
800
|
data to the application.
|
|
725
801
|
|
|
726
802
|
:param func_path: The path to the function. If ``None``, the current notebook is used.
|
|
727
|
-
:param func_name: The name of the function. If
|
|
803
|
+
:param func_name: The name of the function. If ``None``, the normalized class name is used
|
|
804
|
+
(:py:meth:`mlrun.utils.helpers.normalize_name`).
|
|
805
|
+
A ``"-batch"`` suffix is guaranteed to be added if not already there.
|
|
806
|
+
The function name is also used as the application name to use for the results.
|
|
728
807
|
:param tag: Tag for the function.
|
|
729
808
|
:param run_local: Whether to run the function locally or remotely.
|
|
730
809
|
:param auto_build: Whether to auto build the function.
|
|
@@ -777,11 +856,11 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
777
856
|
:param write_output: Whether to write the results and metrics to the time-series DB. Can be ``True`` only
|
|
778
857
|
if ``endpoints`` are passed.
|
|
779
858
|
Note: the model monitoring infrastructure must be up for the writing to work.
|
|
780
|
-
:param
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
859
|
+
:param fail_on_overlap: Relevant only when ``write_output=True``. When ``True``, and the
|
|
860
|
+
requested ``start`` time precedes the ``end`` time of a previous run that also
|
|
861
|
+
wrote to the database - an error is raised.
|
|
862
|
+
If ``False``, when the previously described situation occurs, the relevant time
|
|
863
|
+
window is cut so that it starts at the earliest possible time after ``start``.
|
|
785
864
|
:param stream_profile: The stream datastore profile. It should be provided only when running locally and
|
|
786
865
|
writing the outputs to the database (i.e., when both ``run_local`` and
|
|
787
866
|
``write_output`` are set to ``True``).
|
|
@@ -821,7 +900,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
821
900
|
params["end"] = end.isoformat() if isinstance(end, datetime) else end
|
|
822
901
|
params["base_period"] = base_period
|
|
823
902
|
params["write_output"] = write_output
|
|
824
|
-
params["
|
|
903
|
+
params["fail_on_overlap"] = fail_on_overlap
|
|
825
904
|
if stream_profile:
|
|
826
905
|
if not run_local:
|
|
827
906
|
raise mlrun.errors.MLRunValueError(
|
|
@@ -14,16 +14,13 @@
|
|
|
14
14
|
|
|
15
15
|
import dataclasses
|
|
16
16
|
import json
|
|
17
|
-
import re
|
|
18
17
|
from abc import ABC, abstractmethod
|
|
19
18
|
|
|
20
19
|
from pydantic.v1 import validator
|
|
21
20
|
from pydantic.v1.dataclasses import dataclass
|
|
22
21
|
|
|
23
|
-
import mlrun.common.helpers
|
|
24
|
-
import mlrun.common.model_monitoring.helpers
|
|
25
22
|
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
26
|
-
import mlrun.
|
|
23
|
+
import mlrun.errors
|
|
27
24
|
from mlrun.utils import logger
|
|
28
25
|
|
|
29
26
|
_RESULT_EXTRA_DATA_MAX_SIZE = 998
|
|
@@ -33,10 +30,10 @@ class _ModelMonitoringApplicationDataRes(ABC):
|
|
|
33
30
|
name: str
|
|
34
31
|
|
|
35
32
|
def __post_init__(self):
|
|
36
|
-
|
|
37
|
-
if not re.fullmatch(pat, self.name):
|
|
33
|
+
if not mm_constants.RESULT_NAME_REGEX.fullmatch(self.name):
|
|
38
34
|
raise mlrun.errors.MLRunValueError(
|
|
39
|
-
|
|
35
|
+
"The application result or metric name must comply with the regex "
|
|
36
|
+
f"`{mm_constants.RESULT_NAME_REGEX.pattern}`"
|
|
40
37
|
)
|
|
41
38
|
|
|
42
39
|
@abstractmethod
|
mlrun/run.py
CHANGED
|
@@ -365,7 +365,7 @@ def import_function(url="", secrets=None, db="", project=None, new_name=None):
|
|
|
365
365
|
def import_function_to_dict(url, secrets=None):
|
|
366
366
|
"""Load function spec from local/remote YAML file"""
|
|
367
367
|
obj = get_object(url, secrets)
|
|
368
|
-
runtime = yaml.
|
|
368
|
+
runtime = yaml.safe_load(obj)
|
|
369
369
|
remote = "://" in url
|
|
370
370
|
|
|
371
371
|
code = get_in(runtime, "spec.build.functionSourceCode")
|
|
@@ -1184,11 +1184,13 @@ def get_model_provider(
|
|
|
1184
1184
|
raise_missing_schema_exception=True,
|
|
1185
1185
|
) -> ModelProvider:
|
|
1186
1186
|
"""get mlrun dataitem object (from path/url)"""
|
|
1187
|
-
|
|
1187
|
+
# without caching secrets
|
|
1188
|
+
store_manager.set(db=db)
|
|
1188
1189
|
return store_manager.model_provider_object(
|
|
1189
1190
|
url=url,
|
|
1190
1191
|
default_invoke_kwargs=default_invoke_kwargs,
|
|
1191
1192
|
raise_missing_schema_exception=raise_missing_schema_exception,
|
|
1193
|
+
secrets=secrets,
|
|
1192
1194
|
)
|
|
1193
1195
|
|
|
1194
1196
|
|
mlrun/runtimes/base.py
CHANGED
|
@@ -447,14 +447,17 @@ class BaseRuntime(ModelObj):
|
|
|
447
447
|
:return: Dictionary with all the variables that could be parsed
|
|
448
448
|
"""
|
|
449
449
|
runtime_env = {
|
|
450
|
-
|
|
450
|
+
mlrun_constants.MLRUN_ACTIVE_PROJECT: self.metadata.project
|
|
451
|
+
or config.active_project
|
|
451
452
|
}
|
|
452
453
|
if runobj:
|
|
453
454
|
runtime_env["MLRUN_EXEC_CONFIG"] = runobj.to_json(
|
|
454
455
|
exclude_notifications_params=True
|
|
455
456
|
)
|
|
456
457
|
if runobj.metadata.project:
|
|
457
|
-
runtime_env[
|
|
458
|
+
runtime_env[mlrun_constants.MLRUN_ACTIVE_PROJECT] = (
|
|
459
|
+
runobj.metadata.project
|
|
460
|
+
)
|
|
458
461
|
if runobj.spec.verbose:
|
|
459
462
|
runtime_env["MLRUN_LOG_LEVEL"] = "DEBUG"
|
|
460
463
|
if config.httpdb.api_url:
|