Aller au contenu

Streaming

Passez stream: true pour recevoir les tokens au fil de leur génération, sous forme d'événements server-sent terminés par data: [DONE].

Avec "stream": true, POST /chat/completions renvoie la réponse token par token sous forme d'événements server-sent, au même format qu'OpenAI. Vos utilisateurs voient le texte apparaître immédiatement au lieu d'attendre la réponse complète.

Activer le streaming

Ajoutez stream: true au corps de la requête ; rien d'autre ne change. Avec curl, passez -N pour désactiver la mise en tampon de la sortie.

curl -N https://promptix.tn/api/v1/chat/completions \  -H "Authorization: Bearer $PROMPTIX_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "model": "openai/gpt-6-luna",    "stream": true,    "messages": [{ "role": "user", "content": "Write a short poem about Djerba." }]  }'

Format des événements

La réponse a le type Content-Type: text/event-stream. Chaque événement tient sur une ligne qui commence par data: , suivie d'un objet JSON chat.completion.chunk, puis d'une ligne vide. Le flux se termine par la chaîne littérale data: [DONE].

text/event-streamtext
data: {"id":"gen-1758880023-kQ3vX8mZpL2nR7tW","object":"chat.completion.chunk","created":1758880023,"model":"openai/gpt-6-luna","provider":"OpenAI","choices":[{"index":0,"delta":{"role":"assistant","content":"Palm"},"finish_reason":null}]} data: {"id":"gen-1758880023-kQ3vX8mZpL2nR7tW","object":"chat.completion.chunk","created":1758880023,"model":"openai/gpt-6-luna","provider":"OpenAI","choices":[{"index":0,"delta":{"content":" trees lean"},"finish_reason":null}]} data: {"id":"gen-1758880023-kQ3vX8mZpL2nR7tW","object":"chat.completion.chunk","created":1758880023,"model":"openai/gpt-6-luna","provider":"OpenAI","choices":[{"index":0,"delta":{"content":""},"finish_reason":"stop"}]} data: {"id":"gen-1758880023-kQ3vX8mZpL2nR7tW","object":"chat.completion.chunk","created":1758880023,"model":"openai/gpt-6-luna","provider":"OpenAI","choices":[],"usage":{"prompt_tokens":15,"completion_tokens":48,"total_tokens":63}} data: [DONE]
  • choices[0].delta.content contient le nouveau texte. Le premier chunk porte aussi delta.role.
  • Le chunk dont finish_reason n'est pas nul marque la fin de la réponse (stop, length, tool_calls).
  • Promptix demande toujours l'usage : le dernier chunk avant [DONE] contient un objet usage avec le nombre de tokens. Son tableau choices peut être vide, prévoyez-le.
  • Tous les chunks ont le même id. Conservez-le pour consulter le coût de la requête.
  • Les appels d'outils arrivent par fragments dans delta.tool_calls, comme chez OpenAI.

Lire le flux vous-même

Les SDK OpenAI analysent les événements pour vous. Sans SDK, lisez le corps ligne par ligne, gardez les lignes incomplètes dans un tampon et arrêtez-vous à [DONE].

JavaScript

stream.mjsJavaScript
// Plain fetch (Node 18+, Deno, Bun, edge runtimes): no SDK requiredconst res = await fetch('https://promptix.tn/api/v1/chat/completions', {  method: 'POST',  headers: {    Authorization: `Bearer ${process.env.PROMPTIX_API_KEY}`,    'Content-Type': 'application/json',  },  body: JSON.stringify({    model: 'openai/gpt-6-luna',    stream: true,    messages: [{ role: 'user', content: 'Write a short poem about Djerba.' }],  }),}); // Errors raised before the stream starts (401, 402, 400) come back as plain JSONif (!res.ok) {  const body = await res.json();  throw new Error(typeof body.error === 'string' ? body.error : body.error?.message);} const reader = res.body.getReader();const decoder = new TextDecoder();let buffer = ''; outer: while (true) {  const { done, value } = await reader.read();  if (done) break;  buffer += decoder.decode(value, { stream: true });   const lines = buffer.split('\n');  buffer = lines.pop() ?? ''; // keep the incomplete line for the next read   for (const line of lines) {    if (!line.startsWith('data: ')) continue;    const data = line.slice(6);    if (data === '[DONE]') break outer;     const chunk = JSON.parse(data);    if (chunk.error) throw new Error(chunk.error.message);    process.stdout.write(chunk.choices[0]?.delta?.content ?? '');  }}

Python

stream.pyPython
import jsonimport osimport requests with requests.post(    "https://promptix.tn/api/v1/chat/completions",    headers={"Authorization": f"Bearer {os.environ['PROMPTIX_API_KEY']}"},    json={        "model": "openai/gpt-6-luna",        "stream": True,        "messages": [{"role": "user", "content": "Write a short poem about Djerba."}],    },    stream=True,    timeout=300,) as res:    res.raise_for_status()    for line in res.iter_lines(decode_unicode=True):        if not line or not line.startswith("data: "):            continue        data = line[len("data: "):]        if data == "[DONE]":            break        chunk = json.loads(data)        if "error" in chunk:            raise RuntimeError(chunk["error"]["message"])        if chunk["choices"]:            print(chunk["choices"][0]["delta"].get("content") or "", end="", flush=True)

Erreurs pendant le streaming

Les erreurs d'authentification, de validation, de solde et de plafond de clé surviennent avant le début du streaming : elles sont renvoyées sous forme de réponse JSON classique avec le code correspondant (400, 401, 402). Voir Erreurs et limites.

Une fois le flux commencé, le statut HTTP est déjà 200. Si le fournisseur échoue à ce moment-là, Promptix envoie un dernier événement contenant un objet error et ferme le flux sans [DONE] :

text/event-streamtext
data: {"error":{"message":"Streaming failed","type":"stream_error"}}

Considérez un flux terminé sans [DONE] comme incomplet. Les SDK OpenAI pour Python et Node lèvent une APIError lorsqu'ils reçoivent cet événement.

Facturation et déconnexions

Une requête en streaming coûte autant qu'une requête classique : vous payez les tokens d'entrée et de sortie indiqués dans le chunk usage final.

Fermer la connexion n'annule pas la génération

Si votre client se déconnecte en cours de flux, le modèle continue de générer jusqu'à la fin et les tokens produits sont facturés. Utilisez max_tokens pour borner le coût des longues réponses.

Les flux sont servis avec Content-Type: text/event-stream et Cache-Control: no-cache, et envoyés token par token. Si vous placez un proxy (Nginx, un CDN) devant votre propre backend, désactivez-y la mise en tampon des réponses, sinon les tokens arriveront par paquets. Une requête, streaming compris, peut durer jusqu'à 300 secondes.