huggingface-hub 0.29.0rc2__py3-none-any.whl → 1.1.3__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.
- huggingface_hub/__init__.py +160 -46
- huggingface_hub/_commit_api.py +277 -71
- huggingface_hub/_commit_scheduler.py +15 -15
- huggingface_hub/_inference_endpoints.py +33 -22
- huggingface_hub/_jobs_api.py +301 -0
- huggingface_hub/_local_folder.py +18 -3
- huggingface_hub/_login.py +31 -63
- huggingface_hub/_oauth.py +460 -0
- huggingface_hub/_snapshot_download.py +241 -81
- huggingface_hub/_space_api.py +18 -10
- huggingface_hub/_tensorboard_logger.py +15 -19
- huggingface_hub/_upload_large_folder.py +196 -76
- huggingface_hub/_webhooks_payload.py +3 -3
- huggingface_hub/_webhooks_server.py +15 -25
- huggingface_hub/{commands → cli}/__init__.py +1 -15
- huggingface_hub/cli/_cli_utils.py +173 -0
- huggingface_hub/cli/auth.py +147 -0
- huggingface_hub/cli/cache.py +841 -0
- huggingface_hub/cli/download.py +189 -0
- huggingface_hub/cli/hf.py +60 -0
- huggingface_hub/cli/inference_endpoints.py +377 -0
- huggingface_hub/cli/jobs.py +772 -0
- huggingface_hub/cli/lfs.py +175 -0
- huggingface_hub/cli/repo.py +315 -0
- huggingface_hub/cli/repo_files.py +94 -0
- huggingface_hub/{commands/env.py → cli/system.py} +10 -13
- huggingface_hub/cli/upload.py +294 -0
- huggingface_hub/cli/upload_large_folder.py +117 -0
- huggingface_hub/community.py +20 -12
- huggingface_hub/constants.py +83 -59
- huggingface_hub/dataclasses.py +609 -0
- huggingface_hub/errors.py +99 -30
- huggingface_hub/fastai_utils.py +30 -41
- huggingface_hub/file_download.py +606 -346
- huggingface_hub/hf_api.py +2445 -1132
- huggingface_hub/hf_file_system.py +269 -152
- huggingface_hub/hub_mixin.py +61 -66
- huggingface_hub/inference/_client.py +501 -630
- huggingface_hub/inference/_common.py +133 -121
- huggingface_hub/inference/_generated/_async_client.py +536 -722
- huggingface_hub/inference/_generated/types/__init__.py +6 -1
- huggingface_hub/inference/_generated/types/automatic_speech_recognition.py +5 -6
- huggingface_hub/inference/_generated/types/base.py +10 -7
- huggingface_hub/inference/_generated/types/chat_completion.py +77 -31
- huggingface_hub/inference/_generated/types/depth_estimation.py +2 -2
- huggingface_hub/inference/_generated/types/document_question_answering.py +2 -2
- huggingface_hub/inference/_generated/types/feature_extraction.py +2 -2
- huggingface_hub/inference/_generated/types/fill_mask.py +2 -2
- huggingface_hub/inference/_generated/types/image_to_image.py +8 -2
- huggingface_hub/inference/_generated/types/image_to_text.py +2 -3
- huggingface_hub/inference/_generated/types/image_to_video.py +60 -0
- huggingface_hub/inference/_generated/types/sentence_similarity.py +3 -3
- huggingface_hub/inference/_generated/types/summarization.py +2 -2
- huggingface_hub/inference/_generated/types/table_question_answering.py +5 -5
- huggingface_hub/inference/_generated/types/text2text_generation.py +2 -2
- huggingface_hub/inference/_generated/types/text_generation.py +11 -11
- huggingface_hub/inference/_generated/types/text_to_audio.py +1 -2
- huggingface_hub/inference/_generated/types/text_to_speech.py +1 -2
- huggingface_hub/inference/_generated/types/text_to_video.py +2 -2
- huggingface_hub/inference/_generated/types/token_classification.py +2 -2
- huggingface_hub/inference/_generated/types/translation.py +2 -2
- huggingface_hub/inference/_generated/types/zero_shot_classification.py +2 -2
- huggingface_hub/inference/_generated/types/zero_shot_image_classification.py +2 -2
- huggingface_hub/inference/_generated/types/zero_shot_object_detection.py +1 -3
- huggingface_hub/inference/_mcp/__init__.py +0 -0
- huggingface_hub/inference/_mcp/_cli_hacks.py +88 -0
- huggingface_hub/inference/_mcp/agent.py +100 -0
- huggingface_hub/inference/_mcp/cli.py +247 -0
- huggingface_hub/inference/_mcp/constants.py +81 -0
- huggingface_hub/inference/_mcp/mcp_client.py +395 -0
- huggingface_hub/inference/_mcp/types.py +45 -0
- huggingface_hub/inference/_mcp/utils.py +128 -0
- huggingface_hub/inference/_providers/__init__.py +149 -20
- huggingface_hub/inference/_providers/_common.py +160 -37
- huggingface_hub/inference/_providers/black_forest_labs.py +12 -9
- huggingface_hub/inference/_providers/cerebras.py +6 -0
- huggingface_hub/inference/_providers/clarifai.py +13 -0
- huggingface_hub/inference/_providers/cohere.py +32 -0
- huggingface_hub/inference/_providers/fal_ai.py +231 -22
- huggingface_hub/inference/_providers/featherless_ai.py +38 -0
- huggingface_hub/inference/_providers/fireworks_ai.py +22 -1
- huggingface_hub/inference/_providers/groq.py +9 -0
- huggingface_hub/inference/_providers/hf_inference.py +143 -33
- huggingface_hub/inference/_providers/hyperbolic.py +9 -5
- huggingface_hub/inference/_providers/nebius.py +47 -5
- huggingface_hub/inference/_providers/novita.py +48 -5
- huggingface_hub/inference/_providers/nscale.py +44 -0
- huggingface_hub/inference/_providers/openai.py +25 -0
- huggingface_hub/inference/_providers/publicai.py +6 -0
- huggingface_hub/inference/_providers/replicate.py +46 -9
- huggingface_hub/inference/_providers/sambanova.py +37 -1
- huggingface_hub/inference/_providers/scaleway.py +28 -0
- huggingface_hub/inference/_providers/together.py +34 -5
- huggingface_hub/inference/_providers/wavespeed.py +138 -0
- huggingface_hub/inference/_providers/zai_org.py +17 -0
- huggingface_hub/lfs.py +33 -100
- huggingface_hub/repocard.py +34 -38
- huggingface_hub/repocard_data.py +79 -59
- huggingface_hub/serialization/__init__.py +0 -1
- huggingface_hub/serialization/_base.py +12 -15
- huggingface_hub/serialization/_dduf.py +8 -8
- huggingface_hub/serialization/_torch.py +69 -69
- huggingface_hub/utils/__init__.py +27 -8
- huggingface_hub/utils/_auth.py +7 -7
- huggingface_hub/utils/_cache_manager.py +92 -147
- huggingface_hub/utils/_chunk_utils.py +2 -3
- huggingface_hub/utils/_deprecation.py +1 -1
- huggingface_hub/utils/_dotenv.py +55 -0
- huggingface_hub/utils/_experimental.py +7 -5
- huggingface_hub/utils/_fixes.py +0 -10
- huggingface_hub/utils/_git_credential.py +5 -5
- huggingface_hub/utils/_headers.py +8 -30
- huggingface_hub/utils/_http.py +399 -237
- huggingface_hub/utils/_pagination.py +6 -6
- huggingface_hub/utils/_parsing.py +98 -0
- huggingface_hub/utils/_paths.py +5 -5
- huggingface_hub/utils/_runtime.py +74 -22
- huggingface_hub/utils/_safetensors.py +21 -21
- huggingface_hub/utils/_subprocess.py +13 -11
- huggingface_hub/utils/_telemetry.py +4 -4
- huggingface_hub/{commands/_cli_utils.py → utils/_terminal.py} +4 -4
- huggingface_hub/utils/_typing.py +25 -5
- huggingface_hub/utils/_validators.py +55 -74
- huggingface_hub/utils/_verification.py +167 -0
- huggingface_hub/utils/_xet.py +235 -0
- huggingface_hub/utils/_xet_progress_reporting.py +162 -0
- huggingface_hub/utils/insecure_hashlib.py +3 -5
- huggingface_hub/utils/logging.py +8 -11
- huggingface_hub/utils/tqdm.py +33 -4
- {huggingface_hub-0.29.0rc2.dist-info → huggingface_hub-1.1.3.dist-info}/METADATA +94 -82
- huggingface_hub-1.1.3.dist-info/RECORD +155 -0
- {huggingface_hub-0.29.0rc2.dist-info → huggingface_hub-1.1.3.dist-info}/WHEEL +1 -1
- huggingface_hub-1.1.3.dist-info/entry_points.txt +6 -0
- huggingface_hub/commands/delete_cache.py +0 -428
- huggingface_hub/commands/download.py +0 -200
- huggingface_hub/commands/huggingface_cli.py +0 -61
- huggingface_hub/commands/lfs.py +0 -200
- huggingface_hub/commands/repo_files.py +0 -128
- huggingface_hub/commands/scan_cache.py +0 -181
- huggingface_hub/commands/tag.py +0 -159
- huggingface_hub/commands/upload.py +0 -299
- huggingface_hub/commands/upload_large_folder.py +0 -129
- huggingface_hub/commands/user.py +0 -304
- huggingface_hub/commands/version.py +0 -37
- huggingface_hub/inference_api.py +0 -217
- huggingface_hub/keras_mixin.py +0 -500
- huggingface_hub/repository.py +0 -1477
- huggingface_hub/serialization/_tensorflow.py +0 -95
- huggingface_hub/utils/_hf_folder.py +0 -68
- huggingface_hub-0.29.0rc2.dist-info/RECORD +0 -131
- huggingface_hub-0.29.0rc2.dist-info/entry_points.txt +0 -6
- {huggingface_hub-0.29.0rc2.dist-info → huggingface_hub-1.1.3.dist-info/licenses}/LICENSE +0 -0
- {huggingface_hub-0.29.0rc2.dist-info → huggingface_hub-1.1.3.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from typing import Any, Optional
|
|
2
|
+
|
|
3
|
+
from huggingface_hub.hf_api import InferenceProviderMapping
|
|
4
|
+
|
|
5
|
+
from ._common import BaseConversationalTask
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
_PROVIDER = "cohere"
|
|
9
|
+
_BASE_URL = "https://api.cohere.com"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CohereConversationalTask(BaseConversationalTask):
|
|
13
|
+
def __init__(self):
|
|
14
|
+
super().__init__(provider=_PROVIDER, base_url=_BASE_URL)
|
|
15
|
+
|
|
16
|
+
def _prepare_route(self, mapped_model: str, api_key: str) -> str:
|
|
17
|
+
return "/compatibility/v1/chat/completions"
|
|
18
|
+
|
|
19
|
+
def _prepare_payload_as_dict(
|
|
20
|
+
self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
|
|
21
|
+
) -> Optional[dict]:
|
|
22
|
+
payload = super()._prepare_payload_as_dict(inputs, parameters, provider_mapping_info)
|
|
23
|
+
response_format = parameters.get("response_format")
|
|
24
|
+
if isinstance(response_format, dict) and response_format.get("type") == "json_schema":
|
|
25
|
+
json_schema_details = response_format.get("json_schema")
|
|
26
|
+
if isinstance(json_schema_details, dict) and "schema" in json_schema_details:
|
|
27
|
+
payload["response_format"] = { # type: ignore [index]
|
|
28
|
+
"type": "json_object",
|
|
29
|
+
"schema": json_schema_details["schema"],
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return payload
|
|
@@ -1,31 +1,98 @@
|
|
|
1
1
|
import base64
|
|
2
|
+
import time
|
|
2
3
|
from abc import ABC
|
|
3
|
-
from typing import Any,
|
|
4
|
+
from typing import Any, Optional, Union
|
|
5
|
+
from urllib.parse import urlparse
|
|
4
6
|
|
|
5
|
-
from huggingface_hub
|
|
7
|
+
from huggingface_hub import constants
|
|
8
|
+
from huggingface_hub.hf_api import InferenceProviderMapping
|
|
9
|
+
from huggingface_hub.inference._common import RequestParameters, _as_dict, _as_url
|
|
6
10
|
from huggingface_hub.inference._providers._common import TaskProviderHelper, filter_none
|
|
7
|
-
from huggingface_hub.utils import get_session
|
|
11
|
+
from huggingface_hub.utils import get_session, hf_raise_for_status
|
|
12
|
+
from huggingface_hub.utils.logging import get_logger
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
logger = get_logger(__name__)
|
|
16
|
+
|
|
17
|
+
# Arbitrary polling interval
|
|
18
|
+
_POLLING_INTERVAL = 0.5
|
|
8
19
|
|
|
9
20
|
|
|
10
21
|
class FalAITask(TaskProviderHelper, ABC):
|
|
11
22
|
def __init__(self, task: str):
|
|
12
23
|
super().__init__(provider="fal-ai", base_url="https://fal.run", task=task)
|
|
13
24
|
|
|
14
|
-
def _prepare_headers(self, headers:
|
|
25
|
+
def _prepare_headers(self, headers: dict, api_key: str) -> dict[str, Any]:
|
|
15
26
|
headers = super()._prepare_headers(headers, api_key)
|
|
16
27
|
if not api_key.startswith("hf_"):
|
|
17
28
|
headers["authorization"] = f"Key {api_key}"
|
|
18
29
|
return headers
|
|
19
30
|
|
|
20
|
-
def _prepare_route(self, mapped_model: str) -> str:
|
|
31
|
+
def _prepare_route(self, mapped_model: str, api_key: str) -> str:
|
|
21
32
|
return f"/{mapped_model}"
|
|
22
33
|
|
|
23
34
|
|
|
35
|
+
class FalAIQueueTask(TaskProviderHelper, ABC):
|
|
36
|
+
def __init__(self, task: str):
|
|
37
|
+
super().__init__(provider="fal-ai", base_url="https://queue.fal.run", task=task)
|
|
38
|
+
|
|
39
|
+
def _prepare_headers(self, headers: dict, api_key: str) -> dict[str, Any]:
|
|
40
|
+
headers = super()._prepare_headers(headers, api_key)
|
|
41
|
+
if not api_key.startswith("hf_"):
|
|
42
|
+
headers["authorization"] = f"Key {api_key}"
|
|
43
|
+
return headers
|
|
44
|
+
|
|
45
|
+
def _prepare_route(self, mapped_model: str, api_key: str) -> str:
|
|
46
|
+
if api_key.startswith("hf_"):
|
|
47
|
+
# Use the queue subdomain for HF routing
|
|
48
|
+
return f"/{mapped_model}?_subdomain=queue"
|
|
49
|
+
return f"/{mapped_model}"
|
|
50
|
+
|
|
51
|
+
def get_response(
|
|
52
|
+
self,
|
|
53
|
+
response: Union[bytes, dict],
|
|
54
|
+
request_params: Optional[RequestParameters] = None,
|
|
55
|
+
) -> Any:
|
|
56
|
+
response_dict = _as_dict(response)
|
|
57
|
+
|
|
58
|
+
request_id = response_dict.get("request_id")
|
|
59
|
+
if not request_id:
|
|
60
|
+
raise ValueError("No request ID found in the response")
|
|
61
|
+
if request_params is None:
|
|
62
|
+
raise ValueError(
|
|
63
|
+
f"A `RequestParameters` object should be provided to get {self.task} responses with Fal AI."
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# extract the base url and query params
|
|
67
|
+
parsed_url = urlparse(request_params.url)
|
|
68
|
+
# a bit hacky way to concatenate the provider name without parsing `parsed_url.path`
|
|
69
|
+
base_url = f"{parsed_url.scheme}://{parsed_url.netloc}{'/fal-ai' if parsed_url.netloc == 'router.huggingface.co' else ''}"
|
|
70
|
+
query_param = f"?{parsed_url.query}" if parsed_url.query else ""
|
|
71
|
+
|
|
72
|
+
# extracting the provider model id for status and result urls
|
|
73
|
+
# from the response as it might be different from the mapped model in `request_params.url`
|
|
74
|
+
model_id = urlparse(response_dict.get("response_url")).path
|
|
75
|
+
status_url = f"{base_url}{str(model_id)}/status{query_param}"
|
|
76
|
+
result_url = f"{base_url}{str(model_id)}{query_param}"
|
|
77
|
+
|
|
78
|
+
status = response_dict.get("status")
|
|
79
|
+
logger.info("Generating the output.. this can take several minutes.")
|
|
80
|
+
while status != "COMPLETED":
|
|
81
|
+
time.sleep(_POLLING_INTERVAL)
|
|
82
|
+
status_response = get_session().get(status_url, headers=request_params.headers)
|
|
83
|
+
hf_raise_for_status(status_response)
|
|
84
|
+
status = status_response.json().get("status")
|
|
85
|
+
|
|
86
|
+
return get_session().get(result_url, headers=request_params.headers).json()
|
|
87
|
+
|
|
88
|
+
|
|
24
89
|
class FalAIAutomaticSpeechRecognitionTask(FalAITask):
|
|
25
90
|
def __init__(self):
|
|
26
91
|
super().__init__("automatic-speech-recognition")
|
|
27
92
|
|
|
28
|
-
def _prepare_payload_as_dict(
|
|
93
|
+
def _prepare_payload_as_dict(
|
|
94
|
+
self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
|
|
95
|
+
) -> Optional[dict]:
|
|
29
96
|
if isinstance(inputs, str) and inputs.startswith(("http://", "https://")):
|
|
30
97
|
# If input is a URL, pass it directly
|
|
31
98
|
audio_url = inputs
|
|
@@ -41,7 +108,7 @@ class FalAIAutomaticSpeechRecognitionTask(FalAITask):
|
|
|
41
108
|
|
|
42
109
|
return {"audio_url": audio_url, **filter_none(parameters)}
|
|
43
110
|
|
|
44
|
-
def get_response(self, response: Union[bytes,
|
|
111
|
+
def get_response(self, response: Union[bytes, dict], request_params: Optional[RequestParameters] = None) -> Any:
|
|
45
112
|
text = _as_dict(response)["text"]
|
|
46
113
|
if not isinstance(text, str):
|
|
47
114
|
raise ValueError(f"Unexpected output format from FalAI API. Expected string, got {type(text)}.")
|
|
@@ -52,16 +119,33 @@ class FalAITextToImageTask(FalAITask):
|
|
|
52
119
|
def __init__(self):
|
|
53
120
|
super().__init__("text-to-image")
|
|
54
121
|
|
|
55
|
-
def _prepare_payload_as_dict(
|
|
56
|
-
parameters
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
122
|
+
def _prepare_payload_as_dict(
|
|
123
|
+
self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
|
|
124
|
+
) -> Optional[dict]:
|
|
125
|
+
payload: dict[str, Any] = {
|
|
126
|
+
"prompt": inputs,
|
|
127
|
+
**filter_none(parameters),
|
|
128
|
+
}
|
|
129
|
+
if "width" in payload and "height" in payload:
|
|
130
|
+
payload["image_size"] = {
|
|
131
|
+
"width": payload.pop("width"),
|
|
132
|
+
"height": payload.pop("height"),
|
|
61
133
|
}
|
|
62
|
-
|
|
134
|
+
if provider_mapping_info.adapter_weights_path is not None:
|
|
135
|
+
lora_path = constants.HUGGINGFACE_CO_URL_TEMPLATE.format(
|
|
136
|
+
repo_id=provider_mapping_info.hf_model_id,
|
|
137
|
+
revision="main",
|
|
138
|
+
filename=provider_mapping_info.adapter_weights_path,
|
|
139
|
+
)
|
|
140
|
+
payload["loras"] = [{"path": lora_path, "scale": 1}]
|
|
141
|
+
if provider_mapping_info.provider_id == "fal-ai/lora":
|
|
142
|
+
# little hack: fal requires the base model for stable-diffusion-based loras but not for flux-based
|
|
143
|
+
# See payloads in https://fal.ai/models/fal-ai/lora/api vs https://fal.ai/models/fal-ai/flux-lora/api
|
|
144
|
+
payload["model_name"] = "stabilityai/stable-diffusion-xl-base-1.0"
|
|
145
|
+
|
|
146
|
+
return payload
|
|
63
147
|
|
|
64
|
-
def get_response(self, response: Union[bytes,
|
|
148
|
+
def get_response(self, response: Union[bytes, dict], request_params: Optional[RequestParameters] = None) -> Any:
|
|
65
149
|
url = _as_dict(response)["images"][0]["url"]
|
|
66
150
|
return get_session().get(url).content
|
|
67
151
|
|
|
@@ -70,21 +154,146 @@ class FalAITextToSpeechTask(FalAITask):
|
|
|
70
154
|
def __init__(self):
|
|
71
155
|
super().__init__("text-to-speech")
|
|
72
156
|
|
|
73
|
-
def _prepare_payload_as_dict(
|
|
74
|
-
|
|
157
|
+
def _prepare_payload_as_dict(
|
|
158
|
+
self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
|
|
159
|
+
) -> Optional[dict]:
|
|
160
|
+
return {"text": inputs, **filter_none(parameters)}
|
|
75
161
|
|
|
76
|
-
def get_response(self, response: Union[bytes,
|
|
162
|
+
def get_response(self, response: Union[bytes, dict], request_params: Optional[RequestParameters] = None) -> Any:
|
|
77
163
|
url = _as_dict(response)["audio"]["url"]
|
|
78
164
|
return get_session().get(url).content
|
|
79
165
|
|
|
80
166
|
|
|
81
|
-
class FalAITextToVideoTask(
|
|
167
|
+
class FalAITextToVideoTask(FalAIQueueTask):
|
|
82
168
|
def __init__(self):
|
|
83
169
|
super().__init__("text-to-video")
|
|
84
170
|
|
|
85
|
-
def _prepare_payload_as_dict(
|
|
171
|
+
def _prepare_payload_as_dict(
|
|
172
|
+
self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
|
|
173
|
+
) -> Optional[dict]:
|
|
86
174
|
return {"prompt": inputs, **filter_none(parameters)}
|
|
87
175
|
|
|
88
|
-
def get_response(
|
|
89
|
-
|
|
176
|
+
def get_response(
|
|
177
|
+
self,
|
|
178
|
+
response: Union[bytes, dict],
|
|
179
|
+
request_params: Optional[RequestParameters] = None,
|
|
180
|
+
) -> Any:
|
|
181
|
+
output = super().get_response(response, request_params)
|
|
182
|
+
url = _as_dict(output)["video"]["url"]
|
|
90
183
|
return get_session().get(url).content
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class FalAIImageToImageTask(FalAIQueueTask):
|
|
187
|
+
def __init__(self):
|
|
188
|
+
super().__init__("image-to-image")
|
|
189
|
+
|
|
190
|
+
def _prepare_payload_as_dict(
|
|
191
|
+
self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
|
|
192
|
+
) -> Optional[dict]:
|
|
193
|
+
image_url = _as_url(inputs, default_mime_type="image/jpeg")
|
|
194
|
+
if "target_size" in parameters:
|
|
195
|
+
parameters["image_size"] = parameters.pop("target_size")
|
|
196
|
+
payload: dict[str, Any] = {
|
|
197
|
+
"image_url": image_url,
|
|
198
|
+
**filter_none(parameters),
|
|
199
|
+
}
|
|
200
|
+
if provider_mapping_info.adapter_weights_path is not None:
|
|
201
|
+
lora_path = constants.HUGGINGFACE_CO_URL_TEMPLATE.format(
|
|
202
|
+
repo_id=provider_mapping_info.hf_model_id,
|
|
203
|
+
revision="main",
|
|
204
|
+
filename=provider_mapping_info.adapter_weights_path,
|
|
205
|
+
)
|
|
206
|
+
payload["loras"] = [{"path": lora_path, "scale": 1}]
|
|
207
|
+
|
|
208
|
+
return payload
|
|
209
|
+
|
|
210
|
+
def get_response(
|
|
211
|
+
self,
|
|
212
|
+
response: Union[bytes, dict],
|
|
213
|
+
request_params: Optional[RequestParameters] = None,
|
|
214
|
+
) -> Any:
|
|
215
|
+
output = super().get_response(response, request_params)
|
|
216
|
+
url = _as_dict(output)["images"][0]["url"]
|
|
217
|
+
return get_session().get(url).content
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class FalAIImageToVideoTask(FalAIQueueTask):
|
|
221
|
+
def __init__(self):
|
|
222
|
+
super().__init__("image-to-video")
|
|
223
|
+
|
|
224
|
+
def _prepare_payload_as_dict(
|
|
225
|
+
self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
|
|
226
|
+
) -> Optional[dict]:
|
|
227
|
+
image_url = _as_url(inputs, default_mime_type="image/jpeg")
|
|
228
|
+
payload: dict[str, Any] = {
|
|
229
|
+
"image_url": image_url,
|
|
230
|
+
**filter_none(parameters),
|
|
231
|
+
}
|
|
232
|
+
if provider_mapping_info.adapter_weights_path is not None:
|
|
233
|
+
lora_path = constants.HUGGINGFACE_CO_URL_TEMPLATE.format(
|
|
234
|
+
repo_id=provider_mapping_info.hf_model_id,
|
|
235
|
+
revision="main",
|
|
236
|
+
filename=provider_mapping_info.adapter_weights_path,
|
|
237
|
+
)
|
|
238
|
+
payload["loras"] = [{"path": lora_path, "scale": 1}]
|
|
239
|
+
return payload
|
|
240
|
+
|
|
241
|
+
def get_response(
|
|
242
|
+
self,
|
|
243
|
+
response: Union[bytes, dict],
|
|
244
|
+
request_params: Optional[RequestParameters] = None,
|
|
245
|
+
) -> Any:
|
|
246
|
+
output = super().get_response(response, request_params)
|
|
247
|
+
url = _as_dict(output)["video"]["url"]
|
|
248
|
+
return get_session().get(url).content
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class FalAIImageSegmentationTask(FalAIQueueTask):
|
|
252
|
+
def __init__(self):
|
|
253
|
+
super().__init__("image-segmentation")
|
|
254
|
+
|
|
255
|
+
def _prepare_payload_as_dict(
|
|
256
|
+
self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
|
|
257
|
+
) -> Optional[dict]:
|
|
258
|
+
image_url = _as_url(inputs, default_mime_type="image/png")
|
|
259
|
+
payload: dict[str, Any] = {
|
|
260
|
+
"image_url": image_url,
|
|
261
|
+
**filter_none(parameters),
|
|
262
|
+
"sync_mode": True,
|
|
263
|
+
}
|
|
264
|
+
return payload
|
|
265
|
+
|
|
266
|
+
def get_response(
|
|
267
|
+
self,
|
|
268
|
+
response: Union[bytes, dict],
|
|
269
|
+
request_params: Optional[RequestParameters] = None,
|
|
270
|
+
) -> Any:
|
|
271
|
+
result = super().get_response(response, request_params)
|
|
272
|
+
result_dict = _as_dict(result)
|
|
273
|
+
|
|
274
|
+
if "image" not in result_dict:
|
|
275
|
+
raise ValueError(f"Response from fal ai image-segmentation API does not contain an image: {result_dict}")
|
|
276
|
+
|
|
277
|
+
image_data = result_dict["image"]
|
|
278
|
+
if "url" not in image_data:
|
|
279
|
+
raise ValueError(f"Image data from fal ai image-segmentation API does not contain a URL: {image_data}")
|
|
280
|
+
|
|
281
|
+
image_url = image_data["url"]
|
|
282
|
+
|
|
283
|
+
if isinstance(image_url, str) and image_url.startswith("data:"):
|
|
284
|
+
if "," in image_url:
|
|
285
|
+
mask_base64 = image_url.split(",", 1)[1]
|
|
286
|
+
else:
|
|
287
|
+
raise ValueError(f"Invalid data URL format: {image_url}")
|
|
288
|
+
else:
|
|
289
|
+
# or it's a regular URL, fetch it
|
|
290
|
+
mask_response = get_session().get(image_url)
|
|
291
|
+
hf_raise_for_status(mask_response)
|
|
292
|
+
mask_base64 = base64.b64encode(mask_response.content).decode()
|
|
293
|
+
|
|
294
|
+
return [
|
|
295
|
+
{
|
|
296
|
+
"label": "mask",
|
|
297
|
+
"mask": mask_base64,
|
|
298
|
+
}
|
|
299
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from typing import Any, Optional, Union
|
|
2
|
+
|
|
3
|
+
from huggingface_hub.hf_api import InferenceProviderMapping
|
|
4
|
+
from huggingface_hub.inference._common import RequestParameters, _as_dict
|
|
5
|
+
|
|
6
|
+
from ._common import BaseConversationalTask, BaseTextGenerationTask, filter_none
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
_PROVIDER = "featherless-ai"
|
|
10
|
+
_BASE_URL = "https://api.featherless.ai"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class FeatherlessTextGenerationTask(BaseTextGenerationTask):
|
|
14
|
+
def __init__(self):
|
|
15
|
+
super().__init__(provider=_PROVIDER, base_url=_BASE_URL)
|
|
16
|
+
|
|
17
|
+
def _prepare_payload_as_dict(
|
|
18
|
+
self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
|
|
19
|
+
) -> Optional[dict]:
|
|
20
|
+
params = filter_none(parameters.copy())
|
|
21
|
+
params["max_tokens"] = params.pop("max_new_tokens", None)
|
|
22
|
+
|
|
23
|
+
return {"prompt": inputs, **params, "model": provider_mapping_info.provider_id}
|
|
24
|
+
|
|
25
|
+
def get_response(self, response: Union[bytes, dict], request_params: Optional[RequestParameters] = None) -> Any:
|
|
26
|
+
output = _as_dict(response)["choices"][0]
|
|
27
|
+
return {
|
|
28
|
+
"generated_text": output["text"],
|
|
29
|
+
"details": {
|
|
30
|
+
"finish_reason": output.get("finish_reason"),
|
|
31
|
+
"seed": output.get("seed"),
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class FeatherlessConversationalTask(BaseConversationalTask):
|
|
37
|
+
def __init__(self):
|
|
38
|
+
super().__init__(provider=_PROVIDER, base_url=_BASE_URL)
|
|
@@ -1,6 +1,27 @@
|
|
|
1
|
+
from typing import Any, Optional
|
|
2
|
+
|
|
3
|
+
from huggingface_hub.hf_api import InferenceProviderMapping
|
|
4
|
+
|
|
1
5
|
from ._common import BaseConversationalTask
|
|
2
6
|
|
|
3
7
|
|
|
4
8
|
class FireworksAIConversationalTask(BaseConversationalTask):
|
|
5
9
|
def __init__(self):
|
|
6
|
-
super().__init__(provider="fireworks-ai", base_url="https://api.fireworks.ai
|
|
10
|
+
super().__init__(provider="fireworks-ai", base_url="https://api.fireworks.ai")
|
|
11
|
+
|
|
12
|
+
def _prepare_route(self, mapped_model: str, api_key: str) -> str:
|
|
13
|
+
return "/inference/v1/chat/completions"
|
|
14
|
+
|
|
15
|
+
def _prepare_payload_as_dict(
|
|
16
|
+
self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
|
|
17
|
+
) -> Optional[dict]:
|
|
18
|
+
payload = super()._prepare_payload_as_dict(inputs, parameters, provider_mapping_info)
|
|
19
|
+
response_format = parameters.get("response_format")
|
|
20
|
+
if isinstance(response_format, dict) and response_format.get("type") == "json_schema":
|
|
21
|
+
json_schema_details = response_format.get("json_schema")
|
|
22
|
+
if isinstance(json_schema_details, dict) and "schema" in json_schema_details:
|
|
23
|
+
payload["response_format"] = { # type: ignore [index]
|
|
24
|
+
"type": "json_object",
|
|
25
|
+
"schema": json_schema_details["schema"],
|
|
26
|
+
}
|
|
27
|
+
return payload
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
from ._common import BaseConversationalTask
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class GroqConversationalTask(BaseConversationalTask):
|
|
5
|
+
def __init__(self):
|
|
6
|
+
super().__init__(provider="groq", base_url="https://api.groq.com")
|
|
7
|
+
|
|
8
|
+
def _prepare_route(self, mapped_model: str, api_key: str) -> str:
|
|
9
|
+
return "/openai/v1/chat/completions"
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from functools import lru_cache
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Any,
|
|
4
|
+
from typing import Any, Optional, Union
|
|
5
|
+
from urllib.parse import urlparse, urlunparse
|
|
5
6
|
|
|
6
7
|
from huggingface_hub import constants
|
|
7
|
-
from huggingface_hub.
|
|
8
|
+
from huggingface_hub.hf_api import InferenceProviderMapping
|
|
9
|
+
from huggingface_hub.inference._common import (
|
|
10
|
+
MimeBytes,
|
|
11
|
+
RequestParameters,
|
|
12
|
+
_b64_encode,
|
|
13
|
+
_bytes_to_dict,
|
|
14
|
+
_open_as_mime_bytes,
|
|
15
|
+
)
|
|
8
16
|
from huggingface_hub.inference._providers._common import TaskProviderHelper, filter_none
|
|
9
17
|
from huggingface_hub.utils import build_hf_headers, get_session, get_token, hf_raise_for_status
|
|
10
18
|
|
|
@@ -23,16 +31,21 @@ class HFInferenceTask(TaskProviderHelper):
|
|
|
23
31
|
# special case: for HF Inference we allow not providing an API key
|
|
24
32
|
return api_key or get_token() # type: ignore[return-value]
|
|
25
33
|
|
|
26
|
-
def
|
|
27
|
-
if model is not None:
|
|
28
|
-
return
|
|
29
|
-
|
|
30
|
-
|
|
34
|
+
def _prepare_mapping_info(self, model: Optional[str]) -> InferenceProviderMapping:
|
|
35
|
+
if model is not None and model.startswith(("http://", "https://")):
|
|
36
|
+
return InferenceProviderMapping(
|
|
37
|
+
provider="hf-inference", providerId=model, hf_model_id=model, task=self.task, status="live"
|
|
38
|
+
)
|
|
39
|
+
model_id = model if model is not None else _fetch_recommended_models().get(self.task)
|
|
40
|
+
if model_id is None:
|
|
31
41
|
raise ValueError(
|
|
32
42
|
f"Task {self.task} has no recommended model for HF Inference. Please specify a model"
|
|
33
43
|
" explicitly. Visit https://huggingface.co/tasks for more info."
|
|
34
44
|
)
|
|
35
|
-
|
|
45
|
+
_check_supported_task(model_id, self.task)
|
|
46
|
+
return InferenceProviderMapping(
|
|
47
|
+
provider="hf-inference", providerId=model_id, hf_model_id=model_id, task=self.task, status="live"
|
|
48
|
+
)
|
|
36
49
|
|
|
37
50
|
def _prepare_url(self, api_key: str, mapped_model: str) -> str:
|
|
38
51
|
# hf-inference provider can handle URLs (e.g. Inference Endpoints or TGI deployment)
|
|
@@ -40,28 +53,36 @@ class HFInferenceTask(TaskProviderHelper):
|
|
|
40
53
|
return mapped_model
|
|
41
54
|
return (
|
|
42
55
|
# Feature-extraction and sentence-similarity are the only cases where we handle models with several tasks.
|
|
43
|
-
f"{self.base_url}/pipeline/{self.task}
|
|
56
|
+
f"{self.base_url}/models/{mapped_model}/pipeline/{self.task}"
|
|
44
57
|
if self.task in ("feature-extraction", "sentence-similarity")
|
|
45
58
|
# Otherwise, we use the default endpoint
|
|
46
59
|
else f"{self.base_url}/models/{mapped_model}"
|
|
47
60
|
)
|
|
48
61
|
|
|
49
|
-
def _prepare_payload_as_dict(
|
|
62
|
+
def _prepare_payload_as_dict(
|
|
63
|
+
self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
|
|
64
|
+
) -> Optional[dict]:
|
|
50
65
|
if isinstance(inputs, bytes):
|
|
51
66
|
raise ValueError(f"Unexpected binary input for task {self.task}.")
|
|
52
67
|
if isinstance(inputs, Path):
|
|
53
68
|
raise ValueError(f"Unexpected path input for task {self.task} (got {inputs})")
|
|
54
|
-
return {"inputs": inputs, "parameters":
|
|
69
|
+
return filter_none({"inputs": inputs, "parameters": parameters})
|
|
55
70
|
|
|
56
71
|
|
|
57
72
|
class HFInferenceBinaryInputTask(HFInferenceTask):
|
|
58
|
-
def _prepare_payload_as_dict(
|
|
73
|
+
def _prepare_payload_as_dict(
|
|
74
|
+
self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
|
|
75
|
+
) -> Optional[dict]:
|
|
59
76
|
return None
|
|
60
77
|
|
|
61
78
|
def _prepare_payload_as_bytes(
|
|
62
|
-
self,
|
|
63
|
-
|
|
64
|
-
parameters
|
|
79
|
+
self,
|
|
80
|
+
inputs: Any,
|
|
81
|
+
parameters: dict,
|
|
82
|
+
provider_mapping_info: InferenceProviderMapping,
|
|
83
|
+
extra_payload: Optional[dict],
|
|
84
|
+
) -> Optional[MimeBytes]:
|
|
85
|
+
parameters = filter_none(parameters)
|
|
65
86
|
extra_payload = extra_payload or {}
|
|
66
87
|
has_parameters = len(parameters) > 0 or len(extra_payload) > 0
|
|
67
88
|
|
|
@@ -71,21 +92,36 @@ class HFInferenceBinaryInputTask(HFInferenceTask):
|
|
|
71
92
|
|
|
72
93
|
# Send inputs as raw content when no parameters are provided
|
|
73
94
|
if not has_parameters:
|
|
74
|
-
|
|
75
|
-
data_as_bytes = data if isinstance(data, bytes) else data.read()
|
|
76
|
-
return data_as_bytes
|
|
95
|
+
return _open_as_mime_bytes(inputs)
|
|
77
96
|
|
|
78
97
|
# Otherwise encode as b64
|
|
79
|
-
return
|
|
98
|
+
return MimeBytes(
|
|
99
|
+
json.dumps({"inputs": _b64_encode(inputs), "parameters": parameters, **extra_payload}).encode("utf-8"),
|
|
100
|
+
mime_type="application/json",
|
|
101
|
+
)
|
|
80
102
|
|
|
81
103
|
|
|
82
104
|
class HFInferenceConversational(HFInferenceTask):
|
|
83
105
|
def __init__(self):
|
|
84
|
-
super().__init__("
|
|
85
|
-
|
|
86
|
-
def _prepare_payload_as_dict(
|
|
87
|
-
|
|
88
|
-
|
|
106
|
+
super().__init__("conversational")
|
|
107
|
+
|
|
108
|
+
def _prepare_payload_as_dict(
|
|
109
|
+
self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
|
|
110
|
+
) -> Optional[dict]:
|
|
111
|
+
payload = filter_none(parameters)
|
|
112
|
+
mapped_model = provider_mapping_info.provider_id
|
|
113
|
+
payload_model = parameters.get("model") or mapped_model
|
|
114
|
+
|
|
115
|
+
if payload_model is None or payload_model.startswith(("http://", "https://")):
|
|
116
|
+
payload_model = "dummy"
|
|
117
|
+
|
|
118
|
+
response_format = parameters.get("response_format")
|
|
119
|
+
if isinstance(response_format, dict) and response_format.get("type") == "json_schema":
|
|
120
|
+
payload["response_format"] = {
|
|
121
|
+
"type": "json_object",
|
|
122
|
+
"value": response_format["json_schema"]["schema"],
|
|
123
|
+
}
|
|
124
|
+
return {**payload, "model": payload_model, "messages": inputs}
|
|
89
125
|
|
|
90
126
|
def _prepare_url(self, api_key: str, mapped_model: str) -> str:
|
|
91
127
|
base_url = (
|
|
@@ -97,22 +133,96 @@ class HFInferenceConversational(HFInferenceTask):
|
|
|
97
133
|
|
|
98
134
|
|
|
99
135
|
def _build_chat_completion_url(model_url: str) -> str:
|
|
100
|
-
|
|
101
|
-
|
|
136
|
+
parsed = urlparse(model_url)
|
|
137
|
+
path = parsed.path.rstrip("/")
|
|
102
138
|
|
|
103
|
-
#
|
|
104
|
-
if
|
|
105
|
-
model_url
|
|
139
|
+
# If the path already ends with /chat/completions, we're done!
|
|
140
|
+
if path.endswith("/chat/completions"):
|
|
141
|
+
return model_url
|
|
106
142
|
|
|
143
|
+
# Append /chat/completions if not already present
|
|
144
|
+
if path.endswith("/v1"):
|
|
145
|
+
new_path = path + "/chat/completions"
|
|
146
|
+
# If path was empty or just "/", set the full path
|
|
147
|
+
elif not path:
|
|
148
|
+
new_path = "/v1/chat/completions"
|
|
107
149
|
# Append /v1/chat/completions if not already present
|
|
108
|
-
|
|
109
|
-
|
|
150
|
+
else:
|
|
151
|
+
new_path = path + "/v1/chat/completions"
|
|
110
152
|
|
|
111
|
-
|
|
153
|
+
# Reconstruct the URL with the new path and original query parameters.
|
|
154
|
+
new_parsed = parsed._replace(path=new_path)
|
|
155
|
+
return str(urlunparse(new_parsed))
|
|
112
156
|
|
|
113
157
|
|
|
114
158
|
@lru_cache(maxsize=1)
|
|
115
|
-
def _fetch_recommended_models() ->
|
|
159
|
+
def _fetch_recommended_models() -> dict[str, Optional[str]]:
|
|
116
160
|
response = get_session().get(f"{constants.ENDPOINT}/api/tasks", headers=build_hf_headers())
|
|
117
161
|
hf_raise_for_status(response)
|
|
118
162
|
return {task: next(iter(details["widgetModels"]), None) for task, details in response.json().items()}
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@lru_cache(maxsize=None)
|
|
166
|
+
def _check_supported_task(model: str, task: str) -> None:
|
|
167
|
+
from huggingface_hub.hf_api import HfApi
|
|
168
|
+
|
|
169
|
+
model_info = HfApi().model_info(model)
|
|
170
|
+
pipeline_tag = model_info.pipeline_tag
|
|
171
|
+
tags = model_info.tags or []
|
|
172
|
+
is_conversational = "conversational" in tags
|
|
173
|
+
if task in ("text-generation", "conversational"):
|
|
174
|
+
if pipeline_tag == "text-generation":
|
|
175
|
+
# text-generation + conversational tag -> both tasks allowed
|
|
176
|
+
if is_conversational:
|
|
177
|
+
return
|
|
178
|
+
# text-generation without conversational tag -> only text-generation allowed
|
|
179
|
+
if task == "text-generation":
|
|
180
|
+
return
|
|
181
|
+
raise ValueError(f"Model '{model}' doesn't support task '{task}'.")
|
|
182
|
+
|
|
183
|
+
if pipeline_tag == "text2text-generation":
|
|
184
|
+
if task == "text-generation":
|
|
185
|
+
return
|
|
186
|
+
raise ValueError(f"Model '{model}' doesn't support task '{task}'.")
|
|
187
|
+
|
|
188
|
+
if pipeline_tag == "image-text-to-text":
|
|
189
|
+
if is_conversational and task == "conversational":
|
|
190
|
+
return # Only conversational allowed if tagged as conversational
|
|
191
|
+
raise ValueError("Non-conversational image-text-to-text task is not supported.")
|
|
192
|
+
|
|
193
|
+
if (
|
|
194
|
+
task in ("feature-extraction", "sentence-similarity")
|
|
195
|
+
and pipeline_tag in ("feature-extraction", "sentence-similarity")
|
|
196
|
+
and task in tags
|
|
197
|
+
):
|
|
198
|
+
# feature-extraction and sentence-similarity are interchangeable for HF Inference
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
# For all other tasks, just check pipeline tag
|
|
202
|
+
if pipeline_tag != task:
|
|
203
|
+
raise ValueError(
|
|
204
|
+
f"Model '{model}' doesn't support task '{task}'. Supported tasks: '{pipeline_tag}', got: '{task}'"
|
|
205
|
+
)
|
|
206
|
+
return
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class HFInferenceFeatureExtractionTask(HFInferenceTask):
|
|
210
|
+
def __init__(self):
|
|
211
|
+
super().__init__("feature-extraction")
|
|
212
|
+
|
|
213
|
+
def _prepare_payload_as_dict(
|
|
214
|
+
self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
|
|
215
|
+
) -> Optional[dict]:
|
|
216
|
+
if isinstance(inputs, bytes):
|
|
217
|
+
raise ValueError(f"Unexpected binary input for task {self.task}.")
|
|
218
|
+
if isinstance(inputs, Path):
|
|
219
|
+
raise ValueError(f"Unexpected path input for task {self.task} (got {inputs})")
|
|
220
|
+
|
|
221
|
+
# Parameters are sent at root-level for feature-extraction task
|
|
222
|
+
# See specs: https://github.com/huggingface/huggingface.js/blob/main/packages/tasks/src/tasks/feature-extraction/spec/input.json
|
|
223
|
+
return {"inputs": inputs, **filter_none(parameters)}
|
|
224
|
+
|
|
225
|
+
def get_response(self, response: Union[bytes, dict], request_params: Optional[RequestParameters] = None) -> Any:
|
|
226
|
+
if isinstance(response, bytes):
|
|
227
|
+
return _bytes_to_dict(response)
|
|
228
|
+
return response
|