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.
- mlrun/__init__.py +22 -2
- mlrun/artifacts/document.py +6 -1
- mlrun/artifacts/llm_prompt.py +21 -15
- mlrun/artifacts/model.py +3 -3
- mlrun/common/constants.py +9 -0
- mlrun/common/formatters/artifact.py +1 -0
- mlrun/common/model_monitoring/helpers.py +86 -0
- mlrun/common/schemas/__init__.py +2 -0
- mlrun/common/schemas/auth.py +2 -0
- mlrun/common/schemas/function.py +10 -0
- mlrun/common/schemas/hub.py +30 -18
- mlrun/common/schemas/model_monitoring/__init__.py +2 -0
- mlrun/common/schemas/model_monitoring/constants.py +30 -6
- mlrun/common/schemas/model_monitoring/functions.py +13 -4
- mlrun/common/schemas/model_monitoring/model_endpoints.py +11 -0
- mlrun/common/schemas/pipeline.py +1 -1
- mlrun/common/schemas/serving.py +3 -0
- mlrun/common/schemas/workflow.py +1 -0
- mlrun/common/secrets.py +22 -1
- mlrun/config.py +34 -21
- mlrun/datastore/__init__.py +11 -3
- mlrun/datastore/azure_blob.py +162 -47
- mlrun/datastore/base.py +265 -7
- mlrun/datastore/datastore.py +10 -5
- mlrun/datastore/datastore_profile.py +61 -5
- mlrun/datastore/model_provider/huggingface_provider.py +367 -0
- mlrun/datastore/model_provider/mock_model_provider.py +87 -0
- mlrun/datastore/model_provider/model_provider.py +211 -74
- mlrun/datastore/model_provider/openai_provider.py +243 -71
- mlrun/datastore/s3.py +24 -2
- mlrun/datastore/store_resources.py +4 -4
- mlrun/datastore/storeytargets.py +2 -3
- mlrun/datastore/utils.py +15 -3
- mlrun/db/base.py +27 -19
- mlrun/db/httpdb.py +57 -48
- mlrun/db/nopdb.py +25 -10
- mlrun/execution.py +55 -13
- mlrun/hub/__init__.py +15 -0
- mlrun/hub/module.py +181 -0
- mlrun/k8s_utils.py +105 -16
- mlrun/launcher/base.py +13 -6
- mlrun/launcher/local.py +2 -0
- mlrun/model.py +9 -3
- mlrun/model_monitoring/api.py +66 -27
- mlrun/model_monitoring/applications/__init__.py +1 -1
- mlrun/model_monitoring/applications/base.py +388 -138
- mlrun/model_monitoring/applications/context.py +2 -4
- mlrun/model_monitoring/applications/results.py +4 -7
- mlrun/model_monitoring/controller.py +239 -101
- mlrun/model_monitoring/db/_schedules.py +36 -13
- mlrun/model_monitoring/db/_stats.py +4 -3
- mlrun/model_monitoring/db/tsdb/base.py +29 -9
- mlrun/model_monitoring/db/tsdb/tdengine/schemas.py +4 -5
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +154 -50
- mlrun/model_monitoring/db/tsdb/tdengine/writer_graph_steps.py +51 -0
- mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py +17 -4
- mlrun/model_monitoring/db/tsdb/v3io/v3io_connector.py +245 -51
- mlrun/model_monitoring/helpers.py +28 -5
- mlrun/model_monitoring/stream_processing.py +45 -14
- mlrun/model_monitoring/writer.py +220 -1
- mlrun/platforms/__init__.py +3 -2
- mlrun/platforms/iguazio.py +7 -3
- mlrun/projects/operations.py +16 -11
- mlrun/projects/pipelines.py +2 -2
- mlrun/projects/project.py +157 -69
- mlrun/run.py +97 -20
- mlrun/runtimes/__init__.py +18 -0
- mlrun/runtimes/base.py +14 -6
- mlrun/runtimes/daskjob.py +1 -0
- mlrun/runtimes/local.py +5 -2
- mlrun/runtimes/mounts.py +20 -2
- mlrun/runtimes/nuclio/__init__.py +1 -0
- mlrun/runtimes/nuclio/application/application.py +147 -17
- mlrun/runtimes/nuclio/function.py +72 -27
- mlrun/runtimes/nuclio/serving.py +102 -20
- mlrun/runtimes/pod.py +213 -21
- mlrun/runtimes/utils.py +49 -9
- mlrun/secrets.py +54 -13
- mlrun/serving/remote.py +79 -6
- mlrun/serving/routers.py +23 -41
- mlrun/serving/server.py +230 -40
- mlrun/serving/states.py +605 -232
- mlrun/serving/steps.py +62 -0
- mlrun/serving/system_steps.py +136 -81
- mlrun/serving/v2_serving.py +9 -10
- mlrun/utils/helpers.py +215 -83
- mlrun/utils/logger.py +3 -1
- mlrun/utils/notifications/notification/base.py +18 -0
- mlrun/utils/notifications/notification/git.py +2 -4
- mlrun/utils/notifications/notification/mail.py +38 -15
- mlrun/utils/notifications/notification/slack.py +2 -4
- mlrun/utils/notifications/notification/webhook.py +2 -5
- mlrun/utils/notifications/notification_pusher.py +1 -1
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/METADATA +51 -50
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/RECORD +100 -95
- mlrun/api/schemas/__init__.py +0 -259
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/WHEEL +0 -0
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/entry_points.txt +0 -0
- {mlrun-1.10.0rc16.dist-info → mlrun-1.10.1rc4.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
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
|
-
|
|
195
|
+
secret_value = secret_provider.get(key)
|
|
194
196
|
else:
|
|
195
|
-
|
|
196
|
-
if
|
|
197
|
-
return
|
|
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
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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,
|
|
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
|
-
|
|
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
|