Skip to content
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

Merged
merged 16 commits into from Dec 13, 2023

Conversation

freddyaboulton
Copy link
Collaborator

@freddyaboulton freddyaboulton commented Dec 6, 2023

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

  1. 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

  2. You may need to run the linters: bash scripts/format_backend.sh and bash scripts/format_frontend.sh

@gradio-pr-bot
Copy link
Contributor

gradio-pr-bot commented Dec 6, 2023

🪼 branch checks and previews

Name Status URL
Spaces ready! Spaces preview
Website ready! Website preview
🦄 Changes detected! Details
📓 Notebooks not matching! Details

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"

@gradio-pr-bot
Copy link
Contributor

gradio-pr-bot commented Dec 6, 2023

🦄 change detected

This Pull Request includes changes to the following packages.

Package Version
@gradio/client patch
gradio patch
gradio_client patch
  • Maintainers can select this checkbox to manually select packages to update.

With the following changelog entry.

Python client properly handles hearbeat and log messages. Also handles responses longer than 65k

Maintainers or the PR author can modify the PR title to modify this entry.

Something isn't right?

  • Maintainers can change the version label to modify the version bump.
  • If the bot has failed to detect any changes, or if this pull request needs to update multiple packages to different versions or requires a more comprehensive changelog entry, maintainers can update the changelog file directly.

@freddyaboulton freddyaboulton added the v: patch A change that requires a patch release label Dec 6, 2023
@@ -87,6 +87,19 @@ class SpaceDuplicationError(Exception):
pass


class ServerMessage(str, Enum):
Copy link
Collaborator Author

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

@freddyaboulton freddyaboulton force-pushed the client-heartbeat-truncation-issue branch from 446ded5 to 89b0a83 Compare December 13, 2023 18:32
@freddyaboulton
Copy link
Collaborator Author

I believe test is a flake. Will look into it but this should be good to review.

@abidlabs
Copy link
Member

Awesome! Taking a look

succes, event_id = await blocks._queue.push(body, request, username)
if not succes:
status_code = (
status.HTTP_503_SERVICE_UNAVAILABLE
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

@abidlabs
Copy link
Member

Just to understand, what was the root cause of #6319?

@freddyaboulton
Copy link
Collaborator Author

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) aiter_text was yielding an incomplete message (can't be parsed to json) if it was very long

gradio/routes.py Outdated
Comment on lines 666 to 667
succes, event_id = await blocks._queue.push(body, request, username)
if not succes:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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:

Comment on lines +454 to +461
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
Copy link
Member

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?

Copy link
Collaborator Author

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!

Copy link
Collaborator Author

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]:
Copy link
Member

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):

image

Copy link
Collaborator Author

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?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not in fact :) but after rebuilding it, I still don't see any modal saying that the queue is full. Instead, on the 3rd tab, I just see a job that never enters the queue:

image

@abidlabs
Copy link
Member

abidlabs commented Dec 13, 2023

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):
Copy link
Collaborator Author

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():
Copy link
Collaborator Author

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}` };
Copy link
Collaborator Author

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
Copy link
Collaborator Author

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]:
Copy link
Collaborator Author

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?

Comment on lines +454 to +461
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
Copy link
Collaborator Author

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!

@abidlabs
Copy link
Member

Thanks @freddyaboulton I took another look and this is great!

Just wanted to note a few things:

Copy link
Member

@abidlabs abidlabs left a 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

@freddyaboulton
Copy link
Collaborator Author

Thanks @abidlabs !

@freddyaboulton freddyaboulton merged commit 34f9431 into main Dec 13, 2023
15 checks passed
@freddyaboulton freddyaboulton deleted the client-heartbeat-truncation-issue branch December 13, 2023 22:47
@pngwn pngwn mentioned this pull request Dec 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
v: patch A change that requires a patch release
Projects
None yet
3 participants