mlrun 1.10.0rc37__py3-none-any.whl → 1.10.0rc39__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/artifacts/document.py +6 -1
- mlrun/common/constants.py +6 -0
- mlrun/common/schemas/model_monitoring/constants.py +0 -2
- mlrun/common/secrets.py +9 -1
- mlrun/launcher/local.py +2 -0
- mlrun/model.py +9 -1
- mlrun/model_monitoring/api.py +3 -2
- mlrun/model_monitoring/applications/base.py +6 -3
- mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py +9 -2
- mlrun/projects/project.py +17 -2
- mlrun/run.py +26 -1
- mlrun/runtimes/__init__.py +18 -0
- mlrun/runtimes/base.py +3 -0
- mlrun/runtimes/local.py +5 -2
- mlrun/runtimes/mounts.py +9 -0
- mlrun/runtimes/nuclio/application/application.py +2 -0
- mlrun/runtimes/nuclio/function.py +14 -0
- mlrun/runtimes/nuclio/serving.py +69 -4
- mlrun/runtimes/pod.py +67 -9
- mlrun/serving/server.py +42 -10
- mlrun/serving/states.py +30 -5
- mlrun/utils/helpers.py +86 -10
- mlrun/utils/version/version.json +2 -2
- {mlrun-1.10.0rc37.dist-info → mlrun-1.10.0rc39.dist-info}/METADATA +3 -3
- {mlrun-1.10.0rc37.dist-info → mlrun-1.10.0rc39.dist-info}/RECORD +29 -29
- {mlrun-1.10.0rc37.dist-info → mlrun-1.10.0rc39.dist-info}/WHEEL +0 -0
- {mlrun-1.10.0rc37.dist-info → mlrun-1.10.0rc39.dist-info}/entry_points.txt +0 -0
- {mlrun-1.10.0rc37.dist-info → mlrun-1.10.0rc39.dist-info}/licenses/LICENSE +0 -0
- {mlrun-1.10.0rc37.dist-info → mlrun-1.10.0rc39.dist-info}/top_level.txt +0 -0
mlrun/artifacts/document.py
CHANGED
|
@@ -359,7 +359,12 @@ class DocumentArtifact(Artifact):
|
|
|
359
359
|
self,
|
|
360
360
|
splitter: Optional["TextSplitter"] = None, # noqa: F821
|
|
361
361
|
) -> list["Document"]: # noqa: F821
|
|
362
|
-
|
|
362
|
+
# Try new langchain 1.0+ import path first
|
|
363
|
+
try:
|
|
364
|
+
from langchain_core.documents import Document
|
|
365
|
+
except ImportError:
|
|
366
|
+
# Fall back to old langchain <1.0 import path
|
|
367
|
+
from langchain.schema import Document
|
|
363
368
|
|
|
364
369
|
"""
|
|
365
370
|
Create LC documents from the artifact
|
mlrun/common/constants.py
CHANGED
|
@@ -27,6 +27,12 @@ DASK_LABEL_PREFIX = "dask.org/"
|
|
|
27
27
|
NUCLIO_LABEL_PREFIX = "nuclio.io/"
|
|
28
28
|
RESERVED_TAG_NAME_LATEST = "latest"
|
|
29
29
|
|
|
30
|
+
# Kubernetes DNS-1123 label name length limit
|
|
31
|
+
K8S_DNS_1123_LABEL_MAX_LENGTH = 63
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
RESERVED_BATCH_JOB_SUFFIX = "-batch"
|
|
35
|
+
|
|
30
36
|
JOB_TYPE_WORKFLOW_RUNNER = "workflow-runner"
|
|
31
37
|
JOB_TYPE_PROJECT_LOADER = "project-loader"
|
|
32
38
|
JOB_TYPE_RERUN_WORKFLOW_RUNNER = "rerun-workflow-runner"
|
|
@@ -486,8 +486,6 @@ class ModelMonitoringLabels:
|
|
|
486
486
|
|
|
487
487
|
_RESERVED_FUNCTION_NAMES = MonitoringFunctionNames.list() + [SpecialApps.MLRUN_INFRA]
|
|
488
488
|
|
|
489
|
-
_RESERVED_EVALUATE_FUNCTION_SUFFIX = "-batch"
|
|
490
|
-
|
|
491
489
|
|
|
492
490
|
class ModelEndpointMonitoringMetricType(StrEnum):
|
|
493
491
|
RESULT = "result"
|
mlrun/common/secrets.py
CHANGED
|
@@ -11,10 +11,18 @@
|
|
|
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 re
|
|
15
15
|
from abc import ABC, abstractmethod
|
|
16
16
|
|
|
17
17
|
import mlrun.common.schemas
|
|
18
|
+
from mlrun.config import config as mlconf
|
|
19
|
+
|
|
20
|
+
_AUTH_SECRET_NAME_TEMPLATE = re.escape(
|
|
21
|
+
mlconf.secret_stores.kubernetes.auth_secret_name.format(
|
|
22
|
+
hashed_access_key="",
|
|
23
|
+
)
|
|
24
|
+
)
|
|
25
|
+
AUTH_SECRET_PATTERN = re.compile(f"^{_AUTH_SECRET_NAME_TEMPLATE}.*")
|
|
18
26
|
|
|
19
27
|
|
|
20
28
|
class SecretProviderInterface(ABC):
|
mlrun/launcher/local.py
CHANGED
|
@@ -243,6 +243,8 @@ class ClientLocalLauncher(launcher.ClientBaseLauncher):
|
|
|
243
243
|
|
|
244
244
|
# if the handler has module prefix force "local" (vs "handler") runtime
|
|
245
245
|
kind = "local" if isinstance(handler, str) and "." in handler else ""
|
|
246
|
+
|
|
247
|
+
# Create temporary local function for execution
|
|
246
248
|
fn = mlrun.new_function(meta.name, command=command, args=args, kind=kind)
|
|
247
249
|
fn.metadata = meta
|
|
248
250
|
setattr(fn, "_is_run_local", True)
|
mlrun/model.py
CHANGED
|
@@ -29,6 +29,7 @@ import pydantic.v1.error_wrappers
|
|
|
29
29
|
import mlrun
|
|
30
30
|
import mlrun.common.constants as mlrun_constants
|
|
31
31
|
import mlrun.common.schemas.notification
|
|
32
|
+
import mlrun.common.secrets
|
|
32
33
|
import mlrun.utils.regex
|
|
33
34
|
|
|
34
35
|
from .utils import (
|
|
@@ -1616,7 +1617,14 @@ class RunTemplate(ModelObj):
|
|
|
1616
1617
|
|
|
1617
1618
|
:returns: The RunTemplate object
|
|
1618
1619
|
"""
|
|
1619
|
-
|
|
1620
|
+
if kind == "azure_vault" and isinstance(source, dict):
|
|
1621
|
+
candidate_secret_name = (source.get("k8s_secret") or "").strip()
|
|
1622
|
+
if candidate_secret_name and mlrun.common.secrets.AUTH_SECRET_PATTERN.match(
|
|
1623
|
+
candidate_secret_name
|
|
1624
|
+
):
|
|
1625
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
1626
|
+
f"Forbidden secret '{candidate_secret_name}' matches MLRun auth-secret pattern."
|
|
1627
|
+
)
|
|
1620
1628
|
if kind == "vault" and isinstance(source, list):
|
|
1621
1629
|
source = {"project": self.metadata.project, "secrets": source}
|
|
1622
1630
|
|
mlrun/model_monitoring/api.py
CHANGED
|
@@ -563,9 +563,10 @@ def _create_model_monitoring_function_base(
|
|
|
563
563
|
"An application cannot have the following names: "
|
|
564
564
|
f"{mm_constants._RESERVED_FUNCTION_NAMES}"
|
|
565
565
|
)
|
|
566
|
-
|
|
566
|
+
_, has_valid_suffix, suffix = mlrun.utils.helpers.ensure_batch_job_suffix(name)
|
|
567
|
+
if name and not has_valid_suffix:
|
|
567
568
|
raise mlrun.errors.MLRunValueError(
|
|
568
|
-
"Model monitoring application names cannot end with
|
|
569
|
+
f"Model monitoring application names cannot end with `{suffix}`"
|
|
569
570
|
)
|
|
570
571
|
if func is None:
|
|
571
572
|
func = ""
|
|
@@ -799,10 +799,13 @@ class ModelMonitoringApplicationBase(MonitoringApplicationToDict, ABC):
|
|
|
799
799
|
f"`{mm_constants.APP_NAME_REGEX.pattern}`. "
|
|
800
800
|
"Please choose another `func_name`."
|
|
801
801
|
)
|
|
802
|
-
|
|
803
|
-
job_name
|
|
802
|
+
job_name, was_renamed, suffix = mlrun.utils.helpers.ensure_batch_job_suffix(
|
|
803
|
+
job_name
|
|
804
|
+
)
|
|
805
|
+
if was_renamed:
|
|
804
806
|
mlrun.utils.logger.info(
|
|
805
|
-
'Changing function name - adding `"
|
|
807
|
+
f'Changing function name - adding `"{suffix}"` suffix',
|
|
808
|
+
func_name=job_name,
|
|
806
809
|
)
|
|
807
810
|
|
|
808
811
|
return job_name
|
|
@@ -55,14 +55,12 @@ class TDEngineConnector(TSDBConnector):
|
|
|
55
55
|
"""
|
|
56
56
|
|
|
57
57
|
type: str = mm_schemas.TSDBTarget.TDEngine
|
|
58
|
-
database = f"{tdengine_schemas._MODEL_MONITORING_DATABASE}_{mlrun.mlconf.system_id}"
|
|
59
58
|
|
|
60
59
|
def __init__(
|
|
61
60
|
self,
|
|
62
61
|
project: str,
|
|
63
62
|
profile: DatastoreProfile,
|
|
64
63
|
timestamp_precision: TDEngineTimestampPrecision = TDEngineTimestampPrecision.MICROSECOND,
|
|
65
|
-
**kwargs,
|
|
66
64
|
):
|
|
67
65
|
super().__init__(project=project)
|
|
68
66
|
|
|
@@ -72,6 +70,15 @@ class TDEngineConnector(TSDBConnector):
|
|
|
72
70
|
timestamp_precision
|
|
73
71
|
)
|
|
74
72
|
|
|
73
|
+
if not mlrun.mlconf.system_id:
|
|
74
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
75
|
+
"system_id is not set in mlrun.mlconf. "
|
|
76
|
+
"TDEngineConnector requires system_id to be configured for database name construction. "
|
|
77
|
+
"Please ensure MLRun configuration is properly loaded before creating TDEngineConnector."
|
|
78
|
+
)
|
|
79
|
+
self.database = (
|
|
80
|
+
f"{tdengine_schemas._MODEL_MONITORING_DATABASE}_{mlrun.mlconf.system_id}"
|
|
81
|
+
)
|
|
75
82
|
self._init_super_tables()
|
|
76
83
|
|
|
77
84
|
@property
|
mlrun/projects/project.py
CHANGED
|
@@ -45,6 +45,7 @@ import mlrun.common.runtimes.constants
|
|
|
45
45
|
import mlrun.common.schemas.alert
|
|
46
46
|
import mlrun.common.schemas.artifact
|
|
47
47
|
import mlrun.common.schemas.model_monitoring.constants as mm_constants
|
|
48
|
+
import mlrun.common.secrets
|
|
48
49
|
import mlrun.datastore.datastore_profile
|
|
49
50
|
import mlrun.db
|
|
50
51
|
import mlrun.errors
|
|
@@ -3418,7 +3419,12 @@ class MlrunProject(ModelObj):
|
|
|
3418
3419
|
self._initialized = True
|
|
3419
3420
|
return self.spec._function_objects
|
|
3420
3421
|
|
|
3421
|
-
def with_secrets(
|
|
3422
|
+
def with_secrets(
|
|
3423
|
+
self,
|
|
3424
|
+
kind,
|
|
3425
|
+
source,
|
|
3426
|
+
prefix="",
|
|
3427
|
+
):
|
|
3422
3428
|
"""register a secrets source (file, env or dict)
|
|
3423
3429
|
|
|
3424
3430
|
read secrets from a source provider to be used in workflows, example::
|
|
@@ -3440,12 +3446,21 @@ class MlrunProject(ModelObj):
|
|
|
3440
3446
|
|
|
3441
3447
|
This will enable access to all secrets in vault registered to the current project.
|
|
3442
3448
|
|
|
3443
|
-
:param kind: secret type (file, inline, env, vault)
|
|
3449
|
+
:param kind: secret type (file, inline, env, vault, azure_vault)
|
|
3444
3450
|
:param source: secret data or link (see example)
|
|
3445
3451
|
:param prefix: add a prefix to the keys in this source
|
|
3446
3452
|
|
|
3447
3453
|
:returns: project object
|
|
3448
3454
|
"""
|
|
3455
|
+
# Block using mlrun-auth-secrets.* via azure_vault's k8s_secret param (client-side only)
|
|
3456
|
+
if kind == "azure_vault" and isinstance(source, dict):
|
|
3457
|
+
candidate_secret_name = (source.get("k8s_secret") or "").strip()
|
|
3458
|
+
if candidate_secret_name and mlrun.common.secrets.AUTH_SECRET_PATTERN.match(
|
|
3459
|
+
candidate_secret_name
|
|
3460
|
+
):
|
|
3461
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
3462
|
+
f"Forbidden secret '{candidate_secret_name}' matches MLRun auth-secret pattern."
|
|
3463
|
+
)
|
|
3449
3464
|
|
|
3450
3465
|
if kind == "vault" and isinstance(source, list):
|
|
3451
3466
|
source = {"project": self.metadata.name, "secrets": source}
|
mlrun/run.py
CHANGED
|
@@ -118,7 +118,25 @@ def function_to_module(code="", workdir=None, secrets=None, silent=False):
|
|
|
118
118
|
raise ValueError("nothing to run, specify command or function")
|
|
119
119
|
|
|
120
120
|
command = os.path.join(workdir or "", command)
|
|
121
|
-
|
|
121
|
+
|
|
122
|
+
source_file_path_object, working_dir_path_object = (
|
|
123
|
+
mlrun.utils.helpers.get_source_and_working_dir_paths(command)
|
|
124
|
+
)
|
|
125
|
+
if source_file_path_object.is_relative_to(working_dir_path_object):
|
|
126
|
+
mod_name = mlrun.utils.helpers.get_relative_module_name_from_path(
|
|
127
|
+
source_file_path_object, working_dir_path_object
|
|
128
|
+
)
|
|
129
|
+
elif source_file_path_object.is_relative_to(
|
|
130
|
+
pathlib.Path(tempfile.gettempdir()).resolve()
|
|
131
|
+
):
|
|
132
|
+
mod_name = Path(command).stem
|
|
133
|
+
else:
|
|
134
|
+
raise mlrun.errors.MLRunRuntimeError(
|
|
135
|
+
f"Cannot run source file '{command}': it must be located either under the current working "
|
|
136
|
+
f"directory ('{working_dir_path_object}') or the system temporary directory ('{tempfile.gettempdir()}'). "
|
|
137
|
+
f"This is required when running with local=True."
|
|
138
|
+
)
|
|
139
|
+
|
|
122
140
|
spec = imputil.spec_from_file_location(mod_name, command)
|
|
123
141
|
if spec is None:
|
|
124
142
|
raise OSError(f"cannot import from {command!r}")
|
|
@@ -537,6 +555,7 @@ def new_function(
|
|
|
537
555
|
|
|
538
556
|
# make sure function name is valid
|
|
539
557
|
name = mlrun.utils.helpers.normalize_name(name)
|
|
558
|
+
mlrun.utils.helpers.validate_function_name(name)
|
|
540
559
|
|
|
541
560
|
runner.metadata.name = name
|
|
542
561
|
runner.metadata.project = (
|
|
@@ -576,6 +595,7 @@ def new_function(
|
|
|
576
595
|
)
|
|
577
596
|
|
|
578
597
|
runner.prepare_image_for_deploy()
|
|
598
|
+
|
|
579
599
|
return runner
|
|
580
600
|
|
|
581
601
|
|
|
@@ -780,6 +800,9 @@ def code_to_function(
|
|
|
780
800
|
kind=sub_kind,
|
|
781
801
|
ignored_tags=ignored_tags,
|
|
782
802
|
)
|
|
803
|
+
|
|
804
|
+
mlrun.utils.helpers.validate_function_name(name)
|
|
805
|
+
|
|
783
806
|
spec["spec"]["env"].append(
|
|
784
807
|
{
|
|
785
808
|
"name": "MLRUN_HTTPDB__NUCLIO__EXPLICIT_ACK",
|
|
@@ -832,6 +855,7 @@ def code_to_function(
|
|
|
832
855
|
runtime.spec.build.code_origin = code_origin
|
|
833
856
|
runtime.spec.build.origin_filename = filename or (name + ".ipynb")
|
|
834
857
|
update_common(runtime, spec)
|
|
858
|
+
|
|
835
859
|
return runtime
|
|
836
860
|
|
|
837
861
|
if kind is None or kind in ["", "Function"]:
|
|
@@ -845,6 +869,7 @@ def code_to_function(
|
|
|
845
869
|
|
|
846
870
|
if not name:
|
|
847
871
|
raise ValueError("name must be specified")
|
|
872
|
+
|
|
848
873
|
h = get_in(spec, "spec.handler", "").split(":")
|
|
849
874
|
runtime.handler = h[0] if len(h) <= 1 else h[1]
|
|
850
875
|
runtime.metadata = get_in(spec, "spec.metadata")
|
mlrun/runtimes/__init__.py
CHANGED
|
@@ -221,6 +221,24 @@ class RuntimeKinds:
|
|
|
221
221
|
return True
|
|
222
222
|
return False
|
|
223
223
|
|
|
224
|
+
@staticmethod
|
|
225
|
+
def requires_k8s_name_validation(kind: str) -> bool:
|
|
226
|
+
"""
|
|
227
|
+
Returns True if the runtime kind creates Kubernetes resources that use the function name.
|
|
228
|
+
|
|
229
|
+
Function names for k8s-deployed runtimes must conform to DNS-1123 label requirements:
|
|
230
|
+
- Lowercase alphanumeric characters or '-'
|
|
231
|
+
- Start and end with an alphanumeric character
|
|
232
|
+
- Maximum 63 characters
|
|
233
|
+
|
|
234
|
+
Local runtimes (local, handler) run on the local machine and don't create k8s resources,
|
|
235
|
+
so they don't require k8s naming validation.
|
|
236
|
+
|
|
237
|
+
:param kind: Runtime kind string (job, spark, serving, local, etc.)
|
|
238
|
+
:return: True if function name needs k8s DNS-1123 validation, False otherwise
|
|
239
|
+
"""
|
|
240
|
+
return not RuntimeKinds.is_local_runtime(kind)
|
|
241
|
+
|
|
224
242
|
@staticmethod
|
|
225
243
|
def requires_absolute_artifacts_path(kind):
|
|
226
244
|
"""
|
mlrun/runtimes/base.py
CHANGED
|
@@ -393,6 +393,9 @@ class BaseRuntime(ModelObj):
|
|
|
393
393
|
FutureWarning,
|
|
394
394
|
)
|
|
395
395
|
output_path = output_path or out_path or artifact_path
|
|
396
|
+
|
|
397
|
+
mlrun.utils.helpers.validate_function_name(self.metadata.name)
|
|
398
|
+
|
|
396
399
|
launcher = mlrun.launcher.factory.LauncherFactory().create_launcher(
|
|
397
400
|
self._is_remote, local=local, **launcher_kwargs
|
|
398
401
|
)
|
mlrun/runtimes/local.py
CHANGED
|
@@ -29,12 +29,12 @@ from os import environ, remove
|
|
|
29
29
|
from pathlib import Path
|
|
30
30
|
from subprocess import PIPE, Popen
|
|
31
31
|
from sys import executable
|
|
32
|
+
from typing import Optional
|
|
32
33
|
|
|
33
34
|
from nuclio import Event
|
|
34
35
|
|
|
35
36
|
import mlrun
|
|
36
37
|
import mlrun.common.constants as mlrun_constants
|
|
37
|
-
import mlrun.common.runtimes.constants
|
|
38
38
|
from mlrun.lists import RunList
|
|
39
39
|
|
|
40
40
|
from ..errors import err_to_str
|
|
@@ -201,9 +201,12 @@ class LocalRuntime(BaseRuntime, ParallelRunner):
|
|
|
201
201
|
kind = "local"
|
|
202
202
|
_is_remote = False
|
|
203
203
|
|
|
204
|
-
def to_job(self, image=""):
|
|
204
|
+
def to_job(self, image="", func_name: Optional[str] = None):
|
|
205
205
|
struct = self.to_dict()
|
|
206
206
|
obj = KubejobRuntime.from_dict(struct)
|
|
207
|
+
obj.kind = "job" # Ensure kind is set to 'job' for KubejobRuntime
|
|
208
|
+
if func_name:
|
|
209
|
+
obj.metadata.name = func_name
|
|
207
210
|
if image:
|
|
208
211
|
obj.spec.image = image
|
|
209
212
|
return obj
|
mlrun/runtimes/mounts.py
CHANGED
|
@@ -17,6 +17,8 @@ import typing
|
|
|
17
17
|
import warnings
|
|
18
18
|
from collections import namedtuple
|
|
19
19
|
|
|
20
|
+
import mlrun.common.secrets
|
|
21
|
+
import mlrun.errors
|
|
20
22
|
from mlrun.config import config
|
|
21
23
|
from mlrun.config import config as mlconf
|
|
22
24
|
from mlrun.errors import MLRunInvalidArgumentError
|
|
@@ -412,6 +414,13 @@ def mount_secret(
|
|
|
412
414
|
the specified paths, and unlisted keys will not be
|
|
413
415
|
present."""
|
|
414
416
|
|
|
417
|
+
if secret_name and mlrun.common.secrets.AUTH_SECRET_PATTERN.match(
|
|
418
|
+
secret_name.strip()
|
|
419
|
+
):
|
|
420
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
421
|
+
f"Forbidden secret '{secret_name}' matches MLRun auth-secret pattern."
|
|
422
|
+
)
|
|
423
|
+
|
|
415
424
|
def _mount_secret(runtime: "KubeResource"):
|
|
416
425
|
# Define the secret volume source
|
|
417
426
|
secret_volume_source = {
|
|
@@ -400,6 +400,8 @@ class ApplicationRuntime(RemoteRuntime):
|
|
|
400
400
|
|
|
401
401
|
:return: The default API gateway URL if created or True if the function is ready (deployed)
|
|
402
402
|
"""
|
|
403
|
+
mlrun.utils.helpers.validate_function_name(self.metadata.name)
|
|
404
|
+
|
|
403
405
|
if (self.requires_build() and not self.spec.image) or force_build:
|
|
404
406
|
self._fill_credentials()
|
|
405
407
|
self._build_application_image(
|
|
@@ -655,6 +655,8 @@ class RemoteRuntime(KubeResource):
|
|
|
655
655
|
if tag:
|
|
656
656
|
self.metadata.tag = tag
|
|
657
657
|
|
|
658
|
+
mlrun.utils.helpers.validate_function_name(self.metadata.name)
|
|
659
|
+
|
|
658
660
|
# Attempt auto-mounting, before sending to remote build
|
|
659
661
|
self.try_auto_mount_based_on_config()
|
|
660
662
|
self._fill_credentials()
|
|
@@ -1224,6 +1226,18 @@ class RemoteRuntime(KubeResource):
|
|
|
1224
1226
|
# try to infer the invocation url from the internal and if not exists, use external.
|
|
1225
1227
|
# $$$$ we do not want to use the external invocation url (e.g.: ingress, nodePort, etc.)
|
|
1226
1228
|
|
|
1229
|
+
# if none of urls is set, function was deployed with watch=False
|
|
1230
|
+
# and status wasn't fetched with Nuclio
|
|
1231
|
+
# _get_state fetches the state and updates url
|
|
1232
|
+
if (
|
|
1233
|
+
not self.status.address
|
|
1234
|
+
and not self.status.internal_invocation_urls
|
|
1235
|
+
and not self.status.external_invocation_urls
|
|
1236
|
+
):
|
|
1237
|
+
state, _, _ = self._get_state()
|
|
1238
|
+
if state not in ["ready", "scaledToZero"]:
|
|
1239
|
+
logger.warning(f"Function is in the {state} state")
|
|
1240
|
+
|
|
1227
1241
|
# prefer internal invocation url if running inside k8s cluster
|
|
1228
1242
|
if (
|
|
1229
1243
|
not force_external_address
|
mlrun/runtimes/nuclio/serving.py
CHANGED
|
@@ -44,6 +44,7 @@ from mlrun.serving.states import (
|
|
|
44
44
|
)
|
|
45
45
|
from mlrun.utils import get_caller_globals, logger, set_paths
|
|
46
46
|
|
|
47
|
+
from ...common.secrets import AUTH_SECRET_PATTERN
|
|
47
48
|
from .. import KubejobRuntime
|
|
48
49
|
from ..pod import KubeResourceSpec
|
|
49
50
|
from .function import NuclioSpec, RemoteRuntime, min_nuclio_versions
|
|
@@ -635,7 +636,14 @@ class ServingRuntime(RemoteRuntime):
|
|
|
635
636
|
|
|
636
637
|
:returns: The Runtime (function) object
|
|
637
638
|
"""
|
|
638
|
-
|
|
639
|
+
if kind == "azure_vault" and isinstance(source, dict):
|
|
640
|
+
candidate_secret_name = (source.get("k8s_secret") or "").strip()
|
|
641
|
+
if candidate_secret_name and AUTH_SECRET_PATTERN.match(
|
|
642
|
+
candidate_secret_name
|
|
643
|
+
):
|
|
644
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
645
|
+
f"Forbidden secret '{candidate_secret_name}' matches MLRun auth-secret pattern."
|
|
646
|
+
)
|
|
639
647
|
if kind == "vault" and isinstance(source, list):
|
|
640
648
|
source = {"project": self.metadata.project, "secrets": source}
|
|
641
649
|
|
|
@@ -659,6 +667,9 @@ class ServingRuntime(RemoteRuntime):
|
|
|
659
667
|
:param builder_env: env vars dict for source archive config/credentials e.g. builder_env={"GIT_TOKEN": token}
|
|
660
668
|
:param force_build: set True for force building the image
|
|
661
669
|
"""
|
|
670
|
+
# Validate function name before deploying to k8s
|
|
671
|
+
mlrun.utils.helpers.validate_function_name(self.metadata.name)
|
|
672
|
+
|
|
662
673
|
load_mode = self.spec.load_mode
|
|
663
674
|
if load_mode and load_mode not in ["sync", "async"]:
|
|
664
675
|
raise ValueError(f"illegal model loading mode {load_mode}")
|
|
@@ -855,8 +866,20 @@ class ServingRuntime(RemoteRuntime):
|
|
|
855
866
|
)
|
|
856
867
|
self._mock_server = self.to_mock_server()
|
|
857
868
|
|
|
858
|
-
def to_job(self) -> KubejobRuntime:
|
|
859
|
-
"""Convert this ServingRuntime to a KubejobRuntime, so that the graph can be run as a standalone job.
|
|
869
|
+
def to_job(self, func_name: Optional[str] = None) -> KubejobRuntime:
|
|
870
|
+
"""Convert this ServingRuntime to a KubejobRuntime, so that the graph can be run as a standalone job.
|
|
871
|
+
|
|
872
|
+
Args:
|
|
873
|
+
func_name: Optional custom name for the job function. If not provided, automatically
|
|
874
|
+
appends '-batch' suffix to the serving function name to prevent database collision.
|
|
875
|
+
|
|
876
|
+
Returns:
|
|
877
|
+
KubejobRuntime configured to execute the serving graph as a batch job.
|
|
878
|
+
|
|
879
|
+
Note:
|
|
880
|
+
The job will have a different name than the serving function to prevent database collision.
|
|
881
|
+
The original serving function remains unchanged and can still be invoked after running the job.
|
|
882
|
+
"""
|
|
860
883
|
if self.spec.function_refs:
|
|
861
884
|
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
862
885
|
f"Cannot convert function '{self.metadata.name}' to a job because it has child functions"
|
|
@@ -890,8 +913,50 @@ class ServingRuntime(RemoteRuntime):
|
|
|
890
913
|
parameters=self.spec.parameters,
|
|
891
914
|
graph=self.spec.graph,
|
|
892
915
|
)
|
|
916
|
+
|
|
917
|
+
job_metadata = deepcopy(self.metadata)
|
|
918
|
+
original_name = job_metadata.name
|
|
919
|
+
|
|
920
|
+
if func_name:
|
|
921
|
+
# User provided explicit job name
|
|
922
|
+
job_metadata.name = func_name
|
|
923
|
+
logger.debug(
|
|
924
|
+
"Creating job from serving function with custom name",
|
|
925
|
+
new_name=func_name,
|
|
926
|
+
)
|
|
927
|
+
else:
|
|
928
|
+
job_metadata.name, was_renamed, suffix = (
|
|
929
|
+
mlrun.utils.helpers.ensure_batch_job_suffix(job_metadata.name)
|
|
930
|
+
)
|
|
931
|
+
|
|
932
|
+
# Check if the resulting name exceeds Kubernetes length limit
|
|
933
|
+
if (
|
|
934
|
+
len(job_metadata.name)
|
|
935
|
+
> mlrun.common.constants.K8S_DNS_1123_LABEL_MAX_LENGTH
|
|
936
|
+
):
|
|
937
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
938
|
+
f"Cannot convert serving function '{original_name}' to batch job: "
|
|
939
|
+
f"the resulting name '{job_metadata.name}' ({len(job_metadata.name)} characters) "
|
|
940
|
+
f"exceeds Kubernetes limit of {mlrun.common.constants.K8S_DNS_1123_LABEL_MAX_LENGTH} characters. "
|
|
941
|
+
f"Please provide a custom name via the func_name parameter, "
|
|
942
|
+
f"with at most {mlrun.common.constants.K8S_DNS_1123_LABEL_MAX_LENGTH} characters."
|
|
943
|
+
)
|
|
944
|
+
|
|
945
|
+
if was_renamed:
|
|
946
|
+
logger.info(
|
|
947
|
+
"Creating job from serving function (auto-appended suffix to prevent collision)",
|
|
948
|
+
new_name=job_metadata.name,
|
|
949
|
+
suffix=suffix,
|
|
950
|
+
)
|
|
951
|
+
else:
|
|
952
|
+
logger.debug(
|
|
953
|
+
"Creating job from serving function (name already has suffix)",
|
|
954
|
+
name=original_name,
|
|
955
|
+
suffix=suffix,
|
|
956
|
+
)
|
|
957
|
+
|
|
893
958
|
job = KubejobRuntime(
|
|
894
959
|
spec=spec,
|
|
895
|
-
metadata=
|
|
960
|
+
metadata=job_metadata,
|
|
896
961
|
)
|
|
897
962
|
return job
|
mlrun/runtimes/pod.py
CHANGED
|
@@ -20,6 +20,7 @@ import typing
|
|
|
20
20
|
import warnings
|
|
21
21
|
from collections.abc import Iterable
|
|
22
22
|
from enum import Enum
|
|
23
|
+
from typing import Optional
|
|
23
24
|
|
|
24
25
|
import dotenv
|
|
25
26
|
import kubernetes.client as k8s_client
|
|
@@ -34,6 +35,7 @@ from mlrun.common.schemas import (
|
|
|
34
35
|
SecurityContextEnrichmentModes,
|
|
35
36
|
)
|
|
36
37
|
|
|
38
|
+
from ..common.secrets import AUTH_SECRET_PATTERN
|
|
37
39
|
from ..config import config as mlconf
|
|
38
40
|
from ..k8s_utils import (
|
|
39
41
|
generate_preemptible_nodes_affinity_terms,
|
|
@@ -708,19 +710,44 @@ class KubeResource(BaseRuntime):
|
|
|
708
710
|
def spec(self, spec):
|
|
709
711
|
self._spec = self._verify_dict(spec, "spec", KubeResourceSpec)
|
|
710
712
|
|
|
711
|
-
def set_env_from_secret(
|
|
712
|
-
|
|
713
|
+
def set_env_from_secret(
|
|
714
|
+
self,
|
|
715
|
+
name: str,
|
|
716
|
+
secret: Optional[str] = None,
|
|
717
|
+
secret_key: Optional[str] = None,
|
|
718
|
+
):
|
|
719
|
+
"""Set env var from secret; block auth-secret usage on client side."""
|
|
720
|
+
self._validate_no_auth_secret(
|
|
721
|
+
secret_name=secret,
|
|
722
|
+
)
|
|
713
723
|
secret_key = secret_key or name
|
|
714
724
|
value_from = k8s_client.V1EnvVarSource(
|
|
715
|
-
secret_key_ref=k8s_client.V1SecretKeySelector(
|
|
725
|
+
secret_key_ref=k8s_client.V1SecretKeySelector(
|
|
726
|
+
name=secret,
|
|
727
|
+
key=secret_key,
|
|
728
|
+
)
|
|
729
|
+
)
|
|
730
|
+
return self._set_env(
|
|
731
|
+
name=name,
|
|
732
|
+
value_from=value_from,
|
|
716
733
|
)
|
|
717
|
-
return self._set_env(name, value_from=value_from)
|
|
718
734
|
|
|
719
|
-
def set_env(
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
735
|
+
def set_env(
|
|
736
|
+
self,
|
|
737
|
+
name: str,
|
|
738
|
+
value: Optional[str] = None,
|
|
739
|
+
value_from: Optional[typing.Any] = None,
|
|
740
|
+
):
|
|
741
|
+
"""Set env var; block auth-secret usage when coming from a secret."""
|
|
742
|
+
if value_from is not None:
|
|
743
|
+
secret_name = self._extract_secret_name_from_value_from(
|
|
744
|
+
value_from=value_from,
|
|
745
|
+
)
|
|
746
|
+
self._validate_no_auth_secret(
|
|
747
|
+
secret_name=secret_name,
|
|
748
|
+
)
|
|
749
|
+
return self._set_env(name, value_from=value_from)
|
|
750
|
+
return self._set_env(name, value=str(value) if value is not None else None)
|
|
724
751
|
|
|
725
752
|
def with_annotations(self, annotations: dict):
|
|
726
753
|
"""set a key/value annotations in the metadata of the pod"""
|
|
@@ -1366,6 +1393,37 @@ class KubeResource(BaseRuntime):
|
|
|
1366
1393
|
|
|
1367
1394
|
return self.status.state
|
|
1368
1395
|
|
|
1396
|
+
@staticmethod
|
|
1397
|
+
def _validate_no_auth_secret(
|
|
1398
|
+
secret_name: Optional[str],
|
|
1399
|
+
):
|
|
1400
|
+
"""Raise if secret name matches MLRun auth-secret pattern."""
|
|
1401
|
+
if secret_name and AUTH_SECRET_PATTERN.match(secret_name):
|
|
1402
|
+
raise mlrun.errors.MLRunInvalidArgumentError(
|
|
1403
|
+
f"Forbidden secret '{secret_name}' matches MLRun auth-secret pattern."
|
|
1404
|
+
)
|
|
1405
|
+
|
|
1406
|
+
@staticmethod
|
|
1407
|
+
def _extract_secret_name_from_value_from(
|
|
1408
|
+
value_from: typing.Any,
|
|
1409
|
+
) -> Optional[str]:
|
|
1410
|
+
"""Extract secret name from a V1EnvVarSource or dict representation."""
|
|
1411
|
+
if isinstance(value_from, k8s_client.V1EnvVarSource):
|
|
1412
|
+
if value_from.secret_key_ref:
|
|
1413
|
+
return value_from.secret_key_ref.name
|
|
1414
|
+
elif isinstance(value_from, dict):
|
|
1415
|
+
value_from = (
|
|
1416
|
+
value_from.get("valueFrom")
|
|
1417
|
+
or value_from.get("value_from")
|
|
1418
|
+
or value_from
|
|
1419
|
+
)
|
|
1420
|
+
secret_key_ref = (value_from or {}).get("secretKeyRef") or (
|
|
1421
|
+
value_from or {}
|
|
1422
|
+
).get("secret_key_ref")
|
|
1423
|
+
if isinstance(secret_key_ref, dict):
|
|
1424
|
+
return secret_key_ref.get("name")
|
|
1425
|
+
return None
|
|
1426
|
+
|
|
1369
1427
|
|
|
1370
1428
|
def _resolve_if_type_sanitized(attribute_name, attribute):
|
|
1371
1429
|
attribute_config = sanitized_attributes[attribute_name]
|
mlrun/serving/server.py
CHANGED
|
@@ -23,6 +23,7 @@ import os
|
|
|
23
23
|
import socket
|
|
24
24
|
import traceback
|
|
25
25
|
import uuid
|
|
26
|
+
from collections import defaultdict
|
|
26
27
|
from datetime import datetime, timezone
|
|
27
28
|
from typing import Any, Optional, Union
|
|
28
29
|
|
|
@@ -50,7 +51,7 @@ from ..datastore.store_resources import ResourceCache
|
|
|
50
51
|
from ..errors import MLRunInvalidArgumentError
|
|
51
52
|
from ..execution import MLClientCtx
|
|
52
53
|
from ..model import ModelObj
|
|
53
|
-
from ..utils import get_caller_globals,
|
|
54
|
+
from ..utils import get_caller_globals, get_relative_module_name_from_path
|
|
54
55
|
from .states import (
|
|
55
56
|
FlowStep,
|
|
56
57
|
MonitoredStep,
|
|
@@ -522,10 +523,6 @@ def add_system_steps_to_graph(
|
|
|
522
523
|
monitor_flow_step.after = [
|
|
523
524
|
step_name,
|
|
524
525
|
]
|
|
525
|
-
context.logger.info_with(
|
|
526
|
-
"Server graph after adding system steps",
|
|
527
|
-
graph=str(graph.steps),
|
|
528
|
-
)
|
|
529
526
|
return graph
|
|
530
527
|
|
|
531
528
|
|
|
@@ -583,7 +580,7 @@ async def async_execute_graph(
|
|
|
583
580
|
batch_size: Optional[int],
|
|
584
581
|
read_as_lists: bool,
|
|
585
582
|
nest_under_inputs: bool,
|
|
586
|
-
) ->
|
|
583
|
+
) -> None:
|
|
587
584
|
# Validate that data parameter is a DataItem and not passed via params
|
|
588
585
|
if not isinstance(data, DataItem):
|
|
589
586
|
raise MLRunInvalidArgumentError(
|
|
@@ -593,7 +590,7 @@ async def async_execute_graph(
|
|
|
593
590
|
f"while 'inputs' is for data files that need to be loaded. "
|
|
594
591
|
f"Example: run_function(..., inputs={{'data': 'path/to/data.csv'}}, params={{other_config: value}})"
|
|
595
592
|
)
|
|
596
|
-
|
|
593
|
+
run_call_count = 0
|
|
597
594
|
spec = mlrun.utils.get_serving_spec()
|
|
598
595
|
modname = None
|
|
599
596
|
code = os.getenv("MLRUN_EXEC_CODE")
|
|
@@ -607,7 +604,17 @@ async def async_execute_graph(
|
|
|
607
604
|
# gets set in local flow and not just in the remote pod
|
|
608
605
|
source_file_path = spec.get("filename", None)
|
|
609
606
|
if source_file_path:
|
|
610
|
-
|
|
607
|
+
source_file_path_object, working_dir_path_object = (
|
|
608
|
+
mlrun.utils.helpers.get_source_and_working_dir_paths(source_file_path)
|
|
609
|
+
)
|
|
610
|
+
if not source_file_path_object.is_relative_to(working_dir_path_object):
|
|
611
|
+
raise mlrun.errors.MLRunRuntimeError(
|
|
612
|
+
f"Source file path '{source_file_path}' is not under the current working directory "
|
|
613
|
+
f"(which is required when running with local=True)"
|
|
614
|
+
)
|
|
615
|
+
modname = get_relative_module_name_from_path(
|
|
616
|
+
source_file_path_object, working_dir_path_object
|
|
617
|
+
)
|
|
611
618
|
|
|
612
619
|
namespace = {}
|
|
613
620
|
if modname:
|
|
@@ -682,7 +689,6 @@ async def async_execute_graph(
|
|
|
682
689
|
|
|
683
690
|
if config.log_level.lower() == "debug":
|
|
684
691
|
server.verbose = True
|
|
685
|
-
context.logger.info_with("Initializing states", namespace=namespace)
|
|
686
692
|
kwargs = {}
|
|
687
693
|
if hasattr(context, "is_mock"):
|
|
688
694
|
kwargs["is_mock"] = context.is_mock
|
|
@@ -700,6 +706,7 @@ async def async_execute_graph(
|
|
|
700
706
|
context.logger.info(server.to_yaml())
|
|
701
707
|
|
|
702
708
|
async def run(body):
|
|
709
|
+
nonlocal run_call_count
|
|
703
710
|
event = storey.Event(id=index, body=body)
|
|
704
711
|
if timestamp_column:
|
|
705
712
|
if batching:
|
|
@@ -714,6 +721,7 @@ async def async_execute_graph(
|
|
|
714
721
|
f"Event body '{body}' did not contain timestamp column '{timestamp_column}'"
|
|
715
722
|
)
|
|
716
723
|
event._original_timestamp = body[timestamp_column]
|
|
724
|
+
run_call_count += 1
|
|
717
725
|
return await server.run(event, context)
|
|
718
726
|
|
|
719
727
|
if batching and not batch_size:
|
|
@@ -771,7 +779,31 @@ async def async_execute_graph(
|
|
|
771
779
|
model_endpoint_uids=model_endpoint_uids,
|
|
772
780
|
)
|
|
773
781
|
|
|
774
|
-
|
|
782
|
+
# log the results as artifacts
|
|
783
|
+
num_of_meps_in_the_graph = len(server.graph.model_endpoints_names)
|
|
784
|
+
artifact_path = None
|
|
785
|
+
if (
|
|
786
|
+
"{{run.uid}}" not in context.artifact_path
|
|
787
|
+
): # TODO: delete when IG-22841 is resolved
|
|
788
|
+
artifact_path = "+/{{run.uid}}" # will be concatenated to the context's path in extend_artifact_path
|
|
789
|
+
if num_of_meps_in_the_graph <= 1:
|
|
790
|
+
context.log_dataset(
|
|
791
|
+
"prediction", df=pd.DataFrame(responses), artifact_path=artifact_path
|
|
792
|
+
)
|
|
793
|
+
else:
|
|
794
|
+
# turn this list of samples into a dict of lists, one per model endpoint
|
|
795
|
+
grouped = defaultdict(list)
|
|
796
|
+
for sample in responses:
|
|
797
|
+
for model_name, features in sample.items():
|
|
798
|
+
grouped[model_name].append(features)
|
|
799
|
+
# create a dataframe per model endpoint and log it
|
|
800
|
+
for model_name, features in grouped.items():
|
|
801
|
+
context.log_dataset(
|
|
802
|
+
f"prediction_{model_name}",
|
|
803
|
+
df=pd.DataFrame(features),
|
|
804
|
+
artifact_path=artifact_path,
|
|
805
|
+
)
|
|
806
|
+
context.log_result("num_rows", run_call_count)
|
|
775
807
|
|
|
776
808
|
|
|
777
809
|
def _is_inside_asyncio_loop():
|
mlrun/serving/states.py
CHANGED
|
@@ -522,7 +522,9 @@ class BaseStep(ModelObj):
|
|
|
522
522
|
|
|
523
523
|
root = self._extract_root_step()
|
|
524
524
|
|
|
525
|
-
if not isinstance(root, RootFlowStep)
|
|
525
|
+
if not isinstance(root, RootFlowStep) or (
|
|
526
|
+
isinstance(root, RootFlowStep) and root.engine != "async"
|
|
527
|
+
):
|
|
526
528
|
raise GraphError(
|
|
527
529
|
"ModelRunnerStep can be added to 'Flow' topology graph only"
|
|
528
530
|
)
|
|
@@ -1148,6 +1150,7 @@ class Model(storey.ParallelExecutionRunnable, ModelObj):
|
|
|
1148
1150
|
"artifact_uri",
|
|
1149
1151
|
"shared_runnable_name",
|
|
1150
1152
|
"shared_proxy_mapping",
|
|
1153
|
+
"execution_mechanism",
|
|
1151
1154
|
]
|
|
1152
1155
|
kind = "model"
|
|
1153
1156
|
|
|
@@ -1170,6 +1173,7 @@ class Model(storey.ParallelExecutionRunnable, ModelObj):
|
|
|
1170
1173
|
self.model_artifact: Optional[ModelArtifact] = None
|
|
1171
1174
|
self.model_provider: Optional[ModelProvider] = None
|
|
1172
1175
|
self._artifact_were_loaded = False
|
|
1176
|
+
self._execution_mechanism = None
|
|
1173
1177
|
|
|
1174
1178
|
def __init_subclass__(cls):
|
|
1175
1179
|
super().__init_subclass__()
|
|
@@ -1189,6 +1193,20 @@ class Model(storey.ParallelExecutionRunnable, ModelObj):
|
|
|
1189
1193
|
raise_missing_schema_exception=False,
|
|
1190
1194
|
)
|
|
1191
1195
|
|
|
1196
|
+
# Check if the relevant predict method is implemented when trying to initialize the model
|
|
1197
|
+
if self._execution_mechanism == storey.ParallelExecutionMechanisms.asyncio:
|
|
1198
|
+
if self.__class__.predict_async is Model.predict_async:
|
|
1199
|
+
raise mlrun.errors.ModelRunnerError(
|
|
1200
|
+
f"{self.name} is running with {self._execution_mechanism} execution_mechanism but predict_async() "
|
|
1201
|
+
f"is not implemented"
|
|
1202
|
+
)
|
|
1203
|
+
else:
|
|
1204
|
+
if self.__class__.predict is Model.predict:
|
|
1205
|
+
raise mlrun.errors.ModelRunnerError(
|
|
1206
|
+
f"{self.name} is running with {self._execution_mechanism} execution_mechanism but predict() "
|
|
1207
|
+
f"is not implemented"
|
|
1208
|
+
)
|
|
1209
|
+
|
|
1192
1210
|
def _load_artifacts(self) -> None:
|
|
1193
1211
|
if not self._artifact_were_loaded:
|
|
1194
1212
|
artifact = self._get_artifact_object()
|
|
@@ -1219,11 +1237,11 @@ class Model(storey.ParallelExecutionRunnable, ModelObj):
|
|
|
1219
1237
|
|
|
1220
1238
|
def predict(self, body: Any, **kwargs) -> Any:
|
|
1221
1239
|
"""Override to implement prediction logic. If the logic requires asyncio, override predict_async() instead."""
|
|
1222
|
-
|
|
1240
|
+
raise NotImplementedError("predict() method not implemented")
|
|
1223
1241
|
|
|
1224
1242
|
async def predict_async(self, body: Any, **kwargs) -> Any:
|
|
1225
1243
|
"""Override to implement prediction logic if the logic requires asyncio."""
|
|
1226
|
-
|
|
1244
|
+
raise NotImplementedError("predict_async() method not implemented")
|
|
1227
1245
|
|
|
1228
1246
|
def run(self, body: Any, path: str, origin_name: Optional[str] = None) -> Any:
|
|
1229
1247
|
return self.predict(body)
|
|
@@ -1644,6 +1662,8 @@ class ModelRunnerStep(MonitoredStep):
|
|
|
1644
1662
|
Note when ModelRunnerStep is used in a graph, MLRun automatically imports
|
|
1645
1663
|
the default language model class (LLModel) during function deployment.
|
|
1646
1664
|
|
|
1665
|
+
Note ModelRunnerStep can only be added to a graph that has the flow topology and running with async engine.
|
|
1666
|
+
|
|
1647
1667
|
:param model_selector: ModelSelector instance whose select() method will be used to select models to run on each
|
|
1648
1668
|
event. Optional. If not passed, all models will be run.
|
|
1649
1669
|
:param raise_exception: If True, an error will be raised when model selection fails or if one of the models raised
|
|
@@ -2091,24 +2111,28 @@ class ModelRunnerStep(MonitoredStep):
|
|
|
2091
2111
|
)
|
|
2092
2112
|
model_objects = []
|
|
2093
2113
|
for model, model_params in models.values():
|
|
2114
|
+
model_name = model_params.get("name")
|
|
2094
2115
|
model_params[schemas.MonitoringData.INPUT_PATH] = (
|
|
2095
2116
|
self.class_args.get(
|
|
2096
2117
|
mlrun.common.schemas.ModelRunnerStepData.MONITORING_DATA, {}
|
|
2097
2118
|
)
|
|
2098
|
-
.get(
|
|
2119
|
+
.get(model_name, {})
|
|
2099
2120
|
.get(schemas.MonitoringData.INPUT_PATH)
|
|
2100
2121
|
)
|
|
2101
2122
|
model_params[schemas.MonitoringData.RESULT_PATH] = (
|
|
2102
2123
|
self.class_args.get(
|
|
2103
2124
|
mlrun.common.schemas.ModelRunnerStepData.MONITORING_DATA, {}
|
|
2104
2125
|
)
|
|
2105
|
-
.get(
|
|
2126
|
+
.get(model_name, {})
|
|
2106
2127
|
.get(schemas.MonitoringData.RESULT_PATH)
|
|
2107
2128
|
)
|
|
2108
2129
|
model = get_class(model, namespace).from_dict(
|
|
2109
2130
|
model_params, init_with_params=True
|
|
2110
2131
|
)
|
|
2111
2132
|
model._raise_exception = False
|
|
2133
|
+
model._execution_mechanism = execution_mechanism_by_model_name.get(
|
|
2134
|
+
model_name
|
|
2135
|
+
)
|
|
2112
2136
|
model_objects.append(model)
|
|
2113
2137
|
self._async_object = ModelRunner(
|
|
2114
2138
|
model_selector=model_selector,
|
|
@@ -3018,6 +3042,7 @@ class RootFlowStep(FlowStep):
|
|
|
3018
3042
|
model_params, init_with_params=True
|
|
3019
3043
|
)
|
|
3020
3044
|
model._raise_exception = False
|
|
3045
|
+
model._execution_mechanism = self._shared_models_mechanism[model.name]
|
|
3021
3046
|
self.context.executor.add_runnable(
|
|
3022
3047
|
model, self._shared_models_mechanism[model.name]
|
|
3023
3048
|
)
|
mlrun/utils/helpers.py
CHANGED
|
@@ -253,6 +253,40 @@ def verify_field_regex(
|
|
|
253
253
|
return False
|
|
254
254
|
|
|
255
255
|
|
|
256
|
+
def validate_function_name(name: str) -> None:
|
|
257
|
+
"""
|
|
258
|
+
Validate that a function name conforms to Kubernetes DNS-1123 label requirements.
|
|
259
|
+
|
|
260
|
+
Function names for Kubernetes resources must:
|
|
261
|
+
- Be lowercase alphanumeric characters or '-'
|
|
262
|
+
- Start and end with an alphanumeric character
|
|
263
|
+
- Be at most 63 characters long
|
|
264
|
+
|
|
265
|
+
This validation should be called AFTER normalize_name() has been applied.
|
|
266
|
+
|
|
267
|
+
Refer to https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#dns-label-names
|
|
268
|
+
|
|
269
|
+
:param name: The function name to validate (after normalization)
|
|
270
|
+
:raises MLRunInvalidArgumentError: If the function name is invalid for Kubernetes
|
|
271
|
+
"""
|
|
272
|
+
if not name:
|
|
273
|
+
return
|
|
274
|
+
|
|
275
|
+
verify_field_regex(
|
|
276
|
+
"function.metadata.name",
|
|
277
|
+
name,
|
|
278
|
+
mlrun.utils.regex.dns_1123_label,
|
|
279
|
+
raise_on_failure=True,
|
|
280
|
+
log_message=(
|
|
281
|
+
f"Function name '{name}' is invalid. "
|
|
282
|
+
"Kubernetes function names must be DNS-1123 labels: "
|
|
283
|
+
"lowercase alphanumeric characters or '-', "
|
|
284
|
+
"starting and ending with an alphanumeric character, "
|
|
285
|
+
"and at most 63 characters long."
|
|
286
|
+
),
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
|
|
256
290
|
def validate_builder_source(
|
|
257
291
|
source: str, pull_at_runtime: bool = False, workdir: Optional[str] = None
|
|
258
292
|
):
|
|
@@ -476,6 +510,40 @@ def normalize_name(name: str):
|
|
|
476
510
|
return name.lower()
|
|
477
511
|
|
|
478
512
|
|
|
513
|
+
def ensure_batch_job_suffix(
|
|
514
|
+
function_name: typing.Optional[str],
|
|
515
|
+
) -> tuple[typing.Optional[str], bool, str]:
|
|
516
|
+
"""
|
|
517
|
+
Ensure that a function name has the batch job suffix appended to prevent database collision.
|
|
518
|
+
|
|
519
|
+
This helper is used by to_job() methods in runtimes that convert online functions (serving, local)
|
|
520
|
+
to batch processing jobs. The suffix prevents the job from overwriting the original function in
|
|
521
|
+
the database when both are stored with the same (project, name) key.
|
|
522
|
+
|
|
523
|
+
:param function_name: The original function name (can be None or empty string)
|
|
524
|
+
|
|
525
|
+
:return: A tuple of (modified_name, was_renamed, suffix) where:
|
|
526
|
+
- modified_name: The function name with the batch suffix (if not already present),
|
|
527
|
+
or empty string if input was empty
|
|
528
|
+
- was_renamed: True if the suffix was added, False if it was already present or if name was empty/None
|
|
529
|
+
- suffix: The suffix value that was used (or would have been used)
|
|
530
|
+
|
|
531
|
+
"""
|
|
532
|
+
suffix = mlrun_constants.RESERVED_BATCH_JOB_SUFFIX
|
|
533
|
+
|
|
534
|
+
# Handle None or empty string
|
|
535
|
+
if not function_name:
|
|
536
|
+
return function_name, False, suffix
|
|
537
|
+
|
|
538
|
+
if not function_name.endswith(suffix):
|
|
539
|
+
return (
|
|
540
|
+
f"{function_name}{suffix}",
|
|
541
|
+
True,
|
|
542
|
+
suffix,
|
|
543
|
+
)
|
|
544
|
+
return function_name, False, suffix
|
|
545
|
+
|
|
546
|
+
|
|
479
547
|
class LogBatchWriter:
|
|
480
548
|
def __init__(self, func, batch=16, maxtime=5):
|
|
481
549
|
self.batch = batch
|
|
@@ -970,8 +1038,15 @@ def enrich_image_url(
|
|
|
970
1038
|
else:
|
|
971
1039
|
image_url = "mlrun/mlrun"
|
|
972
1040
|
|
|
973
|
-
if is_mlrun_image and tag
|
|
974
|
-
|
|
1041
|
+
if is_mlrun_image and tag:
|
|
1042
|
+
if ":" not in image_url:
|
|
1043
|
+
image_url = f"{image_url}:{tag}"
|
|
1044
|
+
elif enrich_kfp_python_version:
|
|
1045
|
+
# For mlrun-kfp >= 1.10.0-rc0, append python suffix to existing tag
|
|
1046
|
+
python_suffix = resolve_image_tag_suffix(
|
|
1047
|
+
mlrun_version, client_python_version
|
|
1048
|
+
)
|
|
1049
|
+
image_url = f"{image_url}{python_suffix}" if python_suffix else image_url
|
|
975
1050
|
|
|
976
1051
|
registry = (
|
|
977
1052
|
config.images_registry if is_mlrun_image else config.vendor_images_registry
|
|
@@ -2464,15 +2539,16 @@ def merge_requirements(
|
|
|
2464
2539
|
return [str(req) for req in merged.values()]
|
|
2465
2540
|
|
|
2466
2541
|
|
|
2467
|
-
def
|
|
2542
|
+
def get_source_and_working_dir_paths(source_file_path) -> (pathlib.Path, pathlib.Path):
|
|
2468
2543
|
source_file_path_object = pathlib.Path(source_file_path).resolve()
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2544
|
+
working_dir_path_object = pathlib.Path(".").resolve()
|
|
2545
|
+
return source_file_path_object, working_dir_path_object
|
|
2546
|
+
|
|
2547
|
+
|
|
2548
|
+
def get_relative_module_name_from_path(
|
|
2549
|
+
source_file_path_object, working_dir_path_object
|
|
2550
|
+
) -> str:
|
|
2475
2551
|
relative_path_to_source_file = source_file_path_object.relative_to(
|
|
2476
|
-
|
|
2552
|
+
working_dir_path_object
|
|
2477
2553
|
)
|
|
2478
2554
|
return ".".join(relative_path_to_source_file.with_suffix("").parts)
|
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.0rc39
|
|
4
4
|
Summary: Tracking and config of machine learning runs
|
|
5
5
|
Home-page: https://github.com/mlrun/mlrun
|
|
6
6
|
Author: Yaron Haviv
|
|
@@ -113,7 +113,7 @@ Requires-Dist: apscheduler<4,>=3.11; extra == "api"
|
|
|
113
113
|
Requires-Dist: objgraph~=3.6; extra == "api"
|
|
114
114
|
Requires-Dist: igz-mgmt~=0.4.1; extra == "api"
|
|
115
115
|
Requires-Dist: humanfriendly~=10.0; extra == "api"
|
|
116
|
-
Requires-Dist: fastapi~=0.
|
|
116
|
+
Requires-Dist: fastapi~=0.120.0; extra == "api"
|
|
117
117
|
Requires-Dist: sqlalchemy~=2.0; extra == "api"
|
|
118
118
|
Requires-Dist: sqlalchemy-utils~=0.41.2; extra == "api"
|
|
119
119
|
Requires-Dist: pymysql~=1.1; extra == "api"
|
|
@@ -203,7 +203,7 @@ Requires-Dist: dask~=2023.12.1; python_version < "3.11" and extra == "complete-a
|
|
|
203
203
|
Requires-Dist: databricks-sdk~=0.20.0; extra == "complete-api"
|
|
204
204
|
Requires-Dist: distributed==2024.8; python_version >= "3.11" and extra == "complete-api"
|
|
205
205
|
Requires-Dist: distributed~=2023.12.1; python_version < "3.11" and extra == "complete-api"
|
|
206
|
-
Requires-Dist: fastapi~=0.
|
|
206
|
+
Requires-Dist: fastapi~=0.120.0; extra == "complete-api"
|
|
207
207
|
Requires-Dist: gcsfs<=2025.7.0,>=2025.5.1; extra == "complete-api"
|
|
208
208
|
Requires-Dist: google-cloud-bigquery-storage~=2.17; extra == "complete-api"
|
|
209
209
|
Requires-Dist: google-cloud-bigquery[bqstorage,pandas]==3.14.1; extra == "complete-api"
|
|
@@ -6,25 +6,25 @@ mlrun/execution.py,sha256=Ozu8SjO-nQ6l5vHwqrTQjmP6koMpUqNQpp6qn6jvhVE,58802
|
|
|
6
6
|
mlrun/features.py,sha256=jMEXo6NB36A6iaxNEJWzdtYwUmglYD90OIKTIEeWhE8,15841
|
|
7
7
|
mlrun/k8s_utils.py,sha256=zIacVyvsXrXVO-DdxAoGQOGEDWOGJEFJzYPhPVnn3z8,24548
|
|
8
8
|
mlrun/lists.py,sha256=OlaV2QIFUzmenad9kxNJ3k4whlDyxI3zFbGwr6vpC5Y,8561
|
|
9
|
-
mlrun/model.py,sha256=
|
|
9
|
+
mlrun/model.py,sha256=KnLnJjGa7SsJO1LSTvoMQbmp3JN4LExA7yJqCXvgAY4,89444
|
|
10
10
|
mlrun/render.py,sha256=5DlhD6JtzHgmj5RVlpaYiHGhX84Q7qdi4RCEUj2UMgw,13195
|
|
11
|
-
mlrun/run.py,sha256=
|
|
11
|
+
mlrun/run.py,sha256=BA0CPJqtcM3c-hMmnGl6AccRMyIejJxvCcrSXTwcRwU,49841
|
|
12
12
|
mlrun/secrets.py,sha256=VFETVDJFZ0AGDivYjhYscO_YHnzeBnAebxlio7Svkq0,9633
|
|
13
13
|
mlrun/alerts/__init__.py,sha256=0gtG1BG0DXxFrXegIkjbM1XEN4sP9ODo0ucXrNld1hU,601
|
|
14
14
|
mlrun/alerts/alert.py,sha256=QQFZGydQbx9RvAaSiaH-ALQZVcDKQX5lgizqj_rXW2k,15948
|
|
15
15
|
mlrun/artifacts/__init__.py,sha256=ZrEUNto7tGdnBGteCp9zOyO8b78z7O3xgcpzUt9UHE4,1240
|
|
16
16
|
mlrun/artifacts/base.py,sha256=6x_2KPMNOciiNNUsiKgJ-b6ejxAHm_Ro22xODLoTc44,28559
|
|
17
17
|
mlrun/artifacts/dataset.py,sha256=bhb5Kfbs8P28yjnpN76th5lLEUl5nAqD4VqVzHEVPrM,16421
|
|
18
|
-
mlrun/artifacts/document.py,sha256=
|
|
18
|
+
mlrun/artifacts/document.py,sha256=VYoWcFUB45B-z_nhbedFCpiorZsDBxkTNRLWjYvri0Q,17585
|
|
19
19
|
mlrun/artifacts/helpers.py,sha256=ejTEC9vkI2w5FHn5Gopw3VEIxuni0bazWUnR6BBWZfU,1662
|
|
20
20
|
mlrun/artifacts/llm_prompt.py,sha256=pshXzYXPDBAe6C0vecn9MyRNyPdxrah3c80oZUKkYWA,9840
|
|
21
21
|
mlrun/artifacts/manager.py,sha256=_cDNCS7wwmFIsucJ2uOgHxZQECmIGb8Wye64b6oLgKU,16642
|
|
22
22
|
mlrun/artifacts/model.py,sha256=9yU9NZlxxY_ifSyXOgMnPi_RMDmawY9A-rLi-_VJs4c,25662
|
|
23
23
|
mlrun/artifacts/plots.py,sha256=wmaxVXiAPSCyn3M7pIlcBu9pP3O8lrq0Ewx6iHRDF9s,4238
|
|
24
24
|
mlrun/common/__init__.py,sha256=kXGBqhLN0rlAx0kTXhozGzFsIdSqW0uTSKMmsLgq_is,569
|
|
25
|
-
mlrun/common/constants.py,sha256=
|
|
25
|
+
mlrun/common/constants.py,sha256=BbKFJN6EoFSOmUmvos20ZAty8hWtwG0quTrfbLzrWO4,4294
|
|
26
26
|
mlrun/common/helpers.py,sha256=DIdqs_eN3gO5bZ8iFobIvx8cEiOxYxhFIyut6-O69T0,1385
|
|
27
|
-
mlrun/common/secrets.py,sha256=
|
|
27
|
+
mlrun/common/secrets.py,sha256=SCNLDIzC9-4r7CcJuJJxSAA89dUDcKD7g3yvSKIhqN4,5439
|
|
28
28
|
mlrun/common/types.py,sha256=1gxThbmC0Vd0U1ffIkEwz4T4S7JOgHt70rvw8TCO21c,1073
|
|
29
29
|
mlrun/common/db/__init__.py,sha256=kXGBqhLN0rlAx0kTXhozGzFsIdSqW0uTSKMmsLgq_is,569
|
|
30
30
|
mlrun/common/db/dialects.py,sha256=QN9bx7CTo32IIdJ2J3ZrsX8IUdp_BPxBtl0LyjMEC9g,868
|
|
@@ -74,7 +74,7 @@ mlrun/common/schemas/serving.py,sha256=4ek9JZDagkdeXyfkX6P6xp4deUNSf_kqXUaXcKSuv
|
|
|
74
74
|
mlrun/common/schemas/tag.py,sha256=1wqEiAujsElojWb3qmuyfcaLFjXSNAAQdafkDx7fkn0,891
|
|
75
75
|
mlrun/common/schemas/workflow.py,sha256=Y-FHJnxs5c86yetuOAPdEJPkne__tLPCxjSXSb4lrjo,2541
|
|
76
76
|
mlrun/common/schemas/model_monitoring/__init__.py,sha256=ndeGXCJTE_GvMSb1FfQ5fXvhs0I8nO_yWy1UBSZIifY,1956
|
|
77
|
-
mlrun/common/schemas/model_monitoring/constants.py,sha256=
|
|
77
|
+
mlrun/common/schemas/model_monitoring/constants.py,sha256=GimtHVQmMkCh4e5Fy0YLeeMWP8D5uCvVcWdpM63SPJg,13906
|
|
78
78
|
mlrun/common/schemas/model_monitoring/functions.py,sha256=Ej8ChjmMZq1HP32THNABoktQHN1mdlkSqKbofxu10i4,2536
|
|
79
79
|
mlrun/common/schemas/model_monitoring/grafana.py,sha256=THQlLfPBevBksta8p5OaIsBaJtsNSXexLvHrDxOaVns,2095
|
|
80
80
|
mlrun/common/schemas/model_monitoring/model_endpoints.py,sha256=aevkfKWRbRj2cxabeUrVka49lJ2SRDA7I8rD-Fihr2Q,13648
|
|
@@ -225,10 +225,10 @@ mlrun/launcher/__init__.py,sha256=JL8qkT1lLr1YvW6iP0hmwDTaSR2RfrMDx0-1gWRhTOE,57
|
|
|
225
225
|
mlrun/launcher/base.py,sha256=IgBE-ZS1ZiGzucg5SElGtO4qOB0cqYQfGtZTcRc2S3Y,17378
|
|
226
226
|
mlrun/launcher/client.py,sha256=cl40ZdF2fU1QbUKdl4Xnucb1u2h-8_dn095qIUyxbuM,6402
|
|
227
227
|
mlrun/launcher/factory.py,sha256=RW7mfzEFi8fR0M-4W1JQg1iq3_muUU6OTqT_3l4Ubrk,2338
|
|
228
|
-
mlrun/launcher/local.py,sha256=
|
|
228
|
+
mlrun/launcher/local.py,sha256=sBpVxNoVLiPGDam_PFLJUb0vvKAWjMgqNY0A_RiR0hQ,12218
|
|
229
229
|
mlrun/launcher/remote.py,sha256=zFXE52Cq_7EkC8lfNKT0ceIbye0CfFiundF7O1YU4Xw,7810
|
|
230
230
|
mlrun/model_monitoring/__init__.py,sha256=qDQnncjya9XPTlfvGyfWsZWiXc-glGZrrNja-5QmCZk,782
|
|
231
|
-
mlrun/model_monitoring/api.py,sha256=
|
|
231
|
+
mlrun/model_monitoring/api.py,sha256=yxADX4V7rNPgh2Zh95hIDtWuhkinItay-SfMwm9bqGY,27783
|
|
232
232
|
mlrun/model_monitoring/controller.py,sha256=2XOkOZRB03K9ph6TH-ICspHga-GQOURL0C8-0GTHaTY,43961
|
|
233
233
|
mlrun/model_monitoring/features_drift_table.py,sha256=c6GpKtpOJbuT1u5uMWDL_S-6N4YPOmlktWMqPme3KFY,25308
|
|
234
234
|
mlrun/model_monitoring/helpers.py,sha256=50oFqgIc5xFHYPIVgq3M-Gbr7epqAI5NgHmvOeMy52U,24667
|
|
@@ -236,7 +236,7 @@ mlrun/model_monitoring/stream_processing.py,sha256=bryYO3D0cC10MAQ-liHxUZ79MrL-V
|
|
|
236
236
|
mlrun/model_monitoring/writer.py,sha256=l2D_5Ms5Wq5jfyQRVJbGBBRTMLjMmIAxwPeHWmrc9Kg,16382
|
|
237
237
|
mlrun/model_monitoring/applications/__init__.py,sha256=BwlmRELlFJf2b2YMyv5kUSHNe8--OyqWhDgRlT8a_8g,779
|
|
238
238
|
mlrun/model_monitoring/applications/_application_steps.py,sha256=t9LDIqQUGE10cyjyhlg0QqN1yVx0apD1HpERYLJfm8U,7409
|
|
239
|
-
mlrun/model_monitoring/applications/base.py,sha256=
|
|
239
|
+
mlrun/model_monitoring/applications/base.py,sha256=Bd8bD2kJlS4ZaGAkj88o4iwRjXIE76gYdhXqAjXfUCk,51344
|
|
240
240
|
mlrun/model_monitoring/applications/context.py,sha256=3W3AW4oyJgx_nW_5mDsV59Iy5D3frkfYMQSc6DgBc4c,17004
|
|
241
241
|
mlrun/model_monitoring/applications/histogram_data_drift.py,sha256=2qgfFmrpHf-x0_EaHD-0T28piwSQzw-HH71aV1GwbZs,15389
|
|
242
242
|
mlrun/model_monitoring/applications/results.py,sha256=LfBQOmkpKGvVGNrcj5QiXsRIG2IRgcv_Xqe4QJBmauk,5699
|
|
@@ -252,7 +252,7 @@ mlrun/model_monitoring/db/tsdb/tdengine/__init__.py,sha256=vgBdsKaXUURKqIf3M0y4s
|
|
|
252
252
|
mlrun/model_monitoring/db/tsdb/tdengine/schemas.py,sha256=TuWuaCZw8sV1gSwN2BPmW8Gzwe3dsRN__KkJB9lum00,13116
|
|
253
253
|
mlrun/model_monitoring/db/tsdb/tdengine/stream_graph_steps.py,sha256=Uadj0UvAmln2MxDWod-kAzau1uNlqZh981rPhbUH_5M,2857
|
|
254
254
|
mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connection.py,sha256=dtkaHaWKWERPXylEWMECeetwrz3rWl0P43AADcTjlls,9330
|
|
255
|
-
mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py,sha256=
|
|
255
|
+
mlrun/model_monitoring/db/tsdb/tdengine/tdengine_connector.py,sha256=qq-2Slk9jHc18lUx00w4Oj5_EfnZXlmkvLXPDSbz1zY,54341
|
|
256
256
|
mlrun/model_monitoring/db/tsdb/tdengine/writer_graph_steps.py,sha256=zMof6hUr0dsyor73pnOWkJP62INAvslHU0nUklbT-3w,2053
|
|
257
257
|
mlrun/model_monitoring/db/tsdb/v3io/__init__.py,sha256=aL3bfmQsUQ-sbvKGdNihFj8gLCK3mSys0qDcXtYOwgc,616
|
|
258
258
|
mlrun/model_monitoring/db/tsdb/v3io/stream_graph_steps.py,sha256=sNQFj6qyJx5eSBKRC3gyTc1cfh1l2IkRpPtuZwtzCW0,6844
|
|
@@ -281,17 +281,17 @@ mlrun/platforms/iguazio.py,sha256=32_o95Ntx9z3ciowt2NcnX7tAiLBwX3VB0mbTQ-KrIQ,13
|
|
|
281
281
|
mlrun/projects/__init__.py,sha256=hdCOA6_fp8X4qGGGT7Bj7sPbkM1PayWuaVZL0DkpuZw,1240
|
|
282
282
|
mlrun/projects/operations.py,sha256=Oo7h0TMztI_RVmj0rQxNS1igS_c94HpQZwMIFjiWt0E,21038
|
|
283
283
|
mlrun/projects/pipelines.py,sha256=ZOfuIEHOXfuc4qAkuWvbWhCjP6kqpLkv-yBBaY9RXhg,52219
|
|
284
|
-
mlrun/projects/project.py,sha256=
|
|
285
|
-
mlrun/runtimes/__init__.py,sha256=
|
|
286
|
-
mlrun/runtimes/base.py,sha256=
|
|
284
|
+
mlrun/projects/project.py,sha256=alWbBKoOl_fej8IyFB397r7JYPZkSlRXrSsmVaaQw8U,258335
|
|
285
|
+
mlrun/runtimes/__init__.py,sha256=NSIk1xlUduSY3ZZ2tLmXegw1Z1im5_KjtbmsL874Z6s,9829
|
|
286
|
+
mlrun/runtimes/base.py,sha256=CpIaml8nJbbPt8PQXDzy_AXbJ0LObRWk1lprRBDRYdI,38986
|
|
287
287
|
mlrun/runtimes/daskjob.py,sha256=IN6gKKrmCIjWooj5FgFm-pAb2i7ra1ERRzClfu_rYGI,20102
|
|
288
288
|
mlrun/runtimes/funcdoc.py,sha256=zRFHrJsV8rhDLJwoUhcfZ7Cs0j-tQ76DxwUqdXV_Wyc,9810
|
|
289
289
|
mlrun/runtimes/function_reference.py,sha256=fnMKUEieKgy4JyVLhFpDtr6JvKgOaQP8F_K2H3-Pk9U,5030
|
|
290
290
|
mlrun/runtimes/generators.py,sha256=X8NDlCEPveDDPOHtOGcSpbl3pAVM3DP7fuPj5xVhxEY,7290
|
|
291
291
|
mlrun/runtimes/kubejob.py,sha256=wadCzmSgjv9OU_Ax8CQNHfXLo0v-ev9ZGHUFGcNc9Qw,8577
|
|
292
|
-
mlrun/runtimes/local.py,sha256=
|
|
293
|
-
mlrun/runtimes/mounts.py,sha256=
|
|
294
|
-
mlrun/runtimes/pod.py,sha256=
|
|
292
|
+
mlrun/runtimes/local.py,sha256=sbIFQdU-GBfAwESJGU06hiqXUiwjP6IVvbCieIPGQn4,22311
|
|
293
|
+
mlrun/runtimes/mounts.py,sha256=S2nC5UaZFAl8N5HRyLkD9AUj7RqlPVSYVJcP9W8v4sg,19594
|
|
294
|
+
mlrun/runtimes/pod.py,sha256=4GZCobICnLcIBCjIbdCRuL-4k7tu0CKXzv3iatKD_gg,60192
|
|
295
295
|
mlrun/runtimes/remotesparkjob.py,sha256=BalAea66GleaKeoYTw6ZL1Qr4wf1yRxfgk1-Fkc9Pqg,7864
|
|
296
296
|
mlrun/runtimes/utils.py,sha256=b0T5Qm0WmbHk_I4d14ikzhhjymjIVedaifBi-ymKwOc,17733
|
|
297
297
|
mlrun/runtimes/databricks_job/__init__.py,sha256=kXGBqhLN0rlAx0kTXhozGzFsIdSqW0uTSKMmsLgq_is,569
|
|
@@ -303,11 +303,11 @@ mlrun/runtimes/mpijob/abstract.py,sha256=QjAG4OZ6JEQ58w5-qYNd6hUGwvaW8ynLtlr9jNf
|
|
|
303
303
|
mlrun/runtimes/mpijob/v1.py,sha256=zSlRkiWHz4B3yht66sVf4mlfDs8YT9EnP9DfBLn5VNs,3372
|
|
304
304
|
mlrun/runtimes/nuclio/__init__.py,sha256=osOVMN9paIOuUoOTizmkxMb_OXRP-SlPwXHJSSYK_wk,834
|
|
305
305
|
mlrun/runtimes/nuclio/api_gateway.py,sha256=vH9ClKVP4Mb24rvA67xPuAvAhX-gAv6vVtjVxyplhdc,26969
|
|
306
|
-
mlrun/runtimes/nuclio/function.py,sha256=
|
|
306
|
+
mlrun/runtimes/nuclio/function.py,sha256=vcW7IqCeLzxJ9YFBErqtEkp4MbgomFC1qWFTznBbNEY,56078
|
|
307
307
|
mlrun/runtimes/nuclio/nuclio.py,sha256=sLK8KdGO1LbftlL3HqPZlFOFTAAuxJACZCVl1c0Ha6E,2942
|
|
308
|
-
mlrun/runtimes/nuclio/serving.py,sha256=
|
|
308
|
+
mlrun/runtimes/nuclio/serving.py,sha256=Y7wyqPrBS3Z-0QR6suGbqX36Cq-dKUwHI7zvO1fuLLo,39425
|
|
309
309
|
mlrun/runtimes/nuclio/application/__init__.py,sha256=rRs5vasy_G9IyoTpYIjYDafGoL6ifFBKgBtsXn31Atw,614
|
|
310
|
-
mlrun/runtimes/nuclio/application/application.py,sha256=
|
|
310
|
+
mlrun/runtimes/nuclio/application/application.py,sha256=eVOdGXZF23VMxRhR_cZYArxxaUKKXtEshtomFf-vp3E,34065
|
|
311
311
|
mlrun/runtimes/nuclio/application/reverse_proxy.go,sha256=lEHH74vr2PridIHp1Jkc_NjkrWb5b6zawRrNxHQhwGU,2913
|
|
312
312
|
mlrun/runtimes/sparkjob/__init__.py,sha256=GPP_ekItxiU9Ydn3mJa4Obph02Bg6DO-JYs791_MV58,607
|
|
313
313
|
mlrun/runtimes/sparkjob/spark3job.py,sha256=3dW7RG2T58F2dsUw0TsRvE3SIFcekx3CerLdcaG1f50,41458
|
|
@@ -315,9 +315,9 @@ mlrun/serving/__init__.py,sha256=nriJAcVn5aatwU03T7SsE6ngJEGTxr3wIGt4WuvCCzY,139
|
|
|
315
315
|
mlrun/serving/merger.py,sha256=pfOQoozUyObCTpqXAMk94PmhZefn4bBrKufO3MKnkAc,6193
|
|
316
316
|
mlrun/serving/remote.py,sha256=p29CBtKwbW_l8BzmNg3Uy__0eMf7_OubTMzga_S3EOA,22089
|
|
317
317
|
mlrun/serving/routers.py,sha256=pu5jlSLI4Ml68YP_FMFDhhwPfLcT6lRu5yL5QDgXPHQ,52889
|
|
318
|
-
mlrun/serving/server.py,sha256=
|
|
318
|
+
mlrun/serving/server.py,sha256=UIQON9ytG_4VUa4cMWZ8AxxBzGjBrhFhkC-FrvAHa_o,42593
|
|
319
319
|
mlrun/serving/serving_wrapper.py,sha256=UL9hhWCfMPcTJO_XrkvNaFvck1U1E7oS8trTZyak0cA,835
|
|
320
|
-
mlrun/serving/states.py,sha256=
|
|
320
|
+
mlrun/serving/states.py,sha256=zSaELw7et0tAHa0nZ2_zKTPdxqnXo4WOCwclULkN6no,140363
|
|
321
321
|
mlrun/serving/steps.py,sha256=zbMgJnu-m4n7vhFRgZkCMMifIsCya-TzAj3Gjc-Fgnc,2193
|
|
322
322
|
mlrun/serving/system_steps.py,sha256=BDCJn73h7cUT5AoSSm25Fjg4WwzcEpMQp-ZjMw9ogEc,20025
|
|
323
323
|
mlrun/serving/utils.py,sha256=Zbfqm8TKNcTE8zRBezVBzpvR2WKeKeIRN7otNIaiYEc,4170
|
|
@@ -333,7 +333,7 @@ mlrun/utils/async_http.py,sha256=8Olx8TNNeXB07nEGwlqhEgFgnFAD71vBU_bqaA9JW-w,122
|
|
|
333
333
|
mlrun/utils/azure_vault.py,sha256=IEFizrDGDbAaoWwDr1WoA88S_EZ0T--vjYtY-i0cvYQ,3450
|
|
334
334
|
mlrun/utils/clones.py,sha256=qbAGyEbSvlewn3Tw_DpQZP9z6MGzFhSaZfI1CblX8Fg,7515
|
|
335
335
|
mlrun/utils/condition_evaluator.py,sha256=-nGfRmZzivn01rHTroiGY4rqEv8T1irMyhzxEei-sKc,1897
|
|
336
|
-
mlrun/utils/helpers.py,sha256=
|
|
336
|
+
mlrun/utils/helpers.py,sha256=95nTQRqqsWeDzt19AEvz2pAkbEOfamvWg1es4cC6_us,87120
|
|
337
337
|
mlrun/utils/http.py,sha256=5ZU2VpokaUM_DT3HBSqTm8xjUqTPjZN5fKkSIvKlTl0,8704
|
|
338
338
|
mlrun/utils/logger.py,sha256=uaCgI_ezzaXf7nJDCy-1Nrjds8vSXqDbzmjmb3IyCQo,14864
|
|
339
339
|
mlrun/utils/regex.py,sha256=FcRwWD8x9X3HLhCCU2F0AVKTFah784Pr7ZAe3a02jw8,5199
|
|
@@ -352,11 +352,11 @@ mlrun/utils/notifications/notification/mail.py,sha256=ZyJ3eqd8simxffQmXzqd3bgbAq
|
|
|
352
352
|
mlrun/utils/notifications/notification/slack.py,sha256=wSu_7W0EnGLBNwIgWCYEeTP8j9SPAMPDBnfUcPnVZYA,7299
|
|
353
353
|
mlrun/utils/notifications/notification/webhook.py,sha256=FM5-LQAKAVJKp37MRzR3SsejalcnpM6r_9Oe7znxZEA,5313
|
|
354
354
|
mlrun/utils/version/__init__.py,sha256=YnzE6tlf24uOQ8y7Z7l96QLAI6-QEii7-77g8ynmzy0,613
|
|
355
|
-
mlrun/utils/version/version.json,sha256=
|
|
355
|
+
mlrun/utils/version/version.json,sha256=0zf1ul8-GHaXcJBnhG8fhsvEvZAHbHo2DlsdZIEg4mk,90
|
|
356
356
|
mlrun/utils/version/version.py,sha256=M2hVhRrgkN3SxacZHs3ZqaOsqAA7B6a22ne324IQ1HE,1877
|
|
357
|
-
mlrun-1.10.
|
|
358
|
-
mlrun-1.10.
|
|
359
|
-
mlrun-1.10.
|
|
360
|
-
mlrun-1.10.
|
|
361
|
-
mlrun-1.10.
|
|
362
|
-
mlrun-1.10.
|
|
357
|
+
mlrun-1.10.0rc39.dist-info/licenses/LICENSE,sha256=zTiv1CxWNkOk1q8eJS1G_8oD4gWpWLwWxj_Agcsi8Os,11337
|
|
358
|
+
mlrun-1.10.0rc39.dist-info/METADATA,sha256=ZwFeqUwSkgA190q2yKK3LxcboenfDmI8VitS_siZVy0,26104
|
|
359
|
+
mlrun-1.10.0rc39.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
360
|
+
mlrun-1.10.0rc39.dist-info/entry_points.txt,sha256=1Owd16eAclD5pfRCoJpYC2ZJSyGNTtUr0nCELMioMmU,46
|
|
361
|
+
mlrun-1.10.0rc39.dist-info/top_level.txt,sha256=NObLzw3maSF9wVrgSeYBv-fgnHkAJ1kEkh12DLdd5KM,6
|
|
362
|
+
mlrun-1.10.0rc39.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|