huggingface-hub 0.31.0rc0__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.
Files changed (150) hide show
  1. huggingface_hub/__init__.py +145 -46
  2. huggingface_hub/_commit_api.py +168 -119
  3. huggingface_hub/_commit_scheduler.py +15 -15
  4. huggingface_hub/_inference_endpoints.py +15 -12
  5. huggingface_hub/_jobs_api.py +301 -0
  6. huggingface_hub/_local_folder.py +18 -3
  7. huggingface_hub/_login.py +31 -63
  8. huggingface_hub/_oauth.py +460 -0
  9. huggingface_hub/_snapshot_download.py +239 -80
  10. huggingface_hub/_space_api.py +5 -5
  11. huggingface_hub/_tensorboard_logger.py +15 -19
  12. huggingface_hub/_upload_large_folder.py +172 -76
  13. huggingface_hub/_webhooks_payload.py +3 -3
  14. huggingface_hub/_webhooks_server.py +13 -25
  15. huggingface_hub/{commands → cli}/__init__.py +1 -15
  16. huggingface_hub/cli/_cli_utils.py +173 -0
  17. huggingface_hub/cli/auth.py +147 -0
  18. huggingface_hub/cli/cache.py +841 -0
  19. huggingface_hub/cli/download.py +189 -0
  20. huggingface_hub/cli/hf.py +60 -0
  21. huggingface_hub/cli/inference_endpoints.py +377 -0
  22. huggingface_hub/cli/jobs.py +772 -0
  23. huggingface_hub/cli/lfs.py +175 -0
  24. huggingface_hub/cli/repo.py +315 -0
  25. huggingface_hub/cli/repo_files.py +94 -0
  26. huggingface_hub/{commands/env.py → cli/system.py} +10 -13
  27. huggingface_hub/cli/upload.py +294 -0
  28. huggingface_hub/cli/upload_large_folder.py +117 -0
  29. huggingface_hub/community.py +20 -12
  30. huggingface_hub/constants.py +38 -53
  31. huggingface_hub/dataclasses.py +609 -0
  32. huggingface_hub/errors.py +80 -30
  33. huggingface_hub/fastai_utils.py +30 -41
  34. huggingface_hub/file_download.py +435 -351
  35. huggingface_hub/hf_api.py +2050 -1124
  36. huggingface_hub/hf_file_system.py +269 -152
  37. huggingface_hub/hub_mixin.py +43 -63
  38. huggingface_hub/inference/_client.py +347 -434
  39. huggingface_hub/inference/_common.py +133 -121
  40. huggingface_hub/inference/_generated/_async_client.py +397 -541
  41. huggingface_hub/inference/_generated/types/__init__.py +5 -1
  42. huggingface_hub/inference/_generated/types/automatic_speech_recognition.py +3 -3
  43. huggingface_hub/inference/_generated/types/base.py +10 -7
  44. huggingface_hub/inference/_generated/types/chat_completion.py +59 -23
  45. huggingface_hub/inference/_generated/types/depth_estimation.py +2 -2
  46. huggingface_hub/inference/_generated/types/document_question_answering.py +2 -2
  47. huggingface_hub/inference/_generated/types/feature_extraction.py +2 -2
  48. huggingface_hub/inference/_generated/types/fill_mask.py +2 -2
  49. huggingface_hub/inference/_generated/types/image_to_image.py +6 -2
  50. huggingface_hub/inference/_generated/types/image_to_video.py +60 -0
  51. huggingface_hub/inference/_generated/types/sentence_similarity.py +3 -3
  52. huggingface_hub/inference/_generated/types/summarization.py +2 -2
  53. huggingface_hub/inference/_generated/types/table_question_answering.py +5 -5
  54. huggingface_hub/inference/_generated/types/text2text_generation.py +2 -2
  55. huggingface_hub/inference/_generated/types/text_generation.py +10 -10
  56. huggingface_hub/inference/_generated/types/text_to_video.py +2 -2
  57. huggingface_hub/inference/_generated/types/token_classification.py +2 -2
  58. huggingface_hub/inference/_generated/types/translation.py +2 -2
  59. huggingface_hub/inference/_generated/types/zero_shot_classification.py +2 -2
  60. huggingface_hub/inference/_generated/types/zero_shot_image_classification.py +2 -2
  61. huggingface_hub/inference/_generated/types/zero_shot_object_detection.py +1 -3
  62. huggingface_hub/inference/_mcp/__init__.py +0 -0
  63. huggingface_hub/inference/_mcp/_cli_hacks.py +88 -0
  64. huggingface_hub/inference/_mcp/agent.py +100 -0
  65. huggingface_hub/inference/_mcp/cli.py +247 -0
  66. huggingface_hub/inference/_mcp/constants.py +81 -0
  67. huggingface_hub/inference/_mcp/mcp_client.py +395 -0
  68. huggingface_hub/inference/_mcp/types.py +45 -0
  69. huggingface_hub/inference/_mcp/utils.py +128 -0
  70. huggingface_hub/inference/_providers/__init__.py +82 -7
  71. huggingface_hub/inference/_providers/_common.py +129 -27
  72. huggingface_hub/inference/_providers/black_forest_labs.py +6 -6
  73. huggingface_hub/inference/_providers/cerebras.py +1 -1
  74. huggingface_hub/inference/_providers/clarifai.py +13 -0
  75. huggingface_hub/inference/_providers/cohere.py +20 -3
  76. huggingface_hub/inference/_providers/fal_ai.py +183 -56
  77. huggingface_hub/inference/_providers/featherless_ai.py +38 -0
  78. huggingface_hub/inference/_providers/fireworks_ai.py +18 -0
  79. huggingface_hub/inference/_providers/groq.py +9 -0
  80. huggingface_hub/inference/_providers/hf_inference.py +69 -30
  81. huggingface_hub/inference/_providers/hyperbolic.py +4 -4
  82. huggingface_hub/inference/_providers/nebius.py +33 -5
  83. huggingface_hub/inference/_providers/novita.py +5 -5
  84. huggingface_hub/inference/_providers/nscale.py +44 -0
  85. huggingface_hub/inference/_providers/openai.py +3 -1
  86. huggingface_hub/inference/_providers/publicai.py +6 -0
  87. huggingface_hub/inference/_providers/replicate.py +31 -13
  88. huggingface_hub/inference/_providers/sambanova.py +18 -4
  89. huggingface_hub/inference/_providers/scaleway.py +28 -0
  90. huggingface_hub/inference/_providers/together.py +20 -5
  91. huggingface_hub/inference/_providers/wavespeed.py +138 -0
  92. huggingface_hub/inference/_providers/zai_org.py +17 -0
  93. huggingface_hub/lfs.py +33 -100
  94. huggingface_hub/repocard.py +34 -38
  95. huggingface_hub/repocard_data.py +57 -57
  96. huggingface_hub/serialization/__init__.py +0 -1
  97. huggingface_hub/serialization/_base.py +12 -15
  98. huggingface_hub/serialization/_dduf.py +8 -8
  99. huggingface_hub/serialization/_torch.py +69 -69
  100. huggingface_hub/utils/__init__.py +19 -8
  101. huggingface_hub/utils/_auth.py +7 -7
  102. huggingface_hub/utils/_cache_manager.py +92 -147
  103. huggingface_hub/utils/_chunk_utils.py +2 -3
  104. huggingface_hub/utils/_deprecation.py +1 -1
  105. huggingface_hub/utils/_dotenv.py +55 -0
  106. huggingface_hub/utils/_experimental.py +7 -5
  107. huggingface_hub/utils/_fixes.py +0 -10
  108. huggingface_hub/utils/_git_credential.py +5 -5
  109. huggingface_hub/utils/_headers.py +8 -30
  110. huggingface_hub/utils/_http.py +398 -239
  111. huggingface_hub/utils/_pagination.py +4 -4
  112. huggingface_hub/utils/_parsing.py +98 -0
  113. huggingface_hub/utils/_paths.py +5 -5
  114. huggingface_hub/utils/_runtime.py +61 -24
  115. huggingface_hub/utils/_safetensors.py +21 -21
  116. huggingface_hub/utils/_subprocess.py +9 -9
  117. huggingface_hub/utils/_telemetry.py +4 -4
  118. huggingface_hub/{commands/_cli_utils.py → utils/_terminal.py} +4 -4
  119. huggingface_hub/utils/_typing.py +25 -5
  120. huggingface_hub/utils/_validators.py +55 -74
  121. huggingface_hub/utils/_verification.py +167 -0
  122. huggingface_hub/utils/_xet.py +64 -17
  123. huggingface_hub/utils/_xet_progress_reporting.py +162 -0
  124. huggingface_hub/utils/insecure_hashlib.py +3 -5
  125. huggingface_hub/utils/logging.py +8 -11
  126. huggingface_hub/utils/tqdm.py +5 -4
  127. {huggingface_hub-0.31.0rc0.dist-info → huggingface_hub-1.1.3.dist-info}/METADATA +94 -85
  128. huggingface_hub-1.1.3.dist-info/RECORD +155 -0
  129. {huggingface_hub-0.31.0rc0.dist-info → huggingface_hub-1.1.3.dist-info}/WHEEL +1 -1
  130. huggingface_hub-1.1.3.dist-info/entry_points.txt +6 -0
  131. huggingface_hub/commands/delete_cache.py +0 -474
  132. huggingface_hub/commands/download.py +0 -200
  133. huggingface_hub/commands/huggingface_cli.py +0 -61
  134. huggingface_hub/commands/lfs.py +0 -200
  135. huggingface_hub/commands/repo_files.py +0 -128
  136. huggingface_hub/commands/scan_cache.py +0 -181
  137. huggingface_hub/commands/tag.py +0 -159
  138. huggingface_hub/commands/upload.py +0 -314
  139. huggingface_hub/commands/upload_large_folder.py +0 -129
  140. huggingface_hub/commands/user.py +0 -304
  141. huggingface_hub/commands/version.py +0 -37
  142. huggingface_hub/inference_api.py +0 -217
  143. huggingface_hub/keras_mixin.py +0 -500
  144. huggingface_hub/repository.py +0 -1477
  145. huggingface_hub/serialization/_tensorflow.py +0 -95
  146. huggingface_hub/utils/_hf_folder.py +0 -68
  147. huggingface_hub-0.31.0rc0.dist-info/RECORD +0 -135
  148. huggingface_hub-0.31.0rc0.dist-info/entry_points.txt +0 -6
  149. {huggingface_hub-0.31.0rc0.dist-info → huggingface_hub-1.1.3.dist-info/licenses}/LICENSE +0 -0
  150. {huggingface_hub-0.31.0rc0.dist-info → huggingface_hub-1.1.3.dist-info}/top_level.txt +0 -0
@@ -1,12 +1,12 @@
1
1
  import base64
2
2
  import time
3
3
  from abc import ABC
4
- from typing import Any, Dict, Optional, Union
4
+ from typing import Any, Optional, Union
5
5
  from urllib.parse import urlparse
6
6
 
7
7
  from huggingface_hub import constants
8
8
  from huggingface_hub.hf_api import InferenceProviderMapping
9
- from huggingface_hub.inference._common import RequestParameters, _as_dict
9
+ from huggingface_hub.inference._common import RequestParameters, _as_dict, _as_url
10
10
  from huggingface_hub.inference._providers._common import TaskProviderHelper, filter_none
11
11
  from huggingface_hub.utils import get_session, hf_raise_for_status
12
12
  from huggingface_hub.utils.logging import get_logger
@@ -22,7 +22,7 @@ class FalAITask(TaskProviderHelper, ABC):
22
22
  def __init__(self, task: str):
23
23
  super().__init__(provider="fal-ai", base_url="https://fal.run", task=task)
24
24
 
25
- def _prepare_headers(self, headers: Dict, api_key: str) -> Dict:
25
+ def _prepare_headers(self, headers: dict, api_key: str) -> dict[str, Any]:
26
26
  headers = super()._prepare_headers(headers, api_key)
27
27
  if not api_key.startswith("hf_"):
