Docfile commited on
Commit
b21bde5
·
verified ·
1 Parent(s): bc61ea0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +113 -227
app.py CHANGED
@@ -1,247 +1,133 @@
1
- from flask import Flask, render_template, request, jsonify
2
- from werkzeug.utils import secure_filename
3
  import os
4
  import json
5
  from textwrap import dedent
 
 
6
  from crewai import Agent, Crew, Process, Task
7
- from crewai_tools import SerperDevTool, PDFSearchTool
8
  from crewai import LLM
9
- from typing import List, Dict, Union, Optional
10
- import tempfile
11
- import logging
12
- from datetime import datetime
13
- import hashlib
14
-
15
- # Configure logging
16
- logging.basicConfig(
17
- level=logging.INFO,
18
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
19
- )
20
- logger = logging.getLogger(__name__)
21
-
22
- app = Flask(__name__)
23
 
24
- # Configuration
25
- UPLOAD_FOLDER = tempfile.gettempdir()
26
- ALLOWED_EXTENSIONS = {'pdf'}
27
- MAX_FILE_SIZE = 16 * 1024 * 1024 # 16MB
28
- app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
29
- app.config['MAX_CONTENT_LENGTH'] = MAX_FILE_SIZE
30
 
31
- # Load environment variables
32
- def get_env_variable(var_name: str) -> str:
33
- """Safely get environment variable with error handling."""
34
- value = os.environ.get(var_name)
35
- if value is None:
36
- raise ValueError(f"Environment variable {var_name} is not set")
37
- return value
38
-
39
- try:
40
- # API Keys configuration
41
- os.environ["GOOGLE_API_KEY"] = get_env_variable("GEMINI_API_KEY")
42
- os.environ["GEMINI_API_KEY"] = get_env_variable("GEMINI_API_KEY")
43
- os.environ["SERPER_API_KEY"] = get_env_variable("SERPER_API_KEY")
44
- except ValueError as e:
45
- logger.error(f"Configuration error: {e}")
46
- raise
47
-
48
- # Initialize LLM
49
  llm = LLM(
50
  model="gemini/gemini-1.5-flash",
51
- temperature=0.7,
52
  timeout=120,
53
  max_tokens=8000,
54
  )
55
 
56
- # Initialize tools
57
  search_tool = SerperDevTool()
58
 
