Repository: huggingface/huggingface_hub
Pull request #3011: PoC: `provider="auto"`
Author: login: julien-c
Created: 2025-04-16T17:50:29Z | Merged: 2025-04-24T13:50:56Z



Diff:
diff --git a/docs/source/en/guides/inference.md b/docs/source/en/guides/inference.md
index eb13b56a64..f00902f29e 100644
--- a/docs/source/en/guides/inference.md
+++ b/docs/source/en/guides/inference.md
@@ -98,7 +98,7 @@ all the parameters available for each task.
 
 ### Using a specific provider
 
-If you want to use a specific provider, you can specify it when initializing the client. The default provider is "hf-inference", the Hugging Face Serverless Inference API. Refer to the [Supported providers and tasks](#supported-providers-and-tasks) section for a list of supported providers.
+If you want to use a specific provider, you can specify it when initializing the client. The default value is "auto" which will select the first of the providers available for the model, sorted by the user's order in https://hf.co/settings/inference-providers. Refer to the [Supported providers and tasks](#supported-providers-and-tasks) section for a list of supported providers.
 
 ```python
 >>> from huggingface_hub import InferenceClient
@@ -112,21 +112,20 @@ What if you want to use a specific model? You can specify it either as a paramet
 ```python
 >>> from huggingface_hub import InferenceClient
 # Initialize client for a specific model
->>> client = InferenceClient(model="prompthero/openjourney-v4")
+>>> client = InferenceClient(provider="together", model="meta-llama/Llama-3.1-8B-Instruct")
 >>> client.text_to_image(...)
 # Or use a generic client but pass your model as an argument
->>> client = InferenceClient()
->>> client.text_to_image(..., model="prompthero/openjourney-v4")
+>>> client = InferenceClient(provider="together")
+>>> client.text_to_image(..., model="meta-llama/Llama-3.1-8B-Instruct")
 ```
 
 <Tip>
 
-When using the Hugging Face Inference API (default provider), each task comes with a recommended model from the 200k+ models available on the Hub.
+When using the "hf-inference" provider, each task comes with a recommended model from the 1M+ models available on the Hub.
 However, this recommendation can change over time, so it's best to explicitly set a model once you've decided which one to use.
 For third-party providers, you must always specify a model that is compatible with that provider.
 
-Visit the [Models](https://huggingface.co/models?inference=warm) page on the Hub to explore models available through the Inference API, or check the provider's documentation for their supported models.
-
+Visit the [Models](https://huggingface.co/models?inference=warm) page on the Hub to explore models available through Inference Providers.
 </Tip>
 
 ### Using a specific URL
@@ -149,37 +148,38 @@ Note that you cannot specify both a URL and a provider - they are mutually exclu
 
 ### Authentication
 
-Authentication depends on which provider you are using:
+Authentication can be done in two ways:
 
-1. For the default Hugging Face Inference API, you can authenticate using a [User Access Token](https://huggingface.co/docs/hub/security-tokens):
+**Routed through Hugging Face** : Use Hugging Face as a proxy to access third-party providers. The calls will be routed through Hugging Face's infrastructure using our provider keys, and the usage will be billed directly to your Hugging Face account. 
 
+You can authenticate using a [User Access Token](https://huggingface.co/docs/hub/security-tokens). You can provide your Hugging Face token directly using the `api_key` parameter:
+ 
 ```python
->>> from huggingface_hub import InferenceClient
->>> client = InferenceClient(token="hf_***")
+>>> client = InferenceClient(
+    provider="replicate",
+    api_key="hf_****"  # Your HF token
+)
 ```
 
-By default, it will use the token saved on your machine if you are logged in (see [how to authenticate](https://huggingface.co/docs/huggingface_hub/quick-start#authentication)).
-
-2. For third-party providers, you have two options:
+If you *don't* pass an `api_key`, the client will attempt to find and use a token stored locally on your machine. This typically happens if you've previously logged in. See the [Authentication Guide](https://huggingface.co/docs/huggingface_hub/quick-start#authentication) for details on login.
 
-**Direct access to provider**: Use your own API key to interact directly with the provider's service:
 ```python
 >>> client = InferenceClient(
     provider="replicate",
-    api_key="r8_****"  # Your Replicate API key
+    token="hf_****"  # Your HF token
 )
 ```
 
-**Routed through Hugging Face** : Use Hugging Face as a proxy to access third-party providers. Simply specify
-your Hugging Face token and the provider you want to use. The calls will be routed through Hugging Face's infrastructure
-using our provider keys, and the usage will be billed directly to your Hugging Face account:
+**Direct access to provider**: Use your own API key to interact directly with the provider's service:
 ```python
 >>> client = InferenceClient(
     provider="replicate",
-    token="hf_****"  # Your HF token
+    api_key="r8_****"  # Your Replicate API key
 )
 ```
 
+For more details, refer to the [Inference Providers pricing documentation](https://huggingface.co/docs/inference-providers/pricing#routed-requests-vs-direct-calls).
+
 ## Supported providers and tasks
 
 [`InferenceClient`]'s goal is to provide the easiest interface to run inference on Hugging Face models, on any provider. It has a simple API that supports the most common tasks. Here is a table showing which providers support which tasks:
diff --git a/src/huggingface_hub/inference/_client.py b/src/huggingface_hub/inference/_client.py
index a4ab80b9a9..0505e28fbf 100644
--- a/src/huggingface_hub/inference/_client.py
+++ b/src/huggingface_hub/inference/_client.py
@@ -134,7 +134,7 @@ class InferenceClient:
             documentation for details). When passing a URL as `model`, the client will not append any suffix path to it.
         provider (`str`, *optional*):
             Name of the provider to use for inference. Can be `"black-forest-labs"`, `"cerebras"`, `"cohere"`, `"fal-ai"`, `"fireworks-ai"`, `"hf-inference"`, `"hyperbolic"`, `"nebius"`, `"novita"`, `"openai"`, `"replicate"`, "sambanova"` or `"together"`.
-            defaults to hf-inference (Hugging Face Serverless Inference API).
+            Defaults to "auto" i.e. the first of the providers available for the model, sorted by the user's order in https://hf.co/settings/inference-providers.
             If model is a URL or `base_url` is passed, then `provider` is not used.
         token (`str`, *optional*):
             Hugging Face token. Will default to the locally saved token if not provided.
@@ -165,7 +165,7 @@ def __init__(
         self,
         model: Optional[str] = None,
         *,
-        provider: Optional[PROVIDER_T] = None,
+        provider: Union[Literal["auto"], PROVIDER_T, None] = None,
         token: Optional[str] = None,
         timeout: Optional[float] = None,
         headers: Optional[Dict[str, str]] = None,
@@ -228,7 +228,7 @@ def __init__(
                 )
 
         # Configure provider
-        self.provider = provider if provider is not None else "hf-inference"
+        self.provider = provider
 
         self.cookies = cookies
         self.timeout = timeout
@@ -332,12 +332,13 @@ def audio_classification(
         ]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="audio-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="audio-classification", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=audio,
             parameters={"function_to_apply": function_to_apply, "top_k": top_k},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -380,12 +381,13 @@ def audio_to_audio(
                     f.write(item.blob)
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="audio-to-audio")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="audio-to-audio", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=audio,
             parameters={},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -430,12 +432,13 @@ def automatic_speech_recognition(
         "hello world"
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="automatic-speech-recognition")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="automatic-speech-recognition", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=audio,
             parameters={**(extra_body or {})},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -874,15 +877,15 @@ def chat_completion(
         '{\n\n"activity": "bike ride",\n"animals": ["puppy", "cat", "raccoon"],\n"animals_seen": 3,\n"location": "park"}'
         ```
         """
-        # Get the provider helper
-        provider_helper = get_provider_helper(self.provider, task="conversational")
-
         # Since `chat_completion(..., model=xxx)` is also a payload parameter for the server, we need to handle 'model' differently.
         # `self.model` takes precedence over 'model' argument for building URL.
         # `model` takes precedence for payload value.
         model_id_or_url = self.model or model
         payload_model = model or self.model
 
+        # Get the provider helper
+        provider_helper = get_provider_helper(self.provider, task="conversational", model=payload_model)
+
         # Prepare the payload
         parameters = {
             "model": payload_model,
@@ -984,8 +987,9 @@ def document_question_answering(
         [DocumentQuestionAnsweringOutputElement(answer='us-001', end=16, score=0.9999666213989258, start=16)]
         ```
         """
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="document-question-answering", model=model_id)
         inputs: Dict[str, Any] = {"question": question, "image": _b64_encode(image)}
-        provider_helper = get_provider_helper(self.provider, task="document-question-answering")
         request_parameters = provider_helper.prepare_request(
             inputs=inputs,
             parameters={
@@ -999,7 +1003,7 @@ def document_question_answering(
                 "word_boxes": word_boxes,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -1060,7 +1064,8 @@ def feature_extraction(
         [ 0.28552425, -0.928395  , -1.2077185 , ...,  0.76810825, -2.1069427 ,  0.6236161 ]], dtype=float32)
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="feature-extraction")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="feature-extraction", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=text,
             parameters={
@@ -1070,7 +1075,7 @@ def feature_extraction(
                 "truncation_direction": truncation_direction,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -1121,12 +1126,13 @@ def fill_mask(
         ]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="fill-mask")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="fill-mask", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=text,
             parameters={"targets": targets, "top_k": top_k},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -1170,12 +1176,13 @@ def image_classification(
         [ImageClassificationOutputElement(label='Blenheim spaniel', score=0.9779096841812134), ...]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="image-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="image-classification", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=image,
             parameters={"function_to_apply": function_to_apply, "top_k": top_k},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -1231,7 +1238,8 @@ def image_segmentation(
         [ImageSegmentationOutputElement(score=0.989008, label='LABEL_184', mask=<PIL.PngImagePlugin.PngImageFile image mode=L size=400x300 at 0x7FDD2B129CC0>), ...]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="image-segmentation")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="image-segmentation", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=image,
             parameters={
@@ -1241,7 +1249,7 @@ def image_segmentation(
                 "threshold": threshold,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -1307,7 +1315,8 @@ def image_to_image(
         >>> image.save("tiger.jpg")
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="image-to-image")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="image-to-image", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=image,
             parameters={
@@ -1319,7 +1328,7 @@ def image_to_image(
                 **kwargs,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -1358,12 +1367,13 @@ def image_to_text(self, image: ContentT, *, model: Optional[str] = None) -> Imag
         'a dog laying on the grass next to a flower pot '
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="image-to-text")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="image-to-text", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=image,
             parameters={},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -1409,12 +1419,13 @@ def object_detection(
         [ObjectDetectionOutputElement(score=0.9486683011054993, label='person', box=ObjectDetectionBoundingBox(xmin=59, ymin=39, xmax=420, ymax=510)), ...]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="object-detection")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="object-detection", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=image,
             parameters={"threshold": threshold},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -1482,7 +1493,8 @@ def question_answering(
         QuestionAnsweringOutputElement(answer='Clara', end=16, score=0.9326565265655518, start=11)
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="question-answering")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="question-answering", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=None,
             parameters={
@@ -1496,7 +1508,7 @@ def question_answering(
             },
             extra_payload={"question": question, "context": context},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -1544,13 +1556,14 @@ def sentence_similarity(
         [0.7785726189613342, 0.45876261591911316, 0.2906220555305481]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="sentence-similarity")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="sentence-similarity", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs={"source_sentence": sentence, "sentences": other_sentences},
             parameters={},
             extra_payload={},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -1602,12 +1615,13 @@ def summarization(
             "generate_parameters": generate_parameters,
             "truncation": truncation,
         }
-        provider_helper = get_provider_helper(self.provider, task="summarization")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="summarization", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=text,
             parameters=parameters,
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -1663,13 +1677,14 @@ def table_question_answering(
         TableQuestionAnsweringOutputElement(answer='36542', coordinates=[[0, 1]], cells=['36542'], aggregator='AVERAGE')
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="table-question-answering")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="table-question-answering", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=None,
             parameters={"model": model, "padding": padding, "sequential": sequential, "truncation": truncation},
             extra_payload={"query": query, "table": table},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -1717,13 +1732,14 @@ def tabular_classification(self, table: Dict[str, Any], *, model: Optional[str]
         ["5", "5", "5"]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="tabular-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="tabular-classification", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=None,
             extra_payload={"table": table},
             parameters={},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -1766,13 +1782,14 @@ def tabular_regression(self, table: Dict[str, Any], *, model: Optional[str] = No
         [110, 120, 130]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="tabular-regression")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="tabular-regression", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=None,
             parameters={},
             extra_payload={"table": table},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -1821,7 +1838,8 @@ def text_classification(
         ]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="text-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="text-classification", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=text,
             parameters={
@@ -1829,7 +1847,7 @@ def text_classification(
                 "top_k": top_k,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -2270,13 +2288,14 @@ def text_generation(
                     " Please pass `stream=False` as input."
                 )
 
-        provider_helper = get_provider_helper(self.provider, task="text-generation")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="text-generation", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=prompt,
             parameters=parameters,
             extra_payload={"stream": stream},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
 
@@ -2292,7 +2311,7 @@ def text_generation(
                     prompt=prompt,
                     details=details,
                     stream=stream,
-                    model=model or self.model,
+                    model=model_id,
                     adapter_id=adapter_id,
                     best_of=best_of,
                     decoder_input_details=decoder_input_details,
@@ -2447,7 +2466,8 @@ def text_to_image(
         >>> image.save("astronaut.png")
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="text-to-image")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="text-to-image", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=prompt,
             parameters={
@@ -2461,7 +2481,7 @@ def text_to_image(
                 **(extra_body or {}),
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -2545,7 +2565,8 @@ def text_to_video(
         ...     file.write(video)
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="text-to-video")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="text-to-video", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=prompt,
             parameters={
@@ -2557,7 +2578,7 @@ def text_to_video(
                 **(extra_body or {}),
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -2742,7 +2763,8 @@ def text_to_speech(
         ...     f.write(audio)
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="text-to-speech")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="text-to-speech", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=text,
             parameters={
@@ -2765,7 +2787,7 @@ def text_to_speech(
                 **(extra_body or {}),
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -2831,7 +2853,8 @@ def token_classification(
         ]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="token-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="token-classification", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=text,
             parameters={
@@ -2840,7 +2863,7 @@ def token_classification(
                 "stride": stride,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -2917,7 +2940,8 @@ def translation(
         if src_lang is None and tgt_lang is not None:
             raise ValueError("You cannot specify `tgt_lang` without specifying `src_lang`.")
 
-        provider_helper = get_provider_helper(self.provider, task="translation")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="translation", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=text,
             parameters={
@@ -2928,7 +2952,7 @@ def translation(
                 "generate_parameters": generate_parameters,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -2980,12 +3004,13 @@ def visual_question_answering(
         ]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="visual-question-answering")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="visual-question-answering", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=image,
             parameters={"top_k": top_k},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
             extra_payload={"question": question, "image": _b64_encode(image)},
         )
@@ -3078,7 +3103,8 @@ def zero_shot_classification(
         ]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="zero-shot-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="zero-shot-classification", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=text,
             parameters={
@@ -3087,7 +3113,7 @@ def zero_shot_classification(
                 "hypothesis_template": hypothesis_template,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
@@ -3149,7 +3175,8 @@ def zero_shot_image_classification(
         if len(candidate_labels) < 2:
             raise ValueError("You must specify at least 2 classes to compare.")
 
-        provider_helper = get_provider_helper(self.provider, task="zero-shot-image-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="zero-shot-image-classification", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=image,
             parameters={
@@ -3157,7 +3184,7 @@ def zero_shot_image_classification(
                 "hypothesis_template": hypothesis_template,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = self._inner_post(request_parameters)
diff --git a/src/huggingface_hub/inference/_generated/_async_client.py b/src/huggingface_hub/inference/_generated/_async_client.py
index 9bd872c7d0..80e1324955 100644
--- a/src/huggingface_hub/inference/_generated/_async_client.py
+++ b/src/huggingface_hub/inference/_generated/_async_client.py
@@ -122,7 +122,7 @@ class AsyncInferenceClient:
             documentation for details). When passing a URL as `model`, the client will not append any suffix path to it.
         provider (`str`, *optional*):
             Name of the provider to use for inference. Can be `"black-forest-labs"`, `"cerebras"`, `"cohere"`, `"fal-ai"`, `"fireworks-ai"`, `"hf-inference"`, `"hyperbolic"`, `"nebius"`, `"novita"`, `"openai"`, `"replicate"`, "sambanova"` or `"together"`.
-            defaults to hf-inference (Hugging Face Serverless Inference API).
+            Defaults to "auto" i.e. the first of the providers available for the model, sorted by the user's order in https://hf.co/settings/inference-providers.
             If model is a URL or `base_url` is passed, then `provider` is not used.
         token (`str`, *optional*):
             Hugging Face token. Will default to the locally saved token if not provided.
@@ -155,7 +155,7 @@ def __init__(
         self,
         model: Optional[str] = None,
         *,
-        provider: Optional[PROVIDER_T] = None,
+        provider: Union[Literal["auto"], PROVIDER_T, None] = None,
         token: Optional[str] = None,
         timeout: Optional[float] = None,
         headers: Optional[Dict[str, str]] = None,
@@ -219,7 +219,7 @@ def __init__(
                 )
 
         # Configure provider
-        self.provider = provider if provider is not None else "hf-inference"
+        self.provider = provider
 
         self.cookies = cookies
         self.timeout = timeout
@@ -364,12 +364,13 @@ async def audio_classification(
         ]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="audio-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="audio-classification", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=audio,
             parameters={"function_to_apply": function_to_apply, "top_k": top_k},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -413,12 +414,13 @@ async def audio_to_audio(
                     f.write(item.blob)
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="audio-to-audio")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="audio-to-audio", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=audio,
             parameters={},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -464,12 +466,13 @@ async def automatic_speech_recognition(
         "hello world"
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="automatic-speech-recognition")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="automatic-speech-recognition", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=audio,
             parameters={**(extra_body or {})},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -914,15 +917,15 @@ async def chat_completion(
         '{\n\n"activity": "bike ride",\n"animals": ["puppy", "cat", "raccoon"],\n"animals_seen": 3,\n"location": "park"}'
         ```
         """
-        # Get the provider helper
-        provider_helper = get_provider_helper(self.provider, task="conversational")
-
         # Since `chat_completion(..., model=xxx)` is also a payload parameter for the server, we need to handle 'model' differently.
         # `self.model` takes precedence over 'model' argument for building URL.
         # `model` takes precedence for payload value.
         model_id_or_url = self.model or model
         payload_model = model or self.model
 
+        # Get the provider helper
+        provider_helper = get_provider_helper(self.provider, task="conversational", model=payload_model)
+
         # Prepare the payload
         parameters = {
             "model": payload_model,
@@ -1025,8 +1028,9 @@ async def document_question_answering(
         [DocumentQuestionAnsweringOutputElement(answer='us-001', end=16, score=0.9999666213989258, start=16)]
         ```
         """
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="document-question-answering", model=model_id)
         inputs: Dict[str, Any] = {"question": question, "image": _b64_encode(image)}
-        provider_helper = get_provider_helper(self.provider, task="document-question-answering")
         request_parameters = provider_helper.prepare_request(
             inputs=inputs,
             parameters={
@@ -1040,7 +1044,7 @@ async def document_question_answering(
                 "word_boxes": word_boxes,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -1102,7 +1106,8 @@ async def feature_extraction(
         [ 0.28552425, -0.928395  , -1.2077185 , ...,  0.76810825, -2.1069427 ,  0.6236161 ]], dtype=float32)
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="feature-extraction")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="feature-extraction", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=text,
             parameters={
@@ -1112,7 +1117,7 @@ async def feature_extraction(
                 "truncation_direction": truncation_direction,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -1164,12 +1169,13 @@ async def fill_mask(
         ]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="fill-mask")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="fill-mask", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=text,
             parameters={"targets": targets, "top_k": top_k},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -1214,12 +1220,13 @@ async def image_classification(
         [ImageClassificationOutputElement(label='Blenheim spaniel', score=0.9779096841812134), ...]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="image-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="image-classification", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=image,
             parameters={"function_to_apply": function_to_apply, "top_k": top_k},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -1276,7 +1283,8 @@ async def image_segmentation(
         [ImageSegmentationOutputElement(score=0.989008, label='LABEL_184', mask=<PIL.PngImagePlugin.PngImageFile image mode=L size=400x300 at 0x7FDD2B129CC0>), ...]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="image-segmentation")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="image-segmentation", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=image,
             parameters={
@@ -1286,7 +1294,7 @@ async def image_segmentation(
                 "threshold": threshold,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -1353,7 +1361,8 @@ async def image_to_image(
         >>> image.save("tiger.jpg")
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="image-to-image")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="image-to-image", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=image,
             parameters={
@@ -1365,7 +1374,7 @@ async def image_to_image(
                 **kwargs,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -1405,12 +1414,13 @@ async def image_to_text(self, image: ContentT, *, model: Optional[str] = None) -
         'a dog laying on the grass next to a flower pot '
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="image-to-text")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="image-to-text", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=image,
             parameters={},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -1457,12 +1467,13 @@ async def object_detection(
         [ObjectDetectionOutputElement(score=0.9486683011054993, label='person', box=ObjectDetectionBoundingBox(xmin=59, ymin=39, xmax=420, ymax=510)), ...]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="object-detection")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="object-detection", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=image,
             parameters={"threshold": threshold},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -1531,7 +1542,8 @@ async def question_answering(
         QuestionAnsweringOutputElement(answer='Clara', end=16, score=0.9326565265655518, start=11)
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="question-answering")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="question-answering", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=None,
             parameters={
@@ -1545,7 +1557,7 @@ async def question_answering(
             },
             extra_payload={"question": question, "context": context},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -1594,13 +1606,14 @@ async def sentence_similarity(
         [0.7785726189613342, 0.45876261591911316, 0.2906220555305481]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="sentence-similarity")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="sentence-similarity", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs={"source_sentence": sentence, "sentences": other_sentences},
             parameters={},
             extra_payload={},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -1653,12 +1666,13 @@ async def summarization(
             "generate_parameters": generate_parameters,
             "truncation": truncation,
         }
-        provider_helper = get_provider_helper(self.provider, task="summarization")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="summarization", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=text,
             parameters=parameters,
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -1715,13 +1729,14 @@ async def table_question_answering(
         TableQuestionAnsweringOutputElement(answer='36542', coordinates=[[0, 1]], cells=['36542'], aggregator='AVERAGE')
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="table-question-answering")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="table-question-answering", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=None,
             parameters={"model": model, "padding": padding, "sequential": sequential, "truncation": truncation},
             extra_payload={"query": query, "table": table},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -1770,13 +1785,14 @@ async def tabular_classification(self, table: Dict[str, Any], *, model: Optional
         ["5", "5", "5"]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="tabular-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="tabular-classification", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=None,
             extra_payload={"table": table},
             parameters={},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -1820,13 +1836,14 @@ async def tabular_regression(self, table: Dict[str, Any], *, model: Optional[str
         [110, 120, 130]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="tabular-regression")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="tabular-regression", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=None,
             parameters={},
             extra_payload={"table": table},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -1876,7 +1893,8 @@ async def text_classification(
         ]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="text-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="text-classification", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=text,
             parameters={
@@ -1884,7 +1902,7 @@ async def text_classification(
                 "top_k": top_k,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -2326,13 +2344,14 @@ async def text_generation(
                     " Please pass `stream=False` as input."
                 )
 
-        provider_helper = get_provider_helper(self.provider, task="text-generation")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="text-generation", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=prompt,
             parameters=parameters,
             extra_payload={"stream": stream},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
 
@@ -2348,7 +2367,7 @@ async def text_generation(
                     prompt=prompt,
                     details=details,
                     stream=stream,
-                    model=model or self.model,
+                    model=model_id,
                     adapter_id=adapter_id,
                     best_of=best_of,
                     decoder_input_details=decoder_input_details,
@@ -2504,7 +2523,8 @@ async def text_to_image(
         >>> image.save("astronaut.png")
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="text-to-image")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="text-to-image", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=prompt,
             parameters={
@@ -2518,7 +2538,7 @@ async def text_to_image(
                 **(extra_body or {}),
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -2602,7 +2622,8 @@ async def text_to_video(
         ...     file.write(video)
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="text-to-video")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="text-to-video", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=prompt,
             parameters={
@@ -2614,7 +2635,7 @@ async def text_to_video(
                 **(extra_body or {}),
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -2800,7 +2821,8 @@ async def text_to_speech(
         ...     f.write(audio)
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="text-to-speech")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="text-to-speech", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=text,
             parameters={
@@ -2823,7 +2845,7 @@ async def text_to_speech(
                 **(extra_body or {}),
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -2890,7 +2912,8 @@ async def token_classification(
         ]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="token-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="token-classification", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=text,
             parameters={
@@ -2899,7 +2922,7 @@ async def token_classification(
                 "stride": stride,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -2977,7 +3000,8 @@ async def translation(
         if src_lang is None and tgt_lang is not None:
             raise ValueError("You cannot specify `tgt_lang` without specifying `src_lang`.")
 
-        provider_helper = get_provider_helper(self.provider, task="translation")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="translation", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=text,
             parameters={
@@ -2988,7 +3012,7 @@ async def translation(
                 "generate_parameters": generate_parameters,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -3041,12 +3065,13 @@ async def visual_question_answering(
         ]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="visual-question-answering")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="visual-question-answering", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=image,
             parameters={"top_k": top_k},
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
             extra_payload={"question": question, "image": _b64_encode(image)},
         )
@@ -3141,7 +3166,8 @@ async def zero_shot_classification(
         ]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="zero-shot-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="zero-shot-classification", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=text,
             parameters={
@@ -3150,7 +3176,7 @@ async def zero_shot_classification(
                 "hypothesis_template": hypothesis_template,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
@@ -3213,7 +3239,8 @@ async def zero_shot_image_classification(
         if len(candidate_labels) < 2:
             raise ValueError("You must specify at least 2 classes to compare.")
 
-        provider_helper = get_provider_helper(self.provider, task="zero-shot-image-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="zero-shot-image-classification", model=model_id)
         request_parameters = provider_helper.prepare_request(
             inputs=image,
             parameters={
@@ -3221,7 +3248,7 @@ async def zero_shot_image_classification(
                 "hypothesis_template": hypothesis_template,
             },
             headers=self.headers,
-            model=model or self.model,
+            model=model_id,
             api_key=self.token,
         )
         response = await self._inner_post(request_parameters)
diff --git a/src/huggingface_hub/inference/_providers/__init__.py b/src/huggingface_hub/inference/_providers/__init__.py
index c96b8700e1..ef860833ec 100644
--- a/src/huggingface_hub/inference/_providers/__init__.py
+++ b/src/huggingface_hub/inference/_providers/__init__.py
@@ -1,6 +1,8 @@
-from typing import Dict, Literal
+from typing import Dict, Literal, Optional, Union
 
-from ._common import TaskProviderHelper
+from huggingface_hub.utils import logging
+
+from ._common import TaskProviderHelper, _fetch_inference_provider_mapping
 from .black_forest_labs import BlackForestLabsTextToImageTask
 from .cerebras import CerebrasConversationalTask
 from .cohere import CohereConversationalTask
@@ -21,6 +23,9 @@
 from .together import TogetherConversationalTask, TogetherTextGenerationTask, TogetherTextToImageTask
 
 
+logger = logging.get_logger(__name__)
+
+
 PROVIDER_T = Literal[
     "black-forest-labs",
     "cerebras",
@@ -37,6 +42,8 @@
     "together",
 ]
 
+PROVIDER_OR_POLICY_T = Union[PROVIDER_T, Literal["auto"]]
+
 PROVIDERS: Dict[PROVIDER_T, Dict[str, TaskProviderHelper]] = {
     "black-forest-labs": {
         "text-to-image": BlackForestLabsTextToImageTask(),
@@ -118,24 +125,47 @@
 }
 
 
-def get_provider_helper(provider: PROVIDER_T, task: str) -> TaskProviderHelper:
+def get_provider_helper(
+    provider: Optional[PROVIDER_OR_POLICY_T], task: str, model: Optional[str]
+) -> TaskProviderHelper:
     """Get provider helper instance by name and task.
 
     Args:
-        provider (str): Name of the provider
-        task (str): Name of the task
-
+        provider (`str`, *optional*): name of the provider, or "auto" to automatically select the provider for the model.
+        task (`str`): Name of the task
+        model (`str`, *optional*): Name of the model
     Returns:
         TaskProviderHelper: Helper instance for the specified provider and task
 
     Raises:
         ValueError: If provider or task is not supported
     """
-    if provider not in PROVIDERS:
-        raise ValueError(f"Provider '{provider}' not supported. Available providers: {list(PROVIDERS.keys())}")
-    if task not in PROVIDERS[provider]:
+
+    if model is None and provider in (None, "auto"):
+        provider = "hf-inference"
+
+    if provider is None:
+        logger.info(
+            "Defaulting to 'auto' which will select the first provider available for the model, sorted by the user's order in https://hf.co/settings/inference-providers."
+        )
+        provider = "auto"
+
+    if provider == "auto":
+        if model is None:
+            raise ValueError("Specifying a model is required when provider is 'auto'")
+        provider_mapping = _fetch_inference_provider_mapping(model)
+        provider = next(iter(provider_mapping))
+
+    provider_tasks = PROVIDERS.get(provider)  # type: ignore
+    if provider_tasks is None:
+        raise ValueError(
+            f"Provider '{provider}' not supported. Available values: 'auto' or any provider from {list(PROVIDERS.keys())}."
+            "Passing 'auto' (default value) will automatically select the first provider available for the model, sorted "
+            "by the user's order in https://hf.co/settings/inference-providers."
+        )
+
+    if task not in provider_tasks:
         raise ValueError(
-            f"Task '{task}' not supported for provider '{provider}'. "
-            f"Available tasks: {list(PROVIDERS[provider].keys())}"
+            f"Task '{task}' not supported for provider '{provider}'. Available tasks: {list(provider_tasks.keys())}"
         )
-    return PROVIDERS[provider][task]
+    return provider_tasks[task]
diff --git a/tests/test_inference_client.py b/tests/test_inference_client.py
index f3e993e383..f45f98b398 100644
--- a/tests/test_inference_client.py
+++ b/tests/test_inference_client.py
@@ -342,7 +342,7 @@ def test_chat_completion_with_stream(self, client: InferenceClient):
         assert output[-1].choices[0].finish_reason == "length"
 
     def test_chat_completion_with_non_tgi(self) -> None:
-        client = InferenceClient()
+        client = InferenceClient(provider="hf-inference")
         output = client.chat_completion(
             messages=CHAT_COMPLETION_MESSAGES,
             model=CHAT_COMPLETE_NON_TGI_MODEL,
@@ -427,7 +427,7 @@ def test_chat_completion_unprocessable_entity(self) -> None:
 
         See https://github.com/huggingface/huggingface_hub/issues/2225.
         """
-        client = InferenceClient()
+        client = InferenceClient(provider="hf-inference")
         with pytest.raises(HfHubHTTPError):
             client.chat_completion(
                 "please output 'Observation'",  # Not a list of messages
@@ -816,7 +816,7 @@ def test_accept_header_image(
         bytes_to_image_mock: MagicMock,
     ) -> None:
         """Test that Accept: image/png header is set for image tasks."""
-        client = InferenceClient()
+        client = InferenceClient(provider="hf-inference")
 
         response = client.text_to_image("An astronaut riding a horse")
         assert response == bytes_to_image_mock.return_value
@@ -829,13 +829,13 @@ class TestListDeployedModels(TestBase):
     @expect_deprecation("list_deployed_models")
     @patch("huggingface_hub.inference._client.get_session")
     def test_list_deployed_models_main_frameworks_mock(self, get_session_mock: MagicMock) -> None:
-        InferenceClient().list_deployed_models()
+        InferenceClient(provider="hf-inference").list_deployed_models()
         assert len(get_session_mock.return_value.get.call_args_list) == len(constants.MAIN_INFERENCE_API_FRAMEWORKS)
 
     @expect_deprecation("list_deployed_models")
     @patch("huggingface_hub.inference._client.get_session")
     def test_list_deployed_models_all_frameworks_mock(self, get_session_mock: MagicMock) -> None:
-        InferenceClient().list_deployed_models("all")
+        InferenceClient(provider="hf-inference").list_deployed_models("all")
         assert len(get_session_mock.return_value.get.call_args_list) == len(constants.ALL_INFERENCE_API_FRAMEWORKS)
 
 
@@ -844,6 +844,7 @@ def test_list_deployed_models_all_frameworks_mock(self, get_session_mock: MagicM
 class TestOpenAICompatibility(TestBase):
     def test_base_url_and_api_key(self):
         client = InferenceClient(
+            provider="hf-inference",
             base_url="https://api-inference.huggingface.co/models/meta-llama/Meta-Llama-3-8B-Instruct",
             api_key=os.getenv("HF_INFERENCE_TEST_TOKEN"),
         )
@@ -859,7 +860,10 @@ def test_base_url_and_api_key(self):
         assert "1, 2, 3, 4, 5, 6, 7, 8, 9, 10" in output.choices[0].message.content
 
     def test_without_base_url(self):
-        client = InferenceClient(token=os.getenv("HF_INFERENCE_TEST_TOKEN"))
+        client = InferenceClient(
+            provider="hf-inference",
+            token=os.getenv("HF_INFERENCE_TEST_TOKEN"),
+        )
         output = client.chat.completions.create(
             model="meta-llama/Meta-Llama-3-8B-Instruct",
             messages=[
@@ -872,7 +876,10 @@ def test_without_base_url(self):
         assert "1, 2, 3, 4, 5, 6, 7, 8, 9, 10" in output.choices[0].message.content
 
     def test_with_stream_true(self):
-        client = InferenceClient(token=os.getenv("HF_INFERENCE_TEST_TOKEN"))
+        client = InferenceClient(
+            provider="hf-inference",
+            token=os.getenv("HF_INFERENCE_TEST_TOKEN"),
+        )
         output = client.chat.completions.create(
             model="meta-llama/Meta-Llama-3-8B-Instruct",
             messages=[
@@ -1031,8 +1038,11 @@ def test_resolve_chat_completion_url(model_url: str, expected_url: str):
 
 
 def test_pass_url_as_base_url():
-    client = InferenceClient(base_url="http://localhost:8082/v1/")
-    provider = get_provider_helper("hf-inference", "text-generation")
+    client = InferenceClient(
+        provider="hf-inference",
+        base_url="http://localhost:8082/v1/",
+    )
+    provider = get_provider_helper("hf-inference", "text-generation", "test-model")
     request = provider.prepare_request(
         inputs="The huggingface_hub library is ", parameters={}, headers={}, model=client.model, api_key=None
     )
diff --git a/tests/test_inference_providers.py b/tests/test_inference_providers.py
index 86e2c1ae10..ca90c9c1fd 100644
--- a/tests/test_inference_providers.py
+++ b/tests/test_inference_providers.py
@@ -7,6 +7,7 @@
 from pytest import LogCaptureFixture
 
 from huggingface_hub.inference._common import RequestParameters
+from huggingface_hub.inference._providers import PROVIDERS, get_provider_helper
 from huggingface_hub.inference._providers._common import (
     BaseConversationalTask,
     BaseTextGenerationTask,
@@ -28,21 +29,13 @@
     HFInferenceConversational,
     HFInferenceTask,
 )
-from huggingface_hub.inference._providers.hyperbolic import (
-    HyperbolicTextGenerationTask,
-    HyperbolicTextToImageTask,
-)
+from huggingface_hub.inference._providers.hyperbolic import HyperbolicTextGenerationTask, HyperbolicTextToImageTask
 from huggingface_hub.inference._providers.nebius import NebiusTextToImageTask
-from huggingface_hub.inference._providers.novita import (
-    NovitaConversationalTask,
-    NovitaTextGenerationTask,
-)
+from huggingface_hub.inference._providers.novita import NovitaConversationalTask, NovitaTextGenerationTask
 from huggingface_hub.inference._providers.openai import OpenAIConversationalTask
 from huggingface_hub.inference._providers.replicate import ReplicateTask, ReplicateTextToSpeechTask
 from huggingface_hub.inference._providers.sambanova import SambanovaConversationalTask
-from huggingface_hub.inference._providers.together import (
-    TogetherTextToImageTask,
-)
+from huggingface_hub.inference._providers.together import TogetherTextToImageTask
 
 from .testing_utils import assert_in_logs
 
@@ -925,3 +918,27 @@ def test_recursive_merge(dict1: Dict, dict2: Dict, expected: Dict):
     # does not mutate the inputs
     assert dict1 == initial_dict1
     assert dict2 == initial_dict2
+
+
+def test_get_provider_helper_auto(mocker):
+    """Test the 'auto' provider selection logic."""
+
+    mock_provider_a_helper = mocker.Mock(spec=TaskProviderHelper)
+    mock_provider_b_helper = mocker.Mock(spec=TaskProviderHelper)
+    PROVIDERS["provider-a"] = {"test-task": mock_provider_a_helper}
+    PROVIDERS["provider-b"] = {"test-task": mock_provider_b_helper}
+
+    mocker.patch(
+        "huggingface_hub.inference._providers._fetch_inference_provider_mapping",
+        return_value={
+            "provider-a": mocker.Mock(),
+            "provider-b": mocker.Mock(),
+        },
+    )
+    helper = get_provider_helper(provider="auto", task="test-task", model="test-model")
+
+    # The helper should be the one from provider-a
+    assert helper is mock_provider_a_helper
+
+    PROVIDERS.pop("provider-a", None)
+    PROVIDERS.pop("provider-b", None)


Reviews:
user: login: Wauplin
body: Made a high-level review. Looks good if it's really possible to retrieve provider ordering based on `/api/models/<model_id>` call.

Also in internal typing, I would make sure to distinguish between providers (cerebras, cohere, etc.) and policies (auto, maybe "cheapest", "fastest", etc.) in the future.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T16:17:18Z
user: login: julien-c
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T10:58:22Z
user: login: hanouticelina
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T11:18:46Z
user: login: hanouticelina
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T11:19:00Z
user: login: hanouticelina
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T11:24:24Z
user: login: Wauplin
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T14:33:00Z
user: login: julien-c
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T15:03:23Z
user: login: julien-c
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-23T15:03:37Z
user: login: hanouticelina
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T15:11:41Z
user: login: Wauplin
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T15:56:14Z
user: login: Wauplin
body: Pre-approving though I'm not a fan of removing auto-selection of models for hf-inference
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-23T16:00:10Z
user: login: Wauplin
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-24T05:30:48Z

Review comments:
diff_hunk: @@ -98,7 +98,7 @@ all the parameters available for each task.
 
 ### Using a specific provider
 
-If you want to use a specific provider, you can specify it when initializing the client. The default provider is "hf-inference", the Hugging Face Serverless Inference API. Refer to the [Supported providers and tasks](#supported-providers-and-tasks) section for a list of supported providers.
+If you want to use a specific provider, you can specify it when initializing the client. The default value is "auto" which will select the first of the providers available for the model, sorted by the user's order in https://hf.co/settings/inference-providers.. Refer to the [Supported providers and tasks](#supported-providers-and-tasks) section for a list of supported providers.
path: docs/source/en/guides/inference.md
user: login: Wauplin
body: ```suggestion
If you want to use a specific provider, you can specify it when initializing the client. The default value is "auto" which will select the first of the providers available for the model, sorted by the user's order in https://hf.co/settings/inference-providers. Refer to the [Supported providers and tasks](#supported-providers-and-tasks) section for a list of supported providers.
```
created_at: 2025-04-22T15:29:31Z
author_association: CONTRIBUTOR
diff_hunk: @@ -112,21 +112,19 @@ What if you want to use a specific model? You can specify it either as a paramet
 ```python
 >>> from huggingface_hub import InferenceClient
 # Initialize client for a specific model
->>> client = InferenceClient(model="prompthero/openjourney-v4")
+>>> client = InferenceClient(provider="together", model="meta-llama/Llama-3.1-8B-Instruct")
 >>> client.text_to_image(...)
 # Or use a generic client but pass your model as an argument
->>> client = InferenceClient()
->>> client.text_to_image(..., model="prompthero/openjourney-v4")
+>>> client = InferenceClient(provider="together")
+>>> client.text_to_image(..., model="meta-llama/Llama-3.1-8B-Instruct")
 ```
 
 <Tip>
 
-When using the Hugging Face Inference API (default provider), each task comes with a recommended model from the 200k+ models available on the Hub.
+When using the Hugging Face Inference API, each task comes with a recommended model from the 200k+ models available on the Hub.
path: docs/source/en/guides/inference.md
user: login: Wauplin
body: ```suggestion
When using the "hf-inference" provider, each task comes with a recommended model from the 1M+ models available on the Hub.
```
created_at: 2025-04-22T15:30:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -112,21 +112,19 @@ What if you want to use a specific model? You can specify it either as a paramet
 ```python
 >>> from huggingface_hub import InferenceClient
 # Initialize client for a specific model
->>> client = InferenceClient(model="prompthero/openjourney-v4")
+>>> client = InferenceClient(provider="together", model="meta-llama/Llama-3.1-8B-Instruct")
 >>> client.text_to_image(...)
 # Or use a generic client but pass your model as an argument
->>> client = InferenceClient()
->>> client.text_to_image(..., model="prompthero/openjourney-v4")
+>>> client = InferenceClient(provider="together")
+>>> client.text_to_image(..., model="meta-llama/Llama-3.1-8B-Instruct")
 ```
 
 <Tip>
 
-When using the Hugging Face Inference API (default provider), each task comes with a recommended model from the 200k+ models available on the Hub.
+When using the Hugging Face Inference API, each task comes with a recommended model from the 200k+ models available on the Hub.
 However, this recommendation can change over time, so it's best to explicitly set a model once you've decided which one to use.
 For third-party providers, you must always specify a model that is compatible with that provider.
 
-Visit the [Models](https://huggingface.co/models?inference=warm) page on the Hub to explore models available through the Inference API, or check the provider's documentation for their supported models.
path: docs/source/en/guides/inference.md
user: login: Wauplin
body: ```suggestion
Visit the [Models](https://huggingface.co/models?inference=warm) page on the Hub to explore models available through Inference Providers.
```
created_at: 2025-04-22T15:37:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -149,34 +147,33 @@ Note that you cannot specify both a URL and a provider - they are mutually exclu
 
 ### Authentication
 
-Authentication depends on which provider you are using:
+Authentication can be done in two ways:
path: docs/source/en/guides/inference.md
user: login: Wauplin
body: I would link to https://huggingface.co/docs/inference-providers/pricing#routed-requests-vs-direct-calls somewhere in these section for further details.
created_at: 2025-04-22T15:39:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -134,7 +134,7 @@ class InferenceClient:
             documentation for details). When passing a URL as `model`, the client will not append any suffix path to it.
         provider (`str`, *optional*):
             Name of the provider to use for inference. Can be `"black-forest-labs"`, `"cerebras"`, `"cohere"`, `"fal-ai"`, `"fireworks-ai"`, `"hf-inference"`, `"hyperbolic"`, `"nebius"`, `"novita"`, `"openai"`, `"replicate"`, "sambanova"` or `"together"`.
-            defaults to hf-inference (Hugging Face Serverless Inference API).
+            defaults to "auto" i.e. the first of the providers available for the model, sorted by the user's order in https://hf.co/settings/inference-providers.
path: src/huggingface_hub/inference/_client.py
user: login: Wauplin
body: On Line 168 `provider: Optional[PROVIDER_T] = None,` should be updated to

```py
provider: Union[Literal["auto"], PROVIDER_T, None] = None,
```

to be more explicit IMO
created_at: 2025-04-22T15:41:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -134,7 +134,7 @@ class InferenceClient:
             documentation for details). When passing a URL as `model`, the client will not append any suffix path to it.
         provider (`str`, *optional*):
             Name of the provider to use for inference. Can be `"black-forest-labs"`, `"cerebras"`, `"cohere"`, `"fal-ai"`, `"fireworks-ai"`, `"hf-inference"`, `"hyperbolic"`, `"nebius"`, `"novita"`, `"openai"`, `"replicate"`, "sambanova"` or `"together"`.
-            defaults to hf-inference (Hugging Face Serverless Inference API).
+            defaults to "auto" i.e. the first of the providers available for the model, sorted by the user's order in https://hf.co/settings/inference-providers.
path: src/huggingface_hub/inference/_client.py
user: login: Wauplin
body: ```suggestion
            Defaults to "auto" i.e. the first of the providers available for the model, sorted by the user's order in https://hf.co/settings/inference-providers.
```
created_at: 2025-04-22T15:58:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -332,12 +332,13 @@ def audio_classification(
         ]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="audio-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="audio-classification", model=model_id)  # type: ignore
path: src/huggingface_hub/inference/_client.py
user: login: Wauplin
body: Why the `# type: ignore` in all calls?

Is it because `model or self.model` can be None? If that's the case I would rather fix this in `get_provider_helper` directly => e.g. accept `model: Optional[str]` as input and have a `if (model or self.model) is None and provider == "auto": raise ValueError(...)` check afterwards
created_at: 2025-04-22T16:02:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -332,12 +332,13 @@ def audio_classification(
         ]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="audio-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="audio-classification", model=model_id)  # type: ignore
path: src/huggingface_hub/inference/_client.py
user: login: Wauplin
body: (in general I feel that `# type: ignore` is ok when we internally use something a bit hacky but in the case here it feels more like a normal behavior rather than a corner case)
created_at: 2025-04-22T16:03:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -35,6 +35,7 @@
     "replicate",
     "sambanova",
     "together",
+    "auto",
path: src/huggingface_hub/inference/_providers/__init__.py
user: login: Wauplin
body: I would define a `PROVIDER_OR_POLICY_T = Union[PROVIDER_T, Literal["auto"]` instead. This way we keep `PROVIDER_T` as the proper type describing providers
created_at: 2025-04-22T16:05:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -118,21 +119,30 @@
 }
 
 
-def get_provider_helper(provider: PROVIDER_T, task: str) -> TaskProviderHelper:
+def get_provider_helper(provider: PROVIDER_T, task: str, model: str) -> TaskProviderHelper:
     """Get provider helper instance by name and task.
 
     Args:
         provider (str): Name of the provider
         task (str): Name of the task
-
+        model (str): Name of the model
     Returns:
         TaskProviderHelper: Helper instance for the specified provider and task
 
     Raises:
         ValueError: If provider or task is not supported
     """
-    if provider not in PROVIDERS:
-        raise ValueError(f"Provider '{provider}' not supported. Available providers: {list(PROVIDERS.keys())}")
+    if provider != "auto" and provider not in PROVIDERS:
+        raise ValueError(
+            f"Provider '{provider}' not supported. Available providers: {['auto'] + list(PROVIDERS.keys())}, "
+            "'auto' will automatically select the first provider available for the model, sorted "
+            "by the user's order in https://hf.co/settings/inference-providers."
+        )
+
+    if provider == "auto":
+        provider_mapping = _fetch_inference_provider_mapping(model)
+        provider = next(iter(provider_mapping))
+
path: src/huggingface_hub/inference/_providers/__init__.py
user: login: Wauplin
body: ```suggestion
    if provider == "auto":
        provider_mapping = _fetch_inference_provider_mapping(model)
        provider = next(iter(provider_mapping))

    if provider not in PROVIDERS:
        raise ValueError(
            f"Provider '{provider}' not supported. Available values: 'auto' or any provider from {list(PROVIDERS.keys())}."
            "Passing 'auto' (default value) will automatically select the first provider available for the model, sorted "
            "by the user's order in https://hf.co/settings/inference-providers."
        )
```

better this way IMO to decorrelate the "policy" from the "is this provider supported" check. Kinda corner case but it's possible that a provider is listed on the Hub but not yet supported by the Python client.
created_at: 2025-04-22T16:08:49Z
author_association: CONTRIBUTOR
diff_hunk: @@ -228,7 +228,7 @@ def __init__(
                 )
 
         # Configure provider
-        self.provider = provider if provider is not None else "hf-inference"
+        self.provider = provider if provider is not None else "auto"
path: src/huggingface_hub/inference/_client.py
user: login: Wauplin
body: wondering if we should default to `None` here (meaning `self.provider` is of type `Union[Literal["auto"], PROVIDER_T, None]` and then in `get_provider_helper(self.provider, task=..., model=...)` we default to `"auto"`.

My reasoning is that in case of passing a URL as `model` (e.g. for local TGI or Inference Endpoint), the provider should default to `"hf-inference"` and not `"auto"` (which would fail downstream). If we delegate all the logic to `get_provider_helper`, we should be quite safe in term of corner cases.
created_at: 2025-04-22T16:15:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -35,6 +35,7 @@
     "replicate",
     "sambanova",
     "together",
+    "auto",
path: src/huggingface_hub/inference/_providers/__init__.py
user: login: julien-c
body: yes, good idea
created_at: 2025-04-23T10:58:22Z
author_association: MEMBER
diff_hunk: @@ -35,6 +35,7 @@
     "replicate",
     "sambanova",
     "together",
+    "auto",
path: src/huggingface_hub/inference/_providers/__init__.py
user: login: hanouticelina
body: definitely! addressed in [1f852d6](https://github.com/huggingface/huggingface_hub/pull/3011/commits/1f852d60bf9e719841d578b6f37697f09a69b45a)
created_at: 2025-04-23T11:18:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -332,12 +332,13 @@ def audio_classification(
         ]
         ```
         """
-        provider_helper = get_provider_helper(self.provider, task="audio-classification")
+        model_id = model or self.model
+        provider_helper = get_provider_helper(self.provider, task="audio-classification", model=model_id)  # type: ignore
path: src/huggingface_hub/inference/_client.py
user: login: hanouticelina
body: addressed in [1f852d6](https://github.com/huggingface/huggingface_hub/pull/3011/commits/1f852d60bf9e719841d578b6f37697f09a69b45a)
created_at: 2025-04-23T11:19:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -228,7 +228,7 @@ def __init__(
                 )
 
         # Configure provider
-        self.provider = provider if provider is not None else "hf-inference"
+        self.provider = provider if provider is not None else "auto"
path: src/huggingface_hub/inference/_client.py
user: login: hanouticelina
body: ah yes, that's indeed much better. addressed in [7e7ab1f](https://github.com/huggingface/huggingface_hub/pull/3011/commits/7e7ab1f10d757ac42563a4263504184a64c15b65)

created_at: 2025-04-23T11:24:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -874,15 +877,15 @@ def chat_completion(
         '{\n\n"activity": "bike ride",\n"animals": ["puppy", "cat", "raccoon"],\n"animals_seen": 3,\n"location": "park"}'
         ```
         """
-        # Get the provider helper
-        provider_helper = get_provider_helper(self.provider, task="conversational")
-
         # Since `chat_completion(..., model=xxx)` is also a payload parameter for the server, we need to handle 'model' differently.
         # `self.model` takes precedence over 'model' argument for building URL.
         # `model` takes precedence for payload value.
         model_id_or_url = self.model or model
         payload_model = model or self.model
 
+        # Get the provider helper
+        provider_helper = get_provider_helper(self.provider, task="conversational", model=payload_model)  # type: ignore
path: src/huggingface_hub/inference/_client.py
user: login: Wauplin
body: ```suggestion
        provider_helper = get_provider_helper(self.provider, task="conversational", model=payload_model)
```
created_at: 2025-04-23T13:41:28Z
author_association: CONTRIBUTOR
diff_hunk: @@ -118,24 +121,43 @@
 }
 
 
-def get_provider_helper(provider: PROVIDER_T, task: str) -> TaskProviderHelper:
+def get_provider_helper(
+    provider: Optional[PROVIDER_OR_POLICY_T], task: str, model: Optional[str]
+) -> TaskProviderHelper:
     """Get provider helper instance by name and task.
 
     Args:
-        provider (str): Name of the provider
-        task (str): Name of the task
-
+        provider (`PROVIDER_OR_POLICY_T`, *optional*): name of the provider, or "auto" to automatically select the provider for the model.
path: src/huggingface_hub/inference/_providers/__init__.py
user: login: Wauplin
body: ```suggestion
        provider (`str`, *optional*): name of the provider, or "auto" to automatically select the provider for the model.
```

`PROVIDER_OR_POLICY_T` is quite an internal / technical var rather than a user-understandable type so better to keep `str` IMO
created_at: 2025-04-23T13:44:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -118,24 +121,43 @@
 }
 
 
-def get_provider_helper(provider: PROVIDER_T, task: str) -> TaskProviderHelper:
+def get_provider_helper(
+    provider: Optional[PROVIDER_OR_POLICY_T], task: str, model: Optional[str]
+) -> TaskProviderHelper:
     """Get provider helper instance by name and task.
 
     Args:
-        provider (str): Name of the provider
-        task (str): Name of the task
-
+        provider (`PROVIDER_OR_POLICY_T`, *optional*): name of the provider, or "auto" to automatically select the provider for the model.
+        task (`str`): Name of the task
+        model (`str`, *optional*): Name of the model
     Returns:
         TaskProviderHelper: Helper instance for the specified provider and task
 
     Raises:
         ValueError: If provider or task is not supported
     """
+    if provider is None:
+        warnings.warn(
path: src/huggingface_hub/inference/_providers/__init__.py
user: login: Wauplin
body: I would not warn if provider is None. Defaulting to `"auto"` which default to any provider is the default way of using `InferenceClient` so we shouldn't raise warning IMO (which would indicate something is not done correctly by the user). We can add a `INFO` level log though

created_at: 2025-04-23T13:48:04Z
author_association: CONTRIBUTOR
diff_hunk: @@ -118,24 +121,43 @@
 }
 
 
-def get_provider_helper(provider: PROVIDER_T, task: str) -> TaskProviderHelper:
+def get_provider_helper(
+    provider: Optional[PROVIDER_OR_POLICY_T], task: str, model: Optional[str]
+) -> TaskProviderHelper:
     """Get provider helper instance by name and task.
 
     Args:
-        provider (str): Name of the provider
-        task (str): Name of the task
-
+        provider (`PROVIDER_OR_POLICY_T`, *optional*): name of the provider, or "auto" to automatically select the provider for the model.
+        task (`str`): Name of the task
+        model (`str`, *optional*): Name of the model
     Returns:
         TaskProviderHelper: Helper instance for the specified provider and task
 
     Raises:
         ValueError: If provider or task is not supported
     """
+    if provider is None:
+        warnings.warn(
+            "Defaulting to 'auto' which will select the first provider available for the model, sorted by the user's order in https://hf.co/settings/inference-providers.",
+            UserWarning,
+        )
+        provider = "auto"
+
+    if provider == "auto":
+        if model is None:
+            raise ValueError("Specifying a model is required when provider is 'auto'")
path: src/huggingface_hub/inference/_providers/__init__.py
user: login: Wauplin
body: I think that we should allow someone to do 

```py
InferenceClient().chat_completion(messages=...)
```

i.e. let the client auto-select both the provider and the model. This is already what we're doing which auto-selects `"hf-inference"` and the "best" recommended model that is available. If we raise a ValueError here, we won't be able to do that again.

What I suggest is a logic like this:

```py
if model is None and provider in (None, "auto"):
    provider = "hf-inference"
if provider is None:
    provider = "auto"

if provider = "auto":
    # default to preferred provider
```
created_at: 2025-04-23T13:53:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -118,24 +121,43 @@
 }
 
 
-def get_provider_helper(provider: PROVIDER_T, task: str) -> TaskProviderHelper:
+def get_provider_helper(
+    provider: Optional[PROVIDER_OR_POLICY_T], task: str, model: Optional[str]
+) -> TaskProviderHelper:
     """Get provider helper instance by name and task.
 
     Args:
-        provider (str): Name of the provider
-        task (str): Name of the task
-
+        provider (`PROVIDER_OR_POLICY_T`, *optional*): name of the provider, or "auto" to automatically select the provider for the model.
+        task (`str`): Name of the task
+        model (`str`, *optional*): Name of the model
     Returns:
         TaskProviderHelper: Helper instance for the specified provider and task
 
     Raises:
         ValueError: If provider or task is not supported
     """
+    if provider is None:
+        warnings.warn(
+            "Defaulting to 'auto' which will select the first provider available for the model, sorted by the user's order in https://hf.co/settings/inference-providers.",
+            UserWarning,
+        )
+        provider = "auto"
+
+    if provider == "auto":
+        if model is None:
+            raise ValueError("Specifying a model is required when provider is 'auto'")
path: src/huggingface_hub/inference/_providers/__init__.py
user: login: Wauplin
body: Work for a follow-up PR but I think it makes sense to make `model` optional **no matter the provider** and not only for `"hf-inference"`. Meaning that in the future doing

```py
InferenceClient().chat_completion(messages=...)
```  

should default to the current best model + default provider for it. And not default to best model served by "hf-inference" only. 
created_at: 2025-04-23T13:55:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -118,24 +121,43 @@
 }
 
 
-def get_provider_helper(provider: PROVIDER_T, task: str) -> TaskProviderHelper:
+def get_provider_helper(
+    provider: Optional[PROVIDER_OR_POLICY_T], task: str, model: Optional[str]
+) -> TaskProviderHelper:
     """Get provider helper instance by name and task.
 
     Args:
-        provider (str): Name of the provider
-        task (str): Name of the task
-
+        provider (`PROVIDER_OR_POLICY_T`, *optional*): name of the provider, or "auto" to automatically select the provider for the model.
+        task (`str`): Name of the task
+        model (`str`, *optional*): Name of the model
     Returns:
         TaskProviderHelper: Helper instance for the specified provider and task
 
     Raises:
         ValueError: If provider or task is not supported
     """
+    if provider is None:
+        warnings.warn(
+            "Defaulting to 'auto' which will select the first provider available for the model, sorted by the user's order in https://hf.co/settings/inference-providers.",
+            UserWarning,
+        )
+        provider = "auto"
+
+    if provider == "auto":
+        if model is None:
+            raise ValueError("Specifying a model is required when provider is 'auto'")
+        provider_mapping = _fetch_inference_provider_mapping(model)
+        provider = next(iter(provider_mapping))
+
     if provider not in PROVIDERS:
-        raise ValueError(f"Provider '{provider}' not supported. Available providers: {list(PROVIDERS.keys())}")
-    if task not in PROVIDERS[provider]:
         raise ValueError(
-            f"Task '{task}' not supported for provider '{provider}'. "
-            f"Available tasks: {list(PROVIDERS[provider].keys())}"
+            f"Provider '{provider}' not supported. Available values: 'auto' or any provider from {list(PROVIDERS.keys())}."
+            "Passing 'auto' (default value) will automatically select the first provider available for the model, sorted "
+            "by the user's order in https://hf.co/settings/inference-providers."
+        )
+    provider_tasks = PROVIDERS[provider]  # type: ignore
path: src/huggingface_hub/inference/_providers/__init__.py
user: login: Wauplin
body: (nit) can also do a 

```py
provider_tasks = PROVIDERS.get(provider)
if provider_tasks is None:
    raise ValueError(f"Provider '{provider}' not supported ...")

...
```

to avoid the `# type: ignore`
created_at: 2025-04-23T13:57:11Z
author_association: CONTRIBUTOR
diff_hunk: @@ -925,3 +918,28 @@ def test_recursive_merge(dict1: Dict, dict2: Dict, expected: Dict):
     # does not mutate the inputs
     assert dict1 == initial_dict1
     assert dict2 == initial_dict2
+
+
+def test_get_provider_helper_auto(mocker):
+    """Test the 'auto' provider selection logic."""
+
+    mock_provider_a_helper = mocker.Mock(spec=TaskProviderHelper)
+    mock_provider_b_helper = mocker.Mock(spec=TaskProviderHelper)
+    original_providers = PROVIDERS.copy()
+    PROVIDERS["provider-a"] = {"test-task": mock_provider_a_helper}
+    PROVIDERS["provider-b"] = {"test-task": mock_provider_b_helper}
+
+    mocker.patch(
+        "huggingface_hub.inference._providers._fetch_inference_provider_mapping",
+        return_value={
+            "provider-a": mocker.Mock(),
+            "provider-b": mocker.Mock(),
+        },
+    )
+    helper = get_provider_helper(provider="auto", task="test-task", model="test-model")
+
+    # The helper should be the one from provider-a
+    assert helper is mock_provider_a_helper
+
+    PROVIDERS.clear()
+    PROVIDERS.update(original_providers)
path: tests/test_inference_providers.py
user: login: Wauplin
body: ```suggestion
    PROVIDERS.pop("provider-a", None)
    PROVIDERS.pop("provider-b", None)
```

(nit) (just wary of concurrency issues if we clear + update since tests are done in parallel)
created_at: 2025-04-23T14:32:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -118,24 +121,43 @@
 }
 
 
-def get_provider_helper(provider: PROVIDER_T, task: str) -> TaskProviderHelper:
+def get_provider_helper(
+    provider: Optional[PROVIDER_OR_POLICY_T], task: str, model: Optional[str]
+) -> TaskProviderHelper:
     """Get provider helper instance by name and task.
 
     Args:
-        provider (str): Name of the provider
-        task (str): Name of the task
-
+        provider (`PROVIDER_OR_POLICY_T`, *optional*): name of the provider, or "auto" to automatically select the provider for the model.
+        task (`str`): Name of the task
+        model (`str`, *optional*): Name of the model
     Returns:
         TaskProviderHelper: Helper instance for the specified provider and task
 
     Raises:
         ValueError: If provider or task is not supported
     """
+    if provider is None:
+        warnings.warn(
+            "Defaulting to 'auto' which will select the first provider available for the model, sorted by the user's order in https://hf.co/settings/inference-providers.",
+            UserWarning,
+        )
+        provider = "auto"
+
+    if provider == "auto":
+        if model is None:
+            raise ValueError("Specifying a model is required when provider is 'auto'")
path: src/huggingface_hub/inference/_providers/__init__.py
user: login: julien-c
body: not 100% sure we really need to do auto-model selection to be honest, inference is very model-centric  
created_at: 2025-04-23T15:03:23Z
author_association: MEMBER
diff_hunk: @@ -118,24 +121,43 @@
 }
 
 
-def get_provider_helper(provider: PROVIDER_T, task: str) -> TaskProviderHelper:
+def get_provider_helper(
+    provider: Optional[PROVIDER_OR_POLICY_T], task: str, model: Optional[str]
+) -> TaskProviderHelper:
     """Get provider helper instance by name and task.
 
     Args:
-        provider (str): Name of the provider
-        task (str): Name of the task
-
+        provider (`PROVIDER_OR_POLICY_T`, *optional*): name of the provider, or "auto" to automatically select the provider for the model.
+        task (`str`): Name of the task
+        model (`str`, *optional*): Name of the model
     Returns:
         TaskProviderHelper: Helper instance for the specified provider and task
 
     Raises:
         ValueError: If provider or task is not supported
     """
+    if provider is None:
+        warnings.warn(
+            "Defaulting to 'auto' which will select the first provider available for the model, sorted by the user's order in https://hf.co/settings/inference-providers.",
+            UserWarning,
+        )
+        provider = "auto"
+
+    if provider == "auto":
+        if model is None:
+            raise ValueError("Specifying a model is required when provider is 'auto'")
path: src/huggingface_hub/inference/_providers/__init__.py
user: login: julien-c
body: (i agree with all your other comments on this PR though)
created_at: 2025-04-23T15:03:36Z
author_association: MEMBER
diff_hunk: @@ -118,24 +121,43 @@
 }
 
 
-def get_provider_helper(provider: PROVIDER_T, task: str) -> TaskProviderHelper:
+def get_provider_helper(
+    provider: Optional[PROVIDER_OR_POLICY_T], task: str, model: Optional[str]
+) -> TaskProviderHelper:
     """Get provider helper instance by name and task.
 
     Args:
-        provider (str): Name of the provider
-        task (str): Name of the task
-
+        provider (`PROVIDER_OR_POLICY_T`, *optional*): name of the provider, or "auto" to automatically select the provider for the model.
+        task (`str`): Name of the task
+        model (`str`, *optional*): Name of the model
     Returns:
         TaskProviderHelper: Helper instance for the specified provider and task
 
     Raises:
         ValueError: If provider or task is not supported
     """
+    if provider is None:
+        warnings.warn(
+            "Defaulting to 'auto' which will select the first provider available for the model, sorted by the user's order in https://hf.co/settings/inference-providers.",
+            UserWarning,
+        )
+        provider = "auto"
+
+    if provider == "auto":
+        if model is None:
+            raise ValueError("Specifying a model is required when provider is 'auto'")
+        provider_mapping = _fetch_inference_provider_mapping(model)
+        provider = next(iter(provider_mapping))
+
     if provider not in PROVIDERS:
-        raise ValueError(f"Provider '{provider}' not supported. Available providers: {list(PROVIDERS.keys())}")
-    if task not in PROVIDERS[provider]:
         raise ValueError(
-            f"Task '{task}' not supported for provider '{provider}'. "
-            f"Available tasks: {list(PROVIDERS[provider].keys())}"
+            f"Provider '{provider}' not supported. Available values: 'auto' or any provider from {list(PROVIDERS.keys())}."
+            "Passing 'auto' (default value) will automatically select the first provider available for the model, sorted "
+            "by the user's order in https://hf.co/settings/inference-providers."
+        )
+    provider_tasks = PROVIDERS[provider]  # type: ignore
path: src/huggingface_hub/inference/_providers/__init__.py
user: login: hanouticelina
body: I think you still need the `# type: ignore` for `provider_tasks = PROVIDERS.get(provider)`. I'm getting the same "a Argument 1 to "get" of "dict" has incompatible type error" from mypy
created_at: 2025-04-23T15:11:41Z
author_association: CONTRIBUTOR
diff_hunk: @@ -118,24 +121,43 @@
 }
 
 
