Streaming
Recevez progressivement la sortie du modèle et gérez les événements, les incréments, la fin et les interruptions.
Le streaming permet au client de recevoir des incréments avant que le modèle ne termine sa réponse. Il convient aux interfaces de chat, aux textes longs et aux applications qui affichent rapidement les premiers résultats. Le client doit cependant gérer les limites des événements, l’assemblage du contenu, l’état de fin et les interruptions de connexion.
Le streaming change le mode de retour, pas la tâche
Une même tâche peut généralement retourner une réponse classique ou en streaming. Commencez par une requête classique, puis activez le streaming pour distinguer les erreurs de paramètres des erreurs d’analyse du flux.
Vérifications préalables
- Vérifiez dans le Catalogue de modèles que le modèle cible prend en charge le protocole choisi et le streaming.
- Utilisez l’endpoint et la structure d’événements propres au protocole, plutôt qu’un analyseur qui tente de deviner tous les protocoles.
- Vérifiez que le client, le proxy inverse et la passerelle ne mettent pas toute la réponse en tampon avant de la transmettre.
Les champs complets sont décrits dans les références Chat Completions, Responses, Anthropic Messages et Gemini Stream Generate Content.
Envoyer une requête de streaming minimale
Voici comment activer le streaming avec Chat Completions :
curl --no-buffer 'https://api.agtcloud.cn/v1/chat/completions' \
-H 'Content-Type: application/json' \
-H 'Authorization: Bearer your-api-key' \
--data '{
"model": "MODEL_ID",
"messages": [
{"role": "user", "content": "Expliquez le backoff exponentiel en trois points."}
],
"stream": true
}'--no-buffer permet à cURL d’afficher immédiatement les données reçues, sans modifier la structure des événements côté serveur.
Lire par événement, et non par paquet réseau
Les octets transmis au client à chaque lecture HTTP ne correspondent pas nécessairement à un événement complet. Un objet JSON peut être réparti sur plusieurs blocs, et plusieurs événements peuvent arriver dans un même bloc.
Pour SSE, repérez d’abord les événements complets à l’aide des lignes vides, puis lisez leur contenu data: :
const response = await fetch('https://api.agtcloud.cn/v1/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer your-api-key',
},
body: JSON.stringify({
model: 'MODEL_ID',
messages: [{ role: 'user', content: 'Expliquez le backoff exponentiel.' }],
stream: true,
}),
});
if (!response.ok || !response.body) {
throw new Error(`Échec de la requête : ${response.status}`);
}
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { value, done } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const events = buffer.split('\n\n');
buffer = events.pop() ?? '';
for (const event of events) {
const data = event
.split('\n')
.filter((line) => line.startsWith('data:'))
.map((line) => line.slice(5).trimStart())
.join('\n');
if (!data || data === '[DONE]') continue;
const chunk = JSON.parse(data);
const delta = chunk.choices?.[0]?.delta?.content;
if (delta) process.stdout.write(delta);
}
}En production, gérez également l’annulation, les délais de lecture, les signaux de maintien de connexion, les événements non JSON et le tampon restant à la fin du flux.
Structures de streaming des quatre protocoles
Chat retourne généralement des Chat Completion Chunks. Le texte se trouve dans choices[].delta.content, les paramètres d’outils peuvent figurer dans choices[].delta.tool_calls, et la raison de fin dans finish_reason. Le flux peut se terminer par [DONE].
L’existence d’un endpoint ne signifie pas que tous les modèles retournent tous ces types de contenu. Implémentez uniquement les branches utilisées par l’application et effectivement retournées par le modèle cible.
Assembler le texte et les paramètres d’outils
- Assemblez séparément les incréments de texte par index de candidat, d’élément de sortie ou de Content Block.
- Les noms d’outils, identifiants d’appel et paramètres peuvent arriver en plusieurs fois ; prévoyez un tampon par appel.
- Analysez les paramètres d’outils comme du JSON uniquement lorsqu’ils sont complets ; ne les exécutez pas bloc par bloc.
- Utilisez des tampons distincts pour les résumés de raisonnement, les Thinking Blocks et la réponse finale.
- Si l’Usage arrive à la fin, continuez la lecture jusqu’à l’événement de fin.
Distinguer réussite, échec et interruption
| Situation | Traitement côté client |
|---|---|
| Événement de fin défini par le protocole reçu | Conserver le résultat complet et l’Usage, puis marquer la réussite |
| Événement d’erreur ou d’échec reçu | Conserver le contenu déjà affiché et marquer la requête comme échouée |
| Connexion fermée prématurément | Marquer la réponse comme incomplète, sans présenter le texte reçu comme une réponse complète |
| Arrêt demandé par l’utilisateur | Annuler la lecture et afficher « Génération arrêtée » |
| Aucun nouvel événement pendant une longue période | Annuler selon la politique de délai et conserver les informations de diagnostic nécessaires |
Après avoir affiché une partie du contenu, ne relancez pas indéfiniment la requête de streaming. Une nouvelle tentative peut produire une réponse différente ou répéter des appels d’outils et d’autres effets de bord.
Vérifications avant la mise en production
- La page affiche le texte progressivement, sans attendre la fermeture de la connexion.
- Les caractères chinois et multioctets restent corrects lorsque les blocs sont découpés.
- Les paramètres d’outils sont assemblés par identifiant d’appel et validés avant exécution.
- L’annulation, la déconnexion, les délais dépassés et les erreurs serveur disposent d’états explicites.
- Les journaux ne contiennent pas de clés API, de prompts sensibles complets ni de paramètres d’outils non expurgés.
Poursuivez avec Tool Calling et Erreurs et dépannage.