> This is a page from the ElevenLabs documentation. For a complete page index, fetch https://elevenlabs.io/docs/llms.txt. For the full documentation in a single file, fetch https://elevenlabs.io/docs/llms-full.txt.

# WebSocket multi-contexte

> **Avancé**
>
> L’orchestration d’agents vocaux avec cette API WebSocket multi-contexte est une tâche complexe,
> recommandée aux développeurs expérimentés. Pour une solution davantage gérée, consultez notre
> [produit Agents Platform](/docs/fr/eleven-agents/overview), qui simplifie bon nombre de ces difficultés.

## Vue d’ensemble

La création d’agents vocaux réactifs exige de pouvoir gérer dynamiquement les flux audio, traiter les interruptions avec fluidité et préserver une voix naturelle tout au long des tours de conversation. Notre API WebSocket multi-contexte pour Text to Speech (TTS) est spécifiquement conçue pour ces scénarios.

Cette API étend notre [fonctionnalité WebSocket TTS standard](/docs/fr/eleven-api/guides/how-to/websockets/realtime-tts) en introduisant le concept de « contextes ». Chaque contexte fonctionne comme un flux indépendant de génération audio au sein d’une même connexion WebSocket. Vous pouvez ainsi :

* Gérer plusieurs lignes de parole simultanément, par exemple un agent qui parle tout en préparant une réponse à l’interruption d’un utilisateur.
* Gérer sans rupture les interventions des utilisateurs en fermant un contexte de parole existant et en en démarrant un nouveau.
* Préserver la cohérence prosodique des énoncés au sein d’un même contexte logique.
* Optimiser l’utilisation des ressources en fermant sélectivement les contextes qui ne sont plus nécessaires.

> **Warning**
>
> L’API WebSocket multi-contexte est optimisée pour les applications vocales et n’est pas destinée à
> générer simultanément plusieurs flux audio sans lien entre eux. Chaque connexion est donc limitée à
> 5 contextes simultanés.

Ce guide vous accompagne pour vous connecter au WebSocket multi-contexte, gérer les contextes et appliquer les bonnes pratiques de création d’agents vocaux engageants.

### Bonnes pratiques

> **Note**
>
> Ces bonnes pratiques sont essentielles pour créer des agents vocaux réactifs et efficaces avec
> notre API WebSocket multi-contexte.

#### Utiliser une seule connexion WebSocket

Établissez une connexion WebSocket pour chaque session d’utilisateur final. Cela réduit la
surcharge et la latence par rapport à la création de plusieurs connexions. Au sein de cette même
connexion, vous pouvez gérer plusieurs contextes pour différentes parties de la conversation.

#### Diffuser les réponses par fragments, générer des phrases

Pour générer des réponses longues, diffusez le texte en petits fragments et utilisez l’indicateur
`flush: true` à la fin des phrases complètes. Cela améliore la qualité de l’audio généré et la
réactivité.

#### Gérer les interruptions avec fluidité

Diffusez le texte dans un contexte jusqu’à ce qu’une interruption survienne, puis créez un
nouveau contexte et fermez celui qui existe. Cette approche garantit des transitions fluides
lorsque le fil de la conversation change.

#### Gérer le cycle de vie des contextes

Fermez rapidement les contextes inutilisés. Le serveur peut maintenir jusqu’à 5 contextes
simultanés par connexion, mais vous devez fermer les contextes lorsqu’ils ne sont plus nécessaires.

#### Éviter l’expiration des contextes

Par défaut, les contextes expirent après 20 secondes et sont fermés automatiquement. Le délai
d’inactivité est un paramètre au niveau du WebSocket qui s’applique à tous les contextes et peut
aller jusqu’à 180 secondes si nécessaire. Envoyez un message texte vide dans un contexte pour
réinitialiser le délai d’expiration.

### Gestion des interruptions

Lorsqu’un utilisateur interrompt votre agent, vous devez [fermer le contexte actuel](/docs/fr/api-reference/text-to-speech/v-1-text-to-speech-voice-id-multi-stream-input#send.Close-Context) et [en créer un nouveau](/docs/fr/api-reference/text-to-speech/v-1-text-to-speech-voice-id-multi-stream-input#send.Initialise-Context) :

```python
async def handle_interruption(websocket, old_context_id, new_context_id, new_response):
    # Close the existing context that was interrupted
    await websocket.send(json.dumps({
        "context_id": old_context_id,
        "close_context": True
    }))
    print(f"Closed interrupted context '{old_context_id}'")

    # Create a new context for the new response
    await send_text_in_context(websocket, new_response, new_context_id)
```

```javascript
function handleInterruption(websocket: WebSocket, oldContextId: string, newContextId: string, newResponse: string) {
  // Close the existing context that was interrupted
  websocket.send(JSON.stringify({
    context_id: oldContextId,
    close_context: true
  }));
  console.log(`Closed interrupted context '${oldContextId}'`);

  // Create a new context for the new response
  sendTextInContext(websocket, newResponse, newContextId);
}
```

### Maintenir un contexte actif

Les contextes expirent automatiquement après [20 secondes d’inactivité par défaut](/docs/fr/api-reference/text-to-speech/v-1-text-to-speech-voice-id-multi-stream-input#request.query.inactivity_timeout). Si vous devez maintenir un contexte actif sans générer de texte, par exemple pendant un délai de traitement, vous pouvez envoyer un message texte vide pour réinitialiser le délai d’expiration.

```python
async def keep_context_alive(websocket, context_id):
    await websocket.send(json.dumps({
        "context_id": context_id,
        "text": ""
    }))
```

```javascript
function handleInterruption(websocket: WebSocket, contextId: string) {
  // Close the existing context that was interrupted
  websocket.send(JSON.stringify({
    context_id: oldContextId,
    text: ""
  }));
}
```

### Fermer la connexion WebSocket

Lorsque votre conversation se termine, vous pouvez nettoyer tous les contextes en [fermant le socket](/docs/fr/api-reference/text-to-speech/v-1-text-to-speech-voice-id-multi-stream-input#send.Close-Socket) :

```python
async def end_conversation(websocket):
    # This will close all contexts and close the connection
    await websocket.send(json.dumps({
        "close_socket": True
    }))
    print("Ending conversation and closing WebSocket")`
```

```javascript
function endConversation(websocket: WebSocket) {
  // This will close all contexts and close the connection
  websocket.send(JSON.stringify({
    close_socket: true
  }));
  console.log("Ending conversation and closing WebSocket");
}
```

## Exemple complet d’agent conversationnel

### Prérequis

* Un compte ElevenLabs avec une clé API, découvrez comment [trouver votre clé API](/docs/fr/api-reference/authentication).
* Python ou Node.js, ou un autre environnement d’exécution JavaScript, installé sur votre machine.
* Une connaissance des communications WebSocket. Nous recommandons de lire notre [guide du streaming WebSocket standard](/docs/fr/eleven-api/guides/how-to/websockets/realtime-tts) pour en maîtriser les concepts fondamentaux.

### Configuration

Installez les dépendances nécessaires pour le langage de votre choix :

```python
pip install python-dotenv websockets
```

```javascript
npm install dotenv ws
for TypeScript, you might also want types:
npm install @types/dotenv @types/ws --save-dev
```

Créez un fichier .env dans le répertoire de votre projet afin d’y stocker votre clé API :

**`.env`**

```python .env
ELEVENLABS_API_KEY=your_elevenlabs_api_key_here
```

### Exemple d’agent vocal

> **Note**
>
> Ce code est fourni à titre d’exemple et n’est pas destiné à une utilisation en production

```python maxLines=100
import os
import json
import asyncio
import websockets
from dotenv import load_dotenv

load_dotenv()
ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY")
VOICE_ID = "your_voice_id"
MODEL_ID = "eleven_flash_v2_5"

WEBSOCKET_URI = f"wss://api.elevenlabs.io/v1/text-to-speech/{VOICE_ID}/multi-stream-input?model_id={MODEL_ID}"

async def send_text_in_context(websocket, text, context_id, voice_settings=None):
    """Send text to be synthesized in the specified context."""
    message = {
        "text": text,
        "context_id": context_id,
    }

    # Only include voice_settings for the first message in a context
    if voice_settings:
        message["voice_settings"] = voice_settings

    await websocket.send(json.dumps(message))

async def continue_context(websocket, text, context_id):
    """Add more text to an existing context."""
    await websocket.send(json.dumps({
        "text": text,
        "context_id": context_id
    }))

async def flush_context(websocket, context_id):
    """Force generation of any buffered audio in the context."""
    await websocket.send(json.dumps({
        "context_id": context_id,
        "flush": True
    }))

async def handle_interruption(websocket, old_context_id, new_context_id, new_response):
    """Handle user interruption by closing current context and starting a new one."""
    # Close the existing context that was interrupted
    await websocket.send(json.dumps({
        "context_id": old_context_id,
        "close_context": True
    }))

    # Create a new context for the new response
    await send_text_in_context(websocket, new_response, new_context_id)

async def end_conversation(websocket):
    """End the conversation and close the WebSocket connection."""
    await websocket.send(json.dumps({
        "close_socket": True
    }))

async def receive_messages(websocket):
    """Process incoming WebSocket messages."""
    context_audio = {}
    try:
        async for message in websocket:
            data = json.loads(message)
            context_id = data.get("contextId", "default")

            if data.get("audio"):
                print(f"Received audio for context '{context_id}'")

            if data.get("is_final"):
                print(f"Context '{context_id}' completed")
    except (websockets.exceptions.ConnectionClosed, asyncio.CancelledError):
        print("Message receiving stopped")

async def conversation_agent_demo():
    """Run a complete conversational agent demo."""
    # Connect with API key in headers
    async with websockets.connect(
        WEBSOCKET_URI,
        max_size=16 * 1024 * 1024,
        additional_headers={"xi-api-key": ELEVENLABS_API_KEY}
    ) as websocket:
        # Start receiving messages in background
        receive_task = asyncio.create_task(receive_messages(websocket))

        # Initial agent response
        await send_text_in_context(
            websocket,
            "Hello! I'm your virtual assistant. I can help you with a wide range of topics. What would you like to know about today?",
            "greeting"
        )

        # Wait a bit (simulating user listening)
        await asyncio.sleep(2)

        # Simulate user interruption
        print("USER INTERRUPTS: 'Can you tell me about the weather?'")

        # Handle the interruption by closing current context and starting new one
        await handle_interruption(
            websocket,
            "greeting",
            "weather_response",
            "I'd be happy to tell you about the weather. Currently in your area, it's 72 degrees and sunny with a slight chance of rain later this afternoon."
        )

        # Add more to the weather context
        await continue_context(
            websocket,
            " If you're planning to go outside, you might want to bring a light jacket just in case.",
            "weather_response"
        )

        # Flush at the end of this turn to ensure all audio is generated
        await flush_context(websocket, "weather_response")

        # Wait a bit (simulating user listening)
        await asyncio.sleep(3)

        # Simulate user asking another question
        print("USER: 'What about tomorrow?'")

        # Create a new context for this response
        await send_text_in_context(
            websocket,
            "Tomorrow's forecast shows temperatures around 75 degrees with partly cloudy skies. It should be a beautiful day overall!",
            "tomorrow_weather"
        )

        # Flush and close this context
        await flush_context(websocket, "tomorrow_weather")
        await websocket.send(json.dumps({
            "context_id": "tomorrow_weather",
            "close_context": True
        }))

        # End the conversation
        await asyncio.sleep(2)
        await end_conversation(websocket)

        # Cancel the receive task
        receive_task.cancel()
        try:
            await receive_task
        except asyncio.CancelledError:
            pass

if __name__ == "__main__":
    asyncio.run(conversation_agent_demo())

```

```javascript maxLines=100
// Import required modules
import dotenv from "dotenv";
import fs from "fs";
import WebSocket from "ws";

// Load environment variables
dotenv.config();
const ELEVENLABS_API_KEY = process.env.ELEVENLABS_API_KEY;
const VOICE_ID = "your_voice_id";
const MODEL_ID = "eleven_flash_v2_5";

const WEBSOCKET_URI = `wss://api.elevenlabs.io/v1/text-to-speech/${VOICE_ID}/multi-stream-input?model_id=${MODEL_ID}`;

// Function to send text in a specific context
function sendTextInContext(websocket, text, contextId, voiceSettings = null) {
  const message = {
    text: text,
    context_id: contextId,
  };

  // Only include voice_settings for the first message in a context
  if (voiceSettings) {
    message.voice_settings = voiceSettings;
  }

  websocket.send(JSON.stringify(message));
}

// Function to continue an existing context with more text
function continueContext(websocket, text, contextId) {
  websocket.send(
    JSON.stringify({
      text: text,
      context_id: contextId,
    })
  );
}

// Function to flush a context, forcing generation of buffered audio
function flushContext(websocket, contextId) {
  websocket.send(
    JSON.stringify({
      context_id: contextId,
      flush: true,
    })
  );
}

// Function to handle user interruption
function handleInterruption(websocket, oldContextId, newContextId, newResponse) {
  // Close the existing context that was interrupted
  websocket.send(
    JSON.stringify({
      context_id: oldContextId,
      close_context: true,
    })
  );

  // Create a new context for the new response
  sendTextInContext(websocket, newResponse, newContextId);
}

// Function to end the conversation and close the connection
function endConversation(websocket) {
  websocket.send(
    JSON.stringify({
      close_socket: true,
    })
  );
}

// Function to run the conversation agent demo
async function conversationAgentDemo() {
  // Connect to WebSocket with API key in headers
  const websocket = new WebSocket(WEBSOCKET_URI, {
    headers: {
      "xi-api-key": ELEVENLABS_API_KEY,
    },
    maxPayload: 16 * 1024 * 1024,
  });

  // Set up event handlers
  websocket.on("open", () => {
    // Initial agent response
    sendTextInContext(
      websocket,
      "Hello! I'm your virtual assistant. I can help you with a wide range of topics. What would you like to know about today?",
      "greeting"
    );

    // Simulate wait time (user listening)
    setTimeout(() => {
      // Simulate user interruption
      console.log("USER INTERRUPTS: 'Can you tell me about the weather?'");

      // Handle the interruption
      handleInterruption(
        websocket,
        "greeting",
        "weather_response",
        "I'd be happy to tell you about the weather. Currently in your area, it's 72 degrees and sunny with a slight chance of rain later this afternoon."
      );

      // Add more to the weather context
      setTimeout(() => {
        continueContext(
          websocket,
          " If you're planning to go outside, you might want to bring a light jacket just in case.",
          "weather_response"
        );

        // Flush at the end of this turn
        flushContext(websocket, "weather_response");

        // Simulate wait time (user listening)
        setTimeout(() => {
          // Simulate user asking another question
          console.log("USER: 'What about tomorrow?'");

          // Create a new context for this response
          sendTextInContext(
            websocket,
            "Tomorrow's forecast shows temperatures around 75 degrees with partly cloudy skies. It should be a beautiful day overall!",
            "tomorrow_weather"
          );

          // Flush and close this context
          flushContext(websocket, "tomorrow_weather");
          websocket.send(
            JSON.stringify({
              context_id: "tomorrow_weather",
              close_context: true,
            })
          );

          // End the conversation
          setTimeout(() => {
            endConversation(websocket);
          }, 2000);
        }, 3000);
      }, 500);
    }, 2000);
  });

  // Handle incoming messages
  websocket.on("message", (message) => {
    try {
      const data = JSON.parse(message);
      const contextId = data.contextId || "default";

      if (data.audio) {
        //do stuff
      }

      if (data.is_final) {
        console.log(`Context '${contextId}' completed`);
      }
    } catch (error) {
      console.error("Error parsing message:", error);
    }
  });

  // Handle WebSocket closure
  websocket.on("close", () => {
    console.log("WebSocket connection closed");
  });

  // Handle WebSocket errors
  websocket.on("error", (error) => {
    console.error("WebSocket error:", error);
  });
}

// Run the demo
conversationAgentDemo();
```

## Étapes suivantes

#### [ElevenAgents](/docs/fr/eleven-agents/quickstart)

Créez des agents vocaux prêts pour la production avec la plateforme ElevenAgents complète.

#### [Comprendre le streaming audio](/docs/fr/eleven-api/concepts/audio-streaming)

Découvrez le fonctionnement interne du streaming WebSocket et les facteurs qui influent sur la latence.