Skip to content

Add SSE keepalive comments to prevent connection idle timeouts#121

Closed
adambalogh wants to merge 1 commit into
mainfrom
claude/gpt2-image-gen-timeout-bajwcb
Closed

Add SSE keepalive comments to prevent connection idle timeouts#121
adambalogh wants to merge 1 commit into
mainfrom
claude/gpt2-image-gen-timeout-bajwcb

Conversation

@adambalogh

Copy link
Copy Markdown
Contributor

Summary

Image generation requests can block for 60-120 seconds with no bytes on the wire, causing intermediaries (OHTTP relay, load balancers, browser HTTP/2 sessions) to kill the connection as idle at ~60s. This change adds SSE keepalive comment streaming during long-running provider calls to keep the connection alive while maintaining full backward compatibility with existing response handling.

Key Changes

  • New run_with_sse_keepalive() generator function in image_generation.py:

    • Runs a blocking function in a worker thread via ThreadPoolExecutor
    • Yields SSE comment lines (: keepalive\n\n) at configurable intervals (default 10s) while the function executes
    • Returns the function's result value once complete
    • Propagates exceptions normally
    • Gracefully handles client disconnection (worker thread continues in background)
  • Updated image generation endpoints to use keepalive streaming:

    • create_image_generation_streaming_response() now wraps _run_image_generation() with run_with_sse_keepalive()
    • chat_controller.py wraps image output model invocation with keepalive streaming
  • Comprehensive test coverage in TestSseKeepalive:

    • Verifies keepalive comments are emitted during slow operations
    • Confirms fast operations skip keepalives entirely
    • Tests exception propagation
    • Validates streaming response format (keepalives before data frames, final frame unchanged)
    • Tests error frame delivery

Implementation Details

  • SSE keepalive comments are ignored by SSE parsers per spec, so they're transparent to clients
  • 10-second interval balances wire activity against overhead, accounting for OHTTP controller's one-chunk look-ahead delay
  • ThreadPoolExecutor with wait=False shutdown ensures background provider calls complete even if client disconnects
  • All existing response structure and signing behavior is preserved—keepalives are purely transport-level

https://claude.ai/code/session_01Uhw2GhoEnxG98rmbgpHhhP

…timeouts

gpt-image-2 generations routinely run 60-120s, during which the streaming
chat response carried zero bytes: the image is only emitted on the final SSE
frame. Intermediaries between the browser and the enclave (OHTTP relay, load
balancers, the browser's HTTP/2 session) kill connections idle that long,
surfacing client-side as ERR_HTTP2_PROTOCOL_ERROR at ~60s.

Run the blocking provider call in a worker thread and yield SSE comment
lines (': keepalive') every 10s while it runs. Comments are ignored by SSE
parsers per spec (the chat-app client already skips non-'data:' lines), and
the OHTTP controller seals each yielded chunk as it arrives, so keepalives
keep every hop's idle timers reset without changing the response contract.

Applied to both blocking image paths: the endpoint-based streaming responder
(gpt-image-2, grok-2-image, Seedream/Seedance, GLM-Image) and the Gemini
inline-image invoke in the chat streaming controller.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uhw2GhoEnxG98rmbgpHhhP
@adambalogh adambalogh closed this Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants