Recevoir les réponses en SSE
Guide pratique - consommer le flux Server-Sent Events du canal API depuis un backend Node.js ou un client navigateur.
Utilisez ce guide lorsque vous souhaitez afficher la réponse IA de manière incrémentale au fur et à mesure de sa génération, plutôt que d'attendre la réponse complète.
Résultat attendu : votre client lit le flux SSE et traite chaque delta de texte à sa réception.
Quand utiliser le streaming
Définissez "stream": true dans le corps de la requête lorsque :
- Vous construisez une interface de chat qui affiche le texte au fur et à mesure de son apparition.
- Votre flow produit des réponses longues et vous souhaitez réduire la latence perçue.
- Vous avez besoin de
run_idouconversation_idavant la fin de la réponse (ils arrivent dans le premier événementdata-run).
Pour les automatisations courtes et synchrones (traitement en lot, webhooks vers d'autres systèmes), utilisez "stream": false et lisez la réponse JSON.
Effectuer une requête en streaming
POST /public/v1/api/execute
Authorization: Bearer <clé>
Content-Type: application/json
{"payload":"Expliquez l'impact de ce changement de code.","stream":true}Le serveur répond avec Content-Type: text/event-stream; charset=utf-8 et commence à émettre des événements.
Séquence des événements
Chaque réponse en streaming suit cet ordre :
data: {"type":"data-run","data":{"run_id":"9c1d…","conversation_id":"b1f2…"}}
data: {"type":"text-start","id":"9c1d…"}
data: {"type":"text-delta","id":"9c1d…","delta":"Le changement "}
data: {"type":"text-delta","id":"9c1d…","delta":"introduit une rupture d'API."}
data: {"type":"text-end","id":"9c1d…"}
data: {"type":"finish","finishReason":"stop"}Si l'exécution échoue après le début du streaming :
data: {"type":"error","errorText":"Echec d'exécution : timeout"}Aucun événement finish ne suit un error. Le statut HTTP est déjà 200 à ce stade ; l'événement error est le signal d'échec.
Analyser le flux en Node.js
async function streamApiChannel(
payload: string,
apiKey: string,
onDelta: (delta: string) => void
): Promise<{ runId: string; conversationId: string }> {
const body = JSON.stringify({ payload, stream: true });
const response = await fetch('https://api.example.com/public/v1/api/execute', {
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
},
body,
});
if (!response.ok) {
const err = await response.json();
throw new Error(`${err.error?.code}: ${err.error?.message}`);
}
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let buffer = '';
let runId = '';
let conversationId = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const chunks = buffer.split('\n\n');
buffer = chunks.pop() ?? '';
for (const chunk of chunks) {
if (!chunk.startsWith('data: ')) continue;
const event = JSON.parse(chunk.slice(6));
switch (event.type) {
case 'data-run':
runId = event.data.run_id;
conversationId = event.data.conversation_id;
break;
case 'text-delta':
onDelta(event.delta);
break;
case 'error':
throw new Error(`Erreur de flux : ${event.errorText}`);
case 'finish':
return { runId, conversationId };
}
}
}
return { runId, conversationId };
}Analyser le flux dans un navigateur
const response = await fetch('/public/v1/api/execute', {
method: 'POST',
headers: {
Authorization: 'Bearer ' + publicKey,
'Content-Type': 'application/json',
Origin: window.location.origin,
},
body: JSON.stringify({ payload: userMessage, stream: true }),
});
const reader = response.body!.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
for (const chunk of buffer.split('\n\n')) {
if (!chunk.startsWith('data: ')) continue;
const event = JSON.parse(chunk.slice(6));
if (event.type === 'text-delta') {
outputDiv.textContent += event.delta;
}
}
buffer = buffer.endsWith('\n\n') ? '' : (buffer.split('\n\n').pop() ?? '');
}Les appelants navigateur doivent utiliser une clé nx_pub_ et le canal doit avoir l'origine de la page dans sa liste d'origines autorisées.
Gestion de la déconnexion client
Lorsque le client ferme la connexion, le serveur annule la tâche d'exécution en arrière-plan. Aucune donnée n'est perdue côté serveur ; l'entrée de conversation n'est pas corrompue.
Mise en mémoire tampon par proxy
Si vous routez les requêtes via nginx ou un autre proxy inverse, assurez-vous que la mise en mémoire tampon des réponses est désactivée pour le point d'accès de streaming :
location /public/v1/api/ {
proxy_buffering off;
proxy_pass http://core_api;
}Le serveur définit X-Accel-Buffering: no automatiquement, mais tous les proxys ne respectent pas cet en-tête.
Référence
Pour les définitions complètes des types d'événements et la sémantique des erreurs, voir la référence d'API - section Réponse SSE en streaming.
Signer les requêtes avec HMAC
Guide pratique - ajouter la signature HMAC-SHA256 aux appels de canal API serveur à serveur pour l'intégrité du corps et la protection contre le rejeu.
Dépanner les erreurs d'authentification et de signature
Guide pratique - diagnostiquer et résoudre les erreurs 401, 403 et de signature lors de l'appel du canal API.