|
from flask import Flask, render_template, request, jsonify, Response, stream_with_context |
|
from google import genai |
|
from google.genai import types |
|
import os |
|
from PIL import Image |
|
import tempfile |
|
import json |
|
import logging |
|
|
|
app = Flask(__name__) |
|
|
|
|
|
logging.basicConfig(level=logging.INFO) |
|
|
|
|
|
token = os.environ.get("TOKEN") |
|
if not token: |
|
logging.error("API Token (TOKEN environment variable) not found!") |
|
|
|
|
|
genai_client = genai.Client(api_key=token) |
|
|
|
SAFETY_SETTINGS = [ |
|
types.SafetySetting( |
|
category=types.HarmCategory.HARM_CATEGORY_HATE_SPEECH, |
|
threshold=types.HarmBlockThreshold.BLOCK_NONE, |
|
), |
|
types.SafetySetting( |
|
category=types.HarmCategory.HARM_CATEGORY_HARASSMENT, |
|
threshold=types.HarmBlockThreshold.BLOCK_NONE, |
|
), |
|
types.SafetySetting( |
|
category=types.HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT, |
|
threshold=types.HarmBlockThreshold.BLOCK_NONE, |
|
), |
|
types.SafetySetting( |
|
category=types.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT, |
|
threshold=types.HarmBlockThreshold.BLOCK_NONE, |
|
) |
|
] |
|
|
|
|
|
prompt_tableau = """ |
|
Traite selon cette méthodologie : |
|
|
|
La Méthodologie de la Lecture Méthodique |
|
La lecture méthodique est une analyse approfondie et interprétative d'un extrait de texte. Son objectif est de comprendre comment le texte est construit (sa forme, sa structure, son langage) pour produire du sens et des effets sur le lecteur. Elle se déroule en plusieurs étapes structurées. |
|
Étape 1 : Observation Initiale et Identification du Texte |
|
(Cette partie reste identique à la précédente version : Examen Visuel, Identification Contextuelle, Première Qualification sur le genre, la forme de discours et l'objectif principal.) |
|
Examen Visuel : Mise en page, longueur, paragraphes, typographie... |
|
Identification Contextuelle : Source (auteur, œuvre, date...), situation de l'extrait. |
|
Première Qualification : Genre littéraire, forme de discours (discours/récit), objectif principal (narratif, descriptif, argumentatif, explicatif/informatif). |
|
Étape 2 : Compréhension Globale du Texte |
|
(Cette partie reste identique : S'assurer de la compréhension littérale via des questions clés.) |
|
Thème(s), Situation (Qui, quoi, où, quand ?), Énonciation (Qui parle ? Point de vue/Focalisation ?), Progression, Temps verbaux dominants. |
|
Étape 3 : Analyse Approfondie et Structurée |
|
C'est le cœur de la lecture méthodique. Il s'agit d'analyser comment le texte produit du sens et des effets, en s'appuyant sur des outils précis et nommés individuellement. |
|
Définir les Axes de Lecture : |
|
Trouver deux axes de lecture principaux (idées directrices). |
|
Diviser chaque axe en deux sous-axes qui précisent l'idée principale. |
|
Choisir et Utiliser les Outils d'Analyse Littéraire : |
|
Pour chaque sous-axe, sélectionner deux outils d'analyse pertinents et spécifiques. Il est crucial d'utiliser la terminologie technique reconnue (les noms "officiels") pour chaque outil. Voici des exemples d'outils spécifiques (cette liste n'est pas exhaustive) : |
|
Lexique : Champ lexical (préciser lequel : ex. Champ lexical de la nature), Registre de langue (préciser : ex. Registre familier), Terme mélioratif, Terme péjoratif, Néologisme, Archaïsme, Connotation. Etc... |
|
Grammaire et Syntaxe : Mode verbal (préciser : ex. Subjonctif à valeur de souhait), Temps verbal (préciser avec sa valeur : ex. Imparfait descriptif, Passé simple de premier plan), Type de phrase (ex. Phrase interrogative), Forme de phrase (ex. Forme exclamative, Forme négative), Pronom personnel (préciser lequel et sa référence/valeur : ex. Pronom "on" indéfini), Connecteur logique (préciser : ex. Connecteur d'opposition "mais"), Adjectif qualificatif (noter sa fonction ou valeur : ex. Adjectif antéposé à valeur subjective), Adverbe (noter sa valeur : ex. Adverbe d'intensité). Etc... |
|
Figures de Style : Comparaison, Métaphore, Personnification, Allégorie, Métonymie, Synecdoque, Périphrase, Antithèse, Oxymore, Chiasme, Parallélisme, Anaphore, Accumulation, Gradation, Hyperbole, Litote, Euphémisme, Ironie, Prétérition, Allitération, Assonance. Etc... |
|
Énonciation : Indice de personne (ex. Marque de la première personne "je"), Modalisation (préciser : ex. Adverbe de certitude, Verbe d'opinion), Discours direct, Discours indirect, Discours indirect libre, Focalisation (préciser : Focalisation interne, Focalisation externe, Focalisation zéro). Etc... |
|
Structure et Rythme : Paragraphe (analyser sa fonction), Strophe, Versification (si poésie : type de vers, rimes...), Ponctuation expressive (ex. Points de suspension), Longueur de phrase (ex. Phrase courte et nominale). Etc... |
|
Tonalité / Registre Littéraire : Tonalité lyrique, Tonalité pathétique, Tonalité tragique, Tonalité comique, Tonalité satirique, Tonalité polémique, Tonalité épique, Tonalité fantastique. (Identifier la tonalité dominante ou spécifique à un passage). Etc... |
|
Organiser l'Analyse dans un Tableau : |
|
Présenter les résultats pour chaque sous-axe dans un tableau à trois colonnes : |
|
Je ne veux pas qu'un outil apparaisse deux fois. Et Aussi j'aimerais uniquement des noms officiel. |
|
Outils d'analyse : Nommer précisément et individuellement l'outil identifié. Utilisez le terme technique exact. Un seul outil par ligne/entrée. Par exemple, écrivez "Métaphore" sur une ligne, et si vous trouvez aussi une comparaison pertinente, écrivez "Comparaison" sur une autre ligne. N'écrivez jamais "Métaphore/Comparaison" ou "Figure de style" de manière générale. |
|
Repérage / Citation : Citer le ou les passages exacts du texte où l'outil est repéré. |
|
Interprétation : Expliquer l'effet produit par cet outil spécifique dans le contexte, en lien avec le sous-axe et l'axe de lecture. |
|
Rappel : Analyser un Fait de Langue (Processus en 3 temps pour l'Interprétation) |
|
(Cette partie reste identique : Observation/Repérage [Quoi ?], Explication technique [Comment ?], Interprétation [Pourquoi ? Quel effet ? Quel sens ?]) |
|
Structure Finale de la Présentation de l'Analyse (Synthèse des consignes) |
|
L'analyse méthodique doit être présentée de manière structurée : |
|
Deux axes de lecture distincts. |
|
Chaque axe subdivisé en deux sous-axes. |
|
Chaque sous-axe analysé via trois outils d'analyse spécifiques, nommés individuellement avec leur terme technique reconnu et officiel. |
|
Résultats présentés en tableau(x) ("Outils d'analyse", "Repérage / Citation", "Interprétation"), en respectant la règle un seul outil par entrée dans la première colonne. |
|
""" |
|
|
|
prompt_redaction = """ |
|
III - LE COMMENTAIRE COMPOSÉ |
|
|
|
PRÉSENTATION DE L'EXERCICE |
|
Le commentaire composé, sujet de type 2, est un exercice écrit présenté au baccalauréat. C'est un exercice qui se fait à la base d'un texte. Procéder au commentaire composé d'un texte, c'est rendre compte de sa lecture. Lire un texte, c'est l'analyser, l'expliquer, l'interpréter, le décoder afin de le rendre compréhensible et accessible à tous. L'explication qui sera faite du texte découle des impressions premières dégagées après la lecture de ce dernier. Par ailleurs, ces impressions ou hypothèses de lecture seront justifiées, confirmées ou infirmées grâce à l'exploitation du texte et la convocation d'outils ou instruments d'analyse compris dans le texte. En outre, procéder au commentaire composé d'un texte, c'est aussi respecter un certain nombre de conventions. Le commentaire composé commence par une introduction dont l'objectif est de situer le texte, de le présenter et d'annoncer ses centres d'intérêt. Il propose par la suite un développement qui permet de répondre progressivement aux hypothèses de lecture. Il se termine par une conclusion qui permet d'établir un bilan, le cas échéant une opinion personnelle, et d'effectuer une ouverture à travers un rapprochement littéraire ou thématique avec d'autres œuvres. |
|
|
|
LA RÉDACTION DE L'INTRODUCTION |
|
La rédaction d'un commentaire composé doit comporter trois parties rédigées en un seul paragraphe. Les différentes étapes de l'introduction sont : |
|
|
|
La situation du texte ou la mise en contexte. Dans cette partie, différentes approches sont possibles. La première consiste à s'inspirer de la vie littéraire de l'auteur si le texte a un quelconque lien avec cette dernière. La deuxième, quant à elle, tient compte de la tendance littéraire à laquelle appartient l'auteur du texte (mouvement littéraire). La troisième et dernière consiste à prendre en compte la thématique littéraire du texte étudié. En outre, situer le texte implique aussi que nous identifiions le genre littéraire auquel appartient le texte. De plus, tous les éléments ou renseignements offerts par le paratexte doivent être indiqués, à savoir la source (nom de l'auteur, le titre de l'œuvre d'où est extrait le texte, la ville de publication, la maison d'édition, l'année de publication, la pagination), le chapeau et, le cas échéant, le titre voire la position de l'extrait dans l'œuvre. La présentation du texte. Ici, il faut préciser le type du texte, le ton littéraire du texte, l'idée générale du texte et les mouvements du texte. L'annonce du plan. Dans cette dernière partie de l'introduction, il est recommandé de formuler clairement et précisément les différents axes de lecture ou centres d'intérêt qui constitueront les parties du développement. |
|
|
|
LA RÉDACTION DU COMMENTAIRE COMPOSÉ |
|
Chaque paragraphe du commentaire composé s'appesantit sur la logique d'un axe de lecture. Il comporte un sous-axe développé à l'aide des instruments d'analyse identifiables à travers les références textuelles et leurs interprétations. Cependant, développer ce sous-axe ne consiste pas à juxtaposer les instruments d'analyse les uns après les autres. Il s'agit plutôt d'énoncer un instrument d'analyse et la référence textuelle faisant foi et de l'accompagner de son interprétation. Aussi, les autres outils d'analyse qui seront évoqués obéiront à la même logique que le précédent. Par ailleurs, après avoir énuméré les outils d'analyse permettant de justifier la présence du sous-axe retenu, le paragraphe du commentaire composé se termine par une conclusion partielle. |
|
|
|
Schématisation du développement d'un commentaire composé : |
|
|
|
La phrase chapeau dans laquelle on énumère l'axe 1 : |
|
|
|
Sous-axe 1 : outils d'analyse 1 + référence textuelle + interprétation + connecteur logique d'addition (C.L.A) + outil d'analyse 2 + référence textuelle + interprétation + conclusion partielle. La phrase de transition est le pont entre les deux parties (les deux axes de lecture). |
|
|
|
La phrase chapeau dans laquelle on énumère l'axe 2 : |
|
|
|
Sous-axe 2 : outils d'analyse 1 + référence textuelle + interprétation + connecteur logique d'addition (C.L.A) + outil d'analyse 2 + référence textuelle + interprétation + conclusion partielle. |
|
|
|
IV - LA CONCLUSION DU COMMENTAIRE COMPOSÉ |
|
|
|
Elle comporte deux ou trois parties regroupées en un seul paragraphe. Ces parties sont : |
|
|
|
Le bilan des analyses faites à partir des grandes idées développées dans le corps du devoir. Il s'agit ici de faire une synthèse des éléments d'interprétation mis en évidence dans chaque partie du développement. Cependant, il ne s'agit pas de répéter et développer les grandes idées de chaque partie mais de les résumer. |
|
|
|
L'opinion personnelle fait référence à un intérêt personnel dégagé à partir de la lecture du texte. |
|
|
|
L'élargissement/ouverture peut se faire sous deux approches : la première approche consiste à comparer ou rapprocher le texte étudié à une autre œuvre abordant le même thème. Cela peut être du même auteur ou d'un auteur différent. La deuxième approche consiste à montrer la nouveauté, l'originalité, la distinction, la singularité du texte étudié en rapprochement avec le texte convoqué en tenant compte du thème, ceci pour mettre en relief la manière d'écrire ou le style d'écriture de l'auteur convoqué. Cette dernière approche permet de mettre en évidence les différences stylistiques utilisées par les deux auteurs dans la mesure où ils abordent le même thème. |
|
|
|
Voici un exercice a trous présentant la rédaction.. référence textuelle= repérage. Et ça doit absolument être reporté. |
|
|
|
EXERCICE À TROUS |
|
|
|
Le thème de ... (thème du texte) a souvent fait l'objet de nombreuses préoccupations dans le monde littéraire (mais pas que). C’est dans ce cadre que s'inscrit l'extrait ... (titre du texte) qui fait l'objet de notre étude, de l'écrivain... (nom de l'auteur), tiré de son œuvre ... (préciser le genre littéraire de l'œuvre) ... (maison d'édition), en ... (date de publication) à la (aux) page (s) …. Dans ce texte (type du texte) à ton ... (tonalité du texte/facultative), structure, nous verrons en premier lieu,...(axe 1) et en second lieu, ...(axe 2). |
|
|
|
Dans son extrait (poème), l’auteur met en relief ... (axe 1) à travers ... (sous-axe 1) et ... (sous-axe 2). |
|
|
|
S'agissant de ... (sous-axe 1), l’écrivain utilise ... (outil d'analyse 1 + référence textuelle) pour montrer ... (interprétation). Aussi (de plus), par l'usage de... (outil d'analyse 2 + référence textuelle), l'écrivain ... (interprétation). Mieux encore, ... (outil d'analyse 3 + référence textuelle) nous donne également la possibilité d’appréhender ... (sous-axe 2). |
|
De plus, l'homme de lettres emploie ... (outil d'analyse 1 + référence textuelle) pour ... (interprétation). Il se sert aussi de ...(outil d'analyse 2 + référence textuelle) afin de ... (interprétation). Pour continuer sa (description, représentation), le ...(nationalité de l'auteur) se manque pas de faire recours à ... (outil d'analyse 3 référence textuelle) Ici, il s'agit pour l'auteur autour de ... (sous-axe 1) et ... (sous-axe 2). |
|
|
|
Après avoir démontré ... (axe 1), voyons à présent ... (axe 2). |
|
|
|
En second lieu, le poète (l’écrivain ou l’homme de lettres) met en exergue ... (axe 2) en s’appuyant d’une part, sur... (sous-axe 1) et d’autre part, sur... (sous-axe 2). En ce qui concerne ... (sous-axe 1), l'homme de lettres met d'abord en évidence l'aspect (le caractère) ... (interprétation) comme en témoigne l'emploi (l'usage de) ... (outil d’analyse 1 + référence textuelle). Ensuite, ... (outil d'analyse 2 + référence textuelle) dévoile que... (interprétation) Enfin, ... (outil d'analyse 3 + référence textuelle) suggère que… (interprétation) . ... (axe 2) se révèle grâce à …. |
|
En parlant de ... (sous-axe 2), l'auteur met l'accent en premier sur… (interprétation), comme nous pouvons le voir avec la récurrence de (du/des) ... (rappel du sous-axe 2), le poète (l'auteur) souligne ... (interprétation) toujours dans le même sens de ... (rappel du sous-axe 2) . Il use de ... (outil d’analyse 2 + référence textuelle). Dès lors, on peut déduire que ...(interprétation) utilise ... (outil d’analyse 3 + référence textuelle). |
|
Ainsi, ... (axe 2) est lié (e) à ... (sous-axe 1) et à ... (sous-axe 2). |
|
|
|
Somme toute, ... (titre du texte) organise son sens autour de … (axe 1) et de ... (axe 2). De ces deux centres d’intérêt découlent respectivement, d’une part, … (sous-axe 1 de l'axe 1) et ... (sous-axe 2 de l'axe 1) et, d’autre part, … (sous-axe 1 de l'axe 2) et … (sous-axe 2 de l'axe 2). À travers ce texte, ...(nom de l'auteur) nous ... (opinion personnelle). Une telle optique est perceptible dans la logique de... (nom de l'auteur nous permettant de faire un rapprochement thématique), dans son œuvre ...(titre de l’œuvre), dans lequel il aborde… (bref résumé de l'œuvre en question qui peut être facultatif). |
|
""" |
|
|
|
|
|
generate_config = types.GenerateContentConfig( |
|
safety_settings=SAFETY_SETTINGS |
|
) |
|
|
|
|
|
MODEL_ID_STANDARD = "gemini-2.0-flash" |
|
MODEL_ID_DEEPTHINK = "gemini-2.5-pro-exp-03-25" |
|
|
|
|
|
|
|
def generate_table_stream(image, consignes="", model_id=MODEL_ID_STANDARD): |
|
"""Génère le tableau d'analyse à partir de l'image en intégrant éventuellement des consignes""" |
|
prompt = prompt_tableau |
|
if consignes: |
|
prompt += "\n\nConsignes supplémentaires de l'utilisateur :\n" + consignes |
|
|
|
logging.info(f"Generating table using model: {model_id}") |
|
try: |
|
response_stream = genai_client.models.generate_content_stream( |
|
model=model_id, |
|
contents=[prompt, image], |
|
config=generate_config |
|
) |
|
|
|
for chunk in response_stream: |
|
if chunk.text: |
|
yield json.dumps({"type": "tableau", "chunk": chunk.text}) + "\n" |
|
except Exception as e: |
|
logging.error(f"Error during table generation stream: {e}", exc_info=True) |
|
yield json.dumps({"type": "error", "error": f"Erreur lors de la génération du tableau: {e}"}) + "\n" |
|
|
|
|
|
def generate_dissertation_stream(tableau, model_id=MODEL_ID_STANDARD): |
|
"""Génère la dissertation basée sur le tableau""" |
|
prompt = f""" |
|
{prompt_redaction} |
|
|
|
--- Tableau d'Analyse Fourni --- |
|
{tableau} |
|
--- Fin du Tableau --- |
|
|
|
Écris maintenant une rédaction structurée pour le commentaire composé basé EXCLUSIVEMENT sur le tableau d'analyse fourni ci-dessus, en suivant strictement le modèle de l'exercice à trous présenté dans la première partie de ce prompt. Assure-toi d'utiliser les "Repérage / Citation" du tableau quand le modèle le demande. |
|
""" |
|
|
|
logging.info(f"Generating dissertation using model: {model_id}") |
|
try: |
|
response_stream = genai_client.models.generate_content_stream( |
|
model=model_id, |
|
contents=prompt, |
|
config=generate_config |
|
) |
|
|
|
for chunk in response_stream: |
|
if chunk.text: |
|
yield json.dumps({"type": "dissertation", "chunk": chunk.text}) + "\n" |
|
except Exception as e: |
|
logging.error(f"Error during dissertation generation stream: {e}", exc_info=True) |
|
yield json.dumps({"type": "error", "error": f"Erreur lors de la génération de la dissertation: {e}"}) + "\n" |
|
|
|
|
|
|
|
|
|
|
|
@app.route('/') |
|
def index(): |
|
|
|
return render_template('index.html') |
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.route('/analyze', methods=['POST']) |
|
def analyze(): |
|
if 'image' not in request.files: |
|
logging.warning("Analyze request received without image file.") |
|
return jsonify({'error': 'No image uploaded'}), 400 |
|
|
|
image_file = request.files['image'] |
|
consignes = request.form.get("consignes", "") |
|
|
|
use_deepthink = request.form.get('use_deepthink', 'false').lower() == 'true' |
|
|
|
|
|
if use_deepthink: |
|
model_id = MODEL_ID_DEEPTHINK |
|
logging.info("DeepThink requested, using advanced model.") |
|
else: |
|
model_id = MODEL_ID_STANDARD |
|
logging.info("Standard analysis requested, using standard model.") |
|
|
|
|
|
temp_file = None |
|
try: |
|
|
|
|
|
with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as temp_file: |
|
image_file.save(temp_file.name) |
|
|
|
try: |
|
image = Image.open(temp_file.name) |
|
image.verify() |
|
|
|
image = Image.open(temp_file.name) |
|
except (IOError, SyntaxError) as e: |
|
logging.error(f"Invalid image file uploaded: {e}") |
|
return jsonify({'error': f'Invalid or corrupted image file: {e}'}), 400 |
|
|
|
|
|
|
|
|
|
|
|
@stream_with_context |
|
def generate(): |
|
temp_file_path = temp_file.name |
|
full_tableau_content = "" |
|
try: |
|
logging.info("Starting table generation stream...") |
|
|
|
for chunk_json in generate_table_stream(image, consignes, model_id): |
|
try: |
|
chunk_data = json.loads(chunk_json) |
|
if chunk_data.get("type") == "error": |
|
logging.error(f"Error received from table stream: {chunk_data.get('error')}") |
|
yield chunk_json |
|
return |
|
elif chunk_data.get("type") == "tableau": |
|
full_tableau_content += chunk_data.get("chunk", "") |
|
yield chunk_json |
|
except json.JSONDecodeError: |
|
logging.error(f"Received invalid JSON from table stream: {chunk_json}") |
|
|
|
yield json.dumps({"type": "error", "error": "Invalid data received during table generation."}) + "\n" |
|
return |
|
|
|
logging.info("Table generation stream finished.") |
|
logging.info("Starting dissertation generation stream...") |
|
|
|
|
|
if full_tableau_content: |
|
for chunk_json in generate_dissertation_stream(full_tableau_content, model_id): |
|
try: |
|
chunk_data = json.loads(chunk_json) |
|
if chunk_data.get("type") == "error": |
|
logging.error(f"Error received from dissertation stream: {chunk_data.get('error')}") |
|
yield chunk_json |
|
|
|
elif chunk_data.get("type") == "dissertation": |
|
yield chunk_json |
|
except json.JSONDecodeError: |
|
logging.error(f"Received invalid JSON from dissertation stream: {chunk_json}") |
|
yield json.dumps({"type": "error", "error": "Invalid data received during dissertation generation."}) + "\n" |
|
|
|
else: |
|
logging.warning("Tableau content is empty, skipping dissertation generation.") |
|
yield json.dumps({"type": "error", "error": "Tableau d'analyse non généré, impossible de créer la dissertation."}) + "\n" |
|
|
|
logging.info("Dissertation generation stream finished.") |
|
|
|
except Exception as e: |
|
logging.error(f"Error during streaming generation: {e}", exc_info=True) |
|
|
|
yield json.dumps({"type": "error", "error": f"Une erreur interne est survenue: {e}"}) + "\n" |
|
finally: |
|
|
|
|
|
|
|
pass |
|
|
|
|
|
|
|
response = Response(generate(), content_type='text/event-stream') |
|
|
|
|
|
if temp_file and os.path.exists(temp_file.name): |
|
cleanup_path = temp_file.name |
|
response.call_on_close(lambda: os.unlink(cleanup_path) if os.path.exists(cleanup_path) else None) |
|
logging.info(f"Scheduled cleanup for temp file: {cleanup_path}") |
|
|
|
|
|
return response |
|
|
|
except Exception as e: |
|
|
|
logging.error(f"Error processing upload before streaming: {e}", exc_info=True) |
|
|
|
if temp_file and os.path.exists(temp_file.name): |
|
try: |
|
os.unlink(temp_file.name) |
|
logging.info(f"Cleaned up temp file due to pre-stream error: {temp_file.name}") |
|
except OSError as unlink_error: |
|
logging.error(f"Error unlinking temp file during error handling: {unlink_error}") |
|
return jsonify({'error': f'Error processing file: {e}'}), 500 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__': |
|
|
|
|
|
app.run(debug=True) |