Batch

Le batch permet d'exécuter des inférences asynchrones sur de gros volumes d'entrées en parallèle, réduisant les coûts de calcul tout en traitant de larges volumes de travail avec une réduction de 50 %.

Préparer un lot

Préparer un lot

Préparer et envoyer votre lot

Un lot est composé d'une liste de requêtes API. La structure d'une requête individuelle comprend :

  • Un custom_id unique pour identifier chaque requête et référencer les résultats après l'exécution
  • Un objet body contenant la requête brute telle que vous l'auriez formulée lors de l'appel à l'endpoint d'origine sans batch

Voici un exemple de structuration d'une requête par lots :

{"custom_id": "0", "body": {"model": "mistral-small-latest", "max_tokens": 100, "messages": [{"role": "user", "content": "What is the best French cheese?"}]}}
{"custom_id": "1", "body": {"model": "mistral-small-latest", "max_tokens": 100, "messages": [{"role": "user", "content": "What is the best French wine?"}]}}
Astuce

Chaque ligne doit être un objet JSON valide. N'ajoutez pas de sauts de ligne à l'intérieur d'un objet JSON. Le champ model à l'intérieur de body est optionnel si vous spécifiez le modèle lors de la création du job.

Un objet body de lot peut être n'importe quel corps de requête valide pour l'endpoint que vous utilisez. Ci-dessous, des exemples de lots pour différents endpoints, dont le body correspond au corps de requête de l'endpoint.

Un fichier batch pour les chat completions, avec v1/chat/completions comme point de terminaison et ministral-3b-latest comme modèle, ressemblerait à ceci :

{"custom_id": "0", "body": {"max_tokens": 128, "messages": [{"role": "user", "content": "What is the best French cheese?"}]}}
{"custom_id": "1", "body": {"max_tokens": 512, "temperature": 0.2, "messages": [{"role": "user", "content": "What is the best French wine?"}]}}
{"custom_id": "2", "body": {"max_tokens": 256, "temperature": 0.8, "messages": [{"role": "user", "content": "What is the best French pastry?"}]}}

Sous forme de fichier JSONL, chaque ligne représente une requête vers le point de terminaison et le modèle de l'API.

Explication

Explication

Le corps de la requête suivra le même format que le point de terminaison que vous souhaitez utiliser pour votre batch, à l'exception de l'identifiant du modèle qui ne sera fourni qu'au moment de la création du job pour démarrer l'exécution du batch. Voici un exemple de ligne avec les chat completions :

{
    "custom_id": "2", # Un ID comme métadonnée qui sera retourné dans le fichier de sortie pour identifier la requête
    "body": { # Le corps de la requête
        "max_tokens": 256, # Nombre maximum de tokens correspondant aux tokens générés maximum pour les complétions de chat
        "temperature": 0.8, # La température à utiliser pour l'échantillonnage
        "messages": [ # Les messages pour lesquels générer les complétions de chat
            {
                "role": "user", # Le rôle de l'auteur du message
                "content": "What is the best French pastry?" # Le contenu du message
            }
        ]
    }
}

Pour plus d'informations sur les complétions, consultez la documentation chat completion et la spécification API correspondante.

Pour de gros lots allant jusqu'à 1 million de requêtes, vous créeriez un fichier .jsonl avec les données ci-dessus. Une fois enregistré, vous pouvez envoyer votre fichier d'entrée de lot pour vous assurer qu'il est correctement référencé lors du lancement des batches.

Astuce

Pour les lots de moins de 10 000 requêtes, nous supportons le batch en ligne.

Il existe 2 méthodes principales pour envoyer un fichier de lot :

A. Via Studio :

  • Envoyez vos fichiers dans StudioFichiers.
    • Envoyez le fichier au format décrit précédemment.
    • Définissez purpose sur Batch Processing.
  • Lancez et gérez vos lots dans StudioLots.
    • Créez et lancez un job en fournissant les files, endpoint et model. Vous n'aurez pas besoin d'utiliser l'API pour envoyer vos fichiers ou créer des jobs batch.

B. Via l'API, expliquée ci-dessous :

Pour envoyer votre fichier de lot, vous devez utiliser l'endpoint files.

from mistralai.client import Mistral
import os

api_key = os.environ["MISTRAL_API_KEY"]

client = Mistral(api_key=api_key)

batch_data = client.files.upload(
    file={
        "file_name": "test.jsonl",
        "content": open("test.jsonl", "rb")
    },
    purpose = "batch"
)
Création de lot

Création de lot

Créer un nouveau batch

