Skip to navigation

WebSocket

Maak realtime, interactieve spraakgesprekken met AI-agents

Deze documentatie is bedoeld voor ontwikkelaars die rechtstreeks integreren met de ElevenLabs WebSocket API. Voor meer gemak kun je overwegen de officiële SDK’s van ElevenLabs te gebruiken.

Met de ElevenAgents WebSocket API kun je realtime, interactieve spraakgesprekken met AI-agents voeren. Door een WebSocket-verbinding tot stand te brengen, kun je audio-invoer versturen en realtime audioreacties ontvangen, waarmee je levensechte gesprekservaringen creëert.

Endpoint: wss://api.elevenlabs.io/v1/convai/conversation?agent_id={agent_id}

Authenticatie

Agent ID gebruiken

Voor openbare agents kun je de agent_id rechtstreeks in de WebSocket-URL gebruiken, zonder aanvullende authenticatie:

wss://api.elevenlabs.io/v1/convai/conversation?agent_id=<your-agent-id>

Een ondertekende URL gebruiken

Voor privéagents of gesprekken waarvoor autorisatie nodig is, haal je een ondertekende URL op vanaf je server. Die communiceert veilig met de ElevenLabs API via je API-sleutel.

Voorbeeld met cURL

Request:

curl -X GET "https://api.elevenlabs.io/v1/convai/conversation/get-signed-url?agent_id=<your-agent-id>" \
-H "xi-api-key: <your-api-key>"

Response:

{
"signed_url": "wss://api.elevenlabs.io/v1/convai/conversation?agent_id=<your-agent-id>&token=<token>"
}
Maak je ElevenLabs API-sleutel nooit zichtbaar aan de clientzijde.

WebSocket-events

Events van client naar server

De volgende events kunnen van de client naar de server worden verstuurd:

Stuur contextuele informatie zonder onderbreking om de gespreksstatus bij te werken. Hiermee kun je aanvullende context geven zonder de lopende gespreksstroom te verstoren.

{
"type": "contextual_update",
"text": "User clicked on pricing page"
}

Gebruiksscenario’s:

  • Gebruikersstatus of -voorkeuren bijwerken
  • Omgevingscontext geven
  • Achtergrondinformatie toevoegen
  • Interacties met de gebruikersinterface bijhouden

Belangrijke punten:

  • Onderbreekt de huidige gespreksstroom niet
  • Updates worden als tool calls opgenomen in de gespreksgeschiedenis
  • Helpt context te behouden zonder de natuurlijke dialoog te onderbreken

Contextuele updates worden asynchroon verwerkt en vereisen geen directe reactie van de server.

Voorbeeldimplementatie in Next.js

Dit voorbeeld laat zien hoe je een client voor een op WebSocket gebaseerde gespreksagent implementeert in Next.js met de ElevenLabs WebSocket API.

Hoewel dit voorbeeld het pakket voice-stream gebruikt voor het verwerken van microfooninvoer, kun je je eigen oplossing implementeren voor het opnemen en encoderen van audio. De focus ligt hier op het demonstreren van de WebSocket-verbinding en eventafhandeling met de ElevenLabs API.

1

Vereiste afhankelijkheden installeren

Installeer eerst de benodigde pakketten:

npm install voice-stream

Het pakket voice-stream regelt microfoontoegang en audiostreaming, en encodeert de audio automatisch in base64-indeling zoals vereist door de ElevenLabs API.

Dit voorbeeld gebruikt Tailwind CSS voor de styling. Voeg Tailwind als volgt toe aan je Next.js-project:

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

Volg daarna de officiële installatiehandleiding voor Tailwind CSS in Next.js.

Je kunt de className-attributen ook vervangen door je eigen CSS-stijlen.

2

WebSocket-typen maken

Definieer de typen voor WebSocket-events:

app/types/websocket.ts
type BaseEvent = {
type: string;
};
type UserTranscriptEvent = BaseEvent & {
type: "user_transcript";
user_transcription_event: {
user_transcript: string;
};
};
type AgentResponseEvent = BaseEvent & {
type: "agent_response";
agent_response_event: {
agent_response: string;
};
};
type AgentResponseCorrectionEvent = BaseEvent & {
type: "agent_response_correction";
agent_response_correction_event: {
original_agent_response: string;
corrected_agent_response: string;
};
};
type AudioResponseEvent = BaseEvent & {
type: "audio";
audio_event: {
audio_base_64: string;
event_id: number;
alignment: {
chars: string[];
char_durations_ms: number[];
char_start_times_ms: number[];
};
};
};
type InterruptionEvent = BaseEvent & {
type: "interruption";
interruption_event: {
reason: string;
};
};
type PingEvent = BaseEvent & {
type: "ping";
ping_event: {
event_id: number;
ping_ms?: number;
};
};
type AgentChatResponsePartEvent = BaseEvent & {
type: "agent_chat_response_part";
text_response_part: {
type: "start" | "delta" | "stop";
text: string;
event_id: number;
response_id: string;
};
};
export type ElevenLabsWebSocketEvent =
| UserTranscriptEvent
| AgentResponseEvent
| AgentResponseCorrectionEvent
| AudioResponseEvent
| InterruptionEvent
| PingEvent
| AgentChatResponsePartEvent;
3

WebSocket-hook maken

Maak een aangepaste hook om de WebSocket-verbinding te beheren:

