mlrun 1.10.0rc13__py3-none-any.whl → 1.10.0rc42__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 (107) hide show
  1. mlrun/__init__.py +22 -2
  2. mlrun/artifacts/base.py +0 -31
  3. mlrun/artifacts/document.py +6 -1
  4. mlrun/artifacts/llm_prompt.py +123 -25
  5. mlrun/artifacts/manager.py +0 -5
  6. mlrun/artifacts/model.py +3 -3
  7. mlrun/common/constants.py +10 -1
  8. mlrun/common/formatters/artifact.py +1 -0
  9. mlrun/common/model_monitoring/helpers.py +86 -0
  10. mlrun/common/schemas/__init__.py +3 -0
  11. mlrun/common/schemas/auth.py +2 -0
  12. mlrun/common/schemas/function.py +10 -0
  13. mlrun/common/schemas/hub.py +30 -18
  14. mlrun/common/schemas/model_monitoring/__init__.py +3 -0
  15. mlrun/common/schemas/model_monitoring/constants.py +30 -6
  16. mlrun/common/schemas/model_monitoring/functions.py +14 -5
  17. mlrun/common/schemas/model_monitoring/model_endpoints.py +21 -0
  18. mlrun/common/schemas/pipeline.py +1 -1
  19. mlrun/common/schemas/serving.py +3 -0
  20. mlrun/common/schemas/workflow.py +3 -1
  21. mlrun/common/secrets.py +22 -1
  22. mlrun/config.py +33 -11
  23. mlrun/datastore/__init__.py +11 -3
  24. mlrun/datastore/azure_blob.py +162 -47
  25. mlrun/datastore/datastore.py +9 -4
  26. mlrun/datastore/datastore_profile.py +61 -5
  27. mlrun/datastore/model_provider/huggingface_provider.py +363 -0
  28. mlrun/datastore/model_provider/mock_model_provider.py +87 -0
  29. mlrun/datastore/model_provider/model_provider.py +230 -65
  30. mlrun/datastore/model_provider/openai_provider.py +295 -42
  31. mlrun/datastore/s3.py +24 -2
  32. mlrun/datastore/storeytargets.py +2 -3
  33. mlrun/datastore/utils.py +15 -3
  34. mlrun/db/base.py +47 -19
  35. mlrun/db/httpdb.py +120 -56
  36. mlrun/db/nopdb.py +38 -10
  37. mlrun/execution.py +70 -19
  38. mlrun/hub/__init__.py +15 -0
  39. mlrun/hub/module.py +181 -0
  40. mlrun/k8s_utils.py +105 -16
  41. mlrun/launcher/base.py +13 -6
  42. mlrun/launcher/local.py +15 -0
  43. mlrun/model.py +24 -3
  44. mlrun/model_monitoring/__init__.py +1 -0
  45. mlrun/model_monitoring/api.py +66 -27
  46. mlrun/model_monitoring/applications/__init__.py +1 -1
  47. mlrun/model_monitoring/applications/base.py +509 -117
  48. mlrun/model_monitoring/applications/context.py +2 -4
  49. mlrun/model_monitoring/applications/results.py +4 -7
  50. mlrun/model_monitoring/controller.py +239 -101
  51. mlrun/model_monitoring/db/_schedules.py +116 -33
  52. mlrun/model_monitoring/db/_stats.py +4 -3
  53. mlrun/model_monitoring/db/tsdb/base.py +100 -9
  54. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +11 -6
  55. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +191 -50
  56. mlrun/model_monitoring/db/tsdb/tdengine/writer_graph_steps.py +51 -0
  57. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +17 -4
  58. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +259 -40
  59. mlrun/model_monitoring/helpers.py +54 -9
  60. mlrun/model_monitoring/stream_processing.py +45 -14
  61. mlrun/model_monitoring/writer.py +220 -1
  62. mlrun/platforms/__init__.py +3 -2
  63. mlrun/platforms/iguazio.py +7 -3
  64. mlrun/projects/operations.py +6 -1
  65. mlrun/projects/pipelines.py +46 -26
  66. mlrun/projects/project.py +166 -58
  67. mlrun/run.py +94 -17
  68. mlrun/runtimes/__init__.py +18 -0
  69. mlrun/runtimes/base.py +14 -6
  70. mlrun/runtimes/daskjob.py +7 -0
  71. mlrun/runtimes/local.py +5 -2
  72. mlrun/runtimes/mounts.py +20 -2
  73. mlrun/runtimes/mpijob/abstract.py +6 -0
  74. mlrun/runtimes/mpijob/v1.py +6 -0
  75. mlrun/runtimes/nuclio/__init__.py +1 -0
  76. mlrun/runtimes/nuclio/application/application.py +149 -17
  77. mlrun/runtimes/nuclio/function.py +76 -27
  78. mlrun/runtimes/nuclio/serving.py +97 -15
  79. mlrun/runtimes/pod.py +234 -21
  80. mlrun/runtimes/remotesparkjob.py +6 -0
  81. mlrun/runtimes/sparkjob/spark3job.py +6 -0
  82. mlrun/runtimes/utils.py +49 -11
  83. mlrun/secrets.py +54 -13
  84. mlrun/serving/__init__.py +2 -0
  85. mlrun/serving/remote.py +79 -6
  86. mlrun/serving/routers.py +23 -41
  87. mlrun/serving/server.py +320 -80
  88. mlrun/serving/states.py +725 -157
  89. mlrun/serving/steps.py +62 -0
  90. mlrun/serving/system_steps.py +200 -119
  91. mlrun/serving/v2_serving.py +9 -10
  92. mlrun/utils/helpers.py +288 -88
  93. mlrun/utils/logger.py +3 -1
  94. mlrun/utils/notifications/notification/base.py +18 -0
  95. mlrun/utils/notifications/notification/git.py +2 -4
  96. mlrun/utils/notifications/notification/slack.py +2 -4
  97. mlrun/utils/notifications/notification/webhook.py +2 -5
  98. mlrun/utils/notifications/notification_pusher.py +1 -1
  99. mlrun/utils/retryer.py +15 -2
  100. mlrun/utils/version/version.json +2 -2
  101. {mlrun-1.10.0rc13.dist-info → mlrun-1.10.0rc42.dist-info}/METADATA +45 -51
  102. {mlrun-1.10.0rc13.dist-info → mlrun-1.10.0rc42.dist-info}/RECORD +106 -101
  103. mlrun/api/schemas/__init__.py +0 -259
  104. {mlrun-1.10.0rc13.dist-info → mlrun-1.10.0rc42.dist-info}/WHEEL +0 -0
  105. {mlrun-1.10.0rc13.dist-info → mlrun-1.10.0rc42.dist-info}/entry_points.txt +0 -0
  106. {mlrun-1.10.0rc13.dist-info → mlrun-1.10.0rc42.dist-info}/licenses/LICENSE +0 -0
  107. {mlrun-1.10.0rc13.dist-info → mlrun-1.10.0rc42.dist-info}/top_level.txt +0 -0
