Spaces:
Sleeping
Sleeping
from flask import Flask, render_template, request, Response | |
from google import genai | |
from google.genai import types | |
import os | |
import logging | |
import json | |
load_prompt =""" | |
Tu es yn professeur expérimenté en langue française. | |
# Instruction pour travail argumentatif de français niveau lycée | |
## Paramètres d'entrée attendus : | |
- `sujet` : La question/thème à traiter | |
- `choix` : Type de travail ("discuter", "dissertation", "étayer", "réfuter") | |
- `style` : Style d'écriture souhaité (ex: "raffiné") | |
## Traitement automatique selon le type : | |
### Si choix = "discuter" : | |
Produire un travail argumentatif suivant cette méthodologie : | |
**INTRODUCTION:** | |
- Approche par constat | |
- Problématique | |
- Annonce du plan | |
**DÉVELOPPEMENT:** | |
*Partie 1 : Thèse* | |
- Introduction partielle (énonce la thèse) | |
- Argument 1: Explications + Illustration (exemple + explication) | |
- Argument 2: Explications + Illustration (exemple + explication) | |
- Argument 3: Explications + Illustration (exemple + explication) | |
*Phrase de transition vers la deuxième partie* | |
*Partie 2 : Antithèse* | |
- Introduction partielle (énonce l'antithèse) | |
- Argument 1: Explications + Illustration (exemple + explication) | |
- Argument 2: Explications + Illustration (exemple + explication) | |
- Argument 3: Explications + Illustration (exemple + explication) | |
**CONCLUSION:** | |
- Bilan (Synthèse des arguments pour et contre) | |
- Ouverture du sujet (sous forme de phrase interrogative) | |
### Si choix = "dissertation" : | |
Produire une dissertation suivant cette méthodologie : | |
**Phase 1 : L'Introduction (en un seul paragraphe)** | |
- Amorce : Introduire le thème général | |
- Position du Sujet : Présenter l'auteur/œuvre, citer et reformuler le sujet | |
- Problématique : Poser la question centrale dialectique | |
- Annonce du Plan : Annoncer thèse et antithèse | |
**Phase 2 : Le Développement (en trois parties distinctes)** | |
*Partie 1 : La Thèse* | |
- Explorer et justifier l'affirmation initiale | |
- 2-3 arguments (structure A.I.E : Affirmation-Illustration-Explication) | |
- Exemples littéraires précis et analysés | |
*Partie 2 : L'Antithèse* | |
- Nuancer, critiquer ou présenter le point de vue opposé | |
- 2-3 arguments (structure A.I.E) | |
- Transition cruciale depuis la thèse | |
- Exemples littéraires précis et analysés | |
*Partie 3 : La Synthèse* | |
- Dépasser l'opposition thèse/antithèse | |
- Nouvelle perspective intégrant les éléments valides | |
- 2-3 arguments (structure A.I.E) | |
- Transition depuis l'antithèse | |
- Exemples littéraires précis et analysés | |
**Phase 3 : La Conclusion (en un seul paragraphe)** | |
- Bilan synthétique du parcours dialectique | |
- Réponse claire à la problématique | |
- Ouverture vers contexte plus large | |
### Si choix = "étayer" ou "réfuter" : | |
Produire un travail argumentatif suivant cette méthodologie : | |
**INTRODUCTION:** | |
- Approche par constat | |
- Problématique | |
- Annonce du plan | |
**DÉVELOPPEMENT:** | |
- Phrase chapeau (énonce la thèse principale : pour étayer ou contre pour réfuter) | |
- Argument 1: Explications + Illustration (exemple + explication) | |
- Argument 2: Explications + Illustration (exemple + explication) | |
- Argument 3: Explications + Illustration (exemple + explication) | |
**CONCLUSION:** | |
- Bilan (rappel de la thèse + arguments principaux) | |
- Ouverture du sujet (sous forme de phrase interrogative) | |
## Application du style : | |
Adapter l'écriture selon le style demandé (ex: "raffiné" = vocabulaire soutenu, syntaxe élaborée, références culturelles). | |
## Exemple de traitement : | |
**Entrée :** `sujet='L'homme est inoffensif', choix='étayer', style='raffiné'` | |
**Action :** Générer un travail argumentatif défendant la thèse "L'homme est inoffensif" avec un style raffiné, suivant la méthodologie "étayer". | |
""" | |
logging.basicConfig( | |
level=logging.INFO, | |
format='%(asctime)s - %(levelname)s - %(message)s' | |
) | |
app = Flask(__name__) | |
token = os.environ.get("TOKEN") | |
client = genai.Client(api_key=token) | |
default_generation_config = types.GenerateContentConfig( | |
temperature=1, | |
) | |
STANDARD_MODEL_NAME = "gemini-2.5-flash" | |
DEEPTHINK_MODEL_NAME = "gemini-2.5-pro" | |
# Page d'accueil | |
def index(): | |
logging.info("Page index demandée.") | |
return render_template('index.html') | |
# API pour génération française | |
def gpt_francais(): | |
logging.info(f"Requête {request.method} reçue sur /api/francais") | |
french_prompt = request.args.get('sujet', '').strip() | |
choix = request.args.get('choix', '').strip() | |
style = request.args.get('style', '').strip() | |
use_deepthink = request.args.get('use_deepthink', 'false').lower() == 'true' | |
logging.info( | |
f"Données reçues : sujet='{french_prompt[:50]}', choix='{choix}', style='{style}', deepthink={use_deepthink}" | |
) | |
if not french_prompt: | |
logging.warning("Sujet vide, retour erreur.") | |
return Response( | |
"data: " + json.dumps({'type': 'error', 'content': 'Erreur: Le sujet ne peut pas être vide.'}) + "\n\n", | |
mimetype='text/event-stream' | |
), 400 | |
model_to_use = DEEPTHINK_MODEL_NAME if use_deepthink else STANDARD_MODEL_NAME | |
logging.info(f"Modèle utilisé : {model_to_use}") | |
try: | |
system_instruction = load_prompt() | |
except Exception as e: | |
logging.exception("Erreur lors du chargement du prompt système.") | |
system_instruction = "Tu es un assistant spécialisé en français." | |
user_prompt = f"Sujet: {french_prompt}\nType: {choix}\nStyle: {style}" | |
config = types.GenerateContentConfig( | |
system_instruction=system_instruction, | |
temperature=1 | |
) | |
if use_deepthink: | |
config.thinking_config = types.ThinkingConfig(include_thoughts=True) | |
def generate_stream(): | |
try: | |
logging.info("Démarrage du streaming de génération...") | |
thoughts = "" | |
answer = "" | |
for chunk in client.models.generate_content_stream( | |
model=model_to_use, | |
contents=[user_prompt], | |
config=config | |
): | |
for part in chunk.candidates[0].content.parts: | |
if not part.text: | |
continue | |
elif hasattr(part, 'thought') and part.thought: | |
if not thoughts: | |
logging.info("Premiers éléments de réflexion envoyés.") | |
yield "data: " + json.dumps({'type': 'thoughts_start'}) + "\n\n" | |
thoughts += part.text | |
yield "data: " + json.dumps({'type': 'thought', 'content': part.text}) + "\n\n" | |
else: | |
if not answer: | |
logging.info("Premiers éléments de réponse envoyés.") | |
yield "data: " + json.dumps({'type': 'answer_start'}) + "\n\n" | |
answer += part.text | |
yield "data: " + json.dumps({'type': 'answer', 'content': part.text}) + "\n\n" | |
logging.info("Fin du streaming de génération.") | |
yield "data: " + json.dumps({'type': 'done'}) + "\n\n" | |
except Exception: | |
logging.exception("Erreur pendant la génération de contenu.") | |
yield "data: " + json.dumps({'type': 'error', 'content': 'Erreur serveur pendant la génération.'}) + "\n\n" | |
return Response(generate_stream(), mimetype='text/event-stream') | |
# API pour analyse de texte à partir d'images | |
def gpt_francais_cc(): | |
logging.info("Requête reçue sur /api/etude-texte") | |
if 'images' not in request.files: | |
logging.warning("Aucun fichier image reçu.") | |
return Response( | |
"data: " + json.dumps({'type': 'error', 'content': 'Aucun fichier image envoyé.'}) + "\n\n", | |
mimetype='text/event-stream' | |
), 400 | |
images = request.files.getlist('images') | |
if not images or not images[0].filename: | |
logging.warning("Liste d'images vide.") | |
return Response( | |
"data: " + json.dumps({'type': 'error', 'content': 'Aucune image sélectionnée.'}) + "\n\n", | |
mimetype='text/event-stream' | |
), 400 | |
def generate_image_analysis(): | |
try: | |
logging.info(f"Nombre d'images reçues : {len(images)}") | |
system_instruction = "Tu es un assistant spécialisé dans l'analyse de textes et de documents. réponds strictement aux questions." | |
content = ["Réponds aux questions présentes dans les images."] | |
for img in images: | |
if img.filename: | |
logging.info(f"Traitement image : {img.filename}") | |
img_data = img.read() | |
img_part = types.Part.from_bytes( | |
data=img_data, | |
mime_type=img.content_type or 'image/jpeg' | |
) | |
content.append(img_part) | |
config = types.GenerateContentConfig( | |
system_instruction=system_instruction, | |
temperature=0.7, | |
max_output_tokens=4096 | |
) | |
logging.info("Démarrage du streaming d'analyse d'image...") | |
for chunk in client.models.generate_content_stream( | |
model=STANDARD_MODEL_NAME, | |
contents=content, | |
config=config | |
): | |
for part in chunk.candidates[0].content.parts: | |
if part.text: | |
yield "data: " + json.dumps({'type': 'content', 'content': part.text}) + "\n\n" | |
logging.info("Fin du streaming d'analyse d'image.") | |
yield "data: " + json.dumps({'type': 'done'}) + "\n\n" | |
except Exception: | |
logging.exception("Erreur pendant l'analyse d'image.") | |
yield "data: " + json.dumps({'type': 'error', 'content': "Erreur serveur pendant l'analyse de l'image."}) + "\n\n" | |
return Response(generate_image_analysis(), mimetype='text/event-stream') | |
if __name__ == '__main__': | |
logging.info("Démarrage du serveur Flask avec Gemini SDK...") | |
app.run(debug=True) |