File size: 12,453 Bytes
154424a
2b143d9
 
154424a
 
2b143d9
154424a
 
 
 
2272990
3307d92
154424a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2b143d9
 
 
 
 
 
 
 
8fab093
2b143d9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154424a
2b143d9
 
 
 
 
 
 
 
 
 
 
 
 
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
# app.py
import streamlit as st
import asyncio
from typing import Any, List, Type
from pydantic import BaseModel, Field

from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib import colors

from crewai import Agent, Crew, Process, Task
from crewai_tools import SerperDevTool
from crewai import LLM

# --- PDF Tool from pdf_tool.py ---
class PDFToolInput(BaseModel):
    content: str = Field(..., description="Contenu de l'exposé en format Markdown")
    output_path: str = Field(..., description="Chemin de sortie du fichier PDF")

class PDFTool(BaseTool):
    name: str = "Generate PDF"
    description: str = "Outil pour générer un document PDF à partir de texte en format Markdown."
    args_schema: Type[BaseModel] = PDFToolInput

    def _run(
        self,
        content: str,
        output_path: str = "expose.pdf",
        **kwargs,
    ) -> str:
        try:
            doc = SimpleDocTemplate(output_path, pagesize=letter)
            styles = getSampleStyleSheet()

            # Ajout d'un style personnalisé pour le texte normal
            styles.add(ParagraphStyle(name='CustomBodyText',
                                      parent=styles['Normal'],
                                      fontSize=12,
                                      leading=14,
                                      spaceAfter=10))

            # Ajout d'un style personnalisé pour les titres de section
            styles.add(ParagraphStyle(name='CustomHeading1',
                                      parent=styles['Heading1'],
                                      fontSize=18,
                                      leading=22,
                                      spaceBefore=20,
                                      spaceAfter=6,
                                      textColor=colors.HexColor("#2c3e50"))) # Couleur bleu foncé

            # Ajout d'un style personnalisé pour les titres de sous-section
            styles.add(ParagraphStyle(name='CustomHeading2',
                                      parent=styles['Heading2'],
                                      fontSize=14,
                                      leading=18,
                                      spaceBefore=10,
                                      spaceAfter=4,
                                      textColor=colors.HexColor("#34495e"))) # Couleur bleu-gris

            # Séparer le contenu en sections en utilisant les sauts de ligne comme délimiteurs
            sections = content.split("\n\n")

            Story = []
            for section in sections:
                # Déterminer si la section est un titre de section, un titre de sous-section ou du texte normal
                if section.startswith("# "):
                    # Titre de section
                    title = section[2:].strip()
                    Story.append(Paragraph(title, styles["CustomHeading1"]))
                elif section.startswith("## "):
                    # Titre de sous-section
                    subtitle = section[3:].strip()
                    Story.append(Paragraph(subtitle, styles["CustomHeading2"]))
                else:
                    # Texte normal
                    Story.append(Paragraph(section, styles["CustomBodyText"]))

            doc.build(Story)
            return f"Fichier PDF généré avec succès : {output_path}"
        except Exception as e:
            return f"Erreur lors de la génération du PDF : {e}"


# --- Crew utilities from crew_utils.py ---

# --- Définition des outils ---
search_tool = SerperDevTool()
pdf_tool = PDFTool()

# --- Définition du LLM (Gemini) ---
# Remplace par ta clé API
GEMINI_API_KEY = "AIzaSyD6yZxfVOnh63GXBJjakAupk9aP4CZrgrQ"
llm = LLM(
    model="gemini/gemini-1.5-flash",
    temperature=0.7,
    timeout=120,           # Seconds to wait for response
    max_tokens=8000,
)


# --- Définition des agents ---
# Chef de Projet
project_manager = Agent(
    role="Chef de Projet",
    goal="Coordonner la création d'un exposé complet et de haute qualité sur le thème donné.",
    backstory="Expert en gestion de projet et en coordination d'équipes, capable de diriger des projets complexes jusqu'à leur aboutissement.",
    verbose=True,
    llm=llm,
    allow_delegation=True,
)

# Planificateur
planner = Agent(
    role="Planificateur d'Exposé",
    goal="Créer un plan détaillé et pertinent pour l'exposé.",
    backstory="Spécialiste de la structuration de contenu, capable de générer des plans clairs et logiques pour des exposés complexes.",
    verbose=True,
    llm=llm,
)

# Rédacteurs (3 instances pour gérer l'introduction, le développement et la conclusion)
researcher_intro = Agent(
    role="Rédacteur Spécialisé en Introduction",
    goal="Rédiger une introduction captivante pour l'exposé.",
    backstory="Expert en recherche et en rédaction, capable de produire du contenu informatif et bien écrit sur des sujets variés.",
    verbose=True,
    llm=llm,
    tools=[search_tool],
)

researcher_dev = Agent(
    role="Rédacteur Spécialisé en Développement",
    goal="Rédiger les sections de développement de l'exposé.",
    backstory="Expert en recherche et en rédaction, capable de produire du contenu informatif et bien écrit sur des sujets variés.",
    verbose=True,
    llm=llm,
    tools=[search_tool],
)

researcher_conclusion = Agent(
    role="Rédacteur Spécialisé en Conclusion",
    goal="Rédiger une conclusion percutante pour l'exposé.",
    backstory="Expert en recherche et en rédaction, capable de produire du contenu informatif et bien écrit sur des sujets variés.",
    verbose=True,
    llm=llm,
    tools=[search_tool],
)

# Assembleur
assembler = Agent(
    role="Assembleur d'Exposé",
    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.",
    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.",
    verbose=True,
    llm=llm,
    tools=[pdf_tool],
)

# --- Définition des tâches ---
# Tâche pour le Planificateur
create_plan_task = Task(
    description="""
        Créez un plan détaillé pour un exposé sur le thème suivant : {topic}.
        Le plan doit inclure une introduction, plusieurs sections principales (au moins 3), et une conclusion.
        Assurez-vous que le plan est logique et couvre tous les aspects importants du sujet.
        Divisez les sections principales en sous-sections si nécessaire pour une meilleure organisation.
    """,
    expected_output="Un plan d'exposé structuré avec des titres de sections et sous-sections, en format Markdown.",
    agent=planner,
)

class SectionContent(BaseModel):
    title: str = Field(..., description="Titre de la section")
    content: str = Field(..., description="Contenu de la section")

# Tâches pour les Rédacteurs (exemple pour 3 sections, à adapter)
async def write_section_task(
        researcher: Agent, section_title: str, context: List[Task]
    ) -> Task:
    return Task(
        description=f"""
            Rédigez la section '{section_title}' de l'exposé en vous basant sur le plan fourni.
            Effectuez des recherches approfondies en utilisant l'outil de recherche pour enrichir le contenu.
            La section doit être informative, bien écrite et doit respecter le ton académique d'un exposé.
            Voici le contexte: {context}
        """,
        expected_output=f"Texte complet et bien rédigé pour la section '{section_title}' de l'exposé, au format Markdown.",
        agent=researcher,
        tools=[search_tool],
        output_pydantic=SectionContent,  # Utilisez le modèle Pydantic ici
        context=context,
    )

# Tâche pour l'Assembleur
compile_report_task = Task(
    description="""
        Compilez toutes les sections rédigées de l'exposé dans un seul document.
        Organisez les sections selon le plan fourni par le planificateur.
        Générez un document PDF final prêt pour la présentation.
        Assurez-vous que le document est bien structuré, facile à lire et qu'il respecte les conventions d'un exposé académique.
        Voici le contexte: {context}
    """,
    expected_output="Un document PDF complet de l'exposé, prêt à être présenté.",
    agent=assembler,
    tools=[pdf_tool],
)

# --- Orchestration des tâches avec un processus hiérarchique ---
async def process_section_tasks(
    manager: Agent,
    plan: Any, # Remplace Any par le type de retour attendu de la tâche create_plan_task
    researchers: List[Agent],
):
    # Assume que plan est une liste de titres de sections
    sections = plan.split("\n") # Adapter selon le format réel du plan
    section_tasks: List[Task] = []

    for i, section in enumerate(sections):
        if section != "":
            researcher = researchers[i % len(researchers)]
            section_task = await write_section_task(
                researcher=researcher,
                section_title=section.strip(),
                context=[create_plan_task],
            )
            section_tasks.append(section_task)

    return section_tasks

class ExposeCrew(Crew):
    def __init__(self, agents, tasks, process, manager_llm, verbose):
        super().__init__(agents=agents, tasks=tasks, process=process, manager_llm=manager_llm, verbose=verbose)

    async def kickoff(self, inputs: dict = {}) -> str:
        # Exécuter la tâche de création du plan
        plan = await create_plan_task.execute(context=inputs)
        self.log.info(f"Plan de l'exposé : {plan}")

        # Créer et exécuter les tâches de rédaction de section en parallèle
        section_tasks = await process_section_tasks(
            manager=self.manager_llm,
            plan=plan,
            researchers=[researcher_intro, researcher_dev, researcher_conclusion],
        )
        
        sections_results = await asyncio.gather(*[task.execute(context=[create_plan_task]) for task in section_tasks])
        section_outputs = [result.content for result in sections_results]

        self.log.info(f"Sections rédigées : {section_outputs}")

        # Mettre à jour la description de la tâche de compilation avec les sections rédigées
        compile_report_task.description = f"""
            Compilez toutes les sections rédigées de l'exposé dans un seul document.
            Organisez les sections selon le plan fourni par le planificateur :
            {plan}
            Sections rédigées :
            {section_outputs}
            Générez un document PDF final prêt pour la présentation.
        """
        compile_report_task.context = section_tasks

        # Exécuter la tâche de compilation
        result = await compile_report_task.execute()
        return result

# --- Streamlit App from app.py ---

async def run_crew(topic):
    """Exécute le crew pour générer l'exposé."""
    crew = ExposeCrew(
        agents=[project_manager, planner, researcher_intro, researcher_dev, researcher_conclusion, assembler],
        tasks=[create_plan_task, compile_report_task],
        process=Process.hierarchical,
        manager_llm=llm,
        verbose=True,
    )
    result = await crew.kickoff(inputs={"topic": topic})
    return result

# Interface Streamlit
st.title("Générateur d'Exposés avec CrewAI")

topic = st.text_input("Entrez le thème de l'exposé :")

if st.button("Générer l'exposé"):
    if not topic:
        st.error("Veuillez entrer un thème pour l'exposé.")
    else:
        with st.spinner("Création de l'exposé en cours..."):
            try:
                # Exécuter le crew de manière asynchrone
                result = await asyncio.run(run_crew(topic))
                st.success("Exposé généré avec succès!")

                # Téléchargement du fichier PDF
                with open("expose.pdf", "rb") as file:
                    st.download_button(
                        label="Télécharger l'exposé en PDF",
                        data=file,
                        file_name="expose.pdf",
                        mime="application/pdf"
                    )

            except Exception as e:
                st.error(f"Une erreur s'est produite : {e}")