app/hooks/useAgentConversation.ts
'use client';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useVoiceStream } from 'voice-stream';
import type { ElevenLabsWebSocketEvent } from '../types/websocket';
const sendMessage = (websocket: WebSocket, request: object) => {
if (websocket.readyState !== WebSocket.OPEN) {
return;
}
websocket.send(JSON.stringify(request));
};
export const useAgentConversation = () => {
const websocketRef = useRef<WebSocket>(null);
const [isConnected, setIsConnected] = useState<boolean>(false);
const { startStreaming, stopStreaming } = useVoiceStream({
onAudioChunked: (audioData) => {
if (!websocketRef.current) return;
sendMessage(websocketRef.current, {
user_audio_chunk: audioData,
});
},
});
const startConversation = useCallback(async () => {
if (isConnected) return;
const websocket = new WebSocket("wss://api.elevenlabs.io/v1/convai/conversation");
websocket.onopen = async () => {
setIsConnected(true);
sendMessage(websocket, {
type: "conversation_initiation_client_data",
});
await startStreaming();
};
websocket.onmessage = async (event) => {
const data = JSON.parse(event.data) as ElevenLabsWebSocketEvent;
// Handle ping events to keep connection alive
if (data.type === "ping") {
setTimeout(() => {
sendMessage(websocket, {
type: "pong",
event_id: data.ping_event.event_id,
});
}, data.ping_event.ping_ms);
}
if (data.type === "user_transcript") {
const { user_transcription_event } = data;
console.log("User transcript", user_transcription_event.user_transcript);
}
if (data.type === "agent_response") {
const { agent_response_event } = data;
console.log("Agent response", agent_response_event.agent_response);
}
if (data.type === "agent_response_correction") {
const { agent_response_correction_event } = data;
console.log("Agent response correction", agent_response_correction_event.corrected_agent_response);
}
if (data.type === "interruption") {
// Handle interruption
}
if (data.type === "audio") {
const { audio_event } = data;
// Implement your own audio playback system here
// Note: You'll need to handle audio queuing to prevent overlapping
// as the WebSocket sends audio events in chunks
}
if (data.type === "agent_chat_response_part") {
const { text_response_part } = data;
const { type: partType, text, response_id } = text_response_part;
// Handle the agent's response text as it is generated. Enable
// agent_chat_response_part in the agent's client_events to receive
// this during voice conversations.
console.log("Chat response part:", partType, text, response_id);
}
};
websocketRef.current = websocket;
websocket.onclose = async () => {
websocketRef.current = null;
setIsConnected(false);
stopStreaming();
};
}, [startStreaming, isConnected, stopStreaming]);
const stopConversation = useCallback(async () => {
if (!websocketRef.current) return;
websocketRef.current.close();
}, []);
useEffect(() => {
return () => {
if (websocketRef.current) {
websocketRef.current.close();
}
};
}, []);
return {
startConversation,
stopConversation,
isConnected,
};
};
4

De gesprekscomponent maken

Maak een component die de WebSocket-hook gebruikt:

app/components/Conversation.tsx
'use client';
import { useCallback } from 'react';
import { useAgentConversation } from '../hooks/useAgentConversation';
export function Conversation() {
const { startConversation, stopConversation, isConnected } = useAgentConversation();
const handleStart = useCallback(async () => {
try {
await navigator.mediaDevices.getUserMedia({ audio: true });
await startConversation();
} catch (error) {
console.error('Failed to start conversation:', error);
}
}, [startConversation]);
return (
<div className="flex flex-col items-center gap-4">
<div className="flex gap-2">
<button
onClick={handleStart}
disabled={isConnected}
className="px-4 py-2 bg-blue-500 text-white rounded disabled:bg-gray-300"
>
Start Conversation
</button>
<button
onClick={stopConversation}
disabled={!isConnected}
className="px-4 py-2 bg-red-500 text-white rounded disabled:bg-gray-300"
>
Stop Conversation
</button>
</div>
<div className="flex flex-col items-center">
<p>Status: {isConnected ? 'Connected' : 'Disconnected'}</p>
</div>
</div>
);
}

Volgende stappen

  1. Audio afspelen: Implementeer je eigen systeem voor het afspelen van audio met Web Audio API of een library. Zorg dat je audio in een wachtrij verwerkt om overlap te voorkomen, omdat de WebSocket audio-events in chunks verstuurt.
  2. Foutafhandeling: Voeg logica voor opnieuw proberen en foutopstelmechanismen toe
  3. UI-feedback: Voeg visuele indicatoren toe voor stemactiviteit en verbindingsstatus

Latentiebeheer

Implementeer deze strategieën om vloeiende gesprekken te garanderen:

  • Adaptieve buffering: Pas audiobuffering aan op basis van netwerkomstandigheden.
  • Jitterbuffer: Implementeer een jitterbuffer om variaties in aankomsttijden van pakketten af te vlakken.
  • Ping-pongmonitoring: Gebruik ping- en pong-events om de round-trip time te meten en pas dienovereenkomstig aan.

Best practices voor beveiliging

  • Roteer API-sleutels regelmatig en gebruik omgevingsvariabelen om ze op te slaan.
  • Implementeer rate limiting om misbruik te voorkomen.
  • Leg duidelijk uit waarom je gebruikers om toegang tot de microfoon vraagt.
  • Geoptimaliseerde chunking: Pas de duur van audiochunks aan om latentie en efficiëntie in balans te brengen.

Aanvullende informatiebronnen