Spaces:
Runtime error
Runtime error
| # app.py (Continuation du fichier) | |
| import json | |
| import os | |
| import sys | |
| from flask import Flask, request, jsonify, Response | |
| from flask_cors import CORS | |
| # Importation des modules backend | |
| from model_handler import generate_text_response, get_model_instance, MODEL_LOADED_SUCCESSFULLY | |
| from config import SYSTEM_PROMPT | |
| # --- Initialisation de l'Application Flask --- | |
| app = Flask(__name__) | |
| # La clé secrète n'est plus nécessaire sans session, mais on la laisse pour les bonnes pratiques. | |
| app.secret_key = os.environ.get("FLASK_SECRET_KEY", "simple_dev_key") | |
| # Permettre CORS pour toutes les routes | |
| CORS(app) | |
| # ----------------------------------------------------- | |
| # ROUTE D'API (Chat) | |
| # ----------------------------------------------------- | |
| # Route principale pour la génération de texte (streaming) - Reste INCHANGÉE | |
| def generate_text(): | |
| data = request.get_json(silent=True) | |
| if not data: | |
| return jsonify({"status": "Error", "message": "Données JSON manquantes ou format invalide."}), 400 | |
| prompt = data.get('prompt') | |
| history = data.get('history', []) | |
| if not prompt: | |
| return jsonify({"status": "Error", "message": "Le 'prompt' est requis dans le corps JSON."}), 400 | |
| def stream_with_sse(): | |
| response_stream = generate_text_response( | |
| prompt=prompt, | |
| history=history | |
| ) | |
| try: | |
| for chunk in response_stream: | |
| yield f"data: {json.dumps({'content': chunk})}\n\n" | |
| except Exception as e: | |
| error_message = f"Erreur de streaming LLM: {e}" | |
| print(f"Erreur de streaming: {error_message}", file=sys.stderr) | |
| yield f"data: {json.dumps({'error': 'Erreur interne du modèle de langage. ' + error_message})}\n\n" | |
| finally: | |
| yield "data: [DONE]\n\n" | |
| response = Response( | |
| stream_with_sse(), | |
| mimetype='text/event-stream' | |
| ) | |
| response.headers['Cache-Control'] = 'no-cache' | |
| response.headers['Connection'] = 'keep-alive' | |
| return response | |
| # NOUVELLE ROUTE: Route pour la génération de texte AVEC des fichiers (streaming) | |
| def generate_text_with_files(): | |
| """ | |
| Gère la conversation LLM en streaming avec des fichiers et de l'historique. | |
| Reçoit 'prompt', 'history', et une liste de 'files' via le corps de la requête JSON. | |
| Chaque fichier doit être encodé en base64. | |
| """ | |
| # 1. Préparation des données (JSON body) | |
| data = request.get_json(silent=True) | |
| if not data: | |
| return jsonify({"status": "Error", "message": "Données JSON manquantes ou format invalide."}), 400 | |
| prompt = data.get('prompt', '') # Le prompt peut être vide si seuls des fichiers sont envoyés | |
| history = data.get('history', []) | |
| files = data.get('files', []) # Liste de dict: [{"filename": "...", "content": "base64", "mime_type": "..."}] | |
| # Vérification minimale: au moins un prompt OU un fichier doit être présent | |
| if not prompt and not files: | |
| return jsonify({"status": "Error", "message": "Le 'prompt' ou au moins un fichier est requis."}), 400 | |
| # Fonction générateur pour le streaming SSE (similaire à la première route) | |
| def stream_with_sse_and_files(): | |
| # Le générateur LLM (MODIFIÉ: Passage de l'historique ET des fichiers) | |
| response_stream = generate_text_response( | |
| prompt=prompt, | |
| history=history, | |
| files=files # NOUVEAU: Passage des fichiers | |
| ) | |
| try: | |
| for chunk in response_stream: | |
| # Envoie le chunk comme un événement SSE | |
| yield f"data: {json.dumps({'content': chunk})}\n\n" | |
| except Exception as e: | |
| error_message = f"Erreur de streaming LLM: {e}" | |
| print(f"Erreur de streaming: {error_message}", file=sys.stderr) | |
| yield f"data: {json.dumps({'error': 'Erreur interne du modèle de langage. ' + error_message})}\n\n" | |
| finally: | |
| # Envoie le message de fin | |
| yield "data: [DONE]\n\n" | |
| # 3. Retourner la réponse en streaming SSE | |
| response = Response( | |
| stream_with_sse_and_files(), | |
| mimetype='text/event-stream' | |
| ) | |
| response.headers['Cache-Control'] = 'no-cache' | |
| response.headers['Connection'] = 'keep-alive' | |
| return response | |
| # Route de vérification de santé (Health Check) | |
| def health_check(): | |
| """Vérifie si l'API est opérationnelle et si le modèle LLM est chargé.""" | |
| status_message = "Model Ready" if MODEL_LOADED_SUCCESSFULLY else "Model Error (Check API Key)" | |
| return jsonify({"status": status_message, "service": "Operational"}), 200 | |
| # Route d'accueil simple pour vérification | |
| def index(): | |
| return jsonify({"status": "API Operational", "version": "v1.0"}), 200 | |
| # --- Initialisation de l'Application --- | |
| def init_app(): | |
| """Fonctions à appeler au démarrage.""" | |
| print("--- Démarrage du Backend LLM ---", file=sys.stderr) | |
| # 1. Vérification du statut de l'API Gemini | |
| get_model_instance() | |
| if MODEL_LOADED_SUCCESSFULLY: | |
| print(f"INFO: Configuration API réussie. Modèle LLM prêt. (Vérifiez le log de self-test ci-dessus)", file=sys.stderr) | |
| else: | |
| print(f"ALERTE: 🔴 Client Gemini non initialisé au démarrage. (Vérifiez le log détaillé ci-dessus pour la cause exacte)", file=sys.stderr) | |
| # Exécuter l'initialisation au démarrage de l'application | |
| init_app() | |
| if __name__ == '__main__': | |
| # Utiliser un port spécifique pour Hugging Face Spaces (7860) ou le port par défaut de Flask | |
| port = int(os.environ.get("PORT", 7860)) | |
| app.run(host='0.0.0.0', port=port, debug=True) |