> ## 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.

# Transcription (STT) via WebSocket

> Transcription en streaming et en temps réel via WebSocket, avec VAD sémantique et flush

Le modèle de transcription de Gradium transcrit l'audio en temps réel via
un WebSocket, avec une détection d'activité vocale (VAD) sémantique et un
`flush` à la demande pour une gestion des tours de parole à faible latence.
Ce guide couvre le streaming avec le SDK Python.

<Card title="Transcription ponctuelle d'un fichier complet ?" icon="file-audio" href="/fr/guides/speech-to-text-rest">
  Si vous disposez déjà d'un audio préenregistré, l'endpoint REST POST
  suffit en un seul appel HTTP : consultez le guide REST.
</Card>

## Démarrage rapide

Le chemin le plus court : ouvrez un gestionnaire de contexte `stt_realtime`,
envoyez des fragments audio, puis itérez pour recevoir les transcriptions.

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

async def main():
    client = gradium.client.GradiumClient(api_key="your-api-key")
    stt_config = {"language": "en", "delay_in_frames": 16}

    async with client.stt_realtime(
        model_name="default",
        input_format="pcm",
        json_config=stt_config,
    ) as stt:
        # Producer: push 80 ms PCM frames as they're captured.
        async def producer():
            async for chunk in microphone_chunks():
                await stt.send_audio(chunk)
            await stt.send_eos()

        # Consumer: print transcripts as they arrive.
        async def consumer():
            async for msg in stt:
                if msg["type"] == "text":
                    print(msg["text"], end=" ", flush=True)
                elif msg["type"] == "end_of_stream":
                    return

        await asyncio.gather(producer(), consumer())

asyncio.run(main())
```

Audio attendu : PCM mono 24 kHz, 16 bits signé, en fragments de
1920 échantillons (80 ms). D'autres formats sont pris en charge, voir
[Paramètres de configuration](#paramètres-de-configuration).

## Choisir l'API du SDK

Le SDK Python propose deux façons de faire du streaming STT via
l'endpoint WebSocket. Toutes deux communiquent avec
`wss://api.gradium.ai/api/speech/asr` : choisissez selon la nature de
votre source audio.

| Cas d'usage | Méthode |
| - | - |
| Vous pouvez parcourir l'audio de bout en bout (fichier, buffer en mémoire, générateur fini) et n'avez besoin que de la transcription | `stt_stream` |
| Vous produisez l'audio en direct (micro, téléphonie, boucle d'agent) et voulez réagir à la VAD ou au flush en cours de flux | `stt_realtime` |

`stt_stream` est un unique appel asynchrone qui consomme votre itérable
audio et renvoie un `STTStream` dont la méthode `iter_text()` produit des
segments `TextWithTimestamps`. `stt_realtime` est un **gestionnaire de
contexte asynchrone** : vous envoyez les fragments avec `send_audio()` et
itérez sur le même objet pour recevoir tous les types de messages, `text`,
`step` de VAD, `end_text`, `flushed`.

La configuration se passe sous forme de **dict** pour `stt_stream` et
d'**arguments nommés** pour `stt_realtime`. Le message `setup` envoyé sur
le réseau est le même dans les deux cas.

## Mode pull : `stt_stream`

Utilisez `stt_stream` lorsque l'audio est déjà disponible, par exemple
pour transcrire un fichier ou un buffer complet en mémoire. La fonction
consomme votre itérable audio et produit des segments de texte finalisés.

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

async def main():
    client = gradium.client.GradiumClient(api_key="your-api-key")

    async def audio_generator(audio_data, chunk_size=1920):
        for i in range(0, len(audio_data), chunk_size):
            yield audio_data[i : i + chunk_size]

    setup = {
        "model_name": "default",
        "input_format": "pcm",
        "json_config": {"language": "en", "delay_in_frames": 16},
    }

    stream = await client.stt_stream(setup, audio_generator(audio_data))

    async for segment in stream.iter_text():
        print(f"[{segment.start_s:.2f}s] {segment.text}")

if __name__ == "__main__":
    asyncio.run(main())
```

`iter_text()` ne conserve que les segments de texte et produit des objets
`TextWithTimestamps` (`text`, `start_s`, `stop_s`). Pour observer les
messages de VAD ou `flushed`, utilisez `stt_realtime` ci-dessous.

## Mode push en temps réel : `stt_realtime`

Utilisez `stt_realtime` lorsque l'audio est produit en direct (micro,
téléphonie, agent conversationnel) et que vous devez réagir aux
événements de VAD ou de flush dès leur arrivée. C'est un gestionnaire de
contexte asynchrone : envoyez l'audio avec `send_audio()` et itérez sur
le même objet pour recevoir tous les types de messages.

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

async def main():
    client = gradium.client.GradiumClient(api_key="your-api-key")

    async with client.stt_realtime(
        model_name="default",
        input_format="pcm",
        json_config={"language": "en", "delay_in_frames": 16},
    ) as stt:
        async def producer():
            async for chunk in microphone_chunks():  # 80 ms PCM frames
                await stt.send_audio(chunk)
            await stt.send_eos()

        async def consumer():
            transcript = []
            async for msg in stt:
                if msg["type"] == "text":
                    transcript.append(msg["text"])
                elif msg["type"] == "step":
                    if msg["vad"][2]["inactivity_prob"] > 0.5:
                        print(f"[turn] {' '.join(transcript)}")
                        transcript = []
                elif msg["type"] == "end_of_stream":
                    return

        await asyncio.gather(producer(), consumer())

if __name__ == "__main__":
    asyncio.run(main())
```

La configuration se passe en arguments nommés (`model_name`,
`input_format`, `json_config`, …). Voir
[Paramètres de configuration](#paramètres-de-configuration) ci-dessous.

## Paramètres de configuration

* **`model_name`** : le modèle STT à utiliser (par défaut : `"default"`).
* **`input_format`** : format audio de l'entrée. Valeurs prises en charge :
  * `"pcm"` : PCM brut, 24 kHz, 16 bits signé little-endian, mono. Envoyez
    des fragments de 1920 échantillons (80 ms) pour de meilleurs résultats.
  * `"pcm_8000"`, `"pcm_16000"`, `"pcm_22050"`, `"pcm_24000"`,
    `"pcm_44100"`, `"pcm_48000"` : PCM brut à la fréquence
    d'échantillonnage indiquée (16 bits signé little-endian, mono).
  * `"wav"` : un fichier WAV valide contenant des données PCM
    (`AudioFormat = 1` dans l'en-tête WAV). Les échantillons de 16, 24 et
    32 bits sont pris en charge.
  * `"opus"` : flux Opus encapsulé dans un conteneur Ogg.
  * `"ulaw_8000"` (alias `"mulaw_8000"`) : PCM encodé en mu-law à 8 kHz.
  * `"alaw_8000"` : PCM encodé en A-law à 8 kHz.
* **`json_config`** : options STT avancées. Passez un dict avec le SDK
  Python, ou un objet/une chaîne JSON dans le message `setup` du
  WebSocket brut. Les clés courantes sont `language`, `target_language`,
  `temp`, `padding_bonus` et `delay_in_frames`.

Toute valeur non reconnue provoque une erreur serveur.

## Exemple de langue et de délai

Utilisez `json_config.language` pour définir la langue de l'audio (`"any"`
si elle est inconnue), et `json_config.delay_in_frames` pour arbitrer
entre latence et contexte. Chaque trame dure 80 ms. Des valeurs de délai
faibles rendent le modèle plus réactif ; des valeurs élevées lui donnent
plus de contexte avant d'émettre du texte.

<CodeGroup>
  ```python Python SDK theme={null}
  async with client.stt_realtime(
      model_name="default",
      input_format="pcm",
      json_config={
          "language": "en",
          "delay_in_frames": 16,
      },
  ) as stt:
      ...
  ```

  ```json WebSocket setup theme={null}
  {
    "type": "setup",
    "model_name": "default",
    "input_format": "pcm",
    "json_config": {
      "language": "en",
      "delay_in_frames": 16
    }
  }
  ```
</CodeGroup>

Pour les modèles STT de traduction, utilisez `target_language` ou
`language` pour définir la langue de la transcription en sortie. Voir
[Paramètres de transcription](/fr/guides/transcription-settings#langues).

## Types de messages

Lorsque vous itérez sur une session `stt_realtime`, vous recevez tous les
messages envoyés par le serveur. Les types utiles pour la transcription :

* **`text`** : un segment de texte transcrit, avec un horodatage `start_s`.
* **`end_text`** : finalise le segment `text` précédent avec un horodatage
  `stop_s`. Voir [Horodatages](#horodatages).
* **`step`** : informations de détection d'activité vocale émises toutes
  les 80 ms. Voir [Détection d'activité vocale (VAD)](#détection-d’activité-vocale-vad).
* **`flushed`** : confirmation que tout l'audio envoyé avant un
  `send_flush()` a été traité. Voir [Flush](#flush).
* **`end_of_stream`** : message final après `send_eos()`.

Pour le schéma au niveau du protocole (types de champs complets et JSON
échangé), consultez la
[référence STT WebSocket](/fr/api-reference/endpoint/stt-websocket).

```python theme={null}
async with client.stt_realtime(
    model_name="default",
    input_format="pcm",
    json_config={"language": "en"},
) as stt:
    # ... send audio in a producer task ...

    async for msg in stt:
        if msg["type"] == "text":
            print(f"Transcription: {msg['text']}")

        elif msg["type"] == "step":
            # VAD steps arrive every 80 ms.
            inactivity_prob = msg["vad"][2]["inactivity_prob"]
            print(f"Inactivity probability: {inactivity_prob}")

        elif msg["type"] == "flushed":
            print(f"Flush complete: {msg['flush_id']}")
```

`stt_stream` n'expose que les segments de texte finalisés via
`iter_text()`. Pour observer les messages `step`, `end_text` ou `flushed`,
utilisez `stt_realtime`.

## Horodatages

Chaque message `text` contient un champ `start_s`, l'instant (en secondes)
dans l'audio d'entrée où commence le segment transcrit. Lorsque le
segment est finalisé, le serveur émet un message `end_text` avec le
`stop_s` correspondant :

```json theme={null}
{"type": "text",     "text": "Hello world", "start_s": 0.5, "stream_id": 0}
{"type": "end_text", "stop_s": 2.5, "stream_id": 0}
```

Associez `text` et `end_text` par `stream_id` pour reconstituer les
triplets `(text, start_s, stop_s)` de chaque segment.

Les horodatages sont émis à la granularité du **segment**. Pour des
positions plus fines au niveau du mot ou du caractère, déduisez-les du
texte du segment.

## Détection d'activité vocale (VAD)

Le WebSocket STT de Gradium émet un message `step` toutes les 80 ms
contenant des **prédictions de VAD sémantique sur plusieurs horizons** :
pour chaque horizon (en secondes), le modèle indique la probabilité que
le locuteur reste silencieux pendant toute cette fenêtre. Plus l'horizon
est long, plus l'affirmation est forte. C'est plus informatif qu'un
simple indicateur voisé/non voisé, et c'est conçu pour la gestion des
tours de parole dans les systèmes conversationnels.

```json theme={null}
{
  "type": "step",
  "vad": [
    {"horizon_s": 0.5, "inactivity_prob": 0.9140625},
    {"horizon_s": 1.0, "inactivity_prob": 0.859375},
    {"horizon_s": 2.0, "inactivity_prob": 0.53515625},
    {"horizon_s": 3.0, "inactivity_prob": 0.40625}
  ],
  "step_idx": 5,
  "step_duration_s": 0.08,
  "total_duration_s": 0.4
}
```

### Détecter la fin d'un tour de parole

Comme point de départ, nous recommandons d'observer l'horizon de 1 s, 2 s
ou 3 s et de déclencher lorsque la probabilité d'inactivité dépasse 0,5 :

```python theme={null}
# vad is ordered by horizon: 0.5 s, 1.0 s, 2.0 s, 3.0 s
turn_ended = msg["vad"][3]["inactivity_prob"] > 0.5  # 3 s horizon
```

Ajustez l'horizon et le seuil selon votre application : des horizons
plus courts ou des seuils plus bas rendent le système plus réactif mais
peuvent couper la parole au locuteur ; des horizons plus longs ou des
seuils plus élevés sont plus prudents.

## Délai adaptatif

Le STT de Gradium n'est volontairement pas une boîte noire à latence
fixe. L'option `delay_in_frames` contrôle la quantité de contexte audio
que le modèle utilise avant d'émettre du texte. Chaque trame dure 80 ms,
et le serveur renvoie la valeur active dans le champ
`ready.delay_in_frames`.

Des valeurs élevées donnent plus de contexte au modèle et peuvent
améliorer la stabilité de la transcription. Des valeurs faibles réduisent
la latence. Les valeurs acceptées sont les entiers de `7` à `55`. La
valeur par défaut est `10` si vous ne la définissez pas ; `16` est un bon
point de départ si vous souhaitez un peu plus de stabilité pour des
applications interactives.

```python theme={null}
async with client.stt_realtime(
    model_name="default",
    input_format="pcm",
    json_config={"language": "en", "delay_in_frames": 10},
    wait_for_ready_on_start=True,
) as stt:
    print(stt.ready["delay_in_frames"])
```

À la fin d'un tour de parole, combinez la VAD sémantique avec
`send_flush()` : lorsque votre application estime que le locuteur a
terminé, `flush` demande à Gradium de traiter l'audio déjà envoyé, puis
renvoie `flushed` avec le `flush_id` correspondant. En interne, le worker
suit le délai ajusté après les événements de flush afin que les
horodatages des segments restent alignés sur l'audio traité.

Les messages `step` de VAD sont émis sur le transport WebSocket.

## Flush

Vous pouvez forcer le serveur à traiter immédiatement tout l'audio en mémoire tampon avec `send_flush()`. C'est utile lorsque vous avez besoin des résultats de transcription sans attendre la cadence de traitement normale, par exemple lors d'une pause naturelle dans la conversation ou avant un changement de locuteur.

Le serveur traite tout l'audio en attente, renvoie les résultats de texte en suspens, puis répond par un message `flushed` contenant le même `flush_id` que celui que vous avez fourni.

```python theme={null}
async with client.stt_realtime(
    model_name="default", input_format="pcm", json_config={"language": "en"}
) as stt:
    # Send some audio
    for i in range(0, len(audio_chunk), 1920):
        await stt.send_audio(audio_chunk[i:i + 1920])

    # Force processing of all buffered audio
    await stt.send_flush(flush_id=1)

    # Continue receiving - text results arrive, then a flushed confirmation
    async for msg in stt:
        if msg["type"] == "text":
            print(f"Transcription: {msg['text']}")
        elif msg["type"] == "flushed":
            print(f"All audio up to flush {msg['flush_id']} has been processed")
            break
```

## Options ajustables

Les modèles acceptent une configuration supplémentaire via `json_config`,
notamment `language`, `temp`, `padding_bonus` et `delay_in_frames`.
Utilisez `delay_in_frames` comme réglage de délai adaptatif pour arbitrer
entre latence et qualité. Consultez
[Paramètres de transcription](/fr/guides/transcription-settings) pour le
tableau complet et la façon de passer `json_config` avec le SDK et en REST.

## WebSocket direct

Si vous ne souhaitez pas utiliser le SDK gradium, par exemple parce que
vous appelez l'API depuis un runtime autre que Python, ou que vous voulez
un contrôle total sur les échanges, vous pouvez utiliser directement le
protocole WebSocket. Les messages ont exactement la même forme que ceux
envoyés par le SDK.

Pour des tests interactifs depuis un terminal,
[`wscat`](https://github.com/websockets/wscat) est le moyen le plus rapide
de vérifier la connectivité et d'envoyer les messages à la main :

```bash theme={null}
wscat -c "wss://api.gradium.ai/api/speech/asr" \
  -H "x-api-key: your_api_key"
# After connection, type:
# {"type":"setup","model_name":"default","input_format":"pcm","json_config":{"language":"en","delay_in_frames":16}}
```

Pour un client sans le SDK, utilisez n'importe quelle bibliothèque
WebSocket (ici [`websockets`](https://websockets.readthedocs.io/) en
Python) :

```python theme={null}
import asyncio
import base64
import json

import websockets

CHUNK_BYTES = 1920 * 2  # 80 ms at 24 kHz, 16-bit mono.


async def transcribe(api_key: str, pcm_audio: bytes):
    setup = {
        "type": "setup",
        "model_name": "default",
        "input_format": "pcm",
        "json_config": {
            "language": "en",
            "delay_in_frames": 16,
        },
    }

    async with websockets.connect(
        "wss://api.gradium.ai/api/speech/asr",
        additional_headers={"x-api-key": api_key},
    ) as ws:
        await ws.send(json.dumps(setup))
        ready = json.loads(await ws.recv())
        assert ready["type"] == "ready"

        async def producer():
            for off in range(0, len(pcm_audio), CHUNK_BYTES):
                chunk = pcm_audio[off : off + CHUNK_BYTES]
                await ws.send(json.dumps({
                    "type": "audio",
                    "audio": base64.b64encode(chunk).decode(),
                }))
            await ws.send(json.dumps({"type": "end_of_stream"}))

        async def consumer():
            while True:
                msg = json.loads(await ws.recv())
                if msg["type"] == "text":
                    print(msg["text"])
                elif msg["type"] == "end_of_stream":
                    return
                elif msg["type"] == "error":
                    raise RuntimeError(msg["message"])

        await asyncio.gather(producer(), consumer())

asyncio.run(transcribe("your_api_key", open("input.pcm", "rb").read()))
```

Pour le schéma complet des messages (chaque champ, chaque code d'erreur),
consultez la [référence STT WebSocket](/fr/api-reference/endpoint/stt-websocket).

## Étapes suivantes

<CardGroup cols={2}>
  <Card title="Référence STT WebSocket" icon="code" href="/fr/api-reference/endpoint/stt-websocket">
    Schéma complet du protocole : chaque type de message, chaque champ,
    chaque code d'erreur.
  </Card>

  <Card title="REST en un seul appel" icon="file-audio" href="/fr/guides/speech-to-text-rest">
    Transcrivez un fichier audio complet en une seule requête HTTP.
  </Card>

  <Card title="Exemples GitHub" icon="github" href="https://github.com/gradium-ai/gradbot/tree/main/demos">
    Parcourez des exemples complets d'agents vocaux utilisant le STT et le
    TTS de Gradium.
  </Card>

  <Card title="Paramètres de transcription" icon="sliders" href="/fr/guides/transcription-settings">
    `language`, `temp`, `padding_bonus`, `delay_in_frames`, et comment
    passer `json_config`.
  </Card>

  <Card title="Erreurs" icon="triangle-exclamation" href="/fr/guides/errors">
    Contrats d'erreur pour REST, WebSocket et les réponses en streaming.
  </Card>
</CardGroup>


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