File size: 6,750 Bytes
b7844e1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b237e11
b7844e1
 
 
 
 
 
 
 
 
 
 
 
 
e1feafa
b7844e1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e1feafa
b7844e1
 
 
 
 
 
 
 
 
 
 
 
e1feafa
b7844e1
 
e1feafa
b7844e1
 
 
a415b40
b7844e1
 
 
 
a415b40
b7844e1
 
a415b40
b7844e1
 
687de48
b7844e1
e1feafa
a415b40
b7844e1
 
a415b40
b7844e1
 
 
 
 
 
a415b40
b7844e1
a415b40
b7844e1
 
a415b40
b7844e1
 
a415b40
b7844e1
 
e1feafa
b7844e1
 
 
a415b40
b7844e1
 
a415b40
b7844e1
 
 
 
b237e11
687de48
b7844e1
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
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

# Configuration (use .env in production)
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)