Spaces:
Running
Running
File size: 8,806 Bytes
5fb7c14 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 5fb7c14 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 5fb7c14 fae6b38 5fb7c14 fae6b38 5fb7c14 fae6b38 5fb7c14 fae6b38 6ca0885 fae6b38 533067f fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 5fb7c14 fae6b38 5fb7c14 fae6b38 6ca0885 fae6b38 5fb7c14 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 fae6b38 6ca0885 5fb7c14 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# --- START OF FILE app.py ---
import os
import logging
from flask import Flask, make_response, render_template, request, redirect, url_for, session, jsonify, flash, Response, stream_with_context
from datetime import datetime, timedelta
import psycopg2
from psycopg2.extras import RealDictCursor
from google import genai
from google.genai import types
from utils import load_prompt # Assurez-vous que utils.py et la fonction load_prompt existent
# --- Configuration ---
logging.basicConfig(level=logging.INFO)
# Initialisation de Flask
app = Flask(__name__)
app.secret_key = os.environ.get("FLASK_SECRET_KEY", "uyyhhy77uu")
app.permanent_session_lifetime = timedelta(days=200)
# URLs et Clés API
DATABASE_URL = os.environ.get("DATABASE")
GOOGLE_API_KEY = os.environ.get("TOKEN")
# Configuration du client Gemini
try:
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"},
]
# --- Helpers ---
def create_connection():
return psycopg2.connect(DATABASE_URL)
# --- Routes Principales ---
@app.route('/')
def philosophie():
return render_template("philosophie.html")
# --- Routes API (Non-Streaming) ---
@app.route('/api/philosophy/courses', methods=['GET'])
def get_philosophy_courses():
try:
with create_connection() as conn:
with conn.cursor(cursor_factory=RealDictCursor) as cur:
cur.execute("SELECT id, title, author, updated_at FROM cours_philosophie ORDER BY title")
courses = cur.fetchall()
return jsonify(courses)
except Exception as e:
logging.error(f"Erreur lors de la récupération des cours : {e}")
return jsonify({"error": "Erreur interne du serveur."}), 500
@app.route('/api/philosophy/courses/<int:course_id>', methods=['GET'])
def get_philosophy_course(course_id):
try:
with create_connection() as conn:
with conn.cursor(cursor_factory=RealDictCursor) as cur:
cur.execute("SELECT id, title, content, author, created_at, updated_at FROM cours_philosophie WHERE id = %s", (course_id,))
course = cur.fetchone()
if course:
return jsonify(course)
return jsonify({"error": "Cours non trouvé"}), 404
except Exception as e:
logging.error(f"Erreur lors de la récupération du cours : {e}")
return jsonify({"error": "Erreur interne du serveur."}), 500
# --- Routes de Génération (Streaming) ---
def process_and_stream(model_id, prompt_content):
"""Génère et stream le contenu depuis le modèle Gemini."""
if not client:
yield "Erreur: Le client API n'est pas configuré."
return
try:
stream = client.models.generate_content_stream(
model=model_id,
contents=prompt_content,
config=types.GenerateContentConfig(safety_settings=SAFETY_SETTINGS)
)
for chunk in stream:
if chunk.text:
yield chunk.text
except Exception as e:
logging.error(f"Erreur de streaming Gemini ({model_id}): {e}")
yield f"\n\n**Erreur de génération :** {str(e)}"
@app.route('/stream_philo', methods=['POST'])
@app.route('/stream_philo_deepthink', methods=['POST'])
def stream_philo_text():
data = request.json
phi_prompt = data.get('question', '').strip()
phi_type = data.get('type', '1')
course_id = data.get('courseId')
if not phi_prompt:
return Response("Erreur: Veuillez saisir un sujet.", status=400, mimetype='text/plain')
# Déterminer le modèle et le fichier de prompt
is_deepthink = 'deepthink' in request.path
model_id = "gemini-2.5-pro" if is_deepthink else "gemini-2.5-flash"
prompt_files = {'1': 'philo_type1.txt', '2': 'philo_type2.txt'}
prompt_file = prompt_files.get(phi_type)
if not prompt_file:
return Response(f"Erreur: Type de sujet '{phi_type}' non valide.", status=400, mimetype='text/plain')
# Charger et formater le prompt
prompt_template = load_prompt(prompt_file)
final_prompt = prompt_template.format(phi_prompt=phi_prompt)
# Ajouter le contenu du cours si disponible
if course_id:
try:
with create_connection() as conn:
with conn.cursor(cursor_factory=RealDictCursor) as cur:
cur.execute("SELECT content FROM cours_philosophie WHERE id = %s", (course_id,))
result = cur.fetchone()
if result and result['content']:
final_prompt += f"\n\n--- EXTRAIT DE COURS POUR CONTEXTE ---\n{result['content']}"
except Exception as e:
logging.error(f"Erreur DB pour le cours {course_id}: {e}")
# Continuer sans le contenu du cours en cas d'erreur
def generate():
yield from process_and_stream(model_id, final_prompt)
return Response(stream_with_context(generate()), mimetype='text/plain')
@app.route('/stream_philo_image', methods=['POST'])
def stream_philo_image():
if 'image' not in request.files:
return Response("Erreur: Fichier image manquant.", status=400, mimetype='text/plain')
image_file = request.files['image']
if not image_file.filename:
return Response("Erreur: Aucun fichier sélectionné.", status=400, mimetype='text/plain')
try:
img_bytes = image_file.read()
image_part = types.Part.from_bytes(data=img_bytes, mime_type=image_file.mimetype)
prompt_text = load_prompt('philo_image_analysis.txt')
contents = [prompt_text, image_part]
# Le modèle vision pro est souvent le meilleur pour les images
model_id = "gemini-2.5-pro"
def generate():
yield from process_and_stream(model_id, contents)
return Response(stream_with_context(generate()), mimetype='text/plain')
except Exception as e:
logging.error(f"Erreur lors du traitement de l'image : {e}")
return Response("Erreur interne lors du traitement de l'image.", status=500, mimetype='text/plain')
# --- Routes d'Administration (inchangées) ---
@app.route('/admin/philosophy/courses', methods=['GET', 'POST', 'DELETE'])
def manage_philosophy_courses():
# ... (Le code de cette route reste le même qu'avant)
if request.method == 'GET':
try:
conn = psycopg2.connect(DATABASE_URL)
cur = conn.cursor(cursor_factory=RealDictCursor)
cur.execute("SELECT * FROM cours_philosophie")
courses = cur.fetchall()
cur.close()
conn.close()
return render_template('philosophy_courses.html', courses=courses)
except Exception as e:
flash(f'Erreur lors de la récupération des cours : {e}', 'danger')
return redirect(url_for('manage_philosophy_courses'))
elif request.method == 'POST':
if 'title' in request.form:
try:
title = request.form.get('title')
content = request.form.get('content')
author = request.form.get('author')
conn = psycopg2.connect(DATABASE_URL)
cur = conn.cursor()
cur.execute("INSERT INTO cours_philosophie (title, content, author) VALUES (%s, %s, %s)", (title, content, author))
conn.commit()
cur.close()
conn.close()
flash('Cours ajouté avec succès !', 'success')
return redirect(url_for('manage_philosophy_courses'))
except Exception as e:
flash(f'Erreur lors de l\'ajout du cours : {e}', 'danger')
return redirect(url_for('manage_philosophy_courses'))
else:
try:
course_id = request.form.get('id')
conn = psycopg2.connect(DATABASE_URL)
cur = conn.cursor()
cur.execute("DELETE FROM cours_philosophie WHERE id = %s", (course_id,))
conn.commit()
cur.close()
conn.close()
flash('Cours supprimé avec succès !', 'success')
return redirect(url_for('manage_philosophy_courses'))
except Exception as e:
flash(f'Erreur lors de la suppression du cours : {e}', 'danger')
return redirect(url_for('manage_philosophy_courses'))
# --- END OF FILE app.py --- |