@@ -11,13 +11,21 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
-
15
- from typing import Callable, Optional, TypeVar, Union
14
+ import inspect
15
+ from collections.abc import Awaitable
16
+ from typing import TYPE_CHECKING, Any, Callable, Optional, Union
16
17
 
17
18
  import mlrun
18
- 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
+ )
24
+ from mlrun.datastore.utils import accepts_param
19
25
 
20
- T = TypeVar("T")
26
+ if TYPE_CHECKING:
27
+ from openai._models import BaseModel # noqa
28
+ from openai.types.chat.chat_completion import ChatCompletion
21
29
 
22
30
 
23
31
  class OpenAIProvider(ModelProvider):
@@ -33,6 +41,9 @@ class OpenAIProvider(ModelProvider):
33
41
  operations tailored to the OpenAI API.
34
42
  """
35
43
 
44
+ support_async = True
45
+ response_class = None
46
+
36
47
  def __init__(
37
48
  self,
38
49
  parent,
@@ -56,7 +67,31 @@ class OpenAIProvider(ModelProvider):
56
67
  default_invoke_kwargs=default_invoke_kwargs,
57
68
  )
58
69
  self.options = self.get_client_options()
59
- self.load_client()
70
+
71
+ @classmethod
72
+ def _import_response_class(cls) -> None:
73
+ if not cls.response_class:
74
+ try:
75
+ from openai.types.chat.chat_completion import ChatCompletion
76
+ except ImportError as exc:
77
+ raise ImportError("openai package is not installed") from exc
78
+ cls.response_class = ChatCompletion
79
+
80
+ @staticmethod
81
+ def _extract_string_output(response: "ChatCompletion") -> str:
82
+ """
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.
89
+ """
90
+ if len(response.choices) != 1:
91
+ raise mlrun.errors.MLRunInvalidArgumentError(
92
+ "OpenAIProvider: extracting string from response is only supported for single-response outputs"
93
+ )
94
+ return response.choices[0].message.content
60
95
 
61
96
  @classmethod
62
97
  def parse_endpoint_and_path(cls, endpoint, subpath) -> (str, str):
@@ -67,32 +102,58 @@ class OpenAIProvider(ModelProvider):
67
102
  return endpoint, subpath
68
103
 
69
104
  @property
70
- def model(self):
71
- return self.endpoint
72
-
73
- def load_client(self) -> None:
105
+ def client(self) -> Any:
74
106
  """