28
28
  headers["authorization"] = f"Key {api_key}"
@@ -32,13 +32,67 @@ class FalAITask(TaskProviderHelper, ABC):
32
32
  return f"/{mapped_model}"
33
33
 
34
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
+
35
89
  class FalAIAutomaticSpeechRecognitionTask(FalAITask):
36
90
  def __init__(self):
37
91
  super().__init__("automatic-speech-recognition")
38
92
 
39
93
  def _prepare_payload_as_dict(
40
- self, inputs: Any, parameters: Dict, provider_mapping_info: InferenceProviderMapping
41
- ) -> Optional[Dict]:
94
+ self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
95
+ ) -> Optional[dict]:
42
96
  if isinstance(inputs, str) and inputs.startswith(("http://", "https://")):
43
97
  # If input is a URL, pass it directly
44
98
  audio_url = inputs
@@ -54,7 +108,7 @@ class FalAIAutomaticSpeechRecognitionTask(FalAITask):
54
108
 
55
109
  return {"audio_url": audio_url, **filter_none(parameters)}
56
110
 
57
- def get_response(self, response: Union[bytes, Dict], request_params: Optional[RequestParameters] = None) -> Any:
111
+ def get_response(self, response: Union[bytes, dict], request_params: Optional[RequestParameters] = None) -> Any:
58
112
  text = _as_dict(response)["text"]
59
113
  if not isinstance(text, str):
60
114
  raise ValueError(f"Unexpected output format from FalAI API. Expected string, got {type(text)}.")
@@ -66,9 +120,9 @@ class FalAITextToImageTask(FalAITask):
66
120
  super().__init__("text-to-image")
67
121
 
68
122
  def _prepare_payload_as_dict(
69
- self, inputs: Any, parameters: Dict, provider_mapping_info: InferenceProviderMapping
70
- ) -> Optional[Dict]:
71
- payload: Dict[str, Any] = {
123
+ self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
124
+ ) -> Optional[dict]:
125
+ payload: dict[str, Any] = {
72
126
  "prompt": inputs,
73
127
  **filter_none(parameters),
74
128
  }
@@ -91,7 +145,7 @@ class FalAITextToImageTask(FalAITask):
91
145
 
92
146
  return payload
93
147
 
94
- def get_response(self, response: Union[bytes, Dict], request_params: Optional[RequestParameters] = None) -> Any:
148
+ def get_response(self, response: Union[bytes, dict], request_params: Optional[RequestParameters] = None) -> Any:
95
149
  url = _as_dict(response)["images"][0]["url"]
96
150
  return get_session().get(url).content
97
151
 
@@ -101,72 +155,145 @@ class FalAITextToSpeechTask(FalAITask):
101
155
  super().__init__("text-to-speech")
102
156
 
103
157
  def _prepare_payload_as_dict(
104
- self, inputs: Any, parameters: Dict, provider_mapping_info: InferenceProviderMapping
105
- ) -> Optional[Dict]:
158
+ self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
159
+ ) -> Optional[dict]:
106
160
  return {"text": inputs, **filter_none(parameters)}
107
161
 
108
- def get_response(self, response: Union[bytes, Dict], request_params: Optional[RequestParameters] = None) -> Any:
162
+ def get_response(self, response: Union[bytes, dict], request_params: Optional[RequestParameters] = None) -> Any:
109
163
  url = _as_dict(response)["audio"]["url"]
110
164
  return get_session().get(url).content
111
165
 
112
166
 
113
- class FalAITextToVideoTask(FalAITask):
167
+ class FalAITextToVideoTask(FalAIQueueTask):
114
168
  def __init__(self):
115
169
  super().__init__("text-to-video")
116
170
 
117
- def _prepare_base_url(self, api_key: str) -> str:
118
- if api_key.startswith("hf_"):
119
- return super()._prepare_base_url(api_key)
120
- else:
121
- logger.info(f"Calling '{self.provider}' provider directly.")
122
- return "https://queue.fal.run"
123
-
124
- def _prepare_route(self, mapped_model: str, api_key: str) -> str:
125
- if api_key.startswith("hf_"):
126
- # Use the queue subdomain for HF routing
127
- return f"/{mapped_model}?_subdomain=queue"
128
- return f"/{mapped_model}"
129
-
130
171
  def _prepare_payload_as_dict(
131
- self, inputs: Any, parameters: Dict, provider_mapping_info: InferenceProviderMapping
132
- ) -> Optional[Dict]:
172
+ self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
173
+ ) -> Optional[dict]:
133
174
  return {"prompt": inputs, **filter_none(parameters)}