59
- def create_pdf_tool(pdf_path: Optional[str] = None) -> PDFSearchTool:
60
- """Create a PDFSearchTool with optional PDF path."""
61
- config = {
62
- 'llm': {
63
- 'provider': 'google',
64
- 'config': {
65
- 'model': 'gemini-1.5-flash',
66
- },
67
- },
68
- 'embedder': {
69
- 'provider': 'google',
70
- 'config': {
71
- 'model': 'models/embedding-001',
72
- 'task_type': 'retrieval_document',
73
- },
74
- },
75
- }
76
-
77
- if pdf_path:
78
- return PDFSearchTool(pdf=pdf_path, config=config)
79
- return PDFSearchTool(config=config)
80
-
81
- def allowed_file(filename: str) -> bool:
82
- """Check if the file extension is allowed."""
83
- return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
84
-
85
- def generate_safe_filename(filename: str) -> str:
86
- """Generate a safe filename with timestamp and hash."""
87
- timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
88
- file_hash = hashlib.md5(filename.encode()).hexdigest()[:10]
89
- ext = filename.rsplit('.', 1)[1].lower()
90
- return f"upload_{timestamp}_{file_hash}.{ext}"
91
-
92
- class FlashcardGenerator:
93
- def __init__(self, topic: str, pdf_path: Optional[str] = None):
94
- self.topic = topic
95
- self.pdf_path = pdf_path
96
- self.researcher = self._create_researcher()
97
- self.writer = self._create_writer()
98
-
99
- def _create_researcher(self) -> Agent:
100
- """Create the researcher agent with appropriate tools."""
101
- tools = [search_tool]
102
- if self.pdf_path:
103
- tools.append(create_pdf_tool(self.pdf_path))
104
-
105
- return Agent(
106
- role='Chercheur de Sujets',
107
- goal=dedent(f"""Trouver les informations les plus pertinentes et précises sur {self.topic}
108
- en utilisant l'API SerpApi et en analysant les PDFs fournis."""),
109
- backstory=dedent("""Un chercheur expert spécialisé dans la collecte d'informations sur divers sujets.
110
- Capable d'utiliser l'API SerpApi pour des recherches précises et d'analyser des documents PDF."""),
111
- tools=tools,
112
- llm=llm,
113
- verbose=True,
114
- allow_delegation=False
115
- )
116
-
117
- def _create_writer(self) -> Agent:
118
- """Create the writer agent."""
119
- return Agent(
120
- role='Rédacteur de Flashcards',
121
- goal=dedent("""Créer des flashcards claires et concises en format question-réponse
122
- basées sur les informations fournies par le Chercheur."""),
123
- backstory=dedent("""Un expert en pédagogie et en création de matériel d'apprentissage.
124
- Capable de transformer des informations complexes en flashcards simples et mémorisables."""),
125
- llm=llm,
126
- verbose=True,
127
- allow_delegation=False
128
- )
129
-
130
- def create_research_task(self) -> Task:
131
- """Create the research task."""
132
- description = f"""Effectuer une recherche approfondie sur le sujet '{self.topic}'."""
133
- if self.pdf_path:
134
- description += f" Analyser également le contenu du PDF fourni: {self.pdf_path}"
135
-
136
- return Task(
137
- description=dedent(description),
138
- expected_output="Une liste d'informations pertinentes sur le sujet.",
139
- agent=self.researcher
140
- )
141
-
142
- def create_flashcard_task(self, research_task: Task) -> Task:
143
- """Create the flashcard creation task."""
144
- return Task(
145
- description=dedent("""Transformer les informations fournies par le Chercheur
146
- en une série de flashcards au format JSON. je veux une vingtaine de flashcard très robuste et difficile . Chaque flashcard doit avoir une question
147
- d'un côté et une réponse concise de l'autre. Les réponses doivent être claires et informatives."""),
148
- expected_output="Une liste de flashcards au format JSON.",
149
- agent=self.writer,
150
- context=[research_task]
151
- )
152
-
153
- def generate(self) -> List[Dict[str, str]]:
154
- """Generate flashcards using the crew workflow."""
155
- research_task = self.create_research_task()
156
- flashcard_task = self.create_flashcard_task(research_task)
157
-
158
- crew = Crew(
159
- agents=[self.researcher, self.writer],
160
- tasks=[research_task, flashcard_task],
161
- process=Process.sequential,
162
- verbose=True
163
- )
164
-
165
- result = crew.kickoff()
166
- return self.extract_json_from_result(result.tasks_output[-1].raw)
167
-
168
- @staticmethod
169
- def extract_json_from_result(result_text: str) -> List[Dict[str, str]]:
170
- """Extract and validate JSON from the result text."""
171
- try:
172
- json_start = result_text.find('[')
173
- json_end = result_text.rfind(']') + 1
174
- if json_start == -1 or json_end == 0:
175
- raise ValueError("JSON non trouvé dans le résultat")
176
-
177
- json_str = result_text[json_start:json_end]
178
- flashcards = json.loads(json_str)
179
-
180
- # Validate flashcard format
181
- for card in flashcards:
182
- if not isinstance(card, dict) or 'question' not in card or 'answer' not in card:
183
- raise ValueError("Format de flashcard invalide")
184
-
185
- return flashcards
186
- except (json.JSONDecodeError, ValueError) as e:
187
- logger.error(f"Error extracting JSON: {str(e)}")
188
- raise ValueError(f"Erreur lors de l'extraction du JSON : {str(e)}")
189
 
