Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI | |
| from fastapi import HTTPException | |
| from pydantic import BaseModel | |
| import mlflow.pyfunc | |
| import pandas as pd | |
| import config | |
| import logging | |
| import os | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # 1) URL du Space HF qui héberge MLflow (tracking server) | |
| MLFLOW_TRACKING_URI = os.getenv("MLFLOW_TRACKING_URI", "https://pradelf-getaround-mlflow.hf.space") | |
| MODEL_URI = os.getenv("MODEL_URI", "models:/getaround-price-prediction-model@certification") | |
| BOOL_COLS = [ | |
| "private_parking_available", | |
| "has_gps", | |
| "has_air_conditioning", | |
| "automatic_car", | |
| "has_getaround_connect", | |
| "has_speed_regulator", | |
| "winter_tires", | |
| ] | |
| description = """ | |
| Bienvenue sur l'API de Getaround pour prédire le prix journalier de location d'une voiture en fonction de son année d'expérience! | |
| ## Point de terminaison d'introduction | |
| Pour tester le fonctionnement de l'API, vous pouvez utiliser le point de terminaison d'introduction suivant: | |
| * `/`: **GET** retourne la version de l'API et un message de bienvenue. | |
| ## Point de terminaison de ligne de vie | |
| Cette web fonction permet de vérifier que le serveur de l'API est opérationnel et de surveiller son statut. | |
| * `/health`: **GET** retourne juste OK pour valider que le serveur de l'API est en cours d'exécution sans problèmes. | |
| ## Machine Learning : Point de terminaison du prix de location | |
| Cette terminaison de l'API permet de prédire le prix journalier de location d'une voiture en fonction de ses caractéristiques.. | |
| * `/predict` accepte une requête POST avec un JSON contenant un objet JSON donnant les caractéristiques d'une voiture | |
| et retourne une prédiction du prix journalier de location de celle-ci. | |
| La documentation est ci-dessous 👇 pour chaque point de terminaison (endpoints). | |
| """ | |
| tags_metadata = [ | |
| { | |
| "name": "Point de terminaison d'introduction", | |
| "description": "Terminaison simple de présentation de l'API et de sa version pour vérifier que tout fonctionne correctement.", | |
| }, | |
| { | |
| "name": "Point de terminaison de ligne de vie", | |
| "description": "Point de terminaison de ligne de vie pour surveiller le statut du serveur de l'API. Retourne 'ok' si le serveur fonctionne sans problèmes.", | |
| }, | |
| { | |
| "name": "Point de terminaison du prix de location", | |
| "description": "Point de terminaison permettant de prédire le prix journalier de location d'une voiture en fonction de ses caractéristiques.", | |
| }, | |
| { | |
| "name": "Machine Learning", | |
| "description": "Point de terminaison de prediction du prix de location journalier d'un véhicule en fonction de ses caractéristiques.", | |
| }, | |
| ] | |
| model= None | |
| app = FastAPI( | |
| title="Getaround API pour le prix journalier de location d'une voiture.", | |
| description=description, | |
| version=config.__version__, | |
| contact={ | |
| "name": "Francis Pradel", | |
| "url": "https://promotion.francispradel.fr", | |
| }, | |
| openapi_tags=tags_metadata, | |
| swagger_ui_parameters={"syntaxHighlight": {"theme": "obsidian"}}, | |
| ) | |
| def load_model_on_startup(): | |
| global model | |
| try: | |
| logger.info(f"Chargement du modèle depuis {MLFLOW_TRACKING_URI}") | |
| logger.info(f"Model : {MODEL_URI}") | |
| mlflow.set_tracking_uri(MLFLOW_TRACKING_URI) # ou via env MLFLOW_TRACKING_URI | |
| model = mlflow.pyfunc.load_model(MODEL_URI) | |
| logger.info("Modèle chargé avec succès") | |
| except Exception: | |
| logger.exception("Impossible de charger le modèle au démarrage") | |
| model = None | |
| class RentalFeatures(BaseModel): | |
| model_key: str | |
| mileage: int | |
| engine_power: int | |
| fuel: str | |
| paint_color: str | |
| car_type: str | |
| private_parking_available: bool | |
| has_gps: bool | |
| has_air_conditioning: bool | |
| automatic_car: bool | |
| has_getaround_connect: bool | |
| has_speed_regulator: bool | |
| winter_tires: bool | |
| def greet_json(): | |
| """ | |
| Bonjour | |
| """ | |
| return {"Hello": "World!", "version": config.__version__} | |
| def health(): | |
| """ | |
| Health line to monitor the server status of the API. Returns "ok" if the server is running without issues. | |
| """ | |
| return {"status": "ok"} | |
| async def predict(predictionFeatures: RentalFeatures): | |
| """ | |
| Prediction of car daily rental cost for a given properties of a car ! | |
| """ | |
| # Read data | |
| # pf = [ | |
| # predictionFeatures.model_key or "Peugeot", | |
| # predictionFeatures.mileage or 0, | |
| # predictionFeatures.engine_power or 0, | |
| # predictionFeatures.fuel or "petrol", | |
| # predictionFeatures.car_type or "sedan", | |
| # predictionFeatures.private_parking_available or 1, | |
| # predictionFeatures.has_gps or 0, | |
| # predictionFeatures.has_air_conditioning or 0, | |
| # predictionFeatures.automatic_car or 0, | |
| # predictionFeatures.has_getaround_connect or 0, | |
| # predictionFeatures.has_speed_regulator or 0, | |
| # predictionFeatures.winter_tires or 0, | |
| # ] | |
| try: | |
| payload = predictionFeatures.model_dump() | |
| car_characteristic = pd.DataFrame([payload]) | |
| #car_caracteristic = pd.DataFrame({"Car": pf}) | |
| # Log model from mlflow | |
| BOOL_COLS = [ | |
| "private_parking_available", | |
| "has_gps", | |
| "has_air_conditioning", | |
| "automatic_car", | |
| "has_getaround_connect", | |
| "has_speed_regulator", | |
| "winter_tires", | |
| ] | |
| # Conversion explicite des colonnes booléennes | |
| for col in BOOL_COLS: | |
| car_characteristic[col] = car_characteristic[col].astype(bool) | |
| logger.info("Payload reçu: %s", payload) | |
| logger.info("dtypes:\n%s", car_characteristic.dtypes) | |
| # Prediction from previously loaded model as a PyFuncModel. | |
| prediction = model.predict(car_characteristic) | |
| logger.info(f"Output : {prediction}") | |
| # Format response | |
| response = { | |
| "prediction": float(prediction[0]), | |
| "detail": "Prédiction du tarif journalier (nul si aucun modèle : model.pkl).", | |
| } | |
| return response | |
| except Exception as e: | |
| logger.exception("Erreur dans /predict") | |
| raise HTTPException(status_code=500, detail=str(e)) | |