# app.py import os import logging import json from flask import Flask, jsonify, render_template, request from pydantic import BaseModel, Field from typing import List, Optional import psycopg2 from psycopg2.extras import RealDictCursor from google import genai from google.genai import types from utils import load_prompt # --- Configuration --- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') app = Flask(__name__) app.secret_key = os.environ.get("FLASK_SECRET_KEY", "un-secret-par-defaut") # --- Modèles de Données Pydantic pour la sortie structurée --- class Argument(BaseModel): paragraphe_argumentatif: str = Field(description="Un unique paragraphe formant un argument complet. Il doit commencer par un connecteur logique (ex: 'Premièrement,'), suivi de son développement.") class Partie(BaseModel): chapeau: str = Field(description="Le paragraphe d'introduction de la partie.") arguments: list[Argument] = Field(description="La liste des paragraphes argumentatifs qui suivent le chapeau.") transition: Optional[str] = Field(description="Phrase ou court paragraphe de transition.", default=None) class Dissertation(BaseModel): sujet: str = Field(description="Le sujet exact de la dissertation, tel que posé par l'utilisateur.") prof: str = Field(description="Le nom du professeur, qui est toujours 'Mariam AI'.", default="Mariam AI") introduction: str = Field(description="L'introduction complète de la dissertation.") parties: List[Partie] conclusion: str = Field(description="La conclusion complète de la dissertation.") # --- Configuration Gemini --- try: GOOGLE_API_KEY = os.environ.get("TOKEN") if not GOOGLE_API_KEY: logging.warning("La variable d'environnement TOKEN (GOOGLE_API_KEY) n'est pas définie.") client = None else: client = genai.Client(api_key=GOOGLE_API_KEY) except Exception as e: logging.error(f"Erreur lors de l'initialisation du client GenAI: {e}") client = None 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"}, ] # --- Route Principale --- @app.route('/') def philosophie(): return render_template("philosophie.html") # --- Route API pour la génération de dissertation --- @app.route('/api/generate_dissertation', methods=['POST']) def generate_dissertation_api(): if not client: return jsonify({"error": "Le service IA n'est pas correctement configuré."}), 503 data = request.json sujet = data.get('question', '').strip() if not sujet: return jsonify({"error": "Le champ 'question' est obligatoire."}), 400 try: prompt_template = load_prompt('philo_dissertation_json.txt') final_prompt = prompt_template.format(phi_prompt=sujet) config = types.GenerateContentConfig( safety_settings=SAFETY_SETTINGS, response_mime_type="application/json", response_schema=Dissertation, ) response = client.models.generate_content( model="gemini-2.5-flash", contents=final_prompt, config=config ) if response.parsed: return jsonify(response.parsed.dict()) else: logging.error(f"Erreur de parsing de la réponse structurée. Réponse brute : {response.text}") return jsonify({"error": "Le modèle n'a pas pu générer une structure valide."}), 500 except Exception as e: logging.error(f"Erreur de génération Gemini : {e}") return jsonify({"error": f"Une erreur est survenue avec le service IA : {e}"}), 500 if __name__ == '__main__': app.run(debug=True, port=5000)