Add SSE keepalives during long image generation calls#122
Draft
adambalogh wants to merge 1 commit into
Draft
Conversation
…on't kill the stream
gpt-image-2 generations regularly take 60-120s, and the streaming responder
ran the whole provider call before yielding a single byte. With nothing on
the wire, an idle-timeout proxy between the enclave and the browser resets
the HTTP/2 stream at ~60s, surfacing client-side as
ERR_HTTP2_PROTOCOL_ERROR.
Run the provider call on a worker thread and yield SSE comment frames
(": keepalive") every 10s while it executes. The frames are sealed as
ordinary chunked-OHTTP chunks, so bytes keep flowing across every hop
(enclave -> relay -> browser), and SSE parsers ignore comment lines so the
client contract is unchanged. Applied to both long-silent paths: the
endpoint-based image models (gpt-image-2, grok-2-image, Seedream, GLM-Image)
and the Gemini inline-image invoke in the chat controller.
Also give the provider image call its own 300s read timeout instead of the
shared chat client default, since image generation is far slower than chat.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017bwXiKz9YQsuhTyRcwEnKz
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
Image generation requests to providers take 60-120 seconds with no bytes on the wire. Idle-timeout proxies between the enclave and browser reset such silent streams at ~60s, surfacing as
ERR_HTTP2_PROTOCOL_ERRORon the client. This change emits SSE comment frames ("keepalives") at regular intervals while generation runs to keep bytes flowing and prevent proxy timeouts.Key Changes
New
run_with_sse_keepalives()generator (image_generation.py): Runs a callable on a daemon worker thread, yielding SSE comment frames (: keepalive\n\n) at configurable intervals while the work executes. Returns the callable's result or re-raises any exception. Designed for use withyield fromin streaming response generators.Updated image generation timeout (
image_generation.py): Added_IMAGE_GENERATION_TIMEOUT(300s read, 15s connect) to both/images/generationsPOST calls (form-based edits and JSON generations). Image generation is far slower than chat and needs its own generous timeout instead of the shared client default.Integrated keepalives into streaming response (
image_generation.py): Modifiedcreate_image_generation_streaming_response()to wrap_run_image_generation()withrun_with_sse_keepalives(), ensuring keepalive frames flow while the provider call executes. Final signed frame and error semantics unchanged.Extended keepalives to Gemini image generation (
chat_controller.py): Applied the same keepalive pattern to the Gemini image generation path ingenerate()wheremodel.invoke()is called for image-output models, protecting that long-running call as well.Comprehensive test coverage (
test/test_image_generation.py): AddedTestStreamingKeepalivesclass with four test cases:test_keepalives_flow_while_generation_runs: Verifies keepalives are emitted during slow generation and final signed frame is intacttest_generation_error_still_yields_error_frame: Confirms error frames are properly emitted even with keepalivestest_fast_generation_emits_no_keepalives: Ensures no unnecessary keepalives for quick operationstest_run_with_sse_keepalives_returns_value_and_raises: Unit tests the keepalive generator's return value and exception propagationImplementation Details
_KEEPALIVE_INTERVAL_SECONDS(default 10s, patched to 0.01s in tests for speed)data:lines)https://claude.ai/code/session_017bwXiKz9YQsuhTyRcwEnKz