75
- Initializes the OpenAI SDK client using the provided options.
107
+ Lazily return the synchronous OpenAI client.
76
108
 
77
- This method imports the `OpenAI` class from the `openai` package, instantiates
78
- a client with the given keyword arguments (`self.options`), and assigns it to
79
- `self._client`.
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
80
114
 
81
- It also sets the default operation to `self.client.chat.completions.create`, which is
82
- typically used for invoking chat-based model completions.
115
+ def load_client(self) -> None:
116
+ """
117
+ Lazily initialize the synchronous OpenAI client.
83
118
 
84
- Raises:
85
- 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.
86
121
  """
122
+ if self._client:
123
+ return
87
124
  try:
88
125
  from openai import OpenAI # noqa
89
126
 
90
127
  self._client = OpenAI(**self.options)
91
- self._default_operation = self.client.chat.completions.create
92
128
  except ImportError as exc:
93
129
  raise ImportError("openai package is not installed") from exc
94
130
 
95
- def get_client_options(self):
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
+
156
+ def get_client_options(self) -> dict:
96
157
  res = dict(
97
158
  api_key=self._get_secret_or_env("OPENAI_API_KEY"),
98
159
  organization=self._get_secret_or_env("OPENAI_ORG_ID"),
@@ -103,42 +164,234 @@ class OpenAIProvider(ModelProvider):
103
164
  )
104
165
  return self._sanitize_options(res)
105
166
 
106
- def customized_invoke(
107
- self, operation: Optional[Callable[..., T]] = None, **invoke_kwargs
108
- ) -> Optional[T]:
167
+ def custom_invoke(
168
+ self, operation: Optional[Callable] = None, **invoke_kwargs
169
+ ) -> Union["ChatCompletion", "BaseModel"]:
170
+ """
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.
176
+
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.
179
+
180
+ Example:
181
+ ```python
182
+ result = openai_model_provider.custom_invoke(
183
+ openai_model_provider.client.images.generate,
184
+ prompt="A futuristic cityscape at sunset",
185
+ n=1,
186
+ size="1024x1024",
187
+ )
188
+ ```
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.
200
+ """
201
+
202
+ invoke_kwargs = self.get_invoke_kwargs(invoke_kwargs)
203
+ model_kwargs = {"model": invoke_kwargs.pop("model", None) or self.model}
204
+
205
+ if operation:
206
+ if not callable(operation):
207
+ raise mlrun.errors.MLRunInvalidArgumentError(
208
+ "OpenAI custom_invoke operation must be a callable"
209
+ )
210
+ if not accepts_param(operation, "model"):
211
+ model_kwargs = {}
212
+ return operation(**invoke_kwargs, **model_kwargs)
213
+ else:
214
+ return self.client.chat.completions.create(**invoke_kwargs, **model_kwargs)
215
+
216
+ async def async_custom_invoke(
217
+ self,
218
+ operation: Optional[Callable[..., Awaitable[Any]]] = None,
219
+ **invoke_kwargs,
220
+ ) -> Union["ChatCompletion", "BaseModel"]:
221
+ """
222
+ Asynchronously invokes a model operation from the OpenAI client with the given keyword arguments.
223
+
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.
230
+
231
+ Example:
232
+ ```python
233
+ result = await openai_model_provider.async_custom_invoke(
234
+ openai_model_provider.async_client.images.generate,
235
+ prompt="A futuristic cityscape at sunset",
236
+ n=1,
237
+ size="1024x1024",
238
+ )
239
+ ```
240
+
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.
251
+
252
+ """
109
253
  invoke_kwargs = self.get_invoke_kwargs(invoke_kwargs)
