OpenaiSseGuard
openai_sse_guard is a dependency-free Elixir/OTP observer for bounded,
OpenAI-compatible Server-Sent Events (SSE). It records enough evidence to
make a conservative replay decision when a streaming HTTP connection ends:
terminal marker, protocol hint, output evidence, event limits, and a bounded
error code.
It does not open sockets, make requests, retry, sleep, retain generated text, or decide whether a customer was charged. The caller owns transport, cancellation, idempotency, billing, and replay policy.
Install
Add the package to mix.exs:
def deps do
[
{:openai_sse_guard, "~> 0.1"}
]
end
Then run:
mix deps.get
The library has no runtime dependencies and works with ordinary OTP processes,
Finch, Req, Mint, Plug, or a custom HTTP client.
Observe a stream
For an enumerable of byte chunks, observe/2 keeps reduction and finalization
in one place:
chunks = [
"data: {\\\"choices\\\":[{\\\"delta\\\":{\\\"content\\\":\\\"hello\\\"}}]}\\n\\n",
"data: [DONE]\\n\\n"
]
snapshot = OpenaiSseGuard.observe(chunks)
snapshot.protocol
#=> :chat_completions
snapshot.termination
#=> :done
snapshot.has_output
#=> true
For a long-lived HTTP process, keep the observer in the caller's state and feed each binary chunk as it arrives:
observer = OpenaiSseGuard.new(max_events: 5_000)
observer = OpenaiSseGuard.push(observer, chunk)
snapshot = OpenaiSseGuard.finish(observer)
if snapshot.termination == :unexpected_eof and snapshot.has_output do
# The provider may have produced visible or billable output. Do not replay
# without an application-level idempotency decision.
:manual_decision
end
Chunks may split a UTF-8 code point and may end in the middle of an SSE line. The observer only validates text once a frame boundary is complete, so normal network chunking does not create false malformed events.
State contract
OpenaiSseGuard.Snapshot contains only bounded metadata:
| Field | Meaning |
|---|---|
protocol | :chat_completions, :responses, or :unknown |
termination | :done, :incomplete, :error, :unexpected_eof, or :open |
has_output | A data-bearing event or named event was observed |
saw_terminal_event | A completion, incomplete, or [DONE] marker was seen |
event_count | Complete frames accepted within the event limit |
malformed_event_count | Invalid UTF-8 or over-limit frames |
last_event_type | A short identifier, never provider prose |
error_code | A bounded code or type identifier when present |
The default limit is 64 KiB per frame and 10,000 frames per observer. Limits
are clamped to safe package-wide maxima so an untrusted response cannot request
an unbounded buffer. Unknown data-bearing events are conservative: they set
has_output rather than being treated as replay-safe.
Protocol and retry boundaries
Framing follows the WHATWG Server-Sent Events specification.
response.* event names infer the Responses protocol; a choices field or
[DONE] infers Chat Completions. response.completed,
response.incomplete, and [DONE] are terminal markers. A provider error
event ends in :error and exposes only a bounded identifier.
This package is an observer, not a retry policy. Combine its snapshot with your operation's idempotency key, HTTP status, rendered-byte state, billing semantics, and attempt/time budgets. For provider error categories, consult the OpenAI error-code guide; for server retry hints, see the MDN Retry-After reference.
The AI-ROUTER API gateway is one possible OpenAI-compatible endpoint context. The package is provider-neutral and is not affiliated with or endorsed by OpenAI.
Related implementations
The repository contains a replay-safety decision guide. For the same boundary in other ecosystems, compare the maintained JavaScript package on npm, Python package on PyPI, Ruby package on RubyGems, PHP package on Packagist, Rust package on crates.io, Deno package on JSR, and Dart package on pub.dev. These are contextual implementation links, not claims of endorsement or shared runtime code.
Development
mix deps.get
mix format --check-formatted
mix test
mix docs
See CONTRIBUTING.md, SECURITY.md, and the stream replay-safety guide before changing framing or terminal semantics.
MIT licensed. Maintained by AI-ROUTER contributors.