Documentation
¶
Overview ¶
Package realtime implements a transparent, provider-agnostic websocket reverse proxy for OpenAI-compatible realtime (speech-to-speech) sessions. The provider event schema is the wire format, so frames are relayed verbatim; the gateway's job is credential injection, routing, and observation.
Index ¶
Constants ¶
const (
DefaultCallTTL = 6 * time.Hour
)
DefaultCallTTL bounds how long a WebRTC call stays routable (registry entries and sideband observers): a realtime call outliving it has long ended upstream. maxCalls bounds memory if entries are registered faster than they expire. Both are far above realistic session counts and durations.
const MaxFrameBytes = 16 << 20 // 16 MiB
MaxFrameBytes caps a single realtime message. OpenAI streams base64-encoded audio inside JSON text frames that routinely exceed coder/websocket's 32 KiB default read limit, so the limit is raised well above the largest expected event. Frames larger than this fail the session rather than truncating audio.
Variables ¶
This section is empty.
Functions ¶
func Observe ¶
Observe dials the target websocket and consumes frames until the upstream closes or ctx is canceled, invoking tap on each frame. It backs usage tracking for WebRTC calls: their events flow over the peer connection's data channel and never pass through the gateway, so the gateway attaches to the call's sideband channel and watches for usage events itself.
The relay's heartbeat cadence applies here too: a silently dead provider connection surfaces as a ping timeout instead of leaving the observer blocked in Read until the call TTL expires.
A dial failure is returned as *DialError; a clean close returns nil.
func Proxy ¶
Proxy upgrades the client request to a websocket, dials the upstream target, and relays frames bidirectionally until either side closes, running hooks on the frames as they pass.
The upstream is dialed first: if it fails, the client is not yet upgraded, so a *DialError is returned and the caller may write an HTTP error. Once the client is upgraded the connection is hijacked; Proxy then returns nil on a clean close or the terminal transport error (never a *DialError) for the caller to log.
Types ¶
type CallRegistry ¶
type CallRegistry struct {
// contains filtered or unexported fields
}
CallRegistry is an in-memory call_id -> route map. Like the rate limit counters, it is per-instance state: after a restart (or on another replica) clients fall back to passing model and provider explicitly.
func NewCallRegistry ¶
func NewCallRegistry() *CallRegistry
NewCallRegistry returns an empty registry with production defaults.
func (*CallRegistry) Lookup ¶
func (r *CallRegistry) Lookup(callID string) (CallRoute, bool)
Lookup returns the route registered for a call id, if it is still live.
func (*CallRegistry) Register ¶
func (r *CallRegistry) Register(callID string, route CallRoute)
Register remembers the route for a call id. Empty ids are ignored.
type CallRoute ¶
CallRoute remembers which model and provider a WebRTC call was created with, so a later sideband attach (GET /v1/realtime?call_id=...) can route to the same upstream without the client restating them.
type DialError ¶
type DialError struct{ Err error }
DialError wraps a failure to establish the upstream websocket. Proxy returns it only before the client connection is upgraded, so the caller can still write a normal HTTP error response.
type Hooks ¶ added in v0.1.82
type Hooks struct {
// OnClientFrame observes each client->upstream frame. It backs metering of
// what the client sends, such as input audio for sessions the provider bills
// by duration.
OnClientFrame func([]byte)
// MapClientFrame rewrites each client->upstream frame before it is
// forwarded, returning the frame unchanged when it has nothing to do. It
// carries session policy such as pinning the transcription model.
MapClientFrame func([]byte) []byte
// OnServerFrame observes each upstream->client frame. It backs usage
// tracking, which reads the provider's usage events.
OnServerFrame func([]byte)
}
Hooks observe and shape the frames a session relays. Every hook is optional and runs inline on the relay path, so each must be fast and must not block.