Aller au contenu principal

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é par max_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_reasonlength signifie une réponse incomplète
  • usage dé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 id est indispensable pour le debugging et le support technique