Repository: huggingface/huggingface_hub
Pull request #1622 (2023-08-24 01:26:54)

Add list_deployed_models to inference client
**Add list_deployed_models to HuggingFace🤗 Hub**

References https://github.com/huggingface/huggingface_hub/issues/1557

**Key Features**

- Modifies _client.py to add list_deployed_models functionality. Provides a list of model_id with the framework for a list of tasks provided. 

HuggingFaceDocBuilderDev: The docs for this PR live [here](https://moon-ci-docs.huggingface.co/docs/huggingface_hub/pr_1622). All of your documentation changes will be reflected on that endpoint.

Patch:
diff --git a/src/huggingface_hub/constants.py b/src/huggingface_hub/constants.py
index e08b6a5a..fec3285c 100644
--- a/src/huggingface_hub/constants.py
+++ b/src/huggingface_hub/constants.py
@@ -129,3 +129,41 @@ HF_HUB_ENABLE_HF_TRANSFER: bool = _is_true(os.environ.get("HF_HUB_ENABLE_HF_TRAN
 HF_HUB_LOCAL_DIR_AUTO_SYMLINK_THRESHOLD: int = (
     _as_int(os.environ.get("HF_HUB_LOCAL_DIR_AUTO_SYMLINK_THRESHOLD")) or 5 * 1024 * 1024
 )
+
+# List frameworks that are handled by the InferenceAPI service. Useful to scan endpoints and check which models are
+# deployed and running. Since 95% of the models are using the top 4 frameworks listed below, we scan only those by
+# default. We still keep the full list of supported frameworks in case we want to scan all of them.
+MAIN_INFERENCE_API_FRAMEWORKS = [
+    "diffusers",
+    "sentence-transformers",
+    "text-generation-inference",
+    "transformers",
+]
+
+ALL_INFERENCE_API_FRAMEWORKS = MAIN_INFERENCE_API_FRAMEWORKS + [
+    "adapter-transformers",
+    "allennlp",
+    "asteroid",
+    "bertopic",
+    "doctr",
+    "espnet",
+    "fairseq",
+    "fastai",
+    "fasttext",
+    "flair",
+    "generic",
+    "k2",
+    "keras",
+    "mindspore",
+    "nemo",
+    "open_clip",
+    "paddlenlp",
+    "peft",
+    "pyannote-audio",
+    "sklearn",
+    "spacy",
+    "span-marker",
+    "speechbrain",
+    "stanza",
+    "timm",
+]
diff --git a/src/huggingface_hub/inference/_client.py b/src/huggingface_hub/inference/_client.py
index c2f59c47..4d8c1b56 100644
--- a/src/huggingface_hub/inference/_client.py
+++ b/src/huggingface_hub/inference/_client.py
@@ -52,7 +52,7 @@ from typing import (
 from requests import HTTPError
 from requests.structures import CaseInsensitiveDict
 
-from huggingface_hub.constants import INFERENCE_ENDPOINT
+from huggingface_hub.constants import ALL_INFERENCE_API_FRAMEWORKS, INFERENCE_ENDPOINT, MAIN_INFERENCE_API_FRAMEWORKS
 from huggingface_hub.inference._common import (
     TASKS_EXPECTING_IMAGES,
     ContentT,
@@ -507,7 +507,7 @@ class InferenceClient:
         """
         response = self.post(json={"inputs": text}, model=model, task="feature-extraction")
         np = _import_numpy()
-        return np.array(_bytes_to_dict(response)[0], dtype="float32")
+        return np.array(_bytes_to_dict(response), dtype="float32")
 
     def fill_mask(self, text: str, *, model: Optional[str] = None) -> List[FillMaskOutput]:
         """
@@ -756,6 +756,81 @@ class InferenceClient:
         response = self.post(data=image, model=model, task="image-to-text")
         return _bytes_to_dict(response)[0]["generated_text"]
 
+    def list_deployed_models(
+        self, frameworks: Union[None, str, Literal["all"], List[str]] = None
+    ) -> Dict[str, List[str]]:
+        """
+        List models currently deployed on the Inference API service.
+
+        This helper checks deployed models framework by framework. By default, it will check the 4 main frameworks that
+        are supported and account for 95% of the hosted models. However, if you want a complete list of models you can
+        specify `frameworks="all"` as input. Alternatively, if you know before-hand which framework you are interested
+        in, you can also restrict to search to this one (e.g. `frameworks="text-generation-inference"`). The more
+        frameworks are checked, the more time it will take.
+
+        <Tip>
+
+        This endpoint is mostly useful for discoverability. If you already know which model you want to use and want to
+        check its availability, you can directly use [`~InferenceClient.get_model_status`].
+
+        </Tip>
+
+        Args:
+            frameworks (`Literal["all"]` or `List[str]` or `str`, *optional*):
+                The frameworks to filter on. By default only a subset of the available frameworks are tested. If set to
+                "all", all available frameworks will be tested. It is also possible to provide a single framework or a
+                custom set of frameworks to check.
+
+        Returns:
+            `Dict[str, List[str]]`: A dictionary mapping task names to a sorted list of model IDs.
+
+        Example:
+        ```python
+        >>> from huggingface_hub import InferenceClient
+        >>> client = InferenceClient()
+
+        # Discover zero-shot-classification models currently deployed
+        >>> models = client.list_deployed_models()
+        >>> models["zero-shot-classification"]
+        ['Narsil/deberta-large-mnli-zero-cls', 'facebook/bart-large-mnli', ...]
+
+        # List from only 1 framework
+        >>> client.list_deployed_models("text-generation-inference")
+        {'text-generation': ['bigcode/starcoder', 'meta-llama/Llama-2-70b-chat-hf', ...], ...}
+        ```
+        """
+        # Resolve which frameworks to check
+        if frameworks is None:
+            frameworks = MAIN_INFERENCE_API_FRAMEWORKS
+        elif frameworks == "all":
+            frameworks = ALL_INFERENCE_API_FRAMEWORKS
+        elif isinstance(frameworks, str):
+            frameworks = [frameworks]
+        frameworks = list(set(frameworks))
+
+        # Fetch them iteratively
+        models_by_task: Dict[str, List[str]] = {}
+
+        def _unpack_response(framework: str, items: List[Dict]) -> None:
+            for model in items:
+                if framework == "sentence-transformers":
+                    # Model running with the `sentence-transformers` framework can work with both tasks even if not
+                    # branded as such in the API response
+                    models_by_task.setdefault("feature-extraction", []).append(model["model_id"])
+                    models_by_task.setdefault("sentence-similarity", []).append(model["model_id"])
+                else:
+                    models_by_task.setdefault(model["task"], []).append(model["model_id"])
+
+        for framework in frameworks:
+            response = get_session().get(f"{INFERENCE_ENDPOINT}/framework/{framework}", headers=self.headers)
+            hf_raise_for_status(response)
+            _unpack_response(framework, response.json())
+
+        # Sort alphabetically for discoverability and return
+        for task, models in models_by_task.items():
+            models_by_task[task] = sorted(set(models), key=lambda x: x.lower())
+        return models_by_task
+
     def object_detection(
         self,
         image: ContentT,
@@ -1800,7 +1875,14 @@ class InferenceClient:
 
     def get_model_status(self, model: Optional[str] = None) -> ModelStatus:
         """
-        A function which returns the status of a specific model, from the Inference API.
+        Get the status of a model hosted on the Inference API.
+
+        <Tip>
+
+        This endpoint is mostly useful when you already know which model you want to use and want to check its
+        availability. If you want to discover already deployed models, you should rather use [`~InferenceClient.list_deployed_models`].
+
+        </Tip>
 
         Args:
             model (`str`, *optional*):
diff --git a/src/huggingface_hub/inference/_generated/_async_client.py b/src/huggingface_hub/inference/_generated/_async_client.py
index 43451ee6..5c2df968 100644
--- a/src/huggingface_hub/inference/_generated/_async_client.py
+++ b/src/huggingface_hub/inference/_generated/_async_client.py
@@ -36,7 +36,7 @@ from typing import (
 
 from requests.structures import CaseInsensitiveDict
 
-from huggingface_hub.constants import INFERENCE_ENDPOINT
+from huggingface_hub.constants import ALL_INFERENCE_API_FRAMEWORKS, INFERENCE_ENDPOINT, MAIN_INFERENCE_API_FRAMEWORKS
 from huggingface_hub.inference._common import (
     TASKS_EXPECTING_IMAGES,
     ContentT,
@@ -509,7 +509,7 @@ class AsyncInferenceClient:
         """
         response = await self.post(json={"inputs": text}, model=model, task="feature-extraction")
         np = _import_numpy()
-        return np.array(_bytes_to_dict(response)[0], dtype="float32")
+        return np.array(_bytes_to_dict(response), dtype="float32")
 
     async def fill_mask(self, text: str, *, model: Optional[str] = None) -> List[FillMaskOutput]:
         """
@@ -763,6 +763,87 @@ class AsyncInferenceClient:
         response = await self.post(data=image, model=model, task="image-to-text")
         return _bytes_to_dict(response)[0]["generated_text"]
 
+    async def list_deployed_models(
+        self, frameworks: Union[None, str, Literal["all"], List[str]] = None
+    ) -> Dict[str, List[str]]:
+        """
+        List models currently deployed on the Inference API service.
+
+        This helper checks deployed models framework by framework. By default, it will check the 4 main frameworks that
+        are supported and account for 95% of the hosted models. However, if you want a complete list of models you can
+        specify `frameworks="all"` as input. Alternatively, if you know before-hand which framework you are interested
+        in, you can also restrict to search to this one (e.g. `frameworks="text-generation-inference"`). The more
+        frameworks are checked, the more time it will take.
+
+        <Tip>
+
+        This endpoint is mostly useful for discoverability. If you already know which model you want to use and want to
+        check its availability, you can directly use [`~InferenceClient.get_model_status`].
+
+        </Tip>
+
+        Args:
+            frameworks (`Literal["all"]` or `List[str]` or `str`, *optional*):
+                The frameworks to filter on. By default only a subset of the available frameworks are tested. If set to
+                "all", all available frameworks will be tested. It is also possible to provide a single framework or a
+                custom set of frameworks to check.
+
+        Returns:
+            `Dict[str, List[str]]`: A dictionary mapping task names to a sorted list of model IDs.
+
+        Example:
+        ```py
+        # Must be run in an async contextthon
+        >>> from huggingface_hub import AsyncInferenceClient
+        >>> client = AsyncInferenceClient()
+
+        # Discover zero-shot-classification models currently deployed
+        >>> models = await client.list_deployed_models()
+        >>> models["zero-shot-classification"]
+        ['Narsil/deberta-large-mnli-zero-cls', 'facebook/bart-large-mnli', ...]
+
+        # List from only 1 framework
+        >>> await client.list_deployed_models("text-generation-inference")
+        {'text-generation': ['bigcode/starcoder', 'meta-llama/Llama-2-70b-chat-hf', ...], ...}
+        ```
+        """
+        # Resolve which frameworks to check
+        if frameworks is None:
+            frameworks = MAIN_INFERENCE_API_FRAMEWORKS
+        elif frameworks == "all":
+            frameworks = ALL_INFERENCE_API_FRAMEWORKS
+        elif isinstance(frameworks, str):
+            frameworks = [frameworks]
+        frameworks = list(set(frameworks))
+
+        # Fetch them iteratively
+        models_by_task: Dict[str, List[str]] = {}
+
+        def _unpack_response(framework: str, items: List[Dict]) -> None:
+            for model in items:
+                if framework == "sentence-transformers":
+                    # Model running with the `sentence-transformers` framework can work with both tasks even if not
+                    # branded as such in the API response
+                    models_by_task.setdefault("feature-extraction", []).append(model["model_id"])
+                    models_by_task.setdefault("sentence-similarity", []).append(model["model_id"])
+                else:
+                    models_by_task.setdefault(model["task"], []).append(model["model_id"])
+
+        async def _fetch_framework(framework: str) -> None:
+            async with _import_aiohttp().ClientSession(headers=self.headers) as client:
+                response = await client.get(f"{INFERENCE_ENDPOINT}/framework/{framework}")
+                response.raise_for_status()
+                _unpack_response(framework, await response.json())
+
+        import asyncio
+
+        await asyncio.gather(*[_fetch_framework(framework) for framework in frameworks])
+
+        # Sort alphabetically for discoverability and return
+        for task, models in models_by_task.items():
+            models_by_task[task] = sorted(set(models), key=lambda x: x.lower())
+        return models_by_task
+
     async def object_detection(
         self,
         image: ContentT,
@@ -1823,7 +1904,14 @@ class AsyncInferenceClient:
 
     async def get_model_status(self, model: Optional[str] = None) -> ModelStatus:
         """
-        A function which returns the status of a specific model, from the Inference API.
+        Get the status of a model hosted on the Inference API.
+
+        <Tip>
+
+        This endpoint is mostly useful when you already know which model you want to use and want to check its
+        availability. If you want to discover already deployed models, you should rather use [`~InferenceClient.list_deployed_models`].
+
+        </Tip>
 
         Args:
             model (`str`, *optional*):
diff --git a/utils/generate_async_inference_client.py b/utils/generate_async_inference_client.py
index 3047e50f..3288a89d 100644
--- a/utils/generate_async_inference_client.py
+++ b/utils/generate_async_inference_client.py
@@ -57,6 +57,10 @@ def generate_async_client_code(code: str) -> str:
 
     # Adapt get_model_status
     code = _adapt_get_model_status(code)
+
+    # Adapt list_deployed_models
+    code = _adapt_list_deployed_models(code)
+
     return code
 
 
@@ -359,6 +363,27 @@ def _adapt_get_model_status(code: str) -> str:
     return code.replace(sync_snippet, async_snippet)
 
 
+def _adapt_list_deployed_models(code: str) -> str:
+    sync_snippet = """
+        for framework in frameworks:
+            response = get_session().get(f"{INFERENCE_ENDPOINT}/framework/{framework}", headers=self.headers)
+            hf_raise_for_status(response)
+            _unpack_response(framework, response.json())""".strip()
+
+    async_snippet = """
+        async def _fetch_framework(framework: str) -> None:
+            async with _import_aiohttp().ClientSession(headers=self.headers) as client:
+                response = await client.get(f"{INFERENCE_ENDPOINT}/framework/{framework}")
+                response.raise_for_status()
+                _unpack_response(framework, await response.json())
+
+        import asyncio
+
+        await asyncio.gather(*[_fetch_framework(framework) for framework in frameworks])""".strip()
+
+    return code.replace(sync_snippet, async_snippet)
+
+
 if __name__ == "__main__":
     parser = argparse.ArgumentParser()
     parser.add_argument(


Test patch:
diff --git a/tests/cassettes/InferenceClientVCRTest.test_feature_extraction_with_sentence_transformers.yaml b/tests/cassettes/InferenceClientVCRTest.test_feature_extraction_with_sentence_transformers.yaml
new file mode 100644
index 00000000..dfdc5bdf
--- /dev/null
+++ b/tests/cassettes/InferenceClientVCRTest.test_feature_extraction_with_sentence_transformers.yaml
@@ -0,0 +1,49 @@
+interactions:
+- request:
+    body: '{"inputs": "Hi, who are you?"}'
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '30'
+      Content-Type:
+      - application/json
+      X-Amzn-Trace-Id:
+      - 48c93e28-080d-4a24-97d2-ad328db8ad5f
+      user-agent:
+      - unknown/None; hf_hub/0.17.0.dev0; python/3.10.12; torch/1.12.1; tensorflow/2.11.0;
+        fastcore/1.5.23
+    method: POST
+    uri: https://api-inference.huggingface.co/pipeline/feature-extraction/sentence-transformers/all-MiniLM-L6-v2
+  response:
+    body:
+      string: '[-0.015488003380596638,0.02568504959344864,0.09088589996099472,0.06198530271649361,-0.040129248052835464,-0.05339203402400017,0.09715600311756134,0.03469717502593994,0.015956640243530273,-0.011821158230304718,-0.03288597986102104,-0.03562533110380173,-0.03749241679906845,-0.05202146992087364,0.029130304232239723,0.026269758120179176,0.04752971604466438,-0.03981475532054901,-0.10710455477237701,-0.04527846351265907,-0.05333522707223892,0.01959465816617012,0.005352573469281197,0.06281762570142746,-0.05192131549119949,-0.06000176817178726,0.06045954301953316,0.058989714831113815,-0.04338134080171585,-0.04691532254219055,0.036875441670417786,0.0553438626229763,-0.025663791224360466,0.028891634196043015,-0.002980383113026619,0.1019594594836235,-0.03168764337897301,-0.08663859963417053,0.010402388870716095,0.034213028848171234,0.023748792707920074,-0.005350565072149038,-0.0007556885830126703,0.006971727591007948,0.0924072116613388,-0.09179233014583588,-0.006686083506792784,0.04239071533083916,-0.021530259400606155,0.022891126573085785,-0.08954467624425888,-0.016592347994446754,0.010313213802874088,0.006074502132833004,0.04459349811077118,-0.014703832566738129,-0.058264147490262985,-0.015335035510361195,0.11178608238697052,-0.0288534052670002,-0.03507443889975548,0.03321828693151474,-0.07731037586927414,0.05726179853081703,-0.010513161309063435,-0.015395574271678925,0.005947402212768793,-0.02848624996840954,-0.002042497042566538,-0.022558165714144707,0.02557615004479885,-0.031124237924814224,-0.017573293298482895,0.027407802641391754,0.011907815001904964,-0.13045574724674225,0.05963331460952759,-0.031243789941072464,0.08464351296424866,0.011891472153365612,0.12086126208305359,-0.06646530330181122,-0.03442336991429329,-0.014139499515295029,-0.020020751282572746,-0.024550171568989754,0.03518184274435043,0.02602194994688034,-0.07537377625703812,-0.04512234777212143,-0.11447426676750183,0.06004863977432251,0.050079308450222015,0.002436161506921053,0.008054151199758053,-0.019581535831093788,0.06068527325987816,-0.029069695621728897,-0.11849680542945862,0.19127041101455688,0.04302886500954628,0.057212505489587784,0.018593933433294296,0.04831116273999214,0.02892104536294937,0.03642671927809715,-0.08078905194997787,0.045677654445171356,-0.04328872635960579,-0.03778192400932312,0.044068899005651474,-0.0488777831196785,-0.08991095423698425,-0.015680551528930664,0.023224234580993652,0.023814665153622627,0.024506816640496254,-0.003076164983212948,0.005233271047472954,-0.09886746108531952,0.00412531103938818,-0.008703207597136497,-0.003159345593303442,-0.0053872764110565186,0.02183465100824833,0.032812055200338364,-0.019271252676844597,-6.3860302832115e-33,0.05557152256369591,-0.02944130077958107,0.10268908739089966,0.12549802660942078,-0.10334186255931854,-0.013734928332269192,-0.0017235170817002654,-0.040916670113801956,0.001198898651637137,-0.06809760630130768,0.03835713863372803,-0.011545447632670403,-0.09097802639007568,0.011563128791749477,-0.055977918207645416,0.03141440451145172,0.07879236340522766,-0.014584570191800594,-0.005799459293484688,0.07969091087579727,-0.060531068593263626,0.014752582646906376,-0.011342428624629974,0.058411549776792526,0.02204466052353382,-0.07795903086662292,0.005299396347254515,-0.1344393640756607,0.043024759739637375,0.03288498893380165,0.048845987766981125,0.04022959619760513,0.014033233746886253,-0.009189552627503872,-0.08386309444904327,-0.0639234185218811,0.008618426509201527,-0.04197821766138077,-0.006849868223071098,-0.00877351500093937,-0.0009002164006233215,0.02906033955514431,0.10563940554857254,-0.02103484235703945,-0.07340109348297119,-0.0020454004406929016,0.018356945365667343,0.02422454208135605,-0.014901370741426945,0.03248341381549835,-0.0922296792268753,0.010189760476350784,-0.06558964401483536,0.007468798663467169,-0.018196018412709236,-0.1457824856042862,-0.01584876887500286,-0.00996595062315464,0.038348373025655746,0.05008549615740776,0.002209255937486887,0.10334494709968567,-0.0075745247304439545,0.013419363647699356,-0.0858914703130722,-0.028669729828834534,0.0172968078404665,-0.08050919324159622,0.09611788392066956,-0.04364169389009476,0.005056267604231834,-0.02275102399289608,-0.0027849830221384764,0.03860025480389595,-0.0313274972140789,0.05670100450515747,0.017083516344428062,0.035996995866298676,-0.03225824236869812,-0.06392467767000198,-0.0009643251542001963,0.08038908988237381,-0.004393764305859804,-0.008017303422093391,-0.006596939638257027,-0.06990642845630646,-0.021638210862874985,-0.09537546336650848,-0.04787294566631317,0.030017774552106857,-0.0564022921025753,0.013200130313634872,0.12777258455753326,-0.040990378707647324,-0.04150303453207016,2.7102745092115762e-33,0.11768963187932968,0.009109687991440296,-0.015613820403814316,-0.05708906799554825,0.04869804158806801,-0.054567743092775345,-0.03023681789636612,0.0676855519413948,-0.07275334745645523,0.009499989449977875,0.057831838726997375,-0.03812985122203827,0.06382809579372406,-0.02192682772874832,0.05542948842048645,-0.00788950826972723,0.05338781327009201,-0.0022213817574083805,-0.0673242136836052,-0.0053206440061330795,-0.08455456793308258,0.050038110464811325,-0.06520375609397888,0.002099896315485239,-0.005139470100402832,-0.0538233183324337,0.11823200434446335,0.059073369950056076,-0.028451623395085335,-0.02020588330924511,-0.013894325122237206,-0.012507826089859009,-0.0033883629366755486,0.014008536003530025,0.014685163274407387,0.023647569119930267,-0.027204062789678574,-0.08645278960466385,-0.006931010168045759,-0.087276391685009,-0.03931397199630737,0.058514464646577835,-0.024035293608903885,0.050167959183454514,0.030749473720788956,-0.0521317794919014,0.01849459670484066,0.03702811524271965,-0.036064162850379944,0.02507106214761734,-0.036811839789152145,0.0042779953218996525,0.08267977088689804,-0.019464459270238876,0.013441506773233414,0.058549847453832626,0.03242138400673866,-0.01653973013162613,0.0635439082980156,0.04435884580016136,0.05710609257221222,0.03656454384326935,-0.06139722838997841,0.08804606646299362,0.04480111226439476,0.018774637952446938,-0.038117919117212296,-0.0010337050771340728,-0.04384880140423775,-0.05019111931324005,0.0005711569683626294,-0.0374615341424942,0.035582590848207474,0.004387412685900927,-0.004966605920344591,0.026865949854254723,-0.02991478703916073,0.044912368059158325,-0.0058832005597651005,-1.1274696589680389e-05,0.03685228154063225,-0.051228005439043045,0.0398644357919693,0.05019840970635414,-0.007052907720208168,0.005109584424644709,0.020578941330313683,0.03889390453696251,0.05945917218923569,-0.0016939082415774465,0.015838656574487686,0.04903143271803856,-0.014905771240592003,-0.04540979489684105,-0.025111477822065353,-2.148873434748566e-08,0.012860794551670551,-0.0683913454413414,-0.048115625977516174,0.04716753959655762,0.029169021174311638,0.02530805580317974,-0.02866974286735058,-0.06068040803074837,-0.07865304499864578,0.006381262093782425,0.009587720036506653,0.013831025920808315,-0.0007099353242665529,-0.11363304406404495,0.05787523090839386,-0.042558878660202026,0.018417198210954666,0.009492634795606136,-0.04695336893200874,-0.09048309922218323,0.055524684488773346,0.014696232974529266,0.018304098397493362,0.0006754856440238655,0.011075588874518871,-0.05126316472887993,0.039927490055561066,-0.026545748114585876,-0.03874707967042923,0.03535597398877144,-0.0001164997011073865,0.16886861622333527,-0.07076022773981094,0.011706355959177017,0.024625975638628006,-0.03724432736635208,0.027894044294953346,0.012526650913059711,0.00382417649962008,0.012346706353127956,-0.0025911517441272736,-0.009165307506918907,0.025981910526752472,-0.02358575165271759,0.059070367366075516,0.027259647846221924,0.05028984695672989,-0.13784480094909668,0.07330789417028427,-0.04207126423716545,-0.04864133149385452,-0.027037223801016808,0.07027778029441833,0.09721964597702026,0.010687712579965591,0.0700918659567833,-0.027829717844724655,0.037554703652858734,-0.035384710878133774,0.010031565092504025,0.11184969544410706,-0.02597799524664879,-0.013402690179646015,-0.03893236443400383]'
+    headers:
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '8068'
+      Content-Type:
+      - application/json
+      Date:
+      - Fri, 08 Sep 2023 07:20:46 GMT
+      access-control-allow-credentials:
+      - 'true'
+      vary:
+      - Origin, Access-Control-Request-Method, Access-Control-Request-Headers
+      x-compute-time:
+      - '0.010'
+      x-compute-type:
+      - cache
+      x-request-id:
+      - dQcVrExSwbDQNuIURyPId
+      x-sha:
+      - 7dbbc90392e2f80f3d3c277d6e90027e55de9125
+    status:
+      code: 200
+      message: OK
+version: 1
diff --git a/tests/cassettes/InferenceClientVCRTest.test_feature_extraction.yaml b/tests/cassettes/InferenceClientVCRTest.test_feature_extraction_with_transformers.yaml
similarity index 99%
rename from tests/cassettes/InferenceClientVCRTest.test_feature_extraction.yaml
rename to tests/cassettes/InferenceClientVCRTest.test_feature_extraction_with_transformers.yaml
index 02da3ecc..01e90b09 100644
--- a/tests/cassettes/InferenceClientVCRTest.test_feature_extraction.yaml
+++ b/tests/cassettes/InferenceClientVCRTest.test_feature_extraction_with_transformers.yaml
@@ -12,8 +12,10 @@ interactions:
       - '30'
       Content-Type:
       - application/json
+      X-Amzn-Trace-Id:
+      - 1927a5b2-a186-43c2-ac64-274f65e8eaf7
       user-agent:
-      - unknown/None; hf_hub/0.15.0.dev0; python/3.10.6; torch/1.12.1; tensorflow/2.11.0;
+      - unknown/None; hf_hub/0.17.0.dev0; python/3.10.12; torch/1.12.1; tensorflow/2.11.0;
         fastcore/1.5.23
     method: POST
     uri: https://api-inference.huggingface.co/pipeline/feature-extraction/facebook/bart-base
@@ -28,17 +30,17 @@ interactions:
       Content-Type:
       - application/json
       Date:
-      - Fri, 26 May 2023 08:01:22 GMT
+      - Fri, 08 Sep 2023 07:20:47 GMT
       access-control-allow-credentials:
       - 'true'
       vary:
       - Origin, Access-Control-Request-Method, Access-Control-Request-Headers
       x-compute-time:
-      - '0.032'
+      - '0.037'
       x-compute-type:
       - cache
       x-request-id:
-      - Np8cBNsJriWfE_pCIFaiT
+      - VqJ_6NWGSg8mBsg_jugBF
       x-sha:
       - aadd2ab0ae0c8268c7c9693540e9904811f36177
     status:
diff --git a/tests/test_inference_async_client.py b/tests/test_inference_async_client.py
index 2dcdc79a..6a968c52 100644
--- a/tests/test_inference_async_client.py
+++ b/tests/test_inference_async_client.py
@@ -229,3 +229,17 @@ async def test_get_status_unknown_model() -> None:
 async def test_get_status_model_as_url() -> None:
     with pytest.raises(NotImplementedError):
         await AsyncInferenceClient().get_model_status("https://unkown/model")
+
+
+@pytest.mark.asyncio
+async def test_list_deployed_models_single_frameworks() -> None:
+    models_by_task = await AsyncInferenceClient().list_deployed_models("text-generation-inference")
+    assert isinstance(models_by_task, dict)
+    for task, models in models_by_task.items():
+        assert isinstance(task, str)
+        assert isinstance(models, list)
+        for model in models:
+            assert isinstance(model, str)
+
+    assert "text-generation" in models_by_task
+    assert "bigscience/bloom" in models_by_task["text-generation"]
diff --git a/tests/test_inference_client.py b/tests/test_inference_client.py
index 8852faec..dea70367 100644
--- a/tests/test_inference_client.py
+++ b/tests/test_inference_client.py
@@ -21,6 +21,7 @@ import pytest
 from PIL import Image
 
 from huggingface_hub import InferenceClient, hf_hub_download
+from huggingface_hub.constants import ALL_INFERENCE_API_FRAMEWORKS, MAIN_INFERENCE_API_FRAMEWORKS
 from huggingface_hub.inference._client import _open_as_binary
 from huggingface_hub.utils import HfHubHTTPError, build_hf_headers
 
@@ -133,10 +134,15 @@ class InferenceClientVCRTest(InferenceClientTest):
         output = self.client.document_question_answering(self.document_file, "What is the purchase amount?")
         self.assertEqual(output, [{"answer": "$1,000,000,000"}])
 
-    def test_feature_extraction(self) -> None:
+    def test_feature_extraction_with_transformers(self) -> None:
         embedding = self.client.feature_extraction("Hi, who are you?")
         self.assertIsInstance(embedding, np.ndarray)
-        self.assertEqual(embedding.shape, (8, 768))
+        self.assertEqual(embedding.shape, (1, 8, 768))
+
+    def test_feature_extraction_with_sentence_transformers(self) -> None:
+        embedding = self.client.feature_extraction("Hi, who are you?", model="sentence-transformers/all-MiniLM-L6-v2")
+        self.assertIsInstance(embedding, np.ndarray)
+        self.assertEqual(embedding.shape, (384,))
 
     def test_fill_mask(self) -> None:
         model = "distilroberta-base"
@@ -528,3 +534,33 @@ class TestModelStatus(unittest.TestCase):
         client = InferenceClient()
         with self.assertRaises(NotImplementedError):
             client.get_model_status("https://unkown/model")
+
+
+class TestListDeployedModels(unittest.TestCase):
+    @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()
+        self.assertEqual(
+            len(get_session_mock.return_value.get.call_args_list),
+            len(MAIN_INFERENCE_API_FRAMEWORKS),
+        )
+
+    @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")
+        self.assertEqual(
+            len(get_session_mock.return_value.get.call_args_list),
+            len(ALL_INFERENCE_API_FRAMEWORKS),
+        )
+
+    def test_list_deployed_models_single_frameworks(self) -> None:
+        models_by_task = InferenceClient().list_deployed_models("text-generation-inference")
+        self.assertIsInstance(models_by_task, dict)
+        for task, models in models_by_task.items():
+            self.assertIsInstance(task, str)
+            self.assertIsInstance(models, list)
+            for model in models:
+                self.assertIsInstance(model, str)
+
+        self.assertIn("text-generation", models_by_task)
+        self.assertIn("bigscience/bloom", models_by_task["text-generation"])