Une fois la connexion à la Realtime API établie via WebRTC ou WebSocket, vous pouvez appeler un modèle Realtime (par exemple gpt-realtime-2.1) pour mener des conversations de parole à parole. Pour cela, vous devez envoyer des événements client pour déclencher des actions et écouter les événements serveur pour réagir aux actions de la Realtime API.
Ce guide explique comment s’enchaînent les événements nécessaires pour générer du texte ou de l’audio, fournir une image en entrée ou utiliser l’appel de fonction. Il décrit également comment suivre l’état d’une session Realtime.
Si vous n’avez pas besoin de dialoguer avec le modèle et n’attendez donc aucune réponse de sa part, vous pouvez utiliser la Realtime API en mode transcription.
Sessions Realtime de parole à parole
Une session Realtime conserve l’état des échanges entre le modèle et un client connecté. Elle comprend trois composants principaux :
- L’objet Session définit les paramètres de l’interaction, notamment le modèle utilisé, la voix de sortie et les autres réglages.
- La conversation regroupe les éléments fournis par l’utilisateur et ceux produits par le modèle pendant la session en cours.
- Les réponses sont des éléments audio ou textuels que le modèle génère et qui sont ajoutés à la conversation.
Tampon audio d’entrée et WebSockets
Avec WebRTC, les API WebRTC prennent en charge une grande partie du traitement nécessaire pour envoyer de l’audio au modèle et en recevoir.
Si vous utilisez des WebSockets pour l’audio, vous devez alimenter vous-même le tampon audio d’entrée en envoyant au serveur des événements JSON contenant les données audio encodées en base64.
Ensemble, ces composants constituent une session Realtime. Utilisez les événements client pour mettre à jour l’état de la session et écoutez les événements serveur pour réagir aux changements d’état.
Événements du cycle de vie d’une session
Lorsque vous ouvrez une session via WebRTC ou WebSockets, le serveur envoie un événement session.created pour indiquer que la session est prête. Côté client, vous pouvez modifier la configuration de la session en cours avec l’événement session.update. La plupart des propriétés de la session sont modifiables à tout moment. La propriété voice, qui détermine la voix de sortie du modèle, ne peut plus être modifiée après la première réponse audio du modèle dans cette session. Une session Realtime dure au maximum 60 minutes.
L’exemple suivant met à jour la session avec un événement client session.update. Pour en savoir plus sur l’envoi d’événements client via ces canaux, consultez le guide WebRTC ou WebSocket.
const event = {
type: "session.update",
session: {
type: "realtime",
model: "gpt-realtime-2.1",
// Lock the output to audio (set to ["text"] if you want text without audio)
output_modalities: ["audio"],
audio: {
input: {
format: {
type: "audio/pcm",
rate: 24000,
},
turn_detection: {
type: "semantic_vad",
},
},
output: {
format: {
type: "audio/pcm",
rate: 24000,
},
voice: "marin",
},
},
// Use a server-stored prompt by ID. Optionally pin a version and pass variables.
prompt: {
id: "pmpt_123", // your stored prompt ID
version: "89", // optional: pin a specific version
variables: {
city: "Paris", // example variable used by your prompt
},
},
// You can still set direct session fields; these override prompt fields if they overlap:
instructions:
"Speak clearly and briefly. Confirm understanding before taking actions.",
},
};
// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));Une fois la session mise à jour, le serveur émet un événement session.updated qui contient le nouvel état de la session.
| Événements client associés | Événements serveur associés |
|---|---|
Entrées et sorties textuelles
Pour générer du texte avec un modèle Realtime, vous pouvez ajouter du texte à la conversation en cours, demander au modèle de générer une réponse et écouter les événements envoyés par le serveur pour suivre la progression de la réponse. Pour que le modèle puisse générer du texte, la session doit être configurée avec la modalité text, activée par défaut.
Créez un élément de conversation textuel avec l’événement client conversation.item.create. Cette opération s’apparente à l’envoi d’un message utilisateur (prompt) à Chat Completions via l’API REST.
const event = {
type: "conversation.item.create",
item: {
type: "message",
role: "user",
content: [
{
type: "input_text",
text: "What Prince album sold the most copies?",
},
],
},
};
// WebRTC data channel and WebSocket both have .send()
ws.send(JSON.stringify(event));Après avoir ajouté le message utilisateur à la conversation, envoyez l’événement response.create pour déclencher une réponse du modèle. Choisissez une seule modalité de sortie : audio produit de l’audio accompagné de sa transcription, et text produit uniquement du texte. Pour générer uniquement du texte, précisez-le lors de l’envoi de l’événement client response.create, comme dans l’exemple suivant.
const event = {
type: "response.create",
response: {
output_modalities: ["text"],
},
};
// WebRTC data channel and WebSocket both have .send()
ws.send(JSON.stringify(event));Lorsque la génération de la réponse est terminée, le serveur émet l’événement response.done. Cet événement contient l’intégralité du texte généré par le modèle, comme le montre l’exemple suivant.
function handleEvent(message) {
const data = "data" in message ? message.data : message.toString();
const serverEvent = JSON.parse(data);
if (serverEvent.type === "response.done") {
console.log(serverEvent.response.output[0]);
}
}
// Listen for server messages (WebRTC)
// dataChannel.addEventListener("message", handleEvent);
// Listen for server messages (WebSocket)
ws.on("message", handleEvent);Pendant la génération de la réponse, le serveur émet plusieurs événements de cycle de vie. Vous pouvez écouter ces événements, notamment response.output_text.delta, pour afficher la réponse aux utilisateurs à mesure qu’elle est générée. Le tableau suivant répertorie tous les événements émis par le serveur dans la colonne Événements serveur associés, dans l’ordre approximatif de leur émission. Il indique aussi les événements client utilisés pour générer du texte.
| Événements client associés | Événements serveur associés |
|---|---|
Entrées et sorties audio
La Realtime API permet de dialoguer directement à l’oral avec le modèle, sans étape intermédiaire de synthèse vocale ou de conversion de la parole en texte. Cela réduit la latence des interfaces vocales et donne au modèle davantage d’informations sur le ton et les inflexions de la voix de l’utilisateur.
Choix de la voix
Vous pouvez configurer une session Realtime pour qu’elle utilise l’une des voix intégrées pour la sortie audio. Définissez voice à la création de la session (ou dans un événement response.create) pour choisir la voix du modèle. Les voix disponibles sont alloy, ash, ballad, coral, echo, sage, shimmer, verse, marin et cedar. Une fois que le modèle a émis de l’audio, la propriété voice ne peut plus être modifiée pour cette session. Pour une qualité optimale, nous recommandons marin ou cedar.
Gestion de l’audio avec WebRTC
Lorsque vous utilisez WebRTC, la Realtime API établit une connexion pair à pair avec votre client. Celui-ci reçoit la sortie audio du modèle sous forme de flux multimédia distant. Pour fournir de l’audio au modèle, le client capture le son des périphériques audio (getUserMedia) et ajoute les pistes des flux multimédias à la connexion pair à pair.
L’exemple du guide de connexion WebRTC illustre une configuration simple de l’audio local et distant avec les API du navigateur :
// Create a peer connection
const pc = new RTCPeerConnection();
// Set up to play remote audio from the model
const audioEl = document.createElement("audio");
audioEl.autoplay = true;
pc.ontrack = (e) => (audioEl.srcObject = e.streams[0]);
// Add local audio track for microphone input in the browser
const ms = await navigator.mediaDevices.getUserMedia({
audio: true,
});
pc.addTrack(ms.getTracks()[0]);Cet extrait permet de dialoguer avec la Realtime API. Pour découvrir d’autres types d’interfaces utilisateur, consultez le dépôt d’exemples WebRTC. Vous pouvez aussi essayer les démonstrations en ligne.
Dans le navigateur, les API de capture et de diffusion multimédia permettent notamment d’activer ou de désactiver les microphones et de choisir le périphérique d’entrée.
Événements client et serveur pour l’audio avec WebRTC
Par défaut, les clients WebRTC peuvent transmettre de l’audio à la Realtime API sans envoyer d’événement client au préalable. Dès qu’une piste audio locale est ajoutée à la connexion pair à pair, les utilisateurs peuvent commencer à parler.
Le serveur envoie néanmoins au client WebRTC des événements de cycle de vie pendant les échanges audio sur la connexion pair à pair. Par exemple :
- Lorsque le client envoie de l’audio sur la piste multimédia locale, il reçoit du serveur des événements
input_audio_buffer.speech_started. - Lorsque l’entrée audio locale s’interrompt, le client reçoit l’événement
input_audio_buffer.speech_stopped. - Le client reçoit des événements contenant les nouveaux fragments de la transcription audio en cours.
- Le client reçoit un événement
response.donelorsque le modèle a transcrit la réponse et fini de l’envoyer.
Les API WebRTC de gestion des flux multimédias peuvent suffire à vos besoins. Dans certains cas, vous devrez toutefois utiliser des interfaces de plus bas niveau pour les entrées et sorties audio. La section suivante sur les WebSockets détaille ces interfaces et répertorie les événements nécessaires pour gérer plus finement l’entrée audio.
Gestion de l’audio avec les WebSockets
Avec une connexion WebSocket, votre client doit prendre en charge davantage d’opérations pour envoyer de l’audio au serveur et en recevoir. Le tableau suivant décrit la séquence d’événements nécessaires à ces échanges pendant une session WebSocket.
Les événements sont présentés dans l’ordre du cycle de vie, mais certains, comme les événements delta, peuvent se produire simultanément.
| Étape du cycle de vie | Événements client | Événements serveur |
|---|---|---|
| Initialisation de la session | ||
| Entrée audio de l’utilisateur | (envoi du message audio complet) (envoi de l’audio par blocs successifs) (si la détection d’activité vocale est désactivée) (si la détection d’activité vocale est désactivée) |
|
| Sortie audio du serveur | (si la détection d’activité vocale est désactivée) |
|
Envoi d’un flux audio au serveur
Pour transmettre l’entrée audio au serveur en continu, utilisez l’événement client input_audio_buffer.append. Avec cet événement, vous envoyez à la Realtime API, via la connexion WebSocket, des blocs d’octets audio encodés en base64. La taille de chaque bloc est limitée à 15 Mo.
Définissez session.audio.input.format dans session.update. Pour le format PCM, utilisez {"type": "audio/pcm", "rate": 24000} et envoyez de l’audio mono à 24 kHz.
import fs from "fs";
import decodeAudio from "audio-decode";
// Converts Float32Array of audio data to PCM16 ArrayBuffer
function floatTo16BitPCM(float32Array) {
const buffer = new ArrayBuffer(float32Array.length * 2);
const view = new DataView(buffer);
let offset = 0;
for (let i = 0; i < float32Array.length; i++, offset += 2) {
const sample = Math.max(-1, Math.min(1, float32Array[i]));
view.setInt16(
offset,
Math.round(sample * (sample < 0 ? 0x8000 : 0x7fff)),
true
);
}
return buffer;
}
// Converts a Float32Array to base64-encoded PCM16 data
function base64EncodeAudio(float32Array) {
const arrayBuffer = floatTo16BitPCM(float32Array);
let binary = "";
let bytes = new Uint8Array(arrayBuffer);
const chunkSize = 0x8000; // 32KB chunk size
for (let i = 0; i < bytes.length; i += chunkSize) {
let chunk = bytes.subarray(i, i + chunkSize);
binary += String.fromCharCode(...chunk);
}
return btoa(binary);
}
// Fills the audio buffer with the contents of three files,
// then asks the model to generate a response.
const files = [
"fixtures/sample1.wav",
"fixtures/sample2.wav",
"fixtures/sample3.wav",
];
// Manual commit and response creation require automatic turn detection off.
ws.send(
JSON.stringify({
type: "session.update",
session: {
type: "realtime",
audio: { input: { turn_detection: null } },
},
})
);
for (const filename of files) {
const audioFile = fs.readFileSync(filename);
const audioBuffer = await decodeAudio(audioFile);
if (
audioBuffer.sampleRate !== 24000 ||
audioBuffer.channelData.length !== 1
) {
throw new Error("Expected 24 kHz mono audio: " + filename);
}
const channelData = audioBuffer.channelData[0];
const base64Chunk = base64EncodeAudio(channelData);
ws.send(
JSON.stringify({
type: "input_audio_buffer.append",
audio: base64Chunk,
})
);
}
ws.send(JSON.stringify({ type: "input_audio_buffer.commit" }));
ws.send(
JSON.stringify({
type: "response.create",
response: { output_modalities: ["audio"] },
})
);Envoi de messages audio complets
Vous pouvez également créer des messages de conversation à partir d’enregistrements audio complets. Utilisez l’événement client conversation.item.create pour créer des messages dont le contenu est de type input_audio.
const fullAudio = "<a base64-encoded string of audio bytes>";
const event = {
type: "conversation.item.create",
item: {
type: "message",
role: "user",
content: [
{
type: "input_audio",
audio: fullAudio,
},
],
},
};
// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));Traitement de la sortie audio d’une connexion WebSocket
Pour lire l’audio de sortie côté client, par exemple dans un navigateur web, nous recommandons WebRTC plutôt que les WebSockets. WebRTC assure une transmission plus fiable des flux multimédias aux appareils clients lorsque les conditions réseau varient.
Dans une application où deux serveurs communiquent par WebSocket, écoutez les événements response.output_audio.delta pour récupérer les blocs de données audio du modèle, encodés en base64. Vous pouvez les mettre en mémoire tampon, puis les écrire dans un fichier, ou les transmettre immédiatement à une autre destination, par exemple un appel téléphonique avec Twilio.
Les événements response.output_audio.done et response.done contiennent uniquement la transcription du contenu audio. Pour récupérer les octets audio eux-mêmes, écoutez les événements response.output_audio.delta.
Vous pouvez configurer le format des blocs audio pour toute la session ou pour chaque réponse.
- Pour la session, définissez
session.audio.output.formatdanssession.update. - Pour une réponse, définissez
response.audio.output.formatdansresponse.create.
function handleEvent(message) {
const serverEvent = JSON.parse(message.toString());
if (serverEvent.type === "response.output_audio.delta") {
const audio = Buffer.from(serverEvent.delta, "base64");
console.log(`Received ${audio.length} audio bytes`);
}
}
// Listen for server messages (WebSocket)
ws.on("message", handleEvent);Images en entrée
gpt-realtime-2 et gpt-realtime acceptent également les images en entrée. Vous pouvez joindre une image au contenu d’un message utilisateur pour que le modèle puisse en tenir compte dans sa réponse.
const base64Image = "<a base64-encoded string of image bytes>";
const event = {
type: "conversation.item.create",
item: {
type: "message",
role: "user",
content: [
{
type: "input_image",
image_url: `data:image/{format};base64,${base64Image}`,
},
],
},
};
// WebRTC data channel and WebSocket both have .send()
dataChannel.send(JSON.stringify(event));Détection d’activité vocale
Par défaut, la détection d’activité vocale (VAD) est activée dans les sessions Realtime. L’API détecte quand l’utilisateur commence ou cesse de parler et répond automatiquement.
Pour configurer la VAD, consultez le guide de la détection d’activité vocale.
Désactiver la VAD
Pour désactiver la VAD, définissez turn_detection sur null avec l’événement client session.update. Cela permet de contrôler précisément l’entrée audio, par exemple dans une interface où l’utilisateur appuie sur une touche pour parler.
Lorsque la VAD est désactivée, le client doit envoyer lui-même des événements supplémentaires pour déclencher des réponses audio :
- Envoyez explicitement
input_audio_buffer.commitpour créer dans la conversation un nouvel élément contenant l’entrée de l’utilisateur. - Envoyez explicitement
response.createpour déclencher une réponse audio du modèle. - Envoyez
input_audio_buffer.clearavant le début de l’entrée utilisateur suivante.
Conserver la VAD et désactiver les réponses automatiques
Pour conserver la VAD tout en décidant vous-même quand générer une réponse, définissez turn_detection.interrupt_response et turn_detection.create_response sur false avec l’événement client session.update. La VAD conserve son fonctionnement, mais aucune nouvelle réponse n’est créée automatiquement. Le client peut en déclencher une avec un événement response.create.
Cette configuration convient notamment à la modération, à la validation des entrées ou aux architectures RAG lorsque vous acceptez un peu plus de latence pour mieux contrôler les entrées.
Créer des réponses en dehors de la conversation par défaut
Par défaut, toutes les réponses générées pendant une session sont ajoutées à l’état de sa conversation (la « conversation par défaut »). Vous pouvez toutefois avoir besoin de générer des réponses en dehors du contexte de cette conversation ou de générer plusieurs réponses en parallèle. Vous pouvez aussi vouloir choisir plus précisément les éléments que le modèle prend en compte pour générer une réponse, par exemple en retenant uniquement les N derniers tours de parole.
Pour générer une réponse « hors bande » sans l’ajouter à l’état de la conversation par défaut, définissez le champ response.conversation sur la chaîne none lorsque vous créez la réponse avec l’événement client response.create.
Lorsque vous créez une réponse hors bande, vous aurez probablement besoin de repérer les événements serveur qui lui correspondent. Vous pouvez fournir des métadonnées dans metadata pour identifier la réponse que le modèle génère à la suite de cet événement client.
const prompt = `
Analyze the conversation so far. If it is related to support, output
"support". If it is related to sales, output "sales".
`;
const event = {
type: "response.create",
response: {
// Setting to "none" indicates the response is out of band
// and will not be added to the default conversation
conversation: "none",
// Set metadata to help identify responses sent back from the model
metadata: { topic: "classification" },
// Set any other available response fields
output_modalities: ["text"],
instructions: prompt,
},
};
// WebRTC data channel and WebSocket both have .send()
ws.send(JSON.stringify(event));En écoutant l’événement serveur response.done, vous pouvez alors retrouver le résultat de votre réponse hors bande.
function handleEvent(message) {
const data = "data" in message ? message.data : message.toString();
const serverEvent = JSON.parse(data);
if (
serverEvent.type === "response.done" &&
serverEvent.response.metadata?.topic === "classification"
) {
const { response } = serverEvent;
if (response.status !== "completed" || response.output.length === 0) {
throw new Error(
"Classification response ended with status " + response.status
);
}
console.log(response.output[0]);
}
}
// Listen for server messages (WebRTC)
// dataChannel.addEventListener("message", handleEvent);
// Listen for server messages (WebSocket)
ws.on("message", handleEvent);Créer un contexte personnalisé pour les réponses
Vous pouvez aussi créer, en dehors de la conversation en cours, un contexte personnalisé que le modèle utilisera pour générer une réponse. Pour cela, renseignez le tableau input de l’événement client response.create. Vous pouvez fournir de nouvelles entrées ou référencer des éléments d’entrée existants de la conversation par leur identifiant.
const existingItemId = "item_pizza";
ws.send(
JSON.stringify({
type: "conversation.item.create",
item: {
id: existingItemId,
type: "message",
role: "user",
content: [
{ type: "input_text", text: "We are ordering pizza for dinner." },
],
},
})
);
const event = {
type: "response.create",
response: {
conversation: "none",
metadata: { topic: "pizza" },
output_modalities: ["text"],
// Create a custom input array for this request with whatever context
// is appropriate
input: [
// potentially include existing conversation items:
{
type: "item_reference",
id: existingItemId,
},
{
type: "message",
role: "user",
content: [
{
type: "input_text",
text: "Is it okay to put pineapple on pizza?",
},
],
},
],
},
};
// WebRTC data channel and WebSocket both have .send()
ws.send(JSON.stringify(event));Créer des réponses sans contexte
Vous pouvez également insérer dans la conversation par défaut des réponses qui ignorent toutes les autres instructions et tout le contexte. Pour cela, définissez input sur un tableau vide.
const prompt = `
Say exactly the following:
I'm a little teapot, short and stout!
This is my handle, this is my spout!
`;
const event = {
type: "response.create",
response: {
// An empty input array removes existing context
input: [],
output_modalities: ["text"],
instructions: prompt,
},
};
// WebRTC data channel and WebSocket both have .send()
ws.send(JSON.stringify(event));Appel de fonction
Les modèles Realtime prennent aussi en charge l’appel de fonction, qui permet d’exécuter votre propre code pour étendre les capacités du modèle. Voici comment se déroule un appel :
- Lorsque vous mettez à jour la session ou créez une réponse, vous pouvez indiquer la liste des fonctions que le modèle peut appeler.
- Si le modèle détermine, en traitant l’entrée, qu’il doit appeler une fonction, il ajoute à la conversation des éléments contenant les arguments de l’appel.
- Lorsque le client détecte ces éléments, il exécute votre code avec les arguments fournis.
- Après l’exécution du code, le client crée des éléments de conversation qui contiennent le résultat de l’appel, puis demande au modèle de répondre.
Prenons l’exemple d’une fonction qui fournit aux utilisateurs leur horoscope du jour. Les exemples suivants montrent la structure des événements que le client doit envoyer et des événements que le serveur émet en retour.
Configurer les fonctions que le modèle peut appeler
Indiquez d’abord au modèle quelles fonctions il peut appeler selon l’entrée de l’utilisateur. Vous pouvez configurer les fonctions disponibles pour toute la session ou pour une réponse donnée.
- Pour la session, utilisez la propriété
session.toolsdanssession.update. - Pour une réponse, utilisez la propriété
response.toolsdansresponse.create.
Voici le contenu d’un événement client session.update qui configure une fonction de génération d’horoscope. La fonction prend un seul argument : le signe astrologique pour lequel générer l’horoscope.
{
"type": "session.update",
"session": {
"tools": [
{
"type": "function",
"name": "generate_horoscope",
"description": "Give today's horoscope for an astrological sign.",
"parameters": {
"type": "object",
"properties": {
"sign": {
"type": "string",
"description": "The sign for the horoscope.",
"enum": [
"Aries",
"Taurus",
"Gemini",
"Cancer",
"Leo",
"Virgo",
"Libra",
"Scorpio",
"Sagittarius",
"Capricorn",
"Aquarius",
"Pisces"
]
}
},
"required": ["sign"]
}
}
],
"tool_choice": "auto"
}
}
Les champs description de la fonction et de ses paramètres aident le modèle à déterminer s’il doit appeler la fonction et quelles valeurs lui transmettre. Si le message de l’utilisateur indique qu’il souhaite connaître son horoscope, le modèle appelle cette fonction avec le paramètre sign.
Détecter une demande d’appel de fonction du modèle
Selon les entrées qu’il reçoit, le modèle peut décider d’appeler une fonction pour produire la réponse la plus pertinente. Supposons que votre application ajoute l’élément de conversation suivant avec un événement conversation.item.create, puis crée une réponse :
{
"type": "conversation.item.create",
"item": {
"type": "message",
"role": "user",
"content": [
{
"type": "input_text",
"text": "What is my horoscope? I am an aquarius."
}
]
}
}
Le client envoie ensuite l’événement response.create pour générer une réponse :
{
"type": "response.create"
}
Avant de répondre par du texte ou de l’audio, le modèle génère une réponse contenant les arguments à transmettre à une fonction de votre application. Pour recevoir ces arguments à mesure qu’ils sont générés, écoutez l’événement serveur response.function_call_arguments.delta. L’événement response.done contient également toutes les données nécessaires pour appeler la fonction.
{
"type": "response.done",
"event_id": "event_AeqLA8iR6FK20L4XZs2P6",
"response": {
"object": "realtime.response",
"id": "resp_AeqL8XwMUOri9OhcQJIu9",
"status": "completed",
"status_details": null,
"output": [
{
"object": "realtime.item",
"id": "item_AeqL8gmRWDn9bIsUM2T35",
"type": "function_call",
"status": "completed",
"name": "generate_horoscope",
"call_id": "call_sHlR7iaFwQ2YQOqm",
"arguments": "{\"sign\":\"Aquarius\"}"
}
]
}
}
Dans le JSON envoyé par le serveur, les propriétés suivantes permettent de reconnaître et de traiter une demande d’appel de fonction :
| Propriété | Rôle dans l’appel de fonction |
|---|---|
response.output[0].type |
La valeur function_call indique que la réponse contient les arguments d’un appel à une fonction nommée. |
response.output[0].name |
Nom de la fonction configurée à appeler, ici generate_horoscope. |
response.output[0].arguments |
Chaîne JSON contenant les arguments à transmettre à la fonction, ici "{\"sign\":\"Aquarius\"}". |
response.output[0].call_id |
Identifiant généré par le système pour cet appel. Vous aurez besoin de cet identifiant pour renvoyer le résultat de l’appel au modèle. |
Avec ces informations, votre application peut exécuter le code qui génère l’horoscope et transmettre le résultat au modèle pour qu’il formule une réponse.
Transmettre le résultat d’un appel de fonction au modèle
Lorsque votre application reçoit une réponse du modèle contenant les arguments d’un appel de fonction, elle peut exécuter le code correspondant. Ce code peut, par exemple, interroger des API externes ou accéder à des bases de données.
Pour transmettre le résultat de votre code au modèle, créez un élément de conversation contenant ce résultat avec l’événement client conversation.item.create.
{
"type": "conversation.item.create",
"item": {
"type": "function_call_output",
"call_id": "call_sHlR7iaFwQ2YQOqm",
"output": "{\"horoscope\": \"You will soon meet a new friend.\"}"
}
}
- L’élément de conversation est de type
function_call_output. item.call_idreprend l’identifiant reçu dans l’événementresponse.doneci-dessus.item.outputest une chaîne JSON contenant le résultat de l’appel de fonction.
Après avoir ajouté l’élément contenant le résultat de l’appel, envoyez de nouveau l’événement response.create depuis le client. Le modèle génère alors une réponse à partir des données renvoyées par la fonction.
{
"type": "response.create"
}
Gestion des erreurs
Le serveur émet un événement error chaque fois qu’il rencontre une erreur pendant la session. Dans certains cas, l’erreur provient d’un événement client envoyé par votre application.
Avec HTTP, chaque réponse est implicitement associée à une requête du client. Pour savoir quel événement client a déclenché une erreur sur le serveur, ajoutez une propriété event_id aux événements client. L’exemple suivant illustre cette technique : le client tente d’émettre un événement dont le type n’est pas pris en charge.
const event = {
event_id: "my_awesome_event",
type: "scooby.dooby.doo",
};
dataChannel.send(JSON.stringify(event));En réponse à cet événement client invalide, le serveur émet un événement d’erreur semblable à celui-ci :
{
"type": "error",
"event_id": "event_error_123",
"error": {
"type": "invalid_request_error",
"code": "invalid_value",
"message": "Invalid value: 'scooby.dooby.doo'.",
"param": "type",
"event_id": "my_awesome_event"
}
}
Interruption et troncature
Dans de nombreuses applications vocales, l’utilisateur peut interrompre le modèle pendant qu’il parle. Lorsque la VAD est activée, la Realtime API détecte que l’utilisateur prend la parole, annule la réponse en cours et en commence une nouvelle. Pour poursuivre naturellement la conversation, le modèle doit savoir à quel moment il a été interrompu, par exemple si l’utilisateur demande : « Qu’est-ce que vous venez de dire ? » Il faut alors tronquer la dernière réponse du modèle, c’est-à-dire retirer de la conversation la partie que l’utilisateur n’a pas entendue.
Avec WebRTC et SIP, le serveur gère le tampon audio de sortie. Il connaît donc à chaque instant la durée de l’audio déjà lu. En cas d’interruption par l’utilisateur, le serveur supprime automatiquement la partie qui n’a pas encore été lue.
Avec une connexion WebSocket, le client gère la lecture audio. C’est donc à lui de l’arrêter et de tronquer la réponse. Procédez comme suit :
- À la réception de
input_audio_buffer.speech_started, arrêtez immédiatement la lecture de la réponse audio du modèle et supprimez de la file d’attente l’audio restant pour cet élément. Notez la durée, en millisecondes, de la partie de l’élément que l’utilisateur a effectivement entendue. - Envoyez un événement
conversation.item.truncateavec l’identifiant de l’élément interrompu, l’index du contenu audio et la durée déjà lue dansaudio_end_ms. La partie non lue est ainsi retirée de la conversation. - Lorsque la VAD interrompt une réponse en cours, le serveur l’annule et envoie
response.doneavecresponse.statusdéfini surcancelled. Utilisez cet événement pour constater la fin de la réponse. Arrêtez la lecture et envoyez l’événement de troncature sans attendre de le recevoir.
Voici un exemple :
{
"type": "conversation.item.truncate",
"item_id": "item_1234",
"content_index": 0,
"audio_end_ms": 1500
}
Cet exemple tronque l’audio après 1,5 seconde de lecture.
Peut-on aussi tronquer la transcription ? Le modèle Realtime ne dispose pas d’assez d’informations pour aligner précisément le texte sur l’audio. L’événement conversation.item.truncate coupe donc l’audio à l’endroit indiqué et supprime la transcription textuelle de la partie non lue. Cette opération retire l’audio non lu, mais ne fournit pas de transcription tronquée.
Appuyer pour parler
Par défaut, la Realtime API utilise la détection d’activité vocale (VAD) pour déclencher les réponses du modèle à partir de l’entrée audio. Pour permettre à l’utilisateur d’appuyer sur une touche pour parler, vous pouvez désactiver la VAD et laisser l’application décider quand envoyer l’audio au modèle. Par exemple, enregistrez l’audio tant que l’utilisateur maintient la barre d’espace enfoncée et déclenchez une réponse lorsqu’il la relâche. Dans certaines applications, ce mode fonctionne particulièrement bien : l’utilisateur maîtrise les échanges, les erreurs de détection sont évitées et l’interface réagit rapidement puisqu’elle n’attend pas l’expiration du délai de la VAD.
La mise en œuvre diffère légèrement entre WebSockets et WebRTC. Avec une connexion WebSocket à la Realtime API, tous les événements sont transmis dans l’ordre sur un même canal. Avec WebRTC, l’audio et les événements de contrôle empruntent des canaux distincts.
WebSockets
Avec une connexion WebSocket, le client doit arrêter la lecture audio, gérer les interruptions et déclencher une nouvelle réponse. Procédez comme suit :
- Désactivez la VAD en indiquant
"turn_detection": nulldans un événementsession.update. - Lorsque l’utilisateur appuie sur la touche, lancez l’enregistrement audio côté client.
- Si le modèle génère une réponse, annulez-la en envoyant un événement
response.cancel. - Si une réponse audio du modèle est en cours de lecture, arrêtez immédiatement la lecture et envoyez un événement
conversation.item.truncatepour retirer de la conversation l’audio non lu.
- Si le modèle génère une réponse, annulez-la en envoyant un événement
- Lorsque l’utilisateur relâche la touche, envoyez un message
input_audio_buffer.appendcontenant l’enregistrement pour ajouter ce nouvel audio au tampon d’entrée. - Envoyez un événement
input_audio_buffer.commitpour valider l’audio écrit dans le tampon d’entrée et lancer sa transcription, si elle est activée. - Déclenchez ensuite une réponse avec un événement
response.create.
WebRTC et SIP
Avec WebRTC, la procédure est similaire, mais vous devez vider explicitement le tampon audio d’entrée. Procédez comme suit :
- Désactivez la VAD en indiquant
"turn_detection": nulldans un événementsession.update. - Lorsque l’utilisateur appuie sur la touche, envoyez un événement
input_audio_buffer.clearpour effacer l’entrée audio précédente.- Si le modèle génère une réponse, annulez-la en envoyant un événement
response.cancel. - Si une réponse audio du modèle est en cours de lecture, envoyez un événement
output_audio_buffer.clearpour effacer l’audio non lu. Cet événement tronque également la conversation.
- Si le modèle génère une réponse, annulez-la en envoyant un événement
- Lorsque l’utilisateur relâche la touche, envoyez un événement
input_audio_buffer.commitpour valider l’audio écrit dans le tampon d’entrée et lancer sa transcription, si elle est activée. - Déclenchez ensuite une réponse avec un événement
response.create.