Aller au contenu principal

Utiliser client.chat.parse()

La méthode parse() : le coeur des Custom Structured Outputs

La méthode client.chat.parse() est l’équivalent structuré de client.chat.complete(). Au lieu de retourner du texte libre ou du JSON brut, elle retourne directement un objet typé correspondant à votre modèle Pydantic.

Différence entre complete() et parse()

# JSON Mode — complete() retourne du texte JSON
response = client.chat.complete(
    model="mistral-large-latest",
    messages=messages,
    response_format={"type": "json_object"}
)
raw_json: str = response.choices[0].message.content
data = json.loads(raw_json)  # Parsing manuel

# Custom — parse() retourne un objet typé
response = client.chat.parse(
    model="mistral-large-latest",
    messages=messages,
    response_format=MonSchema
)
obj: MonSchema = response.choices[0].message.parsed  # Objet Pydantic
raw_json: str = response.choices[0].message.content   # JSON brut aussi disponible

La différence clé : parse() vous donne accès à .parsed, un objet Pydantic directement exploitable avec l’autocomplétion de votre IDE et la validation de types.

Exemple complet en Python

from mistralai import Mistral
from pydantic import BaseModel, Field
from typing import Optional

# 1. Définir le schéma
class BookInfo(BaseModel):
    title: str = Field(description="Titre du livre")
    authors: list[str] = Field(description="Liste des auteurs")
    year: Optional[int] = Field(default=None, description="Année de publication")
    genre: str = Field(description="Genre littéraire principal")
    summary: str = Field(description="Résumé en 2-3 phrases")

# 2. Créer le client
client = Mistral(api_key="votre-clé-api")

# 3. Appeler parse()
response = client.chat.parse(
    model="mistral-large-latest",
    messages=[
        {
            "role": "system",
            "content": "Tu es un bibliothécaire expert. Extrais les informations du livre mentionné."
        },
        {
            "role": "user",
            "content": "J'ai adoré '1984' de George Orwell. C'est un roman dystopique publié en 1949 qui décrit une société totalitaire sous surveillance permanente."
        }
    ],
    temperature=0
)

# 4. Utiliser l'objet parsé
book = response.choices[0].message.parsed
print(f"Titre : {book.title}")
print(f"Auteurs : {', '.join(book.authors)}")
print(f"Année : {book.year}")
print(f"Genre : {book.genre}")
print(f"Résumé : {book.summary}")

# 5. Le JSON brut est aussi disponible
raw = response.choices[0].message.content
print(f"\nJSON brut : {raw}")

Le system prompt auto-prepended

Quand vous utilisez parse(), Mistral ajoute automatiquement un préfixe au system prompt :

Your output should be an instance of a JSON object following this schema: { ... }

Ce préfixe contient le schéma JSON généré à partir de votre modèle Pydantic. Vous n’avez pas besoin de décrire le format dans votre prompt — le SDK le fait pour vous.

Cependant, ajouter des instructions contextuelles dans le system prompt reste recommandé :

response = client.chat.parse(
    model="mistral-large-latest",
    messages=[
        {
            "role": "system",
            # Pas besoin de décrire le format JSON — c'est automatique
            # Concentrez-vous sur le CONTEXTE et la QUALITÉ
            "content": """Tu es un analyste financier expert.
Sois précis dans les montants et les pourcentages.
Si une donnée n'est pas disponible dans le texte, utilise null."""
        },
        {"role": "user", "content": texte_rapport}
    ],
    response_format=RapportFinancier
)

Accéder aux données : parsed vs content

La réponse de parse() offre deux accès aux données :

.parsed — l’objet typé

book = response.choices[0].message.parsed

# Accès typé avec autocomplétion IDE
print(book.title)        # str
print(book.authors)      # list[str]
print(book.year)         # Optional[int]

# Itération sur les listes
for author in book.authors:
    print(f"  - {author}")

# Conversion en dictionnaire
book_dict = book.model_dump()

# Sérialisation JSON
book_json = book.model_dump_json(indent=2)

.content — le JSON brut

raw = response.choices[0].message.content

# C'est une string JSON, utile pour :
# - Le logging
# - Le stockage en base de données
# - L'envoi à une autre API

import json
data = json.loads(raw)

Gestion des erreurs

Même avec parse(), des erreurs peuvent survenir. Voici un pattern robuste :

from mistralai import Mistral
from mistralai.models import SDKError
from pydantic import ValidationError

client = Mistral(api_key="votre-clé-api")

def extract_structured(text: str, schema, max_retries: int = 2):
    """Extraction structurée avec gestion d'erreurs."""
    for attempt in range(max_retries + 1):
        try:
            response = client.chat.parse(
                model="mistral-large-latest",
                messages=[
                    {"role": "system", "content": "Extrais les informations demandées."},
                    {"role": "user", "content": text}
                ],
                response_format=schema,
                temperature=0,
                max_tokens=1024
            )

            parsed = response.choices[0].message.parsed
            if parsed is None:
                raise ValueError("Parsed est None — réponse peut-être tronquée")

            return parsed

        except SDKError as e:
            print(f"Erreur API (tentative {attempt + 1}) : {e}")
            if attempt == max_retries:
                raise
        except ValidationError as e:
            print(f"Erreur de validation (tentative {attempt + 1}) : {e}")
            if attempt == max_retries:
                raise

# Utilisation
try:
    book = extract_structured("'Le Petit Prince' de Saint-Exupéry, 1943", BookInfo)
    print(f"Extrait : {book.title} par {', '.join(book.authors)}")
except Exception as e:
    print(f"Échec définitif : {e}")

Vérifier finish_reason

Comme avec complete(), vérifiez que la réponse est complète :

response = client.chat.parse(
    model="mistral-large-latest",
    messages=messages,
    response_format=MonSchema,
    max_tokens=256
)

if response.choices[0].finish_reason != "stop":
    print(f"Attention : finish_reason = {response.choices[0].finish_reason}")
    # "length" = réponse tronquée, augmentez max_tokens

Points clés à retenir

  • client.chat.parse() remplace client.chat.complete() pour les Custom Structured Outputs
  • .parsed retourne un objet Pydantic typé, .content retourne le JSON brut
  • Le schéma est automatiquement ajouté au system prompt — inutile de décrire le format
  • Vérifiez toujours finish_reason et gérez les erreurs SDKError et ValidationError
  • Utilisez model_dump() pour convertir l’objet parsé en dictionnaire Python