Streaming
Définissez "stream": true et le même point d'accès renvoie la réponse de façon incrémentale via des événements envoyés par le serveur, plutôt que d'attendre que tout soit terminé.
Pourquoi utiliser le streaming
Une requête non-streaming bloque jusqu'à ce que le modèle ait fini de générer la réponse entière, puis la renvoie d'un seul bloc. Pour une réponse courte, cela passe rarement inaperçu. Pour une réponse plus longue, cela signifie attendre plusieurs secondes (parfois davantage) devant rien avant que le moindre texte n'apparaisse. Le streaming corrige cela en envoyant la réponse morceau par morceau, au fur et à mesure que le modèle la produit, de sorte qu'un client peut commencer à afficher du texte en une fraction de seconde après l'envoi de la requête plutôt que d'attendre l'ensemble. C'est la différence entre une interface de chat qui paraît instantanée et une autre qui semble se figer à chaque message. C'est une option activable, sans coût supplémentaire à l'usage.
L'activer
{
"model": "tai-4.1",
"messages": [{"role": "user", "content": "1'den 5'e kadar say"}],
"stream": true
}
Le Content-Type de la réponse devient text/event-stream au lieu de application/json. Chaque ligne d'événement est préfixée par data: , et la connexion reste ouverte, envoyant un événement par fragment de nouveau contenu, jusqu'à ce que le flux se termine par une ligne littérale data: [DONE].
La structure d'un fragment
La plupart des fragments transportent un petit morceau de texte nouvellement généré dans choices[0].delta.content :
data: {"id": "chatcmpl-b94...", "object": "chat.completion.chunk", "created": 1788960276, "model": "tai-4.0.6P", "choices": [{"index": 0, "delta": {"content": "1"}, "finish_reason": null}]}
data: {"id": "chatcmpl-b94...", "object": "chat.completion.chunk", "created": 1788960276, "model": "tai-4.0.6P", "choices": [{"index": 0, "delta": {"content": ", 2"}, "finish_reason": null}]}
Concaténer chaque valeur delta.content dans l'ordre, à travers tous les fragments, reconstitue le texte complet de la réponse. Construire cette concaténation est le travail du client ; l'API n'envoie à aucun moment le texte accumulé, seulement le morceau incrémental que chaque fragment ajoute.
Le fragment de fin
Le dernier fragment porteur de contenu contient un delta vide et un véritable finish_reason ("stop", ou "tool_calls" si le modèle demande à appeler un outil, voir Appel d'outils) :
data: {"id": "chatcmpl-b94...", "object": "chat.completion.chunk", "created": 1788960276, "model": "tai-4.0.6P", "choices": [{"index": 0, "delta": {}, "finish_reason": "stop"}]}
Le fragment d'usage
Après le fragment de fin, un fragment supplémentaire est envoyé avec un tableau choices vide et un objet usage renseigné, de même structure que le champ usage de la réponse non-streaming :
data: {"id": "chatcmpl-b94...", "object": "chat.completion.chunk", "created": 1788960276, "model": "tai-4.0.6P", "choices": [], "usage": {"prompt_tokens": 2, "completion_tokens": 3, "total_tokens": 5}}
data: [DONE]
Un client qui ne s'intéresse qu'au texte visible peut ignorer sans risque tout fragment dont le tableau choices est vide ; un client qui souhaite afficher l'usage de tokens ou le coût estimé après coup doit spécifiquement rechercher ce fragment plutôt que de supposer que le fragment de fin le porte.
Lire un flux en code
# Python, avec le paquet officiel openai stream = client.chat.completions.create( model="tai-4.1", messages=[{"role": "user", "content": "Kısa bir şiir yaz"}], stream=True, ) for chunk in stream: if chunk.choices and chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="", flush=True)
Les SDK officiels d'OpenAI (Python et Node) savent déjà analyser cette structure d'événements, puisque c'est la même que celle utilisée par leur propre API ; rien de spécifique à tAI n'est requis côté client, hormis pointer base_url vers https://api.artfical.com/v1, comme expliqué sur la page Authentification.
Un flux se termine toujours proprement
Même si quelque chose se passe mal en cours de route, échec de la génération, dépassement de délai interne d'une requête, quelle qu'en soit la cause, le flux se termine tout de même par un signal visible plutôt que par une simple coupure de connexion sans explication. Dans ce cas, vous verrez un fragment de contenu contenant une courte notice entre crochets, un fragment de fin avec "finish_reason": "stop", puis [DONE], la même fin bien formée qu'un flux réussi normal. Un client doit toujours considérer l'arrivée de [DONE] comme la fin définitive du tour, et ne doit pas non plus supposer qu'un flux qui se termine sans lui a réussi ; une connexion coupée sans [DONE] signifie qu'il faut considérer la requête comme ayant échoué et réessayer, tout comme vous le feriez pour une requête non-streaming ayant dépassé son délai.