mlrun 1.10.0rc17__py3-none-any.whl → 1.10.0rc19__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mlrun might be problematic. Click here for more details.

Files changed (32) hide show
  1. mlrun/__init__.py +21 -2
  2. mlrun/common/constants.py +1 -0
  3. mlrun/common/formatters/artifact.py +1 -0
  4. mlrun/common/schemas/model_monitoring/constants.py +14 -6
  5. mlrun/config.py +14 -0
  6. mlrun/datastore/__init__.py +9 -1
  7. mlrun/datastore/datastore.py +4 -4
  8. mlrun/datastore/datastore_profile.py +26 -0
  9. mlrun/datastore/model_provider/huggingface_provider.py +182 -0
  10. mlrun/datastore/model_provider/model_provider.py +57 -56
  11. mlrun/datastore/model_provider/openai_provider.py +34 -20
  12. mlrun/datastore/utils.py +6 -0
  13. mlrun/launcher/base.py +13 -0
  14. mlrun/model_monitoring/api.py +5 -3
  15. mlrun/model_monitoring/applications/base.py +107 -28
  16. mlrun/model_monitoring/applications/results.py +4 -7
  17. mlrun/run.py +4 -2
  18. mlrun/runtimes/base.py +5 -2
  19. mlrun/runtimes/daskjob.py +1 -0
  20. mlrun/runtimes/nuclio/application/application.py +84 -5
  21. mlrun/runtimes/nuclio/function.py +3 -1
  22. mlrun/serving/server.py +1 -0
  23. mlrun/serving/states.py +5 -2
  24. mlrun/utils/helpers.py +16 -1
  25. mlrun/utils/logger.py +3 -1
  26. mlrun/utils/version/version.json +2 -2
  27. {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc19.dist-info}/METADATA +2 -2
  28. {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc19.dist-info}/RECORD +32 -31
  29. {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc19.dist-info}/WHEEL +0 -0
  30. {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc19.dist-info}/entry_points.txt +0 -0
  31. {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc19.dist-info}/licenses/LICENSE +0 -0
  32. {mlrun-1.10.0rc17.dist-info → mlrun-1.10.0rc19.dist-info}/top_level.txt +0 -0
mlrun/__init__.py CHANGED
@@ -31,6 +31,7 @@ from typing import Optional
31
31
 
32
32
  import dotenv
33
33
 
34
+ from .common.constants import MLRUN_ACTIVE_PROJECT
34
35
  from .config import config as mlconf
35
36
  from .datastore import DataItem, ModelProvider, store_manager
36
37
  from .db import get_run_db
@@ -167,11 +168,29 @@ def set_environment(
167
168
 
168
169
 
169
170
  def get_current_project(silent: bool = False) -> Optional[MlrunProject]:
170
- if not pipeline_context.project and not silent:
171
+ if pipeline_context.project:
172
+ return pipeline_context.project
173
+
174
+ project_name = environ.get(MLRUN_ACTIVE_PROJECT, None)
175
+ if not project_name:
176
+ if not silent:
177
+ raise MLRunInvalidArgumentError(
178
+ "No current project is initialized. Use new, get or load project functions first."
179
+ )
180
+ return None
181
+
182
+ project = load_project(
183
+ name=project_name,
184
+ url=project_name,
185
+ save=False,
186
+ sync_functions=False,
187
+ )
188
+
189
+ if not project and not silent:
171
190
  raise MLRunInvalidArgumentError(
172
191
  "No current project is initialized. Use new, get or load project functions first."
173
192
  )
174
- return pipeline_context.project
193
+ return project
175
194
 
176
195
 
177
196
  def get_sample_path(subpath=""):
mlrun/common/constants.py CHANGED
@@ -30,6 +30,7 @@ RESERVED_TAG_NAME_LATEST = "latest"
30
30
  JOB_TYPE_WORKFLOW_RUNNER = "workflow-runner"
31
31
  JOB_TYPE_PROJECT_LOADER = "project-loader"
32
32
  JOB_TYPE_RERUN_WORKFLOW_RUNNER = "rerun-workflow-runner"
33
+ MLRUN_ACTIVE_PROJECT = "MLRUN_ACTIVE_PROJECT"
33
34
 
34
35
 
35
36
  class MLRunInternalLabels:
@@ -41,6 +41,7 @@ class ArtifactFormat(ObjectFormat, mlrun.common.types.StrEnum):
41
41
  "spec.metrics",
42
42
  "spec.target_path",
43
43
  "spec.parent_uri",
44
+ "spec.has_children",
44
45
  ]
45
46
  ),
46
47
  }[_format]
@@ -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>{_FQN_PART_PATTERN})\."
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>{_FQN_PART_PATTERN})$"
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
 
