mlrun 1.10.0rc18__py3-none-any.whl → 1.10.0rc20__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.

Files changed (38) hide show
  1. mlrun/__init__.py +21 -2
  2. mlrun/common/constants.py +1 -0
  3. mlrun/common/schemas/function.py +10 -0
  4. mlrun/common/schemas/model_monitoring/constants.py +4 -11
  5. mlrun/common/schemas/model_monitoring/model_endpoints.py +2 -0
  6. mlrun/datastore/__init__.py +9 -1
  7. mlrun/datastore/model_provider/huggingface_provider.py +114 -26
  8. mlrun/datastore/model_provider/model_provider.py +144 -70
  9. mlrun/datastore/model_provider/openai_provider.py +95 -37
  10. mlrun/db/base.py +0 -19
  11. mlrun/db/httpdb.py +10 -46
  12. mlrun/db/nopdb.py +0 -10
  13. mlrun/launcher/base.py +13 -6
  14. mlrun/model_monitoring/api.py +43 -22
  15. mlrun/model_monitoring/applications/base.py +1 -1
  16. mlrun/model_monitoring/controller.py +112 -38
  17. mlrun/model_monitoring/db/_schedules.py +13 -9
  18. mlrun/model_monitoring/stream_processing.py +16 -12
  19. mlrun/platforms/__init__.py +3 -2
  20. mlrun/projects/project.py +2 -2
  21. mlrun/run.py +1 -1
  22. mlrun/runtimes/base.py +5 -2
  23. mlrun/runtimes/daskjob.py +1 -0
  24. mlrun/runtimes/nuclio/application/application.py +84 -5
  25. mlrun/runtimes/nuclio/function.py +3 -1
  26. mlrun/serving/server.py +24 -0
  27. mlrun/serving/states.py +80 -30
  28. mlrun/serving/system_steps.py +60 -36
  29. mlrun/utils/helpers.py +37 -13
  30. mlrun/utils/notifications/notification_pusher.py +1 -1
  31. mlrun/utils/version/version.json +2 -2
  32. {mlrun-1.10.0rc18.dist-info → mlrun-1.10.0rc20.dist-info}/METADATA +4 -4
  33. {mlrun-1.10.0rc18.dist-info → mlrun-1.10.0rc20.dist-info}/RECORD +37 -38
  34. mlrun/api/schemas/__init__.py +0 -259
  35. {mlrun-1.10.0rc18.dist-info → mlrun-1.10.0rc20.dist-info}/WHEEL +0 -0
  36. {mlrun-1.10.0rc18.dist-info → mlrun-1.10.0rc20.dist-info}/entry_points.txt +0 -0
  37. {mlrun-1.10.0rc18.dist-info → mlrun-1.10.0rc20.dist-info}/licenses/LICENSE +0 -0
  38. {mlrun-1.10.0rc18.dist-info → mlrun-1.10.0rc20.dist-info}/top_level.txt +0 -0
@@ -13,13 +13,19 @@
13
13
  # limitations under the License.
14
14
  import inspect
15
15
  from collections.abc import Awaitable
16
- from typing import Callable, Optional, TypeVar, Union
16
+ from typing import TYPE_CHECKING, Any, Callable, Optional, Union
17
17
 
18
18
  import mlrun
19
- from mlrun.datastore.model_provider.model_provider import ModelProvider
19
+ from mlrun.datastore.model_provider.model_provider import (
20
+ InvokeResponseFormat,
21
+ ModelProvider,
22
+ UsageResponseKeys,
23
+ )
20
24
  from mlrun.datastore.utils import accepts_param
21
25
 
22
- T = TypeVar("T")
26
+ if TYPE_CHECKING:
27
+ from openai._models import BaseModel # noqa
28
+ from openai.types.chat.chat_completion import ChatCompletion
23
29
 
24
30
 
25
31
  class OpenAIProvider(ModelProvider):