134
175
 
135
176
  def get_response(
136
177
  self,
137
- response: Union[bytes, Dict],
178
+ response: Union[bytes, dict],
138
179
  request_params: Optional[RequestParameters] = None,
139
180
  ) -> Any:
140
- response_dict = _as_dict(response)
181
+ output = super().get_response(response, request_params)
182
+ url = _as_dict(output)["video"]["url"]
183
+ return get_session().get(url).content
141
184
 
142
- request_id = response_dict.get("request_id")
143
- if not request_id:
144
- raise ValueError("No request ID found in the response")
145
- if request_params is None:
146
- raise ValueError(
147
- "A `RequestParameters` object should be provided to get text-to-video responses with Fal AI."
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,
148
205
  )
206
+ payload["loras"] = [{"path": lora_path, "scale": 1}]
149
207
 
150
- # extract the base url and query params
151
- parsed_url = urlparse(request_params.url)
152
- # a bit hacky way to concatenate the provider name without parsing `parsed_url.path`
153
- base_url = f"{parsed_url.scheme}://{parsed_url.netloc}{'/fal-ai' if parsed_url.netloc == 'router.huggingface.co' else ''}"
154
- query_param = f"?{parsed_url.query}" if parsed_url.query else ""
208
+ return payload
155
209
 
156
- # extracting the provider model id for status and result urls
157
- # from the response as it might be different from the mapped model in `request_params.url`
158
- model_id = urlparse(response_dict.get("response_url")).path
159
- status_url = f"{base_url}{str(model_id)}/status{query_param}"
160
- result_url = f"{base_url}{str(model_id)}{query_param}"
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
161
218
 
162
- status = response_dict.get("status")
163
- logger.info("Generating the video.. this can take several minutes.")
164
- while status != "COMPLETED":
165
- time.sleep(_POLLING_INTERVAL)
166
- status_response = get_session().get(status_url, headers=request_params.headers)
167
- hf_raise_for_status(status_response)
168
- status = status_response.json().get("status")
169
219
 
170
- response = get_session().get(result_url, headers=request_params.headers).json()
171
- url = _as_dict(response)["video"]["url"]
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"]
172
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,3 +1,7 @@
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
 
@@ -7,3 +11,17 @@ class FireworksAIConversationalTask(BaseConversationalTask):
7
11
 
8
12
  def _prepare_route(self, mapped_model: str, api_key: str) -> str:
9
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,11 +1,18 @@
1
1
  import json
2
2
  from functools import lru_cache
3
3
  from pathlib import Path
4
- from typing import Any, Dict, Optional, Union
4
+ from typing import Any, Optional, Union
5
+ from urllib.parse import urlparse, urlunparse
5
6
 
6
7
  from huggingface_hub import constants
7
8
  from huggingface_hub.hf_api import InferenceProviderMapping
8
- from huggingface_hub.inference._common import RequestParameters, _b64_encode, _bytes_to_dict, _open_as_binary
9
+ from huggingface_hub.inference._common import (
10
+ MimeBytes,
11
+ RequestParameters,
12
+ _b64_encode,
13
+ _bytes_to_dict,
14
+ _open_as_mime_bytes,
15
+ )
9
16
  from huggingface_hub.inference._providers._common import TaskProviderHelper, filter_none
10
17
  from huggingface_hub.utils import build_hf_headers, get_session, get_token, hf_raise_for_status
11
18
 
@@ -26,7 +33,9 @@ class HFInferenceTask(TaskProviderHelper):
26
33
 
27
34
  def _prepare_mapping_info(self, model: Optional[str]) -> InferenceProviderMapping:
28
35
  if model is not None and model.startswith(("http://", "https://")):