254
+ model_kwargs = {"model": invoke_kwargs.pop("model", None) or self.model}
110
255
  if operation:
111
- return operation(**invoke_kwargs, model=self.model)
256
+ if not inspect.iscoroutinefunction(operation):
257
+ raise mlrun.errors.MLRunInvalidArgumentError(
258
+ "OpenAI async_custom_invoke operation must be a coroutine function"
259
+ )
260
+ if not accepts_param(operation, "model"):
261
+ model_kwargs = {}
262
+ return await operation(**invoke_kwargs, **model_kwargs)
112
263
  else:
113
- return self._default_operation(**invoke_kwargs, model=self.model)
264
+ return await self.async_client.chat.completions.create(
265
+ **invoke_kwargs, **model_kwargs
266
+ )
267
+
268
+ def _response_handler(
269
+ self,
270
+ response: "ChatCompletion",
271
+ invoke_response_format: InvokeResponseFormat = InvokeResponseFormat.FULL,
272
+ **kwargs,
273
+ ) -> ["ChatCompletion", str, dict[str, Any]]:
274
+ if InvokeResponseFormat.is_str_response(invoke_response_format.value):
275
+ str_response = self._extract_string_output(response)
276
+ if invoke_response_format == InvokeResponseFormat.STRING:
277
+ return str_response
278
+ if invoke_response_format == InvokeResponseFormat.USAGE:
279
+ usage = response.to_dict()["usage"]
280
+ response = {
281
+ UsageResponseKeys.ANSWER: str_response,
282
+ UsageResponseKeys.USAGE: usage,
283
+ }
284
+ return response
114
285
 
115
286
  def invoke(
116
287
  self,
117
- messages: Optional[list[dict]] = None,
118
- as_str: bool = False,
288
+ messages: list[dict],
289
+ invoke_response_format: InvokeResponseFormat = InvokeResponseFormat.FULL,
119
290
  **invoke_kwargs,
120
- ) -> Optional[Union[str, T]]:
291
+ ) -> Union[dict[str, Any], str, "ChatCompletion"]:
121
292
  """
122
293
  OpenAI-specific implementation of `ModelProvider.invoke`.
123
- Invokes an OpenAI model operation using the sync client.
124
- For full details, see `ModelProvider.invoke`.
294
+ Invokes an OpenAI model operation using the synchronous client.
295
+
296
+ :param messages:
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
+ }
303
+
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:
316
+ Specifies the format of the returned response. Options:
317
+
318
+ - "string": Returns only the generated text content, taken from a single response.
319
+ - "usage": Combines the generated text with metadata (e.g., token usage), returning a dictionary::
125
320
 
126
- :param messages: Same as ModelProvider.invoke.
321
+ .. code-block:: json
322
+ {
323
+ "answer": "<generated_text>",
324
+ "usage": <ChatCompletion>.to_dict()["usage"]
325
+ }
127
326
 
128
- :param as_str: bool
129
- If `True`, returns only the main content of the first response
130
- (`response.choices[0].message.content`).
131
- If `False`, returns the full response object, whose type depends on
132
- the specific OpenAI SDK operation used (e.g., chat completion, completion, etc.).
327
+ - "full": Returns the full OpenAI `ChatCompletion` object.
133
328
 
134
329
  :param invoke_kwargs:
135
- Same as ModelProvider.invoke.
330
+ Additional keyword arguments passed to the OpenAI client.
136
331
 
332
+ :return:
333
+ A string, dictionary, or `ChatCompletion` object, depending on `invoke_response_format`.
137
334
  """