@@ -36,6 +42,7 @@ class OpenAIProvider(ModelProvider):
36
42
  """
37
43
 
38
44
  support_async = True
45
+ response_class = None
39
46
 
40
47
  def __init__(
41
48
  self,
@@ -62,6 +69,27 @@ class OpenAIProvider(ModelProvider):
62
69
  self.options = self.get_client_options()
63
70
  self.load_client()
64
71
 
72
+ @classmethod
73
+ def _import_response_class(cls) -> None:
74
+ if not cls.response_class:
75
+ try:
76
+ from openai.types.chat.chat_completion import ChatCompletion
77
+ except ImportError as exc:
78
+ raise ImportError("openai package is not installed") from exc
79
+ cls.response_class = ChatCompletion
80
+
81
+ @staticmethod
82
+ def _extract_string_output(response: "ChatCompletion") -> str:
83
+ """
84
+ Extracts the first generated string from Hugging Face pipeline output,
85
+ regardless of whether it's plain text-generation or chat-style output.
86
+ """
87
+ if len(response.choices) != 1:
88
+ raise mlrun.errors.MLRunInvalidArgumentError(
89
+ "OpenAIProvider: extracting string from response is only supported for single-response outputs"
90
+ )
91
+ return response.choices[0].message.content
92
+
65
93
  @classmethod
66
94
  def parse_endpoint_and_path(cls, endpoint, subpath) -> (str, str):
67
95
  if endpoint and subpath:
@@ -101,8 +129,8 @@ class OpenAIProvider(ModelProvider):
101
129
  return self._sanitize_options(res)
102
130
 
103
131
  def custom_invoke(
104
- self, operation: Optional[Callable[..., T]] = None, **invoke_kwargs
105
- ) -> Optional[T]:
132
+ self, operation: Optional[Callable] = None, **invoke_kwargs
133
+ ) -> Union["ChatCompletion", "BaseModel"]:
106
134
  """
107
135
  OpenAI-specific implementation of `ModelProvider.custom_invoke`.
108
136
 
@@ -139,9 +167,9 @@ class OpenAIProvider(ModelProvider):
139
167
 
140
168
  async def async_custom_invoke(
141
169
  self,
142
- operation: Optional[Callable[..., Awaitable[T]]] = None,
170
+ operation: Optional[Callable[..., Awaitable[Any]]] = None,
143
171
  **invoke_kwargs,
144
- ) -> Optional[T]:
172
+ ) -> Union["ChatCompletion", "BaseModel"]:
145
173
  """
146
174
  OpenAI-specific implementation of `ModelProvider.async_custom_invoke`.
147
175
 
@@ -178,60 +206,90 @@ class OpenAIProvider(ModelProvider):
178
206
  **invoke_kwargs, **model_kwargs
179
207
  )
180
208
 
209
+ def _response_handler(
210
+ self,
211
+ response: "ChatCompletion",
212
+ invoke_response_format: InvokeResponseFormat = InvokeResponseFormat.FULL,
213
+ **kwargs,
214
+ ) -> ["ChatCompletion", str, dict[str, Any]]:
215
+ if InvokeResponseFormat.is_str_response(invoke_response_format.value):
216
+ str_response = self._extract_string_output(response)
217
+ if invoke_response_format == InvokeResponseFormat.STRING:
218
+ return str_response
219
+ if invoke_response_format == InvokeResponseFormat.USAGE:
220
+ stats = response.to_dict()["usage"]
221
+ response = {
222
+ UsageResponseKeys.ANSWER: str_response,
223
+ UsageResponseKeys.USAGE: stats,
224
+ }
225
+ return response
226
+
181
227
  def invoke(
182
228
  self,
183
- messages: Optional[list[dict]] = None,
184
- as_str: bool = False,
229
+ messages: list[dict],
230
+ invoke_response_format: InvokeResponseFormat = InvokeResponseFormat.FULL,
185
231
  **invoke_kwargs,
186
- ) -> Optional[Union[str, T]]:
232
+ ) -> Union[dict[str, Any], str, "ChatCompletion"]:
187
233
  """
188
234
  OpenAI-specific implementation of `ModelProvider.invoke`.
189
- Invokes an OpenAI model operation using the sync client.
235
+ Invokes an OpenAI model operation using the synchronous client.
190
236
  For full details, see `ModelProvider.invoke`.
191
237
 
192
- :param messages: Same as ModelProvider.invoke.
238
+ :param messages:
239
+ Same as `ModelProvider.invoke`.
193
240
 
