WebSocket

Créez des conversations vocales interactives en temps réel avec des agents IA

Cette documentation s’adresse aux développeurs intégrant directement l’API WebSocket d’ElevenLabs. Pour plus de simplicité, envisagez d’utiliser les SDK officiels fournis par ElevenLabs.

L’API WebSocket d’ElevenAgents permet de créer des conversations vocales interactives en temps réel avec des agents IA. En établissant une connexion WebSocket, vous pouvez envoyer des entrées audio et recevoir des réponses audio en temps réel, pour créer des expériences conversationnelles réalistes.

Point de terminaison : wss://api.elevenlabs.io/v1/convai/conversation?agent_id={agent_id}

Authentification

Utiliser l’ID de l’agent

Pour les agents publics, vous pouvez utiliser directement agent_id dans l’URL WebSocket, sans authentification supplémentaire :

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

Utiliser une URL signée

Pour les agents privés ou les conversations nécessitant une autorisation, obtenez une URL signée depuis votre serveur, qui communique de manière sécurisée avec l’API ElevenLabs à l’aide de votre clé API.

Exemple avec cURL

Requête :

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>"

Réponse :

{
"signed_url": "wss://api.elevenlabs.io/v1/convai/conversation?agent_id=<your-agent-id>&token=<token>"
}
N’exposez jamais votre clé API ElevenLabs côté client.

Événements WebSocket

Événements du client vers le serveur

Les événements suivants peuvent être envoyés du client vers le serveur :

Envoyez des informations contextuelles sans interrompre la conversation afin de mettre à jour son état. Vous pouvez ainsi fournir un contexte supplémentaire sans perturber le déroulement de la conversation en cours.

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

Cas d’utilisation :

  • Mise à jour du statut ou des préférences de l’utilisateur
  • Fourniture d’un contexte environnemental
  • Ajout d’informations générales
  • Suivi des interactions avec l’interface utilisateur

Points clés :

  • N’interrompt pas le déroulement de la conversation en cours
  • Les mises à jour sont intégrées sous forme d’appels d’outils dans l’historique de la conversation
  • Aide à préserver le contexte sans rompre le dialogue naturel

Les mises à jour contextuelles sont traitées de manière asynchrone et ne nécessitent pas de réponse directe du serveur.

Exemple d’implémentation Next.js

Cet exemple montre comment implémenter un client d’agent conversationnel basé sur WebSocket dans Next.js à l’aide de l’API WebSocket d’ElevenLabs.

Bien que cet exemple utilise le package voice-stream pour gérer l’entrée du microphone, vous pouvez implémenter votre propre solution pour capturer et encoder l’audio. L’objectif est ici de présenter la connexion WebSocket et la gestion des événements avec l’API ElevenLabs.

1

Installer les dépendances requises

Commencez par installer les packages nécessaires :

npm install voice-stream

Le package voice-stream gère l’accès au microphone et le streaming audio, en encodant automatiquement l’audio au format base64 requis par l’API ElevenLabs.

Cet exemple utilise Tailwind CSS pour la mise en forme. Pour ajouter Tailwind à votre projet Next.js :

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

Suivez ensuite le guide officiel de configuration de Tailwind CSS pour Next.js.

Vous pouvez également remplacer les attributs className par vos propres styles CSS.

2

Créer les types WebSocket

Définissez les types des événements WebSocket :

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

Créer le hook WebSocket

Créez un hook personnalisé pour gérer la connexion WebSocket :

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

Créer le composant de conversation

Créez un composant qui utilise le hook WebSocket :

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>
);
}

Prochaines étapes

  1. Lecture audio : implémentez votre propre système de lecture audio à l’aide de Web Audio API ou d’une bibliothèque. Pensez à gérer la mise en file d’attente audio pour éviter les chevauchements, car le WebSocket envoie les événements audio par segments.
  2. Gestion des erreurs : ajoutez une logique de nouvelle tentative et des mécanismes de récupération après erreur.
  3. Retours de l’interface : ajoutez des indicateurs visuels d’activité vocale et de statut de connexion.

Gestion de la latence

Pour garantir des conversations fluides, mettez en œuvre les stratégies suivantes :

  • Mise en mémoire tampon adaptative : ajustez la mise en mémoire tampon audio selon les conditions réseau.
  • Tampon de gigue : implémentez un tampon de gigue afin de lisser les variations des temps d’arrivée des paquets.
  • Surveillance ping-pong : utilisez les événements ping et pong pour mesurer le temps aller-retour et ajuster les paramètres en conséquence.

Bonnes pratiques de sécurité

  • Renouvelez régulièrement les clés API et utilisez des variables d’environnement pour les stocker.
  • Implémentez une limitation du débit pour prévenir les abus.
  • Expliquez clairement votre intention lorsque vous demandez aux utilisateurs l’accès au microphone.
  • Segmentation optimisée : ajustez la durée des segments audio pour équilibrer latence et efficacité.

Ressources supplémentaires