CoolFace
Apppublic

FlorianSC/agritech-interface

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
app.py185 linesDownload Raw Back to root
1import logging2import joblib3import pandas as pd4import uvicorn5import os6from huggingface_hub import hf_hub_download7from pydantic import BaseModel, Field8from fastapi import FastAPI, Request9from fastapi.responses import JSONResponse10from fastapi.exceptions import RequestValidationError11import os12from dotenv import load_dotenv13from huggingface_hub import hf_hub_download14 15# Charge les variables du fichier .env 16load_dotenv() 17 18# Récupère la clé 19token = os.getenv("HF_TOKEN")20 21# On importe la fonction de nettoyage22from scripts.data_cleaning import preparation_yield_df_inference23 24logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')25 26app = FastAPI(title="Agritech Answers")27 28# ==========================================29# CHARGEMENT DYNAMIQUE DU MODÈLE30# ==========================================31def load_remote_pipeline():32    REPO_ID = "FLORIANSC/yield-prediction-model" 33    FILENAME = "randomforest_best_pipeline.joblib"34    35    # On vérifie si on est en local ou sur le Space36    if os.path.exists(FILENAME):37        logging.info("Chargement du modèle local.")38        return joblib.load(FILENAME)39    else:40        logging.info("Modèle local non trouvé. Téléchargement depuis le Hub...")41        token = os.getenv("HF_TOKEN")42        model_path = hf_hub_download(repo_id=REPO_ID, filename=FILENAME, token=token)43        return joblib.load(model_path)44 45# Chargement du pipeline au démarrage46try:47    pipeline = load_remote_pipeline()48    logging.info("Pipeline chargé avec succès.")49except Exception as e:50    logging.error(f"Erreur critique de chargement du modèle : {e}")51    pipeline = None52 53from scripts.config import (REGIONS, ITEMS)54 55class InputPrediction(BaseModel):56    region: REGIONS57    item: ITEMS58    avg_temp: float = Field(..., json_schema_extra={"example": 15.5})59    rainfall_mm: float = Field(..., json_schema_extra={"example": 500.0})60    pesticides_tonnes: float = Field(..., json_schema_extra={"example": 100.0})61 62 63class InputRecommendation(BaseModel):64    region: REGIONS65    avg_temp: float = Field(..., json_schema_extra={"example": 15.5})66    rainfall_mm: float = Field(..., json_schema_extra={"example": 500.0})67    pesticides_tonnes: float = Field(..., json_schema_extra={"example": 100.0})68 69# Gestion des erreurs 40470 71@app.exception_handler(404)72async def custom_404_handler(request: Request, exc):73    return JSONResponse(74        status_code=404,75        content={76            "error_code": 404,77            "message": "Cette route n'existe pas.",78            "detail": f"L'URL '{request.url.path}' est inconnue.",79            "suggestion": "Essayez plutôt /predict ou /health"80        }81    )82# Gestion des erreurs 42283@app.exception_handler(RequestValidationError)84async def validation_exception_handler(request: Request, exc: RequestValidationError):85    errors = exc.errors()86    # On logue l'erreur précise pour le Lead Data Scientist87    logging.error(f"Erreur de validation Pydantic : {errors}")88    89    return JSONResponse(90        status_code=422,91        content={92            "error_code": 422,93            "message": "Données d'entrée invalides (Schéma Pydantic)",94            "details": [95                {"champ": err["loc"][-1], "message": err["msg"], "type_attendu": err["type"]} 96                for err in errors97            ]98        }99    )100 101@app.get("/health")102def health_check():103    return {"status": "OK", "message": "API opérationnelle"}104 105 106@app.post("/predict")107def predict_api(data: InputPrediction):108    try:109        donnees_saisies = pd.DataFrame([data.model_dump()])110 111        # Feature engineering uniquement, sans encodage manuel112        donnees_preparees = preparation_yield_df_inference(input_df=donnees_saisies)113 114        logging.info(f"Colonnes envoyées au pipeline : {list(donnees_preparees.columns)}")115 116        prediction = pipeline.predict(donnees_preparees)[0]117 118        return {119            "prediction": float(prediction/10),120            "unit": "yield"121        }122 123    except Exception as e:124        logging.exception("Erreur pendant la prédiction")125        return JSONResponse(126            status_code=500,127            content={128                "error_code": 500,129                "message": "Erreur lors de la prédiction",130                "detail": str(e)131            }132        )133 134 135CROPS = [136    "Wheat", "Rice", "Maize", "Soybean", "Potatoes",137    "Sweet Potatoes", "Sorghum", "Cassava", "Yams", "Plantains and others"138]139 140 141@app.post("/recommend")142def recommend_api(data: InputRecommendation):143    try:144        results = []145 146        for crop in CROPS:147            donnees_saisies = pd.DataFrame([{148                "region": data.region,149                "item": crop,150                "avg_temp": data.avg_temp,151                "rainfall_mm": data.rainfall_mm,152                "pesticides_tonnes": data.pesticides_tonnes153            }])154 155            # Feature engineering uniquement156            donnees_preparees = preparation_yield_df_inference(input_df=donnees_saisies)157 158            prediction = pipeline.predict(donnees_preparees)[0]159 160            results.append({161                "crop": crop,162                "predicted_yield": float(prediction/10)163            })164 165        results = sorted(results, key=lambda x: x["predicted_yield"], reverse=True)166 167        return {168            "best_crop": results[0]["crop"],169            "ranking": results170        }171 172    except Exception as e:173        logging.exception("Erreur pendant la recommandation")174        return JSONResponse(175            status_code=500,176            content={177                "error_code": 500,178                "message": "Erreur lors de la recommandation",179                "detail": str(e)180            }181        )182 183 184if __name__ == "__main__":185    uvicorn.run("app:app", host="0.0.0.0", port=7860)