File size: 19,749 Bytes
f0c99e3
28b64a0
c6d1c14
28b64a0
 
 
 
 
f0c99e3
28b64a0
f0c99e3
 
28b64a0
dd9d8be
f0c99e3
28b64a0
 
f0c99e3
 
 
af4cdb5
9d6d8b4
f0c99e3
28b64a0
 
 
f0c99e3
 
28b64a0
f0c99e3
28b64a0
 
 
 
f0c99e3
28b64a0
f0c99e3
 
 
28b64a0
f0c99e3
28b64a0
 
 
 
f0c99e3
28b64a0
 
 
f0c99e3
 
 
 
 
 
 
 
 
 
28b64a0
 
 
f0c99e3
28b64a0
f0c99e3
 
 
 
28b64a0
f0c99e3
28b64a0
f0c99e3
 
 
28b64a0
f0c99e3
 
 
af4cdb5
f0c99e3
28b64a0
9d6d8b4
f0c99e3
 
28b64a0
18b4b14
28b64a0
f0c99e3
 
 
 
8f70616
 
 
 
 
18b4b14
 
8f70616
 
 
18b4b14
 
8f70616
 
 
18b4b14
 
8f70616
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f0c99e3
 
28b64a0
 
 
f0c99e3
 
5b078cb
eb3063c
f0c99e3
28b64a0
5b078cb
 
 
 
 
 
 
 
 
 
28b64a0
 
 
f0c99e3
18b4b14
28b64a0
18b4b14
f0c99e3
 
 
 
18b4b14
f0c99e3
 
18b4b14
28b64a0
18b4b14
28b64a0
18b4b14
 
28b64a0
18b4b14
 
9d6d8b4
f0c99e3
28b64a0
 
 
 
 
f0c99e3
28b64a0
 
 
 
f0c99e3
 
28b64a0
 
f0c99e3
28b64a0
 
 
 
 
 
f0c99e3
28b64a0
9d6d8b4
28b64a0
 
 
 
f0c99e3
28b64a0
 
 
 
f0c99e3
 
 
28b64a0
 
f0c99e3
 
 
 
28b64a0
 
af4cdb5
f0c99e3
 
 
 
 
 
 
 
 
 
 
 
 
 
28b64a0
 
f0c99e3
 
28b64a0
f0c99e3
28b64a0
f0c99e3
28b64a0
 
 
 
 
f0c99e3
28b64a0
f0c99e3
28b64a0
f0c99e3
 
 
 
 
 
 
 
af4cdb5
f0c99e3
28b64a0
 
 
 
 
f0c99e3
28b64a0
f0c99e3
28b64a0
f0c99e3
 
28b64a0
 
f0c99e3
 
 
 
 
 
 
af4cdb5
f0c99e3
 
 
 
 
28b64a0
 
f0c99e3
 
 
 
 
 
 
 
 
 
18b4b14
 
f0c99e3
 
 
 
 
 
28b64a0
f0c99e3
 
 
18b4b14
28b64a0
f0c99e3
 
 
 
 
 
 
 
 
 
 
 
af4cdb5
f0c99e3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28b64a0
f0c99e3
28b64a0
f0c99e3
 
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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
from flask import Flask, render_template, request, send_file, jsonify
from google import genai
from google.genai import types
from PIL import Image
import subprocess
import tempfile
import os
import io
import shutil
import base64

app = Flask(__name__)

# --- Cognvstantes ---
MODEL_SINGLE_GENERATION = "gemini-2.5-pro-exp-03-25"  # Modèle avec "thinking" pour la génération complète
LATEX_MENTION = r"\vspace{1cm}\noindent\textit{Ce devoir a été généré par Mariam AI. \url{https://mariam-241.vercel.app}}"

# --- Configuration des secrets ---
# Dans un environnement de production, utilisez les variables d'environnement ou un fichier .env
GOOGLE_API_KEY = os.environ.get('GOOGLE_API_KEY', '')

