Repository navigation
Conversation
Tool calls are buffered until Kiro finishes the whole response, so a large tool input (e.g. Claude Code writing a big file) leaves the client without a single byte for minutes. Clients with an idle-stream watchdog treat that as a dead connection and abort: Claude Code shows "Waiting for API response ... check your network" and retries the whole turn. Add with_keepalive() in streaming_core and wrap both streaming paths: - Anthropic: `event: ping` (the API's own keepalive, skipped by SDKs) - OpenAI: an SSE comment line (`: keepalive`), ignored per the SSE spec Keepalives start only after the first real chunk, so errors raised before the stream starts are unchanged. A client disconnect cancels the pending read and closes the upstream stream. New STREAM_KEEPALIVE_INTERVAL setting (default 10s, 0 disables). Non-streaming responses are not affected.
|
Thanks for the PR! 🎉 Before merge, we need a one-time CLA confirmation. Full CLA text: Please reply once with: You need to write once, all further messages from me can be ignored. |
Author
I have read the CLA and I accept its terms |
This was referenced Oct 21, 2026
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.
Fixes #287
What and why
Tool calls are buffered until Kiro finishes the whole response, so while the model writes a large tool input (e.g. Claude Code writing a big file) the gateway sends the client no bytes at all, sometimes for minutes. Clients with an idle-stream watchdog treat that as a dead connection. Claude Code aborts with "Waiting for API response … check your network" and retries the entire turn. The real Anthropic API never goes silent like this: it sends
pingevents and streams tool input.This PR adds a keepalive that only fires during silence:
streaming_core.with_keepalive(source, keepalive_chunk, interval): passes chunks through unchanged and insertskeepalive_chunkafter everyintervalseconds without a chunk.stream_with_first_token_retry_anthropic): sendsevent: ping/{"type": "ping"}, the API's own keepalive, which SDKs skip.stream_with_first_token_retry): sends: keepalive, an SSE comment line that clients ignore per the SSE spec.STREAM_KEEPALIVE_INTERVAL, default10seconds;0disables it. Documented inconfig.pyand.env.example.Test coverage
15 new tests, full suite green (
1706 passed):TestWithKeepalive(core, 9 tests):interval=0disables keepalivesTestStreamingAnthropicKeepalive:pingevents sent while a tool call is buffered; none when disabled.TestStreamingOpenaiKeepalive: SSE comments sent while a tool call is buffered; none when disabled.TestStreamKeepaliveIntervalConfig: default value and env override.I checked that the Anthropic keepalive test fails on
main(assert len(pings) >= 2).Real-world verification
Running in my local Docker deployment:
message_start(3.6 s) and the tool block (43 s). After it,pingarrives at 13.6 s, 23.6 s and 33.6 s.CLAUDE_BYTE_STREAM_IDLE_TIMEOUT_MS=15000, asked toWritea ~400-line file: the ~54 s stream finished with no retry and wrote all 662 lines. Without the fix, that watchdog aborts the request.