-def get_provider_helper(provider: PROVIDER_T, task: str) -> TaskProviderHelper:
+def get_provider_helper(
+    provider: Optional[PROVIDER_OR_POLICY_T], task: str, model: Optional[str]
+) -> TaskProviderHelper:
     """Get provider helper instance by name and task.
 
     Args:
-        provider (str): Name of the provider
-        task (str): Name of the task
-
+        provider (`PROVIDER_OR_POLICY_T`, *optional*): name of the provider, or "auto" to automatically select the provider for the model.
+        task (`str`): Name of the task
+        model (`str`, *optional*): Name of the model
     Returns:
         TaskProviderHelper: Helper instance for the specified provider and task
 
     Raises:
         ValueError: If provider or task is not supported
     """
+    if provider is None:
+        warnings.warn(
+            "Defaulting to 'auto' which will select the first provider available for the model, sorted by the user's order in https://hf.co/settings/inference-providers.",
+            UserWarning,
+        )
+        provider = "auto"
+
+    if provider == "auto":
+        if model is None:
+            raise ValueError("Specifying a model is required when provider is 'auto'")
+        provider_mapping = _fetch_inference_provider_mapping(model)
+        provider = next(iter(provider_mapping))
+
     if provider not in PROVIDERS:
-        raise ValueError(f"Provider '{provider}' not supported. Available providers: {list(PROVIDERS.keys())}")
-    if task not in PROVIDERS[provider]:
         raise ValueError(
-            f"Task '{task}' not supported for provider '{provider}'. "
-            f"Available tasks: {list(PROVIDERS[provider].keys())}"
+            f"Provider '{provider}' not supported. Available values: 'auto' or any provider from {list(PROVIDERS.keys())}."
+            "Passing 'auto' (default value) will automatically select the first provider available for the model, sorted "
+            "by the user's order in https://hf.co/settings/inference-providers."
+        )
+    provider_tasks = PROVIDERS[provider]  # type: ignore
path: src/huggingface_hub/inference/_providers/__init__.py
user: login: Wauplin
body: ok then... I guess it's annoying because of the "auto" literal :shrug: 
created_at: 2025-04-23T15:56:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -118,24 +121,43 @@
 }
 
 