194
- :param as_str: bool
195
- If `True`, returns only the main content of the first response
196
- (`response.choices[0].message.content`).
197
- If `False`, returns the full response object, whose type depends on
198
- the specific OpenAI SDK operation used (e.g., chat completion, completion, etc.).
241
+ :param invoke_response_format: InvokeResponseFormat
242
+ Specifies the format of the returned response. Options:
243
+
244
+ - "string": Returns only the generated text content, taken from a single response.
245
+ - "stats": Combines the generated text with metadata (e.g., token usage), returning a dictionary:
246
+
247
+ .. code-block:: json
248
+ {
249
+ "answer": "<generated_text>",
250
+ "stats": <ChatCompletion>.to_dict()["usage"]
251
+ }
252
+
253
+ - "full": Returns the full OpenAI `ChatCompletion` object.
199
254
 
200
255
  :param invoke_kwargs:
201
- Same as ModelProvider.invoke.
202
- :return: Same as ModelProvider.invoke.
256
+ Additional keyword arguments passed to the OpenAI client. Same as in `ModelProvider.invoke`.
203
257
 
258
+ :return:
259
+ A string, dictionary, or `ChatCompletion` object, depending on `invoke_response_format`.
204
260
  """
261
+
205
262
  response = self.custom_invoke(messages=messages, **invoke_kwargs)
206
- if as_str:
207
- return response.choices[0].message.content
208
- return response
263
+ return self._response_handler(
264
+ messages=messages,
265
+ invoke_response_format=invoke_response_format,
266
+ response=response,
267
+ )
209
268
 
210
269
  async def async_invoke(
211
270
  self,
212
- messages: Optional[list[dict]] = None,
213
- as_str: bool = False,
271
+ messages: list[dict],
272
+ invoke_response_format=InvokeResponseFormat.FULL,
214
273
  **invoke_kwargs,
215
- ) -> str:
274
+ ) -> Union[str, "ChatCompletion", dict]:
216
275
  """
217
276
  OpenAI-specific implementation of `ModelProvider.async_invoke`.
218
277
  Invokes an OpenAI model operation using the async client.
219
- For full details, see `ModelProvider.async_invoke`.
278
+ For full details, see `ModelProvider.async_invoke` and `OpenAIProvider.invoke`.
220
279
 
221
- :param messages: Same as ModelProvider.async_invoke.
280
+ :param messages: Same as `OpenAIProvider.invoke`.
222
281
 
223
- :param as_str: bool
224
- If `True`, returns only the main content of the first response
225
- (`response.choices[0].message.content`).
226
- If `False`, returns the full awaited response object, whose type depends on
227
- the specific OpenAI SDK operation used (e.g., chat completion, completion, etc.).
282
+ :param invoke_response_format: InvokeResponseFormat
283
+ Same as `OpenAIProvider.invoke`.
228
284
 
229
285
  :param invoke_kwargs:
230
- Same as ModelProvider.async_invoke.
231
- :returns Same as ModelProvider.async_invoke.
286
+ Same as `OpenAIProvider.invoke`.
287
+ :returns Same as `ModelProvider.async_invoke`.
232
288
 
