MisterAI commited on
Commit
788ed0b
·
verified ·
1 Parent(s): 2c22d8e

Create app.py

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