190
- @app.route('/')
191
- def index():
192
- """Render the main page."""
193
- return render_template('index.html')
 
 
 
 
 
 
194
 
195
- @app.route('/generate', methods=['POST'])
196
- def generate_flashcards():
197
- """Handle flashcard generation requests."""
198
  try:
199
- # Validate topic
200
- topic = request.form.get('topic')
201
- if not topic:
202
- return jsonify({'error': 'Veuillez entrer un sujet.'}), 400
203
-
204
- # Handle file upload
205
- pdf_path = None
206
- if 'file' in request.files:
207
- file = request.files['file']
208
- if file and file.filename:
209
- if not allowed_file(file.filename):
210
- return jsonify({'error': 'Format de fichier non supporté. Veuillez utiliser un PDF.'}), 400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211
 
212
- if file.content_length and file.content_length > MAX_FILE_SIZE:
213
- return jsonify({'error': 'Le fichier est trop volumineux. Maximum 16MB.'}), 400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
 
215
- # Generate safe filename and save file
216
- safe_filename = generate_safe_filename(file.filename)
217
- pdf_path = os.path.join(app.config['UPLOAD_FOLDER'], safe_filename)
218
- file.save(pdf_path)
219
- logger.info(f"File saved: {pdf_path}")
220
-
221
- try:
222
- # Generate flashcards
223
- generator = FlashcardGenerator(topic, pdf_path)
224
- flashcards = generator.generate()
225
-
226
- return jsonify({
227
- 'success': True,
228
- 'flashcards': flashcards
229
- })
230
-
231
- finally:
232
- # Clean up PDF file
233
- if pdf_path and os.path.exists(pdf_path):
234
- os.remove(pdf_path)
235
- logger.info(f"File cleaned up: {pdf_path}")
236
-
237
- except Exception as e:
238
- logger.error(f"Error generating flashcards: {str(e)}")
239
- return jsonify({'error': str(e)}), 500
240
-
241
- @app.errorhandler(413)
242
- def request_entity_too_large(error):
243
- """Handle file size limit exceeded error."""
244
- return jsonify({'error': 'Le fichier est trop volumineux. Maximum 16MB.'}), 413
245
-
246
- if __name__ == '__main__':
247
- app.run(debug=True)
 
 
 
1
  import os
2
  import json
3
  from textwrap import dedent
4
+
5
+ import streamlit as st
6
  from crewai import Agent, Crew, Process, Task
7
+ from crewai_tools import SerperDevTool
8
  from crewai import LLM
9
+ from typing import List
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
+ # Configuration des clés API
12
+ os.environ["GEMINI_API_KEY"] = os.environ.get("GEMINI_API_KEY")
13
+ os.environ["SERPER_API_KEY"] = os.environ.get("SERPER_API_KEY")
 
 
 
14
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  llm = LLM(
16
  model="gemini/gemini-1.5-flash",
17
+ temperature=1,
18
  timeout=120,
19
  max_tokens=8000,
20
  )
21
 
22
+ # Initialisation de l'outil de recherche
23
  search_tool = SerperDevTool()
24
 
