diff --git a/backend/requirements.txt b/backend/requirements.txt index ac81c7b..56914f2 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -19,7 +19,6 @@ google-api-python-client==2.173.0 google-auth==2.40.3 google-auth-httplib2==0.2.0 google-genai==1.21.1 -google-generativeai==0.8.5 googleapis-common-protos==1.70.0 greenlet==3.2.3 grpcio==1.73.0 @@ -44,6 +43,7 @@ pydantic-settings==2.10.1 pydantic_core==2.33.2 pyparsing==3.2.3 pypdf==5.6.1 +PyPDF2==3.0.1 python-dateutil==2.9.0.post0 python-docx==1.2.0 python-dotenv==1.1.1 diff --git a/backend/services/ai_service.py b/backend/services/ai_service.py index 3242692..9f854dd 100644 --- a/backend/services/ai_service.py +++ b/backend/services/ai_service.py @@ -3,13 +3,13 @@ import logging import sys from typing import Optional, Dict, Any -from google import genai -from google.genai import types +# MODIFIÉ ICI: Supprime 'from google.genai import types' car types.GenerateContentConfig n'est plus utilisé de cette manière +import google.generativeai as genai import mistralai from mistralai.client import MistralClient from fastapi import HTTPException, status -import anyio # <-- NOUVELLE IMPORTATION : Pour gérer les appels synchrones dans async +import anyio from core.config import settings @@ -28,28 +28,29 @@ try: except Exception as e: logger.error(f"Error during mistralai debug info collection: {e}") +# --- Configuration globale du client Gemini (Ce bloc est maintenant supprimé car la configuration est faite via GenerativeModel) --- +# Vous pouvez retirer ce bloc si vous l'aviez : +# if settings.LLM_PROVIDER == "gemini" and settings.GEMINI_API_KEY: +# try: +# genai.configure( +# api_key=settings.GEMINI_API_KEY, +# client_options={"api_endpoint": "generativelanguage.googleapis.com"} +# ) +# logger.info("GenAI client globally configured with API endpoint.") +# except Exception as e: +# logger.error(f"Erreur lors de la configuration globale de GenAI: {e}") + + class AIService: def __init__(self): self.provider = settings.LLM_PROVIDER self.model_name = settings.GEMINI_MODEL_NAME if self.provider == "gemini" else settings.MISTRAL_MODEL_NAME self.raw_safety_settings = [ - { - "category": "HARM_CATEGORY_HARASSMENT", - "threshold": "BLOCK_NONE" - }, - { - "category": "HARM_CATEGORY_HATE_SPEECH", - "threshold": "BLOCK_NONE" - }, - { - "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", - "threshold": "BLOCK_NONE" - }, - { - "category": "HARM_CATEGORY_DANGEROUS_CONTENT", - "threshold": "BLOCK_NONE" - }, + {"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"}, + {"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"}, + {"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"}, + {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"}, ] self.raw_generation_config = { @@ -60,21 +61,20 @@ class AIService: if self.provider == "gemini": try: - self.client = genai.Client(api_key=settings.GEMINI_API_KEY) - - self.gemini_config = types.GenerateContentConfig( - temperature=self.raw_generation_config["temperature"], - top_p=self.raw_generation_config["top_p"], - top_k=self.raw_generation_config["top_k"], - safety_settings=[ - types.SafetySetting(category=s["category"], threshold=s["threshold"]) - for s in self.raw_safety_settings - ] + # MODIFICATION CRUCIALE ICI : On initialise directement le GenerativeModel + # genai.Client() et types.GenerateContentConfig ne sont plus utilisés directement ici + self.model = genai.GenerativeModel( + model_name=self.model_name, + safety_settings=self.raw_safety_settings, # Passez safety_settings ici + generation_config=self.raw_generation_config # Passez generation_config ici + # La clé API est lue automatiquement depuis GEMINI_API_KEY si elle est configurée. + # Ou vous pouvez la passer explicitement: api_key=settings.GEMINI_API_KEY ) + logger.info(f"Modèle Gemini GenerativeModel initialisé avec modèle : {self.model_name}") except Exception as e: - logger.error(f"Erreur d'initialisation du client Gemini: {e}") - raise ValueError(f"Impossible d'initialiser le client Gemini. Vérifiez votre GEMINI_API_KEY. Erreur: {e}") + logger.error(f"Erreur d'initialisation du modèle Gemini: {e}") + raise ValueError(f"Impossible d'initialiser le modèle Gemini. Vérifiez votre GEMINI_API_KEY et le nom du modèle. Erreur: {e}") elif self.provider == "mistral": if not settings.MISTRAL_API_KEY: @@ -121,12 +121,11 @@ class AIService: {"role": "user", "parts": [{"text": prompt}]} ] - # MODIFIÉ ICI : Utilisation de anyio.to_thread.run_sync pour l'appel synchrone + # MODIFIÉ ICI : 'contents' est maintenant passé comme argument positionnel direct à generate_content response = await anyio.to_thread.run_sync( - self.client.models.generate_content, - model=self.model_name, - contents=contents, - config=self.gemini_config, + self.model.generate_content, + contents, # <-- Correction pour l'erreur "unexpected keyword argument 'contents'" + # Les configurations (température, safety_settings) sont déjà définies lors de l'initialisation de self.model ) response_content = response.text