Créez un nouveau batch, il sera mis en file d'attente pour être traité.

  • Données des requêtes : les données pour les requêtes à traiter par lots. Deux options sont disponibles :
    • input_files : une liste des ID de fichiers d'entrée par lots, voir comment utiliser le batch sur fichier.
    • requests : une liste des requêtes à traiter par lots, voir comment utiliser le batch en ligne.
  • model : vous ne pouvez utiliser qu'un seul modèle (par exemple, codestral-latest) par lot. Vous pouvez cependant exécuter plusieurs lots sur les mêmes fichiers avec différents modèles si vous souhaitez comparer les résultats.
  • endpoint : nous prenons actuellement en charge /v1/embeddings, /v1/chat/completions, /v1/fim/completions, /v1/moderations, /v1/chat/moderations, /v1/ocr, /v1/classifications, /v1/conversations, /v1/audio/transcriptions.
  • metadata : métadonnées personnalisées optionnelles pour le lot.
Batch sur fichier

Batch sur fichier

L'approche standard de batch repose sur des fichiers contenant toutes les requêtes à traiter. Nous prenons en charge jusqu'à 1 million de requêtes dans un seul lot, permettant une gestion efficace de gros volumes de requêtes à moindre coût. Cela convient parfaitement aux tâches nécessitant un débit élevé mais peu sensibles à la latence ou à la priorité.

created_job = client.batch.jobs.create(
    input_files=[batch_data.id],
    model="mistral-small-latest",
    endpoint="/v1/chat/completions",
    metadata={"job_type": "testing"}
)
Batch en ligne

Batch en ligne

Pour les lots de moins de 10 000 requêtes, nous prenons en charge le batch en ligne. Au lieu de créer et télécharger un fichier .jsonl contenant toutes les données de requête, vous pouvez inclure le corps de la requête directement dans la requête de création du traitement. Cette approche est pratique pour les tâches de plus petite échelle ou moins volumineuses.

from mistralai.client import Mistral
import os

api_key = os.environ["MISTRAL_API_KEY"]

client = Mistral(api_key=api_key)

inline_batch_data = [
    {
        "custom_id": "0",
        "body": {
            "max_tokens": 128,
            "messages": [{"role": "user", "content": "What is the best French cheese?"}]
        }
    },
    {
        "custom_id": "1",
        "body": {
            "max_tokens": 512,
            "temperature": 0.2,
            "messages": [{"role": "user", "content": "What is the best French wine?"}]
        }
    },
    {
        "custom_id": "2",
        "body": {
            "max_tokens": 256,
            "temperature": 0.8,
            "messages": [{"role": "user", "content": "What is the best French pastry?"}]
        }
    },
]

created_job = client.batch.jobs.create(
    requests=inline_batch_data,
    model="mistral-small-latest",
    endpoint="/v1/chat/completions",
    metadata={"job_type": "testing"}
)
Récupérer

Récupérer

Récupérer votre batch

Une fois le lot envoyé, vous voudrez récupérer de nombreuses informations telles que :

  • Le statut du batch
  • Les résultats du batch
  • La liste des batches
Obtenir les détails d'un batch

Obtenir les détails d'un batch

Vous pouvez récupérer les détails d'un batch grâce à son ID.

retrieved_job = client.batch.jobs.get(job_id=created_job.id)
Obtenir les résultats du batch

Obtenir les résultats du batch

Une fois le batch terminé, téléchargez les résultats.

output_file_stream = client.files.download(file_id=retrieved_job.output_file)

# Write and save the file
with open('batch_results.jsonl', 'wb') as f:
    f.write(output_file_stream.read())
Lister les batches

Lister les batches

Vous pouvez consulter la liste de vos batches et les filtrer selon divers critères, notamment :

  • Statut : QUEUED, RUNNING, SUCCESS, FAILED, TIMEOUT_EXCEEDED, CANCELLATION_REQUESTED et CANCELLED
  • Metadata : clé et valeur de métadonnées personnalisées pour le lot
list_job = client.batch.jobs.list(
    status="RUNNING",
    metadata={"job_type": "testing"}
)
Demander l'annulation

Demander l'annulation

Annuler n'importe quel Job

Si vous souhaitez annuler un job batch, vous pouvez le faire en envoyant une requête d'annulation.

canceled_job = client.batch.jobs.cancel(job_id=created_job.id)
Un exemple de bout en bout

Un exemple de bout en bout

Voici un exemple de bout en bout montrant comment utiliser l'API batch du début à la fin.