25
+ # Définition des agents
26
+ researcher = Agent(
27
+ role='Chercheur de Sujets',
28
+ goal=dedent("""Trouver les informations les plus pertinentes et précises sur {topic}
29
+ en utilisant l'API SerpApi."""),
30
+ backstory=dedent("""Un chercheur expert spécialisé dans la collecte d'informations sur divers sujets.
31
+ Capable d'utiliser l'API SerpApi pour des recherches précises et efficaces."""),
32
+ tools=[search_tool],
33
+ llm=llm,
34
+ verbose=True,
35
+ allow_delegation=False
36
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
 
38
+ writer = Agent(
39
+ role='Rédacteur de Flashcards',
40
+ goal=dedent("""Créer des flashcards claires et concises en format question-réponse
41
+ basées sur les informations fournies par le Chercheur."""),
42
+ backstory=dedent("""Un expert en pédagogie et en création de matériel d'apprentissage.
43
+ Capable de transformer des informations complexes en flashcards simples et mémorisables."""),
44
+ llm=llm,
45
+ verbose=True,
46
+ allow_delegation=False
47
+ )
48
 
49
+ def extract_json_from_result(result_text: str) -> list:
50
+ """Extrait le JSON des résultats de la crew."""
 
51
  try:
52
+ # Trouve le début et la fin du JSON dans le texte
53
+ json_start = result_text.find('[')
54
+ json_end = result_text.rfind(']') + 1
55
+ if json_start == -1 or json_end == 0:
56
+ raise ValueError("JSON non trouvé dans le résultat")
57
+
58
+ json_str = result_text[json_start:json_end]
59
+ return json.loads(json_str)
60
+ except (json.JSONDecodeError, ValueError) as e:
61
+ raise ValueError(f"Erreur lors de l'extraction du JSON : {str(e)}")
62
+
63
+ # Définition des tâches
64
+ def research_task(topic: str) -> Task:
65
+ return Task(
66
+ description=dedent(f"""Effectuer une recherche approfondie sur le sujet '{topic}'.
67
+ Compiler les informations les plus pertinentes et les plus récentes."""),
68
+ expected_output="Une liste d'informations pertinentes sur le sujet.",
69
+ agent=researcher
70
+ )
71
+
72
+ def flashcard_creation_task(research_task: Task) -> Task:
73
+ return Task(
74
+ description=dedent("""Transformer les informations fournies par le Chercheur
75
+ en une série de flashcards au format JSON. Chaque flashcard doit avoir une question
76
+ d'un côté et une réponse concise de l'autre."""),
77
+ expected_output="Une liste de flashcards au format JSON.",
78
+ agent=writer,
79
+ context=[research_task]
80
+ )
81
+
82
+ # Interface Streamlit
83
+ st.title("🤖 Générateur de Flashcards avec CrewAI")
84
+
85
+ topic = st.text_input("Entrez le sujet des flashcards:", "Intelligence Artificielle")
86
+
87
+ if st.button("Générer les Flashcards"):
88
+ if not topic:
89
+ st.error("Veuillez entrer un sujet.")
90
+ else:
91
+ with st.spinner('Création des flashcards en cours...'):
92
+ try:
93
+ # Création des tâches
94
+ research = research_task(topic)
95
+ flashcard_creation = flashcard_creation_task(research)
96
+
97
+ # Création de la crew
98
+ crew = Crew(
99
+ agents=[researcher, writer],
100
+ tasks=[research, flashcard_creation],
101
+ process=Process.sequential,
102
+ verbose=True
103
+ )
104
+
105
+ # Exécution de la crew
106
+ result = crew.kickoff()
107
 
108
+ # Extraction du JSON depuis le dernier résultat de tâche
109
+ if result.tasks_output and len(result.tasks_output) > 0:
110
+ last_task_output = result.tasks_output[-1].raw
111
+ flashcards = extract_json_from_result(last_task_output)
112
+
113
+ # Affichage des flashcards
114
+ st.success("Flashcards générées avec succès !")
115
+
116
+ # Création d'onglets pour différents modes d'affichage
117
+ tab1, tab2 = st.tabs(["Mode Étude", "Mode JSON"])
118
+
119
+ with tab1:
120
+ for i, card in enumerate(flashcards, 1):
121
+ with st.expander(f"Flashcard {i}: {card['question']}", expanded=False):
122
+ st.write("**Réponse:**", card['answer'])
123
+
124
+ with tab2:
125
+ st.json(flashcards)
126
 
127
+ else:
128
+ st.error("Aucun résultat généré par la crew.")
129
+
130
+ except Exception as e:
131
+ st.error(f"Une erreur est survenue : {str(e)}")
132
+ st.write("Résultat brut pour débogage :")
133
+ st.write(result)