Fix progressive slowdown in text streaming across generations - #86
Open
auroter wants to merge 3 commits into
Open
Fix progressive slowdown in text streaming across generations#86auroter wants to merge 3 commits into
auroter wants to merge 3 commits into
Conversation
Consecutive generations in the Gradio web UI get progressively slower in the text streaming phase. The root cause is twofold: 1. Every text token yields the full chatbot history to Gradio for serialization (~200 yields per generation). As images accumulate in history, serialization gets more expensive per yield, so the consumer falls behind the producer. Fix: throttle yields to at most once per 50ms (~20-40 per generation instead of ~200). 2. PIL Image objects stored in history via gr.Image(pil, type="pil") get re-encoded to PNG by Gradio on every single yield — even for unchanged old messages. A 1024px PNG encode takes ~100ms, so with N prior images and 20 yields, that adds N*2 seconds of overhead per generation. Fix: save images to /tmp files immediately and store the file path in gr.Image instead. File path serialization is nearly free. Together these reduce per-generation overhead from O(tokens * images) to O(1), keeping text streaming speed consistent regardless of history size. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Close the file descriptor from mkstemp() to prevent fd exhaustion after many generations (EMFILE after ~1000 images) - Use gr.Image(filepath) for user-uploaded images too, not just generated ones — same re-encoding problem on every yield - Remove print(tmp_path) when no cache dir is set (undocumented behavioral change from original) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Consecutive generations in the Gradio web UI get progressively slower in the text streaming (thinking/recaption) phase. The model generates tokens at a constant rate (~24 tok/s), but each
yield historysends the full chatbot history to Gradio for serialization. Two compounding issues cause this:yield history, and serialization cost grows with history size (O(tokens × images))gr.Image(pil_image, type="pil")objects in history get re-encoded to PNG by Gradio on every yield — even for unchanged old messages (Gradio creates a new component instance each time inchatbot.py:557). This affects both generated images and user-uploaded images.A 1024px PNG encode takes ~100ms. With N prior images and ~200 yields, this adds N×20 seconds of overhead per generation, making text streaming visibly degrade from fast to ~1 word/second after just 2-3 generations.
Changes (single file:
app/run_chatbot.py)gr.Imageinstead of PIL objects. Generated images are saved to temp files first; user-uploaded images are already file paths from Gradio'sMultimodalTextbox, so droppingtype="pil"avoids an unnecessary PIL round-trip. File path serialization is nearly free vs. re-encoding.tempfile.mkstemp()to prevent fd exhaustion after many generations.Together these reduce per-generation overhead from O(tokens × images) to roughly O(1).
Measured results
Model speed is constant — the slowdown was entirely in the Gradio serialization layer.
Test plan
single_round+thinkmode--image-cache-dirstill saves images correctlyunlimitedcontext mode to verify full history still works🤖 Generated with Claude Code