Update app.py
Browse files
app.py
CHANGED
@@ -1,17 +1,24 @@
|
|
1 |
-
from flask import Flask, render_template, request, jsonify
|
2 |
from google import genai
|
3 |
from google.genai import types
|
4 |
import os
|
5 |
from PIL import Image
|
6 |
import tempfile
|
|
|
|
|
7 |
|
8 |
app = Flask(__name__)
|
9 |
|
|
|
|
|
|
|
10 |
# Configuration de l'API Gemini
|
11 |
token = os.environ.get("TOKEN")
|
|
|
|
|
|
|
|
|
12 |
genai_client = genai.Client(api_key=token)
|
13 |
-
|
14 |
-
|
15 |
|
16 |
SAFETY_SETTINGS = [
|
17 |
types.SafetySetting(
|
@@ -32,91 +39,47 @@ SAFETY_SETTINGS = [
|
|
32 |
)
|
33 |
]
|
34 |
|
|
|
35 |
prompt_tableau = """
|
36 |
-
|
37 |
Traite selon cette méthodologie :
|
38 |
|
39 |
-
. Voici la méthodologie révisée pour insister sur l'utilisation de noms précis et uniques pour chaque outil d'analyse, sans regroupement dans une même case.
|
40 |
-
|
41 |
La Méthodologie de la Lecture Méthodique
|
42 |
-
|
43 |
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.
|
44 |
-
|
45 |
Étape 1 : Observation Initiale et Identification du Texte
|
46 |
-
|
47 |
(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.)
|
48 |
-
|
49 |
Examen Visuel : Mise en page, longueur, paragraphes, typographie...
|
50 |
-
|
51 |
Identification Contextuelle : Source (auteur, œuvre, date...), situation de l'extrait.
|
52 |
-
|
53 |
Première Qualification : Genre littéraire, forme de discours (discours/récit), objectif principal (narratif, descriptif, argumentatif, explicatif/informatif).
|
54 |
-
|
55 |
Étape 2 : Compréhension Globale du Texte
|
56 |
-
|
57 |
(Cette partie reste identique : S'assurer de la compréhension littérale via des questions clés.)
|
58 |
-
|
59 |
Thème(s), Situation (Qui, quoi, où, quand ?), Énonciation (Qui parle ? Point de vue/Focalisation ?), Progression, Temps verbaux dominants.
|
60 |
-
|
61 |
Étape 3 : Analyse Approfondie et Structurée
|
62 |
-
|
63 |
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.
|
64 |
-
|
65 |
Définir les Axes de Lecture :
|
66 |
-
|
67 |
Trouver deux axes de lecture principaux (idées directrices).
|
68 |
-
|
69 |
Diviser chaque axe en deux sous-axes qui précisent l'idée principale.
|
70 |
-
|
71 |
Choisir et Utiliser les Outils d'Analyse Littéraire :
|
72 |
-
|
73 |
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) :
|
74 |
-
|
75 |
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...
|
76 |
-
|
77 |
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...
|
78 |
-
|
79 |
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...
|
80 |
-
|
81 |
É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...
|
82 |
-
|
83 |
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...
|
84 |
-
|
85 |
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...
|
86 |
-
|
87 |
Organiser l'Analyse dans un Tableau :
|
88 |
-
|
89 |
Présenter les résultats pour chaque sous-axe dans un tableau à trois colonnes :
|
90 |
-
|
91 |
Je ne veux pas qu'un outil apparaisse deux fois. Et Aussi j'aimerais uniquement des noms officiel.
|
92 |
-
|
93 |
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.
|
94 |
-
|
95 |
Repérage / Citation : Citer le ou les passages exacts du texte où l'outil est repéré.
|
96 |
-
|
97 |
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.
|
98 |
-
|
99 |
Rappel : Analyser un Fait de Langue (Processus en 3 temps pour l'Interprétation)
|
100 |
-
|
101 |
(Cette partie reste identique : Observation/Repérage [Quoi ?], Explication technique [Comment ?], Interprétation [Pourquoi ? Quel effet ? Quel sens ?])
|
102 |
-
|
103 |
Structure Finale de la Présentation de l'Analyse (Synthèse des consignes)
|
104 |
-
|
105 |
L'analyse méthodique doit être présentée de manière structurée :
|
106 |
-
|
107 |
Deux axes de lecture distincts.
|
108 |
-
|
109 |
Chaque axe subdivisé en deux sous-axes.
|
110 |
-
|
111 |
Chaque sous-axe analysé via trois outils d'analyse spécifiques, nommés individuellement avec leur terme technique reconnu et officiel.
|
112 |
-
|
113 |
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.
|
114 |
-
|
115 |
-
(Note : Conformément à votre demande, cette méthodologie ne détaille pas la rédaction d'une introduction ou d'une conclusion pour le commentaire composé final, mais se concentre sur le processus d'analyse lui-même, en insistant sur la précision et l'unicité des outils nommés.)
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
Réponds en français
|
120 |
"""
|
121 |
|
122 |
prompt_redaction = """
|
@@ -172,78 +135,210 @@ Ainsi, ... (axe 2) est lié (e) à ... (sous-axe 1) et à ... (sous-axe 2).
|
|
172 |
|
173 |
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).
|
174 |
"""
|
|
|
|
|
175 |
generate_config = types.GenerateContentConfig(
|
176 |
-
|
177 |
)
|
178 |
-
model_id="gemini-2.0-flash"
|
179 |
|
|
|
|
|
|
|
|
|
180 |
|
181 |
-
|
182 |
-
def
|
183 |
"""Génère le tableau d'analyse à partir de l'image en intégrant éventuellement des consignes"""
|
184 |
prompt = prompt_tableau
|
185 |
if consignes:
|
186 |
-
prompt += "\n" + consignes
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
194 |
|
195 |
-
def
|
196 |
"""Génère la dissertation basée sur le tableau"""
|
197 |
prompt = f"""
|
198 |
-
|
199 |
-
|
200 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
|
202 |
-
response = client.models.generate_content(
|
203 |
-
model=model_id,
|
204 |
-
contents=promtp,
|
205 |
-
config=generate_config)
|
206 |
-
|
207 |
-
return response.text
|
208 |
|
|
|
209 |
@app.route('/')
|
210 |
def index():
|
|
|
211 |
return render_template('index.html')
|
212 |
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
|
|
217 |
@app.route('/analyze', methods=['POST'])
|
218 |
def analyze():
|
219 |
if 'image' not in request.files:
|
|
|
220 |
return jsonify({'error': 'No image uploaded'}), 400
|
221 |
-
|
222 |
-
# Récupération du champ "consignes" (optionnel)
|
223 |
-
consignes = request.form.get("consignes", "")
|
224 |
image_file = request.files['image']
|
225 |
-
|
226 |
-
#
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
231 |
try:
|
232 |
-
#
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
242 |
except Exception as e:
|
243 |
-
|
244 |
-
|
245 |
-
#
|
246 |
-
os.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
247 |
|
248 |
if __name__ == '__main__':
|
249 |
-
|
|
|
|
|
|
1 |
+
from flask import Flask, render_template, request, jsonify, Response, stream_with_context
|
2 |
from google import genai
|
3 |
from google.genai import types
|
4 |
import os
|
5 |
from PIL import Image
|
6 |
import tempfile
|
7 |
+
import json
|
8 |
+
import logging # Optional: for better logging
|
9 |
|
10 |
app = Flask(__name__)
|
11 |
|
12 |
+
# Configure logging (optional but recommended)
|
13 |
+
logging.basicConfig(level=logging.INFO)
|
14 |
+
|
15 |
# Configuration de l'API Gemini
|
16 |
token = os.environ.get("TOKEN")
|
17 |
+
if not token:
|
18 |
+
logging.error("API Token (TOKEN environment variable) not found!")
|
19 |
+
# Handle the error appropriately - maybe exit or raise an exception
|
20 |
+
# For now, we'll let it potentially fail later if the client is used without a key
|
21 |
genai_client = genai.Client(api_key=token)
|
|
|
|
|
22 |
|
23 |
SAFETY_SETTINGS = [
|
24 |
types.SafetySetting(
|
|
|
39 |
)
|
40 |
]
|
41 |
|
42 |
+
# --- Prompts (Keep them as they are) ---
|
43 |
prompt_tableau = """
|
|
|
44 |
Traite selon cette méthodologie :
|
45 |
|
|
|
|
|
46 |
La Méthodologie de la Lecture Méthodique
|
|
|
47 |
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.
|
|
|
48 |
Étape 1 : Observation Initiale et Identification du Texte
|
|
|
49 |
(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.)
|
|
|
50 |
Examen Visuel : Mise en page, longueur, paragraphes, typographie...
|
|
|
51 |
Identification Contextuelle : Source (auteur, œuvre, date...), situation de l'extrait.
|
|
|
52 |
Première Qualification : Genre littéraire, forme de discours (discours/récit), objectif principal (narratif, descriptif, argumentatif, explicatif/informatif).
|
|
|
53 |
Étape 2 : Compréhension Globale du Texte
|
|
|
54 |
(Cette partie reste identique : S'assurer de la compréhension littérale via des questions clés.)
|
|
|
55 |
Thème(s), Situation (Qui, quoi, où, quand ?), Énonciation (Qui parle ? Point de vue/Focalisation ?), Progression, Temps verbaux dominants.
|
|
|
56 |
Étape 3 : Analyse Approfondie et Structurée
|
|
|
57 |
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.
|
|
|
58 |
Définir les Axes de Lecture :
|
|
|
59 |
Trouver deux axes de lecture principaux (idées directrices).
|
|
|
60 |
Diviser chaque axe en deux sous-axes qui précisent l'idée principale.
|
|
|
61 |
Choisir et Utiliser les Outils d'Analyse Littéraire :
|
|
|
62 |
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) :
|
|
|
63 |
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...
|
|
|
64 |
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...
|
|
|
65 |
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...
|
|
|
66 |
É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...
|
|
|
67 |
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...
|
|
|
68 |
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...
|
|
|
69 |
Organiser l'Analyse dans un Tableau :
|
|
|
70 |
Présenter les résultats pour chaque sous-axe dans un tableau à trois colonnes :
|
|
|
71 |
Je ne veux pas qu'un outil apparaisse deux fois. Et Aussi j'aimerais uniquement des noms officiel.
|
|
|
72 |
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.
|
|
|
73 |
Repérage / Citation : Citer le ou les passages exacts du texte où l'outil est repéré.
|
|
|
74 |
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.
|
|
|
75 |
Rappel : Analyser un Fait de Langue (Processus en 3 temps pour l'Interprétation)
|
|
|
76 |
(Cette partie reste identique : Observation/Repérage [Quoi ?], Explication technique [Comment ?], Interprétation [Pourquoi ? Quel effet ? Quel sens ?])
|
|
|
77 |
Structure Finale de la Présentation de l'Analyse (Synthèse des consignes)
|
|
|
78 |
L'analyse méthodique doit être présentée de manière structurée :
|
|
|
79 |
Deux axes de lecture distincts.
|
|
|
80 |
Chaque axe subdivisé en deux sous-axes.
|
|
|
81 |
Chaque sous-axe analysé via trois outils d'analyse spécifiques, nommés individuellement avec leur terme technique reconnu et officiel.
|
|
|
82 |
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.
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
"""
|
84 |
|
85 |
prompt_redaction = """
|
|
|
135 |
|
136 |
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).
|
137 |
"""
|
138 |
+
# --- End Prompts ---
|
139 |
+
|
140 |
generate_config = types.GenerateContentConfig(
|
141 |
+
safety_settings=SAFETY_SETTINGS
|
142 |
)
|
|
|
143 |
|
144 |
+
# --- Model IDs ---
|
145 |
+
MODEL_ID_STANDARD = "gemini-2.0-flash" # Default model
|
146 |
+
MODEL_ID_DEEPTHINK = "gemini-2.5-pro-exp-03-25" # Advanced model for DeepThink
|
147 |
+
# --- End Model IDs ---
|
148 |
|
149 |
+
# --- Stream Generation Functions ---
|
150 |
+
def generate_table_stream(image, consignes="", model_id=MODEL_ID_STANDARD):
|
151 |
"""Génère le tableau d'analyse à partir de l'image en intégrant éventuellement des consignes"""
|
152 |
prompt = prompt_tableau
|
153 |
if consignes:
|
154 |
+
prompt += "\n\nConsignes supplémentaires de l'utilisateur :\n" + consignes # Make consignes clearer in prompt
|
155 |
+
|
156 |
+
logging.info(f"Generating table using model: {model_id}")
|
157 |
+
try:
|
158 |
+
response_stream = genai_client.models.generate_content_stream(
|
159 |
+
model=model_id, # Use the passed model_id
|
160 |
+
contents=[prompt, image],
|
161 |
+
config=generate_config
|
162 |
+
)
|
163 |
+
|
164 |
+
for chunk in response_stream:
|
165 |
+
if chunk.text:
|
166 |
+
yield json.dumps({"type": "tableau", "chunk": chunk.text}) + "\n"
|
167 |
+
except Exception as e:
|
168 |
+
logging.error(f"Error during table generation stream: {e}", exc_info=True)
|
169 |
+
yield json.dumps({"type": "error", "error": f"Erreur lors de la génération du tableau: {e}"}) + "\n"
|
170 |
+
|
171 |
|
172 |
+
def generate_dissertation_stream(tableau, model_id=MODEL_ID_STANDARD):
|
173 |
"""Génère la dissertation basée sur le tableau"""
|
174 |
prompt = f"""
|
175 |
+
{prompt_redaction}
|
176 |
+
|
177 |
+
--- Tableau d'Analyse Fourni ---
|
178 |
+
{tableau}
|
179 |
+
--- Fin du Tableau ---
|
180 |
+
|
181 |
+
É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.
|
182 |
+
"""
|
183 |
+
|
184 |
+
logging.info(f"Generating dissertation using model: {model_id}")
|
185 |
+
try:
|
186 |
+
response_stream = genai_client.models.generate_content_stream(
|
187 |
+
model=model_id, # Use the passed model_id
|
188 |
+
contents=prompt,
|
189 |
+
config=generate_config
|
190 |
+
)
|
191 |
+
|
192 |
+
for chunk in response_stream:
|
193 |
+
if chunk.text:
|
194 |
+
yield json.dumps({"type": "dissertation", "chunk": chunk.text}) + "\n"
|
195 |
+
except Exception as e:
|
196 |
+
logging.error(f"Error during dissertation generation stream: {e}", exc_info=True)
|
197 |
+
yield json.dumps({"type": "error", "error": f"Erreur lors de la génération de la dissertation: {e}"}) + "\n"
|
198 |
+
|
199 |
+
# --- End Stream Generation Functions ---
|
200 |
|
|
|
|
|
|
|
|
|
|
|
|
|
201 |
|
202 |
+
# --- Flask Routes ---
|
203 |
@app.route('/')
|
204 |
def index():
|
205 |
+
# Assuming your main HTML file is index.html
|
206 |
return render_template('index.html')
|
207 |
|
208 |
+
# Removed /free route as it wasn't mentioned in the final HTML
|
209 |
+
# @app.route('/free')
|
210 |
+
# def free():
|
211 |
+
# return render_template('free.html')
|
212 |
+
|
213 |
@app.route('/analyze', methods=['POST'])
|
214 |
def analyze():
|
215 |
if 'image' not in request.files:
|
216 |
+
logging.warning("Analyze request received without image file.")
|
217 |
return jsonify({'error': 'No image uploaded'}), 400
|
218 |
+
|
|
|
|
|
219 |
image_file = request.files['image']
|
220 |
+
consignes = request.form.get("consignes", "")
|
221 |
+
# Check for the DeepThink flag
|
222 |
+
use_deepthink = request.form.get('use_deepthink', 'false').lower() == 'true'
|
223 |
+
|
224 |
+
# Select model based on the flag
|
225 |
+
if use_deepthink:
|
226 |
+
model_id = MODEL_ID_DEEPTHINK
|
227 |
+
logging.info("DeepThink requested, using advanced model.")
|
228 |
+
else:
|
229 |
+
model_id = MODEL_ID_STANDARD
|
230 |
+
logging.info("Standard analysis requested, using standard model.")
|
231 |
+
|
232 |
+
# Use a temporary file to handle the image upload
|
233 |
+
temp_file = None # Initialize to None
|
234 |
try:
|
235 |
+
# Create a temporary file with a specific suffix if needed, or let NamedTemporaryFile handle it
|
236 |
+
# Using 'delete=False' requires manual cleanup
|
237 |
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as temp_file:
|
238 |
+
image_file.save(temp_file.name)
|
239 |
+
# Ensure image is valid before proceeding
|
240 |
+
try:
|
241 |
+
image = Image.open(temp_file.name)
|
242 |
+
image.verify() # Verify image header
|
243 |
+
# Re-open after verify
|
244 |
+
image = Image.open(temp_file.name)
|
245 |
+
except (IOError, SyntaxError) as e:
|
246 |
+
logging.error(f"Invalid image file uploaded: {e}")
|
247 |
+
return jsonify({'error': f'Invalid or corrupted image file: {e}'}), 400
|
248 |
+
|
249 |
+
# Now 'image' holds the PIL Image object
|
250 |
+
# We need to pass the image object to the stream function
|
251 |
+
# Note: google.generativeai often works directly with PIL Image objects
|
252 |
+
|
253 |
+
@stream_with_context
|
254 |
+
def generate():
|
255 |
+
temp_file_path = temp_file.name # Store path for finally block
|
256 |
+
full_tableau_content = "" # Accumulate full table content for dissertation
|
257 |
+
try:
|
258 |
+
logging.info("Starting table generation stream...")
|
259 |
+
# Phase 1: Génération du tableau, passing the selected model_id
|
260 |
+
for chunk_json in generate_table_stream(image, consignes, model_id):
|
261 |
+
try:
|
262 |
+
chunk_data = json.loads(chunk_json)
|
263 |
+
if chunk_data.get("type") == "error":
|
264 |
+
logging.error(f"Error received from table stream: {chunk_data.get('error')}")
|
265 |
+
yield chunk_json # Forward the error to the client
|
266 |
+
return # Stop generation if table fails
|
267 |
+
elif chunk_data.get("type") == "tableau":
|
268 |
+
full_tableau_content += chunk_data.get("chunk", "")
|
269 |
+
yield chunk_json # Stream chunk to client
|
270 |
+
except json.JSONDecodeError:
|
271 |
+
logging.error(f"Received invalid JSON from table stream: {chunk_json}")
|
272 |
+
# Decide how to handle: yield error, ignore, etc.
|
273 |
+
yield json.dumps({"type": "error", "error": "Invalid data received during table generation."}) + "\n"
|
274 |
+
return # Stop if data is corrupt
|
275 |
+
|
276 |
+
logging.info("Table generation stream finished.")
|
277 |
+
logging.info("Starting dissertation generation stream...")
|
278 |
+
|
279 |
+
# Phase 2: Génération de la dissertation basée sur le tableau COMPLET, passing the selected model_id
|
280 |
+
if full_tableau_content: # Only generate if table content exists
|
281 |
+
for chunk_json in generate_dissertation_stream(full_tableau_content, model_id):
|
282 |
+
try:
|
283 |
+
chunk_data = json.loads(chunk_json)
|
284 |
+
if chunk_data.get("type") == "error":
|
285 |
+
logging.error(f"Error received from dissertation stream: {chunk_data.get('error')}")
|
286 |
+
yield chunk_json # Forward the error
|
287 |
+
# Decide if you want to return here or let it finish
|
288 |
+
elif chunk_data.get("type") == "dissertation":
|
289 |
+
yield chunk_json # Stream chunk to client
|
290 |
+
except json.JSONDecodeError:
|
291 |
+
logging.error(f"Received invalid JSON from dissertation stream: {chunk_json}")
|
292 |
+
yield json.dumps({"type": "error", "error": "Invalid data received during dissertation generation."}) + "\n"
|
293 |
+
# Potentially return here too
|
294 |
+
else:
|
295 |
+
logging.warning("Tableau content is empty, skipping dissertation generation.")
|
296 |
+
yield json.dumps({"type": "error", "error": "Tableau d'analyse non généré, impossible de créer la dissertation."}) + "\n"
|
297 |
+
|
298 |
+
logging.info("Dissertation generation stream finished.")
|
299 |
+
|
300 |
+
except Exception as e:
|
301 |
+
logging.error(f"Error during streaming generation: {e}", exc_info=True)
|
302 |
+
# Yield a JSON error message for the client
|
303 |
+
yield json.dumps({"type": "error", "error": f"Une erreur interne est survenue: {e}"}) + "\n"
|
304 |
+
finally:
|
305 |
+
# Nettoyer le fichier temporaire in the finally block of generate
|
306 |
+
# Ensure the image object is closed if necessary (PIL handles this reasonably well)
|
307 |
+
# image.close() # Usually not needed with 'with open' or tempfile context
|
308 |
+
pass # temp_file is closed by with statement, path needed for unlink
|
309 |
+
|
310 |
+
# Return the streaming response
|
311 |
+
# Make sure temp_file path is accessible for cleanup *after* streaming finishes
|
312 |
+
response = Response(generate(), content_type='text/event-stream')
|
313 |
+
|
314 |
+
# Use response.call_on_close for reliable cleanup after stream finishes/closes
|
315 |
+
if temp_file and os.path.exists(temp_file.name):
|
316 |
+
cleanup_path = temp_file.name
|
317 |
+
response.call_on_close(lambda: os.unlink(cleanup_path) if os.path.exists(cleanup_path) else None)
|
318 |
+
logging.info(f"Scheduled cleanup for temp file: {cleanup_path}")
|
319 |
+
|
320 |
+
|
321 |
+
return response
|
322 |
+
|
323 |
except Exception as e:
|
324 |
+
# Catch errors during file handling or initial PIL processing
|
325 |
+
logging.error(f"Error processing upload before streaming: {e}", exc_info=True)
|
326 |
+
# Ensure cleanup if temp_file was created before the error
|
327 |
+
if temp_file and os.path.exists(temp_file.name):
|
328 |
+
try:
|
329 |
+
os.unlink(temp_file.name)
|
330 |
+
logging.info(f"Cleaned up temp file due to pre-stream error: {temp_file.name}")
|
331 |
+
except OSError as unlink_error:
|
332 |
+
logging.error(f"Error unlinking temp file during error handling: {unlink_error}")
|
333 |
+
return jsonify({'error': f'Error processing file: {e}'}), 500
|
334 |
+
# Note: The 'finally' block for the 'with tempfile' is implicitly handled
|
335 |
+
# but explicit cleanup using response.call_on_close is better for streaming
|
336 |
+
|
337 |
+
|
338 |
+
# --- End Flask Routes ---
|
339 |
+
|
340 |
|
341 |
if __name__ == '__main__':
|
342 |
+
# Set debug=False for production
|
343 |
+
# Use a proper WSGI server like Gunicorn or Waitress in production
|
344 |
+
app.run(debug=True) # debug=True enables auto-reloading and detailed errors
|