-def get_provider_helper(provider: PROVIDER_T, task: str) -> TaskProviderHelper:
+def get_provider_helper(
+    provider: Optional[PROVIDER_OR_POLICY_T], task: str, model: Optional[str]
+) -> TaskProviderHelper:
     """Get provider helper instance by name and task.
 
     Args:
-        provider (str): Name of the provider
-        task (str): Name of the task
-
+        provider (`PROVIDER_OR_POLICY_T`, *optional*): name of the provider, or "auto" to automatically select the provider for the model.
+        task (`str`): Name of the task
+        model (`str`, *optional*): Name of the model
     Returns:
         TaskProviderHelper: Helper instance for the specified provider and task
 
     Raises:
         ValueError: If provider or task is not supported
     """
+    if provider is None:
+        warnings.warn(
+            "Defaulting to 'auto' which will select the first provider available for the model, sorted by the user's order in https://hf.co/settings/inference-providers.",
+            UserWarning,
+        )
+        provider = "auto"
+
+    if provider == "auto":
+        if model is None:
+            raise ValueError("Specifying a model is required when provider is 'auto'")
path: src/huggingface_hub/inference/_providers/__init__.py
user: login: Wauplin
body: Fine to not auto-select the model for all providers then but still think it's good to do it for hf-inference at least to not introduce a breaking change
created_at: 2025-04-23T15:58:46Z
author_association: CONTRIBUTOR

PR comments:
user: login: HuggingFaceDocBuilderDev
created_at: 2025-04-16T17:57:45Z
body: The docs for this PR live [here](https://moon-ci-docs.huggingface.co/docs/huggingface_hub/pr_3011). All of your documentation changes will be reflected on that endpoint. The docs are available until 30 days after the last update.
author_association: NONE
user: login: hanouticelina
created_at: 2025-04-24T13:50:53Z
body: failing tests are unrelated (staging environment is currently mostly down) so let's merge!
author_association: CONTRIBUTOR
user: login: julien-c
created_at: 2025-04-25T07:34:11Z
body: Yay! this is going to be big
author_association: MEMBER