Realtime Voice Agent

Open a real WebSocket to OpenAI's Realtime API for live voice and text

RealtimeAgent opens a real WebSocket to OpenAI’s Realtime API for bidirectional audio and text.

Diagram

Realtime Voice

🎤 Audio / Text

🤖 RealtimeAgent

🔌 WebSocket

🧠 OpenAI Realtime

🔊 Audio / Text back

Quick Start

  1. Connect and send text
    typescript
    import { RealtimeAgent } from 'praisonai';
    
    const agent = new RealtimeAgent({
      name: 'VoiceAssistant',
      llm: 'gpt-4o-realtime-preview',
      apiKey: process.env.OPENAI_API_KEY,
    });
    
    agent.onAudio((chunk) => playAudio(chunk));   // Uint8Array of decoded audio
    agent.onMessage((text) => console.log(text)); // text deltas
    
    await agent.connect();          // Real WebSocket — throws on auth/network failure
    await agent.sendText('Hello!'); // Real API frames — throws if not connected
  2. Stream microphone audio
    typescript
    import { RealtimeAgent } from 'praisonai';
    
    const agent = new RealtimeAgent({
      instructions: 'You are a helpful voice assistant.',
      realtime: { voice: 'nova' },
      apiKey: process.env.OPENAI_API_KEY,
    });
    
    agent.onAudio((chunk) => playAudio(chunk));
    
    await agent.connect();
    agent.sendAudio(microphoneBuffer);  // Uint8Array | ArrayBuffer of PCM16
    agent.commitAudio();                // ask the server to process the buffer

How It Works

Both audio and text turns travel over the same live socket.

Diagram
OpenAI RealtimeWebSocketRealtimeAgentUserOpenAI RealtimeWebSocketRealtimeAgentUserconnect()open handshake (Authorization, OpenAI-Beta)wss://api.openai.com/v1/realtime?model=…openresolve connect()session.update (voice, formats, VAD)sendText(“Hello”) / sendAudio(buffer)conversation.item.create + response.createresponse.text.delta / response.audio.deltaonMessage(text) / onAudio(chunk)
BehaviourWhat actually happens
connect()Resolves only on socket open; rejects on error, close, or timeout
isConnected()Reads the live socket readyState — flips to false on a server-initiated close
sendText() / sendAudio()Send real API frames; throw when not connected
EventsEvery event is parsed off the wire; unknown server types pass through verbatim

Events

Register handlers by event type, or use '*' to receive every server event. Convenience callbacks mirror the Python agent.

typescript
import { RealtimeAgent } from 'praisonai';

const agent = new RealtimeAgent({ apiKey: process.env.OPENAI_API_KEY });

// Typed event handlers
agent.on('response.audio.delta', (event) => playAudio(event.data));
agent.on('response.text.delta', (event) => process.stdout.write(event.data.delta));

// Receive everything, including unknown server types (passed through verbatim)
agent.on('*', (event) => console.log(event.type));

// Python-parity convenience callbacks
agent.onMessage((text) => console.log(text));   // decoded text deltas
agent.onAudio((chunk) => playAudio(chunk));      // decoded audio bytes
agent.onError((err) => console.error(err));

await agent.connect();

Known event types include session.created, session.updated, response.text.delta, response.audio.delta, response.done, and error. Any other type the server sends is emitted untouched.


Configuration Options

Pass realtime as a config object to tune voice and audio.

OptionTypeDefaultDescription
voice`’alloy’ \‘echo’ \‘fable’ \‘onyx’ \‘nova’ \‘shimmer’`'alloy'Response voice
inputFormat`’pcm16′ \‘g711_ulaw’ \‘g711_alaw’`'pcm16'Audio input format
outputFormat`’pcm16′ \‘g711_ulaw’ \‘g711_alaw’`'pcm16'Audio output format
sampleRatenumber24000Local playback hint (not sent to the API)
vadEnabledbooleantrueServer-side voice activity detection
vadThresholdnumber0.5VAD sensitivity
timeoutnumber300Session timeout (seconds)

Agent-level options: llm (default gpt-4o-realtime-preview), apiKey (falls back to OPENAI_API_KEY), url (endpoint override), headers, webSocket (inject a constructor), and connectTimeoutMs (default 30000).

typescript
import { RealtimeAgent } from 'praisonai';

const agent = new RealtimeAgent({
  name: 'VoiceAssistant',
  llm: 'gpt-4o-realtime-preview',
  apiKey: process.env.OPENAI_API_KEY,
  connectTimeoutMs: 10000,
  realtime: {
    voice: 'nova',
    inputFormat: 'pcm16',
    vadEnabled: true,
  },
});
RealtimeAgent API Reference

Full class documentation


Runtime Requirements

The WebSocket implementation is resolved at call time: an injected { webSocket } constructor first, then globalThis.WebSocket, then the optional ws package.

RuntimeHow it connects
Node ≥ 22Global WebSocket with { headers } for Authorization and OpenAI-Beta
Older NodeInstall ws (npm install ws) — the fallback path picks it up
BrowserNo handshake headers; falls back to OpenAI’s documented subprotocol credentials

If no implementation is available, connect() throws with the three ways to fix it (upgrade Node, install ws, or inject a constructor) — it never pretends to connect.


Best Practices

Always try/catch connect()

A bad key, a bad URL, or a timeout rejects. Handle the error instead of assuming a connection succeeded.

Send only after connect() resolves

sendText, sendAudio, commitAudio, and clearAudio throw when disconnected. Await connect() first, and re-check isConnected() after a possible server close.

Handle unknown event types

The server may send event types this SDK doesn’t name. Register '*' so you see them all rather than dropping frames.

Use an ephemeral key in browsers

Browsers cannot set handshake headers, so credentials travel as a subprotocol — visible in the page. Mint a short-lived key for that path.


Related