138
- invoke_kwargs = self.get_invoke_kwargs(invoke_kwargs)
139
- response = self._default_operation(
140
- model=self.endpoint, messages=messages, **invoke_kwargs
335
+
336
+ response = self.custom_invoke(messages=messages, **invoke_kwargs)
337
+ return self._response_handler(
338
+ messages=messages,
339
+ invoke_response_format=invoke_response_format,
340
+ response=response,
341
+ )
342
+
343
+ async def async_invoke(
344
+ self,
345
+ messages: list[dict],
346
+ invoke_response_format=InvokeResponseFormat.FULL,
347
+ **invoke_kwargs,
348
+ ) -> Union[str, "ChatCompletion", dict]:
349
+ """
350
+ OpenAI-specific implementation of `ModelProvider.async_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::
377
+
378
+ .. code-block:: json
379
+ {
380
+ "answer": "<generated_text>",
381
+ "usage": <ChatCompletion>.to_dict()["usage"]
382
+ }
383
+
384
+ - "full": Returns the full OpenAI `ChatCompletion` object.
385
+
386
+ :param invoke_kwargs:
387
+ Additional keyword arguments passed to the OpenAI client.
388
+
389
+ :return:
390
+ A string, dictionary, or `ChatCompletion` object, depending on `invoke_response_format`.
391
+ """
392
+ response = await self.async_custom_invoke(messages=messages, **invoke_kwargs)
393
+ return self._response_handler(
394
+ messages=messages,
395
+ invoke_response_format=invoke_response_format,
396
+ response=response,
141
397
  )
142
- if as_str:
143
- return response.choices[0].message.content
144
- return response
mlrun/datastore/s3.py CHANGED
@@ -13,6 +13,7 @@
13
13
  # limitations under the License.
14
14
 
15
15
  import time
16
+ import warnings
16
17
  from typing import Optional
17
18
  from urllib.parse import urlparse
18
19
 
@@ -28,6 +29,27 @@ from .base import DataStore, FileStats, make_datastore_schema_sanitizer
28
29
  class S3Store(DataStore):
29
30
  using_bucket = True
30
31
 
32
+ # TODO: Remove this in 1.12.0
33
+ def _get_endpoint_url_with_deprecation_warning(self):
34
+ """Get S3 endpoint URL with backward compatibility for deprecated S3_ENDPOINT_URL"""
35
+ # First try the new environment variable
36
+ endpoint_url = self._get_secret_or_env("AWS_ENDPOINT_URL_S3")
37
+ if endpoint_url:
38
+ return endpoint_url
39
+
40
+ # Check for deprecated environment variable
41
+ deprecated_endpoint_url = self._get_secret_or_env("S3_ENDPOINT_URL")
42
+ if deprecated_endpoint_url:
43
+ warnings.warn(
44
+ "S3_ENDPOINT_URL is deprecated in 1.10.0 and will be removed in 1.12.0, "
45
+ "use AWS_ENDPOINT_URL_S3 instead.",
46
+ # TODO: Remove this in 1.12.0
47
+ FutureWarning,
48
+ )
49
+ return deprecated_endpoint_url
50
+
51
+ return None
52
+
31
53
  def __init__(
32
54
  self, parent, schema, name, endpoint="", secrets: Optional[dict] = None
33
55
  ):
@@ -41,7 +63,7 @@ class S3Store(DataStore):
41
63
  access_key_id = self._get_secret_or_env("AWS_ACCESS_KEY_ID")
42
64
  secret_key = self._get_secret_or_env("AWS_SECRET_ACCESS_KEY")
43
65
  token_file = self._get_secret_or_env("AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE")
44
- endpoint_url = self._get_secret_or_env("S3_ENDPOINT_URL")
66
+ endpoint_url = self._get_endpoint_url_with_deprecation_warning()
45
67
  force_non_anonymous = self._get_secret_or_env("S3_NON_ANONYMOUS")
46
68
  profile_name = self._get_secret_or_env("AWS_PROFILE")
47
69
  assume_role_arn = self._get_secret_or_env("MLRUN_AWS_ROLE_ARN")
@@ -159,7 +181,7 @@ class S3Store(DataStore):
159
181
  def get_storage_options(self):
160
182
  force_non_anonymous = self._get_secret_or_env("S3_NON_ANONYMOUS")
161
183
  profile = self._get_secret_or_env("AWS_PROFILE")
162
- endpoint_url = self._get_secret_or_env("S3_ENDPOINT_URL")
184
+ endpoint_url = self._get_endpoint_url_with_deprecation_warning()
163
185
  access_key_id = self._get_secret_or_env("AWS_ACCESS_KEY_ID")
164
186
  secret = self._get_secret_or_env("AWS_SECRET_ACCESS_KEY")
165
187
  token_file = self._get_secret_or_env("AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE")
@@ -18,10 +18,9 @@ from mergedeep import merge
18
18
  from storey import V3ioDriver
19
19
 
20
20
  import mlrun
21
- import mlrun.model_monitoring.helpers
22
21
  from mlrun.datastore.base import DataStore
23
22
  from mlrun.datastore.datastore_profile import (
24
- DatastoreProfileKafkaSource,
23
+ DatastoreProfileKafkaStream,
25
24
  DatastoreProfileKafkaTarget,
26
25
  DatastoreProfileTDEngine,
27
26
  datastore_profile_read,
@@ -138,7 +137,7 @@ class KafkaStoreyTarget(storey.KafkaTarget):
138
137
  datastore_profile = datastore_profile_read(path)
139
138
  if not isinstance(
140
139
  datastore_profile,
141
- (DatastoreProfileKafkaSource, DatastoreProfileKafkaTarget),
140
+ (DatastoreProfileKafkaStream, DatastoreProfileKafkaTarget),
142
141
  ):
143
142
  raise mlrun.errors.MLRunInvalidArgumentError(
144
143
  f"Unsupported datastore profile type: {type(datastore_profile)}"
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
@@ -319,7 +320,13 @@ def parse_url(url):
319
320
  parsed_url = urlparse(url)
320
321
  schema = parsed_url.scheme.lower()
321
322
  endpoint = parsed_url.hostname
322
- if endpoint:
323
+
324
+ # Special handling for WASBS URLs to preserve container information
325
+ if schema in ["wasbs", "wasb"] and parsed_url.netloc and "@" in parsed_url.netloc:
326
+ # For wasbs://container@host format, preserve the full netloc as endpoint
327
+ # This allows the datastore to extract container later
328
+ endpoint = parsed_url.netloc
329
+ elif endpoint:
323
330
  # HACK - urlparse returns the hostname after in lower case - we want the original case:
324
331
  # the hostname is a substring of the netloc, in which it's the original case, so we find the indexes of the
325
332
  # hostname in the netloc and take it from there
@@ -330,6 +337,11 @@ def parse_url(url):
330
337
  endpoint = netloc[
331
338
  hostname_index_in_netloc : hostname_index_in_netloc + len(lower_hostname)
332
339
  ]
333
- if parsed_url.port:
334
- endpoint += f":{parsed_url.port}"
340
+ if parsed_url.port:
341
+ endpoint += f":{parsed_url.port}"
335
342
  return schema, endpoint, parsed_url
343
+
344
+
345
+ def accepts_param(func: callable, param_name):
346
+ sig = inspect.signature(func)
347
+ return param_name in sig.parameters
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
@@ -55,6 +53,12 @@ class RunDBInterface(ABC):
55
53
  def update_run(self, updates: dict, uid, project="", iter=0):
56
54
  pass
57
55
 
56
+ @abstractmethod
57
+ def set_run_retrying_status(
58
+ self, project: str, name: str, run_id: str, retrying: bool
59
+ ):
60
+ pass
61
+
58
62
  @abstractmethod
59
63
  def abort_run(self, uid, project="", iter=0, timeout=45, status_text=""):
60
64
  pass
@@ -439,23 +443,6 @@ class RunDBInterface(ABC):
439
443
  ) -> dict:
440
444
  pass
441
445
 
442
- # TODO: remove in 1.10.0
443
- @deprecated(
444
- version="1.7.0",
445
- reason="'list_features' will be removed in 1.10.0, use 'list_features_v2' instead",
446
- category=FutureWarning,
447
- )
448
- @abstractmethod
449
- def list_features(
450
- self,
451
- project: str,
452
- name: Optional[str] = None,
453
- tag: Optional[str] = None,
454
- entities: Optional[list[str]] = None,
455
- labels: Optional[Union[str, dict[str, Optional[str]], list[str]]] = None,
456
- ) -> mlrun.common.schemas.FeaturesOutput:
457
- pass
458
-
459
446
  @abstractmethod
460
447
  def list_features_v2(
461
448
  self,
@@ -638,6 +625,11 @@ class RunDBInterface(ABC):
638
625
  ):
639
626
  pass
640
627
 
628
+ def wait_for_background_task_to_reach_terminal_state(
629
+ self, name: str, project: str = ""
630
+ ) -> mlrun.common.schemas.BackgroundTask:
631
+ pass
632
+
641
633
  @abstractmethod
642
634
  def retry_pipeline(
643
635
  self,
@@ -730,6 +722,9 @@ class RunDBInterface(ABC):
730
722
  tsdb_metrics: bool = False,
731
723
  metric_list: Optional[list[str]] = None,
732
724
  top_level: bool = False,
725
+ modes: Optional[
726
+ Union[mm_constants.EndpointMode, list[mm_constants.EndpointMode]]
727
+ ] = None,
733
728
  uids: Optional[list[str]] = None,
734
729
  latest_only: bool = False,
735
730
  ) -> mlrun.common.schemas.ModelEndpointList:
@@ -781,6 +776,7 @@ class RunDBInterface(ABC):
781
776
  item_name: Optional[str] = None,
782
777
  tag: Optional[str] = None,
783
778
  version: Optional[str] = None,
779
+ item_type: mlrun.common.schemas.hub.HubSourceType = mlrun.common.schemas.hub.HubSourceType.functions,
784
780
  ):
785
781
  pass
786
782
 
@@ -799,6 +795,7 @@ class RunDBInterface(ABC):
799
795
  version: Optional[str] = None,
800
796
  tag: Optional[str] = None,
801
797
  force_refresh: bool = False,
798
+ object_type: mlrun.common.schemas.hub.HubSourceType = mlrun.common.schemas.hub.HubSourceType.functions,
802
799
  ):