# --- Fonctions Utilitaires ---

def check_latex_installation():
    """Vérifie si pdflatex est accessible dans le PATH système."""
    try:
        subprocess.run(['pdflatex', '--version'], capture_output=True, check=True, timeout=10)
        return True, "✅ Installation LaTeX (pdflatex) trouvée."
    except (subprocess.CalledProcessError, FileNotFoundError, TimeoutError) as e:
        return False, f"❌ pdflatex introuvable ou ne répond pas. Veuillez installer une distribution LaTeX (comme TeX Live ou MiKTeX) et vous assurer qu'elle est dans le PATH système. Erreur: {e}"

def initialize_genai_client():
    """Initialise et retourne le client Google GenAI."""
    try:
        if not GOOGLE_API_KEY:
            return None, "Clé API Google non trouvée dans les variables d'environnement."
            
        client = genai.Client(api_key=GOOGLE_API_KEY, http_options={'api_version':'v1alpha'})
        return client, "✅ Client Mariam AI initialisé."
    except Exception as e:
        return None, f"❌ Erreur lors de l'initialisation du client: {str(e)}"

def clean_latex(raw_latex_text):
    """Nettoie le code LaTeX brut potentiellement fourni par Gemini."""
    if not isinstance(raw_latex_text, str):
        return ""  # Retourne une chaîne vide si l'entrée n'est pas une chaîne

    cleaned = raw_latex_text.strip()

    # Supprime les marqueurs de bloc de code (```latex ... ``` ou ``` ... ```)
    if cleaned.startswith("```latex"):
        cleaned = cleaned[8:]
    elif cleaned.startswith("```"):
        # Gère le cas où il y a un saut de ligne après ```
        lines = cleaned.split('\n', 1)
        if len(lines) > 1:
            cleaned = lines[1]
        else:
            cleaned = cleaned[3:]  # Si pas de saut de ligne, juste enlever ```

    # Supprime les marqueurs de fin de bloc de code
    if cleaned.endswith("```"):
        cleaned = cleaned[:-3].strip()  # Enlève ``` et les espaces potentiels avant

    # Assure que le document commence correctement
    if not cleaned.startswith("\\documentclass"):
        # On pourrait choisir de lever une erreur ou de tenter une correction
        pass

    # Assure que le document se termine exactement par \end{document}
    end_doc_tag = "\\end{document}"
    if end_doc_tag in cleaned:
        end_idx = cleaned.rfind(end_doc_tag) + len(end_doc_tag)
        cleaned = cleaned[:end_idx]
    else:
        # Ajoute seulement si \documentclass est présent pour éviter de créer un doc invalide
        if cleaned.strip().startswith("\\documentclass"):
            cleaned += f"\n{end_doc_tag}"

    return cleaned.strip()  # Retourne le résultat nettoyé

# --- Fonction Principale (API GenAI) ---