233
289
  """
234
290
  response = await self.async_custom_invoke(messages=messages, **invoke_kwargs)
235
- if as_str:
236
- return response.choices[0].message.content
237
- return response
291
+ return self._response_handler(
292
+ messages=messages,
293
+ invoke_response_format=invoke_response_format,
294
+ response=response,
295
+ )
mlrun/db/base.py CHANGED
@@ -16,8 +16,6 @@ import datetime
16
16
  from abc import ABC, abstractmethod
17
17
  from typing import Literal, Optional, Union
18
18
 
19
- from deprecated import deprecated
20
-
21
19
  import mlrun.alerts
22
20
  import mlrun.common
23
21
  import mlrun.common.formatters
@@ -445,23 +443,6 @@ class RunDBInterface(ABC):
445
443
  ) -> dict:
446
444
  pass
447
445
 
448
- # TODO: remove in 1.10.0
449
- @deprecated(
450
- version="1.7.0",
451
- reason="'list_features' will be removed in 1.10.0, use 'list_features_v2' instead",
452
- category=FutureWarning,
453
- )
454
- @abstractmethod
455
- def list_features(
456
- self,
457
- project: str,
458
- name: Optional[str] = None,
459
- tag: Optional[str] = None,
460
- entities: Optional[list[str]] = None,
461
- labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
462
- ) -> mlrun.common.schemas.FeaturesOutput:
463
- pass
464
-
465
446
  @abstractmethod
466
447
  def list_features_v2(
467
448
  self,
mlrun/db/httpdb.py CHANGED
@@ -24,6 +24,7 @@ from datetime import datetime, timedelta
24
24
  from os import environ, path, remove
25
25
  from typing import Literal, Optional, Union
26
26
  from urllib.parse import urlparse
27
+ from uuid import UUID
27
28
 
28
29
  import pydantic.v1
29
30
  import requests
@@ -2554,50 +2555,6 @@ class HTTPRunDB(RunDBInterface):
2554
2555
  resp = self.api_call("GET", path, error_message)
2555
2556
  return FeatureSet.from_dict(resp.json())
2556
2557
 
2557
- def list_features(
2558
- self,
2559
- project: Optional[str] = None,
2560
- name: Optional[str] = None,
2561
- tag: Optional[str] = None,
2562
- entities: Optional[list[str]] = None,
2563
- labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
2564
- ) -> list[dict]:
2565
- """List feature-sets which contain specific features. This function may return multiple versions of the same
2566
- feature-set if a specific tag is not requested. Note that the various filters of this function actually
2567
- refer to the feature-set object containing the features, not to the features themselves.
2568
-
2569
- :param project: Project which contains these features.
2570
- :param name: Name of the feature to look for. The name is used in a like query, and is not case-sensitive. For
2571
- example, looking for ``feat`` will return features which are named ``MyFeature`` as well as ``defeat``.
2572
- :param tag: Return feature-sets which contain the features looked for, and are tagged with the specific tag.
2573
- :param entities: Return only feature-sets which contain an entity whose name is contained in this list.
2574
- :param labels: Filter feature-sets by label key-value pairs or key existence. This can be provided as:
2575
- - A dictionary in the format `{"label": "value"}` to match specific label key-value pairs,
2576
- or `{"label": None}` to check for key existence.
2577
- - A list of strings formatted as `"label=value"` to match specific label key-value pairs,
2578
- or just `"label"` for key existence.
2579
- - A comma-separated string formatted as `"label1=value1,label2"` to match entities with
2580
- the specified key-value pairs or key existence.
2581
- :returns: A list of mapping from feature to a digest of the feature-set, which contains the feature-set
2582
- meta-data. Multiple entries may be returned for any specific feature due to multiple tags or versions
2583
- of the feature-set.
2584
- """
2585
-
2586
- project = project or config.active_project
2587
- labels = self._parse_labels(labels)
2588
- params = {
2589
- "name": name,
2590
- "tag": tag,
2591
- "entity": entities or [],
2592
- "label": labels,
2593
- }
2594
-
2595
- path = f"projects/{project}/features"
2596
-
2597
- error_message = f"Failed listing features, project: {project}, query: {params}"
2598
- resp = self.api_call("GET", path, error_message, params=params)
2599
- return resp.json()["features"]
2600
-
2601
2558
  def list_features_v2(
2602
2559
  self,
2603
2560
  project: Optional[str] = None,
@@ -3834,8 +3791,8 @@ class HTTPRunDB(RunDBInterface):
3834
3791
  If tsdb_metrics=False, this parameter will be ignored and no tsdb metrics
3835
3792
  will be included.
3836
3793
  :param top_level: Whether to return only top level model endpoints.
3837
- :param mode: Specifies the mode of the model endpoint. Can be "real-time", "batch", or both if set
3838
- to None.
3794
+ :param mode: Specifies the mode of the model endpoint. Can be "real-time" (0), "batch" (1), or
3795
+ both if set to None.
3839
3796
  :param uids: A list of unique ids to filter by.
3840
3797
  :param latest_only: Whether to return only the latest model endpoint version.
3841
3798
  :return: A list of model endpoints.
@@ -3968,6 +3925,13 @@ class HTTPRunDB(RunDBInterface):
3968
3925
  raise MLRunInvalidArgumentError(
3969
3926
  "Either endpoint_uid or function_name and function_tag must be provided"
3970
3927
  )
3928
+ if uid:
3929
+ try:
3930
+ UUID(uid)
3931
+ except (ValueError, TypeError):
3932
+ raise MLRunInvalidArgumentError(
3933
+ "endpoint_id must be a valid UUID string"
3934
+ )
3971
3935
 
3972
3936
  def update_model_monitoring_controller(
3973
3937
  self,
mlrun/db/nopdb.py CHANGED
@@ -376,16 +376,6 @@ class NopDB(RunDBInterface):
376
376
  ) -> dict:
377
377
  pass
378
378
 
379
- def list_features(
380
- self,
381
- project: str,
382
- name: Optional[str] = None,
383
- tag: Optional[str] = None,
384
- entities: Optional[list[str]] = None,
385
- labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
386
- ) -> mlrun.common.schemas.FeaturesOutput:
387
- pass
388
-
389
379
  def list_features_v2(
390
380
  self,
391
381
  project: str,
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",
@@ -268,12 +281,6 @@ class BaseLauncher(abc.ABC):
268
281
 
269
282
  run.metadata.name = mlrun.utils.normalize_name(
270
283
  name=name or run.metadata.name or def_name,
271
- # if name or runspec.metadata.name are set then it means that is user defined name and we want to warn the
272
- # user that the passed name needs to be set without underscore, if its not user defined but rather enriched
273
- # from the handler(function) name then we replace the underscore without warning the user.
274
- # most of the time handlers will have `_` in the handler name (python convention is to separate function
275
- # words with `_`), therefore we don't want to be noisy when normalizing the run name
276
- verbose=bool(name or run.metadata.name),
277
284
  )
278
285
  mlrun.utils.verify_field_regex(
279
286
  "run.metadata.name", run.metadata.name, mlrun.utils.regex.run_name
@@ -18,6 +18,7 @@ from datetime import datetime
18
18
 
19
19
  import numpy as np
20
20
  import pandas as pd
21
+ from deprecated import deprecated
21
22
 
22
23
  import mlrun.common.schemas.model_monitoring.constants as mm_constants
23
24
  import mlrun.datastore.base
@@ -45,6 +46,14 @@ DatasetType = typing.Union[
45
46
  ]
46
47
 
47
48
 
49
+ # TODO: Remove this in 1.12.0
50
+ @deprecated(
51
+ version="1.10.0",
52
+ reason="This function is deprecated and will be removed in 1.12. You can generate a model endpoint by either "
53
+ "deploying a monitored serving function as a real-time service or running it as an offline job. "
54
+ "To retrieve model endpoints, use `project.list_model_endpoints()`",
55
+ category=FutureWarning,
56
+ )
48
57
  def get_or_create_model_endpoint(
49
58
  project: str,
50
59
  model_endpoint_name: str,
@@ -67,8 +76,8 @@ def get_or_create_model_endpoint(
67
76
  :param model_endpoint_name: If a new model endpoint is created, the model endpoint name will be presented
68
77
  under this endpoint (applicable only to new endpoint_id).
69
78
  :param model_path: The model store path (applicable only to new endpoint_id).
70
- :param endpoint_id: Model endpoint unique ID. If not exist in DB, will generate a new record based
71
- on the provided `endpoint_id`.
79
+ :param endpoint_id: Model endpoint unique ID. If not exist in DB, will generate a new record with a
80
+ newly generated ID.
72
81
  :param function_name: If a new model endpoint is created, use this function name.
73
82
  :param function_tag: If a new model endpoint is created, use this function tag.
74
83
  :param context: MLRun context. If `function_name` not provided, use the context to generate the
@@ -91,25 +100,26 @@ def get_or_create_model_endpoint(
91
100
  function_name = FunctionURI.from_string(
92
101
  context.to_dict()["spec"]["function"]
93
102
  ).function
94
- try:
95
- model_endpoint = db_session.get_model_endpoint(
96
- project=project,
97
- name=model_endpoint_name,
98
- endpoint_id=endpoint_id,
99
- function_name=function_name,
100
- function_tag=function_tag or "latest",
101
- feature_analysis=feature_analysis,
102
- )
103
- # If other fields provided, validate that they are correspond to the existing model endpoint data
104
- _model_endpoint_validations(
105
- model_endpoint=model_endpoint,
106
- model_path=model_path,
107
- sample_set_statistics=sample_set_statistics,
108
- )
103
+ if endpoint_id or function_name:
104
+ try:
105
+ model_endpoint = db_session.get_model_endpoint(
106
+ project=project,
107
+ name=model_endpoint_name,
108
+ endpoint_id=endpoint_id,
109
+ function_name=function_name,
110
+ function_tag=function_tag or "latest",
111
+ feature_analysis=feature_analysis,
112
+ )
113
+ # If other fields provided, validate that they are correspond to the existing model endpoint data
114
+ _model_endpoint_validations(
115
+ model_endpoint=model_endpoint,
116
+ model_path=model_path,
117
+ sample_set_statistics=sample_set_statistics,
118
+ )
109
119
 
110
- except (mlrun.errors.MLRunNotFoundError, mlrun.errors.MLRunInvalidArgumentError):
111
- # Create a new model endpoint with the provided details
112
- pass
120
+ except mlrun.errors.MLRunNotFoundError:
121
+ # Create a new model endpoint with the provided details
122
+ pass
113
123
  if not model_endpoint:
114
124
  model_endpoint = _generate_model_endpoint(
115
125
  project=project,
@@ -123,6 +133,13 @@ def get_or_create_model_endpoint(
123
133
  return model_endpoint
124
134
 
125
135
 
136
+ # TODO: Remove this in 1.12.0
137
+ @deprecated(
138
+ version="1.10.0",
139
+ reason="This function is deprecated and will be removed in 1.12. "
140
+ "Instead, run a monitored serving function as a job",
141
+ category=FutureWarning,
142
+ )
126
143
  def record_results(
127
144
  project: str,
128
145
  model_path: str,
@@ -144,8 +161,8 @@ def record_results(
144
161
  :param model_path: The model Store path.
145
162
  :param model_endpoint_name: If a new model endpoint is generated, the model endpoint name will be presented
146
163
  under this endpoint.
147
- :param endpoint_id: Model endpoint unique ID. If not exist in DB, will generate a new record based
148
- on the provided `endpoint_id`.
164
+ :param endpoint_id: Model endpoint unique ID. If not exist in DB, will generate a new record with a
165
+ newly generated ID.
149
166
  :param function_name: If a new model endpoint is created, use this function name for generating the
150
167
  function URI.
151
168
  :param context: MLRun context. Note that the context is required generating the model endpoint.
@@ -236,6 +253,7 @@ def _model_endpoint_validations(
236
253
  key=model_obj.key,
237
254
  iter=model_obj.iter,
238
255
  tree=model_obj.tree,
256
+ uid=model_obj.uid,
239
257
  )
240
258
 
241
259
  # Enrich the uri schema with the store prefix
@@ -325,12 +343,15 @@ def _generate_model_endpoint(
325
343
 
326
344
  :return `mlrun.common.schemas.ModelEndpoint` object.
327
345
  """
346
+
328
347
  current_time = datetime_now()
329
348
  model_endpoint = mlrun.common.schemas.ModelEndpoint(
330
349
  metadata=mlrun.common.schemas.ModelEndpointMetadata(
331
350
  project=project,
332
351
  name=model_endpoint_name,
333
352
  endpoint_type=mlrun.common.schemas.model_monitoring.EndpointType.BATCH_EP,
353
+ # Due to backwards compatibility, old batch model endpoint will be analyzed as real time endpoint
354
+ mode=mlrun.common.schemas.model_monitoring.EndpointMode.REAL_TIME,
334
355
  ),
335
356
  spec=mlrun.common.schemas.ModelEndpointSpec(
336
357
  function_name=function_name or "function",
@@ -647,7 +647,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
647
647
  else:
648
648
  class_name = handler_to_class.split(".")[-1].split("::")[0]
649
649
 
650
- job_name = mlrun.utils.normalize_name(class_name, verbose=False)
650
+ job_name = mlrun.utils.normalize_name(class_name)
651
651
 
652
652
  if not mm_constants.APP_NAME_REGEX.fullmatch(job_name):
653
653
  raise mlrun.errors.MLRunValueError(