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

# Vapi

> Utiliser les modèles vocaux Gradium dans les agents vocaux Vapi

Vapi est une plateforme de création et de déploiement d'agents vocaux qui prend en charge la téléphonie, le transport web, les appels au LLM et la gestion des tours de parole. Gradium n'est pas un fournisseur intégré à Vapi : vous le connectez donc via les endpoints personnalisés de Vapi, à savoir un webhook `custom-voice` pour la sortie vocale et un WebSocket `custom-transcriber` pour l'entrée vocale. Vous faites tourner un petit service passerelle, et Vapi continue de gérer le reste de l'appel.

<CardGroup cols={2}>
  <Card title="Guide TTS Gradium" icon="volume-2" href="/fr/guides/text-to-speech">
    Configuration de la synthèse vocale Gradium via WebSocket et comportement du streaming.
  </Card>

  <Card title="Guide STT Gradium" icon="mic" href="/fr/guides/speech-to-text">
    Transcription Gradium via WebSocket, VAD et flush.
  </Card>

  <Card title="Gestion des tours de parole" icon="sliders" href="/fr/guides/recipes/turn-taking">
    Ajustez les horizons et les seuils de la VAD sémantique.
  </Card>

  <Card title="Tableau de bord Vapi" icon="arrow-up-right-from-square" href="https://dashboard.vapi.ai">
    Créez un compte Vapi et un assistant.
  </Card>
</CardGroup>

<Note>
  Vous avez besoin d'un [compte Vapi](https://dashboard.vapi.ai) et d'une clé API Gradium. Cette
  page sert de référence pour la partie Gradium de l'intégration : les contrats des
  endpoints, le code de la passerelle et les options de réglage.
</Note>

## Comment Gradium se connecte

| Endpoint Vapi | Transport | Rôle de votre passerelle |
| - | - | - |
| `custom-voice` | HTTP POST | Vapi envoie le texte à prononcer ; vous renvoyez en flux du PCM brut issu de `tts_realtime`. |
| `custom-transcriber` | WebSocket | Vapi envoie en flux l'audio de l'appel ; vous renvoyez les transcriptions issues de `stt_realtime`. |

Vous pouvez utiliser chaque côté indépendamment. Conserver le transcripteur intégré de Vapi tout en utilisant Gradium pour la sortie vocale est une configuration valide, et inversement.

## Installation

```bash theme={null}
pip install gradium
```

Définissez votre clé API Gradium dans l'environnement :

```bash theme={null}
export GRADIUM_API_KEY=gd_your_api_key_here
```

## Synthèse vocale

Vapi envoie une requête par phrase, contenant le texte et la fréquence d'échantillonnage attendue :

```json theme={null}
{
  "message": {
    "type": "voice-request",
    "text": "Your table is booked for seven o'clock.",
    "sampleRate": 24000
  }
}
```

Lisez `message.sampleRate` et transmettez-le tel quel dans l'`output_format` de Gradium. Vapi attend du PCM brut mono 16 bits little-endian, exactement à la fréquence demandée, envoyé en flux au fur et à mesure de sa production.

```python theme={null}
from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse
from gradium.client import GradiumClient

app = FastAPI()
client = GradiumClient(api_key=GRADIUM_API_KEY)

@app.post("/vapi/tts")
async def vapi_tts(request: Request) -> StreamingResponse:
    message = (await request.json())["message"]
    text, rate = message["text"], message["sampleRate"]

    async def pcm():
        async with client.tts_realtime(
            voice_id="YTpq7expH9539ERJ",
            output_format=f"pcm_{rate}",
        ) as tts:
            await tts.send_text(text)
            await tts.send_eos()
            async for msg in tts:
                if msg["type"] == "audio":
                    yield msg["audio"]
                elif msg["type"] == "end_of_stream":
                    break

    return StreamingResponse(pcm(), media_type="application/octet-stream")
```

<Note>
  Chaque fréquence d'échantillonnage demandée par Vapi a un format PCM Gradium correspondant : aucun rééchantillonnage n'est donc nécessaire. Consultez [Limites](/fr/guides/limits) pour la liste complète des formats.
</Note>

Faites pointer le `voice` de l'assistant vers l'endpoint :

```json theme={null}
{
  "voice": {
    "provider": "custom-voice",
    "server": { "url": "https://your-server.com/vapi/tts" }
  }
}
```

