OpenAI Realtime API Reference Client (beta)

repository·main·Indexed 21 days ago

https://github.com/openai/openai-realtime-api-beta

A reference client library for connecting to OpenAI's Realtime API, designed for prototyping conversational voice and text applications in Node.js and browser environments. It provides high-level abstractions via RealtimeClient, low-level WebSocket access through RealtimeAPI, and conversation state management with RealtimeConversation. Key features include streaming audio (pcm16, 24,000 Hz), tool implementation with callbacks, and session configuration for voice, instructions, and turn detection.

Tokens
7.5K
Snippets
29
Records
31
Agent score
77%

What's inside @openai/realtime-api-beta

  1. Understand the Realtime API primitives

    main

    The library provides three main primitives for interacting with the API:

    1. RealtimeClient: The high-level abstraction recommended for most users. It simplifies control flow and provides high-level utility events (e.g., conversation.updated).
    2. RealtimeAPI (accessed via client.realtime): A thin wrapper over the WebSocket. It is used for low-level connection, authentication, and sending raw items. It does not perform item validation.
    3. RealtimeConversation (accessed via client.conversation): A client-side cache of the current conversation. It includes event validation to ensure items are cached correctly.
  2. Manually define tools via updateSession

    main

    If you want to handle tool calls manually (e.g., to generate a schema without automatic execution), use client.updateSession(). When doing this, you must specify type: 'function' for each tool.

    Note: Tools added via .addTool() are persisted and appended to tools defined in updateSession(). However, every call to updateSession() overrides previous session configurations.

    client.updateSession({
      tools: [
        {
          type: 'function',
          name: 'get_weather',
          description: 'Retrieves weather.',
          parameters: { /* ... schema ... */ },
        },
      ],
    });
    
    // Handle the function call manually via events
    client.on('conversation.updated', ({ item, delta }) => {
      if (item.type === 'function_call') {
        if (delta.arguments) {
          // process arguments
        }
      }
    });
  3. Run tests and debug WebSocket events

    main

    Before running tests, ensure you have a .env file in your project root with your OPENAI_API_KEY configured:

    OPENAI_API_KEY=your_key_here

    Use the following commands to execute the test suite:

    • Standard test run: npm test
    • Debug mode: Use npm test -- --debug to enable debug logs, which will output all events sent to and received from the WebSocket.
    $ npm test
    
    # To run tests with debug logs
    $ npm test -- --debug
  4. Send messages and streaming audio

    main

    Sending Text Messages

    Use client.sendUserMessageContent() to send text or empty audio items to the server:

    client.sendUserMessageContent([{ type: 'input_text', text: `How are you?` }]);

    Sending Streaming Audio

    To stream audio, use client.appendInputAudio(). The default format is pcm16 at 24,000 Hz. If turn_detection is set to 'none', you must call client.createResponse() to trigger a model response after sending the audio chunks.

    // Append audio chunks (Int16Array or ArrayBuffer)
    client.appendInputAudio(new Int16Array(2400));
    
    // Trigger response if turn detection is disabled
    client.createResponse();
    client.sendUserMessageContent([{ type: 'input_text', text: `How are you?` }]);
    
    // Streaming audio example
    client.appendInputAudio(new Int16Array(2400));
    client.createResponse();
  5. Initialize the RealtimeClient

    main

    The RealtimeClient is the primary abstraction for interfacing with the Realtime API. You can configure session parameters like instructions, voice, turn_detection, and input_audio_transcription using client.updateSession() before or after connecting.

    Note: When using in a browser, you must set dangerouslyAllowAPIKeyInBrowser: true to acknowledge the security risk of exposing your API key.

    import { RealtimeClient } from '@openai/realtime-api-beta';
    
    const client = new RealtimeClient({ apiKey: process.env.OPENAI_API_KEY });
    
    // Configure session
    client.updateSession({ instructions: 'You are a great, upbeat friend.' });
    client.updateSession({ voice: 'alloy' });
    client.updateSession({
      turn_detection: { type: 'none' },
      input_audio_transcription: { model: 'whisper-1' },
    });
    
    // Connect to the API
    await client.connect();
  6. Listen for conversation events

    main

    The RealtimeClient extends RealtimeEventHandler, allowing you to listen for high-level conversation updates. Key events include:

    • conversation.updated: Fired when an item or delta is updated.
    • conversation.item.appended: Fired when a new item is added to the conversation.
    • conversation.item.completed: Fired when an item reaches a completed status.
    • conversation.interrupted: Fired when the user interrupts the model (via VAD).

    You can also use utility methods to wait for specific events:

    • waitForNextItem(): Returns a promise that resolves with the next conversation.item.appended event.
    • waitForNextCompletedItem(): Returns a promise that resolves with the next conversation.item.completed event.
    client.on('conversation.item.completed', ({ item }) => {
      console.log('New item completed:', item.id, item.role);
    });
    
    // Or using the async utility
    const { item } = await client.waitForNextCompletedItem();
  7. Manage conversation state with RealtimeConversation

    main

    The RealtimeConversation class is responsible for maintaining the conversation history and validating events for the Realtime API. It tracks both items (messages, function calls, etc.) and responses.

    Key capabilities include:

    • Event Processing: It maps incoming WebSocket events (like response.audio.delta or conversation.item.created) to internal state updates.
    • State Management: It maintains a lookup for items and responses, allowing you to retrieve specific parts of the conversation.
    • Formatted Data: It automatically aggregates deltas (text, audio, transcripts) into a formatted object on each item for easier consumption.
    • History Control: You can reset the entire conversation state using .clear().
    import { RealtimeConversation } from './lib/conversation.js';
    
    const conversation = new RealtimeConversation();
    // Use conversation.processEvent(event) to update state as events arrive
    // Use conversation.getItems() to retrieve the history
  8. Initialize and connect the RealtimeClient

    main

    The RealtimeClient is the primary entry point for interacting with the Realtime API. You can initialize it with connection settings and then call .connect() to establish a WebSocket connection. It is recommended to use .waitForSessionCreated() after connecting to ensure the server has initialized the session before sending commands.

    Constructor Settings:

    • url: The WebSocket URL.
    • apiKey: Your OpenAI API key.
    • dangerouslyAllowAPIKeyInBrowser: Boolean to allow API key usage in client-side environments.
    • debug: Boolean to enable debug logging.
    import { RealtimeClient } from './lib/client.js';
    
    const client = new RealtimeClient({
      url: 'YOUR_WS_URL',
      apiKey: 'YOUR_API_KEY',
      dangerouslyAllowAPIKeyInBrowser: true
    });
    
    await client.connect();
    await client.waitForSessionCreated();
  9. Add and use tools with callbacks

    main

    The easiest way to implement tools is using client.addTool(toolDefinition, callback). The callback is automatically executed with the tool's parameters, and the result is automatically sent back to the model.

    client.addTool(
      {
        name: 'get_weather',
        description: 'Retrieves weather for a location.',
        parameters: {
          type: 'object',
          properties: {
            lat: { type: 'number' },
            lng: { type: 'number' },
            location: { type: 'string' },
          },
          required: ['lat', 'lng', 'location'],
        },
      },
      async ({ lat, lng, location }) => {
        const result = await fetch(`https://api.example.com/weather?lat=${lat}&lng=${lng}`);
        return await result.json();
      },
    );
    client.addTool(
      {
        name: 'get_weather',
        description: 'Retrieves the weather for a given lat, lng coordinate pair. Specify a label for the location.',
        parameters: {
          type: 'object',
          properties: {
            lat: { type: 'number', description: 'Latitude' },
            lng: { type: 'number', description: 'Longitude' },
            location: { type: 'string', description: 'Name of the location' },
          },
          required: ['lat', 'lng', 'location'],
        },
      },
      async ({ lat, lng, location }) => {
        const result = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lng}&current=temperature_2m,wind_speed_10m`);
        const json = await result.json();
        return json;
      },
    );
  10. Handle server events via the realtime.event listener

    main

    To gain fine-grained control over your application, you can listen to the realtime.event event. This allows you to filter for events where the source is 'server', enabling you to respond specifically to payloads emitted by the Realtime API. The event object contains a time (ISO timestamp), a source (either 'client' or 'server'), and the event (the raw JSON payload).

    // all events, can use for logging, debugging, or manual event handling
    client.on('realtime.event', ({ time, source, event }) => {
      // time is an ISO timestamp
      // source is 'client' or 'server'
      // event is the raw event payload (json)
      if (source === 'server') {
        doSomething(event);
      }
    });