Docfile commited on
Commit
154424a
·
verified ·
1 Parent(s): d040edf

Update app.py

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