Repository: huggingface/huggingface_hub
Pull request #1524 (2023-06-21 14:38:51)

Support asyncio with AsyncInferenceClient
[Discussed in slack](https://huggingface.slack.com/archives/C03V11RNS7P/p1687884511434879) (internal link). Adds a AsyncInferenceClient for async calls to the inference endpoints. File is generated automatically by a script. The goal is to start with this and reassess in the future (maybe drop the generation-script at some point if it's too much work to maintain vs too little benefit).

```py
import asyncio
from huggingface_hub import AsyncInferenceClient

async def main():
    client = AsyncInferenceClient()

    print("\nText to image")
    image = await client.text_to_image("Astronaut riding a horse")
    print(image)

    print("\nText generation (100 tokens)")
    text = await client.text_generation(
        "Number between 1 and 1000: 1, 2, ", max_new_tokens=100, model="bigcode/starcoder"
    )
    print(text)

    print("\nText generation (5 tokens, stream + details)")
    async for token in await client.text_generation(
        "Number between 1 and 1000: 1, 2, ", max_new_tokens=5, model="bigcode/starcoder", stream=True, details=True
    ):
        print(token, flush=True)

loop = asyncio.new_event_loop()
loop.run_until_complete(main())
```

```py
Text to image
<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=512x512 at 0x7F4E739D5AB0>

Text generation (100 tokens)
3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,

Text generation (5 tokens, stream + details)
TextGenerationStreamResponse(token=Token(id=37, text='3', logprob=-0.14343262, special=False), generated_text=None, details=None)
TextGenerationStreamResponse(token=Token(id=30, text=',', logprob=-0.17370605, special=False), generated_text=None, details=None)
TextGenerationStreamResponse(token=Token(id=225, text=' ', logprob=-0.4597168, special=False), generated_text=None, details=None)
TextGenerationStreamResponse(token=Token(id=38, text='4', logprob=-0.1583252, special=False), generated_text=None, details=None)
TextGenerationStreamResponse(token=Token(id=30, text=',', logprob=-0.13146973, special=False), generated_text='3, 4,', details=StreamDetails(finish_reason=<FinishReason.Length: 'length'>, generated_tokens=5, seed=None))
```

**TODO:**
- [x] Add some tests. Ideally the same one as VCRed one but with asyncio? Do we need everything?
- [x] Documentation
  - [x] Mention it in [guide](https://moon-ci-docs.huggingface.co/docs/huggingface_hub/pr_1524/en/guides/inference#async-client)
  - [x] Add to [reference](https://moon-ci-docs.huggingface.co/docs/huggingface_hub/pr_1524/en/package_reference/inference_client#huggingface_hub.AsyncInferenceClient)
- [x] Find a solution to tests with VCR + aiohttp + stream=True (2 tests impacted)

HuggingFaceDocBuilderDev: The docs for this PR live [here](https://moon-ci-docs.huggingface.co/docs/huggingface_hub/pr_1524). All of your documentation changes will be reflected on that endpoint.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1524?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface) Report
Patch coverage: **`34.66`**% and project coverage change: **`-2.41`** :warning:
> Comparison is base [(`909fbd6`)](https://app.codecov.io/gh/huggingface/huggingface_hub/commit/909fbd605a6dc10ba8c1dd1356e3084d9fd3b331?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface) 82.59% compared to head [(`0556631`)](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1524?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface) 80.18%.

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


```diff
@@            Coverage Diff             @@
##             main    #1524      +/-   ##
==========================================
- Coverage   82.59%   80.18%   -2.41%     
==========================================
  Files          58       60       +2     
  Lines        6332     6552     +220     
==========================================
+ Hits         5230     5254      +24     
- Misses       1102     1298     +196     
```


| [Impacted Files](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1524?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface) | Coverage Δ | |
|---|---|---|
| [src/huggingface\_hub/\_\_init\_\_.py](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1524?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface#diff-c3JjL2h1Z2dpbmdmYWNlX2h1Yi9fX2luaXRfXy5weQ==) | `75.75% <ø> (ø)` | |
| [src/huggingface\_hub/inference/\_async\_client.py](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1524?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface#diff-c3JjL2h1Z2dpbmdmYWNlX2h1Yi9pbmZlcmVuY2UvX2FzeW5jX2NsaWVudC5weQ==) | `0.00% <0.00%> (ø)` | |
| [src/huggingface\_hub/utils/\_\_init\_\_.py](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1524?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface#diff-c3JjL2h1Z2dpbmdmYWNlX2h1Yi91dGlscy9fX2luaXRfXy5weQ==) | `100.00% <ø> (ø)` | |
| [src/huggingface\_hub/utils/\_runtime.py](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1524?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface#diff-c3JjL2h1Z2dpbmdmYWNlX2h1Yi91dGlscy9fcnVudGltZS5weQ==) | `55.33% <40.00%> (-0.53%)` | :arrow_down: |
| [src/huggingface\_hub/inference/\_common.py](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1524?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface#diff-c3JjL2h1Z2dpbmdmYWNlX2h1Yi9pbmZlcmVuY2UvX2NvbW1vbi5weQ==) | `77.39% <77.39%> (ø)` | |
| [src/huggingface\_hub/inference/\_client.py](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1524?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface#diff-c3JjL2h1Z2dpbmdmYWNlX2h1Yi9pbmZlcmVuY2UvX2NsaWVudC5weQ==) | `78.28% <84.61%> (-3.68%)` | :arrow_down: |

... and [3 files with indirect coverage changes](https://app.codecov.io/gh/huggingface/huggingface_hub/pull/1524/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/1524?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=huggingface).

Wauplin: Thanks for the review and the feedback @LysandreJik.

As discussed on Slack, the plan is to be pragmatic with the generation script:
- IMO it makes sense at the moment to quickly get a AsyncInferenceClient without too much manual duplicated work. In particular I expect that adding new methods should not need to update the generating script.
- I (reasonably) hope that we don't get too much maintenance on InferenceClient once all tasks have been implemented (same as InferenceAPI that got almost unchanged for 2 years).
- If at any point we realize maintaining a script is harder than manually maintaining the async code, I am completely fine with going fully manual. Generating the code a first time was necessary for consistency but once it's there the future changes should hopefully be minimal.
- Let's keep an extra eye opened when adding new tasks! The script should be ok but let's be careful on next PRs :) 

Since we are aligned on this, let's move forward and merge this PR :)

Patch:
diff --git a/.github/workflows/python-quality.yml b/.github/workflows/python-quality.yml
index 9c785d71..646aaf3f 100644
--- a/.github/workflows/python-quality.yml
+++ b/.github/workflows/python-quality.yml
@@ -31,6 +31,7 @@ jobs:
       - run: ruff tests src contrib
       - run: python utils/check_contrib_list.py
       - run: python utils/check_static_imports.py
+      - run: python utils/generate_async_inference_client.py
 
       # Run type checking at least on huggingface_hub root file to check all modules
       # that can be lazy-loaded actually exist.
diff --git a/Makefile b/Makefile
index 3ba5f3da..bed03fef 100644
--- a/Makefile
+++ b/Makefile
@@ -10,12 +10,14 @@ quality:
 	mypy src
 	python utils/check_contrib_list.py
 	python utils/check_static_imports.py
+	python utils/generate_async_inference_client.py
 
 style:
 	black $(check_dirs)
 	ruff $(check_dirs) --fix
 	python utils/check_contrib_list.py --update
 	python utils/check_static_imports.py --update
+	python utils/generate_async_inference_client.py --update
 
 repocard:
 	python utils/push_repocard_examples.py
diff --git a/docs/source/guides/inference.md b/docs/source/guides/inference.md
index c49f7ed0..15566f39 100644
--- a/docs/source/guides/inference.md
+++ b/docs/source/guides/inference.md
@@ -175,6 +175,36 @@ image. [`InferenceClient.post`] is also useful to handle tasks that are not yet
 b'...'
 ```
 
+## Async client
+
+An async version of the client is also provided, based on `asyncio` and `aiohttp`. You can either install `aiohttp`
+directly or use the `[inference]` extra:
+
+```sh
+pip install --upgrade huggingface_hub[inference]
+# or
+# pip install aiohttp
+```
+
+After installation all async API endpoints are available via [`AsyncInferenceClient`]. Its initialization and APIs are
+strictly the same as the sync-only version.
+
+```py
+# Code must be run in a asyncio concurrent context.
+# $ python -m asyncio
+>>> from huggingface_hub import AsyncInferenceClient
+>>> client = AsyncInferenceClient()
+
+>>> image = await client.text_to_image("An astronaut riding a horse on the moon.")
+>>> image.save("astronaut.png")
+
+>>> async for token in await client.text_generation("The Huggingface Hub is", stream=True):
+...     print(token, end="")
+ a platform for sharing and discussing ML-related content.
+```
+
+For more information about the `asyncio` module, please refer to the [official documentation](https://docs.python.org/3/library/asyncio.html).
+
 ## Advanced tips
 
 In the above section, we saw the main aspects of [`InferenceClient`]. Let's dive into some more advanced tips.
diff --git a/docs/source/package_reference/inference_client.md b/docs/source/package_reference/inference_client.md
index 9ce0fa7b..3edeb506 100644
--- a/docs/source/package_reference/inference_client.md
+++ b/docs/source/package_reference/inference_client.md
@@ -20,6 +20,19 @@ for more information on how to use it.
 
 [[autodoc]] InferenceClient
 
+## Async Inference Client
+
+An async version of the client is also provided, based on `asyncio` and `aiohttp`.
+To use it, you can either install `aiohttp` directly or use the `[inference]` extra:
+
+```sh
+pip install --upgrade huggingface_hub[inference]
+# or
+# pip install aiohttp
+```
+
+[[autodoc]] AsyncInferenceClient
+
 ### InferenceTimeoutError
 
 [[autodoc]] InferenceTimeoutError
diff --git a/setup.py b/setup.py
index e4c91f36..1af9002f 100644
--- a/setup.py
+++ b/setup.py
@@ -29,6 +29,11 @@ extras["cli"] = [
     # Note: installs `prompt-toolkit` in the background
 ]
 
+extras["inference"] = [
+    "aiohttp",  # for AsyncInferenceClient
+    "pydantic",  # for text-generation-inference
+]
+
 extras["torch"] = [
     "torch",
 ]
@@ -42,21 +47,25 @@ extras["fastai"] = [
 extras["tensorflow"] = ["tensorflow", "pydot", "graphviz"]
 
 
-extras["testing"] = extras["cli"] + [
-    "jedi",
-    "Jinja2",
-    "pytest",
-    "pytest-cov",
-    "pytest-env",
-    "pytest-xdist",
-    "pytest-vcr",  # to mock Inference
-    "urllib3<2.0",  # VCR.py broken with urllib3 2.0 (see https://urllib3.readthedocs.io/en/stable/v2-migration-guide.html)
-    "soundfile",
-    "Pillow",
-    "gradio",  # to test webhooks
-    "numpy",  # for embeddings
-    "pydantic",  # for text-generation-inference
-]
+extras["testing"] = (
+    extras["cli"]
+    + extras["inference"]
+    + [
+        "jedi",
+        "Jinja2",
+        "pytest",
+        "pytest-cov",
+        "pytest-env",
+        "pytest-xdist",
+        "pytest-vcr",  # to mock Inference
+        "pytest-asyncio",  # for AsyncInferenceClient
+        "urllib3<2.0",  # VCR.py broken with urllib3 2.0 (see https://urllib3.readthedocs.io/en/stable/v2-migration-guide.html)
+        "soundfile",
+        "Pillow",
+        "gradio",  # to test webhooks
+        "numpy",  # for embeddings
+    ]
+)
 
 # Typing extra dependencies list is duplicated in `.pre-commit-config.yaml`
 # Please make sure to update the list there when adding a new typing dependency.
diff --git a/src/huggingface_hub/__init__.py b/src/huggingface_hub/__init__.py
index eb8d17c9..f2d6490d 100644
--- a/src/huggingface_hub/__init__.py
+++ b/src/huggingface_hub/__init__.py
@@ -207,6 +207,9 @@ _SUBMOD_ATTRS = {
         "InferenceClient",
         "InferenceTimeoutError",
     ],
+    "inference._generated._async_client": [
+        "AsyncInferenceClient",
+    ],
     "inference_api": [
         "InferenceApi",
     ],
@@ -503,6 +506,7 @@ if TYPE_CHECKING:  # pragma: no cover
         InferenceClient,  # noqa: F401
         InferenceTimeoutError,  # noqa: F401
     )
+    from .inference._generated._async_client import AsyncInferenceClient  # noqa: F401
     from .inference_api import InferenceApi  # noqa: F401
     from .keras_mixin import (
         KerasModelHubMixin,  # noqa: F401
diff --git a/src/huggingface_hub/inference/_client.py b/src/huggingface_hub/inference/_client.py
index c103fe15..868d1cea 100644
--- a/src/huggingface_hub/inference/_client.py
+++ b/src/huggingface_hub/inference/_client.py
@@ -25,7 +25,6 @@
 # Some TODO:
 # - validate inputs/options/parameters? with Pydantic for instance? or only optionally?
 # - add all tasks
-# - handle async requests
 #
 # NOTE: the philosophy of this client is "let's make it as easy as possible to use it, even if less optimized". Some
 # examples of how it translates:
@@ -34,51 +33,54 @@
 # - Images are parsed as PIL.Image for easier manipulation.
 # - Provides a "recommended model" for each task => suboptimal but user-wise quicker to get a first script running.
 # - Only the main parameters are publicly exposed. Power users can always read the docs for more options.
-import base64
-import io
-import json
 import logging
 import time
 import warnings
-from contextlib import contextmanager
 from dataclasses import asdict
-from pathlib import Path
 from typing import (
     TYPE_CHECKING,
     Any,
-    BinaryIO,
-    ContextManager,
     Dict,
-    Generator,
     Iterable,
     List,
     Optional,
-    Set,
     Union,
     overload,
 )
 
-from requests import HTTPError, Response
+from requests import HTTPError
 from requests.structures import CaseInsensitiveDict
 
-from ..constants import ENDPOINT, INFERENCE_ENDPOINT
-from ..utils import (
-    BadRequestError,
-    build_hf_headers,
-    get_session,
-    hf_raise_for_status,
-    is_numpy_available,
-    is_pillow_available,
+from huggingface_hub.constants import INFERENCE_ENDPOINT
+from huggingface_hub.inference._common import (
+    ContentT,
+    InferenceTimeoutError,
+    _b64_encode,
+    _b64_to_image,
+    _bytes_to_dict,
+    _bytes_to_image,
+    _get_recommended_model,
+    _import_numpy,
+    _is_tgi_server,
+    _open_as_binary,
+    _set_as_non_tgi,
+    _stream_text_generation_response,
 )
-from ..utils._typing import Literal
-from ._text_generation import (
+from huggingface_hub.inference._text_generation import (
     TextGenerationParameters,
     TextGenerationRequest,
     TextGenerationResponse,
     TextGenerationStreamResponse,
     raise_text_generation_error,
 )
-from ._types import ClassificationOutput, ConversationalOutput, ImageSegmentationOutput
+from huggingface_hub.inference._types import ClassificationOutput, ConversationalOutput, ImageSegmentationOutput
+from huggingface_hub.utils import (
+    BadRequestError,
+    build_hf_headers,
+    get_session,
+    hf_raise_for_status,
+)
+from huggingface_hub.utils._typing import Literal
 
 
 if TYPE_CHECKING:
@@ -87,18 +89,6 @@ if TYPE_CHECKING:
 
 logger = logging.getLogger(__name__)
 
-UrlT = str
-PathT = Union[str, Path]
-BinaryT = Union[bytes, BinaryIO]
-ContentT = Union[BinaryT, PathT, UrlT]
-
-# Will be globally fetched only once (see '_fetch_recommended_models')
-_RECOMMENDED_MODELS: Optional[Dict[str, Optional[str]]] = None
-
-
-class InferenceTimeoutError(HTTPError, TimeoutError):
-    """Error raised when a model is unavailable or the request times out."""
-
 
 class InferenceClient:
     """
@@ -143,6 +133,30 @@ class InferenceClient:
     def __repr__(self):
         return f"<InferenceClient(model='{self.model if self.model else ''}', timeout={self.timeout})>"
 
+    @overload
+    def post(  # type: ignore
+        self,
+        *,
+        json: Optional[Union[str, Dict, List]] = None,
+        data: Optional[ContentT] = None,
+        model: Optional[str] = None,
+        task: Optional[str] = None,
+        stream: Literal[False] = ...,
+    ) -> bytes:
+        pass
+
+    @overload
+    def post(  # type: ignore
+        self,
+        *,
+        json: Optional[Union[str, Dict, List]] = None,
+        data: Optional[ContentT] = None,
+        model: Optional[str] = None,
+        task: Optional[str] = None,
+        stream: Literal[True] = ...,
+    ) -> Iterable[bytes]:
+        pass
+
     def post(
         self,
         *,
@@ -151,7 +165,7 @@ class InferenceClient:
         model: Optional[str] = None,
         task: Optional[str] = None,
         stream: bool = False,
-    ) -> Response:
+    ) -> Union[bytes, Iterable[bytes]]:
         """
         Make a POST request to the inference server.
 
@@ -172,7 +186,7 @@ class InferenceClient:
                 Whether to iterate over streaming APIs.
 
         Returns:
-            Response: The `requests` HTTP response.
+            bytes: The raw bytes returned by the server.
 
         Raises:
             [`InferenceTimeoutError`]:
@@ -205,6 +219,7 @@ class InferenceClient:
 
             try:
                 hf_raise_for_status(response)
+                return response.iter_lines() if stream else response.content
             except HTTPError as error:
                 if error.response.status_code == 503:
                     # If Model is unavailable, either raise a TimeoutError...
@@ -220,8 +235,6 @@ class InferenceClient:
                         timeout = max(self.timeout - (time.time() - t0), 1)  # type: ignore
                     continue
                 raise
-            break
-        return response
 
     def audio_classification(
         self,
@@ -259,7 +272,7 @@ class InferenceClient:
         ```
         """
         response = self.post(data=audio, model=model, task="audio-classification")
-        return response.json()
+        return _bytes_to_dict(response)
 
     def automatic_speech_recognition(
         self,
@@ -295,7 +308,7 @@ class InferenceClient:
         ```
         """
         response = self.post(data=audio, model=model, task="automatic-speech-recognition")
-        return response.json()["text"]
+        return _bytes_to_dict(response)["text"]
 
     def conversational(
         self,
@@ -356,7 +369,7 @@ class InferenceClient:
         if parameters is not None:
             payload["parameters"] = parameters
         response = self.post(json=payload, model=model, task="conversational")
-        return response.json()
+        return _bytes_to_dict(response)
 
     def feature_extraction(self, text: str, *, model: Optional[str] = None) -> "np.ndarray":
         """
@@ -392,7 +405,7 @@ class InferenceClient:
         """
         response = self.post(json={"inputs": text}, model=model, task="feature-extraction")
         np = _import_numpy()
-        return np.array(response.json()[0], dtype="float32")
+        return np.array(_bytes_to_dict(response)[0], dtype="float32")
 
     def image_classification(
         self,
@@ -428,7 +441,7 @@ class InferenceClient:
         ```
         """
         response = self.post(data=image, model=model, task="image-classification")
-        return response.json()
+        return _bytes_to_dict(response)
 
     def image_segmentation(
         self,
@@ -472,7 +485,7 @@ class InferenceClient:
 
         # Segment
         response = self.post(data=image, model=model, task="image-segmentation")
-        output = response.json()
+        output = _bytes_to_dict(response)
 
         # Parse masks as PIL Image
         if not isinstance(output, list):
@@ -552,7 +565,6 @@ class InferenceClient:
         }
         if all(parameter is None for parameter in parameters.values()):
             # Either only an image to send => send as raw bytes
-            self.post(data=image, model=model, task="image-to-image")
             data = image
             payload: Optional[Dict[str, Any]] = None
         else:
@@ -564,7 +576,7 @@ class InferenceClient:
                     payload[key] = value
 
         response = self.post(json=payload, data=data, model=model, task="image-to-image")
-        return _response_to_image(response)
+        return _bytes_to_image(response)
 
     def image_to_text(self, image: ContentT, *, model: Optional[str] = None) -> str:
         """
@@ -600,7 +612,7 @@ class InferenceClient:
         ```
         """
         response = self.post(data=image, model=model, task="image-to-text")
-        return response.json()[0]["generated_text"]
+        return _bytes_to_dict(response)[0]["generated_text"]
 
     def sentence_similarity(
         self, sentence: str, other_sentences: List[str], *, model: Optional[str] = None
@@ -647,7 +659,7 @@ class InferenceClient:
             model=model,
             task="sentence-similarity",
         )
-        return response.json()
+        return _bytes_to_dict(response)
 
     def summarization(
         self,
@@ -690,7 +702,7 @@ class InferenceClient:
         if parameters is not None:
             payload["parameters"] = parameters
         response = self.post(json=payload, model=model, task="summarization")
-        return response.json()[0]["summary_text"]
+        return _bytes_to_dict(response)[0]["summary_text"]
 
     @overload
     def text_generation(  # type: ignore
@@ -1026,7 +1038,7 @@ class InferenceClient:
 
         # Handle errors separately for more precise error messages
         try:
-            response = self.post(json=payload, model=model, task="text-generation", stream=stream)
+            bytes_output = self.post(json=payload, model=model, task="text-generation", stream=stream)  # type: ignore
         except HTTPError as e:
             if isinstance(e, BadRequestError) and "The following `model_kwargs` are not used by the model" in str(e):
                 _set_as_non_tgi(model)
@@ -1054,11 +1066,10 @@ class InferenceClient:
 
         # Parse output
         if stream:
-            return _stream_text_generation_response(response, details)  # type: ignore
-        elif details:
-            return TextGenerationResponse(**response.json()[0])
-        else:
-            return response.json()[0]["generated_text"]
+            return _stream_text_generation_response(bytes_output, details)  # type: ignore
+
+        data = _bytes_to_dict(bytes_output)[0]
+        return TextGenerationResponse(**data) if details else data["generated_text"]
 
     def text_to_image(
         self,
@@ -1139,7 +1150,7 @@ class InferenceClient:
             if value is not None:
                 payload[key] = value
         response = self.post(json=payload, model=model, task="text-to-image")
-        return _response_to_image(response)
+        return _bytes_to_image(response)
 
     def text_to_speech(self, text: str, *, model: Optional[str] = None) -> bytes:
         """
@@ -1171,8 +1182,7 @@ class InferenceClient:
         >>> Path("hello_world.flac").write_bytes(audio)
         ```
         """
-        response = self.post(json={"inputs": text}, model=model, task="text-to-speech")
-        return response.content
+        return self.post(json={"inputs": text}, model=model, task="text-to-speech")
 
     def zero_shot_image_classification(
         self, image: ContentT, labels: List[str], *, model: Optional[str] = None
@@ -1220,7 +1230,7 @@ class InferenceClient:
             model=model,
             task="zero-shot-image-classification",
         )
-        return response.json()
+        return _bytes_to_dict(response)
 
     def _resolve_url(self, model: Optional[str] = None, task: Optional[str] = None) -> str:
         model = model or self.model
@@ -1246,165 +1256,3 @@ class InferenceClient:
             # Otherwise, we use the default endpoint
             else f"{INFERENCE_ENDPOINT}/models/{model}"
         )
-
-
-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:
-        response = get_session().get(f"{ENDPOINT}/api/tasks", headers=build_hf_headers())
-        hf_raise_for_status(response)
-        _RECOMMENDED_MODELS = {
-            task: _first_or_none(details["widgetModels"]) for task, details in response.json().items()
-        }
-    return _RECOMMENDED_MODELS
-
-
-@overload
-def _open_as_binary(content: ContentT) -> ContextManager[BinaryT]:
-    ...  # means "if input is not None, output is not None"
-
-
-@overload
-def _open_as_binary(content: Literal[None]) -> ContextManager[Literal[None]]:
-    ...  # means "if input is None, output is None"
-
-
-@contextmanager  # type: ignore
-def _open_as_binary(content: Optional[ContentT]) -> Generator[Optional[BinaryT], None, None]:
-    """Open `content` as a binary file, either from a URL, a local path, or raw bytes.
-
-    Do nothing if `content` is None,
-
-    TODO: handle a PIL.Image as input
-    TODO: handle base64 as input
-    """
-    # If content is a string => must be either a URL or a path
-    if isinstance(content, str):
-        if content.startswith("https://") or content.startswith("http://"):
-            logger.debug(f"Downloading content from {content}")
-            yield get_session().get(content).content  # TODO: retrieve as stream and pipe to post request ?
-            return
-        content = Path(content)
-        if not content.exists():
-            raise FileNotFoundError(
-                f"File not found at {content}. If `data` is a string, it must either be a URL or a path to a local"
-                " file. To pass raw content, please encode it as bytes first."
-            )
-
-    # If content is a Path => open it
-    if isinstance(content, Path):
-        logger.debug(f"Opening content from {content}")
-        with content.open("rb") as f:
-            yield f
-    else:
-        # Otherwise: already a file-like object or None
-        yield content
-
-
-def _b64_encode(content: ContentT) -> str:
-    """Encode a raw file (image, audio) into base64. Can be byes, an opened file, a path or a URL."""
-    with _open_as_binary(content) as data:
-        data_as_bytes = data if isinstance(data, bytes) else data.read()
-        return base64.b64encode(data_as_bytes).decode()
-
-
-def _b64_to_image(encoded_image: str) -> "Image":
-    """Parse a base64-encoded string into a PIL Image."""
-    Image = _import_pil_image()
-    return Image.open(io.BytesIO(base64.b64decode(encoded_image)))
-
-
-def _response_to_image(response: Response) -> "Image":
-    """Parse a Response object into a PIL Image.
-
-    Expects the response body to be raw bytes. To deal with b64 encoded images, use `_b64_to_image` instead.
-    """
-    Image = _import_pil_image()
-    return Image.open(io.BytesIO(response.content))
-
-
-def _stream_text_generation_response(
-    response: Response, details: bool
-) -> Union[Iterable[str], Iterable[TextGenerationStreamResponse]]:
-    # Parse ServerSentEvents
-    for byte_payload in response.iter_lines():
-        # Skip line
-        if byte_payload == b"\n":
-            continue
-
-        payload = byte_payload.decode("utf-8")
-
-        # Event data
-        if payload.startswith("data:"):
-            # Decode payload
-            json_payload = json.loads(payload.lstrip("data:").rstrip("/n"))
-            # Parse payload
-            output = TextGenerationStreamResponse(**json_payload)
-            yield output.token.text if not details else output
-
-
-def _import_pil_image():
-    """Make sure `PIL` is installed on the machine."""
-    if not is_pillow_available():
-        raise ImportError(
-            "Please install Pillow to use deal with images (`pip install Pillow`). If you don't want the image to be"
-            " post-processed, use `client.post(...)` and get the raw response from the server."
-        )
-    from PIL import Image
-
-    return Image
-
-
-def _import_numpy():
-    """Make sure `numpy` is installed on the machine."""
-    if not is_numpy_available():
-        raise ImportError("Please install numpy to use deal with embeddings (`pip install numpy`).")
-    import numpy
-
-    return numpy
-
-
-def _first_or_none(items: List[Any]) -> Optional[Any]:
-    try:
-        return items[0] or None
-    except IndexError:
-        return None
-
-
-# "TGI servers" are servers running with the `text-generation-inference` backend.
-# This backend is the go-to solution to run large language models at scale. However,
-# for some smaller models (e.g. "gpt2") the default `transformers` + `api-inference`
-# solution is still in use.
-#
-# Both approaches have very similar APIs, but not exactly the same. What we do first in
-# the `text_generation` method is to assume the model is served via TGI. If we realize
-# it's not the case (i.e. we receive an HTTP 400 Bad Request), we fallback to the
-# default API with a warning message. We remember for each model if it's a TGI server
-# or not using `_NON_TGI_SERVERS` global variable.
-#
-# For more details, see https://github.com/huggingface/text-generation-inference and
-# https://huggingface.co/docs/api-inference/detailed_parameters#text-generation-task.
-
-_NON_TGI_SERVERS: Set[Optional[str]] = set()
-
-
-def _set_as_non_tgi(model: Optional[str]) -> None:
-    _NON_TGI_SERVERS.add(model)
-
-
-def _is_tgi_server(model: Optional[str]) -> bool:
-    return model not in _NON_TGI_SERVERS
diff --git a/src/huggingface_hub/inference/_common.py b/src/huggingface_hub/inference/_common.py
new file mode 100644
index 00000000..73c3e61d
--- /dev/null
+++ b/src/huggingface_hub/inference/_common.py
@@ -0,0 +1,289 @@
+# coding=utf-8
+# Copyright 2023-present, the HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Contains utilities used by both the sync and async inference clients."""
+import base64
+import io
+import json
+import logging
+from contextlib import contextmanager
+from pathlib import Path
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    AsyncIterable,
+    BinaryIO,
+    ContextManager,
+    Dict,
+    Generator,
+    Iterable,
+    List,
+    Optional,
+    Set,
+    Union,
+    overload,
+)
+
+from requests import HTTPError
+
+from ..constants import ENDPOINT
+from ..utils import (
+    build_hf_headers,
+    get_session,
+    hf_raise_for_status,
+    is_aiohttp_available,
+    is_numpy_available,
+    is_pillow_available,
+)
+from ..utils._typing import Literal
+from ._text_generation import (
+    TextGenerationStreamResponse,
+)
+
+
+if TYPE_CHECKING:
+    from aiohttp import ClientResponse, ClientSession
+    from PIL import Image
+
+# TYPES
+UrlT = str
+PathT = Union[str, Path]
+BinaryT = Union[bytes, BinaryIO]
+ContentT = Union[BinaryT, PathT, UrlT]
+
+logger = logging.getLogger(__name__)
+
+
+class InferenceTimeoutError(HTTPError, TimeoutError):
+    """Error raised when a model is unavailable or the request times out."""
+
+
+## IMPORT UTILS
+
+
+def _import_aiohttp():
+    # Make sure `aiohttp` is installed on the machine.
+    if not is_aiohttp_available():
+        raise ImportError("Please install aiohttp to use `AsyncInferenceClient` (`pip install aiohttp`).")
+    import aiohttp
+
+    return aiohttp
+
+
+def _import_numpy():
+    """Make sure `numpy` is installed on the machine."""
+    if not is_numpy_available():
+        raise ImportError("Please install numpy to use deal with embeddings (`pip install numpy`).")
+    import numpy
+
+    return numpy
+
+
+def _import_pil_image():
+    """Make sure `PIL` is installed on the machine."""
+    if not is_pillow_available():
+        raise ImportError(
+            "Please install Pillow to use deal with images (`pip install Pillow`). If you don't want the image to be"
+            " post-processed, use `client.post(...)` and get the raw response from the server."
+        )
+    from PIL import Image
+
+    return Image
+
+
+## RECOMMENDED MODELS
+
+# Will be globally fetched only once (see '_fetch_recommended_models')
+_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:
+        response = get_session().get(f"{ENDPOINT}/api/tasks", headers=build_hf_headers())
+        hf_raise_for_status(response)
+        _RECOMMENDED_MODELS = {
+            task: _first_or_none(details["widgetModels"]) for task, details in response.json().items()
+        }
+    return _RECOMMENDED_MODELS
+
+
+def _first_or_none(items: List[Any]) -> Optional[Any]:
+    try:
+        return items[0] or None
+    except IndexError:
+        return None
+
+
+## ENCODING / DECODING UTILS
+
+
+@overload
+def _open_as_binary(content: ContentT) -> ContextManager[BinaryT]:
+    ...  # means "if input is not None, output is not None"
+
+
+@overload
+def _open_as_binary(content: Literal[None]) -> ContextManager[Literal[None]]:
+    ...  # means "if input is None, output is None"
+
+
+@contextmanager  # type: ignore
+def _open_as_binary(content: Optional[ContentT]) -> Generator[Optional[BinaryT], None, None]:
+    """Open `content` as a binary file, either from a URL, a local path, or raw bytes.
+
+    Do nothing if `content` is None,
+
+    TODO: handle a PIL.Image as input
+    TODO: handle base64 as input
+    """
+    # If content is a string => must be either a URL or a path
+    if isinstance(content, str):
+        if content.startswith("https://") or content.startswith("http://"):
+            logger.debug(f"Downloading content from {content}")
+            yield get_session().get(content).content  # TODO: retrieve as stream and pipe to post request ?
+            return
+        content = Path(content)
+        if not content.exists():
+            raise FileNotFoundError(
+                f"File not found at {content}. If `data` is a string, it must either be a URL or a path to a local"
+                " file. To pass raw content, please encode it as bytes first."
+            )
+
+    # If content is a Path => open it
+    if isinstance(content, Path):
+        logger.debug(f"Opening content from {content}")
+        with content.open("rb") as f:
+            yield f
+    else:
+        # Otherwise: already a file-like object or None
+        yield content
+
+
+def _b64_encode(content: ContentT) -> str:
+    """Encode a raw file (image, audio) into base64. Can be byes, an opened file, a path or a URL."""
+    with _open_as_binary(content) as data:
+        data_as_bytes = data if isinstance(data, bytes) else data.read()
+        return base64.b64encode(data_as_bytes).decode()
+
+
+def _b64_to_image(encoded_image: str) -> "Image":
+    """Parse a base64-encoded string into a PIL Image."""
+    Image = _import_pil_image()
+    return Image.open(io.BytesIO(base64.b64decode(encoded_image)))
+
+
+def _bytes_to_dict(content: bytes) -> "Image":
+    """Parse bytes from a Response object into a Python dictionary.
+
+    Expects the response body to be encoded-JSON data.
+    """
+    return json.loads(content.decode())
+
+
+def _bytes_to_image(content: bytes) -> "Image":
+    """Parse bytes from a Response object into a PIL Image.
+
+    Expects the response body to be raw bytes. To deal with b64 encoded images, use `_b64_to_image` instead.
+    """
+    Image = _import_pil_image()
+    return Image.open(io.BytesIO(content))
+
+
+## STREAMING UTILS
+
+
+def _stream_text_generation_response(
+    bytes_output_as_lines: Iterable[bytes], details: bool
+) -> Union[Iterable[str], Iterable[TextGenerationStreamResponse]]:
+    # Parse ServerSentEvents
+    for byte_payload in bytes_output_as_lines:
+        # Skip line
+        if byte_payload == b"\n":
+            continue
+
+        payload = byte_payload.decode("utf-8")
+
+        # Event data
+        if payload.startswith("data:"):
+            # Decode payload
+            json_payload = json.loads(payload.lstrip("data:").rstrip("/n"))
+            # Parse payload
+            output = TextGenerationStreamResponse(**json_payload)
+            yield output.token.text if not details else output
+
+
+async def _async_stream_text_generation_response(
+    bytes_output_as_lines: AsyncIterable[bytes], details: bool
+) -> Union[AsyncIterable[str], AsyncIterable[TextGenerationStreamResponse]]:
+    # Parse ServerSentEvents
+    async for byte_payload in bytes_output_as_lines:
+        # Skip line
+        if byte_payload == b"\n":
+            continue
+
+        payload = byte_payload.decode("utf-8")
+
+        # Event data
+        if payload.startswith("data:"):
+            # Decode payload
+            json_payload = json.loads(payload.lstrip("data:").rstrip("/n"))
+            # Parse payload
+            output = TextGenerationStreamResponse(**json_payload)
+            yield output.token.text if not details else output
+
+
+async def _async_yield_from(client: "ClientSession", response: "ClientResponse") -> AsyncIterable[bytes]:
+    async for byte_payload in response.content:
+        yield byte_payload
+    await client.close()
+
+
+# "TGI servers" are servers running with the `text-generation-inference` backend.
+# This backend is the go-to solution to run large language models at scale. However,
+# for some smaller models (e.g. "gpt2") the default `transformers` + `api-inference`
+# solution is still in use.
+#
+# Both approaches have very similar APIs, but not exactly the same. What we do first in
+# the `text_generation` method is to assume the model is served via TGI. If we realize
+# it's not the case (i.e. we receive an HTTP 400 Bad Request), we fallback to the
+# default API with a warning message. We remember for each model if it's a TGI server
+# or not using `_NON_TGI_SERVERS` global variable.
+#
+# For more details, see https://github.com/huggingface/text-generation-inference and
+# https://huggingface.co/docs/api-inference/detailed_parameters#text-generation-task.
+
+_NON_TGI_SERVERS: Set[Optional[str]] = set()
+
+
+def _set_as_non_tgi(model: Optional[str]) -> None:
+    _NON_TGI_SERVERS.add(model)
+
+
+def _is_tgi_server(model: Optional[str]) -> bool:
+    return model not in _NON_TGI_SERVERS
diff --git a/src/huggingface_hub/inference/_generated/__init__.py b/src/huggingface_hub/inference/_generated/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/huggingface_hub/inference/_generated/_async_client.py b/src/huggingface_hub/inference/_generated/_async_client.py
new file mode 100644
index 00000000..7a40403a
--- /dev/null
+++ b/src/huggingface_hub/inference/_generated/_async_client.py
@@ -0,0 +1,1269 @@
+# coding=utf-8
+# Copyright 2023-present, the HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# WARNING
+# This entire file has been adapted from the sync-client code in `src/huggingface_hub/inference/_client.py`.
+# Any change in InferenceClient will be automatically reflected in AsyncInferenceClient.
+# To re-generate the code, run `make style` or `python ./utils/generate_async_inference_client.py --update`.
+# WARNING
+import logging
+import time
+import warnings
+from dataclasses import asdict
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    AsyncIterable,
+    Dict,
+    List,
+    Optional,
+    Union,
+    overload,
+)
+
+from requests.structures import CaseInsensitiveDict
+
+from huggingface_hub.constants import INFERENCE_ENDPOINT
+from huggingface_hub.inference._common import (
+    ContentT,
+    InferenceTimeoutError,
+    _async_stream_text_generation_response,
+    _b64_encode,
+    _b64_to_image,
+    _bytes_to_dict,
+    _bytes_to_image,
+    _get_recommended_model,
+    _import_numpy,
+    _is_tgi_server,
+    _open_as_binary,
+    _set_as_non_tgi,
+)
+from huggingface_hub.inference._text_generation import (
+    TextGenerationParameters,
+    TextGenerationRequest,
+    TextGenerationResponse,
+    TextGenerationStreamResponse,
+    raise_text_generation_error,
+)
+from huggingface_hub.inference._types import ClassificationOutput, ConversationalOutput, ImageSegmentationOutput
+from huggingface_hub.utils import (
+    build_hf_headers,
+)
+from huggingface_hub.utils._typing import Literal
+
+from .._common import _async_yield_from, _import_aiohttp
+
+
+if TYPE_CHECKING:
+    import numpy as np
+    from PIL import Image
+
+logger = logging.getLogger(__name__)
+
+
+class AsyncInferenceClient:
+    """
+    Initialize a new Inference Client.
+
+    [`InferenceClient`] aims to provide a unified experience to perform inference. The client can be used
+    seamlessly with either the (free) Inference API or self-hosted Inference Endpoints.
+
+    Args:
+        model (`str`, `optional`):
+            The model to run inference with. Can be a model id hosted on the Hugging Face Hub, e.g. `bigcode/starcoder`
+            or a URL to a deployed Inference Endpoint. Defaults to None, in which case a recommended model is
+            automatically selected for the task.
+        token (`str`, *optional*):
+            Hugging Face token. Will default to the locally saved token. Pass `token=False` if you don't want to send
+            your token to the server.
+        timeout (`float`, `optional`):
+            The maximum number of seconds to wait for a response from the server. Loading a new model in Inference
+            API can take up to several minutes. Defaults to None, meaning it will loop until the server is available.
+        headers (`Dict[str, str]`, `optional`):
+            Additional headers to send to the server. By default only the authorization and user-agent headers are sent.
+            Values in this dictionary will override the default values.
+        cookies (`Dict[str, str]`, `optional`):
+            Additional cookies to send to the server.
+    """
+
+    def __init__(
+        self,
+        model: Optional[str] = None,
+        token: Union[str, bool, None] = None,
+        timeout: Optional[float] = None,
+        headers: Optional[Dict[str, str]] = None,
+        cookies: Optional[Dict[str, str]] = None,
+    ) -> None:
+        self.model: Optional[str] = model
+        self.headers = CaseInsensitiveDict(build_hf_headers(token=token))  # contains 'authorization' + 'user-agent'
+        if headers is not None:
+            self.headers.update(headers)
+        self.cookies = cookies
+        self.timeout = timeout
+
+    def __repr__(self):
+        return f"<InferenceClient(model='{self.model if self.model else ''}', timeout={self.timeout})>"
+
+    @overload
+    async def post(  # type: ignore
+        self,
+        *,
+        json: Optional[Union[str, Dict, List]] = None,
+        data: Optional[ContentT] = None,
+        model: Optional[str] = None,
+        task: Optional[str] = None,
+        stream: Literal[False] = ...,
+    ) -> bytes:
+        pass
+
+    @overload
+    async def post(  # type: ignore
+        self,
+        *,
+        json: Optional[Union[str, Dict, List]] = None,
+        data: Optional[ContentT] = None,
+        model: Optional[str] = None,
+        task: Optional[str] = None,
+        stream: Literal[True] = ...,
+    ) -> AsyncIterable[bytes]:
+        pass
+
+    async def post(
+        self,
+        *,
+        json: Optional[Union[str, Dict, List]] = None,
+        data: Optional[ContentT] = None,
+        model: Optional[str] = None,
+        task: Optional[str] = None,
+        stream: bool = False,
+    ) -> Union[bytes, AsyncIterable[bytes]]:
+        """
+        Make a POST request to the inference server.
+
+        Args:
+            json (`Union[str, Dict, List]`, *optional*):
+                The JSON data to send in the request body. Defaults to None.
+            data (`Union[str, Path, bytes, BinaryIO]`, *optional*):
+                The content to send in the request body. It can be raw bytes, a pointer to an opened file, a local file
+                path, or a URL to an online resource (image, audio file,...). If both `json` and `data` are passed,
+                `data` will take precedence. At least `json` or `data` must be provided. Defaults to None.
+            model (`str`, *optional*):
+                The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed
+                Inference Endpoint. Will override the model defined at the instance level. Defaults to None.
+            task (`str`, *optional*):
+                The task to perform on the inference. Used only to default to a recommended model if `model` is not
+                provided. At least `model` or `task` must be provided. Defaults to None.
+            stream (`bool`, *optional*):
+                Whether to iterate over streaming APIs.
+
+        Returns:
+            bytes: The raw bytes returned by the server.
+
+        Raises:
+            [`InferenceTimeoutError`]:
+                If the model is unavailable or the request times out.
+            `aiohttp.ClientResponseError`:
+                If the request fails with an HTTP error status code other than HTTP 503.
+        """
+
+        aiohttp = _import_aiohttp()
+
+        url = self._resolve_url(model, task)
+
+        if data is not None and json is not None:
+            warnings.warn("Ignoring `json` as `data` is passed as binary.")
+
+        t0 = time.time()
+        timeout = self.timeout
+        while True:
+            with _open_as_binary(data) as data_as_binary:
+                # Do not use context manager as we don't want to close the connection immediately when returning
+                # a stream
+                client = aiohttp.ClientSession(
+                    headers=self.headers, cookies=self.cookies, timeout=aiohttp.ClientTimeout(self.timeout)
+                )
+
+                try:
+                    response = await client.post(url, headers=build_hf_headers(), json=json, data=data_as_binary)
+                    response_error_payload = None
+                    if response.status != 200:
+                        try:
+                            response_error_payload = await response.json()  # get payload before connection closed
+                        except Exception:
+                            pass
+                    response.raise_for_status()
+                    if stream:
+                        return _async_yield_from(client, response)
+                    else:
+                        content = await response.read()
+                        await client.close()
+                        return content
+                except TimeoutError as error:
+                    await client.close()
+                    # Convert any `TimeoutError` to a `InferenceTimeoutError`
+                    raise InferenceTimeoutError(f"Inference call timed out: {url}") from error
+                except aiohttp.ClientResponseError as error:
+                    error.response_error_payload = response_error_payload
+                    await client.close()
+                    if response.status == 503:
+                        # If Model is unavailable, either raise a TimeoutError...
+                        if timeout is not None and time.time() - t0 > timeout:
+                            raise InferenceTimeoutError(
+                                f"Model not loaded on the server: {url}. Please retry with a higher timeout"
+                                f" (current: {self.timeout})."
+                            ) from error
+                        # ...or wait 1s and retry
+                        logger.info(f"Waiting for model to be loaded on the server: {error}")
+                        time.sleep(1)
+                        if timeout is not None:
+                            timeout = max(self.timeout - (time.time() - t0), 1)  # type: ignore
+                        continue
+                    raise error
+
+    async def audio_classification(
+        self,
+        audio: ContentT,
+        *,
+        model: Optional[str] = None,
+    ) -> List[ClassificationOutput]:
+        """
+        Perform audio classification on the provided audio content.
+
+        Args:
+            audio (Union[str, Path, bytes, BinaryIO]):
+                The audio content to classify. It can be raw audio bytes, a local audio file, or a URL pointing to an
+                audio file.
+            model (`str`, *optional*):
+                The model to use for audio classification. Can be a model ID hosted on the Hugging Face Hub
+                or a URL to a deployed Inference Endpoint. If not provided, the default recommended model for
+                audio classification will be used.
+
+        Returns:
+            `List[Dict]`: The classification output containing the predicted label and its confidence.
+
+        Raises:
+            [`InferenceTimeoutError`]:
+                If the model is unavailable or the request times out.
+            `aiohttp.ClientResponseError`:
+                If the request fails with an HTTP error status code other than HTTP 503.
+
+        Example:
+        ```py
+        # Must be run in an async context
+        >>> from huggingface_hub import AsyncInferenceClient
+        >>> client = AsyncInferenceClient()
+        >>> await client.audio_classification("audio.flac")
+        [{'score': 0.4976358711719513, 'label': 'hap'}, {'score': 0.3677836060523987, 'label': 'neu'},...]
+        ```
+        """
+        response = await self.post(data=audio, model=model, task="audio-classification")
+        return _bytes_to_dict(response)
+
+    async def automatic_speech_recognition(
+        self,
+        audio: ContentT,
+        *,
+        model: Optional[str] = None,
+    ) -> str:
+        """
+        Perform automatic speech recognition (ASR or audio-to-text) on the given audio content.
+
+        Args:
+            audio (Union[str, Path, bytes, BinaryIO]):
+                The content to transcribe. It can be raw audio bytes, local audio file, or a URL to an audio file.
+            model (`str`, *optional*):
+                The model to use for ASR. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed
+                Inference Endpoint. If not provided, the default recommended model for ASR will be used.
+
+        Returns:
+            str: The transcribed text.
+
+        Raises:
+            [`InferenceTimeoutError`]:
+                If the model is unavailable or the request times out.
+            `aiohttp.ClientResponseError`:
+                If the request fails with an HTTP error status code other than HTTP 503.
+
+        Example:
+        ```py
+        # Must be run in an async context
+        >>> from huggingface_hub import AsyncInferenceClient
+        >>> client = AsyncInferenceClient()
+        >>> await client.automatic_speech_recognition("hello_world.flac")
+        "hello world"
+        ```
+        """
+        response = await self.post(data=audio, model=model, task="automatic-speech-recognition")
+        return _bytes_to_dict(response)["text"]
+
+    async def conversational(
+        self,
+        text: str,
+        generated_responses: Optional[List[str]] = None,
+        past_user_inputs: Optional[List[str]] = None,
+        *,
+        parameters: Optional[Dict[str, Any]] = None,
+        model: Optional[str] = None,
+    ) -> ConversationalOutput:
+        """
+        Generate conversational responses based on the given input text (i.e. chat with the API).
+
+        Args:
+            text (`str`):
+                The last input from the user in the conversation.
+            generated_responses (`List[str]`, *optional*):
+                A list of strings corresponding to the earlier replies from the model. Defaults to None.
+            past_user_inputs (`List[str]`, *optional*):
+                A list of strings corresponding to the earlier replies from the user. Should be the same length as
+                `generated_responses`. Defaults to None.
+            parameters (`Dict[str, Any]`, *optional*):
+                Additional parameters for the conversational task. Defaults to None. For more details about the available
+                parameters, please refer to [this page](https://huggingface.co/docs/api-inference/detailed_parameters#conversational-task)
+            model (`str`, *optional*):
+                The model to use for the conversational task. Can be a model ID hosted on the Hugging Face Hub or a URL to
+                a deployed Inference Endpoint. If not provided, the default recommended conversational model will be used.
+                Defaults to None.
+
+        Returns:
+            `Dict`: The generated conversational output.
+
+        Raises:
+            [`InferenceTimeoutError`]:
+                If the model is unavailable or the request times out.
+            `aiohttp.ClientResponseError`:
+                If the request fails with an HTTP error status code other than HTTP 503.
+
+        Example:
+        ```py
+        # Must be run in an async context
+        >>> from huggingface_hub import AsyncInferenceClient
+        >>> client = AsyncInferenceClient()
+        >>> output = await client.conversational("Hi, who are you?")
+        >>> output
+        {'generated_text': 'I am the one who knocks.', 'conversation': {'generated_responses': ['I am the one who knocks.'], 'past_user_inputs': ['Hi, who are you?']}, 'warnings': ['Setting `pad_token_id` to `eos_token_id`:50256 async for open-end generation.']}
+        >>> await client.conversational(
+        ...     "Wow, that's scary!",
+        ...     generated_responses=output["conversation"]["generated_responses"],
+        ...     past_user_inputs=output["conversation"]["past_user_inputs"],
+        ... )
+        ```
+        """
+        payload: Dict[str, Any] = {"inputs": {"text": text}}
+        if generated_responses is not None:
+            payload["inputs"]["generated_responses"] = generated_responses
+        if past_user_inputs is not None:
+            payload["inputs"]["past_user_inputs"] = past_user_inputs
+        if parameters is not None:
+            payload["parameters"] = parameters
+        response = await self.post(json=payload, model=model, task="conversational")
+        return _bytes_to_dict(response)
+
+    async def feature_extraction(self, text: str, *, model: Optional[str] = None) -> "np.ndarray":
+        """
+        Generate embeddings for a given text.
+
+        Args:
+            text (`str`):
+                The text to embed.
+            model (`str`, *optional*):
+                The model to use for the conversational task. Can be a model ID hosted on the Hugging Face Hub or a URL to
+                a deployed Inference Endpoint. If not provided, the default recommended conversational model will be used.
+                Defaults to None.
+
+        Returns:
+            `np.ndarray`: The embedding representing the input text as a float32 numpy array.
+
+        Raises:
+            [`InferenceTimeoutError`]:
+                If the model is unavailable or the request times out.
+            `aiohttp.ClientResponseError`:
+                If the request fails with an HTTP error status code other than HTTP 503.
+
+        Example:
+        ```py
+        # Must be run in an async context
+        >>> from huggingface_hub import AsyncInferenceClient
+        >>> client = AsyncInferenceClient()
+        >>> await client.feature_extraction("Hi, who are you?")
+        array([[ 2.424802  ,  2.93384   ,  1.1750331 , ...,  1.240499, -0.13776633, -0.7889173 ],
+        [-0.42943227, -0.6364878 , -1.693462  , ...,  0.41978157, -2.4336355 ,  0.6162071 ],
+        ...,
+        [ 0.28552425, -0.928395  , -1.2077185 , ...,  0.76810825, -2.1069427 ,  0.6236161 ]], dtype=float32)
+        ```
+        """
+        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")
+
+    async def image_classification(
+        self,
+        image: ContentT,
+        *,
+        model: Optional[str] = None,
+    ) -> List[ClassificationOutput]:
+        """
+        Perform image classification on the given image using the specified model.
+
+        Args:
+            image (`Union[str, Path, bytes, BinaryIO]`):
+                The image to classify. It can be raw bytes, an image file, or a URL to an online image.
+            model (`str`, *optional*):
+                The model to use for image classification. Can be a model ID hosted on the Hugging Face Hub or a URL to a
+                deployed Inference Endpoint. If not provided, the default recommended model for image classification will be used.
+
+        Returns:
+            `List[Dict]`: a list of dictionaries containing the predicted label and associated probability.
+
+        Raises:
+            [`InferenceTimeoutError`]:
+                If the model is unavailable or the request times out.
+            `aiohttp.ClientResponseError`:
+                If the request fails with an HTTP error status code other than HTTP 503.
+
+        Example:
+        ```py
+        # Must be run in an async context
+        >>> from huggingface_hub import AsyncInferenceClient
+        >>> client = AsyncInferenceClient()
+        >>> await client.image_classification("https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Cute_dog.jpg/320px-Cute_dog.jpg")
+        [{'score': 0.9779096841812134, 'label': 'Blenheim spaniel'}, ...]
+        ```
+        """
+        response = await self.post(data=image, model=model, task="image-classification")
+        return _bytes_to_dict(response)
+
+    async def image_segmentation(
+        self,
+        image: ContentT,
+        *,
+        model: Optional[str] = None,
+    ) -> List[ImageSegmentationOutput]:
+        """
+        Perform image segmentation on the given image using the specified model.
+
+        <Tip warning={true}>
+
+        You must have `PIL` installed if you want to work with images (`pip install Pillow`).
+
+        </Tip>
+
+        Args:
+            image (`Union[str, Path, bytes, BinaryIO]`):
+                The image to segment. It can be raw bytes, an image file, or a URL to an online image.
+            model (`str`, *optional*):
+                The model to use for image segmentation. Can be a model ID hosted on the Hugging Face Hub or a URL to a
+                deployed Inference Endpoint. If not provided, the default recommended model for image segmentation will be used.
+
+        Returns:
+            `List[Dict]`: A list of dictionaries containing the segmented masks and associated attributes.
+
+        Raises:
+            [`InferenceTimeoutError`]:
+                If the model is unavailable or the request times out.
+            `aiohttp.ClientResponseError`:
+                If the request fails with an HTTP error status code other than HTTP 503.
+
+        Example:
+        ```py
+        # Must be run in an async context
+        >>> from huggingface_hub import AsyncInferenceClient
+        >>> client = AsyncInferenceClient()
+        >>> await client.image_segmentation("cat.jpg"):
+        [{'score': 0.989008, 'label': 'LABEL_184', 'mask': <PIL.PngImagePlugin.PngImageFile image mode=L size=400x300 at 0x7FDD2B129CC0>}, ...]
+        ```
+        """
+
+        # Segment
+        response = await self.post(data=image, model=model, task="image-segmentation")
+        output = _bytes_to_dict(response)
+
+        # Parse masks as PIL Image
+        if not isinstance(output, list):
+            raise ValueError(f"Server output must be a list. Got {type(output)}: {str(output)[:200]}...")
+        for item in output:
+            item["mask"] = _b64_to_image(item["mask"])
+        return output
+
+    async def image_to_image(
+        self,
+        image: ContentT,
+        prompt: Optional[str] = None,
+        *,
+        negative_prompt: Optional[str] = None,
+        height: Optional[int] = None,
+        width: Optional[int] = None,
+        num_inference_steps: Optional[int] = None,
+        guidance_scale: Optional[float] = None,
+        model: Optional[str] = None,
+        **kwargs,
+    ) -> "Image":
+        """
+        Perform image-to-image translation using a specified model.
+
+        <Tip warning={true}>
+
+        You must have `PIL` installed if you want to work with images (`pip install Pillow`).
+
+        </Tip>
+
+        Args:
+            image (`Union[str, Path, bytes, BinaryIO]`):
+                The input image for translation. It can be raw bytes, an image file, or a URL to an online image.
+            prompt (`str`, *optional*):
+                The text prompt to guide the image generation.
+            negative_prompt (`str`, *optional*):
+                A negative prompt to guide the translation process.
+            height (`int`, *optional*):
+                The height in pixels of the generated image.
+            width (`int`, *optional*):
+                The width in pixels of the generated image.
+            num_inference_steps (`int`, *optional*):
+                The number of denoising steps. More denoising steps usually lead to a higher quality image at the
+                expense of slower inference.
+            guidance_scale (`float`, *optional*):
+                Higher guidance scale encourages to generate images that are closely linked to the text `prompt`,
+                usually at the expense of lower image quality.
+            model (`str`, *optional*):
+                The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed
+                Inference Endpoint. This parameter overrides the model defined at the instance level. Defaults to None.
+
+        Returns:
+            `Image`: The translated image.
+
+        Raises:
+            [`InferenceTimeoutError`]:
+                If the model is unavailable or the request times out.
+            `aiohttp.ClientResponseError`:
+                If the request fails with an HTTP error status code other than HTTP 503.
+
+        Example:
+        ```py
+        # Must be run in an async context
+        >>> from huggingface_hub import AsyncInferenceClient
+        >>> client = AsyncInferenceClient()
+        >>> image = await client.image_to_image("cat.jpg", prompt="turn the cat into a tiger")
+        >>> image.save("tiger.jpg")
+        ```
+        """
+        parameters = {
+            "prompt": prompt,
+            "negative_prompt": negative_prompt,
+            "height": height,
+            "width": width,
+            "num_inference_steps": num_inference_steps,
+            "guidance_scale": guidance_scale,
+            **kwargs,
+        }
+        if all(parameter is None for parameter in parameters.values()):
+            # Either only an image to send => send as raw bytes
+            data = image
+            payload: Optional[Dict[str, Any]] = None
+        else:
+            # Or an image + some parameters => use base64 encoding
+            data = None
+            payload = {"inputs": _b64_encode(image)}
+            for key, value in parameters.items():
+                if value is not None:
+                    payload[key] = value
+
+        response = await self.post(json=payload, data=data, model=model, task="image-to-image")
+        return _bytes_to_image(response)
+
+    async def image_to_text(self, image: ContentT, *, model: Optional[str] = None) -> str:
+        """
+        Takes an input image and return text.
+
+        Models can have very different outputs depending on your use case (image captioning, optical character recognition
+        (OCR), Pix2Struct, etc). Please have a look to the model card to learn more about a model's specificities.
+
+        Args:
+            image (`Union[str, Path, bytes, BinaryIO]`):
+                The input image to caption. It can be raw bytes, an image file, or a URL to an online image..
+            model (`str`, *optional*):
+                The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed
+                Inference Endpoint. This parameter overrides the model defined at the instance level. Defaults to None.
+
+        Returns:
+            `str`: The generated text.
+
+        Raises:
+            [`InferenceTimeoutError`]:
+                If the model is unavailable or the request times out.
+            `aiohttp.ClientResponseError`:
+                If the request fails with an HTTP error status code other than HTTP 503.
+
+        Example:
+        ```py
+        # Must be run in an async context
+        >>> from huggingface_hub import AsyncInferenceClient
+        >>> client = AsyncInferenceClient()
+        >>> await client.image_to_text("cat.jpg")
+        'a cat standing in a grassy field '
+        >>> await client.image_to_text("https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Cute_dog.jpg/320px-Cute_dog.jpg")
+        'a dog laying on the grass next to a flower pot '
+        ```
+        """
+        response = await self.post(data=image, model=model, task="image-to-text")
+        return _bytes_to_dict(response)[0]["generated_text"]
+
+    async def sentence_similarity(
+        self, sentence: str, other_sentences: List[str], *, model: Optional[str] = None
+    ) -> List[float]:
+        """
+        Compute the semantic similarity between a sentence and a list of other sentences by comparing their embeddings.
+
+        Args:
+            sentence (`str`):
+                The main sentence to compare to others.
+            other_sentences (`List[str]`):
+                The list of sentences to compare to.
+            model (`str`, *optional*):
+                The model to use for the conversational task. Can be a model ID hosted on the Hugging Face Hub or a URL to
+                a deployed Inference Endpoint. If not provided, the default recommended conversational model will be used.
+                Defaults to None.
+
+        Returns:
+            `List[float]`: The embedding representing the input text.
+
+        Raises:
+            [`InferenceTimeoutError`]:
+                If the model is unavailable or the request times out.
+            `aiohttp.ClientResponseError`:
+                If the request fails with an HTTP error status code other than HTTP 503.
+
+        Example:
+        ```py
+        # Must be run in an async context
+        >>> from huggingface_hub import AsyncInferenceClient
+        >>> client = AsyncInferenceClient()
+        >>> await client.sentence_similarity(
+        ...     "Machine learning is so easy.",
+        ...     other_sentences=[
+        ...         "Deep learning is so straightforward.",
+        ...         "This is so difficult, like rocket science.",
+        ...         "I can't believe how much I struggled with this.",
+        ...     ],
+        ... )
+        [0.7785726189613342, 0.45876261591911316, 0.2906220555305481]
+        ```
+        """
+        response = await self.post(
+            json={"inputs": {"source_sentence": sentence, "sentences": other_sentences}},
+            model=model,
+            task="sentence-similarity",
+        )
+        return _bytes_to_dict(response)
+
+    async def summarization(
+        self,
+        text: str,
+        *,
+        parameters: Optional[Dict[str, Any]] = None,
+        model: Optional[str] = None,
+    ) -> str:
+        """
+        Generate a summary of a given text using a specified model.
+
+        Args:
+            text (`str`):
+                The input text to summarize.
+            parameters (`Dict[str, Any]`, *optional*):
+                Additional parameters for summarization. Check out this [page](https://huggingface.co/docs/api-inference/detailed_parameters#summarization-task)
+                for more details.
+            model (`str`, *optional*):
+                The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed
+                Inference Endpoint. This parameter overrides the model defined at the instance level. Defaults to None.
+
+        Returns:
+            `str`: The generated summary text.
+
+        Raises:
+            [`InferenceTimeoutError`]:
+                If the model is unavailable or the request times out.
+            `aiohttp.ClientResponseError`:
+                If the request fails with an HTTP error status code other than HTTP 503.
+
+        Example:
+        ```py
+        # Must be run in an async context
+        >>> from huggingface_hub import AsyncInferenceClient
+        >>> client = AsyncInferenceClient()
+        >>> await client.summarization("The Eiffel tower...")
+        'The Eiffel tower is one of the most famous landmarks in the world....'
+        ```
+        """
+        payload: Dict[str, Any] = {"inputs": text}
+        if parameters is not None:
+            payload["parameters"] = parameters
+        response = await self.post(json=payload, model=model, task="summarization")
+        return _bytes_to_dict(response)[0]["summary_text"]
+
+    @overload
+    async def text_generation(  # type: ignore
+        self,
+        prompt: str,
+        *,
+        details: Literal[False] = ...,
+        stream: Literal[False] = ...,
+        model: Optional[str] = None,
+        do_sample: bool = False,
+        max_new_tokens: int = 20,
+        best_of: Optional[int] = None,
+        repetition_penalty: Optional[float] = None,
+        return_full_text: bool = False,
+        seed: Optional[int] = None,
+        stop_sequences: Optional[List[str]] = None,
+        temperature: Optional[float] = None,
+        top_k: Optional[int] = None,
+        top_p: Optional[float] = None,
+        truncate: Optional[int] = None,
+        typical_p: Optional[float] = None,
+        watermark: bool = False,
+    ) -> str:
+        ...
+
+    @overload
+    async def text_generation(  # type: ignore
+        self,
+        prompt: str,
+        *,
+        details: Literal[True] = ...,
+        stream: Literal[False] = ...,
+        model: Optional[str] = None,
+        do_sample: bool = False,
+        max_new_tokens: int = 20,
+        best_of: Optional[int] = None,
+        repetition_penalty: Optional[float] = None,
+        return_full_text: bool = False,
+        seed: Optional[int] = None,
+        stop_sequences: Optional[List[str]] = None,
+        temperature: Optional[float] = None,
+        top_k: Optional[int] = None,
+        top_p: Optional[float] = None,
+        truncate: Optional[int] = None,
+        typical_p: Optional[float] = None,
+        watermark: bool = False,
+    ) -> TextGenerationResponse:
+        ...
+
+    @overload
+    async def text_generation(  # type: ignore
+        self,
+        prompt: str,
+        *,
+        details: Literal[False] = ...,
+        stream: Literal[True] = ...,
+        model: Optional[str] = None,
+        do_sample: bool = False,
+        max_new_tokens: int = 20,
+        best_of: Optional[int] = None,
+        repetition_penalty: Optional[float] = None,
+        return_full_text: bool = False,
+        seed: Optional[int] = None,
+        stop_sequences: Optional[List[str]] = None,
+        temperature: Optional[float] = None,
+        top_k: Optional[int] = None,
+        top_p: Optional[float] = None,
+        truncate: Optional[int] = None,
+        typical_p: Optional[float] = None,
+        watermark: bool = False,
+    ) -> AsyncIterable[str]:
+        ...
+
+    @overload
+    async def text_generation(
+        self,
+        prompt: str,
+        *,
+        details: Literal[True] = ...,
+        stream: Literal[True] = ...,
+        model: Optional[str] = None,
+        do_sample: bool = False,
+        max_new_tokens: int = 20,
+        best_of: Optional[int] = None,
+        repetition_penalty: Optional[float] = None,
+        return_full_text: bool = False,
+        seed: Optional[int] = None,
+        stop_sequences: Optional[List[str]] = None,
+        temperature: Optional[float] = None,
+        top_k: Optional[int] = None,
+        top_p: Optional[float] = None,
+        truncate: Optional[int] = None,
+        typical_p: Optional[float] = None,
+        watermark: bool = False,
+    ) -> AsyncIterable[TextGenerationStreamResponse]:
+        ...
+
+    async def text_generation(
+        self,
+        prompt: str,
+        *,
+        details: bool = False,
+        stream: bool = False,
+        model: Optional[str] = None,
+        do_sample: bool = False,
+        max_new_tokens: int = 20,
+        best_of: Optional[int] = None,
+        repetition_penalty: Optional[float] = None,
+        return_full_text: bool = False,
+        seed: Optional[int] = None,
+        stop_sequences: Optional[List[str]] = None,
+        temperature: Optional[float] = None,
+        top_k: Optional[int] = None,
+        top_p: Optional[float] = None,
+        truncate: Optional[int] = None,
+        typical_p: Optional[float] = None,
+        watermark: bool = False,
+        decoder_input_details: bool = False,
+    ) -> Union[str, TextGenerationResponse, AsyncIterable[str], AsyncIterable[TextGenerationStreamResponse]]:
+        """
+        Given a prompt, generate the following text.
+
+        It is recommended to have Pydantic installed in order to get inputs validated. This is preferable as it allow
+        early failures.
+
+        API endpoint is supposed to run with the `text-generation-inference` backend (TGI). This backend is the
+        go-to solution to run large language models at scale. However, for some smaller models (e.g. "gpt2") the
+        default `transformers` + `api-inference` solution is still in use. Both approaches have very similar APIs, but
+        not exactly the same. This method is compatible with both approaches but some parameters are only available for
+        `text-generation-inference`. If some parameters are ignored, a warning message is triggered but the process
+        continues correctly.
+
+        To learn more about the TGI project, please refer to https://github.com/huggingface/text-generation-inference.
+
+        Args:
+            prompt (`str`):
+                Input text.
+            details (`bool`, *optional*):
+                By default, text_generation returns a string. Pass `details=True` if you want a detailed output (tokens,
+                probabilities, seed, finish reason, etc.). Only available for models running on with the
+                `text-generation-inference` backend.
+            stream (`bool`, *optional*):
+                By default, text_generation returns the full generated text. Pass `stream=True` if you want a stream of
+                tokens to be returned. Only available for models running on with the `text-generation-inference`
+                backend.
+            model (`str`, *optional*):
+                The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed
+                Inference Endpoint. This parameter overrides the model defined at the instance level. Defaults to None.
+            do_sample (`bool`):
+                Activate logits sampling
+            max_new_tokens (`int`):
+                Maximum number of generated tokens
+            best_of (`int`):
+                Generate best_of sequences and return the one if the highest token logprobs
+            repetition_penalty (`float`):
+                The parameter for repetition penalty. 1.0 means no penalty. See [this
+                paper](https://arxiv.org/pdf/1909.05858.pdf) for more details.
+            return_full_text (`bool`):
+                Whether to prepend the prompt to the generated text
+            seed (`int`):
+                Random sampling seed
+            stop_sequences (`List[str]`):
+                Stop generating tokens if a member of `stop_sequences` is generated
+            temperature (`float`):
+                The value used to module the logits distribution.
+            top_k (`int`):
+                The number of highest probability vocabulary tokens to keep for top-k-filtering.
+            top_p (`float`):
+                If set to < 1, only the smallest set of most probable tokens with probabilities that add up to `top_p` or
+                higher are kept for generation.
+            truncate (`int`):
+                Truncate inputs tokens to the given size
+            typical_p (`float`):
+                Typical Decoding mass
+                See [Typical Decoding for Natural Language Generation](https://arxiv.org/abs/2202.00666) for more information
+            watermark (`bool`):
+                Watermarking with [A Watermark for Large Language Models](https://arxiv.org/abs/2301.10226)
+            decoder_input_details (`bool`):
+                Return the decoder input token logprobs and ids. You must set `details=True` as well for it to be taken
+                into account. Defaults to `False`.
+
+        Returns:
+            `Union[str, TextGenerationResponse, Iterable[str], Iterable[TextGenerationStreamResponse]]`:
+            Generated text returned from the server:
+            - if `stream=False` and `details=False`, the generated text is returned as a `str` (default)
+            - if `stream=True` and `details=False`, the generated text is returned token by token as a `Iterable[str]`
+            - if `stream=False` and `details=True`, the generated text is returned with more details as a [`~huggingface_hub.inference._text_generation.TextGenerationResponse`]
+            - if `details=True` and `stream=True`, the generated text is returned token by token as a iterable of [`~huggingface_hub.inference._text_generation.TextGenerationStreamResponse`]
+
+        Raises:
+            `ValidationError`:
+                If input values are not valid. No HTTP call is made to the server.
+            [`InferenceTimeoutError`]:
+                If the model is unavailable or the request times out.
+            `aiohttp.ClientResponseError`:
+                If the request fails with an HTTP error status code other than HTTP 503.
+
+        Example:
+        ```py
+        # Must be run in an async context
+        >>> from huggingface_hub import AsyncInferenceClient
+        >>> client = AsyncInferenceClient()
+
+        # Case 1: generate text
+        >>> await client.text_generation("The huggingface_hub library is ", max_new_tokens=12)
+        '100% open source and built to be easy to use.'
+
+        # Case 2: iterate over the generated tokens. Useful async for large generation.
+        >>> async for token in await client.text_generation("The huggingface_hub library is ", max_new_tokens=12, stream=True):
+        ...     print(token)
+        100
+        %
+        open
+        source
+        and
+        built
+        to
+        be
+        easy
+        to
+        use
+        .
+
+        # Case 3: get more details about the generation process.
+        >>> await client.text_generation("The huggingface_hub library is ", max_new_tokens=12, details=True)
+        TextGenerationResponse(
+            generated_text='100% open source and built to be easy to use.',
+            details=Details(
+                finish_reason=<FinishReason.Length: 'length'>,
+                generated_tokens=12,
+                seed=None,
+                prefill=[
+                    InputToken(id=487, text='The', logprob=None),
+                    InputToken(id=53789, text=' hugging', logprob=-13.171875),
+                    (...)
+                    InputToken(id=204, text=' ', logprob=-7.0390625)
+                ],
+                tokens=[
+                    Token(id=1425, text='100', logprob=-1.0175781, special=False),
+                    Token(id=16, text='%', logprob=-0.0463562, special=False),
+                    (...)
+                    Token(id=25, text='.', logprob=-0.5703125, special=False)
+                ],
+                best_of_sequences=None
+            )
+        )
+
+        # Case 4: iterate over the generated tokens with more details.
+        # Last object is more complete, containing the full generated text and the finish reason.
+        >>> async for details in await client.text_generation("The huggingface_hub library is ", max_new_tokens=12, details=True, stream=True):
+        ...     print(details)
+        ...
+        TextGenerationStreamResponse(token=Token(id=1425, text='100', logprob=-1.0175781, special=False), generated_text=None, details=None)
+        TextGenerationStreamResponse(token=Token(id=16, text='%', logprob=-0.0463562, special=False), generated_text=None, details=None)
+        TextGenerationStreamResponse(token=Token(id=1314, text=' open', logprob=-1.3359375, special=False), generated_text=None, details=None)
+        TextGenerationStreamResponse(token=Token(id=3178, text=' source', logprob=-0.28100586, special=False), generated_text=None, details=None)
+        TextGenerationStreamResponse(token=Token(id=273, text=' and', logprob=-0.5961914, special=False), generated_text=None, details=None)
+        TextGenerationStreamResponse(token=Token(id=3426, text=' built', logprob=-1.9423828, special=False), generated_text=None, details=None)
+        TextGenerationStreamResponse(token=Token(id=271, text=' to', logprob=-1.4121094, special=False), generated_text=None, details=None)
+        TextGenerationStreamResponse(token=Token(id=314, text=' be', logprob=-1.5224609, special=False), generated_text=None, details=None)
+        TextGenerationStreamResponse(token=Token(id=1833, text=' easy', logprob=-2.1132812, special=False), generated_text=None, details=None)
+        TextGenerationStreamResponse(token=Token(id=271, text=' to', logprob=-0.08520508, special=False), generated_text=None, details=None)
+        TextGenerationStreamResponse(token=Token(id=745, text=' use', logprob=-0.39453125, special=False), generated_text=None, details=None)
+        TextGenerationStreamResponse(token=Token(
+            id=25,
+            text='.',
+            logprob=-0.5703125,
+            special=False),
+            generated_text='100% open source and built to be easy to use.',
+            details=StreamDetails(finish_reason=<FinishReason.Length: 'length'>, generated_tokens=12, seed=None)
+        )
+        ```
+        """
+        # NOTE: Text-generation integration is taken from the text-generation-inference project. It has more features
+        # like input/output validation (if Pydantic is installed). See `_text_generation.py` header for more details.
+
+        if decoder_input_details and not details:
+            warnings.warn(
+                "`decoder_input_details=True` has been passed to the server but `details=False` is set meaning that"
+                " the output from the server will be truncated."
+            )
+            decoder_input_details = False
+
+        # Validate parameters
+        parameters = TextGenerationParameters(
+            best_of=best_of,
+            details=details,
+            do_sample=do_sample,
+            max_new_tokens=max_new_tokens,
+            repetition_penalty=repetition_penalty,
+            return_full_text=return_full_text,
+            seed=seed,
+            stop=stop_sequences if stop_sequences is not None else [],
+            temperature=temperature,
+            top_k=top_k,
+            top_p=top_p,
+            truncate=truncate,
+            typical_p=typical_p,
+            watermark=watermark,
+            decoder_input_details=decoder_input_details,
+        )
+        request = TextGenerationRequest(inputs=prompt, stream=stream, parameters=parameters)
+        payload = asdict(request)
+
+        # Remove some parameters if not a TGI server
+        if not _is_tgi_server(model):
+            ignored_parameters = []
+            for key in "watermark", "stop", "details", "decoder_input_details":
+                if payload["parameters"][key] is not None:
+                    ignored_parameters.append(key)
+                del payload["parameters"][key]
+            if len(ignored_parameters) > 0:
+                warnings.warn(
+                    (
+                        "API endpoint/model for text-generation is not served via TGI. Ignoring parameters"
+                        f" {ignored_parameters}."
+                    ),
+                    UserWarning,
+                )
+            if details:
+                warnings.warn(
+                    (
+                        "API endpoint/model for text-generation is not served via TGI. Parameter `details=True` will"
+                        " be ignored meaning only the generated text will be returned."
+                    ),
+                    UserWarning,
+                )
+                details = False
+            if stream:
+                raise ValueError(
+                    "API endpoint/model for text-generation is not served via TGI. Cannot return output as a stream."
+                    " Please pass `stream=False` as input."
+                )
+
+        # Handle errors separately for more precise error messages
+        try:
+            bytes_output = await self.post(json=payload, model=model, task="text-generation", stream=stream)  # type: ignore
+        except _import_aiohttp().ClientResponseError as e:
+            error_message = getattr(e, "response_error_payload", {}).get("error", "")
+            if e.code == 400 and "The following `model_kwargs` are not used by the model" in error_message:
+                _set_as_non_tgi(model)
+                return await self.text_generation(  # type: ignore
+                    prompt=prompt,
+                    details=details,
+                    stream=stream,
+                    model=model,
+                    do_sample=do_sample,
+                    max_new_tokens=max_new_tokens,
+                    best_of=best_of,
+                    repetition_penalty=repetition_penalty,
+                    return_full_text=return_full_text,
+                    seed=seed,
+                    stop_sequences=stop_sequences,
+                    temperature=temperature,
+                    top_k=top_k,
+                    top_p=top_p,
+                    truncate=truncate,
+                    typical_p=typical_p,
+                    watermark=watermark,
+                    decoder_input_details=decoder_input_details,
+                )
+            raise_text_generation_error(e)
+
+        # Parse output
+        if stream:
+            return _async_stream_text_generation_response(bytes_output, details)  # type: ignore
+
+        data = _bytes_to_dict(bytes_output)[0]
+        return TextGenerationResponse(**data) if details else data["generated_text"]
+
+    async def text_to_image(
+        self,
+        prompt: str,
+        *,
+        negative_prompt: Optional[str] = None,
+        height: Optional[float] = None,
+        width: Optional[float] = None,
+        num_inference_steps: Optional[float] = None,
+        guidance_scale: Optional[float] = None,
+        model: Optional[str] = None,
+        **kwargs,
+    ) -> "Image":
+        """
+        Generate an image based on a given text using a specified model.
+
+        <Tip warning={true}>
+
+        You must have `PIL` installed if you want to work with images (`pip install Pillow`).
+
+        </Tip>
+
+        Args:
+            prompt (`str`):
+                The prompt to generate an image from.
+            negative_prompt (`str`, *optional*):
+                An optional negative prompt for the image generation.
+            height (`float`, *optional*):
+                The height in pixels of the image to generate.
+            width (`float`, *optional*):
+                The width in pixels of the image to generate.
+            num_inference_steps (`int`, *optional*):
+                The number of denoising steps. More denoising steps usually lead to a higher quality image at the
+                expense of slower inference.
+            guidance_scale (`float`, *optional*):
+                Higher guidance scale encourages to generate images that are closely linked to the text `prompt`,
+                usually at the expense of lower image quality.
+            model (`str`, *optional*):
+                The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed
+                Inference Endpoint. This parameter overrides the model defined at the instance level. Defaults to None.
+
+        Returns:
+            `Image`: The generated image.
+
+        Raises:
+            [`InferenceTimeoutError`]:
+                If the model is unavailable or the request times out.
+            `aiohttp.ClientResponseError`:
+                If the request fails with an HTTP error status code other than HTTP 503.
+
+        Example:
+        ```py
+        # Must be run in an async context
+        >>> from huggingface_hub import AsyncInferenceClient
+        >>> client = AsyncInferenceClient()
+
+        >>> image = await client.text_to_image("An astronaut riding a horse on the moon.")
+        >>> image.save("astronaut.png")
+
+        >>> image = await client.text_to_image(
+        ...     "An astronaut riding a horse on the moon.",
+        ...     negative_prompt="low resolution, blurry",
+        ...     model="stabilityai/stable-diffusion-2-1",
+        ... )
+        >>> image.save("better_astronaut.png")
+        ```
+        """
+        parameters = {
+            "inputs": prompt,
+            "negative_prompt": negative_prompt,
+            "height": height,
+            "width": width,
+            "num_inference_steps": num_inference_steps,
+            "guidance_scale": guidance_scale,
+            **kwargs,
+        }
+        payload = {}
+        for key, value in parameters.items():
+            if value is not None:
+                payload[key] = value
+        response = await self.post(json=payload, model=model, task="text-to-image")
+        return _bytes_to_image(response)
+
+    async def text_to_speech(self, text: str, *, model: Optional[str] = None) -> bytes:
+        """
+        Synthesize an audio of a voice pronouncing a given text.
+
+        Args:
+            text (`str`):
+                The text to synthesize.
+            model (`str`, *optional*):
+                The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed
+                Inference Endpoint. This parameter overrides the model defined at the instance level. Defaults to None.
+
+        Returns:
+            `bytes`: The generated audio.
+
+        Raises:
+            [`InferenceTimeoutError`]:
+                If the model is unavailable or the request times out.
+            `aiohttp.ClientResponseError`:
+                If the request fails with an HTTP error status code other than HTTP 503.
+
+        Example:
+        ```py
+        # Must be run in an async context
+        >>> from pathlib import Path
+        >>> from huggingface_hub import AsyncInferenceClient
+        >>> client = AsyncInferenceClient()
+
+        >>> audio = await client.text_to_speech("Hello world")
+        >>> Path("hello_world.flac").write_bytes(audio)
+        ```
+        """
+        return await self.post(json={"inputs": text}, model=model, task="text-to-speech")
+
+    async def zero_shot_image_classification(
+        self, image: ContentT, labels: List[str], *, model: Optional[str] = None
+    ) -> List[ClassificationOutput]:
+        """
+        Provide input image and text labels to predict text labels for the image.
+
+        Args:
+            image (`Union[str, Path, bytes, BinaryIO]`):
+                The input image to caption. It can be raw bytes, an image file, or a URL to an online image.
+            labels (`List[str]`):
+                List of string possible labels. The `len(labels)` must be greater than 1.
+            model (`str`, *optional*):
+                The model to use for inference. Can be a model ID hosted on the Hugging Face Hub or a URL to a deployed
+                Inference Endpoint. This parameter overrides the model defined at the instance level. Defaults to None.
+
+        Returns:
+            `List[Dict]`: List of classification outputs containing the predicted labels and their confidence.
+
+        Raises:
+            [`InferenceTimeoutError`]:
+                If the model is unavailable or the request times out.
+            `aiohttp.ClientResponseError`:
+                If the request fails with an HTTP error status code other than HTTP 503.
+
+        Example:
+        ```py
+        # Must be run in an async context
+        >>> from huggingface_hub import AsyncInferenceClient
+        >>> client = AsyncInferenceClient()
+
+        >>> await client.zero_shot_image_classification(
+        ...     "https://upload.wikimedia.org/wikipedia/commons/thumb/4/43/Cute_dog.jpg/320px-Cute_dog.jpg",
+        ...     labels=["dog", "cat", "horse"],
+        ... )
+        [{"label": "dog", "score": 0.956}, ...]
+        ```
+        """
+
+        # Raise valueerror if input is less than 2 labels
+        if len(labels) < 2:
+            raise ValueError("You must specify at least 2 classes to compare. Please specify more than 1 class.")
+
+        response = await self.post(
+            json={"image": _b64_encode(image), "parameters": {"candidate_labels": ",".join(labels)}},
+            model=model,
+            task="zero-shot-image-classification",
+        )
+        return _bytes_to_dict(response)
+
+    def _resolve_url(self, model: Optional[str] = None, task: Optional[str] = None) -> str:
+        model = model or self.model
+
+        # If model is already a URL, ignore `task` and return directly
+        if model is not None and (model.startswith("http://") or model.startswith("https://")):
+            return model
+
+        # # If no model but task is set => fetch the recommended one for this task
+        if model is None:
+            if task is None:
+                raise ValueError(
+                    "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)
+
+        # Compute InferenceAPI url
+        return (
+            # Feature-extraction and sentence-similarity are the only cases where we handle models with several tasks.
+            f"{INFERENCE_ENDPOINT}/pipeline/{task}/{model}"
+            if task in ("feature-extraction", "sentence-similarity")
+            # Otherwise, we use the default endpoint
+            else f"{INFERENCE_ENDPOINT}/models/{model}"
+        )
diff --git a/src/huggingface_hub/inference/_text_generation.py b/src/huggingface_hub/inference/_text_generation.py
index b7a26c7e..dcdc9f0b 100644
--- a/src/huggingface_hub/inference/_text_generation.py
+++ b/src/huggingface_hub/inference/_text_generation.py
@@ -455,8 +455,10 @@ def raise_text_generation_error(http_error: HTTPError) -> NoReturn:
             The HTTPError that have been raised.
     """
     # Try to parse a Text Generation Inference error
+
     try:
-        payload = http_error.response.json()
+        # Hacky way to retrieve payload in case of aiohttp error
+        payload = getattr(http_error, "response_error_payload", None) or http_error.response.json()
         message = payload.get("error")
         error_type = payload.get("error_type")
     except Exception:  # no payload
diff --git a/src/huggingface_hub/utils/__init__.py b/src/huggingface_hub/utils/__init__.py
index 06fca95c..a0417089 100644
--- a/src/huggingface_hub/utils/__init__.py
+++ b/src/huggingface_hub/utils/__init__.py
@@ -49,6 +49,7 @@ from ._paths import filter_repo_objects, IGNORE_GIT_FOLDER_PATTERNS
 from ._experimental import experimental
 from ._runtime import (
     dump_environment_info,
+    get_aiohttp_version,
     get_fastai_version,
     get_fastcore_version,
     get_gradio_version,
@@ -64,6 +65,7 @@ from ._runtime import (
     get_tensorboard_version,
     get_tf_version,
     get_torch_version,
+    is_aiohttp_available,
     is_fastai_available,
     is_fastcore_available,
     is_numpy_available,
diff --git a/src/huggingface_hub/utils/_runtime.py b/src/huggingface_hub/utils/_runtime.py
index 6bd25012..f5f95cb5 100644
--- a/src/huggingface_hub/utils/_runtime.py
+++ b/src/huggingface_hub/utils/_runtime.py
@@ -33,6 +33,7 @@ else:
 _package_versions = {}
 
 _CANDIDATES = {
+    "aiohttp": {"aiohttp"},
     "fastai": {"fastai"},
     "fastcore": {"fastcore"},
     "gradio": {"gradio"},
@@ -88,6 +89,15 @@ def get_hf_hub_version() -> str:
     return __version__
 
 
+# aiohttp
+def is_aiohttp_available() -> bool:
+    return _is_available("aiohttp")
+
+
+def get_aiohttp_version() -> str:
+    return _get_version("aiohttp")
+
+
 # FastAI
 def is_fastai_available() -> bool:
     return _is_available("fastai")
@@ -298,6 +308,7 @@ def dump_environment_info() -> Dict[str, Any]:
     info["tensorboard"] = get_tensorboard_version()
     info["numpy"] = get_numpy_version()
     info["pydantic"] = get_pydantic_version()
+    info["aiohttp"] = get_aiohttp_version()
 
     # Environment variables
     info["ENDPOINT"] = constants.ENDPOINT
diff --git a/utils/generate_async_inference_client.py b/utils/generate_async_inference_client.py
new file mode 100644
index 00000000..e2b17851
--- /dev/null
+++ b/utils/generate_async_inference_client.py
@@ -0,0 +1,351 @@
+# coding=utf-8
+# Copyright 2023-present, the HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Contains a tool to generate `src/huggingface_hub/inference/_generated/_async_client.py`."""
+import argparse
+import os
+import re
+import tempfile
+from pathlib import Path
+from typing import NoReturn
+
+from ruff.__main__ import find_ruff_bin
+
+
+ASYNC_CLIENT_FILE_PATH = (
+    Path(__file__).parents[1] / "src" / "huggingface_hub" / "inference" / "_generated" / "_async_client.py"
+)
+SYNC_CLIENT_FILE_PATH = Path(__file__).parents[1] / "src" / "huggingface_hub" / "inference" / "_client.py"
+
+
+def generate_async_client_code(code: str) -> str:
+    """Generate AsyncInferenceClient source code."""
+    # Warning message "this is an automatically generated file"
+    code = _add_warning_to_file_header(code)
+
+    # Imports specific to asyncio
+    code = _add_imports(code)
+
+    # Define `AsyncInferenceClient`
+    code = _rename_to_AsyncInferenceClient(code)
+
+    # Refactor `.post` method to be async + adapt calls
+    code = _make_post_async(code)
+    code = _await_post_method_call(code)
+    code = _use_async_streaming_util(code)
+
+    # Make all tasks-method async
+    code = _make_tasks_methods_async(code)
+
+    # Adapt text_generation error catching
+    code = _adapt_text_generation_to_async(code)
+
+    # Update some docstrings
+    code = _rename_HTTPError_to_ClientResponseError_in_docstring(code)
+    code = _update_examples_in_public_methods(code)
+    return code
+
+
+def format_source_code(code: str) -> str:
+    """Apply formatter on a generated source code."""
+    with tempfile.TemporaryDirectory() as tmpdir:
+        filepath = Path(tmpdir) / "async_client.py"
+        filepath.write_text(code)
+        ruff_bin = find_ruff_bin()
+        os.spawnv(os.P_WAIT, ruff_bin, ["ruff", str(filepath), "--fix", "--quiet"])
+        return filepath.read_text()
+
+
+def check_async_client(update: bool) -> NoReturn:
+    """Check AsyncInferenceClient is correctly defined and consistent with InferenceClient.
+
+    This script is used in the `make style` and `make quality` checks.
+    """
+    sync_client_code = SYNC_CLIENT_FILE_PATH.read_text()
+    current_async_client_code = ASYNC_CLIENT_FILE_PATH.read_text()
+
+    raw_async_client_code = generate_async_client_code(sync_client_code)
+
+    formatted_async_client_code = format_source_code(raw_async_client_code)
+
+    # If expected `__init__.py` content is different, test fails. If '--update-init-file'
+    # is used, `__init__.py` file is updated before the test fails.
+    if current_async_client_code != formatted_async_client_code:
+        if update:
+            ASYNC_CLIENT_FILE_PATH.write_text(formatted_async_client_code)
+
+            print(
+                "✅ AsyncInferenceClient source code has been updated in"
+                " `./src/huggingface_hub/inference/_generated/_async_client.py`.\n   Please make sure the changes are"
+                " accurate and commit them."
+            )
+            exit(0)
+        else:
+            print(
+                "❌ Expected content mismatch in `./src/huggingface_hub/inference/_generated/_async_client.py`.\n   It"
+                " is most likely that you modified some InferenceClient code and did not update the the"
+                " AsyncInferenceClient one.\n   Please run `make style` or `python"
+                " utils/generate_async_inference_client.py --update`."
+            )
+            exit(1)
+
+    print("✅ All good! (AsyncInferenceClient)")
+    exit(0)
+
+
+def _add_warning_to_file_header(code: str) -> str:
+    warning_message = (
+        "#\n# WARNING\n# This entire file has been adapted from the sync-client code in"
+        " `src/huggingface_hub/inference/_client.py`.\n# Any change in InferenceClient will be automatically reflected"
+        " in AsyncInferenceClient.\n# To re-generate the code, run `make style` or `python"
+        " ./utils/generate_async_inference_client.py --update`.\n# WARNING"
+    )
+    return re.sub(
+        r"""
+        ( # Group1: license (end)
+            \n
+            \#\ limitations\ under\ the\ License.
+            \n
+        )
+        (.*?) # Group2 : all notes and comments (to be replaced)
+        (\nimport[ ]) # Group3: import section (start)
+        """,
+        repl=rf"\1{warning_message}\3",
+        string=code,
+        count=1,
+        flags=re.DOTALL | re.VERBOSE,
+    )
+
+
+def _add_imports(code: str) -> str:
+    # global imports
+    code = re.sub(
+        r"(\nimport .*?\n)",
+        repl=(
+            r"\1" + "from .._common import _async_yield_from, _import_aiohttp\n" + "from typing import AsyncIterable\n"
+        ),
+        string=code,
+        count=1,
+        flags=re.DOTALL,
+    )
+
+    # type-checking imports
+    code = re.sub(
+        r"(\nif TYPE_CHECKING:\n)",
+        repl=r"\1    from aiohttp import ClientResponse, ClientSession\n",
+        string=code,
+        count=1,
+        flags=re.DOTALL,
+    )
+
+    return code
+
+
+def _rename_to_AsyncInferenceClient(code: str) -> str:
+    return code.replace("class InferenceClient:", "class AsyncInferenceClient:", 1)
+
+
+ASYNC_POST_CODE = """
+        aiohttp = _import_aiohttp()
+
+        url = self._resolve_url(model, task)
+
+        if data is not None and json is not None:
+            warnings.warn("Ignoring `json` as `data` is passed as binary.")
+
+        t0 = time.time()
+        timeout = self.timeout
+        while True:
+            with _open_as_binary(data) as data_as_binary:
+                # Do not use context manager as we don't want to close the connection immediately when returning
+                # a stream
+                client = aiohttp.ClientSession(
+                    headers=self.headers, cookies=self.cookies, timeout=aiohttp.ClientTimeout(self.timeout)
+                )
+
+                try:
+                    response = await client.post(url, headers=build_hf_headers(), json=json, data=data_as_binary)
+                    response_error_payload = None
+                    if response.status != 200:
+                        try:
+                            response_error_payload = await response.json()  # get payload before connection closed
+                        except Exception:
+                            pass
+                    response.raise_for_status()
+                    if stream:
+                        return _async_yield_from(client, response)
+                    else:
+                        content = await response.read()
+                        await client.close()
+                        return content
+                except TimeoutError as error:
+                    await client.close()
+                    # Convert any `TimeoutError` to a `InferenceTimeoutError`
+                    raise InferenceTimeoutError(f"Inference call timed out: {url}") from error
+                except aiohttp.ClientResponseError as error:
+                    error.response_error_payload = response_error_payload
+                    await client.close()
+                    if response.status == 503:
+                        # If Model is unavailable, either raise a TimeoutError...
+                        if timeout is not None and time.time() - t0 > timeout:
+                            raise InferenceTimeoutError(
+                                f"Model not loaded on the server: {url}. Please retry with a higher timeout"
+                                f" (current: {self.timeout})."
+                            ) from error
+                        # ...or wait 1s and retry
+                        logger.info(f"Waiting for model to be loaded on the server: {error}")
+                        time.sleep(1)
+                        if timeout is not None:
+                            timeout = max(self.timeout - (time.time() - t0), 1)  # type: ignore
+                        continue
+                    raise error"""
+
+
+def _make_post_async(code: str) -> str:
+    # Update AsyncInferenceClient.post() implementation (use aiohttp instead of requests)
+    code = re.sub(
+        r"""
+        def[ ]post\( # definition
+            (\n.*?\"\"\".*?\"\"\"\n) # Group1: docstring
+            .*? # implementation (to be overwritten)
+        (\n\W*def ) # Group2: next method
+        """,
+        repl=rf"async def post(\1{ASYNC_POST_CODE}\2",
+        string=code,
+        count=1,
+        flags=re.DOTALL | re.VERBOSE,
+    )
+    # Update `post`'s type annotations
+    return code.replace("Iterable[bytes]", "AsyncIterable[bytes]", 2)
+
+
+def _rename_HTTPError_to_ClientResponseError_in_docstring(code: str) -> str:
+    # Update `raises`-part in docstrings
+    return code.replace("`HTTPError`:", "`aiohttp.ClientResponseError`:")
+
+
+def _make_tasks_methods_async(code: str) -> str:
+    # Add `async` keyword in front of public methods (of AsyncClientInference)
+    return re.sub(
+        r"""
+        # Group 1: newline  + 4-spaces indent
+        (\n\ {4})
+        # Group 2: def + method name + parenthesis + optionally type: ignore + self
+        (
+            def[ ] # def
+            [a-z]\w*? # method name (not starting by _)
+            \( # parenthesis
+            (\s*\#[ ]type:[ ]ignore)? # optionally type: ignore
+            \s*self, # expect self, as first arg
+        )""",
+        repl=r"\1async \2",  # insert "async" keyword
+        string=code,
+        flags=re.DOTALL | re.VERBOSE,
+    )
+
+
+def _adapt_text_generation_to_async(code: str) -> str:
+    # Text-generation task has to be handled specifically since it has a recursive call mechanism (to retry on non-tgi
+    # servers)
+
+    # Catch `aiohttp` error instead of `requests` error
+    code = code.replace(
+        """
+        except HTTPError as e:
+            if isinstance(e, BadRequestError) and "The following `model_kwargs` are not used by the model" in str(e):
+    """,
+        """
+        except _import_aiohttp().ClientResponseError as e:
+            error_message = getattr(e, "response_error_payload", {}).get("error", "")
+            if e.code == 400 and "The following `model_kwargs` are not used by the model" in error_message:
+    """,
+    )
+
+    # Await recursive call
+    code = code.replace("return self.text_generation", "return await self.text_generation")
+
+    # Update return types: Iterable -> AsyncIterable
+    code = code.replace(") -> Iterable[str]:", ") -> AsyncIterable[str]:")
+    code = code.replace(
+        ") -> Iterable[TextGenerationStreamResponse]:", ") -> AsyncIterable[TextGenerationStreamResponse]:"
+    )
+    code = code.replace(
+        ") -> Union[str, TextGenerationResponse, Iterable[str], Iterable[TextGenerationStreamResponse]]:",
+        ") -> Union[str, TextGenerationResponse, AsyncIterable[str], AsyncIterable[TextGenerationStreamResponse]]:",
+    )
+
+    return code
+
+
+def _await_post_method_call(code: str) -> str:
+    return code.replace("self.post(", "await self.post(")
+
+
+def _update_example_code_block(code_block: str) -> str:
+    """Update an atomic code block example from a docstring."""
+    code_block = "\n        # Must be run in an async context" + code_block
+    code_block = code_block.replace("InferenceClient", "AsyncInferenceClient")
+    code_block = code_block.replace("client.", "await client.")
+    code_block = code_block.replace(" for ", " async for ")
+    return code_block
+
+
+def _update_examples_in_public_methods(code: str) -> str:
+    for match in re.finditer(
+        r"""
+        \n\s*
+        Example:\n\s* # example section
+        ```py # start
+        (.*?) # code block
+        ``` # end
+        \n
+        """,
+        string=code,
+        flags=re.DOTALL | re.VERBOSE,
+    ):
+        # Example, including code block
+        full_match = match.group()
+
+        # Code block alone
+        code_block = match.group(1)
+
+        # Update code block in example
+        updated_match = full_match.replace(code_block, _update_example_code_block(code_block))
+
+        # Update example in full script
+        code = code.replace(full_match, updated_match)
+
+    return code
+
+
+def _use_async_streaming_util(code: str) -> str:
+    return code.replace(
+        "_stream_text_generation_response",
+        "_async_stream_text_generation_response",
+    )
+
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        "--update",
+        action="store_true",
+        help=(
+            "Whether to re-generate `./src/huggingface_hub/inference/_generated/_async_client.py` if a change is"
+            " detected."
+        ),
+    )
+    args = parser.parse_args()
+
+    check_async_client(update=args.update)


Test patch:
diff --git a/tests/cassettes/test_async_generate_best_of.yaml b/tests/cassettes/test_async_generate_best_of.yaml
new file mode 100644
index 00000000..5457f041
--- /dev/null
+++ b/tests/cassettes/test_async_generate_best_of.yaml
@@ -0,0 +1,40 @@
+interactions:
+- request:
+    body: null
+    headers:
+      user-agent:
+      - unknown/None; hf_hub/0.16.0.dev0; python/3.10.6; torch/1.12.1; tensorflow/2.11.0;
+        fastcore/1.5.23
+    method: POST
+    uri: https://api-inference.huggingface.co/models/google/flan-t5-xxl
+  response:
+    body:
+      string: '[{"generated_text":"","details":{"finish_reason":"length","generated_tokens":1,"seed":6270333398812658761,"prefill":[{"id":0,"text":"<pad>","logprob":null}],"tokens":[{"id":3,"text":"
+        ","logprob":-2.0058594,"special":false}],"best_of_sequences":[{"generated_text":"super","finish_reason":"length","generated_tokens":1,"seed":1747517474421013729,"prefill":[{"id":0,"text":"<pad>","logprob":null}],"tokens":[{"id":1355,"text":"
+        super","logprob":-9.484375,"special":false}]}]}}]'
+    headers:
+      Access-Control-Allow-Credentials:
+      - 'true'
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '475'
+      Content-Type:
+      - application/json
+      Date:
+      - Wed, 28 Jun 2023 11:10:48 GMT
+      Vary:
+      - Origin, Access-Control-Request-Method, Access-Control-Request-Headers
+      x-compute-time:
+      - '59'
+      x-compute-type:
+      - cache
+      x-request-id:
+      - UZNvXhDkv_krGEbjvx8Pi
+      x-sha:
+      - ad196ce8c46191d6a52592960835ff96d30152b5
+    status:
+      code: 200
+      message: OK
+    url: https://api-inference.huggingface.co/models/google/flan-t5-xxl
+version: 1
diff --git a/tests/cassettes/test_async_generate_no_details.yaml b/tests/cassettes/test_async_generate_no_details.yaml
new file mode 100644
index 00000000..b8bcef1e
--- /dev/null
+++ b/tests/cassettes/test_async_generate_no_details.yaml
@@ -0,0 +1,38 @@
+interactions:
+- request:
+    body: null
+    headers:
+      user-agent:
+      - unknown/None; hf_hub/0.16.0.dev0; python/3.10.6; torch/1.12.1; tensorflow/2.11.0;
+        fastcore/1.5.23
+    method: POST
+    uri: https://api-inference.huggingface.co/models/google/flan-t5-xxl
+  response:
+    body:
+      string: '[{"generated_text":""}]'
+    headers:
+      Access-Control-Allow-Credentials:
+      - 'true'
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '23'
+      Content-Type:
+      - application/json
+      Date:
+      - Wed, 28 Jun 2023 11:10:46 GMT
+      Vary:
+      - Origin, Access-Control-Request-Method, Access-Control-Request-Headers
+      x-compute-time:
+      - '56'
+      x-compute-type:
+      - cache
+      x-request-id:
+      - c6fstBTez3qjN6_hIBIsR
+      x-sha:
+      - ad196ce8c46191d6a52592960835ff96d30152b5
+    status:
+      code: 200
+      message: OK
+    url: https://api-inference.huggingface.co/models/google/flan-t5-xxl
+version: 1
diff --git a/tests/cassettes/test_async_generate_non_tgi_endpoint.yaml b/tests/cassettes/test_async_generate_non_tgi_endpoint.yaml
new file mode 100644
index 00000000..784746db
--- /dev/null
+++ b/tests/cassettes/test_async_generate_non_tgi_endpoint.yaml
@@ -0,0 +1,155 @@
+interactions:
+- request:
+    body: null
+    headers:
+      user-agent:
+      - unknown/None; hf_hub/0.16.0.dev0; python/3.10.6; torch/1.12.1; tensorflow/2.11.0;
+        fastcore/1.5.23
+    method: POST
+    uri: https://api-inference.huggingface.co/models/gpt2
+  response:
+    body:
+      string: '{"error":"The following `model_kwargs` are not used by the model: [''details'',
+        ''stop'', ''watermark'', ''decoder_input_details''] (note: typos in the generate
+        arguments will also show up in this list)","warnings":["There was an inference
+        error: The following `model_kwargs` are not used by the model: [''details'',
+        ''stop'', ''watermark'', ''decoder_input_details''] (note: typos in the generate
+        arguments will also show up in this list)"]}'
+    headers:
+      Access-Control-Allow-Credentials:
+      - 'true'
+      Access-Control-Expose-Headers:
+      - x-compute-type, x-compute-time
+      Connection:
+      - keep-alive
+      Content-Type:
+      - application/json
+      Date:
+      - Wed, 28 Jun 2023 11:10:50 GMT
+      Server:
+      - uvicorn
+      Transfer-Encoding:
+      - chunked
+      Vary:
+      - Origin, Access-Control-Request-Method, Access-Control-Request-Headers
+      x-compute-time:
+      - '0.003'
+      x-compute-type:
+      - cpu
+      x-request-id:
+      - ni9shQjphqlYUUZnucA0o
+      x-sha:
+      - e7da7f221d5bf496a48136c0cd264e630fe9fcc8
+    status:
+      code: 400
+      message: Bad Request
+    url: https://api-inference.huggingface.co/models/gpt2
+- request:
+    body: null
+    headers:
+      user-agent:
+      - unknown/None; hf_hub/0.16.0.dev0; python/3.10.6; torch/1.12.1; tensorflow/2.11.0;
+        fastcore/1.5.23
+    method: POST
+    uri: https://api-inference.huggingface.co/models/gpt2
+  response:
+    body:
+      string: '[{"generated_text":" 3 4 5 6 7 8 9 10 11 12"}]'
+    headers:
+      Access-Control-Allow-Credentials:
+      - 'true'
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '46'
+      Content-Type:
+      - application/json
+      Date:
+      - Wed, 28 Jun 2023 11:10:51 GMT
+      Vary:
+      - Origin, Access-Control-Request-Method, Access-Control-Request-Headers
+      x-compute-time:
+      - '0.243'
+      x-compute-type:
+      - cache
+      x-request-id:
+      - XQJuWC-iyJJs02qtiEuCt
+      x-sha:
+      - e7da7f221d5bf496a48136c0cd264e630fe9fcc8
+    status:
+      code: 200
+      message: OK
+    url: https://api-inference.huggingface.co/models/gpt2
+- request:
+    body: null
+    headers:
+      user-agent:
+      - unknown/None; hf_hub/0.16.0.dev0; python/3.10.6; torch/1.12.1; tensorflow/2.11.0;
+        fastcore/1.5.23
+    method: POST
+    uri: https://api-inference.huggingface.co/models/gpt2
+  response:
+    body:
+      string: '[{"generated_text":" 7 8 9 10 11 12 13 14 15 16"}]'
+    headers:
+      Access-Control-Allow-Credentials:
+      - 'true'
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '50'
+      Content-Type:
+      - application/json
+      Date:
+      - Wed, 28 Jun 2023 11:10:52 GMT
+      Vary:
+      - Origin, Access-Control-Request-Method, Access-Control-Request-Headers
+      x-compute-time:
+      - '0.205'
+      x-compute-type:
+      - cache
+      x-request-id:
+      - crNm2PdZ8Ni0UKV00cDnF
+      x-sha:
+      - e7da7f221d5bf496a48136c0cd264e630fe9fcc8
+    status:
+      code: 200
+      message: OK
+    url: https://api-inference.huggingface.co/models/gpt2
+- request:
+    body: null
+    headers:
+      user-agent:
+      - unknown/None; hf_hub/0.16.0.dev0; python/3.10.6; torch/1.12.1; tensorflow/2.11.0;
+        fastcore/1.5.23
+    method: POST
+    uri: https://api-inference.huggingface.co/models/gpt2
+  response:
+    body:
+      string: '[{"generated_text":" 3 4 5 6 7 8 9 10 11 12"}]'
+    headers:
+      Access-Control-Allow-Credentials:
+      - 'true'
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '46'
+      Content-Type:
+      - application/json
+      Date:
+      - Wed, 28 Jun 2023 11:10:53 GMT
+      Vary:
+      - Origin, Access-Control-Request-Method, Access-Control-Request-Headers
+      x-compute-time:
+      - '0.243'
+      x-compute-type:
+      - cache
+      x-request-id:
+      - 9mmxYlk7FpogZEK3X9Y2k
+      x-sha:
+      - e7da7f221d5bf496a48136c0cd264e630fe9fcc8
+    status:
+      code: 200
+      message: OK
+    url: https://api-inference.huggingface.co/models/gpt2
+version: 1
diff --git a/tests/cassettes/test_async_generate_stream_no_details.yaml b/tests/cassettes/test_async_generate_stream_no_details.yaml
new file mode 100644
index 00000000..cfaafdca
--- /dev/null
+++ b/tests/cassettes/test_async_generate_stream_no_details.yaml
@@ -0,0 +1,45 @@
+interactions:
+- request:
+    body: null
+    headers:
+      user-agent:
+      - unknown/None; hf_hub/0.16.0.dev0; python/3.10.6; torch/1.12.1; tensorflow/2.11.0;
+        fastcore/1.5.23
+    method: POST
+    uri: https://api-inference.huggingface.co/models/google/flan-t5-xxl
+  response:
+    body:
+      string: 'data:{"token":{"id":3,"text":" ","logprob":-2.0078125,"special":false},"generated_text":"","details":null}
+
+
+        '
+    headers:
+      Access-Control-Allow-Credentials:
+      - 'true'
+      Access-Control-Allow-Origin:
+      - '*'
+      Cache-Control:
+      - no-cache
+      Connection:
+      - keep-alive
+      Content-Type:
+      - text/event-stream
+      Date:
+      - Thu, 29 Jun 2023 09:54:38 GMT
+      Transfer-Encoding:
+      - chunked
+      Vary:
+      - origin, Origin, Access-Control-Request-Method, Access-Control-Request-Headers
+      x-compute-characters:
+      - '4'
+      x-compute-type:
+      - gpu+optimized
+      x-request-id:
+      - ns79PRTJZScULySDDEv_d
+      x-sha:
+      - ad196ce8c46191d6a52592960835ff96d30152b5
+    status:
+      code: 200
+      message: OK
+    url: https://api-inference.huggingface.co/models/google/flan-t5-xxl
+version: 1
diff --git a/tests/cassettes/test_async_generate_stream_with_details.yaml b/tests/cassettes/test_async_generate_stream_with_details.yaml
new file mode 100644
index 00000000..4833b82d
--- /dev/null
+++ b/tests/cassettes/test_async_generate_stream_with_details.yaml
@@ -0,0 +1,39 @@
+interactions:
+- request:
+    body: null
+    headers:
+      user-agent:
+      - unknown/None; hf_hub/0.16.0.dev0; python/3.10.6; torch/1.12.1; tensorflow/2.11.0;
+        fastcore/1.5.23
+    method: POST
+    uri: https://api-inference.huggingface.co/models/google/flan-t5-xxl
+  response:
+    body:
+      string: 'data:{"token":{"id":3,"text":" ","logprob":-2.0078125,"special":false},"generated_text":"","details":{"finish_reason":"length","generated_tokens":1,"seed":null}}
+
+
+        '
+    headers:
+      Access-Control-Allow-Credentials:
+      - 'true'
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '163'
+      Content-Type:
+      - text/event-stream
+      Date:
+      - Thu, 29 Jun 2023 09:53:59 GMT
+      Vary:
+      - Origin, Access-Control-Request-Method, Access-Control-Request-Headers
+      x-compute-type:
+      - cache
+      x-request-id:
+      - HjvS2lx32U4PwG-D3Gm9J
+      x-sha:
+      - ad196ce8c46191d6a52592960835ff96d30152b5
+    status:
+      code: 200
+      message: OK
+    url: https://api-inference.huggingface.co/models/google/flan-t5-xxl
+version: 1
diff --git a/tests/cassettes/test_async_generate_validation_error.yaml b/tests/cassettes/test_async_generate_validation_error.yaml
new file mode 100644
index 00000000..5095044d
--- /dev/null
+++ b/tests/cassettes/test_async_generate_validation_error.yaml
@@ -0,0 +1,37 @@
+interactions:
+- request:
+    body: null
+    headers:
+      user-agent:
+      - unknown/None; hf_hub/0.16.0.dev0; python/3.10.6; torch/1.12.1; tensorflow/2.11.0;
+        fastcore/1.5.23
+    method: POST
+    uri: https://api-inference.huggingface.co/models/google/flan-t5-xxl
+  response:
+    body:
+      string: '{"error":"Input validation error: `inputs` tokens + `max_new_tokens`
+        must be <= 1512. Given: 2 `inputs` tokens and 10000 `max_new_tokens`","error_type":"validation"}'
+    headers:
+      Access-Control-Allow-Credentials:
+      - 'true'
+      Access-Control-Allow-Origin:
+      - '*'
+      Connection:
+      - keep-alive
+      Content-Type:
+      - application/json
+      Date:
+      - Wed, 28 Jun 2023 11:10:49 GMT
+      Transfer-Encoding:
+      - chunked
+      Vary:
+      - origin, Origin, Access-Control-Request-Method, Access-Control-Request-Headers
+      x-request-id:
+      - 150LdPvga5vff_3f0MeMv
+      x-sha:
+      - ad196ce8c46191d6a52592960835ff96d30152b5
+    status:
+      code: 422
+      message: Unprocessable Entity
+    url: https://api-inference.huggingface.co/models/google/flan-t5-xxl
+version: 1
diff --git a/tests/cassettes/test_async_generate_with_details.yaml b/tests/cassettes/test_async_generate_with_details.yaml
new file mode 100644
index 00000000..9631211e
--- /dev/null
+++ b/tests/cassettes/test_async_generate_with_details.yaml
@@ -0,0 +1,39 @@
+interactions:
+- request:
+    body: null
+    headers:
+      user-agent:
+      - unknown/None; hf_hub/0.16.0.dev0; python/3.10.6; torch/1.12.1; tensorflow/2.11.0;
+        fastcore/1.5.23
+    method: POST
+    uri: https://api-inference.huggingface.co/models/google/flan-t5-xxl
+  response:
+    body:
+      string: '[{"generated_text":"","details":{"finish_reason":"length","generated_tokens":1,"seed":null,"prefill":[{"id":0,"text":"<pad>","logprob":null}],"tokens":[{"id":3,"text":"
+        ","logprob":-2.0078125,"special":false}]}}]'
+    headers:
+      Access-Control-Allow-Credentials:
+      - 'true'
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '212'
+      Content-Type:
+      - application/json
+      Date:
+      - Wed, 28 Jun 2023 11:10:47 GMT
+      Vary:
+      - Origin, Access-Control-Request-Method, Access-Control-Request-Headers
+      x-compute-time:
+      - '60'
+      x-compute-type:
+      - cache
+      x-request-id:
+      - xRLXLRG8hYGeww6snaG8C
+      x-sha:
+      - ad196ce8c46191d6a52592960835ff96d30152b5
+    status:
+      code: 200
+      message: OK
+    url: https://api-inference.huggingface.co/models/google/flan-t5-xxl
+version: 1
diff --git a/tests/cassettes/test_async_sentence_similarity.yaml b/tests/cassettes/test_async_sentence_similarity.yaml
new file mode 100644
index 00000000..60690016
--- /dev/null
+++ b/tests/cassettes/test_async_sentence_similarity.yaml
@@ -0,0 +1,604 @@
+interactions:
+- request:
+    body: null
+    headers:
+      Accept:
+      - '*/*'
+      Accept-Encoding:
+      - gzip, deflate
+      Connection:
+      - keep-alive
+      user-agent:
+      - unknown/None; hf_hub/0.16.0.dev0; python/3.10.6; torch/1.12.1; tensorflow/2.11.0;
+        fastcore/1.5.23
+    method: GET
+    uri: https://hub-ci.huggingface.co/api/tasks
+  response:
+    body:
+      string: "{\"audio-classification\":{\"datasets\":[{\"description\":\"A benchmark
+        of 10 different audio tasks.\",\"id\":\"superb\"}],\"demo\":{\"inputs\":[{\"filename\":\"audio.wav\",\"type\":\"audio\"}],\"outputs\":[{\"data\":[{\"label\":\"Up\",\"score\":0.2},{\"label\":\"Down\",\"score\":0.8}],\"type\":\"chart\"}]},\"metrics\":[{\"description\":\"\",\"id\":\"accuracy\"},{\"description\":\"\",\"id\":\"recall\"},{\"description\":\"\",\"id\":\"precision\"},{\"description\":\"\",\"id\":\"f1\"}],\"models\":[{\"description\":\"An
+        easy-to-use model for Command Recognition.\",\"id\":\"speechbrain/google_speech_command_xvector\"},{\"description\":\"An
+        Emotion Recognition model.\",\"id\":\"ehcalabres/wav2vec2-lg-xlsr-en-speech-emotion-recognition\"},{\"description\":\"A
+        language identification model.\",\"id\":\"facebook/mms-lid-126\"}],\"spaces\":[{\"description\":\"An
+        application that can predict the language spoken in a given audio.\",\"id\":\"akhaliq/Speechbrain-audio-classification\"}],\"summary\":\"Audio
+        classification is the task of assigning a label or class to a given audio.
+        It can be used for recognizing which command a user is giving or the emotion
+        of a statement, as well as identifying a speaker.\",\"widgetModels\":[\"facebook/mms-lid-126\"],\"youtubeId\":\"KWwzcmG98Ds\",\"id\":\"audio-classification\",\"label\":\"Audio
+        Classification\",\"libraries\":[\"speechbrain\",\"transformers\"]},\"audio-to-audio\":{\"datasets\":[{\"description\":\"512-element
+        X-vector embeddings of speakers from CMU ARCTIC dataset.\",\"id\":\"Matthijs/cmu-arctic-xvectors\"}],\"demo\":{\"inputs\":[{\"filename\":\"input.wav\",\"type\":\"audio\"}],\"outputs\":[{\"filename\":\"label-0.wav\",\"type\":\"audio\"},{\"filename\":\"label-1.wav\",\"type\":\"audio\"}]},\"metrics\":[{\"description\":\"The
+        Signal-to-Noise ratio is the relationship between the target signal level
+        and the background noise level. It is calculated as the logarithm of the target
+        signal divided by the background noise, in decibels.\",\"id\":\"snri\"},{\"description\":\"The
+        Signal-to-Distortion ratio is the relationship between the target signal and
+        the sum of noise, interference, and artifact errors\",\"id\":\"sdri\"}],\"models\":[{\"description\":\"A
+        solid model of audio source separation.\",\"id\":\"speechbrain/sepformer-wham\"},{\"description\":\"A
+        speech enhancement model.\",\"id\":\"speechbrain/metricgan-plus-voicebank\"}],\"spaces\":[{\"description\":\"An
+        application for speech separation.\",\"id\":\"younver/speechbrain-speech-separation\"},{\"description\":\"An
+        application for audio style transfer.\",\"id\":\"nakas/audio-diffusion_style_transfer\"}],\"summary\":\"Audio-to-Audio
+        is a family of tasks in which the input is an audio and the output is one
+        or multiple generated audios. Some example tasks are speech enhancement and
+        source separation.\",\"widgetModels\":[\"speechbrain/sepformer-wham\"],\"youtubeId\":\"iohj7nCCYoM\",\"id\":\"audio-to-audio\",\"label\":\"Audio-to-Audio\",\"libraries\":[\"asteroid\",\"speechbrain\"]},\"automatic-speech-recognition\":{\"datasets\":[{\"description\":\"An
+        English dataset with 1,000 hours of data.\",\"id\":\"librispeech_asr\"},{\"description\":\"Dataset
+        in 60 languages including demographic information.\",\"id\":\"common_voice\"},{\"description\":\"High
+        quality, multi-speaker audio data and their transcriptions  in various languages.\",\"id\":\"openslr\"}],\"demo\":{\"inputs\":[{\"filename\":\"input.flac\",\"type\":\"audio\"}],\"outputs\":[{\"label\":\"Transcript\",\"content\":\"Going
+        along slushy country roads and speaking to damp audiences in...\",\"type\":\"text\"}]},\"metrics\":[{\"description\":\"\",\"id\":\"wer\"},{\"description\":\"\",\"id\":\"cer\"}],\"models\":[{\"description\":\"A
+        good generic ASR model.\",\"id\":\"facebook/wav2vec2-base-960h\"},{\"description\":\"A
+        powerful ASR model.\",\"id\":\"openai/whisper-large-v2\"},{\"description\":\"An
+        end-to-end model that performs Automatic Speech Recognition and Speech Translation.\",\"id\":\"facebook/s2t-small-mustc-en-fr-st\"}],\"spaces\":[{\"description\":\"A
+        powerful general-purpose speech recognition application.\",\"id\":\"openai/whisper\"},{\"description\":\"Fastest
+        speech recognition application.\",\"id\":\"sanchit-gandhi/whisper-jax\"},{\"description\":\"An
+        application that transcribes speeches in YouTube videos.\",\"id\":\"jeffistyping/Youtube-Whisperer\"}],\"summary\":\"Automatic
+        Speech Recognition (ASR), also known as Speech to Text (STT), is the task
+        of transcribing a given audio to text. It has many applications, such as voice
+        user interfaces.\",\"widgetModels\":[\"openai/whisper-large-v2\"],\"youtubeId\":\"TksaY_FDgnk\",\"id\":\"automatic-speech-recognition\",\"label\":\"Automatic
+        Speech Recognition\",\"libraries\":[\"espnet\",\"nemo\",\"speechbrain\",\"transformers\",\"transformers.js\"]},\"conversational\":{\"datasets\":[{\"description\":\"A
+        dataset of 7k conversations explicitly designed to exhibit multiple conversation
+        modes: displaying personality, having empathy, and demonstrating knowledge.\",\"id\":\"blended_skill_talk\"},{\"description\":\"ConvAI
+        is a dataset of human-to-bot conversations labeled for quality. This data
+        can be used to train a metric for evaluating dialogue systems\",\"id\":\"conv_ai_2\"},{\"description\":\"EmpatheticDialogues,
+        is a dataset of 25k conversations grounded in emotional situations\",\"id\":\"empathetic_dialogues\"}],\"demo\":{\"inputs\":[{\"label\":\"Input\",\"content\":\"Hey
+        my name is Julien! How are you?\",\"type\":\"text\"}],\"outputs\":[{\"label\":\"Answer\",\"content\":\"Hi
+        Julien! My name is Julia! I am well.\",\"type\":\"text\"}]},\"metrics\":[{\"description\":\"BLEU
+        score is calculated by counting the number of shared single or subsequent
+        tokens between the generated sequence and the reference. Subsequent n tokens
+        are called \u201Cn-grams\u201D. Unigram refers to a single token while bi-gram
+        refers to token pairs and n-grams refer to n subsequent tokens. The score
+        ranges from 0 to 1, where 1 means the translation perfectly matched and 0
+        did not match at all\",\"id\":\"bleu\"}],\"models\":[{\"description\":\"A
+        faster and smaller model than the famous BERT model.\",\"id\":\"facebook/blenderbot-400M-distill\"},{\"description\":\"DialoGPT
+        is a large-scale pretrained dialogue response generation model for multiturn
+        conversations.\",\"id\":\"microsoft/DialoGPT-large\"}],\"spaces\":[{\"description\":\"A
+        chatbot based on Blender model.\",\"id\":\"EXFINITE/BlenderBot-UI\"}],\"summary\":\"Conversational
+        response modelling is the task of generating conversational text that is relevant,
+        coherent and knowledgable given a prompt. These models have applications in
+        chatbots, and as a part of voice assistants\",\"widgetModels\":[\"facebook/blenderbot-400M-distill\"],\"youtubeId\":\"\",\"id\":\"conversational\",\"label\":\"Conversational\",\"libraries\":[\"transformers\"]},\"depth-estimation\":{\"datasets\":[{\"description\":\"NYU
+        Depth V2 Dataset: Video dataset containing both RGB and depth sensor data\",\"id\":\"sayakpaul/nyu_depth_v2\"}],\"demo\":{\"inputs\":[{\"filename\":\"depth-estimation-input.jpg\",\"type\":\"img\"}],\"outputs\":[{\"filename\":\"depth-estimation-output.png\",\"type\":\"img\"}]},\"metrics\":[],\"models\":[{\"description\":\"Strong
+        Depth Estimation model trained on 1.4 million images.\",\"id\":\"Intel/dpt-large\"},{\"description\":\"Strong
+        Depth Estimation model trained on the KITTI dataset.\",\"id\":\"vinvino02/glpn-kitti\"}],\"spaces\":[{\"description\":\"An
+        application that predicts the depth of an image and then reconstruct the 3D
+        model as voxels.\",\"id\":\"radames/dpt-depth-estimation-3d-voxels\"},{\"description\":\"An
+        application that can estimate the depth in a given image.\",\"id\":\"keras-io/Monocular-Depth-Estimation\"}],\"summary\":\"Depth
+        estimation is the task of predicting depth of the objects present in an image.\",\"widgetModels\":[\"\"],\"youtubeId\":\"\",\"id\":\"depth-estimation\",\"label\":\"Depth
+        Estimation\",\"libraries\":[\"transformers\"]},\"document-question-answering\":{\"datasets\":[{\"description\":\"Dataset
+        from the 2020 DocVQA challenge. The documents are taken from the UCSF Industry
+        Documents Library.\",\"id\":\"eliolio/docvqa\"}],\"demo\":{\"inputs\":[{\"label\":\"Question\",\"content\":\"What
+        is the idea behind the consumer relations efficiency team?\",\"type\":\"text\"},{\"filename\":\"document-question-answering-input.png\",\"type\":\"img\"}],\"outputs\":[{\"label\":\"Answer\",\"content\":\"Balance
+        cost efficiency with quality customer service\",\"type\":\"text\"}]},\"metrics\":[{\"description\":\"The
+        evaluation metric for the DocVQA challenge is the Average Normalized Levenshtein
+        Similarity (ANLS). This metric is flexible to character regognition errors
+        and compares the predicted answer with the ground truth answer.\",\"id\":\"anls\"},{\"description\":\"Exact
+        Match is a metric based on the strict character match of the predicted answer
+        and the right answer. For answers predicted correctly, the Exact Match will
+        be 1. Even if only one character is different, Exact Match will be 0\",\"id\":\"exact-match\"}],\"models\":[{\"description\":\"A
+        LayoutLM model for the document QA task, fine-tuned on DocVQA and SQuAD2.0.\",\"id\":\"impira/layoutlm-document-qa\"},{\"description\":\"A
+        special model for OCR-free Document QA task. Donut model fine-tuned on DocVQA.\",\"id\":\"naver-clova-ix/donut-base-finetuned-docvqa\"}],\"spaces\":[{\"description\":\"A
+        robust document question answering application.\",\"id\":\"impira/docquery\"},{\"description\":\"An
+        application that can answer questions from invoices.\",\"id\":\"impira/invoices\"}],\"summary\":\"Document
+        Question Answering (also known as Document Visual Question Answering) is the
+        task of answering questions on document images. Document question answering
+        models take a (document, question) pair as input and return an answer in natural
+        language. Models usually rely on multi-modal features, combining text, position
+        of words (bounding-boxes) and image.\",\"widgetModels\":[\"impira/layoutlm-document-qa\"],\"youtubeId\":\"\",\"id\":\"document-question-answering\",\"label\":\"Document
+        Question Answering\",\"libraries\":[\"transformers\"]},\"feature-extraction\":{\"datasets\":[{\"description\":\"Wikipedia
+        dataset containing cleaned articles of all languages. Can be used to train
+        `feature-extraction` models.\",\"id\":\"wikipedia\"}],\"demo\":{\"inputs\":[{\"label\":\"Input\",\"content\":\"India,
+        officially the Republic of India, is a country in South Asia.\",\"type\":\"text\"}],\"outputs\":[{\"table\":[[\"Dimension
+        1\",\"Dimension 2\",\"Dimension 3\"],[\"2.583383083343506\",\"2.757075071334839\",\"0.9023529887199402\"],[\"8.29393482208252\",\"1.1071064472198486\",\"2.03399395942688\"],[\"-0.7754912972450256\",\"-1.647324562072754\",\"-0.6113331913948059\"],[\"0.07087723910808563\",\"1.5942802429199219\",\"1.4610432386398315\"]],\"type\":\"tabular\"}]},\"metrics\":[{\"description\":\"\",\"id\":\"\"}],\"models\":[{\"description\":\"A
+        powerful feature extraction model for natural language processing tasks.\",\"id\":\"facebook/bart-base\"},{\"description\":\"A
+        strong feature extraction model for coding tasks.\",\"id\":\"microsoft/codebert-base\"}],\"spaces\":[],\"summary\":\"Feature
+        extraction refers to the process of transforming raw data into numerical features
+        that can be processed while preserving the information in the original dataset.\",\"widgetModels\":[\"facebook/bart-base\"],\"id\":\"feature-extraction\",\"label\":\"Feature
+        Extraction\",\"libraries\":[\"sentence-transformers\",\"transformers\",\"transformers.js\"]},\"fill-mask\":{\"datasets\":[{\"description\":\"A
+        common dataset that is used to train models for many languages.\",\"id\":\"wikipedia\"},{\"description\":\"A
+        large English dataset with text crawled from the web.\",\"id\":\"c4\"}],\"demo\":{\"inputs\":[{\"label\":\"Input\",\"content\":\"The
+        <mask> barked at me\",\"type\":\"text\"}],\"outputs\":[{\"type\":\"chart\",\"data\":[{\"label\":\"wolf\",\"score\":0.487},{\"label\":\"dog\",\"score\":0.061},{\"label\":\"cat\",\"score\":0.058},{\"label\":\"fox\",\"score\":0.047},{\"label\":\"squirrel\",\"score\":0.025}]}]},\"metrics\":[{\"description\":\"Cross
+        Entropy is a metric that calculates the difference between two probability
+        distributions. Each probability distribution is the distribution of predicted
+        words\",\"id\":\"cross_entropy\"},{\"description\":\"Perplexity is the exponential
+        of the cross-entropy loss. It evaluates the probabilities assigned to the
+        next word by the model. Lower perplexity indicates better performance\",\"id\":\"perplexity\"}],\"models\":[{\"description\":\"A
+        faster and smaller model than the famous BERT model.\",\"id\":\"distilbert-base-uncased\"},{\"description\":\"A
+        multilingual model trained on 100 languages.\",\"id\":\"xlm-roberta-base\"}],\"spaces\":[],\"summary\":\"Masked
+        language modeling is the task of masking some of the words in a sentence and
+        predicting which words should replace those masks. These models are useful
+        when we want to get a statistical understanding of the language in which the
+        model is trained in.\",\"widgetModels\":[\"distilroberta-base\"],\"youtubeId\":\"mqElG5QJWUg\",\"id\":\"fill-mask\",\"label\":\"Fill-Mask\",\"libraries\":[\"transformers\",\"transformers.js\"]},\"image-classification\":{\"datasets\":[{\"description\":\"Benchmark
+        dataset used for image classification with images that belong to 100 classes.\",\"id\":\"cifar100\"},{\"description\":\"Dataset
+        consisting of images of garments.\",\"id\":\"fashion_mnist\"}],\"demo\":{\"inputs\":[{\"filename\":\"image-classification-input.jpeg\",\"type\":\"img\"}],\"outputs\":[{\"type\":\"chart\",\"data\":[{\"label\":\"Egyptian
+        cat\",\"score\":0.514},{\"label\":\"Tabby cat\",\"score\":0.193},{\"label\":\"Tiger
+        cat\",\"score\":0.068}]}]},\"metrics\":[{\"description\":\"\",\"id\":\"accuracy\"},{\"description\":\"\",\"id\":\"recall\"},{\"description\":\"\",\"id\":\"precision\"},{\"description\":\"\",\"id\":\"f1\"}],\"models\":[{\"description\":\"A
+        strong image classification model.\",\"id\":\"google/vit-base-patch16-224\"},{\"description\":\"A
+        robust image classification model.\",\"id\":\"facebook/deit-base-distilled-patch16-224\"},{\"description\":\"A
+        strong image classification model.\",\"id\":\"facebook/convnext-large-224\"}],\"spaces\":[{\"description\":\"An
+        application that classifies what a given image is about.\",\"id\":\"nielsr/perceiver-image-classification\"}],\"summary\":\"Image
+        classification is the task of assigning a label or class to an entire image.
+        Images are expected to have only one class for each image. Image classification
+        models take an image as input and return a prediction about which class the
+        image belongs to.\",\"widgetModels\":[\"google/vit-base-patch16-224\"],\"youtubeId\":\"tjAIM7BOYhw\",\"id\":\"image-classification\",\"label\":\"Image
+        Classification\",\"libraries\":[\"keras\",\"timm\",\"transformers\",\"transformers.js\"]},\"image-segmentation\":{\"datasets\":[{\"description\":\"Scene
+        segmentation dataset.\",\"id\":\"scene_parse_150\"}],\"demo\":{\"inputs\":[{\"filename\":\"image-segmentation-input.jpeg\",\"type\":\"img\"}],\"outputs\":[{\"filename\":\"image-segmentation-output.png\",\"type\":\"img\"}]},\"metrics\":[{\"description\":\"Average
+        Precision (AP) is the Area Under the PR Curve (AUC-PR). It is calculated for
+        each semantic class separately\",\"id\":\"Average Precision\"},{\"description\":\"Mean
+        Average Precision (mAP) is the overall average of the AP values\",\"id\":\"Mean
+        Average Precision\"},{\"description\":\"Intersection over Union (IoU) is the
+        overlap of segmentation masks. Mean IoU is the average of the IoU of all semantic
+        classes\",\"id\":\"Mean Intersection over Union\"},{\"description\":\"AP\u03B1
+        is the Average Precision at the IoU threshold of a \u03B1 value, for example,
+        AP50 and AP75\",\"id\":\"AP\u03B1\"}],\"models\":[{\"description\":\"Solid
+        panoptic segmentation model trained on the COCO 2017 benchmark dataset.\",\"id\":\"facebook/detr-resnet-50-panoptic\"},{\"description\":\"Semantic
+        segmentation model trained on ADE20k benchmark dataset.\",\"id\":\"microsoft/beit-large-finetuned-ade-640-640\"},{\"description\":\"Semantic
+        segmentation model trained on ADE20k benchmark dataset with 512x512 resolution.\",\"id\":\"nvidia/segformer-b0-finetuned-ade-512-512\"},{\"description\":\"Semantic
+        segmentation model trained Cityscapes dataset.\",\"id\":\"facebook/mask2former-swin-large-cityscapes-semantic\"},{\"description\":\"Panoptic
+        segmentation model trained COCO (common objects) dataset.\",\"id\":\"facebook/mask2former-swin-large-coco-panoptic\"}],\"spaces\":[{\"description\":\"A
+        semantic segmentation application that can predict unseen instances out of
+        the box.\",\"id\":\"facebook/ov-seg\"},{\"description\":\"One of the strongest
+        segmentation applications.\",\"id\":\"jbrinkma/segment-anything\"},{\"description\":\"A
+        semantic segmentation application that predicts human silhouettes.\",\"id\":\"keras-io/Human-Part-Segmentation\"},{\"description\":\"An
+        instance segmentation application to predict neuronal cell types from microscopy
+        images.\",\"id\":\"rashmi/sartorius-cell-instance-segmentation\"},{\"description\":\"An
+        application that segments videos.\",\"id\":\"ArtGAN/Segment-Anything-Video\"},{\"description\":\"An
+        panoptic segmentation application built for outdoor environments.\",\"id\":\"segments/panoptic-segment-anything\"}],\"summary\":\"Image
+        Segmentation divides an image into segments where each pixel in the image
+        is mapped to an object. This task has multiple variants such as instance segmentation,
+        panoptic segmentation and semantic segmentation.\",\"widgetModels\":[\"facebook/detr-resnet-50-panoptic\"],\"youtubeId\":\"dKE8SIt9C-w\",\"id\":\"image-segmentation\",\"label\":\"Image
+        Segmentation\",\"libraries\":[\"transformers\",\"transformers.js\"]},\"image-to-image\":{\"datasets\":[{\"description\":\"Synthetic
+        dataset, for image relighting\",\"id\":\"VIDIT\"},{\"description\":\"Multiple
+        images of celebrities, used for facial expression translation\",\"id\":\"huggan/CelebA-faces\"}],\"demo\":{\"inputs\":[{\"filename\":\"image-to-image-input.jpeg\",\"type\":\"img\"}],\"outputs\":[{\"filename\":\"image-to-image-output.png\",\"type\":\"img\"}]},\"isPlaceholder\":false,\"metrics\":[{\"description\":\"Peak
+        Signal to Noise Ratio (PSNR) is an approximation of the human perception,
+        considering the ratio of the absolute intensity with respect to the variations.
+        Measured in dB, a high value indicates a high fidelity.\",\"id\":\"PSNR\"},{\"description\":\"Structural
+        Similarity Index (SSIM) is a perceptual metric which compares the luminance,
+        contrast and structure of two images. The values of SSIM range between -1
+        and 1, and higher values indicate closer resemblance to the original image.\",\"id\":\"SSIM\"},{\"description\":\"Inception
+        Score (IS) is an analysis of the labels predicted by an image classification
+        model when presented with a sample of the generated images.\",\"id\":\"IS\"}],\"models\":[{\"description\":\"A
+        model that enhances images captured in low light conditions.\",\"id\":\"keras-io/low-light-image-enhancement\"},{\"description\":\"A
+        model that increases the resolution of an image.\",\"id\":\"keras-io/super-resolution\"},{\"description\":\"A
+        model that creates a set of variations of the input image in the style of
+        DALL-E using Stable Diffusion.\",\"id\":\"lambdalabs/sd-image-variations-diffusers\"},{\"description\":\"A
+        model that generates images based on segments in the input image and the text
+        prompt.\",\"id\":\"mfidabel/controlnet-segment-anything\"},{\"description\":\"A
+        model that takes an image and an instruction to edit the image.\",\"id\":\"timbrooks/instruct-pix2pix\"}],\"spaces\":[{\"description\":\"Image
+        enhancer application for low light.\",\"id\":\"keras-io/low-light-image-enhancement\"},{\"description\":\"Style
+        transfer application.\",\"id\":\"keras-io/neural-style-transfer\"},{\"description\":\"An
+        application that generates images based on segment control.\",\"id\":\"mfidabel/controlnet-segment-anything\"},{\"description\":\"Image
+        generation application that takes image control and text prompt.\",\"id\":\"hysts/ControlNet\"},{\"description\":\"Colorize
+        any image using this app.\",\"id\":\"ioclab/brightness-controlnet\"},{\"description\":\"Edit
+        images with instructions.\",\"id\":\"timbrooks/instruct-pix2pix\"}],\"summary\":\"Image-to-image
+        is the task of transforming a source image to match the characteristics of
+        a target image or a target image domain. Any image manipulation and enhancement
+        is possible with image to image models.\",\"widgetModels\":[\"lllyasviel/sd-controlnet-canny\"],\"youtubeId\":\"\",\"id\":\"image-to-image\",\"label\":\"Image-to-Image\",\"libraries\":[]},\"image-to-text\":{\"datasets\":[{\"description\":\"Dataset
+        from 12M image-text of Reddit\",\"id\":\"red_caps\"},{\"description\":\"Dataset
+        from 3.3M images of Google\",\"id\":\"datasets/conceptual_captions\"}],\"demo\":{\"inputs\":[{\"filename\":\"savanna.jpg\",\"type\":\"img\"}],\"outputs\":[{\"label\":\"Detailed
+        description\",\"content\":\"a herd of giraffes and zebras grazing in a field\",\"type\":\"text\"}]},\"metrics\":[],\"models\":[{\"description\":\"A
+        robust image captioning model.\",\"id\":\"Salesforce/blip-image-captioning-large\"},{\"description\":\"A
+        strong image captioning model.\",\"id\":\"nlpconnect/vit-gpt2-image-captioning\"},{\"description\":\"A
+        strong optical character recognition model.\",\"id\":\"microsoft/trocr-base-printed\"},{\"description\":\"A
+        strong visual question answering model for scientific diagrams.\",\"id\":\"google/pix2struct-ai2d-base\"},{\"description\":\"A
+        strong captioning model for UI components.\",\"id\":\"google/pix2struct-widget-captioning-base\"},{\"description\":\"A
+        captioning model for images that contain text.\",\"id\":\"google/pix2struct-textcaps-base\"}],\"spaces\":[{\"description\":\"A
+        robust image captioning application.\",\"id\":\"flax-community/image-captioning\"},{\"description\":\"An
+        application that transcribes handwritings into text.\",\"id\":\"nielsr/TrOCR-handwritten\"},{\"description\":\"An
+        application that can caption images and answer questions about a given image.\",\"id\":\"Salesforce/BLIP\"},{\"description\":\"An
+        application that can caption images and answer questions with a conversational
+        agent.\",\"id\":\"Salesforce/BLIP2\"},{\"description\":\"An image captioning
+        application that demonstrates the effect of noise on captions.\",\"id\":\"johko/capdec-image-captioning\"}],\"summary\":\"Image
+        to text models output a text from a given image. Image captioning or optical
+        character recognition can be considered as the most common applications of
+        image to text.\",\"widgetModels\":[\"Salesforce/blip-image-captioning-base\"],\"youtubeId\":\"\",\"id\":\"image-to-text\",\"label\":\"Image-to-Text\",\"libraries\":[\"transformers.js\"]},\"object-detection\":{\"datasets\":[{\"description\":\"Widely
+        used benchmark dataset for multiple Vision tasks.\",\"id\":\"merve/coco2017\"}],\"demo\":{\"inputs\":[{\"filename\":\"object-detection-input.jpg\",\"type\":\"img\"}],\"outputs\":[{\"filename\":\"object-detection-output.jpg\",\"type\":\"img\"}]},\"metrics\":[{\"description\":\"The
+        Average Precision (AP) metric is the Area Under the PR Curve (AUC-PR). It
+        is calculated for each class separately\",\"id\":\"Average Precision\"},{\"description\":\"The
+        Mean Average Precision (mAP) metric is the overall average of the AP values\",\"id\":\"Mean
+        Average Precision\"},{\"description\":\"The AP\u03B1 metric is the Average
+        Precision at the IoU threshold of a \u03B1 value, for example, AP50 and AP75\",\"id\":\"AP\u03B1\"}],\"models\":[{\"description\":\"Solid
+        object detection model trained on the benchmark dataset COCO 2017.\",\"id\":\"facebook/detr-resnet-50\"},{\"description\":\"Strong
+        object detection model trained on ImageNet-21k dataset.\",\"id\":\"microsoft/beit-base-patch16-224-pt22k-ft22k\"}],\"spaces\":[{\"description\":\"An
+        object detection application that can detect unseen objects out of the box.\",\"id\":\"adirik/OWL-ViT\"},{\"description\":\"An
+        application that contains various object detection models to try from.\",\"id\":\"Gradio-Blocks/Object-Detection-With-DETR-and-YOLOS\"},{\"description\":\"An
+        application that shows multiple cutting edge techniques for object detection
+        and tracking.\",\"id\":\"kadirnar/torchyolo\"},{\"description\":\"An object
+        tracking, segmentation and inpainting application.\",\"id\":\"VIPLab/Track-Anything\"}],\"summary\":\"Object
+        Detection models allow users to identify objects of certain defined classes.
+        Object detection models receive an image as input and output the images with
+        bounding boxes and labels on detected objects.\",\"widgetModels\":[\"facebook/detr-resnet-50\"],\"youtubeId\":\"WdAeKSOpxhw\",\"id\":\"object-detection\",\"label\":\"Object
+        Detection\",\"libraries\":[\"transformers\",\"transformers.js\"]},\"video-classification\":{\"datasets\":[{\"description\":\"Benchmark
+        dataset used for video classification with videos that belong to 400 classes.\",\"id\":\"kinetics400\"}],\"demo\":{\"inputs\":[{\"filename\":\"video-classification-input.gif\",\"type\":\"img\"}],\"outputs\":[{\"type\":\"chart\",\"data\":[{\"label\":\"Playing
+        Guitar\",\"score\":0.514},{\"label\":\"Playing Tennis\",\"score\":0.193},{\"label\":\"Cooking\",\"score\":0.068}]}]},\"metrics\":[{\"description\":\"\",\"id\":\"accuracy\"},{\"description\":\"\",\"id\":\"recall\"},{\"description\":\"\",\"id\":\"precision\"},{\"description\":\"\",\"id\":\"f1\"}],\"models\":[{\"description\":\"Strong
+        Video Classification model trained on the Kinects 400 dataset.\",\"id\":\"MCG-NJU/videomae-base-finetuned-kinetics\"},{\"description\":\"Strong
+        Video Classification model trained on the Kinects 400 dataset.\",\"id\":\"microsoft/xclip-base-patch32\"}],\"spaces\":[{\"description\":\"An
+        application that classifies video at different timestamps.\",\"id\":\"nateraw/lavila\"},{\"description\":\"An
+        application that classifies video.\",\"id\":\"fcakyon/video-classification\"}],\"summary\":\"Video
+        classification is the task of assigning a label or class to an entire video.
+        Videos are expected to have only one class for each video. Video classification
+        models take a video as input and return a prediction about which class the
+        video belongs to.\",\"widgetModels\":[],\"youtubeId\":\"\",\"id\":\"video-classification\",\"label\":\"Video
+        Classification\",\"libraries\":[]},\"question-answering\":{\"datasets\":[{\"description\":\"A
+        famous question answering dataset based on English articles from Wikipedia.\",\"id\":\"squad_v2\"},{\"description\":\"A
+        dataset of aggregated anonymized actual queries issued to the Google search
+        engine.\",\"id\":\"natural_questions\"}],\"demo\":{\"inputs\":[{\"label\":\"Question\",\"content\":\"Which
+        name is also used to describe the Amazon rainforest in English?\",\"type\":\"text\"},{\"label\":\"Context\",\"content\":\"The
+        Amazon rainforest, also known in English as Amazonia or the Amazon Jungle\",\"type\":\"text\"}],\"outputs\":[{\"label\":\"Answer\",\"content\":\"Amazonia\",\"type\":\"text\"}]},\"metrics\":[{\"description\":\"Exact
+        Match is a metric based on the strict character match of the predicted answer
+        and the right answer. For answers predicted correctly, the Exact Match will
+        be 1. Even if only one character is different, Exact Match will be 0\",\"id\":\"exact-match\"},{\"description\":\"
+        The F1-Score metric is useful if we value both false positives and false negatives
+        equally. The F1-Score is calculated on each word in the predicted sequence
+        against the correct answer\",\"id\":\"f1\"}],\"models\":[{\"description\":\"A
+        robust baseline model for most question answering domains.\",\"id\":\"deepset/roberta-base-squad2\"},{\"description\":\"A
+        special model that can answer questions from tables!\",\"id\":\"google/tapas-base-finetuned-wtq\"}],\"spaces\":[{\"description\":\"An
+        application that can answer a long question from Wikipedia.\",\"id\":\"deepset/wikipedia-assistant\"}],\"summary\":\"Question
+        Answering models can retrieve the answer to a question from a given text,
+        which is useful for searching for an answer in a document. Some question answering
+        models can generate answers without context!\",\"widgetModels\":[\"deepset/roberta-base-squad2\"],\"youtubeId\":\"ajPx5LwJD-I\",\"id\":\"question-answering\",\"label\":\"Question
+        Answering\",\"libraries\":[\"adapter-transformers\",\"allennlp\",\"transformers\",\"transformers.js\"]},\"reinforcement-learning\":{\"datasets\":[{\"description\":\"A
+        curation of widely used datasets for Data Driven Deep Reinforcement Learning
+        (D4RL)\",\"id\":\"edbeeching/decision_transformer_gym_replay\"}],\"demo\":{\"inputs\":[{\"label\":\"State\",\"content\":\"Red
+        traffic light, pedestrians are about to pass.\",\"type\":\"text\"}],\"outputs\":[{\"label\":\"Action\",\"content\":\"Stop
+        the car.\",\"type\":\"text\"},{\"label\":\"Next State\",\"content\":\"Yellow
+        light, pedestrians have crossed.\",\"type\":\"text\"}]},\"metrics\":[{\"description\":\"Accumulated
+        reward across all time steps discounted by a factor that ranges between 0
+        and 1 and determines how much the agent optimizes for future relative to immediate
+        rewards. Measures how good is the policy ultimately found by a given algorithm
+        considering uncertainty over the future.\",\"id\":\"Discounted Total Reward\"},{\"description\":\"Average
+        return obtained after running the policy for a certain number of evaluation
+        episodes. As opposed to total reward, mean reward considers how much reward
+        a given algorithm receives while learning.\",\"id\":\"Mean Reward\"},{\"description\":\"Measures
+        how good a given algorithm is after a predefined time. Some algorithms may
+        be guaranteed to converge to optimal behavior across many time steps. However,
+        an agent that reaches an acceptable level of optimality after a given time
+        horizon may be preferable to one that ultimately reaches optimality but takes
+        a long time.\",\"id\":\"Level of Performance After Some Time\"}],\"models\":[{\"description\":\"A
+        Reinforcement Learning model trained on expert data from the Gym Hopper environment\",\"id\":\"edbeeching/decision-transformer-gym-hopper-expert\"},{\"description\":\"A
+        PPO agent playing seals/CartPole-v0 using the stable-baselines3 library and
+        the RL Zoo.\",\"id\":\"HumanCompatibleAI/ppo-seals-CartPole-v0\"}],\"spaces\":[{\"description\":\"An
+        application for a cute puppy agent learning to catch a stick.\",\"id\":\"ThomasSimonini/Huggy\"},{\"description\":\"An
+        application to play Snowball Fight with a reinforcement learning agent.\",\"id\":\"ThomasSimonini/SnowballFight\"}],\"summary\":\"Reinforcement
+        learning is the computational approach of learning from action by interacting
+        with an environment through trial and error and receiving rewards (negative
+        or positive) as feedback\",\"widgetModels\":[],\"youtubeId\":\"q0BiUn5LiBc\",\"id\":\"reinforcement-learning\",\"label\":\"Reinforcement
+        Learning\",\"libraries\":[\"transformers\",\"stable-baselines3\",\"ml-agents\",\"sample-factory\"]},\"sentence-similarity\":{\"datasets\":[{\"description\":\"Bing
+        queries with relevant passages from various web sources.\",\"id\":\"ms_marco\"}],\"demo\":{\"inputs\":[{\"label\":\"Source
+        sentence\",\"content\":\"Machine learning is so easy.\",\"type\":\"text\"},{\"label\":\"Sentences
+        to compare to\",\"content\":\"Deep learning is so straightforward.\",\"type\":\"text\"},{\"label\":\"\",\"content\":\"This
+        is so difficult, like rocket science.\",\"type\":\"text\"},{\"label\":\"\",\"content\":\"I
+        can't believe how much I struggled with this.\",\"type\":\"text\"}],\"outputs\":[{\"type\":\"chart\",\"data\":[{\"label\":\"Deep
+        learning is so straightforward.\",\"score\":0.623},{\"label\":\"This is so
+        difficult, like rocket science.\",\"score\":0.413},{\"label\":\"I can't believe
+        how much I struggled with this.\",\"score\":0.256}]}]},\"metrics\":[{\"description\":\"Reciprocal
+        Rank is a measure used to rank the relevancy of documents given a set of documents.
+        Reciprocal Rank is the reciprocal of the rank of the document retrieved, meaning,
+        if the rank is 3, the Reciprocal Rank is 0.33. If the rank is 1, the Reciprocal
+        Rank is 1\",\"id\":\"Mean Reciprocal Rank\"},{\"description\":\"The similarity
+        of the embeddings is evaluated mainly on cosine similarity. It is calculated
+        as the cosine of the angle between two vectors. It is particularly useful
+        when your texts are not the same length\",\"id\":\"Cosine Similarity\"}],\"models\":[{\"description\":\"This
+        model works well for sentences and paragraphs and can be used for clustering/grouping
+        and semantic searches.\",\"id\":\"sentence-transformers/all-mpnet-base-v2\"},{\"description\":\"A
+        multilingual model trained for FAQ retrieval.\",\"id\":\"clips/mfaq\"}],\"spaces\":[{\"description\":\"An
+        application that leverages sentence similarity to answer questions from YouTube
+        videos.\",\"id\":\"Gradio-Blocks/Ask_Questions_To_YouTube_Videos\"},{\"description\":\"An
+        application that retrieves relevant PubMed abstracts for a given online article
+        which can be used as further references.\",\"id\":\"Gradio-Blocks/pubmed-abstract-retriever\"},{\"description\":\"An
+        application that leverages sentence similarity to summarize text.\",\"id\":\"nickmuchi/article-text-summarizer\"},{\"description\":\"A
+        guide that explains how Sentence Transformers can be used for semantic search.\",\"id\":\"sentence-transformers/Sentence_Transformers_for_semantic_search\"}],\"summary\":\"Sentence
+        Similarity is the task of determining how similar two texts are. Sentence
+        similarity models convert input texts into vectors (embeddings) that capture
+        semantic information and calculate how close (similar) they are between them.
+        This task is particularly useful for information retrieval and clustering/grouping.\",\"widgetModels\":[\"sentence-transformers/all-MiniLM-L6-v2\"],\"youtubeId\":\"VCZq5AkbNEU\",\"id\":\"sentence-similarity\",\"label\":\"Sentence
+        Similarity\",\"libraries\":[\"sentence-transformers\",\"spacy\",\"transformers.js\"]},\"summarization\":{\"datasets\":[{\"description\":\"News
+        articles in five different languages along with their summaries. Widely used
+        for benchmarking multilingual summarization models.\",\"id\":\"mlsum\"},{\"description\":\"English
+        conversations and their summaries. Useful for benchmarking conversational
+        agents.\",\"id\":\"samsum\"}],\"demo\":{\"inputs\":[{\"label\":\"Input\",\"content\":\"The
+        tower is 324 metres (1,063 ft) tall, about the same height as an 81-storey
+        building, and the tallest structure in Paris. Its base is square, measuring
+        125 metres (410 ft) on each side. It was the first structure to reach a height
+        of 300 metres. Excluding transmitters, the Eiffel Tower is the second tallest
+        free-standing structure in France after the Millau Viaduct.\",\"type\":\"text\"}],\"outputs\":[{\"label\":\"Output\",\"content\":\"The
+        tower is 324 metres (1,063 ft) tall, about the same height as an 81-storey
+        building. It was the first structure to reach a height of 300 metres.\",\"type\":\"text\"}]},\"metrics\":[{\"description\":\"The
+        generated sequence is compared against its summary, and the overlap of tokens
+        are counted. ROUGE-N refers to overlap of N subsequent tokens, ROUGE-1 refers
+        to overlap of single tokens and ROUGE-2 is the overlap of two subsequent tokens.\",\"id\":\"rouge\"}],\"models\":[{\"description\":\"A
+        strong summarization model trained on English news articles. Excels at generating
+        factual summaries.\",\"id\":\"facebook/bart-large-cnn\"},{\"description\":\"A
+        summarization model trained on medical articles.\",\"id\":\"google/bigbird-pegasus-large-pubmed\"}],\"spaces\":[{\"description\":\"An
+        application that can summarize long paragraphs.\",\"id\":\"pszemraj/summarize-long-text\"},{\"description\":\"A
+        much needed summarization application for terms and conditions.\",\"id\":\"ml6team/distilbart-tos-summarizer-tosdr\"},{\"description\":\"An
+        application that summarizes long documents.\",\"id\":\"pszemraj/document-summarization\"},{\"description\":\"An
+        application that can detect errors in abstractive summarization.\",\"id\":\"ml6team/post-processing-summarization\"}],\"summary\":\"Summarization
+        is the task of producing a shorter version of a document while preserving
+        its important information. Some models can extract text from the original
+        input, while other models can generate entirely new text.\",\"widgetModels\":[\"sshleifer/distilbart-cnn-12-6\"],\"youtubeId\":\"yHnr5Dk2zCI\",\"id\":\"summarization\",\"label\":\"Summarization\",\"libraries\":[\"transformers\",\"transformers.js\"]},\"table-question-answering\":{\"datasets\":[{\"description\":\"The
+        WikiTableQuestions dataset is a large-scale dataset for the task of question
+        answering on semi-structured tables.\",\"id\":\"wikitablequestions\"},{\"description\":\"WikiSQL
+        is a dataset of 80654 hand-annotated examples of questions and SQL queries
+        distributed across 24241 tables from Wikipedia.\",\"id\":\"wikisql\"}],\"demo\":{\"inputs\":[{\"table\":[[\"Rank\",\"Name\",\"No.of
+        reigns\",\"Combined days\"],[\"1\",\"lou Thesz\",\"3\",\"3749\"],[\"2\",\"Ric
+        Flair\",\"8\",\"3103\"],[\"3\",\"Harley Race\",\"7\",\"1799\"]],\"type\":\"tabular\"},{\"label\":\"Question\",\"content\":\"What
+        is the number of reigns for Harley Race?\",\"type\":\"text\"}],\"outputs\":[{\"label\":\"Result\",\"content\":\"7\",\"type\":\"text\"}]},\"metrics\":[{\"description\":\"Checks
+        whether the predicted answer(s) is the same as the ground-truth answer(s).\",\"id\":\"Denotation
+        Accuracy\"}],\"models\":[{\"description\":\"A table question answering model
+        that is capable of neural SQL execution, i.e., employ TAPEX to execute a SQL
+        query on a given table.\",\"id\":\"microsoft/tapex-base\"},{\"description\":\"A
+        robust table question answering model.\",\"id\":\"google/tapas-base-finetuned-wtq\"}],\"spaces\":[{\"description\":\"An
+        application that answers questions based on table CSV files.\",\"id\":\"katanaml/table-query\"}],\"summary\":\"Table
+        Question Answering (Table QA) is the answering a question about an information
+        on a given table.\",\"widgetModels\":[\"google/tapas-base-finetuned-wtq\"],\"id\":\"table-question-answering\",\"label\":\"Table
+        Question Answering\",\"libraries\":[\"transformers\"]},\"tabular-classification\":{\"datasets\":[{\"description\":\"A
+        comprehensive curation of datasets covering all benchmarks.\",\"id\":\"inria-soda/tabular-benchmark\"}],\"demo\":{\"inputs\":[{\"table\":[[\"Glucose\",\"Blood
+        Pressure \",\"Skin Thickness\",\"Insulin\",\"BMI\"],[\"148\",\"72\",\"35\",\"0\",\"33.6\"],[\"150\",\"50\",\"30\",\"0\",\"35.1\"],[\"141\",\"60\",\"29\",\"1\",\"39.2\"]],\"type\":\"tabular\"}],\"outputs\":[{\"table\":[[\"Diabetes\"],[\"1\"],[\"1\"],[\"0\"]],\"type\":\"tabular\"}]},\"metrics\":[{\"description\":\"\",\"id\":\"accuracy\"},{\"description\":\"\",\"id\":\"recall\"},{\"description\":\"\",\"id\":\"precision\"},{\"description\":\"\",\"id\":\"f1\"}],\"models\":[{\"description\":\"Breast
+        cancer prediction model based on decision trees.\",\"id\":\"scikit-learn/cancer-prediction-trees\"}],\"spaces\":[{\"description\":\"An
+        application that can predict defective products on a production line.\",\"id\":\"scikit-learn/tabular-playground\"},{\"description\":\"An
+        application that compares various tabular classification techniques on different
+        datasets.\",\"id\":\"scikit-learn/classification\"}],\"summary\":\"Tabular
+        classification is the task of classifying a target category (a group) based
+        on set of attributes.\",\"widgetModels\":[\"scikit-learn/tabular-playground\"],\"youtubeId\":\"\",\"id\":\"tabular-classification\",\"label\":\"Tabular
+        Classification\",\"libraries\":[\"sklearn\"]},\"tabular-regression\":{\"datasets\":[{\"description\":\"A
+        comprehensive curation of datasets covering all benchmarks.\",\"id\":\"inria-soda/tabular-benchmark\"}],\"demo\":{\"inputs\":[{\"table\":[[\"Car
+        Name\",\"Horsepower\",\"Weight\"],[\"ford torino\",\"140\",\"3,449\"],[\"amc
+        hornet\",\"97\",\"2,774\"],[\"toyota corolla\",\"65\",\"1,773\"]],\"type\":\"tabular\"}],\"outputs\":[{\"table\":[[\"MPG
+        (miles per gallon)\"],[\"17\"],[\"18\"],[\"31\"]],\"type\":\"tabular\"}]},\"metrics\":[{\"description\":\"\",\"id\":\"mse\"},{\"description\":\"Coefficient
+        of determination (or R-squared) is a measure of how well the model fits the
+        data. Higher R-squared is considered a better fit.\",\"id\":\"r-squared\"}],\"models\":[{\"description\":\"Fish
+        weight prediction based on length measurements and species.\",\"id\":\"scikit-learn/Fish-Weight\"}],\"spaces\":[{\"description\":\"An
+        application that can predict weight of a fish based on set of attributes.\",\"id\":\"scikit-learn/fish-weight-prediction\"}],\"summary\":\"Tabular
+        regression is the task of predicting a numerical value given a set of attributes.\",\"widgetModels\":[\"scikit-learn/Fish-Weight\"],\"youtubeId\":\"\",\"id\":\"tabular-regression\",\"label\":\"Tabular
+        Regression\",\"libraries\":[\"sklearn\"]},\"text-classification\":{\"datasets\":[{\"description\":\"A
+        widely used dataset used to benchmark multiple variants of text classification.\",\"id\":\"glue\"},{\"description\":\"A
+        text classification dataset used to benchmark natural language inference models\",\"id\":\"snli\"}],\"demo\":{\"inputs\":[{\"label\":\"Input\",\"content\":\"I
+        love Hugging Face!\",\"type\":\"text\"}],\"outputs\":[{\"type\":\"chart\",\"data\":[{\"label\":\"POSITIVE\",\"score\":0.9},{\"label\":\"NEUTRAL\",\"score\":0.1},{\"label\":\"NEGATIVE\",\"score\":0}]}]},\"metrics\":[{\"description\":\"\",\"id\":\"accuracy\"},{\"description\":\"\",\"id\":\"recall\"},{\"description\":\"\",\"id\":\"precision\"},{\"description\":\"The
+        F1 metric is the harmonic mean of the precision and recall. It can be calculated
+        as: F1 = 2 * (precision * recall) / (precision + recall)\",\"id\":\"f1\"}],\"models\":[{\"description\":\"A
+        robust model trained for sentiment analysis.\",\"id\":\"distilbert-base-uncased-finetuned-sst-2-english\"},{\"description\":\"Multi-genre
+        natural language inference model.\",\"id\":\"roberta-large-mnli\"}],\"spaces\":[{\"description\":\"An
+        application that can classify financial sentiment.\",\"id\":\"IoannisTr/Tech_Stocks_Trading_Assistant\"},{\"description\":\"A
+        dashboard that contains various text classification tasks.\",\"id\":\"miesnerjacob/Multi-task-NLP\"},{\"description\":\"An
+        application that analyzes user reviews in healthcare.\",\"id\":\"spacy/healthsea-demo\"}],\"summary\":\"Text
+        Classification is the task of assigning a label or class to a given text.
+        Some use cases are sentiment analysis, natural language inference, and assessing
+        grammatical correctness.\",\"widgetModels\":[\"distilbert-base-uncased-finetuned-sst-2-english\"],\"youtubeId\":\"leNG9fN9FQU\",\"id\":\"text-classification\",\"label\":\"Text
+        Classification\",\"libraries\":[\"adapter-transformers\",\"spacy\",\"transformers\",\"transformers.js\"]},\"text-generation\":{\"datasets\":[{\"description\":\"A
+        large multilingual dataset of text crawled from the web.\",\"id\":\"mc4\"},{\"description\":\"Diverse
+        open-source data consisting of 22 smaller high-quality datasets. It was used
+        to train GPT-Neo.\",\"id\":\"the_pile\"}],\"demo\":{\"inputs\":[{\"label\":\"Input\",\"content\":\"Once
+        upon a time,\",\"type\":\"text\"}],\"outputs\":[{\"label\":\"Output\",\"content\":\"Once
+        upon a time, we knew that our ancestors were on the verge of extinction. The
+        great explorers and poets of the Old World, from Alexander the Great to Chaucer,
+        are dead and gone. A good many of our ancient explorers and poets have\",\"type\":\"text\"}]},\"metrics\":[{\"description\":\"Cross
+        Entropy is a metric that calculates the difference between two probability
+        distributions. Each probability distribution is the distribution of predicted
+        words\",\"id\":\"Cross Entropy\"},{\"description\":\"The Perplexity metric
+        is the exponential of the cross-entropy loss. It evaluates the probabilities
+        assigned to the next word by the model. Lower perplexity indicates better
+        performance\",\"id\":\"Perplexity\"}],\"models\":[{\"description\":\"A large
+        language model trained for text generation.\",\"id\":\"bigscience/bloom-560m\"},{\"description\":\"A
+        large code generation model that can generate code in 80+ languages.\",\"id\":\"bigcode/starcoder\"},{\"description\":\"A
+        model trained to follow instructions, uses Pythia-12b as base model.\",\"id\":\"databricks/dolly-v2-12b\"},{\"description\":\"A
+        model trained to follow instructions curated by community, uses Pythia-12b
+        as base model.\",\"id\":\"OpenAssistant/oasst-sft-4-pythia-12b-epoch-3.5\"},{\"description\":\"A
+        large language model trained to generate text in English.\",\"id\":\"stabilityai/stablelm-tuned-alpha-7b\"},{\"description\":\"A
+        model trained to follow instructions, based on mosaicml/mpt-7b.\",\"id\":\"mosaicml/mpt-7b-instruct\"},{\"description\":\"A
+        large language model trained to generate text in English.\",\"id\":\"EleutherAI/pythia-12b\"},{\"description\":\"A
+        large text-to-text model trained to follow instructions.\",\"id\":\"google/flan-ul2\"},{\"description\":\"A
+        large and powerful text generation model.\",\"id\":\"tiiuae/falcon-40b\"}],\"spaces\":[{\"description\":\"A
+        robust text generation model that can perform various tasks through natural
+        language prompting.\",\"id\":\"huggingface/bloom_demo\"},{\"description\":\"An
+        text generation based application that can write code for 80+ languages.\",\"id\":\"bigcode/bigcode-playground\"},{\"description\":\"An
+        text generation based application for conversations.\",\"id\":\"h2oai/h2ogpt-chatbot\"},{\"description\":\"An
+        text generation application that combines OpenAI and Hugging Face models.\",\"id\":\"microsoft/HuggingGPT\"},{\"description\":\"An
+        text generation application that uses StableLM-tuned-alpha-7b.\",\"id\":\"stabilityai/stablelm-tuned-alpha-chat\"},{\"description\":\"An
+        UI that uses StableLM-tuned-alpha-7b.\",\"id\":\"togethercomputer/OpenChatKit\"}],\"summary\":\"Generating
+        text is the task of producing new text. These models can, for example, fill
+        in incomplete text or paraphrase.\",\"widgetModels\":[\"tiiuae/falcon-7b-instruct\"],\"youtubeId\":\"Vpjb1lu0MDk\",\"id\":\"text-generation\",\"label\":\"Text
+        Generation\",\"libraries\":[\"transformers\",\"transformers.js\"]},\"text-to-image\":{\"datasets\":[{\"description\":\"RedCaps
+        is a large-scale dataset of 12M image-text pairs collected from Reddit.\",\"id\":\"red_caps\"},{\"description\":\"Conceptual
+        Captions is a dataset consisting of ~3.3M images annotated with captions.\",\"id\":\"conceptual_captions\"}],\"demo\":{\"inputs\":[{\"label\":\"Input\",\"content\":\"A
+        city above clouds, pastel colors, Victorian style\",\"type\":\"text\"}],\"outputs\":[{\"filename\":\"image.jpeg\",\"type\":\"img\"}]},\"metrics\":[],\"models\":[{\"description\":\"A
+        latent text-to-image diffusion model capable of generating photo-realistic
+        images given any text input.\",\"id\":\"CompVis/stable-diffusion-v1-4\"},{\"description\":\"A
+        model that can be used to generate images based on text prompts. The DALL\xB7E
+        Mega model is the largest version of DALLE Mini.\",\"id\":\"dalle-mini/dalle-mega\"},{\"description\":\"A
+        text-to-image model that can generate coherent text inside image.\",\"id\":\"DeepFloyd/IF-I-XL-v1.0\"},{\"description\":\"A
+        powerful text-to-image model.\",\"id\":\"kakaobrain/karlo-v1-alpha\"}],\"spaces\":[{\"description\":\"A
+        powerful text-to-image application.\",\"id\":\"stabilityai/stable-diffusion\"},{\"description\":\"An
+        text-to-image application that can generate coherent text inside the image.\",\"id\":\"DeepFloyd/IF\"},{\"description\":\"An
+        powerful text-to-image application that can generate images.\",\"id\":\"kakaobrain/karlo\"},{\"description\":\"An
+        powerful text-to-image application that can generates 3D representations.\",\"id\":\"hysts/Shap-E\"}],\"summary\":\"Generates
+        images from input text. These models can be used to generate and modify images
+        based on text prompts.\",\"widgetModels\":[\"CompVis/stable-diffusion-v1-4\"],\"youtubeId\":\"\",\"id\":\"text-to-image\",\"label\":\"Text-to-Image\",\"libraries\":[]},\"text-to-speech\":{\"datasets\":[{\"description\":\"Thousands
+        of short audio clips of a single speaker.\",\"id\":\"lj_speech\"},{\"description\":\"Multi-speaker
+        English dataset.\",\"id\":\"LibriTTS\"}],\"demo\":{\"inputs\":[{\"label\":\"Input\",\"content\":\"I
+        love audio models on the Hub!\",\"type\":\"text\"}],\"outputs\":[{\"filename\":\"audio.wav\",\"type\":\"audio\"}]},\"metrics\":[{\"description\":\"The
+        Mel Cepstral Distortion (MCD) metric is used to calculate the quality of generated
+        speech.\",\"id\":\"mel cepstral distortion\"}],\"models\":[{\"description\":\"An
+        end-to-end speech synthesis model.\",\"id\":\"microsoft/speecht5_tts\"},{\"description\":\"An
+        multi-lingual TTS model.\",\"id\":\"facebook/mms-tts\"},{\"description\":\"A
+        powerful TTS model.\",\"id\":\"suno/bark\"}],\"spaces\":[{\"description\":\"An
+        application for end-to-end text-to-speech.\",\"id\":\"rendchevi/nix-tts\"},{\"description\":\"An
+        application that contains multiple speech recognition models for various languages
+        and datasets.\",\"id\":\"coqui/CoquiTTS\"},{\"description\":\"An application
+        that synthesizes speech for various speaker types.\",\"id\":\"Matthijs/speecht5-tts-demo\"}],\"summary\":\"Text-to-Speech
+        (TTS) is the task of generating natural sounding speech given text input.
+        TTS models can be extended to have a single model that generates speech for
+        multiple speakers and multiple languages.\",\"widgetModels\":[\"microsoft/speecht5_tts\"],\"youtubeId\":\"NW62DpzJ274\",\"id\":\"text-to-speech\",\"label\":\"Text-to-Speech\",\"libraries\":[\"espnet\",\"tensorflowtts\"]},\"text-to-video\":{\"datasets\":[],\"demo\":{\"inputs\":[],\"outputs\":[]},\"isPlaceholder\":true,\"metrics\":[],\"models\":[],\"spaces\":[],\"summary\":\"\",\"widgetModels\":[],\"id\":\"text-to-video\",\"label\":\"Text-to-Video\",\"libraries\":[]},\"token-classification\":{\"datasets\":[{\"description\":\"A
+        widely used dataset useful to benchmark named entity recognition models.\",\"id\":\"conll2003\"},{\"description\":\"A
+        multilingual dataset of Wikipedia articles annotated for named entity recognition
+        in over 150 different languages.\",\"id\":\"wikiann\"}],\"demo\":{\"inputs\":[{\"label\":\"Input\",\"content\":\"My
+        name is Omar and I live in Z\xFCrich.\",\"type\":\"text\"}],\"outputs\":[{\"text\":\"My
+        name is Omar and I live in Z\xFCrich.\",\"tokens\":[{\"type\":\"PERSON\",\"start\":11,\"end\":15},{\"type\":\"GPE\",\"start\":30,\"end\":36}],\"type\":\"text-with-tokens\"}]},\"metrics\":[{\"description\":\"\",\"id\":\"accuracy\"},{\"description\":\"\",\"id\":\"recall\"},{\"description\":\"\",\"id\":\"precision\"},{\"description\":\"\",\"id\":\"f1\"}],\"models\":[{\"description\":\"A
+        robust performance model to identify people, locations, organizations and
+        names of miscellaneous entities.\",\"id\":\"dslim/bert-base-NER\"},{\"description\":\"Flair
+        models are typically the state of the art in named entity recognition tasks.\",\"id\":\"flair/ner-english\"}],\"spaces\":[{\"description\":\"An
+        application that can recognizes entities, extracts noun chunks and recognizes
+        various linguistic features of each token.\",\"id\":\"spacy/gradio_pipeline_visualizer\"}],\"summary\":\"Token
+        classification is a natural language understanding task in which a label is
+        assigned to some tokens in a text. Some popular token classification subtasks
+        are Named Entity Recognition (NER) and Part-of-Speech (PoS) tagging. NER models
+        could be trained to identify specific entities in a text, such as dates, individuals
+        and places; and PoS tagging would identify, for example, which words in a
+        text are verbs, nouns, and punctuation marks.\",\"widgetModels\":[\"dslim/bert-base-NER\"],\"youtubeId\":\"wVHdVlPScxA\",\"id\":\"token-classification\",\"label\":\"Token
+        Classification\",\"libraries\":[\"adapter-transformers\",\"flair\",\"spacy\",\"span-marker\",\"stanza\",\"transformers\",\"transformers.js\"]},\"translation\":{\"datasets\":[{\"description\":\"A
+        dataset of copyright-free books translated into 16 different languages.\",\"id\":\"opus_books\"},{\"description\":\"An
+        example of translation between programming languages. This dataset consists
+        of functions in Java and C#.\",\"id\":\"code_x_glue_cc_code_to_code_trans\"}],\"demo\":{\"inputs\":[{\"label\":\"Input\",\"content\":\"My
+        name is Omar and I live in Z\xFCrich.\",\"type\":\"text\"}],\"outputs\":[{\"label\":\"Output\",\"content\":\"Mein
+        Name ist Omar und ich wohne in Z\xFCrich.\",\"type\":\"text\"}]},\"metrics\":[{\"description\":\"BLEU
+        score is calculated by counting the number of shared single or subsequent
+        tokens between the generated sequence and the reference. Subsequent n tokens
+        are called \u201Cn-grams\u201D. Unigram refers to a single token while bi-gram
+        refers to token pairs and n-grams refer to n subsequent tokens. The score
+        ranges from 0 to 1, where 1 means the translation perfectly matched and 0
+        did not match at all\",\"id\":\"bleu\"},{\"description\":\"\",\"id\":\"sacrebleu\"}],\"models\":[{\"description\":\"A
+        model that translates from English to French.\",\"id\":\"Helsinki-NLP/opus-mt-en-fr\"},{\"description\":\"A
+        general-purpose Transformer that can be used to translate from English to
+        German, French, or Romanian.\",\"id\":\"t5-base\"}],\"spaces\":[{\"description\":\"An
+        application that can translate between 100 languages.\",\"id\":\"Iker/Translate-100-languages\"},{\"description\":\"An
+        application that can translate between English, Spanish and Hindi.\",\"id\":\"EuroPython2022/Translate-with-Bloom\"}],\"summary\":\"Translation
+        is the task of converting text from one language to another.\",\"widgetModels\":[\"t5-small\"],\"youtubeId\":\"1JvfrvZgi6c\",\"id\":\"translation\",\"label\":\"Translation\",\"libraries\":[\"transformers\",\"transformers.js\"]},\"unconditional-image-generation\":{\"datasets\":[{\"description\":\"The
+        CIFAR-100 dataset consists of 60000 32x32 colour images in 100 classes, with
+        600 images per class.\",\"id\":\"cifar100\"},{\"description\":\"Multiple images
+        of celebrities, used for facial expression translation.\",\"id\":\"CelebA\"}],\"demo\":{\"inputs\":[{\"label\":\"Seed\",\"content\":\"42\",\"type\":\"text\"},{\"label\":\"Number
+        of images to generate:\",\"content\":\"4\",\"type\":\"text\"}],\"outputs\":[{\"filename\":\"unconditional-image-generation-output.jpeg\",\"type\":\"img\"}]},\"metrics\":[{\"description\":\"The
+        inception score (IS) evaluates the quality of generated images. It measures
+        the diversity of the generated images (the model predictions are evenly distributed
+        across all possible labels) and their 'distinction' or 'sharpness' (the model
+        confidently predicts a single label for each image).\",\"id\":\"Inception
+        score (IS)\"},{\"description\":\"The Fr\xE9chet Inception Distance (FID) evaluates
+        the quality of images created by a generative model by calculating the distance
+        between feature vectors for real and generated images.\",\"id\":\"Fre\u0107het
+        Inception Distance (FID)\"}],\"models\":[{\"description\":\"High-quality image
+        generation model trained on the CIFAR-10 dataset. It synthesizes images of
+        the ten classes presented in the dataset using diffusion probabilistic models,
+        a class of latent variable models inspired by considerations from nonequilibrium
+        thermodynamics.\",\"id\":\"google/ddpm-cifar10-32\"},{\"description\":\"High-quality
+        image generation model trained on the 256x256 CelebA-HQ dataset. It synthesizes
+        images of faces using diffusion probabilistic models, a class of latent variable
+        models inspired by considerations from nonequilibrium thermodynamics.\",\"id\":\"google/ddpm-celebahq-256\"}],\"spaces\":[{\"description\":\"An
+        application that can generate realistic faces.\",\"id\":\"CompVis/celeba-latent-diffusion\"}],\"summary\":\"Unconditional
+        image generation is the task of generating images with no condition in any
+        context (like a prompt text or another image). Once trained, the model will
+        create images that resemble its training data distribution.\",\"widgetModels\":[\"\"],\"youtubeId\":\"\",\"id\":\"unconditional-image-generation\",\"label\":\"Unconditional
+        Image Generation\",\"libraries\":[]},\"visual-question-answering\":{\"datasets\":[{\"description\":\"A
+        widely used dataset containing questions (with answers) about images.\",\"id\":\"Graphcore/vqa\"},{\"description\":\"A
+        dataset to benchmark visual reasoning based on text in images.\",\"id\":\"textvqa\"}],\"demo\":{\"inputs\":[{\"filename\":\"elephant.jpeg\",\"type\":\"img\"},{\"label\":\"Question\",\"content\":\"What
+        is in this image?\",\"type\":\"text\"}],\"outputs\":[{\"type\":\"chart\",\"data\":[{\"label\":\"elephant\",\"score\":0.97},{\"label\":\"elephants\",\"score\":0.06},{\"label\":\"animal\",\"score\":0.003}]}]},\"isPlaceholder\":false,\"metrics\":[{\"description\":\"\",\"id\":\"accuracy\"},{\"description\":\"Measures
+        how much a predicted answer differs from the ground truth based on the difference
+        in their semantic meaning.\",\"id\":\"wu-palmer similarity\"}],\"models\":[{\"description\":\"A
+        visual question answering model trained to convert charts and plots to text.\",\"id\":\"google/deplot\"},{\"description\":\"A
+        visual question answering model trained for mathematical reasoning and chart
+        derendering from images.\",\"id\":\"google/matcha-base \"},{\"description\":\"A
+        strong visual question answering that answers questions from book covers.\",\"id\":\"google/pix2struct-ocrvqa-large\"}],\"spaces\":[{\"description\":\"An
+        application that can answer questions based on images.\",\"id\":\"nielsr/vilt-vqa\"},{\"description\":\"An
+        application that can caption images and answer questions about a given image.
+        \",\"id\":\"Salesforce/BLIP\"},{\"description\":\"An application that can
+        caption images and answer questions about a given image. \",\"id\":\"vumichien/Img2Prompt\"}],\"summary\":\"Visual
+        Question Answering is the task of answering open-ended questions based on
+        an image. They output natural language responses to natural language questions.\",\"widgetModels\":[\"dandelin/vilt-b32-finetuned-vqa\"],\"youtubeId\":\"\",\"id\":\"visual-question-answering\",\"label\":\"Visual
+        Question Answering\",\"libraries\":[]},\"zero-shot-classification\":{\"datasets\":[{\"description\":\"A
+        widely used dataset used to benchmark multiple variants of text classification.\",\"id\":\"glue\"},{\"description\":\"The
+        Multi-Genre Natural Language Inference (MultiNLI) corpus is a crowd-sourced
+        collection of 433k sentence pairs annotated with textual entailment information.\",\"id\":\"MultiNLI\"},{\"description\":\"FEVER
+        is a publicly available dataset for fact extraction and verification against
+        textual sources.\",\"id\":\"FEVER\"}],\"demo\":{\"inputs\":[{\"label\":\"Text
+        Input\",\"content\":\"Dune is the best movie ever.\",\"type\":\"text\"},{\"label\":\"Candidate
+        Labels\",\"content\":\"CINEMA, ART, MUSIC\",\"type\":\"text\"}],\"outputs\":[{\"type\":\"chart\",\"data\":[{\"label\":\"CINEMA\",\"score\":0.9},{\"label\":\"ART\",\"score\":0.1},{\"label\":\"MUSIC\",\"score\":0}]}]},\"metrics\":[],\"models\":[{\"description\":\"Powerful
+        zero-shot text classification model\",\"id\":\"facebook/bart-large-mnli\"}],\"spaces\":[],\"summary\":\"Zero-shot
+        text classification is a task in natural language processing where a model
+        is trained on a set of labeled examples but is then able to classify new examples
+        from previously unseen classes.\",\"widgetModels\":[\"facebook/bart-large-mnli\"],\"id\":\"zero-shot-classification\",\"label\":\"Zero-Shot
+        Classification\",\"libraries\":[\"transformers\",\"transformers.js\"]},\"zero-shot-image-classification\":{\"datasets\":[{\"description\":\"\",\"id\":\"\"}],\"demo\":{\"inputs\":[{\"filename\":\"image-classification-input.jpeg\",\"type\":\"img\"},{\"label\":\"Classes\",\"content\":\"cat,
+        dog, bird\",\"type\":\"text\"}],\"outputs\":[{\"type\":\"chart\",\"data\":[{\"label\":\"Cat\",\"score\":0.664},{\"label\":\"Dog\",\"score\":0.329},{\"label\":\"Bird\",\"score\":0.008}]}]},\"metrics\":[{\"description\":\"Computes
+        the number of times the correct label appears in top K labels predicted\",\"id\":\"top-K
+        accuracy\"}],\"models\":[{\"description\":\"Robust image classification model
+        trained on publicly available image-caption data.\",\"id\":\"openai/clip-vit-base-patch16\"},{\"description\":\"Robust
+        image classification model trained on publicly available image-caption data
+        trained on additional high pixel data for better performance.\",\"id\":\"openai/clip-vit-large-patch14-336\"},{\"description\":\"Strong
+        image classification model for biomedical domain.\",\"id\":\"microsoft/BiomedCLIP-PubMedBERT_256-vit_base_patch16_224\"}],\"spaces\":[{\"description\":\"An
+        application that leverages zero shot image classification to find best captions
+        to generate an image. \",\"id\":\"pharma/CLIP-Interrogator\"}],\"summary\":\"Zero
+        shot image classification is the task of classifying previously unseen classes
+        during training of a model.\",\"widgetModels\":[\"openai/clip-vit-large-patch14-336\"],\"youtubeId\":\"\",\"id\":\"zero-shot-image-classification\",\"label\":\"Zero-Shot
+        Image Classification\",\"libraries\":[\"transformers.js\"]}}"
+    headers:
+      Access-Control-Allow-Origin:
+      - https://hub-ci.huggingface.co
+      Access-Control-Expose-Headers:
+      - X-Repo-Commit,X-Request-Id,X-Error-Code,X-Error-Message,ETag,Link,Accept-Ranges,Content-Range
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '56498'
+      Content-Type:
+      - application/json; charset=utf-8
+      Date:
+      - Wed, 28 Jun 2023 11:46:22 GMT
+      ETag:
+      - W/"dcb2-9pFYIscGsHQh4SeX6alHKqUty68"
+      Vary:
+      - Origin
+      X-Powered-By:
+      - huggingface-moon
+      X-Request-Id:
+      - Root=1-649c1d8e-2d39bb6e35bcb1d2482c7c50
+    status:
+      code: 200
+      message: OK
+- request:
+    body: null
+    headers:
+      user-agent:
+      - unknown/None; hf_hub/0.16.0.dev0; python/3.10.6; torch/1.12.1; tensorflow/2.11.0;
+        fastcore/1.5.23
+    method: POST
+    uri: https://api-inference.huggingface.co/pipeline/sentence-similarity/sentence-transformers/all-MiniLM-L6-v2
+  response:
+    body:
+      string: '[0.7785726189613342,0.4587625563144684,0.2906219959259033]'
+    headers:
+      Access-Control-Allow-Credentials:
+      - 'true'
+      Connection:
+      - keep-alive
+      Content-Length:
+      - '58'
+      Content-Type:
+      - application/json
+      Date:
+      - Wed, 28 Jun 2023 11:46:24 GMT
+      Vary:
+      - Origin, Access-Control-Request-Method, Access-Control-Request-Headers
+      x-compute-time:
+      - '0.031'
+      x-compute-type:
+      - cache
+      x-request-id:
+      - HTPTTEayayyY6Dh2328Rx
+      x-sha:
+      - 7dbbc90392e2f80f3d3c277d6e90027e55de9125
+    status:
+      code: 200
+      message: OK
+    url: https://api-inference.huggingface.co/pipeline/sentence-similarity/sentence-transformers/all-MiniLM-L6-v2
+version: 1
diff --git a/tests/test_inference_async_client.py b/tests/test_inference_async_client.py
new file mode 100644
index 00000000..1c98528e
--- /dev/null
+++ b/tests/test_inference_async_client.py
@@ -0,0 +1,200 @@
+# coding=utf-8
+# Copyright 2023-present, the HuggingFace Inc. team.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Contains tests for AsyncInferenceClient.
+
+Tests are run directly with pytest instead of unittest.TestCase as it's much easier to run with asyncio.
+
+Not all tasks are tested. We extensively test `text_generation` method since it's the most complex one (has different
+return types + uses streaming requests on demand). Tests are mostly duplicates from test_inference_text_generation.py`.
+
+For completeness we also run a test on a simple task (`test_async_sentence_similarity`) and assume all other tasks
+work as well.
+"""
+import inspect
+
+import pytest
+
+import huggingface_hub.inference._common
+from huggingface_hub import AsyncInferenceClient, InferenceClient
+from huggingface_hub.inference._common import _is_tgi_server
+from huggingface_hub.inference._text_generation import FinishReason, InputToken
+from huggingface_hub.inference._text_generation import ValidationError as TextGenerationValidationError
+
+
+@pytest.fixture(autouse=True)
+def patch_non_tgi_server(monkeypatch: pytest.MonkeyPatch):
+    monkeypatch.setattr(huggingface_hub.inference._common, "_NON_TGI_SERVERS", set())
+
+
+@pytest.fixture
+def tgi_client() -> AsyncInferenceClient:
+    return AsyncInferenceClient(model="google/flan-t5-xxl")
+
+
+@pytest.mark.vcr
+@pytest.mark.asyncio
+async def test_async_generate_no_details(tgi_client: AsyncInferenceClient) -> None:
+    response = await tgi_client.text_generation("test", details=False, max_new_tokens=1)
+    assert response == ""
+
+
+@pytest.mark.vcr
+@pytest.mark.asyncio
+async def test_async_generate_with_details(tgi_client: AsyncInferenceClient) -> None:
+    response = await tgi_client.text_generation("test", details=True, max_new_tokens=1, decoder_input_details=True)
+
+    assert response.generated_text == ""
+    assert response.details.finish_reason == FinishReason.Length
+    assert response.details.generated_tokens == 1
+    assert response.details.seed is None
+    assert len(response.details.prefill) == 1
+    assert response.details.prefill[0] == InputToken(id=0, text="<pad>", logprob=None)
+    assert len(response.details.tokens) == 1
+    assert response.details.tokens[0].id == 3
+    assert response.details.tokens[0].text == " "
+    assert not response.details.tokens[0].special
+
+
+@pytest.mark.vcr
+@pytest.mark.asyncio
+async def test_async_generate_best_of(tgi_client: AsyncInferenceClient) -> None:
+    response = await tgi_client.text_generation(
+        "test", max_new_tokens=1, best_of=2, do_sample=True, decoder_input_details=True, details=True
+    )
+
+    assert response.details.seed is not None
+    assert response.details.best_of_sequences is not None
+    assert len(response.details.best_of_sequences) == 1
+    assert response.details.best_of_sequences[0].seed is not None
+
+
+@pytest.mark.vcr
+@pytest.mark.asyncio
+async def test_async_generate_validation_error(tgi_client: AsyncInferenceClient) -> None:
+    with pytest.raises(TextGenerationValidationError):
+        await tgi_client.text_generation("test", max_new_tokens=10_000)
+
+
+@pytest.mark.vcr
+@pytest.mark.asyncio
+async def test_async_generate_non_tgi_endpoint(tgi_client: AsyncInferenceClient) -> None:
+    text = await tgi_client.text_generation("0 1 2", model="gpt2", max_new_tokens=10)
+    assert text == " 3 4 5 6 7 8 9 10 11 12"
+    assert not _is_tgi_server("gpt2")
+
+    # Watermark is ignored (+ warning)
+    with pytest.warns(UserWarning):
+        await tgi_client.text_generation("4 5 6", model="gpt2", max_new_tokens=10, watermark=True)
+
+    # Return as detail even if details=True (+ warning)
+    with pytest.warns(UserWarning):
+        text = await tgi_client.text_generation("0 1 2", model="gpt2", max_new_tokens=10, details=True)
+    assert isinstance(text, str)
+
+    # Return as stream raises error
+    with pytest.raises(ValueError):
+        await tgi_client.text_generation("0 1 2", model="gpt2", max_new_tokens=10, stream=True)
+
+
+@pytest.mark.vcr
+@pytest.mark.asyncio
+async def test_async_generate_stream_no_details(tgi_client: AsyncInferenceClient) -> None:
+    responses = [
+        response async for response in await tgi_client.text_generation("test", max_new_tokens=1, stream=True)
+    ]
+
+    assert len(responses) == 1
+    response = responses[0]
+
+    assert isinstance(response, str)
+    assert response == " "
+
+
+@pytest.mark.vcr
+@pytest.mark.asyncio
+async def test_async_generate_stream_with_details(tgi_client: AsyncInferenceClient) -> None:
+    responses = [
+        response
+        async for response in await tgi_client.text_generation("test", max_new_tokens=1, stream=True, details=True)
+    ]
+
+    assert len(responses) == 1
+    response = responses[0]
+
+    assert response.generated_text == ""
+    assert response.details.finish_reason == FinishReason.Length
+    assert response.details.generated_tokens == 1
+    assert response.details.seed is None
+
+
+@pytest.mark.vcr
+@pytest.mark.asyncio
+async def test_async_sentence_similarity() -> None:
+    async_client = AsyncInferenceClient()
+    scores = await async_client.sentence_similarity(
+        "Machine learning is so easy.",
+        other_sentences=[
+            "Deep learning is so straightforward.",
+            "This is so difficult, like rocket science.",
+            "I can't believe how much I struggled with this.",
+        ],
+    )
+    assert scores == [0.7785726189613342, 0.4587625563144684, 0.2906219959259033]
+
+
+def test_sync_vs_async_signatures() -> None:
+    client = InferenceClient()
+    async_client = AsyncInferenceClient()
+
+    # Some methods have to be tested separately.
+    special_methods = ["post", "text_generation"]
+
+    # Post: this is not automatically tested. No need to test its signature separately.
+
+    # Text-generation: return type changes from Iterable[...] to AsyncIterable[...] but input parameters are the same
+    sync_method = getattr(client, "text_generation")
+    assert not inspect.iscoroutinefunction(sync_method)
+    async_method = getattr(async_client, "text_generation")
+    assert inspect.iscoroutinefunction(async_method)
+
+    sync_sig = inspect.signature(sync_method)
+    async_sig = inspect.signature(async_method)
+    assert sync_sig.parameters == async_sig.parameters
+    assert sync_sig.return_annotation != async_sig.return_annotation
+
+    [name for name in dir(client) if (not name.startswith("_")) and inspect.ismethod(getattr(client, name))]
+
+    # Check that all methods are consistent between InferenceClient and AsyncInferenceClient
+    for name in dir(client):
+        if not inspect.ismethod(getattr(client, name)):  # not a method
+            continue
+        if name.startswith("_"):  # not public method
+            continue
+        if name in special_methods:  # tested separately
+            continue
+
+        # Check that the sync method is not async
+        sync_method = getattr(client, name)
+        assert not inspect.iscoroutinefunction(sync_method)
+
+        # Check that the async method is async
+        async_method = getattr(async_client, name)
+        assert inspect.iscoroutinefunction(async_method)
+
+        # Check that expected inputs and outputs are the same
+        sync_sig = inspect.signature(sync_method)
+        async_sig = inspect.signature(async_method)
+        assert sync_sig.parameters == async_sig.parameters
+        assert sync_sig.return_annotation == async_sig.return_annotation
diff --git a/tests/test_inference_client.py b/tests/test_inference_client.py
index 757a47f1..abba7509 100644
--- a/tests/test_inference_client.py
+++ b/tests/test_inference_client.py
@@ -56,7 +56,7 @@ class InferenceClientTest(unittest.TestCase):
 
 @pytest.mark.vcr
 @with_production_testing
-@patch("huggingface_hub.inference._client._fetch_recommended_models", lambda: _RECOMMENDED_MODELS_FOR_VCR)
+@patch("huggingface_hub.inference._common._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
@@ -188,6 +188,9 @@ class InferenceClientVCRTest(InferenceClientTest):
             ),
         )
 
+    def test_text_generation(self) -> None:
+        """Tested separately in `test_inference_text_generation.py`."""
+
     def test_text_to_image(self) -> None:
         image = self.client.text_to_image("An astronaut riding a horse on the moon.")
         self.assertIsInstance(image, Image.Image)
@@ -303,7 +306,7 @@ class TestHeadersAndCookies(unittest.TestCase):
         """Test that headers and cookies are correctly passed to the request."""
         client = InferenceClient(headers={"X-My-Header": "foo"}, cookies={"my-cookie": "bar"})
         response = client.post(data=b"content", model="username/repo_name")
-        self.assertEqual(response, get_session_mock().post.return_value)
+        self.assertEqual(response, get_session_mock().post.return_value.content)
 
         expected_user_agent = build_hf_headers()["user-agent"]
         get_session_mock().post.assert_called_once_with(
diff --git a/tests/test_inference_text_generation.py b/tests/test_inference_text_generation.py
index a426fb8b..6f76be05 100644
--- a/tests/test_inference_text_generation.py
+++ b/tests/test_inference_text_generation.py
@@ -11,7 +11,7 @@ from pydantic import ValidationError
 from requests import HTTPError
 
 from huggingface_hub import InferenceClient
-from huggingface_hub.inference._client import _NON_TGI_SERVERS
+from huggingface_hub.inference._common import _NON_TGI_SERVERS
 from huggingface_hub.inference._text_generation import (
     FinishReason,
     GenerationError,
@@ -136,7 +136,7 @@ def _mocked_error(payload: Dict) -> MagicMock:
 
 
 @pytest.mark.vcr
-@patch.dict("huggingface_hub.inference._client._NON_TGI_SERVERS", {})
+@patch.dict("huggingface_hub.inference._common._NON_TGI_SERVERS", {})
 class TestTextGenerationClientVCR(unittest.TestCase):
     """Use VCR test to avoid making requests to the prod infra."""