## Transcription

Vapi ouvre un WebSocket, envoie une trame JSON `start`, puis envoie en flux du PCM 16 bits entrelacé. Lorsque `channels` vaut `2`, le canal 0 porte l'appelant et le canal 1 l'assistant : transmettez **uniquement le canal 0**, sinon l'agent transcrit sa propre voix.

```python theme={null}
import asyncio, json
from fastapi import FastAPI, WebSocket
from gradium.client import GradiumClient

app = FastAPI()
client = GradiumClient(api_key=GRADIUM_API_KEY)

def caller_channel(pcm: bytes, channels: int) -> bytes:
    """Keep channel 0 from interleaved 16-bit PCM."""
    if channels == 1:
        return pcm
    stride = channels * 2
    return b"".join(pcm[i:i + 2] for i in range(0, len(pcm) - stride + 1, stride))

@app.websocket("/vapi/transcriber")
async def vapi_transcriber(ws: WebSocket) -> None:
    await ws.accept()
    start = json.loads(await ws.receive_text())
    channels = int(start.get("channels", 2))
    transcript, high_vad_steps = [], 0

    async def send(text: str, transcript_type: str) -> None:
        await ws.send_text(json.dumps({
            "type": "transcriber-response",
            "transcription": text,
            "channel": "customer",
            "transcriptType": transcript_type,
        }))

    async with client.stt_realtime(
        input_format=f"pcm_{start['sampleRate']}",
        json_config={"language": "en", "delay_in_frames": 16},
    ) as stt:

        async def to_gradium() -> None:
            while True:
                pcm = await ws.receive_bytes()
                await stt.send_audio(caller_channel(pcm, channels))

        async def to_vapi() -> None:
            nonlocal transcript, high_vad_steps
            async for msg in stt:
                if msg["type"] == "text":
                    transcript.append(msg["text"])
                    await send(" ".join(transcript).strip(), "partial")

                elif msg["type"] == "step":
                    inactivity = msg["vad"][-1]["inactivity_prob"]
                    high_vad_steps = high_vad_steps + 1 if inactivity > 0.5 else 0
                    if high_vad_steps >= 3 and transcript:
                        await stt.send_flush(flush_id=1)

                elif msg["type"] == "flushed":
                    await send(" ".join(transcript).strip(), "final")
                    transcript, high_vad_steps = [], 0

        # Stop as soon as either side ends, so the Gradium session and the
        # audio pump are torn down when the caller hangs up.
        tasks = [asyncio.create_task(to_gradium()),
                 asyncio.create_task(to_vapi())]
        try:
            await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
        finally:
            for task in tasks:
                task.cancel()
```

Envoyez les transcriptions `partial` en plus des `final` : Vapi doit voir la parole pendant qu'elle a lieu pour que l'interruption (barge-in) fonctionne. Considérez `flushed` comme la fin du tour plutôt que de finaliser dès que le seuil de VAD est franchi, car Gradium conserve encore de l'audio dans sa fenêtre de décodage. Consultez [Gestion des tours de parole avec la VAD sémantique](/fr/guides/recipes/turn-taking) pour ajuster l'horizon et le seuil.

Faites pointer le `transcriber` de l'assistant vers la passerelle en `wss` :

```json theme={null}
{
  "transcriber": {
    "provider": "custom-transcriber",
    "server": { "url": "wss://your-server.com/vapi/transcriber" }
  }
}
```

## Sécuriser les endpoints

Les deux endpoints sont des points d'accès HTTP publics. Définissez un `secret` sur l'objet `server`
dans la configuration de l'assistant : Vapi l'envoie dans un en-tête `X-Vapi-Secret` à chaque
requête. Rejetez tout ce qui ne correspond pas :

```python theme={null}
if request.headers.get("x-vapi-secret") != VAPI_WEBHOOK_SECRET:
    raise HTTPException(status_code=401, detail="bad secret")
```

## Configuration

