Emit SSE heartbeats carrying queue and attempt state, so callers can tell a queued turn from a hung one #104
Labels
No labels
autonomy
async-consult
autonomy
epic
autonomy
headless
autonomy
live-collab
coherence-core
priority
P0
priority
P1
priority
P2
priority
P3
priority
P4
qa-fixture
role/ai
role/creator
role/design
role/director
role/engineer
role/exec
role/human
role/ops
role/qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
coilyco-flight-deck/agent-proxy#104
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
A caller waiting on
POST /v1/chat/completionscannot distinguish four states that look identical from outside: admitted and queued, retrying after a validation failure, generating, and hung. It gets silence, then either a response or a deadline.sirens-echocurrently resolves that ambiguity by giving up at ~179.5s and telling the user the turn timed out — including in cases where the proxy was still doing legitimate work.Evidence, 24h window
litellm_requestqueue.waitupstream.chatlitellm's p99 sits 54s above the caller's ~179.5s deadline, so the slowest turns are guaranteed to be cut regardless of what was asked. Measured from the Discord side (
coilyco-gaming/sirens-echo#160), four independent user-visible failures landed at 180.3s, 180.5s, 180.5s, 180.6s — a 26% failure rate over 19 exchanges, with the caller unable to report anything more useful than "timed out."Proposal
When the request is streaming, emit SSE comment lines during the pre-generation phase:
SSE comments — lines beginning with
:— are ignored by every spec-compliant SSE client, so existing consumers are unaffected without any change on their side. Consumers that want the signal parse them; consumers that don't, drop them. That property is why I would prefer comments over empty-delta chunks ({"choices":[{"delta":{}}]}), which some OpenAI-compatible clients mishandle.Cadence: on state transition, plus a keepalive every N seconds while a state persists.
Retry visibility is the part I would not skip
coilyco-gaming/sirens-echo#137documents threeresilience.attemptspans burning ~9s before a turn 502s. From outside, that is indistinguishable from one slow attempt. Surfacingattempt n of 3turns a silent retry storm into something a caller can log, display, and alert on — and it is the cheapest observability win in this proposal.Non-streaming requests
Heartbeats have nowhere to go on a non-streaming response without breaking the response contract. Options, in order of preference:
stream: true. Cleanest; no new surface.Retry-Afterplus a queue-depth header on the 503 path when admission would exceed a threshold — complements rather than replaces this.This does nothing on its own
A total request deadline ignores heartbeats by construction.
sirens-echocurrently wraps the call in a total context deadline (context deadline exceededat 179.456s), which fires on schedule no matter how many bytes arrive. The caller must move to an idle/read timeout with a larger overall ceiling for any of this to change behaviour.Filing that half separately against
sirens-echo. This issue is only the emitting side, and it should be understood as inert until the consuming side lands — worth stating plainly so nobody ships this and expects the timeouts to stop.Acceptance
Related
coilyco-gaming/sirens-echo#160— the 180s deadline, measured from both sidescoilyco-gaming/sirens-echo#162— 53 KB uncached prefix, and the p50/p99 data abovecoilyco-gaming/sirens-echo#137— the three-attempt retry path this would surfacecoilyco-gaming/sirens-echo#111— the Discord-side progress element that would consume this