À l'issue de cette section, vous aurez découvert

Mise en place du TP

Pour ce TP, utilisez la branche suivante :

git checkout 10_start_pydantic

L'API d'exposition du modèle (exposition/model_as_a_service/inference.py) contient un endpoint /predict qui
accepte aujourd'hui Ws1_avg sans aucun contrôle : n'importe quelle valeur (texte, nombre négatif, chaîne vide...)
est acceptée et transmise telle quelle au modèle. Cela peut provoquer des erreurs silencieuses ou des prédictions
aberrantes en aval, sans jamais donner de message d'erreur clair à l'appelant de l'API.

Nous allons voir comment pydantic permet de garantir, dès la frontière de l'API, que les données reçues respectent un contrat métier (ici : Ws1_avg doit être un entier positif).

Qu'est-ce que pydantic ?


pydantic
est une bibliothèque Python de validation de données basée sur les annotations de type (type hints). Elle permet de définir des modèles (classes héritant de BaseModel) qui décrivent la forme attendue d'une donnée, puis de valider et convertir automatiquement des données brutes (dictionnaires, JSON, query params...) vers ces modèles.

Concrètement, pydantic permet de :

pydantic et FastAPI

FastAPI est construit au-dessus de pydantic : chaque fois qu'un paramètre d'un endpoint est annoté avec un
type Python (ou un modèle pydantic), FastAPI utilise pydantic pour :

  1. Parser et convertir la donnée reçue (query param, corps de requête JSON, etc.) vers le type attendu
  2. Valider que la donnée respecte les contraintes déclarées
  3. Renvoyer automatiquement une erreur HTTP 422 Unprocessable Entity avec un message détaillé si la validation échoue
  4. Documenter automatiquement le contrat de l'API dans le schéma OpenAPI (/docs)

Exemple générique : valider qu'un paramètre de requête est un entier strictement positif.

from typing import Annotated

from fastapi import Depends, FastAPI
from pydantic import BaseModel, Field

app = FastAPI()


class ItemQueryParams(BaseModel):
    quantity: int = Field(gt=0, description="Quantité commandée, doit être un entier positif")


@app.get("/items")
def get_items(params: Annotated[ItemQueryParams, Depends()]):
    return {"quantity": params.quantity}

Avec ce code :

Aucune ligne de code de validation manuelle (if quantity <= 0: raise ...) n'est nécessaire : pydantic et FastAPI
s'en chargent à partir de la seule déclaration du modèle.

Application au TP : valider Ws1_avg dans inference.py

Exercice : dans exposition/model_as_a_service/inference.py, l'endpoint /predict accepte actuellement
Ws1_avg comme un simple str, sans validation :

@app.get("/predict")
def predict_endpoint(Ws1_avg: str):  # noqa: N803
    received_wind_speed_avg = Ws1_avg
    ...

À vous de jouer :

  1. Définir un modèle pydantic PredictionQueryParams avec un champ Ws1_avg de type int, contraint à être
    strictement positif grâce à Field(gt=0, ...).
  2. Modifier la signature de predict_endpoint pour recevoir ce modèle en dépendance (Annotated[PredictionQueryParams, Depends()])
    au lieu du paramètre str brut.
  3. Adapter le corps de la fonction pour lire la valeur validée via params.Ws1_avg.

Une fois l'exercice réalisé, l'endpoint doit se comporter ainsi :

Pour tester manuellement une fois l'API lancée :

curl "http://localhost:5000/predict?Ws1_avg=5"
curl "http://localhost:5000/predict?Ws1_avg=-3"
curl "http://localhost:5000/predict?Ws1_avg=abc"

La documentation interactive générée automatiquement par FastAPI (http://localhost:5000/docs) reflète elle aussi
la contrainte : le paramètre Ws1_avg y est décrit comme un entier strictement positif.

Les instructions du tp suivant sont ici