| Paramètre | S'applique à | Description |
| - | - | - |
| `api_key` | STT, TTS | Clé API Gradium. Par défaut : `GRADIUM_API_KEY`. |
| `model_name` | STT, TTS | Nom du modèle Gradium. Par défaut : `default`. |
| `voice_id` | TTS | ID de la voix Gradium pour les réponses synthétisées. |
| `output_format` | TTS | À définir sur `pcm_<rate>` à partir du `message.sampleRate` de Vapi. |
| `pronunciation_id` | TTS | ID facultatif d'un dictionnaire de prononciation. |
| `input_format` | STT | À définir sur `pcm_<rate>` à partir du `sampleRate` de la trame `start`. |
| `json_config.language` | STT | Langue de transcription : `en`, `fr`, `es`, `pt`, `de`, ou `any` pour la détection automatique. Définissez-la toujours. |
| `json_config.delay_in_frames` | STT | Contexte accumulé avant l'émission du texte. Chaque trame dure 80 ms. |
| `json_config.keywords` | STT | Oriente la reconnaissance vers les noms propres et le vocabulaire métier. |

## Réduire la latence grâce au multiplexage

Vapi envoie une requête `custom-voice` par phrase : ouvrir un nouveau WebSocket à chaque requête fait donc payer l'établissement de la connexion plusieurs fois par réponse de l'agent. Gardez une connexion ouverte par fréquence d'échantillonnage et faites-y transiter les requêtes concurrentes avec `close_ws_on_eos: false` et un `client_req_id` propre à chaque requête.

Renouvelez les connexions du pool avant d'atteindre la limite de durée de session, et repliez-vous sur une connexion à usage unique si un socket du pool se ferme avant d'avoir produit de l'audio. Consultez [Multiplexage](/fr/guides/multiplexing) pour le contrat complet.

## Laisser Gradium décider de la fin du tour

Par défaut, Vapi applique sa propre détection de fin de parole (endpointing) sur vos transcriptions partielles, ce qui peut entrer en concurrence avec la VAD de Gradium et faire répondre l'assistant avant l'arrivée de la transcription finale issue du flush. Confiez cette décision à Gradium avec un modèle d'endpointing personnalisé :

```json theme={null}
{
  "startSpeakingPlan": {
    "waitSeconds": 0.25,
    "smartEndpointingPlan": {
      "provider": "custom-endpointing-model",
      "server": { "url": "https://your-server.com/vapi/endpointing", "timeoutSeconds": 3 }
    }
  }
}
```

Vapi envoie alors un `call.endpointing.request` à chaque mise à jour de la transcription, et votre serveur répond en indiquant combien de temps attendre encore, en fonction de la session Gradium en cours :

```python theme={null}
@app.post("/vapi/endpointing")
async def endpointing(body: dict) -> dict:
    if body.get("message", {}).get("type") != "call.endpointing.request":
        return {"ok": True}

    state = gradium_turn_state()          # your live STT session

    if not state["active"]:
        return {"timeoutSeconds": 1.0}    # no session, keep the call moving
    if state["seconds_since_final"] < 2.5:
        return {"timeoutSeconds": 0.05}   # Gradium flushed, the turn is over
    if state["inactivity_prob"] < 0.5:
        return {"timeoutSeconds": 5.0}    # caller is audibly mid-sentence
    return {"timeoutSeconds": 3.0}        # a pause; the final should arrive
```

<Note>
  Supprimez votre référence à la session Gradium à la fin d'un appel. Si une session terminée reste enregistrée, les premières requêtes d'endpointing de l'appel suivant reçoivent une réponse fondée sur un état de tour obsolète.
</Note>

## Appels longs

Une session Gradium dure au maximum 300 secondes. Reconnectez la session Gradium de manière transparente lorsqu'elle se termine, tout en gardant ouvert le socket Vapi, afin que les appels de toute durée continuent d'être transcrits. Voir [Limites](/fr/guides/limits).

## Quand utiliser Vapi avec Gradium

* **Agents téléphoniques** : laissez Vapi gérer les numéros de téléphone, le SIP et le routage des appels pendant que Gradium s'occupe de la voix.
* **Changement de fournisseur vocal** : conservez un assistant Vapi existant, son LLM et ses outils, et ne changez que la couche voix ou transcription.
* **Voix personnalisées** : utilisez une voix Gradium clonée instantanément dans un agent Vapi. Voir [Voix](/fr/guides/voices/overview).
* **Vocabulaire métier** : améliorez la reconnaissance des noms de plats, de marques et de produits grâce au [renforcement de mots-clés (keyword boosting)](/fr/guides/recipes/keyword-boosting) et aux [règles de réécriture de texte](/fr/guides/text-rewriting).


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