Repository: bentoml/BentoML

Issue:
bug: `bentoml.client` API is broken
### Describe the bug

In bentoml 1.1.7, when I run a service using `bentoml serve` and try to connect to it using the `bentoml.client` API, it always throws exceptions.

### To reproduce

Steps to reproduce the bug:

1. start serving any bentoml model at `localhost:3000` using `bentoml serve model.module:service`
2. in another terminal, run the following python code:
    ```python
   from bentoml.client import Client
   client = Client.from_url("http://localhost:3000")
   client.call("predict", "model input")
    ```
   When I run this, I get the following output:
   ```
   Traceback (most recent call last):
     File "/Users/alexparker/personal/bentoml-client-bug/call_service.py", line 4, in <module>
       client.call("predict", "model input")
     File "/Users/alexparker/personal/bentoml-client-bug/venv/lib/python3.11/site-packages/bentoml/_internal/client/__init__.py", line 284, in call
       return self._call(inp, _bentoml_api=self._svc.apis[bentoml_api_name], **kwargs)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     File "/Users/alexparker/personal/bentoml-client-bug/venv/lib/python3.11/site-packages/bentoml/_internal/client/http.py", line 311, in _call
       req_body = asyncio.run(get_body)
                  ^^^^^^^^^^^^^^^^^^^^^
     File "/Users/alexparker/.pyenv/versions/3.11.6/lib/python3.11/asyncio/runners.py", line 190, in run
       return runner.run(main)
              ^^^^^^^^^^^^^^^^
     File "/Users/alexparker/.pyenv/versions/3.11.6/lib/python3.11/asyncio/runners.py", line 89, in run
       raise ValueError("a coroutine was expected, got {!r}".format(coro))
   ValueError: a coroutine was expected, got <function SyncHTTPClient._call.<locals>.get_body at 0x10744bc40>
   ```
3. Something similar happens when running the async client:
   ```python
   import asyncio
   from bentoml.client import AsyncHTTPClient
   async def main():
       client = await AsyncHTTPClient.from_url("http://localhost:3000")
       await client.call("predict", "model input")
   asyncio.run(main())
   ```
   ```
   Traceback (most recent call last):
     File "/Users/alexparker/personal/bentoml-client-bug/call_service_async.py", line 6, in <module>
       asyncio.run(main())
     File "/Users/alexparker/.pyenv/versions/3.11.6/lib/python3.11/asyncio/runners.py", line 190, in run
       return runner.run(main)
              ^^^^^^^^^^^^^^^^
     File "/Users/alexparker/.pyenv/versions/3.11.6/lib/python3.11/asyncio/runners.py", line 118, in run
       return self._loop.run_until_complete(task)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     File "/Users/alexparker/.pyenv/versions/3.11.6/lib/python3.11/asyncio/base_events.py", line 653, in run_until_complete
       return future.result()
              ^^^^^^^^^^^^^^^
     File "/Users/alexparker/personal/bentoml-client-bug/call_service_async.py", line 5, in main
       await client.call("predict", "model input")
     File "/Users/alexparker/personal/bentoml-client-bug/venv/lib/python3.11/site-packages/bentoml/_internal/client/__init__.py", line 161, in call
       return await self._call(
              ^^^^^^^^^^^^^^^^^
     File "/Users/alexparker/personal/bentoml-client-bug/venv/lib/python3.11/site-packages/bentoml/_internal/client/http.py", line 157, in _call
       req_body = fake_resp.body
                  ^^^^^^^^^^^^^^
   AttributeError: 'StreamingResponse' object has no attribute 'body'
   ```

### Expected behavior

In bentoml 1.1.6, the client doesn't throw an exception and I can get the output of the response.