def generate_complete_latex(client, image_bytes):
    """
    Génère une solution complète en LaTeX à partir de l'image en une seule étape.
    """
    try:
        # Convertir les bytes en image PIL
        image = Image.open(io.BytesIO(image_bytes))
        
        prompt = """
        **PROMPT AMÉLIORÉ**

# RÔLE & OBJECTIF
Tu es un expert en mathématiques du supérieur, un tuteur pédagogue exceptionnel, et un maître utilisateur de LaTeX. Ton objectif est de générer une correction LaTeX complète, rigoureuse, pédagogique et visuellement impeccable pour l'exercice de mathématiques fourni. Le résultat doit être un fichier source `.tex` autonome, directement compilable via `pdflatex`, et d'une qualité de présentation professionnelle, similaire à l'exemple fourni précédemment.

# CONTEXTE
*   **Input:** L'énoncé de l'exercice de mathématiques sera fourni sous forme de texte (copié/collé par l'utilisateur) immédiatement après ce prompt.
*   **Niveau Cible:** Élève de Terminale (système français, filière scientifique ou équivalent).
*   **Output Attendu:** **Uniquement** le code source LaTeX (`.tex`) brut et complet. Aucune explication ou texte d'accompagnement en dehors du code lui-même. Pas de marqueurs de code (comme ```latex ... ```).

# TÂCHE PRINCIPALE
1.  **Analyse:** Lis et comprends parfaitement l'énoncé de l'exercice fourni.
2.  **Résolution:** Résous l'exercice de manière exhaustive, en détaillant chaque étape de raisonnement et de calcul.
3.  **Rédaction LaTeX:** Rédige la solution complète *directement* en code LaTeX, en respectant **scrupuleusement** toutes les spécifications techniques, de style et de contenu ci-dessous.

# SPÉCIFICATIONS TECHNIQUES DU CODE LATEX

1.  **Document de Base:**
    *   Classe: `\documentclass[12pt,a4paper]{article}`
    *   Encodage: `\usepackage[utf8]{inputenc}`, `\usepackage[T1]{fontenc}`
    *   Langue: `\usepackage[french]{babel}`
    *   Police: `\usepackage{lmodern}` (Latin Modern)
    *   Typographie Fine: `\usepackage{microtype}`

2.  **Packages Indispensables (Configurer si nécessaire):**
    *   Maths: `amsmath`, `amssymb`, `amsfonts`, `mathtools`, `bm` (pour gras maths), `siunitx` (unités SI).
    *   Mise en Page: `geometry` (marges `a4paper, margin=2.5cm`), `setspace` (utiliser `\onehalfspacing`), `fancyhdr` (pour en-têtes/pieds), `titlesec` & `titletoc` (personnalisation titres/TDM), `multicol` (si besoin).
    *   Visuel & Couleurs: `xcolor` (définir les couleurs ci-dessous), `tcolorbox` (avec `theorems`, `skins`, `breakable`, `hooks`), `fontawesome5` (pour icônes).
    *   Liens & PDF: `hyperref` (configuré pour liens colorés, métadonnées PDF), `bookmark`.
    *   Graphiques: `tikz` (avec `calc`, `shapes`, `arrows.meta`, `positioning`), `pgfplots` (avec `compat=1.18`, `fillbetween`).

3.  **Configuration Générale:**
    *   Paragraphes: Pas d'indentation (`\setlength{\parindent}{0pt}`), espacement entre paragraphes (`\setlength{\parskip}{1.2ex plus 0.5ex minus 0.2ex}`).
    *   Compilabilité: Code valide pour `pdflatex`.

# STYLE & PRÉSENTATION PROFESSIONNELLE (Inspiré de l'exemple)

1.  **Couleurs à Définir (`\definecolor`):**
    *   `maincolor`: Bleu (e.g., `{RGB}{0, 90, 160}`)
    *   `secondcolor`: Vert-bleu (e.g., `{RGB}{0, 150, 136}`)
    *   `thirdcolor`: Violet (e.g., `{RGB}{140, 0, 140}`)
    *   `accentcolor`: Orange (e.g., `{RGB}{255, 140, 0}`)
    *   `ubgcolor`: Fond clair (e.g., `{RGB}{245, 247, 250}`)
    *   `codebackground`: Fond code (e.g., `{RGB}{245, 245, 245}`)
    *   `gridcolor`: Grille PGFPlots (e.g., `{RGB}{220, 220, 220}`)
    *   `asymptotecolor`: Asymptotes (e.g., `{RGB}{200, 0, 0}`)

2.  **Page de Titre / Présentation Initiale:**
    *   Créer une page de titre distincte (`titlepage`) ou un bloc titre élégant après `\begin{document}` utilisant `\maketitle` (redéfini si nécessaire).
    *   Inclure titre clair, sous-titre (e.g., "Exercice X: Solution Détaillée"), auteur générique (e.g., "Solution Proposée"), date (`\today`).
    *   Optionnel : Page séparée pour l'énoncé encadré et un plan de résolution avant la table des matières.

3.  **Structure & Navigation:**
    *   Table des matières (`\tableofcontents`) après la présentation initiale.
    *   En-têtes/Pieds de page (`fancyhdr`): Infos discrètes (titre exo, page), règles fines colorées.
    *   Titres (`titlesec`): Sections/sous-sections numérotées avec style distinctif (e.g., numéro dans cercle/rectangle coloré, ligne de séparation).

4.  **Boîtes `tcolorbox` Personnalisées (avec icônes `fontawesome5`):**
    *   `enoncebox` (Grisâtre, `\faBook`): Pour l'énoncé principal.
    *   `definitionbox` (Couleur secondaire, `\faLightbulb`): Pour définitions, théorèmes clés.
    *   `resultbox` (Couleur accent, `\faCheckCircle`): Pour les résultats finaux importants.
    *   `notebox` (Couleur tertiaire, `\faInfoCircle`): Pour remarques, astuces, points de méthode.
    *   `examplebox` (Vert, `\faClipboard`): Pour exemples illustratifs ou rappels de méthode.
    *   Configurer ces boîtes pour être `breakable`, avec titre stylisé.

5.  **Commandes Mathématiques Personnalisées (`\newcommand`):**
    *   Ensembles: `\R`, `\C`, `\N`, `\Z`, `\Q`.
    *   Limites: `\limx{}`, `\limxp{}`, `\limxm{}`, `\limsinf`, `\liminf`.
    *   Dérivées: `\derivee{}{} `, `\ddx{}`, `\dfdx{}`.
    *   Divers: `\abs{}`, `\norm{}`, `\vect{}`, `\ds` (`\displaystyle`).
    *   Mise en valeur: `\highlight{}` (fond jaune?), `\finalresult{}` (fond couleur accent?).
    *   Environnements: `importanteq` (pour équations clés encadrées, via `empheq`).

6.  **Graphiques (`pgfplots`):**
    *   Si pertinent (étude de fonction, géométrie): Inclure un graphique propre.
    *   Configurer `pgfplotsset`: Axes centrés (`axis lines=middle`), flèches (`-Latex`), grille (`grid=both`), labels (`xlabel`, `ylabel`), légende (`legend pos=...`), couleurs définies plus haut.
    *   Tracer la courbe principale (trait plein, couleur principale), asymptotes (pointillés, couleurs dédiées), points remarquables.

# CONTENU PÉDAGOGIQUE DE LA SOLUTION

1.  **Clarté et Rigueur:**
    *   Rappeler l'énoncé initialement dans `enoncebox`.
    *   Structurer la solution logiquement (par question, par étape).
    *   Justifier *chaque* étape de calcul ou de raisonnement.
    *   Utiliser un langage mathématique précis et une rédaction claire en français.
    *   Aligner soigneusement les équations (`align*`, `cases`).

2.  **Pédagogie:**
    *   Expliquer les "pourquoi" derrière les méthodes utilisées.
    *   Utiliser les `notebox` pour des conseils, pièges à éviter, ou rappels de cours.
    *   Mettre en évidence les définitions/théorèmes clés dans `definitionbox`.
    *   Encapsuler les résultats finaux dans `resultbox` ou avec `\finalresult`.

3.  **Complétude:**
    *   Traiter toutes les questions de l'énoncé.
    *   Inclure les interprétations graphiques demandées ou pertinentes.
    *   Conclure si nécessaire en résumant les points essentiels.

# CONTRAINTES STRICTES
*   Le seul output doit être le code LaTeX brut.
*   Aucun texte avant `\documentclass` ou après `\end{document}` (sauf la mention spéciale).
*   Inclure la ligne `{Conçu par Mariam AI}` juste avant `\end{document}`.
        
        
        """

        response = client.models.generate_content(
            model=MODEL_SINGLE_GENERATION,
            contents=[image, prompt],
            config=genai.types.GenerateContentConfig(
                temperature=0.3,
            ),
    
        )

        latex_content_raw = None
        thinking_content = None

        # Extrait le contenu LaTeX et le raisonnement (thought)
        if response.candidates and response.candidates[0].content and response.candidates[0].content.parts:
            for part in response.candidates[0].content.parts:
                if getattr(part, 'thought', None):
                    thinking_content = part.text
                else:
                    latex_content_raw = part.text

        if latex_content_raw:
            latex_content_cleaned = clean_latex(latex_content_raw)
            
            # Vérification supplémentaire : est-ce que la mention obligatoire est présente ?
            if LATEX_MENTION not in latex_content_cleaned:
                # Tente de l'insérer avant \end{document}
                end_doc_tag = "\\end{document}"
                if end_doc_tag in latex_content_cleaned:
                    latex_content_cleaned = latex_content_cleaned.replace(end_doc_tag, f"{LATEX_MENTION}\n{end_doc_tag}")
                else:
                    # Si \end{document} n'est pas là, ajoute les deux à la fin
                    latex_content_cleaned += f"\n{LATEX_MENTION}\n{end_doc_tag}"
                    
            return latex_content_cleaned, thinking_content, None
        else:
            return None, thinking_content, "Aucun contenu LaTeX n'a été généré par le modèle."

    except genai.types.StopCandidateException as e:
        return None, None, "❌ Génération stoppée (possible contenu inapproprié)"
    except Exception as e:
        return None, None, f"❌ Erreur lors de la génération du LaTeX: {str(e)}"

