Repository: huggingface/huggingface_hub
Pull request #1770 (2023-10-20 17:24:48)

Adding `InferenceClient.get_recommended_model`
Closes https://github.com/huggingface/huggingface_hub/issues/1767

HuggingFaceDocBuilderDev: The docs for this PR live [here](https://moon-ci-docs.huggingface.co/docs/huggingface_hub/pr_1770). All of your documentation changes will be reflected on that endpoint.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1770?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface) Report
Attention: `1 lines` in your changes are missing coverage. Please review.
> Comparison is base [(`3a99199`)](https://app.codecov.io/gh/huggingface/huggingface_hub/commit/3a9919966391f7fd91066fe760c11bb5ea5059e5?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface) 82.60% compared to head [(`988c513`)](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1770?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface) 82.17%.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main    #1770      +/-   ##
==========================================
- Coverage   82.60%   82.17%   -0.44%     
==========================================
  Files          62       62              
  Lines        7302     7310       +8     
==========================================
- Hits         6032     6007      -25     
- Misses       1270     1303      +33     
```


| [Files](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1770?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface) | Coverage Δ | |
|---|---|---|
| [src/huggingface\_hub/inference/\_client.py](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1770?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface#diff-c3JjL2h1Z2dpbmdmYWNlX2h1Yi9pbmZlcmVuY2UvX2NsaWVudC5weQ==) | `81.98% <100.00%> (+0.47%)` | :arrow_up: |
| [src/huggingface\_hub/inference/\_common.py](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1770?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface#diff-c3JjL2h1Z2dpbmdmYWNlX2h1Yi9pbmZlcmVuY2UvX2NvbW1vbi5weQ==) | `90.98% <ø> (-0.43%)` | :arrow_down: |
| [...gingface\_hub/inference/\_generated/\_async\_client.py](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1770?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface#diff-c3JjL2h1Z2dpbmdmYWNlX2h1Yi9pbmZlcmVuY2UvX2dlbmVyYXRlZC9fYXN5bmNfY2xpZW50LnB5) | `60.95% <87.50%> (+0.60%)` | :arrow_up: |

... and [5 files with indirect coverage changes](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1770/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface)


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1770?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface).


Patch:
diff --git a/src/huggingface_hub/inference/_client.py b/src/huggingface_hub/inference/_client.py
index 90e0552a..b4270971 100644
--- a/src/huggingface_hub/inference/_client.py
+++ b/src/huggingface_hub/inference/_client.py
@@ -63,7 +63,7 @@ from huggingface_hub.inference._common import (
     _bytes_to_dict,
     _bytes_to_image,
     _bytes_to_list,
-    _get_recommended_model,
+    _fetch_recommended_models,
     _import_numpy,
     _is_tgi_server,
     _open_as_binary,
@@ -1870,7 +1870,12 @@ class InferenceClient:
                     "You must specify at least a model (repo_id or URL) or a task, either when instantiating"
                     " `InferenceClient` or when making a request."
                 )
-            model = _get_recommended_model(task)
+            model = self.get_recommended_model(task)
+            logger.info(
+                f"Using recommended model {model} for task {task}. Note that it is"
+                f" encouraged to explicitly set `model='{model}'` as the recommended"
+                " models list might get updated without prior notice."
+            )
 
         # Compute InferenceAPI url
         return (
@@ -1881,6 +1886,30 @@ class InferenceClient:
             else f"{INFERENCE_ENDPOINT}/models/{model}"
         )
 
+    @staticmethod
+    def get_recommended_model(task: str) -> str:
+        """
+        Get the model Hugging Face recommends for the input task.
+
+        Args:
+            task (`str`):
+                The Hugging Face task to get which model Hugging Face recommends.
+                All available tasks can be found [here](https://huggingface.co/tasks).
+
+        Returns:
+            `str`: Name of the model recommended for the input task.
+
+        Raises:
+            `ValueError`: If Hugging Face has no recommendation for the input task.
+        """
+        model = _fetch_recommended_models().get(task)
+        if model is None:
+            raise ValueError(
+                f"Task {task} has no recommended model. Please specify a model"
+                " explicitly. Visit https://huggingface.co/tasks for more info."
+            )
+        return model
+
     def get_model_status(self, model: Optional[str] = None) -> ModelStatus:
         """
         Get the status of a model hosted on the Inference API.
diff --git a/src/huggingface_hub/inference/_common.py b/src/huggingface_hub/inference/_common.py
index 206c70ca..9e147a5c 100644
--- a/src/huggingface_hub/inference/_common.py
+++ b/src/huggingface_hub/inference/_common.py
@@ -140,20 +140,6 @@ def _import_pil_image():
 _RECOMMENDED_MODELS: Optional[Dict[str, Optional[str]]] = None
 
 
-def _get_recommended_model(task: str) -> str:
-    model = _fetch_recommended_models().get(task)
-    if model is None:
-        raise ValueError(
-            f"Task {task} has no recommended task. Please specify a model explicitly. Visit"
-            " https://huggingface.co/tasks for more info."
-        )
-    logger.info(
-        f"Using recommended model {model} for task {task}. Note that it is encouraged to explicitly set"
-        f" `model='{model}'` as the recommended models list might get updated without prior notice."
-    )
-    return model
-
-
 def _fetch_recommended_models() -> Dict[str, Optional[str]]:
     global _RECOMMENDED_MODELS
     if _RECOMMENDED_MODELS is None:
diff --git a/src/huggingface_hub/inference/_generated/_async_client.py b/src/huggingface_hub/inference/_generated/_async_client.py
index dcb29783..c5b5daec 100644
--- a/src/huggingface_hub/inference/_generated/_async_client.py
+++ b/src/huggingface_hub/inference/_generated/_async_client.py
@@ -49,7 +49,7 @@ from huggingface_hub.inference._common import (
     _bytes_to_dict,
     _bytes_to_image,
     _bytes_to_list,
-    _get_recommended_model,
+    _fetch_recommended_models,
     _import_numpy,
     _is_tgi_server,
     _open_as_binary,
@@ -1898,7 +1898,12 @@ class AsyncInferenceClient:
                     "You must specify at least a model (repo_id or URL) or a task, either when instantiating"
                     " `InferenceClient` or when making a request."
                 )
-            model = _get_recommended_model(task)
+            model = self.get_recommended_model(task)
+            logger.info(
+                f"Using recommended model {model} for task {task}. Note that it is"
+                f" encouraged to explicitly set `model='{model}'` as the recommended"
+                " models list might get updated without prior notice."
+            )
 
         # Compute InferenceAPI url
         return (
@@ -1909,6 +1914,30 @@ class AsyncInferenceClient:
             else f"{INFERENCE_ENDPOINT}/models/{model}"
         )
 
+    @staticmethod
+    def get_recommended_model(task: str) -> str:
+        """
+        Get the model Hugging Face recommends for the input task.
+
+        Args:
+            task (`str`):
+                The Hugging Face task to get which model Hugging Face recommends.
+                All available tasks can be found [here](https://huggingface.co/tasks).
+
+        Returns:
+            `str`: Name of the model recommended for the input task.
+
+        Raises:
+            `ValueError`: If Hugging Face has no recommendation for the input task.
+        """
+        model = _fetch_recommended_models().get(task)
+        if model is None:
+            raise ValueError(
+                f"Task {task} has no recommended model. Please specify a model"
+                " explicitly. Visit https://huggingface.co/tasks for more info."
+            )
+        return model
+
     async def get_model_status(self, model: Optional[str] = None) -> ModelStatus:
         """
         Get the status of a model hosted on the Inference API.


Test patch:
diff --git a/tests/test_inference_client.py b/tests/test_inference_client.py
index 084e91a1..d919fc4f 100644
--- a/tests/test_inference_client.py
+++ b/tests/test_inference_client.py
@@ -67,7 +67,7 @@ class InferenceClientTest(unittest.TestCase):
 
 @pytest.mark.vcr
 @with_production_testing
-@patch("huggingface_hub.inference._common._fetch_recommended_models", lambda: _RECOMMENDED_MODELS_FOR_VCR)
+@patch("huggingface_hub.inference._client._fetch_recommended_models", lambda: _RECOMMENDED_MODELS_FOR_VCR)
 class InferenceClientVCRTest(InferenceClientTest):
     """
     Test for the main tasks implemented in InferenceClient. Since Inference API can be flaky, we use VCR.py and
@@ -155,6 +155,14 @@ class InferenceClientVCRTest(InferenceClientTest):
             {"score", "sequence", "token", "token_str"},
         )
 
+    def test_get_recommended_model_has_recommendation(self) -> None:
+        assert self.client.get_recommended_model("feature-extraction") == "facebook/bart-base"
+        assert self.client.get_recommended_model("translation") == "t5-small"
+
+    def test_get_recommended_model_no_recommendation(self) -> None:
+        with pytest.raises(ValueError):
+            self.client.get_recommended_model("text-generation")
+
     def test_image_classification(self) -> None:
         output = self.client.image_classification(self.image_file)
         self.assertIsInstance(output, list)