Skip to main content

Provider adapter overview

Every provider integration follows the same UI pattern:
const adapter = createProviderAdapter(/* provider connection */)

export function VoiceUI() {
  return <Orb adapter={adapter} theme="circle" aria-label="Start voice assistant" />
}
The orb subscribes to the adapter and calls its start() and stop() methods. The adapter converts provider events into idle, connecting, listening, thinking, speaking, and error signals, including normalized input and output volume when the provider exposes audio.

Setup at a glance

ProviderBrowser code you provideWhat orb-ui owns
VapiA configured Vapi client and assistant IDProvider event mapping and output metering
ElevenLabsThe Conversation class and agent/session credentialSession lifecycle, state mapping, playback, and both volume levels
LiveKitLiveKit runtime helpers plus a token source or endpointRoom lifecycle, agent discovery, playback, and both volume levels
PipecatA configured PipecatClient and its connect callbackRTVI state mapping, remote playback, and both volume levels
OpenAI RealtimeA callback that returns a fresh client secretBrowser WebRTC, microphone, playback, state, and both volume levels
Gemini LiveA callback that opens the official Google Live sessionMicrophone PCM streaming, turn detection, playback, state, and both volume levels
Provider authentication stays explicit. Standard OpenAI and Gemini API keys belong on your server; the browser should receive only short-lived provider credentials. LiveKit participant tokens should also be minted on a server. Pipecat Cloud can use its public agent-start key in the browser while private deployment credentials remain server-side.

The three connection shapes

Most adapters fit one of three small patterns.

Wrap an existing provider client

Vapi and Pipecat already have a browser client. Pass that client to orb-ui:
const adapter = createVapiAdapter(vapi, { assistantId })

const pipecatAdapter = createPipecatAdapter(pipecatClient, {
  connect: () => pipecatClient.connect({ webrtcUrl: '/api/offer' }),
})

Let orb-ui own the browser session

OpenAI Realtime uses native browser WebRTC, so the only required integration seam is a fresh client secret:
const adapter = createOpenAIRealtimeAdapter({
  getClientSecret: () =>
    fetch('/api/openai-realtime-token', { method: 'POST' })
      .then((response) => response.json())
      .then((data) => data.value),
})

Let the official SDK open the session

Gemini Live session creation stays in the application because @google/genai is an app-owned, optional dependency. orb-ui receives the connected session through one callback:
const adapter = createGeminiLiveAdapter({
  connect: async (callbacks) => {
    const token = await fetch('/api/gemini-live-token', { method: 'POST' }).then((response) =>
      response.json(),
    )
    const client = new GoogleGenAI({
      apiKey: token.value,
      httpOptions: { apiVersion: 'v1alpha' },
    })
    return client.live.connect({ model: token.model, config: token.config, callbacks })
  },
})
The adapter uses client-side activity detection by default, so the matching Gemini session config must disable automatic activity detection. The adapter sends explicit activity start/end markers using its local voice detector. Set activityDetection: 'server' only when the Gemini session keeps automatic activity detection enabled.

Advanced options stay optional

The adapter guides document alternate transports, existing-session modes, runtime overrides, and audio calibration hooks. Those options are escape hatches. Start with the first example in each guide and add advanced configuration only when your application already owns that part of the provider lifecycle.
Last modified on July 10, 2026