New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Python client properly handles hearbeat and log messages. Also handles responses longer than 65k #6693
Conversation
🪼 branch checks and previews
The demo notebooks don't match the run.py files. Please run this command from the root of the repo and then commit the changes: pip install nbformat && cd demo && python generate_notebooks.py Install Gradio from this PR pip install https://gradio-builds.s3.amazonaws.com/99250ab27898bfc2b6d47b6302048abfca9bf534/gradio-4.9.0-py3-none-any.whl Install Gradio Python Client from this PR pip install "gradio-client @ git+https://github.com/gradio-app/gradio@99250ab27898bfc2b6d47b6302048abfca9bf534#subdirectory=client/python" |
🦄 change detectedThis Pull Request includes changes to the following packages.
With the following changelog entry.
Maintainers or the PR author can modify the PR title to modify this entry.
|
@@ -87,6 +87,19 @@ class SpaceDuplicationError(Exception): | |||
pass | |||
|
|||
|
|||
class ServerMessage(str, Enum): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added this to make it easier to keep track of all the messages and avoid typos etc
85c3b97
to
58a060c
Compare
446ded5
to
89b0a83
Compare
I believe test is a flake. Will look into it but this should be good to review. |
Awesome! Taking a look |
succes, event_id = await blocks._queue.push(body, request, username) | ||
if not succes: | ||
status_code = ( | ||
status.HTTP_503_SERVICE_UNAVAILABLE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice
Just to understand, what was the root cause of #6319? |
I think there were two problems. 1) the "heartbeat" message was not being handled by the client 2) |
gradio/routes.py
Outdated
succes, event_id = await blocks._queue.push(body, request, username) | ||
if not succes: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
succes, event_id = await blocks._queue.push(body, request, username) | |
if not succes: | |
success, event_id = await blocks._queue.push(body, request, username) | |
if not success: |
async for line in response.aiter_lines(): | ||
line = line.rstrip("\n") | ||
if len(line) == 0: | ||
continue | ||
if line.startswith("data:"): | ||
resp = json.loads(line[5:]) | ||
if resp["msg"] in [ServerMessage.log, ServerMessage.heartbeat]: | ||
continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that these changes were made in stream_sse_v0
. Do you also want to make similar changes (particularly the heartbeat one) in stream_sse_v1
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops - yea I think it should be both!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually the stream_sse_v1 case is handled in Client.stream_messages
! Stream_sse_v1 doesn't directly connect to the SSE endpoint like v0 does. It just pulls messages off a message queue.
@@ -144,15 +145,16 @@ def _resolve_concurrency_limit(self, default_concurrency_limit): | |||
|
|||
async def push( | |||
self, body: PredictBody, request: fastapi.Request, username: str | None | |||
): | |||
) -> tuple[bool, str]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This might have broken max_size
.
I ran this demo and submitted jobs on 3 separate tabs:
import time
import gradio as gr
def greet(name):
time.sleep(20)
return "Hello " + name + "!"
demo = gr.Interface(fn=greet, inputs="text", outputs="text").queue(max_size=1)
if __name__ == "__main__":
demo.launch(show_api=False)
On the final tab, I see this error modal (instead of one telling me that the queue is full):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@abidlabs Did you build the frontend before running?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took a first pass on this PR, and I can confirm #6319 and #6601 have been fixed. I left some small comments above @freddyaboulton if you can take a look. Happy to take another pass on this PR soon! |
job2.result() | ||
job3.result() | ||
|
||
def test_json_parse_error(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the repro from #6494 and it's passing so I think we can close the issue
self.stream_open = False | ||
return | ||
elif message == "": | ||
async for line in response.aiter_lines(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this works too @aliabid94 ?
output = await response.json(); | ||
status = response.status; | ||
} catch (e) { | ||
output = { error: `Could not parse server response: ${e}` }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What was happening is that we were raising exceptions in the server, which means FastAPI returns the payload as a plain string. Since it can't be coverted to JSON, this errors out and the whole client/app hangs.
Also fixed in the server by raising HTTPException
succes, event_id = await blocks._queue.push(body, request, username) | ||
if not succes: | ||
status_code = ( | ||
status.HTTP_503_SERVICE_UNAVAILABLE |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using a different status code for QUEUE_FULL so that we can show the right error message in the app
@@ -144,15 +145,16 @@ def _resolve_concurrency_limit(self, default_concurrency_limit): | |||
|
|||
async def push( | |||
self, body: PredictBody, request: fastapi.Request, username: str | None | |||
): | |||
) -> tuple[bool, str]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@abidlabs Did you build the frontend before running?
async for line in response.aiter_lines(): | ||
line = line.rstrip("\n") | ||
if len(line) == 0: | ||
continue | ||
if line.startswith("data:"): | ||
resp = json.loads(line[5:]) | ||
if resp["msg"] in [ServerMessage.log, ServerMessage.heartbeat]: | ||
continue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whoops - yea I think it should be both!
Thanks @freddyaboulton I took another look and this is great! Just wanted to note a few things:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed for me now, thank you! Great PR @freddyaboulton
Let's do a patch release with this PR and #6525
Thanks @abidlabs ! |
Description
Closes: #6319
Closes: #6601
Closes: #6541
Closes: #6494
Closes: #6776
🎯 PRs Should Target Issues
Before your create a PR, please check to see if there is an existing issue for this change. If not, please create an issue before you create this PR, unless the fix is very small.
Not adhering to this guideline will result in the PR being closed.
Tests
PRs will only be merged if tests pass on CI. To run the tests locally, please set up your Gradio environment locally and run the tests:
bash scripts/run_all_tests.sh
You may need to run the linters:
bash scripts/format_backend.sh
andbash scripts/format_frontend.sh