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:

FieldMeaning
protocol:chat_completions, :responses, or :unknown
termination:done, :incomplete, :error, :unexpected_eof, or :open
has_outputA data-bearing event or named event was observed
saw_terminal_eventA completion, incomplete, or [DONE] marker was seen
event_countComplete frames accepted within the event limit
malformed_event_countInvalid UTF-8 or over-limit frames
last_event_typeA short identifier, never provider prose
error_codeA 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.

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.