Streaming SSE
Réponses en temps réel avec Server-Sent Events
Par défaut, l’API Chat Completions attend que le modèle ait terminé sa réponse complète avant de vous la renvoyer. Pour les réponses longues, cela peut signifier plusieurs secondes d’attente. Le streaming résout ce problème en envoyant les tokens au fur et à mesure de leur génération.
Le protocole utilisé est Server-Sent Events (SSE), un standard HTTP qui permet au serveur de pousser des données vers le client en continu sur une connexion persistante.
Streaming vs non-streaming
Mode non-streaming (par défaut)
Le client envoie la requête et attend la réponse complète :
Client ──── requête ────► Serveur
(attend 3-10s)
Client ◄── réponse complète ── Serveur
Avantages : réponse atomique, facile à traiter, un seul objet JSON. Inconvénient : latence perçue élevée — l’utilisateur ne voit rien pendant la génération.
Mode streaming
Le serveur envoie les tokens un par un (ou par petits groupes) :
Client ──── requête ────► Serveur
Client ◄── chunk 1 ─────── Serveur (après ~200ms)
Client ◄── chunk 2 ─────── Serveur (après ~250ms)
Client ◄── chunk 3 ─────── Serveur (après ~300ms)
...
Client ◄── [DONE] ──────── Serveur
Avantages : latence perçue réduite drastiquement, meilleure expérience utilisateur. Inconvénient : reconstruction du message côté client, gestion de l’état plus complexe.
Le protocole SSE en détail
Chaque événement SSE a la forme suivante dans le flux HTTP :
data: {"id":"cmpl-abc","choices":[{"delta":{"content":"Bon"}}],"usage":null}
data: {"id":"cmpl-abc","choices":[{"delta":{"content":"jour"}}],"usage":null}
data: {"id":"cmpl-abc","choices":[{"delta":{"content":" !"}}],"usage":null}
data: [DONE]
Points importants :
- Chaque ligne commence par
data:suivi d’un objet JSON - Le champ
delta(et nonmessage) contient le fragment de texte - Le dernier événement est
data: [DONE]pour signaler la fin du flux - Les lignes vides séparent les événements
Activer le streaming en Python
Avec le SDK Mistral, le streaming s’active en appelant chat.stream() au lieu de chat.complete() :
from mistralai import Mistral
import os
client = Mistral(api_key=os.getenv("MISTRAL_API_KEY"))
stream = client.chat.stream(
model="mistral-large-latest",
messages=[
{"role": "user", "content": "Expliquez le fonctionnement d'un moteur électrique."}
]
)
for chunk in stream:
content = chunk.data.choices[0].delta.content
if content:
print(content, end="", flush=True)
print() # Retour à la ligne final
Le flush=True force l’affichage immédiat de chaque fragment dans le terminal.
Structure d’un chunk de streaming
Chaque chunk reçu diffère légèrement d’une réponse non-streaming :
# Non-streaming : response.choices[0].message.content
# Streaming : chunk.data.choices[0].delta.content
# Le premier chunk contient souvent le rôle
# chunk.data.choices[0].delta.role == "assistant"
# Les chunks suivants contiennent le texte par fragments
# chunk.data.choices[0].delta.content == "Bon"
# Le dernier chunk contient finish_reason
# chunk.data.choices[0].finish_reason == "stop"
Reconstruire la réponse complète
En streaming, vous devez accumuler les fragments pour obtenir la réponse finale :
from mistralai import Mistral
import os
client = Mistral(api_key=os.getenv("MISTRAL_API_KEY"))
stream = client.chat.stream(
model="mistral-large-latest",
messages=[{"role": "user", "content": "Listez 5 langages de programmation."}]
)
full_response = ""
finish_reason = None
for chunk in stream:
delta = chunk.data.choices[0].delta
if delta.content:
full_response += delta.content
print(delta.content, end="", flush=True)
if chunk.data.choices[0].finish_reason:
finish_reason = chunk.data.choices[0].finish_reason
print(f"\n\nFinish reason : {finish_reason}")
print(f"Réponse complète ({len(full_response)} caractères)")
Quand utiliser le streaming
| Cas d’usage | Recommandation |
|---|---|
| Chatbot interactif | Streaming (meilleure UX) |
| Traitement batch | Non-streaming (plus simple) |
| API backend pour frontend | Streaming (SSE vers le navigateur) |
| Pipeline de données | Non-streaming (traitement atomique) |
| Extraction structurée | Non-streaming (parsing JSON plus fiable) |
Points clés à retenir
- Le streaming réduit la latence perçue en envoyant les tokens au fil de la génération
- Le protocole SSE est un standard HTTP simple et robuste
- En streaming, utilisez
delta.contentau lieu demessage.content - Accumulez les fragments côté client pour reconstruire la réponse complète
- Privilégiez le streaming pour les interfaces utilisateur, le non-streaming pour le traitement automatisé