Réponse et usage
Mis à jour le 29 juillet 2026
Comprendre ce que l’API vous renvoie
La plupart des développeurs qui découvrent l’API lisent une seule ligne de la réponse — le texte généré — et ignorent tout le reste. C’est un tort. Chaque appel à /v1/chat/completions retourne un objet JSON riche en informations : c’est là que vous trouverez de quoi suivre vos coûts, détecter une réponse tronquée avant qu’elle n’arrive chez l’utilisateur, et retrouver un appel précis quand vous ouvrez un ticket au support.
Anatomie complète de la réponse
Commençons par les métadonnées, celles qu’on regarde rarement et qu’on regrette de ne pas avoir enregistrées le jour d’un incident :
from mistralai import Mistral
import os
client = Mistral(api_key=os.getenv("MISTRAL_API_KEY"))
response = client.chat.complete(
model="mistral-large-latest",
messages=[{"role": "user", "content": "Qu'est-ce que le machine learning ?"}],
max_tokens=500
)
# Accéder aux métadonnées
print(f"ID de la requête : {response.id}")
print(f"Modèle utilisé : {response.model}")
print(f"Créé le : {response.created}")
Le champ id est un identifiant unique attribué à chaque requête : c’est la référence que vous communiquerez au support technique, et celle qui vous permettra de relier une plainte utilisateur à un appel précis dans vos logs. Le champ model confirme le modèle effectivement utilisé, information loin d’être anodine quand vous appelez un alias -latest et que le comportement change du jour au lendemain sans que votre code ait bougé.
Le tableau choices
Le cœur de la réponse se trouve dans choices. Par défaut, ce tableau contient un seul élément, et c’est de lui que vous extrayez à la fois le texte et le verdict sur la manière dont la génération s’est terminée :
choice = response.choices[0]
# Le message généré
print(choice.message.role) # "assistant"
print(choice.message.content) # Le texte de la réponse
# L'index de cette réponse dans le tableau
print(choice.index) # 0
# La raison d'arrêt
print(choice.finish_reason) # "stop", "length", ou "tool_calls"
Si vous demandez plusieurs réponses avec le paramètre n, que nous verrons à la leçon 15, choices contiendra plusieurs éléments et l’index reprendra tout son sens.
Les valeurs de finish_reason
Ce champ conditionne la fiabilité de votre application. Trois valeurs sont possibles. stop signale que le modèle a terminé naturellement sa réponse : c’est le cas normal, celui où vous pouvez afficher le contenu sans réserve. length signifie que la génération a été coupée par max_tokens, donc que la réponse est potentiellement incomplète — un résumé amputé de sa conclusion, une liste qui s’arrête au septième élément. tool_calls, enfin, indique que le modèle demande l’exécution d’un outil externe dans le cadre du function calling, et que la balle est dans votre camp.
Chacun de ces cas appelle un traitement distinct, qu’il vaut mieux écrire une fois pour toutes :
response = client.chat.complete(
model="mistral-large-latest",
messages=[{"role": "user", "content": "Rédigez un rapport détaillé."}],
max_tokens=200
)
choice = response.choices[0]
if choice.finish_reason == "stop":
# Réponse complète — traitement normal
resultat = choice.message.content
elif choice.finish_reason == "length":
# Réponse tronquée — relancer avec plus de tokens ou avertir
print("Attention : la réponse a été tronquée.")
# Option : relancer avec max_tokens plus élevé
# Option : demander au modèle de continuer
elif choice.finish_reason == "tool_calls":
# Le modèle demande un appel d'outil
for tool_call in choice.message.tool_calls:
print(f"Outil demandé : {tool_call.function.name}")
Remarquez que la branche length ne se contente pas de laisser passer : soit vous relancez avec un plafond plus élevé, soit vous demandez au modèle de continuer, soit vous prévenez l’utilisateur. Ce qu’il ne faut jamais faire, c’est afficher le texte tronqué comme s’il était complet.
L’objet usage — suivre vos coûts
Le champ usage décompte les tokens consommés par la requête, et il le fait séparément pour l’entrée et pour la sortie :
usage = response.usage
print(f"Tokens d'entrée : {usage.prompt_tokens}")
print(f"Tokens de sortie : {usage.completion_tokens}")
print(f"Total : {usage.total_tokens}")
Cette distinction n’est pas cosmétique : les modèles Mistral ont des tarifs différenciés entrée/sortie, et une fonctionnalité qui envoie un long contexte pour obtenir trois mots n’a pas du tout le même profil de coût qu’une rédaction longue à partir d’une consigne brève. Le calcul tient en quelques lignes :
# Tarifs hypothétiques par million de tokens (vérifiez les tarifs actuels)
TARIF_INPUT = 2.0 # $/M tokens en entrée
TARIF_OUTPUT = 6.0 # $/M tokens en sortie
cout_input = (usage.prompt_tokens / 1_000_000) * TARIF_INPUT
cout_output = (usage.completion_tokens / 1_000_000) * TARIF_OUTPUT
cout_total = cout_input + cout_output
print(f"Coût de cet appel : ${cout_total:.6f}")
Un appel isolé coûte une fraction de centime, ce qui rassure à tort. Multipliez par le trafic réel d’une application et la question devient : quelle fonctionnalité consomme quoi ? Pour y répondre, agrégez les métriques d’usage dès le premier déploiement, en journalisant chaque appel avec le nom de la fonctionnalité qui l’a déclenché :
import json
from datetime import datetime
def log_usage(response, endpoint_name: str):
"""Enregistre les métriques d'usage pour chaque appel API."""
log_entry = {
"timestamp": datetime.utcnow().isoformat(),
"endpoint": endpoint_name,
"model": response.model,
"prompt_tokens": response.usage.prompt_tokens,
"completion_tokens": response.usage.completion_tokens,
"total_tokens": response.usage.total_tokens,
"finish_reason": response.choices[0].finish_reason,
"request_id": response.id
}
with open("api_usage.jsonl", "a") as f:
f.write(json.dumps(log_entry) + "\n")
# Utilisation
response = client.chat.complete(
model="mistral-large-latest",
messages=[{"role": "user", "content": "Bonjour"}]
)
log_usage(response, "chatbot_principal")
Un fichier JSONL de ce type, relu au bout de deux semaines, vous dira sans discussion possible quel écran de votre produit pèse le plus lourd sur la facture — et combien d’appels se terminent en length.
Le contenu de la réponse
Le champ content du message assistant peut prendre deux formes, exactement comme côté requête :
# Forme simple — chaîne de caractères
content = response.choices[0].message.content
# "Le machine learning est une branche de l'intelligence artificielle..."
# Forme structurée — liste de blocs (rare en chat standard)
# [{"type": "text", "text": "..."}]
En pratique, pour les appels Chat Completions classiques, content est toujours une chaîne de caractères ; la forme structurée est réservée aux réponses multimodales.
Mise en pratique
Rassemblons tout cela dans une fonction utilitaire, celle que vous copierez d’un projet à l’autre. Elle assemble les messages, effectue l’appel et retourne un dictionnaire qui expose autant les métriques que le texte :
from mistralai import Mistral
def chat_complete(
client: Mistral,
prompt: str,
system: str = "",
model: str = "mistral-large-latest",
max_tokens: int = 1000
) -> dict:
"""Appel Chat Completions avec gestion d'erreur et métriques."""
messages = []
if system:
messages.append({"role": "system", "content": system})
messages.append({"role": "user", "content": prompt})
response = client.chat.complete(
model=model,
messages=messages,
max_tokens=max_tokens
)
return {
"content": response.choices[0].message.content,
"finish_reason": response.choices[0].finish_reason,
"tokens_in": response.usage.prompt_tokens,
"tokens_out": response.usage.completion_tokens,
"model": response.model,
"id": response.id
}
Le point important tient dans ce que la fonction retourne : jamais le texte seul. En remontant finish_reason, les compteurs de tokens et l’identifiant de requête jusqu’à l’appelant, vous rendez impossible l’erreur la plus commune — traiter une réponse coupée comme une réponse valide.
Points clés à retenir
- La réponse contient
choices(contenu),usage(tokens), et des métadonnées (id,model) - Vérifiez toujours
finish_reason—lengthsignifie une réponse incomplète usagedécompte séparément les tokens d’entrée et de sortie pour un suivi précis des coûts- Mettez en place un logging des métriques dès le premier déploiement en production
- Le champ
idest indispensable pour le debugging et le support technique