Docfile commited on
Commit
0e3dbd4
·
verified ·
1 Parent(s): 83f3482

Update app.py

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