The offending lines are [here](https://github.com/bentoml/BentoML/blob/v1.1.7/src/bentoml/_internal/client/http.py#L308-L311) for the sync client:
```python
            async def get_body():
                return "".join([s async for s in fake_resp.body_iterator])

            req_body = asyncio.run(get_body)
```

And [here](https://github.com/bentoml/BentoML/blob/v1.1.7/src/bentoml/_internal/client/http.py#L156-L159) for the async client:
```python
        if isinstance(fake_resp, starlette.responses.StreamingResponse):
            req_body = fake_resp.body
        else:
            req_body = fake_resp.body
```

They seem like a simple enough fix, so I'd be happy to open a PR.

### Environment

#### Environment variable

```bash
BENTOML_DEBUG=''
BENTOML_QUIET=''
BENTOML_BUNDLE_LOCAL_BUILD=''
BENTOML_DO_NOT_TRACK=''
BENTOML_CONFIG=''
BENTOML_CONFIG_OPTIONS=''
BENTOML_PORT=''
BENTOML_HOST=''
BENTOML_API_WORKERS=''
```

#### System information

`bentoml`: 1.1.7
`python`: 3.11.6
`platform`: macOS-13.5.2-arm64-arm-64bit
`uid_gid`: 502:20
<details><summary><code>pip_packages</code></summary>

<br>

```
aiohttp==3.8.6
aiosignal==1.3.1
anyio==4.0.0
appdirs==1.4.4
asgiref==3.7.2
async-timeout==4.0.3
attrs==23.1.0
bentoml==1.1.7
build==1.0.3
cattrs==23.1.2
certifi==2023.7.22
charset-normalizer==3.3.0
circus==0.18.0
click==8.1.7
click-option-group==0.5.6
cloudpickle==3.0.0
contextlib2==21.6.0
deepmerge==1.1.0
Deprecated==1.2.14
frozenlist==1.4.0
fs==2.4.16
h11==0.14.0
httpcore==0.18.0
httpx==0.25.0
idna==3.4
importlib-metadata==6.8.0
inflection==0.5.1
Jinja2==3.1.2
markdown-it-py==3.0.0
MarkupSafe==2.1.3
mdurl==0.1.2
multidict==6.0.4
numpy==1.26.1
opentelemetry-api==1.20.0
opentelemetry-instrumentation==0.41b0
opentelemetry-instrumentation-aiohttp-client==0.41b0
opentelemetry-instrumentation-asgi==0.41b0
opentelemetry-sdk==1.20.0
opentelemetry-semantic-conventions==0.41b0
opentelemetry-util-http==0.41b0
packaging==23.2
pathspec==0.11.2
pip-requirements-parser==32.0.1
pip-tools==7.3.0
prometheus-client==0.17.1
psutil==5.9.6
Pygments==2.16.1
pynvml==11.5.0
pyparsing==3.1.1
pyproject_hooks==1.0.0
python-dateutil==2.8.2
python-json-logger==2.0.7
python-multipart==0.0.6
PyYAML==6.0.1
pyzmq==25.1.1
requests==2.31.0
rich==13.6.0
schema==0.7.5
simple-di==0.1.5
six==1.16.0
sniffio==1.3.0
starlette==0.31.1
tornado==6.3.3
typing_extensions==4.8.0
urllib3==2.0.7
uvicorn==0.23.2
watchfiles==0.21.0
wrapt==1.15.0
yarl==1.9.2
zipp==3.17.0
```

</details>




Discussion:

Resolving patch:
diff --git a/src/bentoml/_internal/client/http.py b/src/bentoml/_internal/client/http.py
--- a/src/bentoml/_internal/client/http.py
+++ b/src/bentoml/_internal/client/http.py
@@ -154,7 +154,7 @@ async def _call(
 
         # TODO: Temporary workaround before moving everything to StreamingResponse
         if isinstance(fake_resp, starlette.responses.StreamingResponse):
-            req_body = fake_resp.body
+            req_body = "".join([s async for s in fake_resp.body_iterator])
         else:
             req_body = fake_resp.body
 
@@ -308,7 +308,7 @@ def _call(
             async def get_body():
                 return "".join([s async for s in fake_resp.body_iterator])
 
-            req_body = asyncio.run(get_body)
+            req_body = asyncio.run(get_body())
         else:
             req_body = fake_resp.body
 


Test patch: