Skip to content

bodhi-realtime-agent / LLMTransport

Interface: LLMTransport ​

Defined in: src/types/transport.ts:597

Properties ​

audioFormat ​

readonly audioFormat: AudioFormatSpec

Defined in: src/types/transport.ts:634


capabilities ​

readonly capabilities: TransportCapabilities

Defined in: src/types/transport.ts:599

Static capabilities — read before connecting, used for orchestrator branching.


currentDialGen? ​

readonly optional currentDialGen?: number

Defined in: src/types/transport.ts:624

Dial counter: advances per dial and when the current dial fails.


currentTransportGeneration? ​

readonly optional currentTransportGeneration?: number

Defined in: src/types/transport.ts:626

Post-setup generation counter (advances only on a completed setup).


isConnected ​

readonly isConnected: boolean

Defined in: src/types/transport.ts:613


onAudioOutput? ​

optional onAudioOutput?: (base64Data) => void

Defined in: src/types/transport.ts:774

Parameters ​

base64Data ​

string

Returns ​

void


onCacheBust? ​

optional onCacheBust?: (reason) => void

Defined in: src/types/transport.ts:897

Fires immediately before the framework emits a session.update that changes instructions or tools — i.e. before a guaranteed full prompt-cache bust on the next response. Pure telemetry.

Parameters ​

reason ​

"instructions_changed" | "tools_changed"

Returns ​

void


onClose? ​

optional onClose?: (code?, reason?) => void

Defined in: src/types/transport.ts:798

Parameters ​

code? ​

number

reason? ​

string

Returns ​

void


onConnectionLifecycle? ​

optional onConnectionLifecycle?: (event) => void

Defined in: src/types/transport.ts:872

Connection-lifecycle facts (attempt / setup / close), correlated by connectAttemptId. An event's failure (and an attempt-closereason for a provider failure) may hold provider-supplied text: see ConnectFailure before logging it.

Parameters ​

event ​

ConnectionLifecycleEvent

Returns ​

void


onCredentialExpiry? ​

optional onCredentialExpiry?: () => void

Defined in: src/types/transport.ts:856

The credential of the current connection reached its expiry (GeminiLiveTransport with a credentialProvider). A consumer that can reconnect now should start that reconnect synchronously, as for a GoAway: right after this returns the transport closes the connection, unless a disconnect() already detached it, and reports the close through onClose and a generation-close lifecycle event.

Returns ​

void


onError? ​

optional onError?: (error) => void

Defined in: src/types/transport.ts:797

Parameters ​

error ​

LLMTransportError

Returns ​

void


onFirstAudioChunk? ​

optional onFirstAudioChunk?: () => void

Defined in: src/types/transport.ts:820

Fires once per model response, on the FIRST audio chunk emitted to the client. Distinct from onModelTurnStart (which fires as a response begins, including tool-only turns) — this marks the moment audio actually begins, the anchor for TTS-first-audio / stop-to-first-audio latency. Transports reset their per-response "audio started" flag when a new response begins.

Returns ​

void


onGenerationEnd? ​

optional onGenerationEnd?: (generationId, reason) => void

Defined in: src/types/transport.ts:813

That generation closed, and why. Fires exactly once per start.

Parameters ​

generationId ​

string

reason ​

GenerationEndReason

Returns ​

void


onGenerationStart? ​

optional onGenerationStart?: (generationId) => void

Defined in: src/types/transport.ts:811

A model generation opened. Paired with exactly one onGenerationEnd. A generation is one answer: it outlives the provider's turn boundary, because a tool call that finishes the answer can arrive after it.

Parameters ​

generationId ​

string

Returns ​

void


onGoAway? ​

optional onGoAway?: (timeLeft) => void

Defined in: src/types/transport.ts:849

Parameters ​

timeLeft ​

string

Returns ​

void


onGroundingMetadata? ​

optional onGroundingMetadata?: (metadata) => void

Defined in: src/types/transport.ts:858

Parameters ​

metadata ​

Record<string, unknown>

Returns ​

void


onInputTranscription? ​

optional onInputTranscription?: (text) => void

Defined in: src/types/transport.ts:794

User-speech transcription from the transport's own recognizer.

Parameters ​

text ​

string

An incremental delta, not the whole utterance restated. Gemini Live streams many small deltas per utterance (often mid-word); OpenAI and Qwen fire once per utterance from conversation.item.input_audio_transcription.completed, which is the degenerate one-delta case. Consumers must therefore accumulate across calls and reset at each utterance boundary — treating a call as the complete transcript collapses the utterance to its last fragment.

Returns ​

void


onInterrupted? ​

optional onInterrupted?: (serverTurnId?) => void

Defined in: src/types/transport.ts:784

Parameters ​

serverTurnId? ​

number

Monotonic id of the server turn being interrupted (see onTurnComplete). undefined for transports without server turns.

Returns ​

void


onModelTurnStart? ​

optional onModelTurnStart?: (generationId?) => void

Defined in: src/types/transport.ts:807

Fires when the model begins a response (audio, text, tool call). Used by VoiceSession to trigger STT provider commit.

Parameters ​

generationId? ​

string

The generation that opened, on transports that model generations (GeminiLiveTransport: once per generation, with the same id as onGenerationStart, so a tool call after turnComplete that finishes the same answer does not fire it again); undefined elsewhere.

Returns ​

void


onOutputTranscription? ​

optional onOutputTranscription?: (text) => void

Defined in: src/types/transport.ts:795

Parameters ​

text ​

string

Returns ​

void


onProviderEvidence? ​

optional onProviderEvidence?: (ev) => void

Defined in: src/types/transport.ts:903

Normalized provider-evidence delivery (speech-evidence design §1). Adapters of transports that declare capabilities.providerEvidenceKinds emit each normalized event here; the framework routes it into the session's evidence ledger. Never emit kinds you did not declare.

Parameters ​

ev ​

ProviderEvidenceEvent

Returns ​

void


onRealtimeLLMUsage? ​

optional onRealtimeLLMUsage?: (usage) => void

Defined in: src/types/transport.ts:861

Optional: fires when the provider reports token or duration usage for billing/observability.

Parameters ​

usage ​

RealtimeLLMUsageEvent

Returns ​

void


onReasoningDone? ​

optional onReasoningDone?: (info) => void

Defined in: src/types/transport.ts:886

Fires when the model's reasoning step completes for the current response, before any audio/text answer is emitted. durationMs is the wall-clock time the reasoning step took; reasoningTokens is the count if the provider exposes it (otherwise undefined).

Parameters ​

info ​
durationMs ​

number

reasoningTokens? ​

number

Returns ​

void


onReasoningStart? ​

optional onReasoningStart?: () => void

Defined in: src/types/transport.ts:880

Fires when the model begins emitting its hidden reasoning trace for the current response. Useful for latency observability.

Returns ​

void


onReasoningSummary? ​

optional onReasoningSummary?: (text) => void

Defined in: src/types/transport.ts:891

Fires with a streamed chunk of the optional reasoning summary text (when the model was configured to emit one). Surface this to ops telemetry only — never to end users.

Parameters ​

text ​

string

Returns ​

void


onResumptionUpdate? ​

optional onResumptionUpdate?: (handle, resumable) => void

Defined in: src/types/transport.ts:857

Parameters ​

handle ​

string

resumable ​

boolean

Returns ​

void


onSessionReady? ​

optional onSessionReady?: (sessionId) => void

Defined in: src/types/transport.ts:796

Parameters ​

sessionId ​

string

Returns ​

void


onSpeechStarted? ​

optional onSpeechStarted?: () => void

Defined in: src/types/transport.ts:837

Fires when the transport detects user speech via VAD. Used for TTS-level barge-in when the LLM is idle but TTS is still playing. OpenAI: wired to input_audio_buffer.speech_started. Gemini: may require custom VAD signal — needs empirical testing.

Returns ​

void


onTextDone? ​

optional onTextDone?: () => void

Defined in: src/types/transport.ts:831

Fires when the model's text response is complete for this turn. Signals that all text for the current response has been delivered. Ordering contract: fires after all onTextOutput, before onTurnComplete.

Returns ​

void


onTextOutput? ​

optional onTextOutput?: (text) => void

Defined in: src/types/transport.ts:826

Fires when the model produces text output (text-mode responses). Only active when responseModality is 'text' (i.e., external TTS in use).

Parameters ​

text ​

string

Incremental text chunk (may be partial word/sentence)

Returns ​

void


onToolCall? ​

optional onToolCall?: (calls) => void

Defined in: src/types/transport.ts:775

Parameters ​

calls ​

TransportToolCall[]

Returns ​

void


onToolCallCancel? ​

optional onToolCallCancel?: (ids) => void

Defined in: src/types/transport.ts:776

Parameters ​

ids ​

string[]

Returns ​

void


onTurnComplete? ​

optional onTurnComplete?: (serverTurnId?) => void

Defined in: src/types/transport.ts:781

Parameters ​

serverTurnId? ​

number

Monotonic id of the server turn that completed, when the transport models server turns explicitly (Gemini external-TTS path). Consumers dedupe finalization by this id. undefined for transports that do not track server turns.

Returns ​

void


onUsageMetadata? ​

optional onUsageMetadata?: (usage) => void

Defined in: src/types/transport.ts:865

Optional: the provider's raw usage payload, once per server message that carries it (including messages that also carry audio, a tool call or goAway). Declared by GeminiLiveTransport only.

Parameters ​

usage ​

TransportUsageMetadata

Returns ​

void


onUserSpeechStopped? ​

optional onUserSpeechStopped?: () => void

Defined in: src/types/transport.ts:846

Fires when the provider's server VAD detects end of user speech. OpenAI/Qwen: wired to input_audio_buffer.speech_stopped. Gemini Live has no equivalent wire event (callback never fires there — the framework falls back to client-VAD timing). The latency anchor derived from this is callback receipt time: biased late by the provider's silence window + network hop (a bounded lower-bound bias — see the observability design, investment-hai-metrics-observability.md §11).

Returns ​

void

Methods ​

abortIncumbent()? ​

optional abortIncumbent(): Promise<"closed" | "forced">

Defined in: src/types/transport.ts:622

Synchronously strand the incumbent connection — advance the dial generation so its callbacks, an in-flight reconnect() continuation and a later-resolving dial can no longer install or deliver anything, detach the session, and drop the resumption handle — then close it within a bounded time. Never rejects: 'closed' when the close completed, 'forced' on timeout or close error. Unlike disconnect(), never awaited unboundedly.

Returns ​

Promise<"closed" | "forced">


cancelResponse()? ​

optional cancelResponse(opts?): Promise<void>

Defined in: src/types/transport.ts:669

Cancel the in-flight response — wire-only actuation. May be called from any framework barge-in path that has decided to interrupt. Idempotent.

MUST NOT invoke onInterrupted or any other framework callback — the caller is responsible for finalizeTurn(interrupted). The implementation only sends wire messages and updates the transport's own state.

MUST NOT reject. Transient send failures are caught and logged internally; the returned promise still resolves so fire-and-forget callers cannot trigger unhandled-rejection warnings.

When truncate.audioEndMs is supplied and the transport tracks an active assistant audio item, it also sends a per-provider truncate (OpenAI: conversation.item.truncate). truncate: 'generated' asks the transport to compute audioEndMs from its own per-response generated-audio counter (e.g. OpenAI's audioOutputMs); transports without such a counter ignore the sentinel. Omitting truncate only skips truncation — cancelResponse still stops generation if a response is in flight.

When waitForDone is true, the returned promise resolves only after the trailing response.done(status:'cancelled') arrives from the provider (or a 2000 ms timeout, whichever comes first). Use this before sending a new response.create so cancel and create cannot race. Default false.

Parameters ​

opts? ​

CancelResponseOptions

Returns ​

Promise<void>


clearAudio() ​

clearAudio(): void

Defined in: src/types/transport.ts:638

Returns ​

void


clearInputAudio()? ​

optional clearInputAudio(): void

Defined in: src/types/transport.ts:676

Clear the provider's pending input-audio buffer (OpenAI Realtime: input_audio_buffer.clear). Optional — Gemini and other transports without a server-side append-then-commit input buffer omit this. Called by VoiceSession at grace-window arming to discard any pre-arming echo residue; safe to call when the buffer is empty.

Returns ​

void


clearResumption()? ​

optional clearResumption(): void

Defined in: src/types/transport.ts:630

Drop every copy of the session resumption handle the transport holds, so the next dial opens a fresh server session instead of resuming the current one. Does not touch the connection itself.

Returns ​

void


commitAudio() ​

commitAudio(): void

Defined in: src/types/transport.ts:637

Returns ​

void


connect() ​

connect(config?): Promise<void>

Defined in: src/types/transport.ts:602

Parameters ​

config? ​

LLMTransportConfig

Returns ​

Promise<void>


disconnect() ​

disconnect(): Promise<void>

Defined in: src/types/transport.ts:603

Returns ​

Promise<void>


elicitResponse()? ​

optional elicitResponse(): void

Defined in: src/types/transport.ts:754

Best-effort re-elicit of a model response from existing/restored context, without injecting new content. Used after a watchdog-driven reconnect to recover a turn the model silently dropped. Optional — transports that auto-generate (or cannot elicit without content) may omit it; callers fall back to triggerGeneration().

Implement this only when the provider accepts a genuinely content-less request. No in-tree transport does: Gemini rejects one server-side (1007, see the note in GeminiLiveTransport), and OpenAI/Qwen rely on the triggerGeneration() fallback. A rejection here is especially costly because it surfaces as an async socket close, not a throw, so the watchdog reads it as a transport failure and retries into the reconnect budget.

Returns ​

void


getActiveServerTurnId()? ​

optional getActiveServerTurnId(): number | undefined

Defined in: src/types/transport.ts:771

The transport's currently-active server-turn id, or undefined when no server turn is active (idle/closed) or the transport does not model server turns. Read synchronously from a model-output callback to bind the framework Turn to the server turn at birth. Active-only by contract: it must NOT return a stale id between turns.

Returns ​

number | undefined


getDiagnostics()? ​

optional getDiagnostics(): TransportDiagnostics

Defined in: src/types/transport.ts:875

Snapshot of the upstream send counters and the current transport generation. Absent on transports that do not count sends.

Returns ​

TransportDiagnostics


quiesce()? ​

optional quiesce(): Promise<void>

Defined in: src/types/transport.ts:687

Pause the transport without disconnecting:

  1. Cancel any in-flight model response (provider chooses how).
  2. Suppress onAudioOutput deltas until unquiesce() is called.
  3. Leave the WebSocket open and conversation state intact.

Idempotent. Used by VoiceSession to transition into transcription mode without tearing the transport down. When omitted, VoiceSession falls back to its framework-layer audio-output guard.

Returns ​

Promise<void>


reconnect() ​

reconnect(state?): Promise<void>

Defined in: src/types/transport.ts:604

Parameters ​

state? ​

ReconnectState

Returns ​

Promise<void>


replayHistory()? ​

optional replayHistory(items): void

Defined in: src/types/transport.ts:612

Replay prior turns into the live upstream session as quiet context (no response requested). Called AFTER connect() and BEFORE the next send, for a session that has no server-side conversation: the host's initialHistory on the first connection that sets up (resume), and the whole conversation on a host-recovery replacement connection. Not idempotent: the caller sends a given conversation to a given connection once. Optional — a transport that cannot prefill omits it (VoiceSession calls it as transport.replayHistory?.(...)). Distinct from reconnect recovery, which transports drive internally from ReconnectState.

Parameters ​

items ​

readonly ReplayItem[]

Returns ​

void


replayUserTurn()? ​

optional replayUserTurn(turn): boolean

Defined in: src/types/transport.ts:763

Re-send a retained user utterance as a complete user turn, eliciting a response. Used by the response watchdog when a stalled turn left pending user input the provider never answered — first in-place on the existing connection, then once more after a reconnect if the model stays silent. Returns true if the transport dispatched it; false/absent → caller falls back. Optional. Gemini sends clientContent inline audio with an explicit turnComplete (no server-VAD dependence; Phase 0 validated).

Parameters ​

turn ​

RetainedUserTurn

Returns ​

boolean


sendAudio() ​

sendAudio(base64Data): void

Defined in: src/types/transport.ts:633

Parameters ​

base64Data ​

string

Returns ​

void


sendContent() ​

sendContent(turns, turnComplete?): void

Defined in: src/types/transport.ts:708

Parameters ​

turns ​

ContentTurn[]

turnComplete? ​

boolean

Returns ​

void


sendFile() ​

sendFile(base64Data, mimeType): void

Defined in: src/types/transport.ts:718

Parameters ​

base64Data ​

string

mimeType ​

string

Returns ​

void


sendInlineFile()? ​

optional sendInlineFile(base64Data, mimeType): void

Defined in: src/types/transport.ts:723

Send a file of any MIME type inline in the conversation content, without completing the turn (documents that sendFile's realtime media path cannot carry). Optional: without it, such files go to sendFile.

Parameters ​

base64Data ​

string

mimeType ​

string

Returns ​

void


sendLiveText()? ​

optional sendLiveText(turns): boolean

Defined in: src/types/transport.ts:715

Send live, generation-triggering text through the provider's realtime input. Returns true when the send was dispatched or accepted into a send buffer (flushed best effort) and false when it was not (no text, not connected, or a send that failed). Never throws. Optional: a transport without it takes live text through sendContent(turns, true).

Parameters ​

turns ​

ContentTurn[]

Returns ​

boolean


sendToolResult() ​

sendToolResult(result): void

Defined in: src/types/transport.ts:726

Parameters ​

result ​

TransportToolResult

Returns ​

void


transferSession() ​

transferSession(config, state?): Promise<void>

Defined in: src/types/transport.ts:705

Parameters ​

config ​

SessionUpdate

state? ​

ReconnectState

Returns ​

Promise<void>


triggerGeneration() ​

triggerGeneration(instructions?, overrides?): void

Defined in: src/types/transport.ts:737

Trigger a model response.

Parameters ​

instructions? ​

string

Optional one-off instruction (passed as response.create.response.instructions on OpenAI — does not mutate the session prefix, so it is cache-safe for subsequent turns).

overrides? ​

Optional per-response overrides. reasoning.effort bumps the dial for this Response only (resets to session default on the next turn). Only honored by transports advertising capabilities.reasoningEffort.

reasoning? ​

{ effort: ReasoningEffort; }

reasoning.effort ​

ReasoningEffort

Returns ​

void


unquiesce()? ​

optional unquiesce(): Promise<void>

Defined in: src/types/transport.ts:691

Resume normal operation after quiesce(). After this resolves, onAudioOutput fires again on the next response. Idempotent.

Returns ​

Promise<void>


updateSession() ​

updateSession(config): void | Promise<void>

Defined in: src/types/transport.ts:702

Apply a session update. Pre-connect: state-only mutation; coalesces with prior pre-connect calls and resolves immediately. The merged config is sent in the single session.update issued at connect time. Post-connect: serialized via the transport's internal FIFO queue; each call produces one wire session.update and awaits its ack. A custom transport may instead apply the update synchronously and return void.

Parameters ​

config ​

SessionUpdate

Returns ​

void | Promise<void>

Built with VitePress