29
- return InferenceProviderMapping(providerId=model, hf_model_id=model, task=self.task, status="live")
36
+ return InferenceProviderMapping(
37
+ provider="hf-inference", providerId=model, hf_model_id=model, task=self.task, status="live"
38
+ )
30
39
  model_id = model if model is not None else _fetch_recommended_models().get(self.task)
31
40
  if model_id is None:
32
41
  raise ValueError(
@@ -34,7 +43,9 @@ class HFInferenceTask(TaskProviderHelper):
34
43
  " explicitly. Visit https://huggingface.co/tasks for more info."
35
44
  )
36
45
  _check_supported_task(model_id, self.task)
37
- return InferenceProviderMapping(providerId=model_id, hf_model_id=model_id, task=self.task, status="live")
46
+ return InferenceProviderMapping(
47
+ provider="hf-inference", providerId=model_id, hf_model_id=model_id, task=self.task, status="live"
48
+ )
38
49
 
39
50
  def _prepare_url(self, api_key: str, mapped_model: str) -> str:
40
51
  # hf-inference provider can handle URLs (e.g. Inference Endpoints or TGI deployment)
@@ -49,29 +60,29 @@ class HFInferenceTask(TaskProviderHelper):
49
60
  )
50
61
 
51
62
  def _prepare_payload_as_dict(
52
- self, inputs: Any, parameters: Dict, provider_mapping_info: InferenceProviderMapping
53
- ) -> Optional[Dict]:
63
+ self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
64
+ ) -> Optional[dict]:
54
65
  if isinstance(inputs, bytes):
55
66
  raise ValueError(f"Unexpected binary input for task {self.task}.")
56
67
  if isinstance(inputs, Path):
57
68
  raise ValueError(f"Unexpected path input for task {self.task} (got {inputs})")
58
- return {"inputs": inputs, "parameters": filter_none(parameters)}
69
+ return filter_none({"inputs": inputs, "parameters": parameters})
59
70
 
60
71
 
61
72
  class HFInferenceBinaryInputTask(HFInferenceTask):
62
73
  def _prepare_payload_as_dict(
63
- self, inputs: Any, parameters: Dict, provider_mapping_info: InferenceProviderMapping
64
- ) -> Optional[Dict]:
74
+ self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
75
+ ) -> Optional[dict]:
65
76
  return None
66
77
 
67
78
  def _prepare_payload_as_bytes(
68
79
  self,
69
80
  inputs: Any,
70
- parameters: Dict,
81
+ parameters: dict,
71
82
  provider_mapping_info: InferenceProviderMapping,
72
- extra_payload: Optional[Dict],
73
- ) -> Optional[bytes]:
74
- parameters = filter_none({k: v for k, v in parameters.items() if v is not None})
83
+ extra_payload: Optional[dict],
84
+ ) -> Optional[MimeBytes]:
85
+ parameters = filter_none(parameters)
75
86
  extra_payload = extra_payload or {}
76
87
  has_parameters = len(parameters) > 0 or len(extra_payload) > 0
77
88
 
@@ -81,12 +92,13 @@ class HFInferenceBinaryInputTask(HFInferenceTask):
81
92
 
82
93
  # Send inputs as raw content when no parameters are provided
83
94
  if not has_parameters:
84
- with _open_as_binary(inputs) as data:
85
- data_as_bytes = data if isinstance(data, bytes) else data.read()
86
- return data_as_bytes
95
+ return _open_as_mime_bytes(inputs)
87
96
 
88
97
  # Otherwise encode as b64
89
- return json.dumps({"inputs": _b64_encode(inputs), "parameters": parameters, **extra_payload}).encode("utf-8")
98
+ return MimeBytes(
99
+ json.dumps({"inputs": _b64_encode(inputs), "parameters": parameters, **extra_payload}).encode("utf-8"),
100
+ mime_type="application/json",
101
+ )
90
102
 
91
103
 
92
104
  class HFInferenceConversational(HFInferenceTask):
@@ -94,15 +106,22 @@ class HFInferenceConversational(HFInferenceTask):
94
106
  super().__init__("conversational")
95
107
 
