mlrun 1.10.0rc23__py3-none-any.whl → 1.10.0rc25__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 (32) hide show
  1. mlrun/common/schemas/hub.py +14 -0
  2. mlrun/common/schemas/model_monitoring/constants.py +1 -0
  3. mlrun/common/schemas/model_monitoring/model_endpoints.py +10 -1
  4. mlrun/config.py +5 -1
  5. mlrun/datastore/azure_blob.py +66 -43
  6. mlrun/datastore/datastore_profile.py +8 -2
  7. mlrun/datastore/model_provider/huggingface_provider.py +118 -30
  8. mlrun/datastore/model_provider/model_provider.py +66 -8
  9. mlrun/datastore/model_provider/openai_provider.py +149 -47
  10. mlrun/db/base.py +1 -1
  11. mlrun/db/httpdb.py +6 -4
  12. mlrun/db/nopdb.py +1 -0
  13. mlrun/model_monitoring/api.py +2 -2
  14. mlrun/model_monitoring/applications/base.py +22 -10
  15. mlrun/model_monitoring/applications/context.py +1 -4
  16. mlrun/model_monitoring/controller.py +10 -2
  17. mlrun/model_monitoring/db/_schedules.py +2 -4
  18. mlrun/platforms/iguazio.py +7 -3
  19. mlrun/projects/project.py +28 -24
  20. mlrun/runtimes/nuclio/__init__.py +1 -0
  21. mlrun/runtimes/nuclio/application/application.py +26 -2
  22. mlrun/runtimes/nuclio/function.py +10 -0
  23. mlrun/runtimes/nuclio/serving.py +4 -0
  24. mlrun/runtimes/utils.py +22 -5
  25. mlrun/serving/server.py +25 -14
  26. mlrun/utils/version/version.json +2 -2
  27. {mlrun-1.10.0rc23.dist-info → mlrun-1.10.0rc25.dist-info}/METADATA +23 -22
  28. {mlrun-1.10.0rc23.dist-info → mlrun-1.10.0rc25.dist-info}/RECORD +32 -32
  29. {mlrun-1.10.0rc23.dist-info → mlrun-1.10.0rc25.dist-info}/WHEEL +0 -0
  30. {mlrun-1.10.0rc23.dist-info → mlrun-1.10.0rc25.dist-info}/entry_points.txt +0 -0
  31. {mlrun-1.10.0rc23.dist-info → mlrun-1.10.0rc25.dist-info}/licenses/LICENSE +0 -0
  32. {mlrun-1.10.0rc23.dist-info → mlrun-1.10.0rc25.dist-info}/top_level.txt +0 -0
@@ -67,7 +67,6 @@ class OpenAIProvider(ModelProvider):
67
67
  default_invoke_kwargs=default_invoke_kwargs,
68
68
  )
69
69
  self.options = self.get_client_options()
70
- self.load_client()
71
70
 
72
71
  @classmethod
73
72
  def _import_response_class(cls) -> None:
@@ -81,8 +80,12 @@ class OpenAIProvider(ModelProvider):
81
80
  @staticmethod
82
81
  def _extract_string_output(response: "ChatCompletion") -> str:
83
82
  """
84
- Extracts the first generated string from Hugging Face pipeline output,
85
- regardless of whether it's plain text-generation or chat-style output.
83
+ Extracts the text content of the first choice from an OpenAI ChatCompletion response.
84
+ Only supports responses with a single choice. Raises an error if multiple choices exist.
85
+
86
+ :param response: The ChatCompletion response from OpenAI.
87
+ :return: The text content of the first message in the response.
88
+ :raises MLRunInvalidArgumentError: If the response contains more than one choice.
86
89
  """
87
90
  if len(response.choices) != 1:
88
91
  raise mlrun.errors.MLRunInvalidArgumentError(
@@ -98,25 +101,58 @@ class OpenAIProvider(ModelProvider):
98
101
  subpath = ""
99
102
  return endpoint, subpath
100
103
 
101
- def load_client(self) -> None:
104
+ @property
105
+ def client(self) -> Any:
102
106
  """
103
- Initializes the OpenAI SDK client using the provided options.
107
+ Lazily return the synchronous OpenAI client.
108
+
109
+ If the client has not been initialized yet, it will be created
110
+ by calling `load_client`.
111
+ """
112
+ self.load_client()
113
+ return self._client
104
114
 
105
- This method imports the `OpenAI` class from the `openai` package, instantiates
106
- a client with the given keyword arguments (`self.options`), and assigns it to
107
- `self._client` and `self._async_client`.
115
+ def load_client(self) -> None:
116
+ """
117
+ Lazily initialize the synchronous OpenAI client.
108
118
 
109
- Raises:
110
- ImportError: If the `openai` package is not installed.
119
+ The client is created only if it does not already exist.
120
+ Raises ImportError if the openai package is not installed.
111
121
  """
122
+ if self._client:
123
+ return
112
124
  try:
113
- from openai import OpenAI, AsyncOpenAI # noqa
125
+ from openai import OpenAI # noqa
114
126
 
115
127
  self._client = OpenAI(**self.options)
116
- self._async_client = AsyncOpenAI(**self.options)
117
128
  except ImportError as exc:
118
129
  raise ImportError("openai package is not installed") from exc
119
130
 
131
+ def load_async_client(self) -> None:
132
+ """
133
+ Lazily initialize the asynchronous OpenAI client.
134
+
135
+ The client is created only if it does not already exist.
136
+ Raises ImportError if the openai package is not installed.
137
+ """
138
+ if not self._async_client:
139
+ try:
140
+ from openai import AsyncOpenAI # noqa
141
+
142
+ self._async_client = AsyncOpenAI(**self.options)
143
+ except ImportError as exc:
144
+ raise ImportError("openai package is not installed") from exc
145
+
146
+ @property
147
+ def async_client(self) -> Any:
148
+ """
149
+ Return the asynchronous OpenAI client, creating it on first access.
150
+
151
+ The client is lazily initialized via `load_async_client`.
152
+ """
153
+ self.load_async_client()
154
+ return self._async_client
155
+
120
156
  def get_client_options(self) -> dict:
121
157
  res = dict(
122
158
  api_key=self._get_secret_or_env("OPENAI_API_KEY"),
@@ -132,25 +168,37 @@ class OpenAIProvider(ModelProvider):
132
168
  self, operation: Optional[Callable] = None, **invoke_kwargs
133
169
  ) -> Union["ChatCompletion", "BaseModel"]:
134
170
  """
135
- OpenAI-specific implementation of `ModelProvider.custom_invoke`.
171
+ Invokes a model operation from the OpenAI client with the given keyword arguments.
172
+
173
+ This method provides flexibility to either:
174
+ - Call a specific OpenAI client operation (e.g., `client.images.generate`).
175
+ - Default to `chat.completions.create` when no operation is provided.
136
176
 
137
- Invokes an OpenAI model operation using the sync client. For full details, see
138
- `ModelProvider.custom_invoke`.
177
+ The operation must be a callable that accepts keyword arguments. If the callable
178
+ does not accept a `model` parameter, it will be omitted from the call.
139
179
 
140
180
  Example:
141
181
  ```python
142
- result = openai_model_provider.invoke(
182
+ result = openai_model_provider.custom_invoke(
143
183
  openai_model_provider.client.images.generate,
144
184
  prompt="A futuristic cityscape at sunset",
145
185
  n=1,
146
186
  size="1024x1024",
147
187
  )
148
188
  ```
149
- :param operation: Same as ModelProvider.custom_invoke.
150
- :param invoke_kwargs: Same as ModelProvider.custom_invoke.
151
- :return: Same as ModelProvider.custom_invoke.
152
189
 
190
+ :param operation: A callable representing the OpenAI operation to invoke.
191
+ If not provided, defaults to `client.chat.completions.create`.
192
+
193
+ :param invoke_kwargs: Additional keyword arguments to pass to the operation.
194
+ These are merged with `default_invoke_kwargs` and may
195
+ include parameters such as `temperature`, `max_tokens`,
196
+ or `messages`.
197
+
198
+ :return: The full response returned by the operation, typically
199
+ an OpenAI `ChatCompletion` or other OpenAI SDK model.
153
200
  """
201
+
154
202
  invoke_kwargs = self.get_invoke_kwargs(invoke_kwargs)
155
203
  model_kwargs = {"model": invoke_kwargs.pop("model", None) or self.model}
156
204
 
@@ -171,24 +219,35 @@ class OpenAIProvider(ModelProvider):
171
219
  **invoke_kwargs,
172
220
  ) -> Union["ChatCompletion", "BaseModel"]:
173
221
  """
174
- OpenAI-specific implementation of `ModelProvider.async_custom_invoke`.
222
+ Asynchronously invokes a model operation from the OpenAI client with the given keyword arguments.
175
223
 
176
- Invokes an OpenAI model operation using the async client. For full details, see
177
- `ModelProvider.async_custom_invoke`.
224
+ This method provides flexibility to either:
225
+ - Call a specific async OpenAI client operation (e.g., `async_client.images.generate`).
226
+ - Default to `chat.completions.create` when no operation is provided.
227
+
228
+ The operation must be an async callable that accepts keyword arguments.
229
+ If the callable does not accept a `model` parameter, it will be omitted from the call.
178
230
 
179
231
  Example:
180
- ```python
181
- result = openai_model_provider.invoke(
232
+ ```python
233
+ result = await openai_model_provider.async_custom_invoke(
182
234
  openai_model_provider.async_client.images.generate,
183
235
  prompt="A futuristic cityscape at sunset",
184
236
  n=1,
185
237
  size="1024x1024",
186
238
  )
187
- ```
239
+ ```
188
240
 
189
- :param operation: Same as ModelProvider.async_custom_invoke.
190
- :param invoke_kwargs: Same as ModelProvider.async_custom_invoke.
191
- :return: Same as ModelProvider.async_custom_invoke.
241
+ :param operation: An async callable representing the OpenAI operation to invoke.
242
+ If not provided, defaults to `async_client.chat.completions.create`.
243
+
244
+ :param invoke_kwargs: Additional keyword arguments to pass to the operation.
245
+ These are merged with `default_invoke_kwargs` and may
246
+ include parameters such as `temperature`, `max_tokens`,
247
+ or `messages`.
248
+
249
+ :return: The full response returned by the awaited operation,
250
+ typically an OpenAI `ChatCompletion` or other OpenAI SDK model.
192
251
 
193
252
  """
194
253
  invoke_kwargs = self.get_invoke_kwargs(invoke_kwargs)
@@ -217,10 +276,10 @@ class OpenAIProvider(ModelProvider):
217
276
  if invoke_response_format == InvokeResponseFormat.STRING:
218
277
  return str_response
219
278
  if invoke_response_format == InvokeResponseFormat.USAGE:
220
- stats = response.to_dict()["usage"]
279
+ usage = response.to_dict()["usage"]
221
280
  response = {
222
281
  UsageResponseKeys.ANSWER: str_response,
223
- UsageResponseKeys.USAGE: stats,
282
+ UsageResponseKeys.USAGE: usage,
224
283
  }
225
284
  return response
226
285
 
@@ -233,27 +292,42 @@ class OpenAIProvider(ModelProvider):
233
292
  """
234
293
  OpenAI-specific implementation of `ModelProvider.invoke`.
235
294
  Invokes an OpenAI model operation using the synchronous client.
236
- For full details, see `ModelProvider.invoke`.
237
295
 
238
296
  :param messages:
239
- Same as `ModelProvider.invoke`.
297
+ A list of dictionaries representing the conversation history or input messages.
298
+ Each dictionary should follow the format::
299
+ {
300
+ "role": "system" | "user" | "assistant",
301
+ "content": "Message content as a string",
302
+ }
240
303
 
241
- :param invoke_response_format: InvokeResponseFormat
304
+ Example:
305
+
306
+ .. code-block:: json
307
+
308
+ [
309
+ {"role": "system", "content": "You are a helpful assistant."},
310
+ {"role": "user", "content": "What is the capital of France?"}
311
+ ]
312
+
313
+ Defaults to None if no messages are provided.
314
+
315
+ :param invoke_response_format:
242
316
  Specifies the format of the returned response. Options:
243
317
 
244
318
  - "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:
319
+ - "usage": Combines the generated text with metadata (e.g., token usage), returning a dictionary::
246
320
 
247
- .. code-block:: json
248
- {
249
- "answer": "<generated_text>",
250
- "stats": <ChatCompletion>.to_dict()["usage"]
251
- }
321
+ .. code-block:: json
322
+ {
323
+ "answer": "<generated_text>",
324
+ "usage": <ChatCompletion>.to_dict()["usage"]
325
+ }
252
326
 
253
327
  - "full": Returns the full OpenAI `ChatCompletion` object.
254
328
 
255
329
  :param invoke_kwargs:
256
- Additional keyword arguments passed to the OpenAI client. Same as in `ModelProvider.invoke`.
330
+ Additional keyword arguments passed to the OpenAI client.
257
331
 
258
332
  :return:
259
333
  A string, dictionary, or `ChatCompletion` object, depending on `invoke_response_format`.
@@ -274,18 +348,46 @@ class OpenAIProvider(ModelProvider):
274
348
  ) -> Union[str, "ChatCompletion", dict]:
275
349
  """
276
350
  OpenAI-specific implementation of `ModelProvider.async_invoke`.
277
- Invokes an OpenAI model operation using the async client.
278
- For full details, see `ModelProvider.async_invoke` and `OpenAIProvider.invoke`.
351
+ Invokes an OpenAI model operation using the asynchronous client.
352
+
353
+ :param messages:
354
+ A list of dictionaries representing the conversation history or input messages.
355
+ Each dictionary should follow the format::
356
+ {
357
+ "role": "system" | "user" | "assistant",
358
+ "content": "Message content as a string",
359
+ }
360
+
361
+ Example:
362
+
363
+ .. code-block:: json
364
+
365
+ [
366
+ {"role": "system", "content": "You are a helpful assistant."},
367
+ {"role": "user", "content": "What is the capital of France?"}
368
+ ]
369
+
370
+ Defaults to None if no messages are provided.
371
+
372
+ :param invoke_response_format:
373
+ Specifies the format of the returned response. Options:
374
+
375
+ - "string": Returns only the generated text content, taken from a single response.
376
+ - "usage": Combines the generated text with metadata (e.g., token usage), returning a dictionary::
279
377
 
280
- :param messages: Same as `OpenAIProvider.invoke`.
378
+ .. code-block:: json
379
+ {
380
+ "answer": "<generated_text>",
381
+ "usage": <ChatCompletion>.to_dict()["usage"]
382
+ }
281
383
 
282
- :param invoke_response_format: InvokeResponseFormat
283
- Same as `OpenAIProvider.invoke`.
384
+ - "full": Returns the full OpenAI `ChatCompletion` object.
284
385
 
285
386
  :param invoke_kwargs:
286
- Same as `OpenAIProvider.invoke`.
287
- :returns Same as `ModelProvider.async_invoke`.
387
+ Additional keyword arguments passed to the OpenAI client.
288
388
 
