Docfile commited on
Commit
0a6c0de
·
verified ·
1 Parent(s): 1a081ec

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +97 -278
app.py CHANGED
@@ -1,312 +1,131 @@
1
- # app.py
2
- import streamlit as st
3
- import asyncio
4
- from typing import Any, List, Type
5
- from pydantic import BaseModel, Field
6
-
7
- from reportlab.lib.pagesizes import letter
8
- from reportlab.platypus import SimpleDocTemplate, Paragraph
9
- from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
10
- from reportlab.lib import colors
11
-
12
- from crewai import Agent, Crew, Process, Task
13
  from crewai_tools import SerperDevTool
 
14
  from crewai import LLM
15
- from crewai.tools.base_tool import BaseTool
16
-
17
-
18
- # --- PDF Tool from pdf_tool.py ---
19
- class PDFToolInput(BaseModel):
20
- content: str = Field(..., description="Contenu de l'exposé en format Markdown")
21
- output_path: str = Field(..., description="Chemin de sortie du fichier PDF")
22
-
23
- class PDFTool(BaseTool):
24
- name: str = "Generate PDF"
25
- description: str = "Outil pour générer un document PDF à partir de texte en format Markdown."
26
- args_schema: Type[BaseModel] = PDFToolInput
27
-
28
- def _run(
29
- self,
30
- content: str,
31
- output_path: str = "expose.pdf",
32
- **kwargs,
33
- ) -> str:
34
- try:
35
- doc = SimpleDocTemplate(output_path, pagesize=letter)
36
- styles = getSampleStyleSheet()
37
-
38
- # Ajout d'un style personnalisé pour le texte normal
39
- styles.add(ParagraphStyle(name='CustomBodyText',
40
- parent=styles['Normal'],
41
- fontSize=12,
42
- leading=14,
43
- spaceAfter=10))
44
-
45
- # Ajout d'un style personnalisé pour les titres de section
46
- styles.add(ParagraphStyle(name='CustomHeading1',
47
- parent=styles['Heading1'],
48
- fontSize=18,
49
- leading=22,
50
- spaceBefore=20,
51
- spaceAfter=6,
52
- textColor=colors.HexColor("#2c3e50"))) # Couleur bleu foncé
53
-
54
- # Ajout d'un style personnalisé pour les titres de sous-section
55
- styles.add(ParagraphStyle(name='CustomHeading2',
56
- parent=styles['Heading2'],
57
- fontSize=14,
58
- leading=18,
59
- spaceBefore=10,
60
- spaceAfter=4,
61
- textColor=colors.HexColor("#34495e"))) # Couleur bleu-gris
62
-
63
- # Séparer le contenu en sections en utilisant les sauts de ligne comme délimiteurs
64
- sections = content.split("\n\n")
65
 
66
- Story = []
67
- for section in sections:
68
- # Déterminer si la section est un titre de section, un titre de sous-section ou du texte normal
69
- if section.startswith("# "):
70
- # Titre de section
71
- title = section[2:].strip()
72
- Story.append(Paragraph(title, styles["CustomHeading1"]))
73
- elif section.startswith("## "):
74
- # Titre de sous-section
75
- subtitle = section[3:].strip()
76
- Story.append(Paragraph(subtitle, styles["CustomHeading2"]))
77
- else:
78
- # Texte normal
79
- Story.append(Paragraph(section, styles["CustomBodyText"]))
80
-
81
- doc.build(Story)
82
- return f"Fichier PDF généré avec succès : {output_path}"
83
- except Exception as e:
84
- return f"Erreur lors de la génération du PDF : {e}"
85
-
86
-
87
- # --- Crew utilities from crew_utils.py ---
88
-
89
- # --- Définition des outils ---
90
- search_tool = SerperDevTool()
91
- pdf_tool = PDFTool()
92
-
93
- # --- Définition du LLM (Gemini) ---
94
- # Remplace par ta clé API
95
- GEMINI_API_KEY = "AIzaSyD6yZxfVOnh63GXBJjakAupk9aP4CZrgrQ"
96
  llm = LLM(
97
  model="gemini/gemini-1.5-flash",
98
  temperature=0.7,
99
- timeout=120, # Seconds to wait for response
100
  max_tokens=8000,
101
  )
102
 
103
 
104
- # --- Définition des agents ---
105
- # Chef de Projet
106
- project_manager = Agent(
107
- role="Chef de Projet",
108
- goal="Coordonner la création d'un exposé complet et de haute qualité sur le thème donné.",
109
- backstory="Expert en gestion de projet et en coordination d'équipes, capable de diriger des projets complexes jusqu'à leur aboutissement.",
110
- verbose=True,
111
- llm=llm,
112
- allow_delegation=True,
113
- )
114
 
