Repository navigation
End streamed answers that generate below an upstream's minimum speed - #384
Merged
Merged
Conversation
An upstream can now set `min_speed: { tokens_per_sec, window_secs }`
(1-1000 tokens/s, 5-120 s, window defaults to 20 and is always written
back). From the first token on, a streamed answer (SSE or Bedrock
eventstream) is judged once a second: when the average speed over the
last `window_secs` seconds is below the minimum, the answer ends with a
503 error frame in the client's format (Anthropic overloaded_error,
Responses response.failed/server_error, Gemini UNAVAILABLE, Chat error
object), the upstream connection is dropped, the upstream records one
failure and the conversation no longer sticks to it this turn.
- Speed uses the same estimate as the live speed (text, reasoning and
tool arguments, calibrated by pace). Silences of 5 s or more are taken
out of the window; a second with less than half a window left after
that is not judged.
- The request fails with gw.upstream.too_slow (upstream, rate, min,
secs), source `upstream`. RequestFailed gains `tokens_per_sec`, set
only for this failure and kept in the request row. Usage the upstream
had not reported yet is filled with the estimates.
- New validation code config.min_speed_range (upstream, field, value,
min, max); one-click repair drops min_speed when the rate is out of
range and resets window_secs to 20 when the window is.
- ProviderView/ProviderInput gain min_speed (MinSpeedView/MinSpeedInput);
config packs, sharing and sync carry it as part of the upstream.
- Converted streams can now end with a status-specific error
(StreamConverter::fail_with); other cut-offs are unchanged.
- Control API version 58. Request store schema unchanged.
Co-Authored-By: Claude Opus 5.5 <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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
What
A new per-upstream setting,
providers[].min_speed:From the first token on, a streamed answer (SSE or Bedrock eventstream) is judged once a second. When the average speed over the last
window_secsseconds is belowtokens_per_sec, the gateway:overloaded_error, Responsesresponse.failed/server_error, GeminiUNAVAILABLE, Chat{"error": …}) and drops the upstream connection;gw.upstream.too_slow(upstream,rate,min,secs), sourceupstream;failures_to_pause) and stops the conversation from sticking to it this turn;Speed uses the same estimate as the live speed. Silences of 5 seconds or more are taken out of the window (the idle timeout handles those), and a second with less than half a window left after that is not judged. Non-streamed answers and WebSocket turns are not judged. Unset means off; behaviour is unchanged.
Contract
ProviderView.min_speed?: MinSpeedView { tokens_per_sec, window_secs },ProviderInput.min_speed?: MinSpeedInput { tokens_per_sec, window_secs }.Event::RequestFailedgainstokens_per_sec?: number, set only for this failure.gw.upstream.too_slow,config.min_speed_range(upstream,field,value,min,max). One-click repair dropsmin_speedwhen the rate is out of range and resetswindow_secsto 20 when the window is.CONTROL_API_VERSION57 → 58. Request store schema unchanged.min_speedwith the upstream.StreamConverter::fail_with(status, …)lets converted streams end with a status-specific error; other cut-offs keep usingfail.Tests
tw-gateway/tests/min_speed.rs(fake upstreams, the "second" scaled to 50 ms): stays fast, short slowdown, long pause mid-stream, long wait before the first token, upstream without a minimum, non-streamed answer, slowdown for a window (frame, failure, speed, estimated usage, upstream dropped, health), Chat/Responses/Gemini clients, Bedrock eventstream.🤖 Generated with Claude Code