803
800
  pass
804
801
 
@@ -810,6 +807,19 @@ class RunDBInterface(ABC):
810
807
  version: Optional[str] = None,
811
808
  tag: str = "latest",
812
809
  force_refresh: bool = False,
810
+ item_type: mlrun.common.schemas.hub.HubSourceType = mlrun.common.schemas.hub.HubSourceType.functions,
811
+ ):
812
+ pass
813
+
814
+ @abstractmethod
815
+ def get_hub_asset(
816
+ self,
817
+ source_name: str,
818
+ item_name: str,
819
+ asset_name: str,
820
+ version: Optional[str] = None,
821
+ tag: str = "latest",
822
+ item_type: mlrun.common.schemas.hub.HubSourceType = mlrun.common.schemas.hub.HubSourceType.functions,
813
823
  ):
814
824
  pass
815
825
 
@@ -1118,6 +1128,15 @@ class RunDBInterface(ABC):
1118
1128
  ) -> None:
1119
1129
  pass
1120
1130
 
1131
+ @abstractmethod
1132
+ def delete_model_monitoring_metrics(
1133
+ self,
1134
+ project: str,
1135
+ application_name: str,
1136
+ endpoint_ids: Optional[list[str]] = None,
1137
+ ) -> None:
1138
+ pass
1139
+
1121
1140
  @abstractmethod
1122
1141
  def get_monitoring_function_summaries(
1123
1142
  self,
@@ -1145,3 +1164,12 @@ class RunDBInterface(ABC):
1145
1164
  @abstractmethod
1146
1165
  def get_project_summary(self, project: str) -> mlrun.common.schemas.ProjectSummary:
1147
1166
  pass
1167
+
1168
+ @abstractmethod
1169
+ def get_drift_over_time(
1170
+ self,
1171
+ project: str,
1172
+ start: Optional[datetime.datetime] = None,
1173
+ end: Optional[datetime.datetime] = None,
1174
+ ) -> mlrun.common.schemas.model_monitoring.ModelEndpointDriftValues:
1175
+ pass