115
- # Planificateur
116
- planner = Agent(
117
- role="Planificateur d'Exposé",
118
- goal="Créer un plan détaillé et pertinent pour l'exposé.",
119
- backstory="Spécialiste de la structuration de contenu, capable de générer des plans clairs et logiques pour des exposés complexes.",
120
- verbose=True,
121
- llm=llm,
122
- )
123
 
124
- # Rédacteurs (3 instances pour gérer l'introduction, le développement et la conclusion)
125
- researcher_intro = Agent(
126
- role="Rédacteur Spécialisé en Introduction",
127
- goal="Rédiger une introduction captivante pour l'exposé.",
128
- backstory="Expert en recherche et en rédaction, capable de produire du contenu informatif et bien écrit sur des sujets variés.",
129
- verbose=True,
130
  llm=llm,
131
- tools=[search_tool],
 
 
 
132
  )
133
 
134
- researcher_dev = Agent(
135
- role="Rédacteur Spécialisé en Développement",
136
- goal="Rédiger les sections de développement de l'exposé.",
137
- backstory="Expert en recherche et en rédaction, capable de produire du contenu informatif et bien écrit sur des sujets variés.",
138
- verbose=True,
139
- llm=llm,
140
  tools=[search_tool],
 
 
 
 
141
  )
142
 
143
- researcher_conclusion = Agent(
144
- role="Rédacteur Spécialisé en Conclusion",
145
- goal="Rédiger une conclusion percutante pour l'exposé.",
146
- backstory="Expert en recherche et en rédaction, capable de produire du contenu informatif et bien écrit sur des sujets variés.",
147
- verbose=True,
148
  llm=llm,
 
149
  tools=[search_tool],
 
 
 
 
150
  )
151
 
152
- # Assembleur
153
- assembler = Agent(
154
- role="Assembleur d'Exposé",
155
- goal="Compiler toutes les sections rédigées de l'exposé dans un seul document et générer un document PDF final de qualité professionnelle.",
156
- backstory="Expert en mise en forme et en compilation de documents, capable de transformer des contenus distincts en un document final harmonieux et esthétique.",
157
- verbose=True,
158
  llm=llm,
159
- tools=[pdf_tool],
 
 
160
  )
161
 
162
- # --- Définition des tâches ---
163
- # Tâche pour le Planificateur
164
- create_plan_task = Task(
165
- description="""
166
- Créez un plan détaillé pour un exposé sur le thème suivant : {topic}.
167
- Le plan doit inclure une introduction, plusieurs sections principales (au moins 3), et une conclusion.
168
- Assurez-vous que le plan est logique et couvre tous les aspects importants du sujet.
169
- Divisez les sections principales en sous-sections si nécessaire pour une meilleure organisation.
170
- """,
171
- expected_output="Un plan d'exposé structuré avec des titres de sections et sous-sections, en format Markdown.",
172
- agent=planner,
173
  )
174
 
175
- class SectionContent(BaseModel):
176
- title: str = Field(..., description="Titre de la section")
177
- content: str = Field(..., description="Contenu de la section")
178
-
179
- # Tâches pour les Rédacteurs (exemple pour 3 sections, à adapter)
180
- async def write_section_task(
181
- researcher: Agent, section_title: str, context: List[Task]
182
- ) -> Task:
183
- return Task(
184
- description=f"""
185
- Rédigez la section '{section_title}' de l'exposé en vous basant sur le plan fourni.
186
- Effectuez des recherches approfondies en utilisant l'outil de recherche pour enrichir le contenu.
187
- La section doit être informative, bien écrite et doit respecter le ton académique d'un exposé.
188
- Voici le contexte: {context}
189
- """,
190
- expected_output=f"Texte complet et bien rédigé pour la section '{section_title}' de l'exposé, au format Markdown.",
191
- agent=researcher,
192
- tools=[search_tool],
193
- output_pydantic=SectionContent, # Utilisez le modèle Pydantic ici
194
- context=context,
195
- )
196
-
197
- # Tâche pour l'Assembleur
198
- compile_report_task = Task(
199
- description="""
200
- Compilez toutes les sections rédigées de l'exposé dans un seul document.
201
- Organisez les sections selon le plan fourni par le planificateur.
202
- Générez un document PDF final prêt pour la présentation.
203
- Assurez-vous que le document est bien structuré, facile à lire et qu'il respecte les conventions d'un exposé académique.
204
- Voici le contexte: {context}
205
- """,
206
- expected_output="Un document PDF complet de l'exposé, prêt à être présenté.",
207
- agent=assembler,
208
- tools=[pdf_tool],
209
  )
210
 