# --- Fonction de Compilation LaTeX ---

def compile_latex_to_pdf(latex_content):
    """
    Compile une chaîne de caractères contenant du code LaTeX en fichier PDF.
    Utilise un répertoire temporaire pour les fichiers intermédiaires.
    """
    if not latex_content:
        return None, "Impossible de compiler : contenu LaTeX vide."

    # Utilise un répertoire temporaire sécurisé qui sera nettoyé automatiquement
    with tempfile.TemporaryDirectory() as temp_dir:
        tex_filename = "solution.tex"
        pdf_filename = "solution.pdf"
        tex_filepath = os.path.join(temp_dir, tex_filename)
        pdf_filepath = os.path.join(temp_dir, pdf_filename)
        log_filepath = os.path.join(temp_dir, "solution.log")  # Pour le débogage

        # Écrit le contenu LaTeX dans le fichier .tex avec encodage UTF-8
        try:
            with open(tex_filepath, "w", encoding="utf-8") as f:
                f.write(latex_content)
        except IOError as e:
            return None, f"❌ Erreur lors de l'écriture du fichier .tex temporaire: {str(e)}"

        # Exécute pdflatex
        command = [
            "pdflatex",
            "-interaction=nonstopmode",
            f"-output-directory={temp_dir}",
            f"-jobname={os.path.splitext(pdf_filename)[0]}",  # Nom sans extension
            tex_filepath
        ]

        pdf_generated = False
        compilation_output = ""
        
        for pass_num in range(1, 3):  # Lance jusqu'à 2 passes
            try:
                # Augmentation du timeout pour les compilations potentiellement longues
                result = subprocess.run(command, capture_output=True, check=True, text=True, encoding='utf-8', errors='replace', timeout=60)
                compilation_output += f"Passe {pass_num}: Succès\n"
                
                # Vérifie si le PDF a été créé après la première passe (au moins)
                if os.path.exists(pdf_filepath):
                    pdf_generated = True

            except subprocess.CalledProcessError as e:
                error_message = f"❌ Erreur lors de la compilation LaTeX (Passe {pass_num}).\n"
                error_message += f"Code de retour: {e.returncode}\n"
                error_message += f"Sortie: {e.stdout}\n"
                error_message += f"Erreurs: {e.stderr}\n"
                
                # Essayer de lire le fichier .log pour plus d'infos
                try:
                    with open(log_filepath, "r", encoding="utf-8", errors='replace') as log_file:
                        error_message += f"Extrait du fichier log (solution.log):\n{log_file.read()[-2000:]}"
                except IOError:
                    error_message += "Impossible de lire le fichier log de LaTeX."
                
                return None, error_message

            except subprocess.TimeoutExpired:
                return None, "❌ La compilation LaTeX a dépassé le délai imparti. Le document est peut-être trop complexe ou contient une boucle infinie."
            
            except Exception as e:
                return None, f"❌ Une erreur inattendue est survenue lors de l'exécution de pdflatex: {str(e)}"

        # Vérifie si le fichier PDF existe après les passes
        if pdf_generated and os.path.exists(pdf_filepath):
            try:
                # Lit le contenu binaire du PDF généré
                with open(pdf_filepath, "rb") as f:
                    pdf_data = f.read()
                return pdf_data, "✅ PDF généré avec succès !"
            except IOError as e:
                return None, f"❌ Erreur lors de la lecture du fichier PDF généré: {str(e)}"
        else:
            error_message = "❌ Le fichier PDF n'a pas été généré après la compilation LaTeX.\n"
            # Tenter de lire le log
            try:
                with open(log_filepath, "r", encoding="utf-8", errors='replace') as log_file:
                    error_message += f"Extrait du fichier log (solution.log) après échec de génération PDF:\n{log_file.read()[-2000:]}"
            except IOError:
                error_message += "Impossible de lire le fichier log de LaTeX."
            return None, error_message

