mlrun 1.10.0rc17__py3-none-any.whl → 1.10.0rc18__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.
- mlrun/common/formatters/artifact.py +1 -0
- mlrun/common/schemas/model_monitoring/constants.py +14 -6
- mlrun/config.py +14 -0
- mlrun/datastore/datastore.py +4 -4
- mlrun/datastore/datastore_profile.py +26 -0
- mlrun/datastore/model_provider/huggingface_provider.py +183 -0
- mlrun/datastore/model_provider/model_provider.py +6 -1
- mlrun/datastore/model_provider/openai_provider.py +24 -12
- mlrun/datastore/utils.py +6 -0
- mlrun/model_monitoring/api.py +5 -3
- mlrun/model_monitoring/applications/base.py +107 -28
- mlrun/model_monitoring/applications/results.py +4 -7
- mlrun/run.py +3 -1
- mlrun/serving/states.py +1 -1
- mlrun/utils/helpers.py +6 -1
- mlrun/utils/logger.py +3 -1
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc18.dist-info}/METADATA +2 -2
- {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc18.dist-info}/RECORD +23 -22
- {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc18.dist-info}/WHEEL +0 -0
- {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc18.dist-info}/entry_points.txt +0 -0
- {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc18.dist-info}/licenses/LICENSE +0 -0
- {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc18.dist-info}/top_level.txt +0 -0
|
@@ -487,25 +487,33 @@ class ModelMonitoringLabels:
|
|
|
487
487
|
|
|
488
488
|
_RESERVED_FUNCTION_NAMES = MonitoringFunctionNames.list() + [SpecialApps.MLRUN_INFRA]
|
|
489
489
|
|
|
490
|
+
_RESERVED_EVALUATE_FUNCTION_SUFFIX = "-batch"
|
|
491
|
+
|
|
490
492
|
|
|
491
493
|
class ModelEndpointMonitoringMetricType(StrEnum):
|
|
492
494
|
RESULT = "result"
|
|
493
495
|
METRIC = "metric"
|
|
494
496
|
|
|
495
497
|
|
|
498
|
+
# refer to `mlrun.utils.regex.project_name`
|
|
499
|
+
_INNER_PROJECT_PATTERN = r"[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?"
|
|
500
|
+
PROJECT_PATTERN = rf"^{_INNER_PROJECT_PATTERN}$"
|
|
501
|
+
|
|
502
|
+
MODEL_ENDPOINT_ID_PATTERN = r"^[a-zA-Z0-9_-]+$"
|
|
503
|
+
|
|
496
504
|
_FQN_PART_PATTERN = r"[a-zA-Z0-9_-]+"
|
|
505
|
+
_RESULT_NAME_PATTERN = r"[a-zA-Z_][a-zA-Z0-9_]*"
|
|
506
|
+
|
|
497
507
|
FQN_PATTERN = (
|
|
498
|
-
rf"^(?P<project>{
|
|
508
|
+
rf"^(?P<project>{_INNER_PROJECT_PATTERN})\."
|
|
499
509
|
rf"(?P<app>{_FQN_PART_PATTERN})\."
|
|
500
510
|
rf"(?P<type>{ModelEndpointMonitoringMetricType.RESULT}|{ModelEndpointMonitoringMetricType.METRIC})\."
|
|
501
|
-
rf"(?P<name>{
|
|
511
|
+
rf"(?P<name>{_RESULT_NAME_PATTERN})$"
|
|
502
512
|
)
|
|
503
513
|
FQN_REGEX = re.compile(FQN_PATTERN)
|
|
514
|
+
APP_NAME_REGEX = re.compile(_FQN_PART_PATTERN)
|
|
515
|
+
RESULT_NAME_REGEX = re.compile(_RESULT_NAME_PATTERN)
|
|
504
516
|
|
|
505
|
-
# refer to `mlrun.utils.regex.project_name`
|
|
506
|
-
PROJECT_PATTERN = r"^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?$"
|
|
507
|
-
MODEL_ENDPOINT_ID_PATTERN = r"^[a-zA-Z0-9_-]+$"
|
|
508
|
-
RESULT_NAME_PATTERN = r"[a-zA-Z_][a-zA-Z0-9_]*"
|
|
509
517
|
|
|
510
518
|
INTERSECT_DICT_KEYS = {
|
|
511
519
|
ModelEndpointMonitoringMetricType.METRIC: "intersect_metrics",
|
mlrun/config.py
CHANGED
|
@@ -194,6 +194,7 @@ default_config = {
|
|
|
194
194
|
"v3io_framesd": "http://framesd:8080",
|
|
195
195
|
"model_providers": {
|
|
196
196
|
"openai_default_model": "gpt-4o",
|
|
197
|
+
"huggingface_default_model": "microsoft/Phi-3-mini-4k-instruct",
|
|
197
198
|
},
|
|
198
199
|
# default node selector to be applied to all functions - json string base64 encoded format
|
|
199
200
|
"default_function_node_selector": "e30=",
|
|
@@ -1238,6 +1239,19 @@ class Config:
|
|
|
1238
1239
|
"""
|
|
1239
1240
|
return self.is_running_on_iguazio()
|
|
1240
1241
|
|
|
1242
|
+
@staticmethod
|
|
1243
|
+
def get_run_retry_staleness_threshold_timedelta() -> timedelta:
|
|
1244
|
+
"""
|
|
1245
|
+
Get the staleness threshold in timedelta for run retries.
|
|
1246
|
+
This is used to determine if a run is stale and should be retried.
|
|
1247
|
+
|
|
1248
|
+
:return: The staleness threshold in timedelta.
|
|
1249
|
+
"""
|
|
1250
|
+
staleness_threshold = int(
|
|
1251
|
+
mlrun.mlconf.monitoring.runs.retry.staleness_threshold
|
|
1252
|
+
)
|
|
1253
|
+
return timedelta(minutes=staleness_threshold)
|
|
1254
|
+
|
|
1241
1255
|
def to_dict(self):
|
|
1242
1256
|
return copy.deepcopy(self._cfg)
|
|
1243
1257
|
|
mlrun/datastore/datastore.py
CHANGED
|
@@ -38,6 +38,7 @@ from ..utils import DB_SCHEMA, RunKeys
|
|
|
38
38
|
from .base import DataItem, DataStore, HttpStore
|
|
39
39
|
from .filestore import FileStore
|
|
40
40
|
from .inmem import InMemoryStore
|
|
41
|
+
from .model_provider.huggingface_provider import HuggingFaceProvider
|
|
41
42
|
from .model_provider.openai_provider import OpenAIProvider
|
|
42
43
|
from .store_resources import get_store_resource, is_store_uri
|
|
43
44
|
from .v3io import V3ioStore
|
|
@@ -102,8 +103,7 @@ def schema_to_store(schema) -> DataStore.__subclasses__():
|
|
|
102
103
|
def schema_to_model_provider(
|
|
103
104
|
schema: str, raise_missing_schema_exception=True
|
|
104
105
|
) -> type[ModelProvider]:
|
|
105
|
-
|
|
106
|
-
schema_dict = {"openai": OpenAIProvider}
|
|
106
|
+
schema_dict = {"openai": OpenAIProvider, "huggingface": HuggingFaceProvider}
|
|
107
107
|
provider_class = schema_dict.get(schema, None)
|
|
108
108
|
if not provider_class:
|
|
109
109
|
if raise_missing_schema_exception:
|
|
@@ -247,7 +247,7 @@ class StoreManager:
|
|
|
247
247
|
|
|
248
248
|
if schema == "ds":
|
|
249
249
|
datastore_profile = datastore_profile_read(url, project_name, secrets)
|
|
250
|
-
secrets = merge(secrets or {}, datastore_profile.secrets() or {})
|
|
250
|
+
secrets = merge({}, secrets or {}, datastore_profile.secrets() or {})
|
|
251
251
|
url = datastore_profile.url(subpath)
|
|
252
252
|
schema, endpoint, parsed_url = parse_url(url)
|
|
253
253
|
subpath = parsed_url.path
|
|
@@ -281,7 +281,7 @@ class StoreManager:
|
|
|
281
281
|
endpoint, subpath
|
|
282
282
|
)
|
|
283
283
|
remote_client = remote_client_class(
|
|
284
|
-
self, schema, cache_key,
|
|
284
|
+
self, schema, cache_key, endpoint, secrets=secrets, **kwargs
|
|
285
285
|
)
|
|
286
286
|
if not secrets and not mlrun.config.is_running_as_api():
|
|
287
287
|
cache[cache_key] = remote_client
|
|
@@ -486,6 +486,31 @@ class OpenAIProfile(DatastoreProfile):
|
|
|
486
486
|
return f"{self.type}://{subpath.lstrip('/')}"
|
|
487
487
|
|
|
488
488
|
|
|
489
|
+
class HuggingFaceProfile(DatastoreProfile):
|
|
490
|
+
type: str = pydantic.v1.Field("huggingface")
|
|
491
|
+
_private_attributes = ("token", "model_kwargs")
|
|
492
|
+
task: typing.Optional[str] = None
|
|
493
|
+
token: typing.Optional[str] = None
|
|
494
|
+
device: typing.Optional[typing.Union[int, str]] = None
|
|
495
|
+
device_map: typing.Union[str, dict[str, typing.Union[int, str]], None] = None
|
|
496
|
+
trust_remote_code: bool = None
|
|
497
|
+
model_kwargs: typing.Optional[dict[str, typing.Any]] = None
|
|
498
|
+
|
|
499
|
+
def secrets(self) -> dict:
|
|
500
|
+
keys = {
|
|
501
|
+
"HF_TASK": self.task,
|
|
502
|
+
"HF_TOKEN": self.token,
|
|
503
|
+
"HF_DEVICE": self.device,
|
|
504
|
+
"HF_DEVICE_MAP": self.device_map,
|
|
505
|
+
"HF_TRUST_REMOTE_CODE": self.trust_remote_code,
|
|
506
|
+
"HF_MODEL_KWARGS": self.model_kwargs,
|
|
507
|
+
}
|
|
508
|
+
return {k: v for k, v in keys.items() if v}
|
|
509
|
+
|
|
510
|
+
def url(self, subpath):
|
|
511
|
+
return f"{self.type}://{subpath.lstrip('/')}"
|
|
512
|
+
|
|
513
|
+
|
|
489
514
|
_DATASTORE_TYPE_TO_PROFILE_CLASS: dict[str, type[DatastoreProfile]] = {
|
|
490
515
|
"v3io": DatastoreProfileV3io,
|
|
491
516
|
"s3": DatastoreProfileS3,
|
|
@@ -500,6 +525,7 @@ _DATASTORE_TYPE_TO_PROFILE_CLASS: dict[str, type[DatastoreProfile]] = {
|
|
|
500
525
|
"taosws": DatastoreProfileTDEngine,
|
|
501
526
|
"config": ConfigProfile,
|
|
502
527
|
"openai": OpenAIProfile,
|
|
528
|
+
"huggingface": HuggingFaceProfile,
|
|
503
529
|
}
|
|
504
530
|
|
|
505
531
|
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Copyright 2025 Iguazio
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from typing import TYPE_CHECKING, Optional, TypeVar, Union
|
|
16
|
+
|
|
17
|
+
import mlrun
|
|
18
|
+
from mlrun.datastore.model_provider.model_provider import ModelProvider
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from transformers.pipelines.base import Pipeline
|
|
22
|
+
|
|
23
|
+
T = TypeVar("T")
|
|
24
|
+
ChatType = list[dict[str, str]] # according to transformers.pipelines.text_generation
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class HuggingFaceProvider(ModelProvider):
|
|
28
|
+
"""
|
|
29
|
+
HuggingFaceProvider is a wrapper around the Hugging Face Transformers pipeline
|
|
30
|
+
that provides an interface for interacting with a wide range of Hugging Face models.
|
|
31
|
+
|
|
32
|
+
It supports synchronous operations, enabling flexible integration into various workflows.
|
|
33
|
+
|
|
34
|
+
This class extends the ModelProvider base class and implements Hugging Face-specific
|
|
35
|
+
functionality, including pipeline initialization, default text generation operations,
|
|
36
|
+
and custom operations tailored to the Hugging Face Transformers pipeline API.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
parent,
|
|
42
|
+
schema,
|
|
43
|
+
name,
|
|
44
|
+
endpoint="",
|
|
45
|
+
secrets: Optional[dict] = None,
|
|
46
|
+
default_invoke_kwargs: Optional[dict] = None,
|
|
47
|
+
):
|
|
48
|
+
endpoint = endpoint or mlrun.mlconf.model_providers.huggingface_default_model
|
|
49
|
+
if schema != "huggingface":
|
|
50
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
51
|
+
"HuggingFaceProvider supports only 'huggingface' as the provider kind."
|
|
52
|
+
)
|
|
53
|
+
super().__init__(
|
|
54
|
+
parent=parent,
|
|
55
|
+
kind=schema,
|
|
56
|
+
name=name,
|
|
57
|
+
endpoint=endpoint,
|
|
58
|
+
secrets=secrets,
|
|
59
|
+
default_invoke_kwargs=default_invoke_kwargs,
|
|
60
|
+
)
|
|
61
|
+
self.options = self.get_client_options()
|
|
62
|
+
self._expected_operation_type = None
|
|
63
|
+
self.load_client()
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def _extract_string_output(result) -> str:
|
|
67
|
+
"""
|
|
68
|
+
Extracts the first generated string from Hugging Face pipeline output,
|
|
69
|
+
regardless of whether it's plain text-generation or chat-style output.
|
|
70
|
+
"""
|
|
71
|
+
if not isinstance(result, list) or len(result) == 0:
|
|
72
|
+
raise ValueError("Empty or invalid pipeline output")
|
|
73
|
+
|
|
74
|
+
return result[0].get("generated_text")
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def parse_endpoint_and_path(cls, endpoint, subpath) -> (str, str):
|
|
78
|
+
if endpoint and subpath:
|
|
79
|
+
endpoint = endpoint + subpath
|
|
80
|
+
# In HuggingFace, "/" in a model name is part of the name — `subpath` is not used.
|
|
81
|
+
subpath = ""
|
|
82
|
+
return endpoint, subpath
|
|
83
|
+
|
|
84
|
+
def load_client(self) -> None:
|
|
85
|
+
"""
|
|
86
|
+
Initializes the Hugging Face pipeline using the provided options.
|
|
87
|
+
|
|
88
|
+
This method imports the `pipeline` function from the `transformers` package,
|
|
89
|
+
creates a pipeline instance with the specified task and model (from `self.options`),
|
|
90
|
+
and assigns it to `self._client`.
|
|
91
|
+
|
|
92
|
+
Note: Hugging Face pipelines are synchronous and do not support async invocation.
|
|
93
|
+
|
|
94
|
+
Raises:
|
|
95
|
+
ImportError: If the `transformers` package is not installed.
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
from transformers import pipeline, AutoModelForCausalLM # noqa
|
|
99
|
+
from transformers import AutoTokenizer # noqa
|
|
100
|
+
from transformers.pipelines.base import Pipeline # noqa
|
|
101
|
+
|
|
102
|
+
self._client = pipeline(model=self.model, **self.options)
|
|
103
|
+
self._expected_operation_type = Pipeline
|
|
104
|
+
except ImportError as exc:
|
|
105
|
+
raise ImportError("transformers package is not installed") from exc
|
|
106
|
+
|
|
107
|
+
def get_client_options(self):
|
|
108
|
+
res = dict(
|
|
109
|
+
task=self._get_secret_or_env("HF_TASK") or "text-generation",
|
|
110
|
+
token=self._get_secret_or_env("HF_TOKEN"),
|
|
111
|
+
device=self._get_secret_or_env("HF_DEVICE"),
|
|
112
|
+
device_map=self._get_secret_or_env("HF_DEVICE_MAP"),
|
|
113
|
+
trust_remote_code=self._get_secret_or_env("HF_TRUST_REMOTE_CODE"),
|
|
114
|
+
model_kwargs=self._get_secret_or_env("HF_MODEL_KWARGS"),
|
|
115
|
+
)
|
|
116
|
+
return self._sanitize_options(res)
|
|
117
|
+
|
|
118
|
+
def custom_invoke(
|
|
119
|
+
self, operation: Optional["Pipeline"] = None, **invoke_kwargs
|
|
120
|
+
) -> Optional[T]:
|
|
121
|
+
"""
|
|
122
|
+
HuggingFace implementation of `ModelProvider.custom_invoke`.
|
|
123
|
+
Use the default config in provider client/ user defined client:
|
|
124
|
+
|
|
125
|
+
Example:
|
|
126
|
+
```python
|
|
127
|
+
image = Image.open(image_path)
|
|
128
|
+
pipeline_object = pipeline("image-classification", model="microsoft/resnet-50")
|
|
129
|
+
result = hf_provider.custom_invoke(
|
|
130
|
+
pipeline_object,
|
|
131
|
+
inputs=image,
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
:param operation: A pipeline object
|
|
137
|
+
:param invoke_kwargs: Keyword arguments to pass to the operation.
|
|
138
|
+
:return: The full response returned by the operation.
|
|
139
|
+
|
|
140
|
+
"""
|
|
141
|
+
invoke_kwargs = self.get_invoke_kwargs(invoke_kwargs)
|
|
142
|
+
if operation:
|
|
143
|
+
if not isinstance(operation, self._expected_operation_type):
|
|
144
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
145
|
+
"Huggingface operation must inherit" " from 'Pipeline' object"
|
|
146
|
+
)
|
|
147
|
+
return operation(**invoke_kwargs)
|
|
148
|
+
else:
|
|
149
|
+
return self.client(**invoke_kwargs)
|
|
150
|
+
|
|
151
|
+
def invoke(
|
|
152
|
+
self,
|
|
153
|
+
messages: Union[str, list[str], ChatType, list[ChatType]] = None,
|
|
154
|
+
as_str: bool = False,
|
|
155
|
+
**invoke_kwargs,
|
|
156
|
+
) -> Optional[Union[str, list, T]]:
|
|
157
|
+
"""
|
|
158
|
+
HuggingFace-specific implementation of `ModelProvider.invoke`.
|
|
159
|
+
Invokes a HuggingFace model operation using the synchronous client.
|
|
160
|
+
For complete usage details, refer to `ModelProvider.invoke`.
|
|
161
|
+
:param messages:
|
|
162
|
+
Same as ModelProvider.invoke.
|
|
163
|
+
|
|
164
|
+
:param as_str:
|
|
165
|
+
If `True`, returns only the main content from a single response
|
|
166
|
+
(intended for single-response use cases).
|
|
167
|
+
If `False`, returns the full response object, whose type depends on
|
|
168
|
+
the client (e.g., `pipeline`).
|
|
169
|
+
|
|
170
|
+
:param invoke_kwargs:
|
|
171
|
+
Same as ModelProvider.invoke.
|
|
172
|
+
:return: Same as ModelProvider.invoke.
|
|
173
|
+
"""
|
|
174
|
+
if self.client.task != "text-generation":
|
|
175
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
176
|
+
"HuggingFaceProvider.invoke supports text-generation task only"
|
|
177
|
+
)
|
|
178
|
+
if as_str:
|
|
179
|
+
invoke_kwargs["return_full_text"] = False
|
|
180
|
+
response = self.custom_invoke(text_inputs=messages, **invoke_kwargs)
|
|
181
|
+
if as_str:
|
|
182
|
+
return self._extract_string_output(response)
|
|
183
|
+
return response
|
|
@@ -148,7 +148,12 @@ class ModelProvider(BaseRemoteClient):
|
|
|
148
148
|
|
|
149
149
|
@property
|
|
150
150
|
def model(self) -> Optional[str]:
|
|
151
|
-
|
|
151
|
+
"""
|
|
152
|
+
Returns the model identifier used by the underlying SDK.
|
|
153
|
+
|
|
154
|
+
:return: A string representing the model ID, or None if not set.
|
|
155
|
+
"""
|
|
156
|
+
return self.endpoint
|
|
152
157
|
|
|
153
158
|
def get_invoke_kwargs(self, invoke_kwargs) -> dict:
|
|
154
159
|
kwargs = self.default_invoke_kwargs.copy()
|
|
@@ -11,11 +11,13 @@
|
|
|
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
|
+
import inspect
|
|
14
15
|
from collections.abc import Awaitable
|
|
15
16
|
from typing import Callable, Optional, TypeVar, Union
|
|
16
17
|
|
|
17
18
|
import mlrun
|
|
18
19
|
from mlrun.datastore.model_provider.model_provider import ModelProvider
|
|
20
|
+
from mlrun.datastore.utils import accepts_param
|
|
19
21
|
|
|
20
22
|
T = TypeVar("T")
|
|
21
23
|
|
|
@@ -68,10 +70,6 @@ class OpenAIProvider(ModelProvider):
|
|
|
68
70
|
subpath = ""
|
|
69
71
|
return endpoint, subpath
|
|
70
72
|
|
|
71
|
-
@property
|
|
72
|
-
def model(self) -> Optional[str]:
|
|
73
|
-
return self.endpoint
|
|
74
|
-
|
|
75
73
|
def load_client(self) -> None:
|
|
76
74
|
"""
|
|
77
75
|
Initializes the OpenAI SDK client using the provided options.
|
|
@@ -126,12 +124,18 @@ class OpenAIProvider(ModelProvider):
|
|
|
126
124
|
|
|
127
125
|
"""
|
|
128
126
|
invoke_kwargs = self.get_invoke_kwargs(invoke_kwargs)
|
|
127
|
+
model_kwargs = {"model": invoke_kwargs.pop("model", None) or self.model}
|
|
128
|
+
|
|
129
129
|
if operation:
|
|
130
|
-
|
|
130
|
+
if not callable(operation):
|
|
131
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
132
|
+
"OpenAI custom_invoke operation must be a callable"
|
|
133
|
+
)
|
|
134
|
+
if not accepts_param(operation, "model"):
|
|
135
|
+
model_kwargs = {}
|
|
136
|
+
return operation(**invoke_kwargs, **model_kwargs)
|
|
131
137
|
else:
|
|
132
|
-
return self.client.chat.completions.create(
|
|
133
|
-
**invoke_kwargs, model=self.model
|
|
134
|
-
)
|
|
138
|
+
return self.client.chat.completions.create(**invoke_kwargs, **model_kwargs)
|
|
135
139
|
|
|
136
140
|
async def async_custom_invoke(
|
|
137
141
|
self,
|
|
@@ -145,25 +149,33 @@ class OpenAIProvider(ModelProvider):
|
|
|
145
149
|
`ModelProvider.async_custom_invoke`.
|
|
146
150
|
|
|
147
151
|
Example:
|
|
148
|
-
|
|
152
|
+
```python
|
|
149
153
|
result = openai_model_provider.invoke(
|
|
150
154
|
openai_model_provider.async_client.images.generate,
|
|
151
155
|
prompt="A futuristic cityscape at sunset",
|
|
152
156
|
n=1,
|
|
153
157
|
size="1024x1024",
|
|
154
158
|
)
|
|
155
|
-
|
|
159
|
+
```
|
|
160
|
+
|
|
156
161
|
:param operation: Same as ModelProvider.async_custom_invoke.
|
|
157
162
|
:param invoke_kwargs: Same as ModelProvider.async_custom_invoke.
|
|
158
163
|
:return: Same as ModelProvider.async_custom_invoke.
|
|
159
164
|
|
|
160
165
|
"""
|
|
161
166
|
invoke_kwargs = self.get_invoke_kwargs(invoke_kwargs)
|
|
167
|
+
model_kwargs = {"model": invoke_kwargs.pop("model", None) or self.model}
|
|
162
168
|
if operation:
|
|
163
|
-
|
|
169
|
+
if not inspect.iscoroutinefunction(operation):
|
|
170
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
171
|
+
"OpenAI async_custom_invoke operation must be a coroutine function"
|
|
172
|
+
)
|
|
173
|
+
if not accepts_param(operation, "model"):
|
|
174
|
+
model_kwargs = {}
|
|
175
|
+
return await operation(**invoke_kwargs, **model_kwargs)
|
|
164
176
|
else:
|
|
165
177
|
return await self.async_client.chat.completions.create(
|
|
166
|
-
**invoke_kwargs,
|
|
178
|
+
**invoke_kwargs, **model_kwargs
|
|
167
179
|
)
|
|
168
180
|
|
|
169
181
|
def invoke(
|
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
|
|
@@ -333,3 +334,8 @@ def parse_url(url):
|
|
|
333
334
|
if parsed_url.port:
|
|
334
335
|
endpoint += f":{parsed_url.port}"
|
|
335
336
|
return schema, endpoint, parsed_url
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def accepts_param(func: callable, param_name):
|
|
340
|
+
sig = inspect.signature(func)
|
|
341
|
+
return param_name in sig.parameters
|
mlrun/model_monitoring/api.py
CHANGED
|
@@ -19,8 +19,6 @@ from datetime import datetime
|
|
|
19
19
|
import numpy as np
|
|
20
20
|
import pandas as pd
|
|
21
21
|
|
|
22
|
-
import mlrun.artifacts
|
|
23
|
-
import mlrun.common.helpers
|
|
24
22
|
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
25
23
|
import mlrun.datastore.base
|
|
26
24
|
import mlrun.feature_store
|
|
@@ -538,10 +536,14 @@ def _create_model_monitoring_function_base(
|
|
|
538
536
|
This function does not set the labels or mounts v3io.
|
|
539
537
|
"""
|
|
540
538
|
if name in mm_constants._RESERVED_FUNCTION_NAMES:
|
|
541
|
-
raise mlrun.errors.
|
|
539
|
+
raise mlrun.errors.MLRunValueError(
|
|
542
540
|
"An application cannot have the following names: "
|
|
543
541
|
f"{mm_constants._RESERVED_FUNCTION_NAMES}"
|
|
544
542
|
)
|
|
543
|
+
if name and name.endswith(mm_constants._RESERVED_EVALUATE_FUNCTION_SUFFIX):
|
|
544
|
+
raise mlrun.errors.MLRunValueError(
|
|
545
|
+
"Model monitoring application names cannot end with `-batch`"
|
|
546
|
+
)
|
|
545
547
|
if func is None:
|
|
546
548
|
func = ""
|
|
547
549
|
func_obj = typing.cast(
|
|
@@ -25,6 +25,7 @@ import pandas as pd
|
|
|
25
25
|
|
|
26
26
|
import mlrun
|
|
27
27
|
import mlrun.common.constants as mlrun_constants
|
|
28
|
+
import mlrun.common.helpers
|
|
28
29
|
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
29
30
|
import mlrun.datastore.datastore_profile as ds_profile
|
|
30
31
|
import mlrun.errors
|
|
@@ -33,6 +34,7 @@ import mlrun.model_monitoring.applications.context as mm_context
|
|
|
33
34
|
import mlrun.model_monitoring.applications.results as mm_results
|
|
34
35
|
import mlrun.model_monitoring.db._schedules as mm_schedules
|
|
35
36
|
import mlrun.model_monitoring.helpers as mm_helpers
|
|
37
|
+
import mlrun.utils
|
|
36
38
|
from mlrun.serving.utils import MonitoringApplicationToDict
|
|
37
39
|
from mlrun.utils import logger
|
|
38
40
|
|
|
@@ -194,7 +196,25 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
194
196
|
Optional[mm_schedules.ModelMonitoringSchedulesFileApplication],
|
|
195
197
|
]
|
|
196
198
|
]:
|
|
197
|
-
endpoints_output: dict[
|
|
199
|
+
endpoints_output: dict[
|
|
200
|
+
str,
|
|
201
|
+
list[
|
|
202
|
+
tuple[
|
|
203
|
+
mm_context.MonitoringApplicationContext,
|
|
204
|
+
Union[
|
|
205
|
+
mm_results.ModelMonitoringApplicationResult,
|
|
206
|
+
mm_results.ModelMonitoringApplicationMetric,
|
|
207
|
+
list[
|
|
208
|
+
Union[
|
|
209
|
+
mm_results.ModelMonitoringApplicationResult,
|
|
210
|
+
mm_results.ModelMonitoringApplicationMetric,
|
|
211
|
+
mm_results._ModelMonitoringApplicationStats,
|
|
212
|
+
]
|
|
213
|
+
],
|
|
214
|
+
],
|
|
215
|
+
]
|
|
216
|
+
],
|
|
217
|
+
] = defaultdict(list)
|
|
198
218
|
application_schedules = nullcontext()
|
|
199
219
|
if write_output:
|
|
200
220
|
cls._check_writer_is_up(project)
|
|
@@ -220,11 +240,21 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
220
240
|
profile=stream_profile,
|
|
221
241
|
)
|
|
222
242
|
for endpoint_id, outputs in endpoints_output.items():
|
|
243
|
+
writer_events = []
|
|
244
|
+
for ctx, res in outputs:
|
|
245
|
+
if isinstance(res, list):
|
|
246
|
+
writer_events.extend(
|
|
247
|
+
_serialize_context_and_result(
|
|
248
|
+
context=ctx, result=sub_res
|
|
249
|
+
)
|
|
250
|
+
for sub_res in res
|
|
251
|
+
)
|
|
252
|
+
else:
|
|
253
|
+
writer_events.append(
|
|
254
|
+
_serialize_context_and_result(context=ctx, result=res)
|
|
255
|
+
)
|
|
223
256
|
writer_stream.push(
|
|
224
|
-
|
|
225
|
-
_serialize_context_and_result(context=ctx, result=res)
|
|
226
|
-
for ctx, res in outputs
|
|
227
|
-
],
|
|
257
|
+
writer_events,
|
|
228
258
|
partition_key=endpoint_id,
|
|
229
259
|
)
|
|
230
260
|
logger.debug(
|
|
@@ -238,6 +268,14 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
238
268
|
)
|
|
239
269
|
application_schedules.__exit__(None, None, None)
|
|
240
270
|
|
|
271
|
+
@classmethod
|
|
272
|
+
def _get_application_name(cls, context: "mlrun.MLClientCtx") -> str:
|
|
273
|
+
"""Get the application name from the context via the function URI"""
|
|
274
|
+
_, application_name, _, _ = mlrun.common.helpers.parse_versioned_object_uri(
|
|
275
|
+
context.to_dict().get("spec", {}).get("function", "")
|
|
276
|
+
)
|
|
277
|
+
return application_name
|
|
278
|
+
|
|
241
279
|
def _handler(
|
|
242
280
|
self,
|
|
243
281
|
context: "mlrun.MLClientCtx",
|
|
@@ -250,7 +288,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
250
288
|
end: Optional[str] = None,
|
|
251
289
|
base_period: Optional[int] = None,
|
|
252
290
|
write_output: bool = False,
|
|
253
|
-
|
|
291
|
+
fail_on_overlap: bool = True,
|
|
254
292
|
stream_profile: Optional[ds_profile.DatastoreProfile] = None,
|
|
255
293
|
):
|
|
256
294
|
"""
|
|
@@ -271,7 +309,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
271
309
|
"working with endpoints, without any custom data-frame input"
|
|
272
310
|
)
|
|
273
311
|
|
|
274
|
-
application_name = self.
|
|
312
|
+
application_name = self._get_application_name(context)
|
|
275
313
|
|
|
276
314
|
feature_stats = (
|
|
277
315
|
mm_api.get_sample_set_statistics(reference_data)
|
|
@@ -320,7 +358,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
320
358
|
application_schedules=application_schedules,
|
|
321
359
|
endpoint_id=endpoint_id,
|
|
322
360
|
application_name=application_name,
|
|
323
|
-
|
|
361
|
+
fail_on_overlap=fail_on_overlap,
|
|
324
362
|
):
|
|
325
363
|
result = call_do_tracking(
|
|
326
364
|
event={
|
|
@@ -443,7 +481,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
443
481
|
end_dt: datetime,
|
|
444
482
|
base_period: Optional[int],
|
|
445
483
|
application_name: str,
|
|
446
|
-
|
|
484
|
+
fail_on_overlap: bool,
|
|
447
485
|
) -> datetime:
|
|
448
486
|
"""Make sure that the (app, endpoint) pair doesn't write output before the last analyzed window"""
|
|
449
487
|
if application_schedules:
|
|
@@ -452,7 +490,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
452
490
|
)
|
|
453
491
|
if last_analyzed:
|
|
454
492
|
if start_dt < last_analyzed:
|
|
455
|
-
if
|
|
493
|
+
if not fail_on_overlap:
|
|
456
494
|
if last_analyzed < end_dt and base_period is None:
|
|
457
495
|
logger.warn(
|
|
458
496
|
"Setting the start time to last_analyzed since the original start time precedes "
|
|
@@ -499,7 +537,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
499
537
|
],
|
|
500
538
|
endpoint_id: str,
|
|
501
539
|
application_name: str,
|
|
502
|
-
|
|
540
|
+
fail_on_overlap: bool,
|
|
503
541
|
) -> Iterator[tuple[Optional[datetime], Optional[datetime]]]:
|
|
504
542
|
if start is None or end is None:
|
|
505
543
|
# A single window based on the `sample_data` input - see `_handler`.
|
|
@@ -516,7 +554,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
516
554
|
end_dt=end_dt,
|
|
517
555
|
base_period=base_period,
|
|
518
556
|
application_name=application_name,
|
|
519
|
-
|
|
557
|
+
fail_on_overlap=fail_on_overlap,
|
|
520
558
|
)
|
|
521
559
|
|
|
522
560
|
if base_period is None:
|
|
@@ -589,6 +627,42 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
589
627
|
"""
|
|
590
628
|
return f"{handler_to_class}::{cls._handler.__name__}"
|
|
591
629
|
|
|
630
|
+
@classmethod
|
|
631
|
+
def _determine_job_name(
|
|
632
|
+
cls,
|
|
633
|
+
*,
|
|
634
|
+
func_name: Optional[str],
|
|
635
|
+
class_handler: Optional[str],
|
|
636
|
+
handler_to_class: str,
|
|
637
|
+
) -> str:
|
|
638
|
+
"""
|
|
639
|
+
Determine the batch app's job name. This name is used also as the application name,
|
|
640
|
+
which is retrieved in `_get_application_name`.
|
|
641
|
+
"""
|
|
642
|
+
if func_name:
|
|
643
|
+
job_name = func_name
|
|
644
|
+
else:
|
|
645
|
+
if not class_handler:
|
|
646
|
+
class_name = cls.__name__
|
|
647
|
+
else:
|
|
648
|
+
class_name = handler_to_class.split(".")[-1].split("::")[0]
|
|
649
|
+
|
|
650
|
+
job_name = mlrun.utils.normalize_name(class_name, verbose=False)
|
|
651
|
+
|
|
652
|
+
if not mm_constants.APP_NAME_REGEX.fullmatch(job_name):
|
|
653
|
+
raise mlrun.errors.MLRunValueError(
|
|
654
|
+
"The function name does not comply with the required pattern "
|
|
655
|
+
f"`{mm_constants.APP_NAME_REGEX.pattern}`. "
|
|
656
|
+
"Please choose another `func_name`."
|
|
657
|
+
)
|
|
658
|
+
if not job_name.endswith(mm_constants._RESERVED_EVALUATE_FUNCTION_SUFFIX):
|
|
659
|
+
job_name += mm_constants._RESERVED_EVALUATE_FUNCTION_SUFFIX
|
|
660
|
+
mlrun.utils.logger.info(
|
|
661
|
+
'Changing function name - adding `"-batch"` suffix', func_name=job_name
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
return job_name
|
|
665
|
+
|
|
592
666
|
@classmethod
|
|
593
667
|
def to_job(
|
|
594
668
|
cls,
|
|
@@ -628,7 +702,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
628
702
|
* ``end``, ``datetime``
|
|
629
703
|
* ``base_period``, ``int``
|
|
630
704
|
* ``write_output``, ``bool``
|
|
631
|
-
* ``
|
|
705
|
+
* ``fail_on_overlap``, ``bool``
|
|
632
706
|
|
|
633
707
|
For Git sources, add the source archive to the returned job and change the handler:
|
|
634
708
|
|
|
@@ -647,7 +721,10 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
647
721
|
:py:class:`~mlrun.model_monitoring.applications.ModelMonitoringApplicationBase`,
|
|
648
722
|
is used.
|
|
649
723
|
:param func_path: The path to the function. If ``None``, the current notebook is used.
|
|
650
|
-
:param func_name: The name of the function. If
|
|
724
|
+
:param func_name: The name of the function. If ``None``, the normalized class name is used
|
|
725
|
+
(:py:meth:`mlrun.utils.helpers.normalize_name`).
|
|
726
|
+
A ``"-batch"`` suffix is guaranteed to be added if not already there.
|
|
727
|
+
The function name is also used as the application name to use for the results.
|
|
651
728
|
:param tag: Tag for the function.
|
|
652
729
|
:param image: Docker image to run the job on (when running remotely).
|
|
653
730
|
:param with_repo: Whether to clone the current repo to the build source.
|
|
@@ -668,12 +745,11 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
668
745
|
handler_to_class = class_handler or cls.__name__
|
|
669
746
|
handler = cls.get_job_handler(handler_to_class)
|
|
670
747
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
job_name = func_name if func_name else class_name
|
|
748
|
+
job_name = cls._determine_job_name(
|
|
749
|
+
func_name=func_name,
|
|
750
|
+
class_handler=class_handler,
|
|
751
|
+
handler_to_class=handler_to_class,
|
|
752
|
+
)
|
|
677
753
|
|
|
678
754
|
job = cast(
|
|
679
755
|
mlrun.runtimes.KubejobRuntime,
|
|
@@ -712,7 +788,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
712
788
|
end: Optional[datetime] = None,
|
|
713
789
|
base_period: Optional[int] = None,
|
|
714
790
|
write_output: bool = False,
|
|
715
|
-
|
|
791
|
+
fail_on_overlap: bool = True,
|
|
716
792
|
stream_profile: Optional[ds_profile.DatastoreProfile] = None,
|
|
717
793
|
) -> "mlrun.RunObject":
|
|
718
794
|
"""
|
|
@@ -724,7 +800,10 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
724
800
|
data to the application.
|
|
725
801
|
|
|
726
802
|
:param func_path: The path to the function. If ``None``, the current notebook is used.
|
|
727
|
-
:param func_name: The name of the function. If
|
|
803
|
+
:param func_name: The name of the function. If ``None``, the normalized class name is used
|
|
804
|
+
(:py:meth:`mlrun.utils.helpers.normalize_name`).
|
|
805
|
+
A ``"-batch"`` suffix is guaranteed to be added if not already there.
|
|
806
|
+
The function name is also used as the application name to use for the results.
|
|
728
807
|
:param tag: Tag for the function.
|
|
729
808
|
:param run_local: Whether to run the function locally or remotely.
|
|
730
809
|
:param auto_build: Whether to auto build the function.
|
|
@@ -777,11 +856,11 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
777
856
|
:param write_output: Whether to write the results and metrics to the time-series DB. Can be ``True`` only
|
|
778
857
|
if ``endpoints`` are passed.
|
|
779
858
|
Note: the model monitoring infrastructure must be up for the writing to work.
|
|
780
|
-
:param
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
859
|
+
:param fail_on_overlap: Relevant only when ``write_output=True``. When ``True``, and the
|
|
860
|
+
requested ``start`` time precedes the ``end`` time of a previous run that also
|
|
861
|
+
wrote to the database - an error is raised.
|
|
862
|
+
If ``False``, when the previously described situation occurs, the relevant time
|
|
863
|
+
window is cut so that it starts at the earliest possible time after ``start``.
|
|
785
864
|
:param stream_profile: The stream datastore profile. It should be provided only when running locally and
|
|
786
865
|
writing the outputs to the database (i.e., when both ``run_local`` and
|
|
787
866
|
``write_output`` are set to ``True``).
|
|
@@ -821,7 +900,7 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
821
900
|
params["end"] = end.isoformat() if isinstance(end, datetime) else end
|
|
822
901
|
params["base_period"] = base_period
|
|
823
902
|
params["write_output"] = write_output
|
|
824
|
-
params["
|
|
903
|
+
params["fail_on_overlap"] = fail_on_overlap
|
|
825
904
|
if stream_profile:
|
|
826
905
|
if not run_local:
|
|
827
906
|
raise mlrun.errors.MLRunValueError(
|
|
@@ -14,16 +14,13 @@
|
|
|
14
14
|
|
|
15
15
|
import dataclasses
|
|
16
16
|
import json
|
|
17
|
-
import re
|
|
18
17
|
from abc import ABC, abstractmethod
|
|
19
18
|
|
|
20
19
|
from pydantic.v1 import validator
|
|
21
20
|
from pydantic.v1.dataclasses import dataclass
|
|
22
21
|
|
|
23
|
-
import mlrun.common.helpers
|
|
24
|
-
import mlrun.common.model_monitoring.helpers
|
|
25
22
|
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
26
|
-
import mlrun.
|
|
23
|
+
import mlrun.errors
|
|
27
24
|
from mlrun.utils import logger
|
|
28
25
|
|
|
29
26
|
_RESULT_EXTRA_DATA_MAX_SIZE = 998
|
|
@@ -33,10 +30,10 @@ class _ModelMonitoringApplicationDataRes(ABC):
|
|
|
33
30
|
name: str
|
|
34
31
|
|
|
35
32
|
def __post_init__(self):
|
|
36
|
-
|
|
37
|
-
if not re.fullmatch(pat, self.name):
|
|
33
|
+
if not mm_constants.RESULT_NAME_REGEX.fullmatch(self.name):
|
|
38
34
|
raise mlrun.errors.MLRunValueError(
|
|
39
|
-
|
|
35
|
+
"The application result or metric name must comply with the regex "
|
|
36
|
+
f"`{mm_constants.RESULT_NAME_REGEX.pattern}`"
|
|
40
37
|
)
|
|
41
38
|
|
|
42
39
|
@abstractmethod
|
mlrun/run.py
CHANGED
|
@@ -1184,11 +1184,13 @@ def get_model_provider(
|
|
|
1184
1184
|
raise_missing_schema_exception=True,
|
|
1185
1185
|
) -> ModelProvider:
|
|
1186
1186
|
"""get mlrun dataitem object (from path/url)"""
|
|
1187
|
-
|
|
1187
|
+
# without caching secrets
|
|
1188
|
+
store_manager.set(db=db)
|
|
1188
1189
|
return store_manager.model_provider_object(
|
|
1189
1190
|
url=url,
|
|
1190
1191
|
default_invoke_kwargs=default_invoke_kwargs,
|
|
1191
1192
|
raise_missing_schema_exception=raise_missing_schema_exception,
|
|
1193
|
+
secrets=secrets,
|
|
1192
1194
|
)
|
|
1193
1195
|
|
|
1194
1196
|
|
mlrun/serving/states.py
CHANGED
|
@@ -1206,7 +1206,7 @@ class Model(storey.ParallelExecutionRunnable, ModelObj):
|
|
|
1206
1206
|
|
|
1207
1207
|
class LLModel(Model):
|
|
1208
1208
|
def __init__(
|
|
1209
|
-
self, name: str, input_path: Optional[Union[str, list[str]]], **kwargs
|
|
1209
|
+
self, name: str, input_path: Optional[Union[str, list[str]]] = None, **kwargs
|
|
1210
1210
|
):
|
|
1211
1211
|
super().__init__(name, **kwargs)
|
|
1212
1212
|
self._input_path = split_path(input_path)
|
mlrun/utils/helpers.py
CHANGED
|
@@ -800,7 +800,12 @@ def remove_tag_from_artifact_uri(uri: str) -> Optional[str]:
|
|
|
800
800
|
"store://key:tag" => "store://key"
|
|
801
801
|
"store://models/remote-model-project/my_model#0@tree" => unchanged (no tag)
|
|
802
802
|
"""
|
|
803
|
-
|
|
803
|
+
add_store = False
|
|
804
|
+
if mlrun.datastore.is_store_uri(uri):
|
|
805
|
+
uri = uri.removeprefix(DB_SCHEMA + "://")
|
|
806
|
+
add_store = True
|
|
807
|
+
uri = re.sub(r"(#[^:@\s]*)?:[^@^:\s]+(?=(@|\^|$))", lambda m: m.group(1) or "", uri)
|
|
808
|
+
return uri if not add_store else DB_SCHEMA + "://" + uri
|
|
804
809
|
|
|
805
810
|
|
|
806
811
|
def extend_hub_uri_if_needed(uri) -> tuple[str, bool]:
|
mlrun/utils/logger.py
CHANGED
|
@@ -393,12 +393,14 @@ def resolve_formatter_by_kind(
|
|
|
393
393
|
|
|
394
394
|
|
|
395
395
|
def create_test_logger(name: str = "mlrun", stream: IO[str] = stdout) -> Logger:
|
|
396
|
-
|
|
396
|
+
logger = create_logger(
|
|
397
397
|
level="debug",
|
|
398
398
|
formatter_kind=FormatterKinds.HUMAN_EXTENDED.name,
|
|
399
399
|
name=name,
|
|
400
400
|
stream=stream,
|
|
401
401
|
)
|
|
402
|
+
logger._logger.propagate = True # pass records up to pytest’s handler
|
|
403
|
+
return logger
|
|
402
404
|
|
|
403
405
|
|
|
404
406
|
def create_logger(
|
mlrun/utils/version/version.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mlrun
|
|
3
|
-
Version: 1.10.
|
|
3
|
+
Version: 1.10.0rc18
|
|
4
4
|
Summary: Tracking and config of machine learning runs
|
|
5
5
|
Home-page: https://github.com/mlrun/mlrun
|
|
6
6
|
Author: Yaron Haviv
|
|
@@ -44,7 +44,7 @@ Requires-Dist: semver~=3.0
|
|
|
44
44
|
Requires-Dist: dependency-injector~=4.41
|
|
45
45
|
Requires-Dist: fsspec<2024.7,>=2023.9.2
|
|
46
46
|
Requires-Dist: v3iofs~=0.1.17
|
|
47
|
-
Requires-Dist: storey~=1.10.
|
|
47
|
+
Requires-Dist: storey~=1.10.9
|
|
48
48
|
Requires-Dist: inflection~=0.5.0
|
|
49
49
|
Requires-Dist: python-dotenv~=1.0
|
|
50
50
|
Requires-Dist: setuptools>=75.2
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
mlrun/__init__.py,sha256=Y_AFhZV1hEx4vfiO-cyjup0aLGcp6R0SeL75GqLFQrc,7514
|
|
2
2
|
mlrun/__main__.py,sha256=wQNaxW7QsqFBtWffnPkw-497fnpsrQzUnscBQQAP_UM,48364
|
|
3
|
-
mlrun/config.py,sha256=
|
|
3
|
+
mlrun/config.py,sha256=XAAb68MwEHpuPddPMtKBULtFk0hI9YC25DniYQk1DKk,72853
|
|
4
4
|
mlrun/errors.py,sha256=bAk0t_qmCxQSPNK0TugOAfA5R6f0G6OYvEvXUWSJ_5U,9062
|
|
5
5
|
mlrun/execution.py,sha256=dJ4PFwg5AlDHbCL2Q9dVDjWA_i64UTq2qBiF8kTU9tw,56922
|
|
6
6
|
mlrun/features.py,sha256=jMEXo6NB36A6iaxNEJWzdtYwUmglYD90OIKTIEeWhE8,15841
|
|
@@ -8,7 +8,7 @@ mlrun/k8s_utils.py,sha256=mMnGyouHoJC93ZD2KGf9neJM1pD7mR9IXLnHOEwYVTQ,21469
|
|
|
8
8
|
mlrun/lists.py,sha256=OlaV2QIFUzmenad9kxNJ3k4whlDyxI3zFbGwr6vpC5Y,8561
|
|
9
9
|
mlrun/model.py,sha256=wHtM8LylSOEFk6Hxl95CVm8DOPhofjsANYdIvKHH6dw,88956
|
|
10
10
|
mlrun/render.py,sha256=5DlhD6JtzHgmj5RVlpaYiHGhX84Q7qdi4RCEUj2UMgw,13195
|
|
11
|
-
mlrun/run.py,sha256=
|
|
11
|
+
mlrun/run.py,sha256=6I4I_88Slpj27WzGnoq0c02SNpDaGsJllC8Y-2BED0E,46951
|
|
12
12
|
mlrun/secrets.py,sha256=dZPdkc_zzfscVQepOHUwmzFqnBavDCBXV9DQoH_eIYM,7800
|
|
13
13
|
mlrun/alerts/__init__.py,sha256=0gtG1BG0DXxFrXegIkjbM1XEN4sP9ODo0ucXrNld1hU,601
|
|
14
14
|
mlrun/alerts/alert.py,sha256=QQFZGydQbx9RvAaSiaH-ALQZVcDKQX5lgizqj_rXW2k,15948
|
|
@@ -30,7 +30,7 @@ mlrun/common/types.py,sha256=1gxThbmC0Vd0U1ffIkEwz4T4S7JOgHt70rvw8TCO21c,1073
|
|
|
30
30
|
mlrun/common/db/__init__.py,sha256=kXGBqhLN0rlAx0kTXhozGzFsIdSqW0uTSKMmsLgq_is,569
|
|
31
31
|
mlrun/common/db/dialects.py,sha256=QN9bx7CTo32IIdJ2J3ZrsX8IUdp_BPxBtl0LyjMEC9g,868
|
|
32
32
|
mlrun/common/formatters/__init__.py,sha256=au7S3M3wa9964RpQhFSvflk5-i5SWMeb3kek8Gvt4kg,889
|
|
33
|
-
mlrun/common/formatters/artifact.py,sha256=
|
|
33
|
+
mlrun/common/formatters/artifact.py,sha256=JRs1B3nEvTdn4h69FLHjZZY_Dy40E9-BgvxfjcL_DKU,1503
|
|
34
34
|
mlrun/common/formatters/base.py,sha256=85vQ0t4ZfqCs8b8QV1RLfRcEvANztKvTvsa__sD3zTo,4099
|
|
35
35
|
mlrun/common/formatters/feature_set.py,sha256=SmSSiNYXZtmzQDCvQJhIpArtjOInmDN0g1xFqrDdWsw,1534
|
|
36
36
|
mlrun/common/formatters/function.py,sha256=H8bHilWSn1NXm5WWlFH2dx8slomCGm3e8ghsmWOOh48,1493
|
|
@@ -75,7 +75,7 @@ mlrun/common/schemas/serving.py,sha256=4ek9JZDagkdeXyfkX6P6xp4deUNSf_kqXUaXcKSuv
|
|
|
75
75
|
mlrun/common/schemas/tag.py,sha256=1wqEiAujsElojWb3qmuyfcaLFjXSNAAQdafkDx7fkn0,891
|
|
76
76
|
mlrun/common/schemas/workflow.py,sha256=Y-FHJnxs5c86yetuOAPdEJPkne__tLPCxjSXSb4lrjo,2541
|
|
77
77
|
mlrun/common/schemas/model_monitoring/__init__.py,sha256=FqFiFIDcylquQdY0XTBamB5kMzMrMFEpVYM_ecsVfLg,1925
|
|
78
|
-
mlrun/common/schemas/model_monitoring/constants.py,sha256=
|
|
78
|
+
mlrun/common/schemas/model_monitoring/constants.py,sha256=WhuUWgOwk91BI0dP5c1rm6X_W0V4UBV3l6KvRNHHE-E,13898
|
|
79
79
|
mlrun/common/schemas/model_monitoring/functions.py,sha256=GpfSGp05D87wEKemECD3USL368pvnAM2WfS-nef5qOg,2210
|
|
80
80
|
mlrun/common/schemas/model_monitoring/grafana.py,sha256=THQlLfPBevBksta8p5OaIsBaJtsNSXexLvHrDxOaVns,2095
|
|
81
81
|
mlrun/common/schemas/model_monitoring/model_endpoints.py,sha256=aCrVqgoJsUEwvDJ84YFabDSy78CHcVBV3b0RdWj4JUw,13250
|
|
@@ -88,8 +88,8 @@ mlrun/datastore/__init__.py,sha256=UKGHkUfj5A9dqTiOeW4UScvwv0vq91NS_xs3-_QA7Jk,5
|
|
|
88
88
|
mlrun/datastore/alibaba_oss.py,sha256=E0t0-e9Me2t2Mux2LWdC9riOG921TgNjhoy897JJX7o,4932
|
|
89
89
|
mlrun/datastore/azure_blob.py,sha256=3LG7tOTwT97ZFBmyq-sfAIe5_SkuFgisRQtipv4kKUw,12779
|
|
90
90
|
mlrun/datastore/base.py,sha256=yLdnFCL2k_rcasdbxXjnQr7Lwm-A79LnW9AITtn9-p4,25450
|
|
91
|
-
mlrun/datastore/datastore.py,sha256=
|
|
92
|
-
mlrun/datastore/datastore_profile.py,sha256=
|
|
91
|
+
mlrun/datastore/datastore.py,sha256=gOlMyPDelD9CRieoraDPYf1NNig_GrQRuuQxLmRq8Bo,13298
|
|
92
|
+
mlrun/datastore/datastore_profile.py,sha256=Y4VtaatIK4UXuTdpffCkAcsCBSxj5KOgnX7KlL-Yds8,23803
|
|
93
93
|
mlrun/datastore/dbfs_store.py,sha256=CJwst1598qxiu63-Qa0c3e5E8LjeCv1XbMyWI7A6irY,6560
|
|
94
94
|
mlrun/datastore/filestore.py,sha256=OcykjzhbUAZ6_Cb9bGAXRL2ngsOpxXSb4rR0lyogZtM,3773
|
|
95
95
|
mlrun/datastore/google_cloud_storage.py,sha256=NREwZT7BCI0HfmOGkjpy5S3psiL_rgQSi43MaazJcKk,8711
|
|
@@ -105,12 +105,13 @@ mlrun/datastore/spark_utils.py,sha256=dn0RWpYzee-M8UZw-NVuHAdqlNAZ7VO-fNtI8ZiDky
|
|
|
105
105
|
mlrun/datastore/store_resources.py,sha256=s2794zqkzy_mjRMvRedDNs_tycTLoF8wxTqsWRQphCE,6839
|
|
106
106
|
mlrun/datastore/storeytargets.py,sha256=TvHbY3XS0qOg8ImXW1ET61UnjUPcYJbfSGAga3vgC-o,6492
|
|
107
107
|
mlrun/datastore/targets.py,sha256=8dRnLy1wBYJbVyommYkpGeztdT1CsfFHZY6Zh7o8X-Q,79165
|
|
108
|
-
mlrun/datastore/utils.py,sha256=
|
|
108
|
+
mlrun/datastore/utils.py,sha256=jxvq4lgQfgqb7dwKe4Kp51fYCCyOvitEdIfV2mzlqxg,11936
|
|
109
109
|
mlrun/datastore/v3io.py,sha256=sMn5473k_bXyIJovNf0rahbVHRmO0YPdOwIhbs06clg,8201
|
|
110
110
|
mlrun/datastore/vectorstore.py,sha256=k-yom5gfw20hnVG0Rg7aBEehuXwvAloZwn0cx0VGals,11708
|
|
111
111
|
mlrun/datastore/model_provider/__init__.py,sha256=kXGBqhLN0rlAx0kTXhozGzFsIdSqW0uTSKMmsLgq_is,569
|
|
112
|
-
mlrun/datastore/model_provider/
|
|
113
|
-
mlrun/datastore/model_provider/
|
|
112
|
+
mlrun/datastore/model_provider/huggingface_provider.py,sha256=KTmErt_WHhAV9t8803_iCHoa7jO-0y-7bch7KMTMDKo,7264
|
|
113
|
+
mlrun/datastore/model_provider/model_provider.py,sha256=dJIc1R0wbExsk-uzWgpt9w_FK027mr4lTBxYpQx-icY,8083
|
|
114
|
+
mlrun/datastore/model_provider/openai_provider.py,sha256=Tl3HXLDOtHkp54rb0ZCddlCWU-gUruhFJwSC6ocbv1Y,9157
|
|
114
115
|
mlrun/datastore/wasbfs/__init__.py,sha256=s5Ul-0kAhYqFjKDR2X0O2vDGDbLQQduElb32Ev56Te4,1343
|
|
115
116
|
mlrun/datastore/wasbfs/fs.py,sha256=ge8NK__5vTcFT-krI155_8RDUywQw4SIRX6BWATXy9Q,6299
|
|
116
117
|
mlrun/db/__init__.py,sha256=WqJ4x8lqJ7ZoKbhEyFqkYADd9P6E3citckx9e9ZLcIU,1163
|
|
@@ -225,7 +226,7 @@ mlrun/launcher/factory.py,sha256=RW7mfzEFi8fR0M-4W1JQg1iq3_muUU6OTqT_3l4Ubrk,233
|
|
|
225
226
|
mlrun/launcher/local.py,sha256=3gv-IQYoIChSmRaZ0vLUh0Tu26oLMCx9GbBYh4fWygQ,12161
|
|
226
227
|
mlrun/launcher/remote.py,sha256=zFXE52Cq_7EkC8lfNKT0ceIbye0CfFiundF7O1YU4Xw,7810
|
|
227
228
|
mlrun/model_monitoring/__init__.py,sha256=qDQnncjya9XPTlfvGyfWsZWiXc-glGZrrNja-5QmCZk,782
|
|
228
|
-
mlrun/model_monitoring/api.py,sha256=
|
|
229
|
+
mlrun/model_monitoring/api.py,sha256=9UsBIy8LYeAOoiIDTYthuD0mx1TYZxeGgaEM_H2qBkM,26092
|
|
229
230
|
mlrun/model_monitoring/controller.py,sha256=FVckATzREAzldj68D1KxcnKSdilgKUDRdqhRUf9XpWU,39592
|
|
230
231
|
mlrun/model_monitoring/features_drift_table.py,sha256=c6GpKtpOJbuT1u5uMWDL_S-6N4YPOmlktWMqPme3KFY,25308
|
|
231
232
|
mlrun/model_monitoring/helpers.py,sha256=0xhIYKzhaBrgyjLiA_ekCZsXzi3GBXpLyG40Bhj-PTY,23596
|
|
@@ -233,10 +234,10 @@ mlrun/model_monitoring/stream_processing.py,sha256=Mzn9Pelcblw8UzOFLGKb9oXOX0tkP
|
|
|
233
234
|
mlrun/model_monitoring/writer.py,sha256=rGRFzSOkqZWvD3Y6sVk2H1Gepfnkzkp9ce00PsApTLo,8288
|
|
234
235
|
mlrun/model_monitoring/applications/__init__.py,sha256=MaH_n4GiqqQvSkntM5yQ7_FCANtM_IfgK-IJTdo4G_E,757
|
|
235
236
|
mlrun/model_monitoring/applications/_application_steps.py,sha256=t9LDIqQUGE10cyjyhlg0QqN1yVx0apD1HpERYLJfm8U,7409
|
|
236
|
-
mlrun/model_monitoring/applications/base.py,sha256=
|
|
237
|
+
mlrun/model_monitoring/applications/base.py,sha256=tfxXcE7WOvPEd68b6gbZWFG-8gkeeSaRRN0G0HYn0C8,43932
|
|
237
238
|
mlrun/model_monitoring/applications/context.py,sha256=fAGFNCyNhSnVJPSIeJxv-XmEL2JhDmjK5Ouog9qyvdc,17035
|
|
238
239
|
mlrun/model_monitoring/applications/histogram_data_drift.py,sha256=2qgfFmrpHf-x0_EaHD-0T28piwSQzw-HH71aV1GwbZs,15389
|
|
239
|
-
mlrun/model_monitoring/applications/results.py,sha256=
|
|
240
|
+
mlrun/model_monitoring/applications/results.py,sha256=LfBQOmkpKGvVGNrcj5QiXsRIG2IRgcv_Xqe4QJBmauk,5699
|
|
240
241
|
mlrun/model_monitoring/applications/evidently/__init__.py,sha256=-DqdPnBSrjZhFvKOu_Ie3MiFvlur9sPTZpZ1u0_1AE8,690
|
|
241
242
|
mlrun/model_monitoring/applications/evidently/base.py,sha256=shH9YwuFrGNWy1IDAbv622l-GE4o1z_u1bqhqTyTHDA,5661
|
|
242
243
|
mlrun/model_monitoring/db/__init__.py,sha256=r47xPGZpIfMuv8J3PQCZTSqVPMhUta4sSJCZFKcS7FM,644
|
|
@@ -313,7 +314,7 @@ mlrun/serving/remote.py,sha256=Igha2FipK3-6rV_PZ1K464kTbiTu8rhc6SMm-HiEJ6o,18817
|
|
|
313
314
|
mlrun/serving/routers.py,sha256=SmBOlHn7rT2gWTa-W8f16UB0UthgIFc4D1cPOZAA9ss,54003
|
|
314
315
|
mlrun/serving/server.py,sha256=_P_SR4_7YKqruVzzDHgSPHWlNLGPG5-ksSUwuGhnmjg,38851
|
|
315
316
|
mlrun/serving/serving_wrapper.py,sha256=UL9hhWCfMPcTJO_XrkvNaFvck1U1E7oS8trTZyak0cA,835
|
|
316
|
-
mlrun/serving/states.py,sha256
|
|
317
|
+
mlrun/serving/states.py,sha256=-eo1IGLm96Kd0bTrwj211nUcwomMizQ6MxrqtURNxAg,124069
|
|
317
318
|
mlrun/serving/system_steps.py,sha256=tCxkJ54peOzRTMaqvHQCbcwx0ITqZkSpGXbtpRUEfzU,18463
|
|
318
319
|
mlrun/serving/utils.py,sha256=Zbfqm8TKNcTE8zRBezVBzpvR2WKeKeIRN7otNIaiYEc,4170
|
|
319
320
|
mlrun/serving/v1_serving.py,sha256=c6J_MtpE-Tqu00-6r4eJOCO6rUasHDal9W2eBIcrl50,11853
|
|
@@ -328,9 +329,9 @@ mlrun/utils/async_http.py,sha256=8Olx8TNNeXB07nEGwlqhEgFgnFAD71vBU_bqaA9JW-w,122
|
|
|
328
329
|
mlrun/utils/azure_vault.py,sha256=IEFizrDGDbAaoWwDr1WoA88S_EZ0T--vjYtY-i0cvYQ,3450
|
|
329
330
|
mlrun/utils/clones.py,sha256=qbAGyEbSvlewn3Tw_DpQZP9z6MGzFhSaZfI1CblX8Fg,7515
|
|
330
331
|
mlrun/utils/condition_evaluator.py,sha256=-nGfRmZzivn01rHTroiGY4rqEv8T1irMyhzxEei-sKc,1897
|
|
331
|
-
mlrun/utils/helpers.py,sha256=
|
|
332
|
+
mlrun/utils/helpers.py,sha256=fMlwtYBzUK98nCTFCA5FGm1imqIdpJi0CuAPmO10YZs,82641
|
|
332
333
|
mlrun/utils/http.py,sha256=5ZU2VpokaUM_DT3HBSqTm8xjUqTPjZN5fKkSIvKlTl0,8704
|
|
333
|
-
mlrun/utils/logger.py,sha256=
|
|
334
|
+
mlrun/utils/logger.py,sha256=uaCgI_ezzaXf7nJDCy-1Nrjds8vSXqDbzmjmb3IyCQo,14864
|
|
334
335
|
mlrun/utils/regex.py,sha256=FcRwWD8x9X3HLhCCU2F0AVKTFah784Pr7ZAe3a02jw8,5199
|
|
335
336
|
mlrun/utils/retryer.py,sha256=SHddxyNdUjIyvNJ3idTDyBzXARihCSuo3zWlZj6fqB0,7852
|
|
336
337
|
mlrun/utils/singleton.py,sha256=fNOfAUtha6OPCV_M1umWnGD0iabnnRwBke9otIspv30,868
|
|
@@ -347,11 +348,11 @@ mlrun/utils/notifications/notification/mail.py,sha256=ZyJ3eqd8simxffQmXzqd3bgbAq
|
|
|
347
348
|
mlrun/utils/notifications/notification/slack.py,sha256=kfhogR5keR7Zjh0VCjJNK3NR5_yXT7Cv-x9GdOUW4Z8,7294
|
|
348
349
|
mlrun/utils/notifications/notification/webhook.py,sha256=zxh8CAlbPnTazsk6r05X5TKwqUZVOH5KBU2fJbzQlG4,5330
|
|
349
350
|
mlrun/utils/version/__init__.py,sha256=YnzE6tlf24uOQ8y7Z7l96QLAI6-QEii7-77g8ynmzy0,613
|
|
350
|
-
mlrun/utils/version/version.json,sha256=
|
|
351
|
+
mlrun/utils/version/version.json,sha256=1hAlhHbCP3fWtgQa3l5zk4ROJntlDhuVxmDS5HRBMiY,90
|
|
351
352
|
mlrun/utils/version/version.py,sha256=M2hVhRrgkN3SxacZHs3ZqaOsqAA7B6a22ne324IQ1HE,1877
|
|
352
|
-
mlrun-1.10.
|
|
353
|
-
mlrun-1.10.
|
|
354
|
-
mlrun-1.10.
|
|
355
|
-
mlrun-1.10.
|
|
356
|
-
mlrun-1.10.
|
|
357
|
-
mlrun-1.10.
|
|
353
|
+
mlrun-1.10.0rc18.dist-info/licenses/LICENSE,sha256=zTiv1CxWNkOk1q8eJS1G_8oD4gWpWLwWxj_Agcsi8Os,11337
|
|
354
|
+
mlrun-1.10.0rc18.dist-info/METADATA,sha256=Rx-oVfq1W7TA73agpQeaFDhrevZ_RYVTXG6ormcXWvk,26195
|
|
355
|
+
mlrun-1.10.0rc18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
356
|
+
mlrun-1.10.0rc18.dist-info/entry_points.txt,sha256=1Owd16eAclD5pfRCoJpYC2ZJSyGNTtUr0nCELMioMmU,46
|
|
357
|
+
mlrun-1.10.0rc18.dist-info/top_level.txt,sha256=NObLzw3maSF9wVrgSeYBv-fgnHkAJ1kEkh12DLdd5KM,6
|
|
358
|
+
mlrun-1.10.0rc18.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|