96
108
  def _prepare_payload_as_dict(
97
- self, inputs: Any, parameters: Dict, provider_mapping_info: InferenceProviderMapping
98
- ) -> Optional[Dict]:
109
+ self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
110
+ ) -> Optional[dict]:
111
+ payload = filter_none(parameters)
99
112
  mapped_model = provider_mapping_info.provider_id
100
113
  payload_model = parameters.get("model") or mapped_model
101
114
 
102
115
  if payload_model is None or payload_model.startswith(("http://", "https://")):
103
116
  payload_model = "dummy"
104
117
 
105
- return {**filter_none(parameters), "model": payload_model, "messages": inputs}
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}
106
125
 
107
126
  def _prepare_url(self, api_key: str, mapped_model: str) -> str:
108
127
  base_url = (
@@ -114,22 +133,30 @@ class HFInferenceConversational(HFInferenceTask):
114
133
 
115
134
 
116
135
  def _build_chat_completion_url(model_url: str) -> str:
117
- # Strip trailing /
118
- model_url = model_url.rstrip("/")
136
+ parsed = urlparse(model_url)
137
+ path = parsed.path.rstrip("/")
119
138
 
120
- # Append /chat/completions if not already present
121
- if model_url.endswith("/v1"):
122
- model_url += "/chat/completions"
139
+ # If the path already ends with /chat/completions, we're done!
140
+ if path.endswith("/chat/completions"):
141
+ return model_url
123
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"
124
149
  # Append /v1/chat/completions if not already present
125
- if not model_url.endswith("/chat/completions"):
126
- model_url += "/v1/chat/completions"
150
+ else:
151
+ new_path = path + "/v1/chat/completions"
127
152
 
128
- return model_url
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))
129
156
 
130
157
 
131
158
  @lru_cache(maxsize=1)
132
- def _fetch_recommended_models() -> Dict[str, Optional[str]]:
159
+ def _fetch_recommended_models() -> dict[str, Optional[str]]:
133
160
  response = get_session().get(f"{constants.ENDPOINT}/api/tasks", headers=build_hf_headers())
134
161
  hf_raise_for_status(response)
135
162
  return {task: next(iter(details["widgetModels"]), None) for task, details in response.json().items()}
@@ -183,7 +210,19 @@ class HFInferenceFeatureExtractionTask(HFInferenceTask):
183
210
  def __init__(self):
184
211
  super().__init__("feature-extraction")
185
212
 
186
- def get_response(self, response: Union[bytes, Dict], request_params: Optional[RequestParameters] = None) -> Any:
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:
187
226
  if isinstance(response, bytes):
188
227
  return _bytes_to_dict(response)
189
228
  return response
@@ -1,5 +1,5 @@
1
1
  import base64
2
- from typing import Any, Dict, Optional, Union
2
+ from typing import Any, Optional, Union
3
3
 
4
4
  from huggingface_hub.hf_api import InferenceProviderMapping
5
5
  from huggingface_hub.inference._common import RequestParameters, _as_dict
@@ -14,8 +14,8 @@ class HyperbolicTextToImageTask(TaskProviderHelper):
14
14
  return "/v1/images/generations"
15
15
 
16
16
  def _prepare_payload_as_dict(
17
- self, inputs: Any, parameters: Dict, provider_mapping_info: InferenceProviderMapping
18
- ) -> Optional[Dict]:
17
+ self, inputs: Any, parameters: dict, provider_mapping_info: InferenceProviderMapping
18
+ ) -> Optional[dict]:
19
19
  mapped_model = provider_mapping_info.provider_id
20
20
  parameters = filter_none(parameters)
21
21
  if "num_inference_steps" in parameters:
@@ -29,7 +29,7 @@ class HyperbolicTextToImageTask(TaskProviderHelper):
29
29
  parameters["height"] = 512
30
30
  return {"prompt": inputs, "model_name": mapped_model, **parameters}
31
31
 
32
- def get_response(self, response: Union[bytes, Dict], request_params: Optional[RequestParameters] = None) -> Any:
32
+ def get_response(self, response: Union[bytes, dict], request_params: Optional[RequestParameters] = None) -> Any:
33
33
  response_dict = _as_dict(response)
34
34
  return base64.b64decode(response_dict["images"][0]["image"])
35
35