|
import os |
|
import json |
|
from textwrap import dedent |
|
from flask import Flask, render_template, request, jsonify |
|
from crewai import Agent, Crew, Process, Task |
|
from crewai_tools import SerperDevTool |
|
from langchain_google_genai import ChatGoogleGenerativeAI |
|
from typing import List, Dict, Any |
|
|
|
|
|
os.environ["GEMINI_API_KEY"] = "AIzaSyCQpoVCdk7h7MvAQKZOUfcnkQYVkHmAKwI" |
|
os.environ["SERPER_API_KEY"] = "9b90a274d9e704ff5b21c0367f9ae1161779b573" |
|
gemini_api_key = os.environ.get("GEMINI_API_KEY") |
|
serper_api_key = os.environ.get("SERPER_API_KEY") |
|
|
|
if not gemini_api_key: |
|
raise ValueError("Gemini API key missing.") |
|
if not serper_api_key: |
|
raise ValueError("Serper API key missing.") |
|
|
|
llm = ChatGoogleGenerativeAI( |
|
model="gemini-2.0-flash", |
|
temperature=0.7, |
|
google_api_key=gemini_api_key, |
|
max_output_tokens=8000, |
|
convert_system_message_to_human=True |
|
) |
|
|
|
search_tool = SerperDevTool() |
|
|
|
researcher = Agent( |
|
role='Chercheur de Sujets Académiques', |
|
goal=dedent("""Find factual, precise information on {topic} using the web search tool. |
|
Focus on key concepts, definitions, dates, and notable facts."""), |
|
backstory=dedent("""A meticulous researcher with a developed critical mind. |
|
Synthesizes information from multiple sources to extract the factual essence."""), |
|
tools=[search_tool], |
|
llm=llm, |
|
verbose=True, |
|
allow_delegation=False, |
|
max_iter=5 |
|
) |
|
|
|
quiz_creator = Agent( |
|
role='Concepteur Pédagogique de Quiz', |
|
goal=dedent("""Create an engaging multiple-choice quiz based on the factual information |
|
provided by the Researcher on {topic}. |
|
Formulate clear questions, plausible options, and identify the correct answer. |
|
Include a concise explanation for each question."""), |
|
backstory=dedent("""A specialist in pedagogical design who excels at transforming |
|
raw information into effective evaluation tools."""), |
|
llm=llm, |
|
verbose=True, |
|
allow_delegation=False, |
|
max_iter=3 |
|
) |
|
|
|
def extract_json_from_result(result_text: str) -> List[Dict[str, Any]]: |
|
try: |
|
json_start = result_text.find('[') |
|
json_end = result_text.rfind(']') + 1 |
|
|
|
if json_start != -1 and json_end != 0 and json_end > json_start: |
|
json_str = result_text[json_start:json_end] |
|
parsed_json = json.loads(json_str) |
|
if isinstance(parsed_json, list): |
|
for item in parsed_json: |
|
if not all(k in item for k in ('question', 'options', 'correct_answer')): |
|
raise ValueError("Invalid quiz element structure in JSON.") |
|
return parsed_json |
|
else: |
|
raise ValueError("The extracted JSON is not a list.") |
|
else: |
|
try: |
|
parsed_json = json.loads(result_text) |
|
if isinstance(parsed_json, list): |
|
for item in parsed_json: |
|
if not all(k in item for k in ('question', 'options', 'correct_answer')): |
|
raise ValueError("Invalid quiz element structure in JSON.") |
|
return parsed_json |
|
else: |
|
raise ValueError("The JSON found is not a list.") |
|
except json.JSONDecodeError: |
|
raise ValueError("No valid JSON block (list of objects) found in the result.") |
|
|
|
except json.JSONDecodeError as e: |
|
raise ValueError(f"JSON decoding error: {str(e)}. Contenu reçu : '{result_text[:200]}...'") |
|
except ValueError as e: |
|
raise e |
|
|
|
def research_task(topic: str, agent: Agent) -> Task: |
|
return Task( |
|
description=dedent(f"""Thorough research on '{topic}'. |
|
Identify and compile key factual information: definitions, dates, |
|
important figures, fundamental concepts, and significant events. |
|
Structure the information clearly and concisely."""), |
|
expected_output=dedent(f"""A synthetic report containing the most relevant information |
|
on '{topic}', ready to be used to create a quiz. |
|
Must include precise and verifiable facts."""), |
|
agent=agent, |
|
) |
|
|
|
def quiz_creation_task(topic: str, agent: Agent, context_task: Task) -> Task: |
|
return Task( |
|
description=dedent(f"""Based STRICTLY on the information provided in the context |
|
of the research on '{topic}', create a multiple-choice quiz. |
|
Generate between 8 and 12 pertinent questions. |
|
For each question, provide: 'question', 'options', 'correct_answer', and 'explanation'. |
|
The output format MUST be a valid JSON list and NOTHING ELSE."""), |
|
expected_output=dedent("""A valid JSON list, where each element is a quiz question |
|
with the keys 'question', 'options', 'correct_answer', and 'explanation'."""), |
|
agent=agent, |
|
context=[context_task] |
|
) |
|
|
|
app = Flask(__name__) |
|
app.secret_key = os.urandom(24) |
|
|
|
@app.route('/') |
|
def index(): |
|
return render_template('index.html') |
|
|
|
@app.route('/generate', methods=['POST']) |
|
def generate_quiz_endpoint(): |
|
if not request.is_json: |
|
return jsonify({'error': 'Invalid request, JSON expected.'}), 400 |
|
|
|
data = request.get_json() |
|
topic = data.get('topic') |
|
|
|
if not topic or not isinstance(topic, str) or len(topic.strip()) == 0: |
|
return jsonify({'error': 'The "topic" field is missing or invalid.'}), 400 |
|
|
|
topic = topic.strip() |
|
|
|
try: |
|
task_research = research_task(topic=topic, agent=researcher) |
|
task_quiz = quiz_creation_task(topic=topic, agent=quiz_creator, context_task=task_research) |
|
|
|
quiz_crew = Crew( |
|
agents=[researcher, quiz_creator], |
|
tasks=[task_research, task_quiz], |
|
process=Process.sequential, |
|
verbose=2 |
|
) |
|
|
|
crew_result = quiz_crew.kickoff(inputs={'topic': topic}) |
|
|
|
if not crew_result or not hasattr(quiz_crew, 'tasks_output') or not quiz_crew.tasks_output: |
|
return jsonify({'error': 'Quiz generation failed (no crew output).'}), 500 |
|
|
|
last_task_output = quiz_crew.tasks_output[-1] |
|
raw_output = last_task_output.raw |
|
|
|
if not raw_output: |
|
return jsonify({'error': 'Quiz generation failed (empty task output).'}), 500 |
|
|
|
try: |
|
quiz_data = extract_json_from_result(raw_output) |
|
return jsonify({'success': True, 'quiz': quiz_data}) |
|
|
|
except ValueError as json_error: |
|
return jsonify({'error': f'Error during quiz finalization: {json_error}'}), 500 |
|
|
|
except Exception as e: |
|
import traceback |
|
traceback.print_exc() |
|
return jsonify({'error': f'A server error occurred: {str(e)}'}), 500 |
|
|
|
if __name__ == '__main__': |
|
app.run(host='0.0.0.0', port=5000, debug=True) |