@@ -39,6 +39,7 @@ __all__ = [
39
39
  from urllib.parse import urlparse
40
40
 
41
41
  import fsspec
42
+ import storey
42
43
 
43
44
  import mlrun.datastore.wasbfs
44
45
  from mlrun.datastore.datastore_profile import (
@@ -168,11 +169,12 @@ def get_stream_pusher(stream_path: str, **kwargs):
168
169
  raise ValueError(f"unsupported stream path {stream_path}")
169
170
 
170
171
 
171
- class _DummyStream:
172
+ class _DummyStream(storey.MapClass):
172
173
  """stream emulator for tests and debug"""
173
174
 
174
175
  def __init__(self, event_list=None, **kwargs):
175
176
  self.event_list = event_list or []
177
+ super().__init__(**kwargs)
176
178
 
177
179
  def push(self, data, **kwargs):
178
180
  if not isinstance(data, list):
@@ -180,3 +182,9 @@ class _DummyStream:
180
182
  for item in data:
181
183
  logger.info(f"dummy stream got event: {item}, kwargs={kwargs}")
182
184
  self.event_list.append(item)
185
+
186
+ def do(self, event):
187
+ if not isinstance(event, list):
188
+ event = [event]
189
+ for item in event:
190
+ self.event_list.append(item)
@@ -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
- # TODO add hugging face and http
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, parsed_url.netloc, secrets=secrets, **kwargs
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,182 @@
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, Any, Optional, 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
+ from transformers.pipelines.text_generation import ChatType
23
+
24
+
25
+ class HuggingFaceProvider(ModelProvider):
26
+ """
27
+ HuggingFaceProvider is a wrapper around the Hugging Face Transformers pipeline
28
+ that provides an interface for interacting with a wide range of Hugging Face models.
29
+
30
+ It supports synchronous operations, enabling flexible integration into various workflows.
31
+
32
+ This class extends the ModelProvider base class and implements Hugging Face-specific
33
+ functionality, including pipeline initialization, default text generation operations,
34
+ and custom operations tailored to the Hugging Face Transformers pipeline API.
35
+ """
36
+
37
+ def __init__(
38
+ self,
39
+ parent,
40
+ schema,
41
+ name,
42
+ endpoint="",
43
+ secrets: Optional[dict] = None,
44
+ default_invoke_kwargs: Optional[dict] = None,
45
+ ):
46
+ endpoint = endpoint or mlrun.mlconf.model_providers.huggingface_default_model
47
+ if schema != "huggingface":
48
+ raise mlrun.errors.MLRunInvalidArgumentError(
49
+ "HuggingFaceProvider supports only 'huggingface' as the provider kind."
50
+ )
51
+ super().__init__(
52
+ parent=parent,
53
+ kind=schema,
54
+ name=name,
55
+ endpoint=endpoint,
56
+ secrets=secrets,
57
+ default_invoke_kwargs=default_invoke_kwargs,
58
+ )
59
+ self.options = self.get_client_options()
60
+ self._expected_operation_type = None
61
+ self.load_client()
62
+
63
+ @staticmethod
64
+ def _extract_string_output(result) -> str:
65
+ """
66
+ Extracts the first generated string from Hugging Face pipeline output,
67
+ regardless of whether it's plain text-generation or chat-style output.
68
+ """
69
+ if not isinstance(result, list) or len(result) == 0:
70
+ raise ValueError("Empty or invalid pipeline output")
71
+
72
+ return result[0].get("generated_text")
73
+
74
+ @classmethod
75
+ def parse_endpoint_and_path(cls, endpoint, subpath) -> (str, str):
76
+ if endpoint and subpath:
77
+ endpoint = endpoint + subpath
78
+ # In HuggingFace, "/" in a model name is part of the name — `subpath` is not used.
79
+ subpath = ""
80
+ return endpoint, subpath
81
+
82
+ def load_client(self) -> None:
83
+ """
84
+ Initializes the Hugging Face pipeline using the provided options.
85
+
86
+ This method imports the `pipeline` function from the `transformers` package,
87
+ creates a pipeline instance with the specified task and model (from `self.options`),
88
+ and assigns it to `self._client`.
89
+
90
+ Note: Hugging Face pipelines are synchronous and do not support async invocation.
91
+
92
+ Raises:
93
+ ImportError: If the `transformers` package is not installed.
94
+ """
95
+ try:
96
+ from transformers import pipeline, AutoModelForCausalLM # noqa
97
+ from transformers import AutoTokenizer # noqa
98
+ from transformers.pipelines.base import Pipeline # noqa
99
+
100
+ self._client = pipeline(model=self.model, **self.options)
101
+ self._expected_operation_type = Pipeline
102
+ except ImportError as exc:
103
+ raise ImportError("transformers package is not installed") from exc
104
+
105
+ def get_client_options(self):
106
+ res = dict(
107
+ task=self._get_secret_or_env("HF_TASK") or "text-generation",
108
+ token=self._get_secret_or_env("HF_TOKEN"),
109
+ device=self._get_secret_or_env("HF_DEVICE"),
110
+ device_map=self._get_secret_or_env("HF_DEVICE_MAP"),
111
+ trust_remote_code=self._get_secret_or_env("HF_TRUST_REMOTE_CODE"),
112
+ model_kwargs=self._get_secret_or_env("HF_MODEL_KWARGS"),
113
+ )
114
+ return self._sanitize_options(res)
115
+
116
+ def custom_invoke(
117
+ self, operation: Optional["Pipeline"] = None, **invoke_kwargs
118
+ ) -> Union[list, dict, Any]:
119
+ """
120
+ HuggingFace implementation of `ModelProvider.custom_invoke`.
121
+ Use the default config in provider client/ user defined client:
122
+
123
+ Example:
124
+ ```python
125
+ image = Image.open(image_path)
126
+ pipeline_object = pipeline("image-classification", model="microsoft/resnet-50")
127
+ result = hf_provider.custom_invoke(
128
+ pipeline_object,
129
+ inputs=image,
130
+ )
131
+ ```
132
+
133
+
134
+ :param operation: A pipeline object
135
+ :param invoke_kwargs: Keyword arguments to pass to the operation.
136
+ :return: The full response returned by the operation.
137
+
138
+ """
139
+ invoke_kwargs = self.get_invoke_kwargs(invoke_kwargs)
140
+ if operation:
141
+ if not isinstance(operation, self._expected_operation_type):
142
+ raise mlrun.errors.MLRunInvalidArgumentError(
143
+ "Huggingface operation must inherit" " from 'Pipeline' object"
144
+ )
145
+ return operation(**invoke_kwargs)
146
+ else:
147
+ return self.client(**invoke_kwargs)
148
+
149
+ def invoke(
150
+ self,
151
+ messages: Union[str, list[str], "ChatType", list["ChatType"]] = None,
152
+ as_str: bool = False,
153
+ **invoke_kwargs,
154
+ ) -> Union[str, list]:
155
+ """
156
+ HuggingFace-specific implementation of `ModelProvider.invoke`.
157
+ Invokes a HuggingFace model operation using the synchronous client.
158
+ For complete usage details, refer to `ModelProvider.invoke`.
159
+
160
+ :param messages:
161
+ Same as ModelProvider.invoke.
162
+
163
+ :param as_str:
164
+ If `True`, return only the main content (e.g., generated text) from a
165
+ **single-response output** — intended for use cases where you expect exactly one result.
166
+
167
+ If `False`, return the **full raw response object**, which is a list of dictionaries.
168
+
169
+ :param invoke_kwargs:
170
+ Same as ModelProvider.invoke.
171
+ :return: Same as ModelProvider.invoke.
172
+ """
173
+ if self.client.task != "text-generation":
174
+ raise mlrun.errors.MLRunInvalidArgumentError(
175
+ "HuggingFaceProvider.invoke supports text-generation task only"
176
+ )
177
+ if as_str:
178
+ invoke_kwargs["return_full_text"] = False
179
+ response = self.custom_invoke(text_inputs=messages, **invoke_kwargs)
180
+ if as_str:
181
+ return self._extract_string_output(response)
182
+ return response
@@ -12,15 +12,13 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
  from collections.abc import Awaitable
15
- from typing import Any, Callable, Optional, TypeVar, Union
15
+ from typing import Any, Callable, Optional, Union
16
16
 
17
17
  import mlrun.errors
18
18
  from mlrun.datastore.remote_client import (
19
19
  BaseRemoteClient,
20
20
  )
21
21
 
22
- T = TypeVar("T")
23
-
24
22
 
25
23
  class ModelProvider(BaseRemoteClient):
26
24
  """
@@ -79,12 +77,66 @@ class ModelProvider(BaseRemoteClient):
79
77
 
80
78
  raise NotImplementedError("load_client method is not implemented")
81
79
 
80
+ @property
81
+ def client(self) -> Any:
82
+ return self._client
83
+
84
+ @property
85
+ def model(self) -> Optional[str]:
86
+ """
87
+ Returns the model identifier used by the underlying SDK.
88
+
89
+ :return: A string representing the model ID, or None if not set.
90
+ """
91
+ return self.endpoint
92
+
93
+ def get_invoke_kwargs(self, invoke_kwargs) -> dict:
94
+ kwargs = self.default_invoke_kwargs.copy()
95
+ kwargs.update(invoke_kwargs)
96
+ return kwargs
97
+
98
+ @property
99
+ def async_client(self) -> Any:
100
+ if not self.support_async:
101
+ raise mlrun.errors.MLRunInvalidArgumentError(
102
+ f"{self.__class__.__name__} does not support async operations"
103
+ )
104
+ return self._async_client
105
+
106
+ def custom_invoke(self, operation: Optional[Callable], **invoke_kwargs) -> Any:
107
+ """
108
+ Invokes a model operation from a provider (e.g., OpenAI, Hugging Face, etc.) with the given keyword arguments.
109
+
110
+ Useful for dynamically calling model methods like text generation, chat completions, or image generation.
111
+ The operation must be a callable that accepts keyword arguments.
112
+
113
+ :param operation: A callable representing the model operation (e.g., a client method).
114
+ :param invoke_kwargs: Keyword arguments to pass to the operation.
115
+ :return: The full response returned by the operation.
116
+ """
117
+ raise NotImplementedError("custom_invoke method is not implemented")
118
+
119
+ async def async_custom_invoke(
120
+ self, operation: Optional[Callable[..., Awaitable[Any]]], **invoke_kwargs
121
+ ) -> Any:
122
+ """
123
+ Asynchronously invokes a model operation from a provider (e.g., OpenAI, Hugging Face, etc.)
124
+ with the given keyword arguments.
125
+
126
+ The operation must be an async callable (e.g., a method from an async client) that accepts keyword arguments.
127
+
128
+ :param operation: An async callable representing the model operation (e.g., an async_client method).
129
+ :param invoke_kwargs: Keyword arguments to pass to the operation.
130
+ :return: The full response returned by the awaited operation.
131
+ """
132
+ raise NotImplementedError("async_custom_invoke is not implemented")
133
+
82
134
  def invoke(
83
135
  self,
84
136
  messages: Optional[list[dict]] = None,
85
137
  as_str: bool = False,
86
138
  **invoke_kwargs,
87
- ) -> Optional[Union[str, T]]:
139
+ ) -> Union[str, Any]:
88
140
  """
89
141
  Invokes a generative AI model with the provided messages and additional parameters.
90
142
  This method is designed to be a flexible interface for interacting with various
@@ -127,62 +179,11 @@ class ModelProvider(BaseRemoteClient):
127
179
  """
128
180
  raise NotImplementedError("invoke method is not implemented")
129
181
 
130
- def custom_invoke(
131
- self, operation: Optional[Callable[..., T]] = None, **invoke_kwargs
132
- ) -> Optional[T]:
133
- """
134
- Invokes a model operation from a provider (e.g., OpenAI, Hugging Face, etc.) with the given keyword arguments.
135
-
136
- Useful for dynamically calling model methods like text generation, chat completions, or image generation.
137
- The operation must be a callable that accepts keyword arguments.
138
-
139
- :param operation: A callable representing the model operation (e.g., a client method).
140
- :param invoke_kwargs: Keyword arguments to pass to the operation.
141
- :return: The full response returned by the operation.
142
- """
143
- raise NotImplementedError("custom_invoke method is not implemented")
144
-
145
- @property
146
- def client(self) -> Any:
147
- return self._client
148
-
149
- @property
150
- def model(self) -> Optional[str]:
151
- return None
152
-
153
- def get_invoke_kwargs(self, invoke_kwargs) -> dict:
154
- kwargs = self.default_invoke_kwargs.copy()
155
- kwargs.update(invoke_kwargs)
156
- return kwargs
157
-
158
- @property
159
- def async_client(self) -> Any:
160
- if not self.support_async:
161
- raise mlrun.errors.MLRunInvalidArgumentError(
162
- f"{self.__class__.__name__} does not support async operations"
163
- )
164
- return self._async_client
165
-
166
- async def async_custom_invoke(
167
- self, operation: Optional[Callable[..., Awaitable[T]]], **invoke_kwargs
168
- ) -> Optional[T]:
169
- """
170
- Asynchronously invokes a model operation from a provider (e.g., OpenAI, Hugging Face, etc.)
171
- with the given keyword arguments.
172
-
173
- The operation must be an async callable (e.g., a method from an async client) that accepts keyword arguments.
174
-
175
- :param operation: An async callable representing the model operation (e.g., an async_client method).
176
- :param invoke_kwargs: Keyword arguments to pass to the operation.
177
- :return: The full response returned by the awaited operation.
178
- """
179
- raise NotImplementedError("async_custom_invoke is not implemented")
180
-
181
182
  async def async_invoke(
182
183
  self,
183
184
  messages: Optional[list[dict]] = None,
184
185
  as_str: bool = False,
185
186
  **invoke_kwargs,
186
- ) -> Optional[str]:
187
+ ) -> Union[str, Any]:
187
188
  """Async version of `invoke`. See `invoke` for full documentation."""
188
189
  raise NotImplementedError("async_invoke is not implemented")