MisterAI commited on
Commit
9f2d35d
·
verified ·
1 Parent(s): 001609e

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +382 -0
app.py ADDED
@@ -0,0 +1,382 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ##Modif 138
2
+
3
+ import gradio as gr
4
+ from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
5
+ from pptx import Presentation
6
+ from pptx.util import Inches, Pt
7
+ from pptx.enum.text import PP_ALIGN
8
+ import torch
9
+ from llama_cpp import Llama
10
+ import time
11
+ from PIL import Image
12
+ import io
13
+ import requests
14
+ from diffusers import FluxPipeline
15
+
16
+ # Configuration des modèles disponibles
17
+ TEXT_MODELS = {
18
+ "Mistral Nemo 2407 (GGUF)": "MisterAI/Bartowski_MistralAI_Mistral-Nemo-Instruct-2407-IQ4_XS.gguf",
19
+ "Mixtral 8x7B": "mistralai/Mixtral-8x7B-v0.1",
20
+ "Lucie 7B": "OpenLLM-France/Lucie-7B"
21
+ }
22
+
23
+ IMAGE_MODELS = {
24
+ "FLUX.1": "black-forest-labs/FLUX.1-schnell",
25
+ "ArtifyAI": "ImageInception/ArtifyAI-v1.1"
26
+ }
27
+
28
+ # Préprompt amélioré pour une meilleure structuration
29
+ PREPROMPT = """Vous êtes un assistant IA expert en création de présentations PowerPoint professionnelles.
30
+ Générez une présentation structurée et détaillée en suivant ce format EXACT:
31
+
32
+ TITRE: [Titre principal de la présentation]
33
+
34
+ DIAPO 1:
35
+ Titre: [Titre de la diapo]
36
+ Points:
37
+ - Point 1
38
+ - Point 2
39
+ - Point 3
40
+ Image: [Description détaillée de l'image souhaitée pour cette diapo. Soyez très précis dans la description pour permettre
41
+ une génération d'image de qualité. Par exemple : "Une illustration professionnelle montrant un concept clé de cybersécurité
42
+ avec des éléments visuels modernes, un style épuré et des couleurs corporate (fond noir, couleurs bleu électrique, rouge, gris, blanc).
43
+ L'image doit être claire, minimaliste et adaptée à une présentation professionnelle."]
44
+
45
+ DIAPO 2:
46
+ Titre: [Titre de la diapo]
47
+ Points:
48
+ - Point 1
49
+ - Point 2
50
+ - Point 3
51
+ Image: [Description détaillée de l'image souhaitée pour cette diapo]
52
+
53
+ [Continuez avec ce format pour chaque diapositive]
54
+
55
+ Analysez le texte suivant et créez une présentation professionnelle avec des descriptions d'images pertinentes :"""
56
+
57
+ class PresentationGenerator:
58
+ def __init__(self):
59
+ self.token = os.getenv('Authentification_HF')
60
+ if not self.token:
61
+ raise ValueError("Token d'authentification HuggingFace non trouvé")
62
+ login(self.token)
63
+ self.text_model = None
64
+ self.text_tokenizer = None
65
+ self.image_pipeline = None
66
+
67
+ def load_text_model(self, model_name):
68
+ """Charge le modèle de génération de texte"""
69
+ model_id = TEXT_MODELS[model_name]
70
+ if model_id.endswith('.gguf'):
71
+ # Configuration pour les modèles GGUF
72
+ model_path = hf_hub_download(
73
+ repo_id=model_id.split('/')[0] + '/' + model_id.split('/')[1],
74
+ filename=model_id.split('/')[-1],
75
+ token=self.token
76
+ )
77
+ self.text_model = Llama(
78
+ model_path=model_path,
79
+ n_ctx=4096,
80
+ n_batch=512,
81
+ verbose=False
82
+ )
83
+ else:
84
+ # Configuration pour les modèles Transformers standards
85
+ self.text_tokenizer = AutoTokenizer.from_pretrained(model_id, token=self.token)
86
+ self.text_model = AutoModelForCausalLM.from_pretrained(
87
+ model_id,
88
+ torch_dtype=torch.bfloat16,
89
+ device_map="auto",
90
+ token=self.token
91
+ )
92
+
93
+ def load_image_model(self, model_name):
94
+ """Charge le modèle de génération d'images"""
95
+ model_id = IMAGE_MODELS[model_name]
96
+ self.image_pipeline = gr.load("models/black-forest-labs/FLUX.1-schnell")
97
+ print(f"Modèle d'image FLUX chargé : {model_id}")
98
+
99
+ def generate_text(self, prompt, temperature=0.7, max_tokens=4096):
100
+ """Génère le texte de la présentation"""
101
+ if isinstance(self.text_model, Llama):
102
+ response = self.text_model(
103
+ prompt,
104
+ max_tokens=max_tokens,
105
+ temperature=temperature,
106
+ echo=False
107
+ )
108
+ return response['choices'][0]['text']
109
+ else:
110
+ inputs = self.text_tokenizer.apply_chat_template(
111
+ [{"role": "user", "content": prompt}],
112
+ return_tensors="pt",
113
+ return_dict=True
114
+ )
115
+ outputs = self.text_model.generate(
116
+ **inputs,
117
+ max_new_tokens=max_tokens,
118
+ temperature=temperature
119
+ )
120
+ return self.text_tokenizer.decode(outputs[0], skip_special_tokens=True)
121
+
122
+ def generate_image(self, prompt, negative_prompt="", num_inference_steps=30):
123
+ """Génère une image pour la diapositive"""
124
+ try:
125
+ image = self.image_pipeline(
126
+ prompt=prompt,
127
+ negative_prompt=negative_prompt,
128
+ num_inference_steps=num_inference_steps
129
+ )[0] # Pipeline retourne une liste d'images, on prend la première
130
+ return image
131
+ except Exception as e:
132
+ print(f"Erreur lors de la génération de l'image: {str(e)}")
133
+ return None
134
+
135
+ def parse_presentation_content(self, content):
136
+ """Parse le contenu généré en sections pour les diapositives"""
137
+ slides = []
138
+ current_slide = None
139
+
140
+ for line in content.split('\n'):
141
+ line = line.strip()
142
+ if line.startswith('TITRE:'):
143
+ slides.append({'type': 'title', 'title': line[6:].strip()})
144
+ elif line.startswith('DIAPO'):
145
+ if current_slide:
146
+ slides.append(current_slide)
147
+ current_slide = {'type': 'content', 'title': '', 'points': [], 'image_prompt': ''}
148
+ elif line.startswith('Titre:') and current_slide:
149
+ current_slide['title'] = line[6:].strip()
150
+ elif line.startswith('- ') and current_slide:
151
+ current_slide['points'].append(line[2:].strip())
152
+ elif line.startswith('Image:') and current_slide:
153
+ current_slide['image_prompt'] = line[6:].strip()
154
+
155
+ if current_slide:
156
+ slides.append(current_slide)
157
+
158
+ return slides
159
+
160
+ def create_presentation(self, slides):
161
+ """Crée la présentation PowerPoint avec texte et images"""
162
+ prs = Presentation()
163
+
164
+ # Première diapo (titre)
165
+ title_slide = prs.slides.add_slide(prs.slide_layouts[0])
166
+ title_slide.shapes.title.text = slides[0]['title']
167
+
168
+ # Autres diapos
169
+ for slide in slides[1:]:
170
+ content_slide = prs.slides.add_slide(prs.slide_layouts[1])
171
+ content_slide.shapes.title.text = slide['title']
172
+
173
+ # Ajout du texte
174
+ if slide['points']:
175
+ body = content_slide.shapes.placeholders[1].text_frame
176
+ body.clear()
177
+ for point in slide['points']:
178
+ p = body.add_paragraph()
179
+ p.text = point
180
+ p.level = 0
181
+
182
+ # Ajout de l'image si disponible
183
+ if slide.get('image_prompt'):
184
+ image = self.generate_image(slide['image_prompt'])
185
+ if image:
186
+ # Sauvegarde temporaire de l'image
187
+ img_path = f"temp_slide_{slides.index(slide)}.png"
188
+ image.save(img_path)
189
+
190
+ # Ajout de l'image à la diapositive
191
+ left = Inches(1)
192
+ top = Inches(2.5)
193
+ content_slide.shapes.add_picture(img_path, left, top, height=Inches(4))
194
+
195
+ # Suppression du fichier temporaire
196
+ os.remove(img_path)
197
+
198
+ return prs
199
+
200
+ def generate_presentation_with_progress(text, text_model_name, image_model_name, temperature, max_tokens, negative_prompt):
201
+ """Fonction principale de génération avec suivi de progression"""
202
+ try:
203
+ start_time = time.time()
204
+ generator = PresentationGenerator()
205
+
206
+ # Chargement des modèles
207
+ yield "Chargement des modèles...", None, None
208
+ generator.load_text_model(text_model_name)
209
+ generator.load_image_model(image_model_name)
210
+
211
+ # Génération du contenu
212
+ yield "Génération du contenu de la présentation...", None, None
213
+ full_prompt = PREPROMPT + "\n\n" + text
214
+ generated_content = generator.generate_text(full_prompt, temperature, max_tokens)
215
+
216
+ # Création de la présentation
217
+ yield "Création de la présentation PowerPoint...", generated_content, None
218
+ slides = generator.parse_presentation_content(generated_content)
219
+ prs = generator.create_presentation(slides)
220
+
221
+ # Sauvegarde
222
+ output_path = "presentation.pptx"
223
+ prs.save(output_path)
224
+
225
+ execution_time = time.time() - start_time
226
+ status = f"Présentation générée avec succès en {execution_time:.2f} secondes!"
227
+
228
+ return status, generated_content, output_path
229
+
230
+ except Exception as e:
231
+ return f"Erreur: {str(e)}", None, None
232
+
233
+ # CSS personnalisé pour un thème sombre amélioré
234
+ css = """
235
+ /* Thème sombre personnalisé */
236
+ .gradio-container {
237
+ background-color: #000000 !important;
238
+ color: #ffffff !important;
239
+ }
240
+
241
+ .gr-form, .gr-box, .gr-panel {
242
+ border-radius: 8px !important;
243
+ background-color: #1a1a1a !important;
244
+ border: 1px solid #333333 !important;
245
+ color: #ffffff !important;
246
+ }
247
+
248
+ .gr-input, .gr-textarea, .gr-dropdown {
249
+ background-color: #2d2d2d !important;
250
+ color: #ffffff !important;
251
+ border: 1px solid #404040 !important;
252
+ }
253
+
254
+ .gr-button {
255
+ background-color: #2d2d2d !important;
256
+ color: #ffffff !important;
257
+ border: 1px solid #404040 !important;
258
+ transition: all 0.3s ease !important;
259
+ }
260
+
261
+ .gr-button:hover {
262
+ background-color: #404040 !important;
263
+ transform: translateY(-2px) !important;
264
+ }
265
+
266
+ /* Textes et labels */
267
+ h1, h2, h3, p, label, .gr-text {
268
+ color: #ffffff !important;
269
+ }
270
+
271
+ /* Scrollbar */
272
+ ::-webkit-scrollbar {
273
+ width: 8px;
274
+ height: 8px;
275
+ }
276
+
277
+ ::-webkit-scrollbar-track {
278
+ background: #1a1a1a;
279
+ }
280
+
281
+ ::-webkit-scrollbar-thumb {
282
+ background: #404040;
283
+ border-radius: 4px;
284
+ }
285
+
286
+ ::-webkit-scrollbar-thumb:hover {
287
+ background: #4a4a4a;
288
+ }
289
+ """
290
+
291
+ with gr.Blocks(theme=gr.themes.Default(), css=css) as demo:
292
+ gr.Markdown(
293
+ """
294
+ # 🎯 Générateur de Présentations PowerPoint IA
295
+
296
+ Créez des présentations professionnelles automatiquement avec l'aide de l'IA.
297
+ """
298
+ )
299
+
300
+ with gr.Row():
301
+ with gr.Column(scale=1):
302
+ text_model_choice = gr.Dropdown(
303
+ choices=list(TEXT_MODELS.keys()),
304
+ value=list(TEXT_MODELS.keys())[0],
305
+ label="Modèle de génération de texte"
306
+ )
307
+ image_model_choice = gr.Dropdown(
308
+ choices=list(IMAGE_MODELS.keys()),
309
+ value=list(IMAGE_MODELS.keys())[0],
310
+ label="Modèle de génération d'images"
311
+ )
312
+ temperature = gr.Slider(
313
+ minimum=0.1,
314
+ maximum=1.0,
315
+ value=0.7,
316
+ step=0.1,
317
+ label="Température"
318
+ )
319
+ max_tokens = gr.Slider(
320
+ minimum=1000,
321
+ maximum=4096,
322
+ value=2048,
323
+ step=256,
324
+ label="Tokens maximum"
325
+ )
326
+ negative_prompt = gr.Textbox(
327
+ lines=2,
328
+ label="Prompt négatif pour les images",
329
+ placeholder="Ce que vous ne voulez pas voir dans les images..."
330
+ )
331
+
332
+ with gr.Row():
333
+ with gr.Column(scale=2):
334
+ input_text = gr.Textbox(
335
+ lines=10,
336
+ label="Votre texte",
337
+ placeholder="Décrivez le contenu que vous souhaitez pour votre présentation..."
338
+ )
339
+ # Modification du composant File pour supprimer l'argument multiple
340
+ file_upload = gr.File(
341
+ label="Documents de référence (PDF, Images)",
342
+ file_types=["pdf", "png", "jpg", "jpeg"],
343
+ type="binary"
344
+ )
345
+
346
+ with gr.Row():
347
+ generate_btn = gr.Button("🚀 Générer la présentation", variant="primary")
348
+
349
+ with gr.Row():
350
+ with gr.Column():
351
+ status_output = gr.Textbox(
352
+ label="Statut",
353
+ lines=2
354
+ )
355
+ generated_content = gr.Textbox(
356
+ label="Contenu généré",
357
+ lines=10,
358
+ show_copy_button=True
359
+ )
360
+ output_file = gr.File(
361
+ label="Présentation PowerPoint"
362
+ )
363
+
364
+ generate_btn.click(
365
+ fn=generate_presentation_with_progress,
366
+ inputs=[
367
+ input_text,
368
+ text_model_choice,
369
+ image_model_choice,
370
+ temperature,
371
+ max_tokens,
372
+ negative_prompt
373
+ ],
374
+ outputs=[
375
+ status_output,
376
+ generated_content,
377
+ output_file
378
+ ]
379
+ )
380
+
381
+ if __name__ == "__main__":
382
+ demo.launch()