> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gradium.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Multiplexage

> Envoyer plusieurs requêtes indépendantes sur une seule connexion WebSocket

Le multiplexage vous permet d'envoyer plusieurs requêtes indépendantes
sur une seule connexion WebSocket. Chaque requête logique reçoit un
`client_req_id` ; le serveur recopie cette valeur dans les réponses afin
que votre client puisse renvoyer l'audio, le texte, la VAD et les
événements de fin vers le bon appelant.

C'est utile si vous voulez éviter d'ouvrir un nouveau WebSocket pour
chaque énoncé court, ou si un serveur doit traiter en parallèle de
nombreuses requêtes TTS à faible latence.

## Fonctionnement du multiplexage

| Mode | Utilisation | Comportement |
| - | - | - |
| Usage unique | Envoyez le setup sans `close_ws_on_eos: false` | Le socket se ferme après `end_of_stream`. |
| Réutilisable séquentiel | Définissez `close_ws_on_eos: false`, sans `client_req_id` | Le socket reste ouvert et traite une requête à la fois. |
| Multiplexé concurrent | Définissez `close_ws_on_eos: false` et incluez `client_req_id` dans chaque message | Plusieurs requêtes actives partagent le socket. |

Pour exécuter des requêtes simultanées sur le même socket :

1. Définissez `close_ws_on_eos: false` dans chaque `setup`.
2. Générez un `client_req_id` unique par requête logique.
3. Incluez ce même `client_req_id` dans le `setup`, les messages
   d'entrée et le `end_of_stream`.
4. Routez les réponses selon leur `client_req_id`.

Si vous réutilisez un `client_req_id` alors que la requête précédente
est encore active, le serveur renvoie une erreur de protocole.

## Exemple TTS

```python theme={null}
import asyncio
import collections
import gradium


async def synthesize_many():
    setup = {
        "voice_id": "RI2y7oBdsQJmkgFF",
        "output_format": "wav",
        "close_ws_on_eos": False,  # Enable multiplexing.
    }
    texts = [
        "First request. Second part, last one.",
        "Second request. Second part, last one again.",
    ]

    client = gradium.client.GradiumClient(api_key="your-api-key")
    async with client.tts_realtime(send_setup_on_start=False) as stream:

        async def send_loop():
            for idx, text in enumerate(texts):
                stamp = {"client_req_id": f"req-{idx:02d}"}
                await stream.send_setup(setup | stamp)
                await stream.send_text(text, **stamp)
                await stream.send_eos(**stamp)

        async def recv_loop():
            audio = collections.defaultdict(list)
            num_eos = 0
            async for msg in stream:
                if msg["type"] == "audio":
                    audio[msg.get("client_req_id")].append(msg["audio"])
                elif msg["type"] == "end_of_stream":
                    num_eos += 1
                    if num_eos == len(texts):
                        break
            return audio

        _, audio = await asyncio.gather(send_loop(), recv_loop())
        return {k: b"".join(v) for k, v in audio.items()}


audio_by_request = asyncio.run(synthesize_many())
```

Avec le SDK Python, chaque message `audio` contient des octets déjà
décodés. Si vous communiquez directement avec le WebSocket, `audio` est
encodé en base64.

## Échanges sur le fil

```json Client theme={null}
{"type":"setup","voice_id":"RI2y7oBdsQJmkgFF","output_format":"pcm","close_ws_on_eos":false,"client_req_id":"req-a"}
{"type":"text","text":"First request.","client_req_id":"req-a"}
{"type":"end_of_stream","client_req_id":"req-a"}
{"type":"setup","voice_id":"RI2y7oBdsQJmkgFF","output_format":"pcm","close_ws_on_eos":false,"client_req_id":"req-b"}
{"type":"text","text":"Second request.","client_req_id":"req-b"}
{"type":"end_of_stream","client_req_id":"req-b"}
```

```json Server theme={null}
{"type":"ready","request_id":"...","client_req_id":"req-a","sample_rate":48000,"frame_size":3840}
{"type":"ready","request_id":"...","client_req_id":"req-b","sample_rate":48000,"frame_size":3840}
{"type":"audio","client_req_id":"req-b","audio":"...","start_s":0.0,"stop_s":0.08}
{"type":"audio","client_req_id":"req-a","audio":"...","start_s":0.0,"stop_s":0.08}
{"type":"end_of_stream","client_req_id":"req-b"}
{"type":"end_of_stream","client_req_id":"req-a"}
```

Les réponses peuvent arriver entrelacées. Ne partez pas du principe que
la première requête se termine avant que la seconde commence à renvoyer
de l'audio.

## Remarques sur la transcription

Le même mécanisme `client_req_id` existe sur les WebSockets de
transcription. Ne l'utilisez que si chaque source audio constitue un
flux logique distinct et que votre client sait router chaque fragment
audio, chaque flush et chaque `end_of_stream` vers la bonne requête.

```json theme={null}
{"type":"setup","model_name":"default","input_format":"pcm","json_config":{"language":"en"},"close_ws_on_eos":false,"client_req_id":"caller-1"}
{"type":"audio","audio":"base64_audio","client_req_id":"caller-1"}
{"type":"flush","flush_id":1,"client_req_id":"caller-1"}
{"type":"end_of_stream","client_req_id":"caller-1"}
```

Pour la plupart des applications de microphone en direct ou de
téléphonie, un WebSocket de transcription par flux de locuteur en direct
est plus simple à appréhender. Ne multiplexez la transcription que si le
coût des connexions compte et que vous disposez de tests de routage
rigoureux.

## Fermer un socket réutilisable

Une fois toutes les requêtes logiques terminées, envoyez un
`end_of_stream` sans `client_req_id` pour fermer le socket réutilisable :

```json theme={null}
{"type":"end_of_stream"}
```

Si des requêtes sont encore en cours, le serveur ferme le socket une
fois qu'elles sont terminées. Si aucune requête n'est active, il le
ferme immédiatement.

## Gestion des erreurs

Les erreurs incluent `client_req_id` lorsque le serveur peut identifier
la requête logique concernée :

```json theme={null}
{"type":"error","message":"Session already active (req-a).","code":1002,"client_req_id":"req-a"}
```

Considérez une `error` comme définitive pour la requête logique
concernée. Selon l'erreur et la présence d'autres sessions actives, le
WebSocket peut se fermer une fois les requêtes en cours terminées.

## Voir aussi

<CardGroup cols={2}>
  <Card title="Cycle de vie WebSocket" icon="diagram-project" href="/fr/guides/websocket-lifecycle">
    Setup, ready, entrée, flush, fin et erreurs.
  </Card>

  <Card title="Des tokens LLM à la synthèse vocale en streaming" icon="message-lines" href="/fr/guides/recipes/llm-to-tts">
    Diffusez du texte généré tout en préservant la prosodie.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.