HendSta commited on
Commit
6fba229
·
1 Parent(s): 67df1ef

create space

Browse files
Files changed (1) hide show
  1. app.py +118 -82
app.py CHANGED
@@ -18,15 +18,18 @@ import xml.etree.ElementTree as ET
18
  from fastapi.responses import JSONResponse
19
  from sklearn.base import BaseEstimator, TransformerMixin
20
  import sys
21
- from huggingface_hub import hf_hub_download
22
- from transformers import AutoTokenizer, AutoModelForCausalLM
23
- import torch
24
 
25
  app = FastAPI()
26
 
 
 
 
 
27
  app.add_middleware(
28
  CORSMiddleware,
29
- allow_origins=["http://localhost:4200"], # URL de votre frontend Angular
30
  allow_credentials=True,
31
  allow_methods=["*"],
32
  allow_headers=["*"],
@@ -54,54 +57,42 @@ class NumericConverter(BaseEstimator, TransformerMixin):
54
 
55
  sys.modules['__main__'].NumericConverter = NumericConverter
56
 
57
- # Charger les modèles ML depuis Hugging Face
58
- def load_models_from_hf():
59
- """Charge tous les modèles depuis Hugging Face"""
60
- global pipeline, analyze_risk_model, llm_tokenizer, llm_model
61
-
62
- print("Loading models from Hugging Face...")
63
-
64
- # Charger le modèle d'analyse médicale
65
- try:
66
- model_path = hf_hub_download(
67
- repo_id="HendSta/analyse_medicale",
68
- filename="modele_analyse_medicale_final.joblib"
69
- )
70
- pipeline = joblib.load(model_path)
71
- print(" Modèle d'analyse médicale chargé avec succès")
72
- except Exception as e:
73
- print(f"❌ Erreur lors du chargement du modèle d'analyse médicale: {e}")
74
- raise
75
-
76
- # Charger le modèle d'analyse de risque
77
- try:
78
- analyze_risk_model_path = hf_hub_download(
79
- repo_id="HendSta/analyse_row",
80
- filename="analyze_row_final.joblib"
81
- )
82
- analyze_risk_model = joblib.load(analyze_risk_model_path)
83
- print("✅ Modèle d'analyse de risque chargé avec succès")
84
- except Exception as e:
85
- print(f"❌ Erreur lors du chargement du modèle d'analyse de risque: {e}")
86
- raise
87
-
88
- # Charger le modèle LLM
89
- try:
90
- llm_tokenizer = AutoTokenizer.from_pretrained("HendSta/biomistral-finetuned-fullv3")
91
- llm_model = AutoModelForCausalLM.from_pretrained("HendSta/biomistral-finetuned-fullv3")
92
- print("✅ Modèle LLM chargé avec succès")
93
- except Exception as e:
94
- print(f"❌ Erreur lors du chargement du modèle LLM: {e}")
95
- raise
96
-
97
- # Initialiser les modèles avec gestion d'erreur
98
- try:
99
- load_models_from_hf()
100
- print("🎉 Tous les modèles ont été chargés avec succès!")
101
- except Exception as e:
102
- print(f"💥 Erreur critique lors du chargement des modèles: {e}")
103
- print("L'application ne peut pas démarrer sans les modèles.")
104
- raise
105
 
106
  # Créer un imputer pour gérer les valeurs NaN
107
  imputer = SimpleImputer(strategy='constant', fill_value=0)
@@ -419,35 +410,10 @@ def to_native(val):
419
  return val
420
 
421
  # ==== API Endpoints ====
422
- @app.get("/health")
423
- def health_check():
424
- """Vérifie que tous les modèles sont chargés correctement"""
425
- try:
426
- # Vérifier que tous les modèles sont disponibles
427
- models_status = {
428
- "analyse_medicale_model": pipeline is not None,
429
- "analyze_risk_model": analyze_risk_model is not None,
430
- "llm_model": llm_model is not None,
431
- "llm_tokenizer": llm_tokenizer is not None
432
- }
433
-
434
- all_loaded = all(models_status.values())
435
-
436
- return {
437
- "status": "healthy" if all_loaded else "unhealthy",
438
- "models_loaded": models_status,
439
- "message": "Tous les modèles sont chargés" if all_loaded else "Certains modèles ne sont pas chargés"
440
- }
441
- except Exception as e:
442
- return {
443
- "status": "error",
444
- "error": str(e)
445
- }
446
-
447
  @app.post("/predict", response_model=PredictionResult)
448
  def predict(data: InputData):
449
  df = pd.DataFrame([data.dict()])
450
- preds = pipeline.predict(df)[0]
451
  return PredictionResult(
452
  **data.dict(),
453
  CodParametre=preds[0],
@@ -494,7 +460,7 @@ async def upload_file(file: UploadFile = File(...)):
494
  # Si on a du PDF et qu'on a besoin de prédire les paramètres
495
  if file_extension == "pdf":
496
  # Faire la prédiction
497
- preds = pipeline.predict(df)
498
 
499
  # Créer les résultats avec les prédictions
500
  results = []
@@ -531,8 +497,6 @@ async def upload_file(file: UploadFile = File(...)):
531
  def analyze_risk(param: dict = Body(...)):
532
  import pandas as pd
533
  import numpy as np
534
- # Utiliser le modèle globalement chargé
535
- model = analyze_risk_model
536
 
537
  # Préparer le DataFrame à partir du paramètre reçu
538
  df_test = pd.DataFrame([param])
@@ -579,7 +543,7 @@ def analyze_risk(param: dict = Body(...)):
579
  features_for_ml = df_result[['DeltaValeurPrecedente', 'RatioValeurPrecedente',
580
  'PourcentageValeurMin', 'PourcentageValeurMax',
581
  'EcartNormalise', 'ValeurActuelle', 'CodeParametre']]
582
- predicted_risk_num = model.predict(features_for_ml)[0]
583
  risk_map = {0: 'Aucun', 1: 'Faible', 2: 'Modéré', 3: 'Élevé'}
584
  degre_risque = risk_map.get(int(predicted_risk_num), 'Inconnu')
585
 
@@ -618,6 +582,78 @@ def analyze_risk(param: dict = Body(...)):
618
  "conseil": to_native(conseil)
619
  }
620
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
  # Fonction de debug temporaire pour tester l'extraction
622
  def debug_extraction(line):
623
  """Teste l'extraction d'une ligne et affiche les résultats"""
 
18
  from fastapi.responses import JSONResponse
19
  from sklearn.base import BaseEstimator, TransformerMixin
20
  import sys
21
+ from huggingface_hub import hf_hub_download, InferenceClient
22
+ from transformers import pipeline as hf_textgen_pipeline
 
23
 
24
  app = FastAPI()
25
 
26
+ # Configure CORS for local dev and Spaces by env var (default: allow all)
27
+ cors_env = os.getenv("CORS_ALLOW_ORIGINS", "*")
28
+ allow_origins = ["*"] if cors_env.strip() == "*" else [o.strip() for o in cors_env.split(",") if o.strip()]
29
+
30
  app.add_middleware(
31
  CORSMiddleware,
32
+ allow_origins=allow_origins,
33
  allow_credentials=True,
34
  allow_methods=["*"],
35
  allow_headers=["*"],
 
57
 
58
  sys.modules['__main__'].NumericConverter = NumericConverter
59
 
60
+ # ==== Hugging Face Hub model loading ====
61
+ HF_REPO_MEDICALE = os.getenv("HF_REPO_MEDICALE", "HendSta/analyse_medicale")
62
+ HF_REPO_ROW = os.getenv("HF_REPO_ROW", "HendSta/analyse_row")
63
+ HF_REPO_LLM = os.getenv("HF_REPO_LLM", "HendSta/biomistral-finetuned-fullv3")
64
+
65
+ def load_joblib_from_hub(repo_id: str, candidate_filenames: List[str]):
66
+ last_error: Optional[Exception] = None
67
+ for filename in candidate_filenames:
68
+ try:
69
+ file_path = hf_hub_download(repo_id=repo_id, filename=filename, local_dir="hub_models", local_dir_use_symlinks=False)
70
+ return joblib.load(file_path)
71
+ except Exception as e:
72
+ last_error = e
73
+ continue
74
+ raise RuntimeError(f"Impossible de charger un modèle depuis {repo_id}. Dernière erreur: {last_error}")
75
+
76
+ # Charger les modèles ML depuis le Hub
77
+ medical_pipeline = load_joblib_from_hub(
78
+ HF_REPO_MEDICALE,
79
+ [
80
+ "modele_analyse_medicale_final.joblib",
81
+ "pipeline.joblib",
82
+ "model.joblib",
83
+ "model.pkl",
84
+ ],
85
+ )
86
+
87
+ risk_model = load_joblib_from_hub(
88
+ HF_REPO_ROW,
89
+ [
90
+ "analyze_row_final.joblib",
91
+ "analyse_row_final.joblib",
92
+ "model.joblib",
93
+ "model.pkl",
94
+ ],
95
+ )
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
  # Créer un imputer pour gérer les valeurs NaN
98
  imputer = SimpleImputer(strategy='constant', fill_value=0)
 
410
  return val
411
 
412
  # ==== API Endpoints ====
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413
  @app.post("/predict", response_model=PredictionResult)
414
  def predict(data: InputData):
415
  df = pd.DataFrame([data.dict()])
416
+ preds = medical_pipeline.predict(df)[0]
417
  return PredictionResult(
418
  **data.dict(),
419
  CodParametre=preds[0],
 
460
  # Si on a du PDF et qu'on a besoin de prédire les paramètres
461
  if file_extension == "pdf":
462
  # Faire la prédiction
463
+ preds = medical_pipeline.predict(df)
464
 
465
  # Créer les résultats avec les prédictions
466
  results = []
 
497
  def analyze_risk(param: dict = Body(...)):
498
  import pandas as pd
499
  import numpy as np
 
 
500
 
501
  # Préparer le DataFrame à partir du paramètre reçu
502
  df_test = pd.DataFrame([param])
 
543
  features_for_ml = df_result[['DeltaValeurPrecedente', 'RatioValeurPrecedente',
544
  'PourcentageValeurMin', 'PourcentageValeurMax',
545
  'EcartNormalise', 'ValeurActuelle', 'CodeParametre']]
546
+ predicted_risk_num = risk_model.predict(features_for_ml)[0]
547
  risk_map = {0: 'Aucun', 1: 'Faible', 2: 'Modéré', 3: 'Élevé'}
548
  degre_risque = risk_map.get(int(predicted_risk_num), 'Inconnu')
549
 
 
582
  "conseil": to_native(conseil)
583
  }
584
 
585
+ # ==== LLM (text-generation) endpoint ====
586
+ _llm_generator = None
587
+ _llm_via_api = False
588
+
589
+ def get_llm_generator():
590
+ global _llm_generator, _llm_via_api
591
+ if _llm_generator is not None:
592
+ return _llm_generator
593
+
594
+ # Prefer Inference API by default to avoid OOM in CPU Spaces
595
+ use_api_default = os.getenv("USE_INFERENCE_API", "1")
596
+ use_api = use_api_default.lower() in {"1", "true", "yes"}
597
+
598
+ if use_api:
599
+ token = os.getenv("HF_TOKEN") or os.getenv("HF_API_TOKEN")
600
+ _llm_generator = InferenceClient(model=HF_REPO_LLM, token=token)
601
+ _llm_via_api = True
602
+ return _llm_generator
603
+
604
+ # Fallback to local transformers pipeline
605
+ try:
606
+ trust_code = os.getenv("HF_TRUST_REMOTE_CODE", "1") != "0"
607
+ _llm_generator = hf_textgen_pipeline(
608
+ task="text-generation",
609
+ model=HF_REPO_LLM,
610
+ trust_remote_code=trust_code,
611
+ )
612
+ _llm_via_api = False
613
+ except Exception:
614
+ token = os.getenv("HF_TOKEN") or os.getenv("HF_API_TOKEN")
615
+ _llm_generator = InferenceClient(model=HF_REPO_LLM, token=token)
616
+ _llm_via_api = True
617
+ return _llm_generator
618
+
619
+ class GenerateRequest(BaseModel):
620
+ prompt: str
621
+ max_new_tokens: int = 256
622
+ temperature: float = 0.7
623
+ top_p: float = 0.95
624
+ repetition_penalty: float = 1.1
625
+
626
+ class GenerateResponse(BaseModel):
627
+ output: str
628
+
629
+ @app.post("/llm-generate", response_model=GenerateResponse)
630
+ def llm_generate(req: GenerateRequest):
631
+ try:
632
+ generator = get_llm_generator()
633
+ if _llm_via_api:
634
+ text = generator.text_generation(
635
+ req.prompt,
636
+ max_new_tokens=req.max_new_tokens,
637
+ temperature=req.temperature,
638
+ top_p=req.top_p,
639
+ repetition_penalty=req.repetition_penalty,
640
+ do_sample=True,
641
+ )
642
+ else:
643
+ outputs = generator(
644
+ req.prompt,
645
+ max_new_tokens=req.max_new_tokens,
646
+ temperature=req.temperature,
647
+ top_p=req.top_p,
648
+ repetition_penalty=req.repetition_penalty,
649
+ do_sample=True,
650
+ pad_token_id=50256,
651
+ )
652
+ text = outputs[0].get("generated_text", "") if isinstance(outputs, list) and outputs else str(outputs)
653
+ return GenerateResponse(output=text)
654
+ except Exception as e:
655
+ raise HTTPException(status_code=500, detail=f"Erreur LLM: {str(e)}")
656
+
657
  # Fonction de debug temporaire pour tester l'extraction
658
  def debug_extraction(line):
659
  """Teste l'extraction d'une ligne et affiche les résultats"""