# --- Routes Flask ---

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/check-latex', methods=['GET'])
def check_latex():
    latex_installed, message = check_latex_installation()
    return jsonify({
        'success': latex_installed,
        'message': message
    })

@app.route('/check-api', methods=['GET'])
def check_api():
    client, message = initialize_genai_client()
    return jsonify({
        'success': client is not None,
        'message': message
    })

@app.route('/process', methods=['POST'])
def process():
    if 'image' not in request.files:
        return jsonify({'success': False, 'message': 'Aucune image téléchargée'})
        
    file = request.files['image']
    if file.filename == '':
        return jsonify({'success': False, 'message': 'Aucun fichier sélectionné'})
    
    # Lire l'image
    image_bytes = file.read()
    
    # Initialiser le client
    client, message = initialize_genai_client()
    if client is None:
        return jsonify({'success': False, 'message': message})
    
    # Générer le LaTeX
    latex_content, thinking_content, error = generate_complete_latex(client, image_bytes)
    if error:
        return jsonify({'success': False, 'message': error})
        
    # Compiler en PDF
    pdf_data, message = compile_latex_to_pdf(latex_content)
    if pdf_data is None:
        return jsonify({
            'success': False, 
            'message': message,
            'latex': latex_content,
            'thinking': thinking_content
        })
    
    # Encoder le PDF en base64 pour le retourner au front-end
    pdf_base64 = base64.b64encode(pdf_data).decode('utf-8')
    
    # Retourner les données
    return jsonify({
        'success': True,
        'message': 'PDF généré avec succès',
        'latex': latex_content,
        'thinking': thinking_content,
        'pdf_base64': pdf_base64
    })

@app.route('/download-pdf', methods=['POST'])
def download_pdf():
    if 'pdf_data' not in request.json:
        return jsonify({'success': False, 'message': 'Données PDF manquantes'})
    
    try:
        pdf_data = base64.b64decode(request.json['pdf_data'])
        
        # Créer un fichier temporaire pour le PDF
        with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as temp_file:
            temp_file.write(pdf_data)
            temp_path = temp_file.name
        
        # Envoyer le fichier
        return send_file(
            temp_path,
            mimetype='application/pdf',
            as_attachment=True,
            download_name='solution_mariam_ai.pdf'
        )
    except Exception as e:
        return jsonify({'success': False, 'message': f'Erreur lors du téléchargement: {str(e)}'})

if __name__ == "__main__":
    app.run(debug=True)