389
+ :return:
390
+ A string, dictionary, or `ChatCompletion` object, depending on `invoke_response_format`.
289
391
  """
290
392
  response = await self.async_custom_invoke(messages=messages, **invoke_kwargs)
291
393
  return self._response_handler(
mlrun/db/base.py CHANGED
@@ -722,7 +722,7 @@ class RunDBInterface(ABC):
722
722
  tsdb_metrics: bool = False,
723
723
  metric_list: Optional[list[str]] = None,
724
724
  top_level: bool = False,
725
- mode: Optional[mlrun.common.schemas.EndpointMode] = None,
725
+ modes: Optional[list[mm_constants.EndpointMode]] = None,
726
726
  uids: Optional[list[str]] = None,
727
727
  latest_only: bool = False,
728
728
  ) -> mlrun.common.schemas.ModelEndpointList:
mlrun/db/httpdb.py CHANGED
@@ -3770,7 +3770,7 @@ class HTTPRunDB(RunDBInterface):
3770
3770
  tsdb_metrics: bool = False,
3771
3771
  metric_list: Optional[list[str]] = None,
3772
3772
  top_level: bool = False,
3773
- mode: mm_constants.EndpointMode = None,
3773
+ modes: Optional[list[mm_constants.EndpointMode]] = None,
3774
3774
  uids: Optional[list[str]] = None,
3775
3775
  latest_only: bool = False,
3776
3776
  ) -> mlrun.common.schemas.ModelEndpointList:
@@ -3791,8 +3791,8 @@ class HTTPRunDB(RunDBInterface):
3791
3791
  If tsdb_metrics=False, this parameter will be ignored and no tsdb metrics
3792
3792
  will be included.
3793
3793
  :param top_level: Whether to return only top level model endpoints.
3794
- :param mode: Specifies the mode of the model endpoint. Can be "real-time" (0), "batch" (1), or
3795
- both if set to None.
3794
+ :param modes: Specifies the modes of the model endpoints. Can be "real-time" (0), "batch" (1),
3795
+ "batch_legacy" (2). If set to None, all are included.
3796
3796
  :param uids: A list of unique ids to filter by.
3797
3797
  :param latest_only: Whether to return only the latest model endpoint version.
3798
3798
  :return: A list of model endpoints.
@@ -3801,6 +3801,8 @@ class HTTPRunDB(RunDBInterface):
3801
3801
  labels = self._parse_labels(labels)
3802
3802
  if names and isinstance(names, str):
3803
3803
  names = [names]
3804
+ if isinstance(modes, mm_constants.EndpointMode):
3805
+ modes = [modes]
3804
3806
  response = self.api_call(
3805
3807
  method=mlrun.common.types.HTTPMethod.GET,
3806
3808
  path=path,
@@ -3816,7 +3818,7 @@ class HTTPRunDB(RunDBInterface):
3816
3818
  "tsdb-metrics": tsdb_metrics,
3817
3819
  "metric": metric_list,
3818
3820
  "top-level": top_level,
3819
- "mode": mode,
3821
+ "mode": modes,
3820
3822
  "uid": uids,
3821
3823
  "latest-only": latest_only,
3822
3824
  },
mlrun/db/nopdb.py CHANGED
@@ -626,6 +626,7 @@ class NopDB(RunDBInterface):
626
626
  tsdb_metrics: bool = False,
627
627
  metric_list: Optional[list[str]] = None,
628
628
  top_level: bool = False,
629
+ modes: Optional[list[mm_constants.EndpointMode]] = None,
629
630
  uids: Optional[list[str]] = None,
630
631
  latest_only: bool = False,
631
632
  ) -> mlrun.common.schemas.ModelEndpointList:
@@ -350,8 +350,8 @@ def _generate_model_endpoint(
350
350
  project=project,
351
351
  name=model_endpoint_name,
352
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,
353
+ # Due to backwards compatibility, this endpoint will be created as a legacy batch endpoint.
354
+ mode=mlrun.common.schemas.model_monitoring.EndpointMode.BATCH_LEGACY,
355
355
  ),
356
356
  spec=mlrun.common.schemas.ModelEndpointSpec(
357
357
  function_name=function_name or "function",
@@ -18,7 +18,7 @@ from abc import ABC, abstractmethod
18
18
  from collections import defaultdict
19
19
  from collections.abc import Iterator
20
20
  from contextlib import contextmanager, nullcontext
21
- from datetime import datetime, timedelta
21
+ from datetime import datetime, timedelta, timezone
22
22
  from typing import Any, Literal, Optional, Union, cast
23
23
 
24
24
  import pandas as pd
@@ -591,6 +591,16 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
591
591
  start_dt = datetime.fromisoformat(start)
592
592
  end_dt = datetime.fromisoformat(end)
593
593
 
594
+ # If `start_dt` and `end_dt` do not include time zone information - change them to UTC
595
+ if (start_dt.tzinfo is None) and (end_dt.tzinfo is None):
596
+ start_dt = start_dt.replace(tzinfo=timezone.utc)
597
+ end_dt = end_dt.replace(tzinfo=timezone.utc)
598
+ elif (start_dt.tzinfo is None) or (end_dt.tzinfo is None):
599
+ raise mlrun.errors.MLRunValueError(
600
+ "The start and end times must either both include time zone information or both be naive (no time "
601
+ f"zone). Asserting the above failed, aborting the evaluate request: start={start}, end={end}."
602
+ )
603
+
594
604
  if existing_data_handling != ExistingDataHandling.delete_all:
595
605
  start_dt = cls._validate_monotonically_increasing_data(
596
606
  application_schedules=application_schedules,
@@ -841,7 +851,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
841
851
  :py:meth:`~mlrun.model_monitoring.applications.ModelMonitoringApplicationBase.do_tracking`
842
852
  model monitoring logic as a :py:class:`~mlrun.runtimes.KubejobRuntime`, which is an MLRun function.
843
853
 
844
- This function has default values for all of its arguments. You should be change them when you want to pass
854
+ This function has default values for all of its arguments. You should change them when you want to pass
845
855
  data to the application.
846
856
 
847
857
  :param func_path: The path to the function. If ``None``, the current notebook is used.
@@ -858,6 +868,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
858
868
  :param reference_data: Pandas data-frame or :py:class:`~mlrun.artifacts.dataset.DatasetArtifact` URI as
859
869
  the reference dataset.
860
870
  When set, its statistics override the model endpoint's feature statistics.
871
+ You do not need to have a model endpoint to use this option.
861
872
  :param image: Docker image to run the job on (when running remotely).
862
873
  :param with_repo: Whether to clone the current repo to the build source.
863
874
  :param class_handler: The relative path to the class, useful when using Git sources or code from images.
@@ -878,8 +889,9 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
878
889
  :param start: The start time of the endpoint's data, not included.
879
890
  If you want the model endpoint's data at ``start`` included, you need to subtract a
880
891
  small ``datetime.timedelta`` from it.
881
- Make sure to include the time zone when constructing `datetime.datetime` objects
882
- manually.
892
+ Make sure to include the time zone when constructing ``datetime.datetime`` objects
893
+ manually. When both ``start`` and ``end`` times do not include a time zone, they will
894
+ be treated as UTC.
883
895
  :param end: The end time of the endpoint's data, included.
884
896
  Please note: when ``start`` and ``end`` are set, they create a left-open time interval
885
897
  ("window") :math:`(\\operatorname{start}, \\operatorname{end}]` that excludes the
@@ -902,13 +914,13 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
902
914
  if ``endpoints`` are passed.
903
915
  Note: the model monitoring infrastructure must be up for the writing to work.
904
916
  :param existing_data_handling:
905
- How to handle the existing application data for the model endpoints when writing the
906
- new data. Relevant only when ``write_output=True``. The default is
907
- ``"fail_on_overlap"``. The options are:
917
+ How to handle the existing application data for the model endpoints when writing
918
+ new data whose requested ``start`` time precedes the ``end`` time of a previous run
919
+ that also wrote to the database. Relevant only when ``write_output=True``.
920
+ The options are:
908
921
 
909
- - ``"fail_on_overlap"``: when the requested ``start`` time precedes the
910
- ``end`` time of a previous run that also wrote to the database - an error is raised.
911
- - ``"skip_overlap"``: when the previously described situation occurs, the relevant
922
+ - ``"fail_on_overlap"``: Default. An error is raised.
923
+ - ``"skip_overlap"``: the overlapping data is ignored and the
912
924
  time window is cut so that it starts at the earliest possible time after ``start``.
913
925
  - ``"delete_all"``: delete all the data that was written by the application to the
914
926
  model endpoints, regardless of the time window, and write the new data.
@@ -24,15 +24,12 @@ import mlrun.common.schemas.model_monitoring.constants as mm_constants
24
24
  import mlrun.errors
25
25
  import mlrun.feature_store as fstore
26
26
  import mlrun.feature_store.feature_set as fs
27
- import mlrun.features
28
27
  import mlrun.serving
29
28
  import mlrun.utils
30
29
  from mlrun.artifacts import Artifact, DatasetArtifact, ModelArtifact, get_model
31
30
  from mlrun.common.model_monitoring.helpers import FeatureStats
32
31
  from mlrun.common.schemas import ModelEndpoint
33
- from mlrun.model_monitoring.helpers import (
34
- calculate_inputs_statistics,
35
- )
32
+ from mlrun.model_monitoring.helpers import calculate_inputs_statistics
36
33
 
37
34
 
38
35
  class _ArtifactsLogger(Protocol):
@@ -801,7 +801,11 @@ class MonitoringApplicationController:
801
801
  logger.info("Starting monitoring controller chief")
802
802
  applications_names = []
803
803
  endpoints = self.project_obj.list_model_endpoints(
804
- tsdb_metrics=False, mode=mm_constants.EndpointMode.REAL_TIME
804
+ tsdb_metrics=False,
805
+ modes=[
806
+ mm_constants.EndpointMode.REAL_TIME,
807
+ mm_constants.EndpointMode.BATCH_LEGACY,
808
+ ],
805
809
  ).endpoints
806
810
  last_request_dict = self.tsdb_connector.get_last_request(
807
811
  endpoint_ids=[mep.metadata.uid for mep in endpoints]
@@ -859,7 +863,11 @@ class MonitoringApplicationController:
859
863
  for endpoint in endpoints:
860
864
  last_request = last_request_dict.get(endpoint.metadata.uid, None)
861
865
  if isinstance(last_request, float):
862
- last_request = pd.to_datetime(last_request, unit="ms", utc=True)
866
+ last_request = datetime.datetime.fromtimestamp(
867
+ last_request, tz=datetime.timezone.utc
868
+ )
869
+ elif isinstance(last_request, pd.Timestamp):
870
+ last_request = last_request.to_pydatetime()
863
871
  endpoint.status.last_request = (
864
872
  last_request or endpoint.status.last_request
865
873
  )
@@ -16,7 +16,7 @@ import json
16
16
  import sys
17
17
  from abc import ABC, abstractmethod
18
18
  from contextlib import AbstractContextManager
19
- from datetime import datetime, timezone
19
+ from datetime import datetime
20
20
  from types import TracebackType
21
21
  from typing import TYPE_CHECKING, Final, Optional
22
22
 
@@ -281,9 +281,7 @@ class ModelMonitoringSchedulesFileApplication(ModelMonitoringSchedulesFileBase):
281
281
  self, endpoint_uid: str, last_analyzed: datetime
282
282
  ) -> None:
283
283
  self._check_open_schedules()
284
- self._schedules[endpoint_uid] = last_analyzed.astimezone(
285
- timezone.utc
286
- ).isoformat()
284
+ self._schedules[endpoint_uid] = last_analyzed.isoformat()
287
285
 
288
286
  def delete_endpoints_last_analyzed(self, endpoint_uids: list[str]) -> None:
289
287
  self._check_open_schedules()
@@ -96,7 +96,11 @@ class OutputStream:
96
96
  if access_key:
97
97
  v3io_client_kwargs["access_key"] = access_key
98
98
 
99
- self._v3io_client = v3io.dataplane.Client(**v3io_client_kwargs)
99
+ if not mock:
100
+ self._v3io_client = v3io.dataplane.Client(**v3io_client_kwargs)
101
+ else:
102
+ self._v3io_client = None
103
+
100
104
  self._container, self._stream_path = split_path(stream_path)
101
105
  self._shards = shards
102
106
  self._retention_in_hours = retention_in_hours
@@ -105,7 +109,7 @@ class OutputStream:
105
109
  self._mock = mock
106
110
  self._mock_queue = []
107
111
 
108
- def create_stream(self):
112
+ def create_stream(self) -> None:
109
113
  # this import creates an import loop via the utils module, so putting it in execution path
110
114
  from mlrun.utils.helpers import logger
111
115
 
@@ -210,7 +214,7 @@ class KafkaOutputStream:
210
214
  self._initialized = False
211
215
 
212
216
  def _lazy_init(self):
213
- if self._initialized:
217
+ if self._initialized or self._mock:
214
218
  return
215
219
 
216
220
  import kafka
mlrun/projects/project.py CHANGED
@@ -2749,16 +2749,18 @@ class MlrunProject(ModelObj):
2749
2749
  | Creating a function with non project source is done by specifying a module ``handler`` and on the
2750
2750
  returned function set the source with ``function.with_source_archive(<source>)``.
2751
2751
 
2752
- Support URL prefixes:
2752
+ Supported URL prefixes:
2753
2753
 
2754
- | Object (s3://, v3io://, ..)
2755
- | MLRun DB e.g. db://project/func:ver
2756
- | Functions hub/market: e.g. hub://auto-trainer:master
2754
+ - Object: s3://, v3io://, etc.
2755
+ - MLRun DB: e.g db://project/func:ver
2756
+ - Function hub/market: e.g. hub://auto-trainer:master
2757
2757
 
2758
2758
  Examples::
2759
2759
 
2760
2760
  proj.set_function(func_object)
2761
- proj.set_function("http://.../mynb.ipynb", "train")
2761
+ proj.set_function(
2762
+ "http://.../mynb.ipynb", "train", kind="job", image="mlrun/mlrun"
2763
+ )
2762
2764
  proj.set_function("./func.yaml")
2763
2765
  proj.set_function("hub://get_toy_data", "getdata")
2764
2766
 
@@ -2785,18 +2787,6 @@ class MlrunProject(ModelObj):
2785
2787
  # By providing a path to a pip requirements file
2786
2788
  proj.set_function("my.py", requirements="requirements.txt")
2787
2789
 
2788
- One of the most important parameters is 'kind', used to specify the chosen runtime. The options are:
2789
- - local: execute a local python or shell script
2790
- - job: insert the code into a Kubernetes pod and execute it
2791
- - nuclio: insert the code into a real-time serverless nuclio function
2792
- - serving: insert code into orchestrated nuclio function(s) forming a DAG
2793
- - dask: run the specified python code / script as Dask Distributed job
2794
- - mpijob: run distributed Horovod jobs over the MPI job operator
2795
- - spark: run distributed Spark job using Spark Kubernetes Operator
2796
- - remote-spark: run distributed Spark job on remote Spark service
2797
- - databricks: run code on Databricks cluster (python scripts, Spark etc.)
2798
- - application: run a long living application (e.g. a web server, UI, etc.)
2799
-
2800
2790
  Learn more about :doc:`../../concepts/functions-overview`.
2801
2791
 
2802
2792
  :param func: Function object or spec/code url, None refers to current Notebook
@@ -2804,8 +2794,20 @@ class MlrunProject(ModelObj):
2804
2794
  Versions (e.g. myfunc:v1). If the `tag` parameter is provided, the tag in the name
2805
2795
  must match the tag parameter.
2806
2796
  Specifying a tag in the name will update the project's tagged function (myfunc:v1)
2807
- :param kind: Runtime kind e.g. job, nuclio, spark, dask, mpijob
2808
- Default: job
2797
+ :param kind: Default: job. One of
2798
+
2799
+ - local: execute a local python or shell script
2800
+ - job: insert the code into a Kubernetes pod and execute it
2801
+ - nuclio: insert the code into a real-time serverless nuclio function
2802
+ - serving: insert code into orchestrated nuclio function(s) forming a DAG
2803
+ - dask: run the specified python code / script as Dask Distributed job
2804
+ - mpijob: run distributed Horovod jobs over the MPI job operator
2805
+ - spark: run distributed Spark job using Spark Kubernetes Operator
2806
+ - remote-spark: run distributed Spark job on remote Spark service
2807
+ - databricks: run code on Databricks cluster (python scripts, Spark etc.)
2808
+ - application: run a long living application (e.g. a web server, UI, etc.)
2809
+ - handler: execute a python handler (used automatically in notebooks or for debug)
2810
+
2809
2811
  :param image: Docker image to be used, can also be specified in the function object/yaml
2810
2812
  :param handler: Default function handler to invoke (can only be set with .py/.ipynb files)
2811
2813
  :param with_repo: Add (clone) the current repo to the build source - use when the function code is in
@@ -3944,7 +3946,9 @@ class MlrunProject(ModelObj):
3944
3946
  start: Optional[datetime.datetime] = None,
3945
3947
  end: Optional[datetime.datetime] = None,
3946
3948
  top_level: bool = False,
3947
- mode: Optional[mlrun.common.schemas.EndpointMode] = None,
3949
+ modes: Optional[
3950
+ Union[mm_constants.EndpointMode, list[mm_constants.EndpointMode]]
3951
+ ] = None,
3948
3952
  uids: Optional[list[str]] = None,
3949
3953
  latest_only: bool = False,
3950
3954
  tsdb_metrics: bool = False,
@@ -3960,7 +3964,7 @@ class MlrunProject(ModelObj):
3960
3964
  5) function_tag
3961
3965
  6) labels
3962
3966
  7) top level
3963
- 8) mode
3967
+ 8) modes
3964
3968
  9) uids
3965
3969
  10) start and end time, corresponding to the `created` field.
3966
3970
  By default, when no filters are applied, all available endpoints for the given project will be listed.
@@ -3982,8 +3986,8 @@ class MlrunProject(ModelObj):
3982
3986
  :param start: The start time to filter by.Corresponding to the `created` field.
3983
3987
  :param end: The end time to filter by. Corresponding to the `created` field.
3984
3988
  :param top_level: If true will return only routers and endpoint that are NOT children of any router.
3985
- :param mode: Specifies the mode of the model endpoint. Can be "real-time" (0), "batch" (1), or
3986
- both if set to None.
3989
+ :param modes: Specifies the mode of the model endpoint. Can be "real-time" (0), "batch" (1),
3990
+ "batch_legacy" (2). If set to None, all are included.
3987
3991
  :param uids: If passed will return a list `ModelEndpoint` object with uid in uids.
3988
3992
  :param tsdb_metrics: When True, the time series metrics will be added to the output
3989
3993
  of the resulting.
@@ -4005,7 +4009,7 @@ class MlrunProject(ModelObj):
4005
4009
  start=start,
4006
4010
  end=end,
4007
4011
  top_level=top_level,
4008
- mode=mode,
4012
+ modes=modes,
4009
4013
  uids=uids,
4010
4014
  latest_only=latest_only,
4011
4015
  tsdb_metrics=tsdb_metrics,