Repository: huggingface/huggingface_hub (python)

Issue:
Handle proxy with AsyncInferenceClient post request
There is a problem when you try to do a post request with an AsyncInferenceClient object : 
It doesn't handle the case where you're using a proxy, so the post request never reaches the given URL.
I wanted to add the ability to configure the proxy to perform this post request.

Here is a proposal of what I did to add this feature : 

```
class AsyncInferenceClient : 
   def __init__(
      self,
      ....
      proxy: Optional[str] = None
   ) -> None:
      ...
      self.proxy = proxy

   async def post(...):
      ...
      try:
         response = await client.post(url, json=json, data=data_as_binary, proxy=self.proxy)
      ...


class InferenceClient():
   def __init__(
      self,
      ...
      proxy: Optional[str] = None
   ) -> None: 
      ...
      self.proxy = proxy

   def post(...):
      ...
      try: 
         response = get_session().post(url, ..., proxies=self.proxy)
      ...
```


I did for both synchronous and asynchronous InferenceClient because of the following code in HuggingFaceEndpoint : 

```
class HuggingFaceEndpoint(LLM):
   def validate_environment(...) -> Dict:
      ...
      values["client"] = InferenceClient(
            model=values["model"],
            timeout=values["timeout"],
            token=huggingfacehub_api_token,
            **values["server_kwargs"],
        )
        values["async_client"] = AsyncInferenceClient(
            model=values["model"],
            timeout=values["timeout"],
            token=huggingfacehub_api_token,
            **values["server_kwargs"],
        )
```


To finish, here is an example of how to build a HuggingFaceEndpoint object with a specified proxy for our AsyncInferenceClient : 

`HuggingFaceEndpoint(endpoint_url=... , ... , server_kwargs={"proxy": "http://localhost:3128"})`

Pull request:
Add proxy support on async client
Closes #2349 

Patch:
diff --git a/src/huggingface_hub/inference/_client.py b/src/huggingface_hub/inference/_client.py
index d0917724..6648158e 100644
--- a/src/huggingface_hub/inference/_client.py
+++ b/src/huggingface_hub/inference/_client.py
@@ -157,6 +157,7 @@ class InferenceClient:
         timeout: Optional[float] = None,
         headers: Optional[Dict[str, str]] = None,
         cookies: Optional[Dict[str, str]] = None,
+        proxies: Optional[Any] = None,
     ) -> None:
         self.model: Optional[str] = model
         self.token: Union[str, bool, None] = token
@@ -165,6 +166,7 @@ class InferenceClient:
             self.headers.update(headers)
         self.cookies = cookies
         self.timeout = timeout
+        self.proxies = proxies
 
     def __repr__(self):
         return f"<InferenceClient(model='{self.model if self.model else ''}', timeout={self.timeout})>"
@@ -264,6 +266,7 @@ class InferenceClient:
                         cookies=self.cookies,
                         timeout=self.timeout,
                         stream=stream,
+                        proxies=self.proxies,
                     )
                 except TimeoutError as error:
                     # Convert any `TimeoutError` to a `InferenceTimeoutError`
diff --git a/src/huggingface_hub/inference/_generated/_async_client.py b/src/huggingface_hub/inference/_generated/_async_client.py
index d7571de1..2c29b4e4 100644
--- a/src/huggingface_hub/inference/_generated/_async_client.py
+++ b/src/huggingface_hub/inference/_generated/_async_client.py
@@ -142,6 +142,7 @@ class AsyncInferenceClient:
         timeout: Optional[float] = None,
         headers: Optional[Dict[str, str]] = None,
         cookies: Optional[Dict[str, str]] = None,
+        proxies: Optional[Any] = None,
     ) -> None:
         self.model: Optional[str] = model
         self.token: Union[str, bool, None] = token
@@ -150,6 +151,7 @@ class AsyncInferenceClient:
             self.headers.update(headers)
         self.cookies = cookies
         self.timeout = timeout
+        self.proxies = proxies
 
     def __repr__(self):
         return f"<InferenceClient(model='{self.model if self.model else ''}', timeout={self.timeout})>"
@@ -250,7 +252,7 @@ class AsyncInferenceClient:
                 )
 
                 try:
-                    response = await client.post(url, json=json, data=data_as_binary)
+                    response = await client.post(url, json=json, data=data_as_binary, proxy=self.proxies)
                     response_error_payload = None
                     if response.status != 200:
                         try:
diff --git a/utils/generate_async_inference_client.py b/utils/generate_async_inference_client.py
index eaa0d7f0..ce3e5cfa 100644
--- a/utils/generate_async_inference_client.py
+++ b/utils/generate_async_inference_client.py
@@ -198,7 +198,7 @@ ASYNC_POST_CODE = """
                 )
 
                 try:
-                    response = await client.post(url, json=json, data=data_as_binary)
+                    response = await client.post(url, json=json, data=data_as_binary, proxy=self.proxies)
                     response_error_payload = None
                     if response.status != 200:
                         try:


Test patch:
diff --git a/tests/test_inference_client.py b/tests/test_inference_client.py
index 78595f78..06fdc705 100644
--- a/tests/test_inference_client.py
+++ b/tests/test_inference_client.py
@@ -767,7 +767,9 @@ class TestHeadersAndCookies(unittest.TestCase):
     @patch("huggingface_hub.inference._client.get_session")
     def test_mocked_post(self, get_session_mock: MagicMock) -> None:
         """Test that headers and cookies are correctly passed to the request."""
-        client = InferenceClient(headers={"X-My-Header": "foo"}, cookies={"my-cookie": "bar"})
+        client = InferenceClient(
+            headers={"X-My-Header": "foo"}, cookies={"my-cookie": "bar"}, proxies="custom proxies"
+        )
         response = client.post(data=b"content", model="username/repo_name")
         self.assertEqual(response, get_session_mock().post.return_value.content)
 
@@ -780,6 +782,7 @@ class TestHeadersAndCookies(unittest.TestCase):
             cookies={"my-cookie": "bar"},
             timeout=None,
             stream=False,
+            proxies="custom proxies",
         )
 
     @patch("huggingface_hub.inference._client._bytes_to_image")