mlrun 1.10.0rc16__py3-none-any.whl → 1.10.1rc4__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 (101) hide show
  1. mlrun/__init__.py +22 -2
  2. mlrun/artifacts/document.py +6 -1
  3. mlrun/artifacts/llm_prompt.py +21 -15
  4. mlrun/artifacts/model.py +3 -3
  5. mlrun/common/constants.py +9 -0
  6. mlrun/common/formatters/artifact.py +1 -0
  7. mlrun/common/model_monitoring/helpers.py +86 -0
  8. mlrun/common/schemas/__init__.py +2 -0
  9. mlrun/common/schemas/auth.py +2 -0
  10. mlrun/common/schemas/function.py +10 -0
  11. mlrun/common/schemas/hub.py +30 -18
  12. mlrun/common/schemas/model_monitoring/__init__.py +2 -0
  13. mlrun/common/schemas/model_monitoring/constants.py +30 -6
  14. mlrun/common/schemas/model_monitoring/functions.py +13 -4
  15. mlrun/common/schemas/model_monitoring/model_endpoints.py +11 -0
  16. mlrun/common/schemas/pipeline.py +1 -1
  17. mlrun/common/schemas/serving.py +3 -0
  18. mlrun/common/schemas/workflow.py +1 -0
  19. mlrun/common/secrets.py +22 -1
  20. mlrun/config.py +34 -21
  21. mlrun/datastore/__init__.py +11 -3
  22. mlrun/datastore/azure_blob.py +162 -47
  23. mlrun/datastore/base.py +265 -7
  24. mlrun/datastore/datastore.py +10 -5
  25. mlrun/datastore/datastore_profile.py +61 -5
  26. mlrun/datastore/model_provider/huggingface_provider.py +367 -0
  27. mlrun/datastore/model_provider/mock_model_provider.py +87 -0
  28. mlrun/datastore/model_provider/model_provider.py +211 -74
  29. mlrun/datastore/model_provider/openai_provider.py +243 -71
  30. mlrun/datastore/s3.py +24 -2
  31. mlrun/datastore/store_resources.py +4 -4
  32. mlrun/datastore/storeytargets.py +2 -3
  33. mlrun/datastore/utils.py +15 -3
  34. mlrun/db/base.py +27 -19
  35. mlrun/db/httpdb.py +57 -48
  36. mlrun/db/nopdb.py +25 -10
  37. mlrun/execution.py +55 -13
  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 +2 -0
  43. mlrun/model.py +9 -3
  44. mlrun/model_monitoring/api.py +66 -27
  45. mlrun/model_monitoring/applications/__init__.py +1 -1
  46. mlrun/model_monitoring/applications/base.py +388 -138
  47. mlrun/model_monitoring/applications/context.py +2 -4
  48. mlrun/model_monitoring/applications/results.py +4 -7
  49. mlrun/model_monitoring/controller.py +239 -101
  50. mlrun/model_monitoring/db/_schedules.py +36 -13
  51. mlrun/model_monitoring/db/_stats.py +4 -3
  52. mlrun/model_monitoring/db/tsdb/base.py +29 -9
  53. mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +4 -5
  54. mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +154 -50
  55. mlrun/model_monitoring/db/tsdb/tdengine/writer_graph_steps.py +51 -0
  56. mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +17 -4
  57. mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +245 -51
  58. mlrun/model_monitoring/helpers.py +28 -5
  59. mlrun/model_monitoring/stream_processing.py +45 -14
  60. mlrun/model_monitoring/writer.py +220 -1
  61. mlrun/platforms/__init__.py +3 -2
  62. mlrun/platforms/iguazio.py +7 -3
  63. mlrun/projects/operations.py +16 -11
  64. mlrun/projects/pipelines.py +2 -2
  65. mlrun/projects/project.py +157 -69
  66. mlrun/run.py +97 -20
  67. mlrun/runtimes/__init__.py +18 -0
  68. mlrun/runtimes/base.py +14 -6
  69. mlrun/runtimes/daskjob.py +1 -0
  70. mlrun/runtimes/local.py +5 -2
  71. mlrun/runtimes/mounts.py +20 -2
  72. mlrun/runtimes/nuclio/__init__.py +1 -0
  73. mlrun/runtimes/nuclio/application/application.py +147 -17
  74. mlrun/runtimes/nuclio/function.py +72 -27
  75. mlrun/runtimes/nuclio/serving.py +102 -20
  76. mlrun/runtimes/pod.py +213 -21
  77. mlrun/runtimes/utils.py +49 -9
  78. mlrun/secrets.py +54 -13
  79. mlrun/serving/remote.py +79 -6
  80. mlrun/serving/routers.py +23 -41
  81. mlrun/serving/server.py +230 -40
  82. mlrun/serving/states.py +605 -232
  83. mlrun/serving/steps.py +62 -0
  84. mlrun/serving/system_steps.py +136 -81
  85. mlrun/serving/v2_serving.py +9 -10
  86. mlrun/utils/helpers.py +215 -83
  87. mlrun/utils/logger.py +3 -1
  88. mlrun/utils/notifications/notification/base.py +18 -0
  89. mlrun/utils/notifications/notification/git.py +2 -4
  90. mlrun/utils/notifications/notification/mail.py +38 -15
  91. mlrun/utils/notifications/notification/slack.py +2 -4
  92. mlrun/utils/notifications/notification/webhook.py +2 -5
  93. mlrun/utils/notifications/notification_pusher.py +1 -1
  94. mlrun/utils/version/version.json +2 -2
  95. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/METADATA +51 -50
  96. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/RECORD +100 -95
  97. mlrun/api/schemas/__init__.py +0 -259
  98. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/WHEEL +0 -0
  99. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/entry_points.txt +0 -0
  100. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/licenses/LICENSE +0 -0
  101. {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/top_level.txt +0 -0
mlrun/secrets.py CHANGED
@@ -11,9 +11,9 @@
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
-
14
+ import json
15
15
  from ast import literal_eval
16
- from os import environ, getenv
16
+ from os import environ
17
17
  from typing import Callable, Optional, Union
18
18
 
19
19
  from .utils import AzureVaultStore, list2dict
@@ -161,6 +161,9 @@ def get_secret_or_env(
161
161
  4. An MLRun-generated env. variable, mounted from a project secret (to be used in MLRun runtimes)
162
162
  5. The default value
163
163
 
164
+ Also supports discovering the value inside any environment variable that contains a JSON-encoded list
165
+ of dicts with fields: {'name': 'KEY', 'value': 'VAL', 'value_from': ...}. This fallback is applied
166
+ after checking normal environment variables and before returning the default.
164
167
  Example::
165
168
 
166
169
  secrets = {"KEY1": "VALUE1"}
@@ -187,18 +190,56 @@ def get_secret_or_env(
187
190
  if prefix:
188
191
  key = f"{prefix}_{key}"
189
192
 
190
- value = None
191
193
  if secret_provider:
192
194
  if isinstance(secret_provider, (dict, SecretsStore)):
193
- value = secret_provider.get(key)
195
+ secret_value = secret_provider.get(key)
194
196
  else:
195
- value = secret_provider(key)
196
- if value:
197
- return value
197
+ secret_value = secret_provider(key)
198
+ if secret_value:
199
+ return secret_value
200
+
201
+ direct_environment_value = environ.get(key)
202
+ if direct_environment_value:
203
+ return direct_environment_value
204
+
205
+ json_list_value = _find_value_in_json_env_lists(key)
206
+ if json_list_value is not None:
207
+ return json_list_value
208
+
209
+ mlrun_env_key = SecretsStore.k8s_env_variable_name_for_secret(key)
210
+ mlrun_env_value = environ.get(mlrun_env_key)
211
+ if mlrun_env_value:
212
+ return mlrun_env_value
198
213
 
199
- return (
200
- value
201
- or getenv(key)
202
- or getenv(SecretsStore.k8s_env_variable_name_for_secret(key))
203
- or default
204
- )
214
+ return default
215
+
216
+
217
+ def _find_value_in_json_env_lists(
218
+ secret_name: str,
219
+ ) -> Optional[str]:
220
+ """
221
+ Scan all environment variables. If any env var contains a JSON-encoded list
222
+ of dicts shaped like {'name': str, 'value': str|None, 'value_from': ...},
223
+ return the 'value' for the entry whose 'name' matches secret_name.
224
+ """
225
+ for environment_variable_value in environ.values():
226
+ if not environment_variable_value or not isinstance(
227
+ environment_variable_value, str
228
+ ):
229
+ continue
230
+ # Fast precheck to skip obvious non-JSON strings
231
+ first_char = environment_variable_value.lstrip()[:1]
232
+ if first_char not in ("[", "{"):
233
+ continue
234
+ try:
235
+ parsed_value = json.loads(environment_variable_value)
236
+ except ValueError:
237
+ continue
238
+ if isinstance(parsed_value, list):
239
+ for entry in parsed_value:
240
+ if isinstance(entry, dict) and entry.get("name") == secret_name:
241
+ value_in_entry = entry.get("value")
242
+ # Match original semantics: empty string is treated as "not found"
243
+ if value_in_entry:
244
+ return value_in_entry
245
+ return None
mlrun/serving/remote.py CHANGED
@@ -23,10 +23,14 @@ import storey
23
23
  from storey.flow import _ConcurrentJobExecution
24
24
 
25
25
  import mlrun
26
+ import mlrun.common.schemas
26
27
  import mlrun.config
28
+ import mlrun.platforms
29
+ import mlrun.utils.async_http
27
30
  from mlrun.errors import err_to_str
28
- from mlrun.utils import logger
31
+ from mlrun.utils import dict_to_json, logger
29
32
 
33
+ from ..config import config
30
34
  from .utils import (
31
35
  _extract_input_data,
32
36
  _update_result_body,
@@ -73,7 +77,9 @@ class RemoteStep(storey.SendToHttp):
73
77
 
74
78
  :param url: http(s) url or function [project/]name to call
75
79
  :param subpath: path (which follows the url), use `$path` to use the event.path
76
- :param method: HTTP method (GET, POST, ..), default to POST
80
+ :param method: The HTTP method to use for the request (e.g., "GET", "POST", "PUT", "DELETE").
81
+ If not provided, the step will try to use `event.method` at runtime, and if that
82
+ is also missing, it defaults to `"POST"`.
77
83
  :param headers: dictionary with http header values
78
84
  :param url_expression: an expression for getting the url from the event, e.g. "event['url']"
79
85
  :param body_expression: an expression for getting the request body from the event, e.g. "event['data']"
@@ -150,8 +156,8 @@ class RemoteStep(storey.SendToHttp):
150
156
  async def _process_event(self, event):
151
157
  # async implementation (with storey)
152
158
  body = self._get_event_or_body(event)
153
- method, url, headers, body = self._generate_request(event, body)
154
- kwargs = {}
159
+ method, url, headers, body, kwargs = self._generate_request(event, body)
160
+ kwargs = kwargs or {}
155
161
  if self.timeout:
156
162
  kwargs["timeout"] = aiohttp.ClientTimeout(total=self.timeout)
157
163
  try:
@@ -191,7 +197,7 @@ class RemoteStep(storey.SendToHttp):
191
197
  )
192
198
 
193
199
  body = _extract_input_data(self._input_path, event.body)
194
- method, url, headers, body = self._generate_request(event, body)
200
+ method, url, headers, body, kwargs = self._generate_request(event, body)
195
201
  try:
196
202
  resp = self._session.request(
197
203
  method,
@@ -200,6 +206,7 @@ class RemoteStep(storey.SendToHttp):
200
206
  headers=headers,
201
207
  data=body,
202
208
  timeout=self.timeout,
209
+ **kwargs,
203
210
  )
204
211
  except requests.exceptions.ReadTimeout as err:
205
212
  raise requests.exceptions.ReadTimeout(
@@ -240,7 +247,7 @@ class RemoteStep(storey.SendToHttp):
240
247
  body = json.dumps(body)
241
248
  headers["Content-Type"] = "application/json"
242
249
 
243
- return method, url, headers, body
250
+ return method, url, headers, body, {}
244
251
 
245
252
  def _get_data(self, data, headers):
246
253
  if (
@@ -454,3 +461,69 @@ class BatchHttpRequests(_ConcurrentJobExecution):
454
461
  ) and isinstance(data, (str, bytes)):
455
462
  data = json.loads(data)
456
463
  return data
464
+
465
+
466
+ class MLRunAPIRemoteStep(RemoteStep):
467
+ def __init__(
468
+ self, method: str, path: str, fill_placeholders: Optional[bool] = None, **kwargs
469
+ ):
470
+ """
471
+ Graph step implementation for calling MLRun API endpoints
472
+
473
+ :param method: The HTTP method to use for the request (e.g., "GET", "POST", "PUT", "DELETE").
474
+ If not provided, the step will try to use `event.method` at runtime, and if that
475
+ is also missing, it defaults to `"POST"`.
476
+ :param path: API path (e.g. /api/projects)
477
+ :param fill_placeholders: if True, fill placeholders in the path using event fields (default to False)
478
+ :param kwargs: other arguments passed to RemoteStep
479
+ """
480
+ super().__init__(url="", method=method, **kwargs)
481
+ self.rundb = None
482
+ self.path = path
483
+ self.fill_placeholders = fill_placeholders
484
+
485
+ def _generate_request(self, event, body):
486
+ method = self.method or event.method or "POST"
487
+ kw = {
488
+ key: value
489
+ for key, value in (
490
+ ("params", body.get("params")),
491
+ ("json", body.get("json")),
492
+ )
493
+ if value is not None
494
+ }
495
+
496
+ headers = self.headers or {}
497
+ headers.update(body.get("headers", {}))
498
+
499
+ if self.rundb.user:
500
+ kw["auth"] = (self.rundb.user, self.rundb.password)
501
+ elif self.rundb.token_provider:
502
+ token = self.rundb.token_provider.get_token()
503
+ if token:
504
+ # Iguazio auth doesn't support passing token through bearer, so use cookie instead
505
+ if self.rundb.token_provider.is_iguazio_session():
506
+ session_cookie = f'session=j:{{"sid": "{token}"}}'
507
+ headers["cookie"] = session_cookie
508
+ else:
509
+ if "Authorization" not in kw.setdefault("headers", {}):
510
+ headers.update({"Authorization": "Bearer " + token})
511
+
512
+ if mlrun.common.schemas.HeaderNames.client_version not in headers:
513
+ headers.update(
514
+ {
515
+ mlrun.common.schemas.HeaderNames.client_version: self.rundb.client_version,
516
+ mlrun.common.schemas.HeaderNames.python_version: self.rundb.python_version,
517
+ "User-Agent": f"{requests.utils.default_user_agent()} mlrun/{config.version}",
518
+ }
519
+ )
520
+
521
+ url = self.url.format(**body) if self.fill_placeholders else self.url
522
+ headers["Content-Type"] = "application/json"
523
+ return method, url, headers, dict_to_json(body), kw
524
+
525
+ def post_init(self, mode="sync", **kwargs):
526
+ super().post_init(mode=mode, **kwargs)
527
+ self.fill_placeholders = self.fill_placeholders or False
528
+ self.rundb = mlrun.get_run_db()
529
+ self.url = self.rundb.get_base_api_url(self.path)
mlrun/serving/routers.py CHANGED
@@ -31,6 +31,9 @@ import mlrun.common.model_monitoring
31
31
  import mlrun.common.schemas.model_monitoring
32
32
  from mlrun.utils import logger, now_date
33
33
 
34
+ from ..common.model_monitoring.helpers import (
35
+ get_model_endpoints_creation_task_status,
36
+ )
34
37
  from .utils import RouterToDict, _extract_input_data, _update_result_body
35
38
  from .v2_serving import _ModelLogPusher
36
39
 
@@ -171,46 +174,6 @@ class BaseModelRouter(RouterToDict):
171
174
  """run tasks after processing the event"""
172
175
  return event
173
176
 
174
- def _get_background_task_status(
175
- self,
176
- ) -> mlrun.common.schemas.BackgroundTaskState:
177
- self._background_task_check_timestamp = now_date()
178
- server: mlrun.serving.GraphServer = getattr(
179
- self.context, "_server", None
180
- ) or getattr(self.context, "server", None)
181
- if not self.context.is_mock:
182
- if server.model_endpoint_creation_task_name:
183
- background_task = mlrun.get_run_db().get_project_background_task(
184
- server.project, server.model_endpoint_creation_task_name
185
- )
186
- logger.debug(
187
- "Checking model endpoint creation task status",
188
- task_name=server.model_endpoint_creation_task_name,
189
- )
190
- if (
191
- background_task.status.state
192
- in mlrun.common.schemas.BackgroundTaskState.terminal_states()
193
- ):
194
- logger.info(
195
- f"Model endpoint creation task completed with state {background_task.status.state}"
196
- )
197
- else: # in progress
198
- logger.info(
199
- f"Model endpoint creation task is still in progress with the current state: "
200
- f"{background_task.status.state}. Events will not be monitored for the next "
201
- f"{mlrun.mlconf.model_endpoint_monitoring.model_endpoint_creation_check_period} seconds",
202
- name=self.name,
203
- background_task_check_timestamp=self._background_task_check_timestamp.isoformat(),
204
- )
205
- return background_task.status.state
206
- else:
207
- logger.error(
208
- "Model endpoint creation task name not provided. This function is not being monitored.",
209
- )
210
- elif self.context.monitoring_mock:
211
- return mlrun.common.schemas.BackgroundTaskState.succeeded
212
- return mlrun.common.schemas.BackgroundTaskState.failed
213
-
214
177
  def _update_background_task_state(self, event):
215
178
  if not self.background_task_reached_terminal_state and (
216
179
  self._background_task_check_timestamp is None
@@ -219,7 +182,26 @@ class BaseModelRouter(RouterToDict):
219
182
  seconds=mlrun.mlconf.model_endpoint_monitoring.model_endpoint_creation_check_period
220
183
  )
221
184
  ):
222
- self._background_task_current_state = self._get_background_task_status()
185
+ server: mlrun.serving.GraphServer = getattr(
186
+ self.context, "_server", None
187
+ ) or getattr(self.context, "server", None)
188
+ if not self.context.is_mock:
189
+ (
190
+ self._background_task_current_state,
191
+ self._background_task_check_timestamp,
192
+ _,
193
+ ) = get_model_endpoints_creation_task_status(server)
194
+ elif self.context.monitoring_mock:
195
+ self._background_task_current_state = (
196
+ mlrun.common.schemas.BackgroundTaskState.succeeded
197
+ )
198
+ self._background_task_check_timestamp = mlrun.utils.now_date()
199
+ else:
200
+ self._background_task_current_state = (
201
+ mlrun.common.schemas.BackgroundTaskState.failed
202
+ )
203
+ self._background_task_check_timestamp = mlrun.utils.now_date()
204
+
223
205
  if event.body:
224
206
  event.body["background_task_state"] = (
225
207
  self._background_task_current_state