211
- # --- Orchestration des tâches avec un processus hiérarchique ---
212
- async def process_section_tasks(
213
- manager: Agent,
214
- plan: Any, # Remplace Any par le type de retour attendu de la tâche create_plan_task
215
- researchers: List[Agent],
216
- ):
217
- # Assume que plan est une liste de titres de sections
218
- sections = plan.split("\n") # Adapter selon le format réel du plan
219
- section_tasks: List[Task] = []
220
-
221
- for i, section in enumerate(sections):
222
- if section != "":
223
- researcher = researchers[i % len(researchers)]
224
- section_task = await write_section_task(
225
- researcher=researcher,
226
- section_title=section.strip(),
227
- context=[create_plan_task],
228
- )
229
- section_tasks.append(section_task)
230
-
231
- return section_tasks
232
-
233
- class ExposeCrew(Crew):
234
- def __init__(self, agents, tasks, process, manager_llm, verbose):
235
- super().__init__(agents=agents, tasks=tasks, process=process, manager_llm=manager_llm, verbose=verbose)
236
-
237
- async def kickoff(self, inputs: dict = {}) -> str:
238
- # Exécuter la tâche de création du plan
239
- plan = await create_plan_task.execute(context=inputs)
240
- self.log.info(f"Plan de l'exposé : {plan}")
241
-
242
- # Créer et exécuter les tâches de rédaction de section en parallèle
243
- section_tasks = await process_section_tasks(
244
- manager=self.manager_llm,
245
- plan=plan,
246
- researchers=[researcher_intro, researcher_dev, researcher_conclusion],
247
- )
248
-
249
- sections_results = await asyncio.gather(*[task.execute(context=[create_plan_task]) for task in section_tasks])
250
- section_outputs = [result.content for result in sections_results]
251
-
252
- self.log.info(f"Sections rédigées : {section_outputs}")
253
-
254
- # Mettre à jour la description de la tâche de compilation avec les sections rédigées
255
- compile_report_task.description = f"""
256
- Compilez toutes les sections rédigées de l'exposé dans un seul document.
257
- Organisez les sections selon le plan fourni par le planificateur :
258
- {plan}
259
- Sections rédigées :
260
- {section_outputs}
261
- Générez un document PDF final prêt pour la présentation.
262
- """
263
- compile_report_task.context = section_tasks
264
-
265
- # Exécuter la tâche de compilation
266
- result = await compile_report_task.execute()
267
- return result
268
-
269
- # --- Streamlit App from app.py ---
270
-
271
- async def run_crew(topic):
272
- """Exécute le crew pour générer l'exposé."""
273
- crew = ExposeCrew(
274
- agents=[project_manager, planner, researcher_intro, researcher_dev, researcher_conclusion, assembler],
275
- tasks=[create_plan_task, compile_report_task],
276
- process=Process.hierarchical,
277
- manager_llm=llm,
278
- verbose=True,
279
- )
280
- result = await crew.kickoff(inputs={"topic": topic})
281
- return result
282
-
283
-
284
- # Interface Streamlit
285
- st.title("Générateur d'Exposés avec CrewAI")
286
-
287
- topic = st.text_input("Entrez le thème de l'exposé :")
288
 
289
- async def handle_generate():
290
- if not topic:
291
- st.error("Veuillez entrer un thème pour l'exposé.")
292
- else:
293
- with st.spinner("Création de l'exposé en cours..."):
294
- try:
295
- # Exécuter le crew de manière asynchrone
296
- result = await run_crew(topic)
297
- st.success("Exposé généré avec succès!")
298
 
299
- # Téléchargement du fichier PDF
300
- with open("expose.pdf", "rb") as file:
301
- st.download_button(
302
- label="Télécharger l'exposé en PDF",
303
- data=file,
304
- file_name="expose.pdf",
305
- mime="application/pdf"
306
- )
307
 
308
- except Exception as e:
309
- st.error(f"Une erreur s'est produite : {e}")
310
 
 
311
  if st.button("Générer l'exposé"):
312
- asyncio.run(handle_generate())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from crewai import Agent, Task, Crew, Process
 
 
 
 
 
 
 
 
 
 
3
  from crewai_tools import SerperDevTool
4
+ import streamlit as st
5
  from crewai import LLM
6
+ from crewai.tools import FileWriteTool # Importez FileWriteTool
7
+ # Configuration des clés API pour les outils
8
+ os.environ["SERPER_API_KEY"] = st.secrets["SERPER_API_KEY"]
9
+ os.environ["GEMINI_API_KEY"] = "AIzaSyAxG22bkNc0Nhsjs5aTh7H7e8LWJWy21Kw"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  llm = LLM(
12
  model="gemini/gemini-1.5-flash",
13
  temperature=0.7,
14
+ timeout=120, # Secondes d'attente pour la réponse
15
  max_tokens=8000,
16
  )
17
 
18
 
