Réponse et usage
Comprendre ce que l’API vous renvoie
Chaque appel à /v1/chat/completions retourne un objet JSON riche en informations. Savoir décortiquer cette réponse est essentiel pour construire des applications robustes, suivre vos coûts, et détecter les anomalies.
Anatomie complète de la réponse
Voici la structure détaillée d’une réponse standard :
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}")
L’objet response contient plusieurs champs de métadonnées. Le champ id est un identifiant unique pour chaque requête, utile pour le debugging et le support technique. Le champ model confirme le modèle effectivement utilisé (important si vous utilisez un alias comme -latest).
Le tableau choices
Le cœur de la réponse se trouve dans choices. Par défaut, ce tableau contient un seul élément :
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 (leçon 15), choices contiendra plusieurs éléments.
Les valeurs de finish_reason
Ce champ est crucial pour la fiabilité de votre application :
stop— Le modèle a terminé naturellement sa réponse. C’est le cas normal.length— Le modèle a été coupé parmax_tokens. La réponse est potentiellement incomplète.tool_calls— Le modèle demande l’exécution d’un outil externe (function calling).
Voici comment gérer chaque cas en production :
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}")
L’objet usage — suivre vos coûts
Le champ usage décompte les tokens consommés par la requête :
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}")
Calculer le coût d’un appel
Les modèles Mistral ont des tarifs différenciés entrée/sortie. Voici un calcul typique :
# 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}")
Surveiller la consommation en production
Pour une application en production, agrégez les métriques d’usage :
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")
Le contenu de la réponse
Le champ content du message assistant peut prendre deux formes :
# 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 utilisée pour les réponses multimodales.
Mise en pratique
Voici une fonction utilitaire complète qui encapsule l’appel API avec gestion d’erreur et logging :
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
}
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