Un exemple complet d'utilisation de l'API Mistral pour exécuter un batch avec des données aléatoires. Couvre :

  • La création d'un client
  • La génération de données d'entrée aléatoires
  • La création d'un fichier d'entrée
  • L'exécution d'un batch
  • Le téléchargement des résultats
import argparse
import json
import os
import random
import time
from io import BytesIO

import httpx
from mistralai.client import File, Mistral

def create_client():
    """
    Create a Mistral client using the API key from environment variables.

    Returns:
        Mistral: An instance of the Mistral client.
    """
    return Mistral(api_key=os.environ["MISTRAL_API_KEY"])

def generate_random_string(start, end):
    """
    Generate a random string of variable length.

    Args:
        start (int): Minimum length of the string.
        end (int): Maximum length of the string.

    Returns:
        str: A randomly generated string.
    """
    length = random.randrange(start, end)
    return ' '.join(random.choices('abcdefghijklmnopqrstuvwxyz', k=length))

def print_stats(batch_job):
    """
    Print the statistics of the batch job.

    Args:
        batch_job: The batch job object containing job statistics.
    """
    print(f"Total requests: {batch_job.total_requests}")
    print(f"Failed requests: {batch_job.failed_requests}")
    print(f"Successful requests: {batch_job.succeeded_requests}")
    print(
        f"Percent done: {round((batch_job.succeeded_requests + batch_job.failed_requests) / batch_job.total_requests, 4) * 100}")


def create_input_file(client, num_samples):
    """
    Create an input file for the batch job.

    Args:
        client (Mistral): The Mistral client instance.
        num_samples (int): Number of samples to generate.

    Returns:
        File: The uploaded input file object.
    """
    buffer = BytesIO()
    for idx in range(num_samples):
        request = {
            "custom_id": str(idx),
            "body": {
                "max_tokens": random.randint(10, 1000),
                "messages": [{"role": "user", "content": generate_random_string(100, 5000)}]
            }
        }
        buffer.write(json.dumps(request).encode("utf-8"))
        buffer.write("\n".encode("utf-8"))
    return client.files.upload(file=File(file_name="file.jsonl", content=buffer.getvalue()), purpose="batch")


def run_batch_job(client, input_file, model):
    """
    Run a batch job using the provided input file and model.

    Args:
        client (Mistral): The Mistral client instance.
        input_file (File): The input file object.
        model (str): The model to use for the batch job.

    Returns:
        BatchJob: The completed batch job object.
    """
    batch_job = client.batch.jobs.create(
        input_files=[input_file.id],
        model=model,
        endpoint="/v1/chat/completions",
        metadata={"job_type": "testing"}
    )

    while batch_job.status in ["QUEUED", "RUNNING"]:
        batch_job = client.batch.jobs.get(job_id=batch_job.id)
        print_stats(batch_job)
        time.sleep(1)

    print(f"Batch job {batch_job.id} completed with status: {batch_job.status}")
    return batch_job


def download_file(client, file_id, output_path):
    """
    Download a file from the Mistral server.

    Args:
        client (Mistral): The Mistral client instance.
        file_id (str): The ID of the file to download.
        output_path (str): The path where the file will be saved.
    """
    if file_id is not None:
        print(f"Downloading file to {output_path}")
        output_file = client.files.download(file_id=file_id)
        with open(output_path, "w") as f:
            for chunk in output_file.stream:
                f.write(chunk.decode("utf-8"))
        print(f"Downloaded file to {output_path}")


def main(num_samples, success_path, error_path, model):
    """
    Main function to run the batch job.

    Args:
        num_samples (int): Number of samples to process.
        success_path (str): Path to save successful outputs.
        error_path (str): Path to save error outputs.
        model (str): Model name to use.
    """
    client = create_client()
    input_file = create_input_file(client, num_samples)
    print(f"Created input file {input_file}")

    batch_job = run_batch_job(client, input_file, model)
    print(f"Job duration: {batch_job.completed_at - batch_job.created_at} seconds")
    download_file(client, batch_job.error_file, error_path)
    download_file(client, batch_job.output_file, success_path)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Run Mistral AI batch job")
    parser.add_argument("--num_samples", type=int, default=100, help="Number of samples to process")
    parser.add_argument("--success_path", type=str, default="output.jsonl", help="Path to save successful outputs")
    parser.add_argument("--error_path", type=str, default="error.jsonl", help="Path to save error outputs")
    parser.add_argument("--model", type=str, default="codestral-latest", help="Model name to use")

    args = parser.parse_args()

    main(args.num_samples, args.success_path, args.error_path, args.model)
FAQ

FAQ