19
+ # Définition des outils
20
+ search_tool = SerperDevTool()
21
+
22
+ # Définir FileWriteTool
23
+ file_writer_tool = FileWriteTool()
 
 
 
 
 
24
 
 
 
 
 
 
 
 
 
25
 
26
+ # Agents
27
+ coordinateur = Agent(
28
+ role="Coordinateur général",
29
+ goal="Superviser la création d'un exposé complet et formaté.",
 
 
30
  llm=llm,
31
+ backstory=(
32
+ "Vous êtes un coordinateur expert, chargé de gérer chaque étape "
33
+ "du processus pour produire un exposé captivant et structuré."
34
+ ),
35
  )
36
 
37
+ planificateur = Agent(
38
+ role="Planificateur",
39
+ goal="Créer un plan structuré pour un exposé basé sur un thème donné.",
 
 
 
40
  tools=[search_tool],
41
+ llm=llm,
42
+ backstory=(
43
+ "Vous êtes un planificateur méthodique, spécialiste de l'organisation d'idées en sections."
44
+ ),
45
  )
46
 
47
+ redacteur = Agent(
48
+ role="Rédacteur",
 
 
 
49
  llm=llm,
50
+ goal="Rédiger des sections spécifiques de l'exposé en suivant un plan fourni.",
51
  tools=[search_tool],
52
+ backstory=(
53
+ "Vous êtes un rédacteur talentueux capable de transformer un sujet complexe "
54
+ "en un texte clair et captivant."
55
+ ),
56
  )
57
 
58
+ formatteur = Agent(
59
+ role="Expert en mise en page",
60
+ goal="Assembler et formater l'exposé final dans un fichier PDF professionnel.",
61
+ tools=[file_writer_tool], # Inclut maintenant FileWriteTool
 
 
62
  llm=llm,
63
+ backstory=(
64
+ "Vous êtes un expert en mise en page, dédié à produire des documents captivants."
65
+ ),
66
  )
67
 
68
+ # Tâches
69
+ generer_plan_task = Task(
70
+ description=(
71
+ "Créer un plan structuré détaillé à partir d'un thème principal. "
72
+ "Le plan doit inclure une introduction, des sections principales, et une conclusion."
73
+ ),
74
+ expected_output="Un plan détaillé avec sections et sous-sections.",
75
+ agent=planificateur,
 
 
 
76
  )
77
 
78
+ rediger_section_task = Task(
79
+ description=(
80
+ "Rédiger une section précise de l'exposé en suivant les instructions du plan. "
81
+ "Assurez-vous que le contenu est clair et engageant."
82
+ ),
83
+ expected_output="Une section rédigée en 3-4 paragraphes.",
84
+ agent=redacteur,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  )
86
 
87
+ assembler_et_formatter_task = Task(
88
+ description=(
89
+ "Assembler toutes les sections rédigées selon le plan et produire un fichier PDF final."
90
+ "Enregistrez la sortie dans un fichier appelé expose_final.pdf."
91
+ ),
92
+ expected_output="Un fichier PDF formaté professionnellement.",
93
+ agent=formatteur,
94
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95
 
96
+ # Crew avec un agent manager - Ne pas inclure le coordinateur dans la liste des agents
97
+ crew = Crew(
98
+ agents=[planificateur, redacteur, formatteur],
99
+ tasks=[generer_plan_task, rediger_section_task, assembler_et_formatter_task],
100
+ process=Process.hierarchical,
101
+ manager_agent=coordinateur,
102
+ )
 
 
103
 
104
+ # Interface utilisateur avec Streamlit
105
+ st.title("Générateur d'exposés")
106
+ st.write(
107
+ "Ce générateur d'exposés produit un document PDF complet à partir d'un thème principal."
108
+ )
 
 
 
109
 
110
+ # Entrée de l'utilisateur
111
+ theme = st.text_input("Entrez le thème de votre exposé :", value="Exemple : L'intelligence artificielle dans l'éducation")
112
 
113
+ # Bouton de génération
114
  if st.button("Générer l'exposé"):
115
+ st.info("Génération de l'exposé en cours, cela peut prendre quelques instants...")
116
+ try:
117
+ # Lancement de la Crew avec le thème donné
118
+ result = crew.kickoff(inputs={"theme": theme})
119
+
120
+ # Récupération et affichage du fichier PDF généré
121
+ pdf_file_path = result.tasks_output[-1].raw
122
+ with open(pdf_file_path, "rb") as pdf_file:
123
+ st.success("Exposé généré avec succès !")
124
+ st.download_button(
125
+ label="Télécharger l'exposé au format PDF",
126
+ data=pdf_file,
127
+ file_name="expose_final.pdf",
128
+ mime="application/pdf",
129
+ )
130
+ except Exception as e:
131
+ st.error(f"Une erreur est survenue : {e}")