A Producer-Consumer EventStream Channel That Decouples LLM Streaming from Agent Loops
Streaming LLM responses into an agent loop usually means coupling the transport protocol directly to the consumption logic, which makes swapping providers or handling backpressure painful. A decoupled EventStream channel keeps the agent's control flow simple while adapter code handles the mess of vendor-specific SSE formats and error recovery.
When an LLM streams responses, the API adapter pushes data chunks at one speed while the agent loop consumes them at another. An EventStream channel bridges that gap with an in-memory buffer built on two queues: one for events and one for suspended consumers. The producer calls `push()` without knowing whether the consumer is ready; the consumer iterates with `for await...of` without caring whether data arrives immediately or later.
The implementation wraps an `AsyncIterable` class that validates events, detects terminal signals, and exposes a final-result promise for metadata like token counts. A single-iterator guard prevents multiple consumers from racing on the same stream. Error handling funnels API failures and network interruptions into a `fail()` method that rejects every waiting consumer and the final-result promise at once.
Adapter code from different LLM providers can normalize their varying SSE payloads into a uniform event format before pushing into the channel, so the agent sees a consistent stream regardless of which model sits behind it.
Decoupling the transport from the consumption loop with a producer-consumer channel is a pattern that generalizes beyond LLM streaming to any async data source an agent might poll.
The single-iterator guard is a practical constraint that avoids the complexity of fan-out semantics, but it also means the channel is deliberately not a general-purpose pub-sub bus.
Storing suspended `resolve` functions in a `waiters` array is essentially a hand-rolled async coordination primitive; it replaces what a library like RxJS would do with a Subject, at a fraction of the conceptual overhead.