|
from typing import Any, Optional |
|
from smolagents.tools import Tool |
|
import json |
|
import random |
|
|
|
|
|
class QCMTool(Tool): |
|
""" |
|
A tool for running multiple-choice question (QCM) quizzes. |
|
It picks a random question, checks the user's answer, and provides feedback. |
|
""" |
|
name = "qcm_tool" |
|
description = "A tool for running multiple-choice question (QCM) quizzes. It picks a random question, checks the user's answer, and provides feedback." |
|
inputs = { |
|
'user_answer': { |
|
'type': 'string', |
|
'description': 'The user\'s selected answer (e.g., "A", "B", "C", "D").' |
|
} |
|
} |
|
output_type = "string" |
|
|
|
def __init__(self, json_file: str, *args, **kwargs): |
|
""" |
|
Initialize the QCM tool with a JSON file containing questions. |
|
|
|
Args: |
|
json_file (str): Path to the JSON file containing the questions. |
|
""" |
|
super().__init__(*args, **kwargs) |
|
self.json_file = json_file |
|
self.questions = self._load_questions() |
|
self.is_initialized = True |
|
|
|
def _load_questions(self): |
|
""" |
|
Load questions from the JSON file. |
|
|
|
Returns: |
|
list: A list of questions loaded from the JSON file. |
|
|
|
Raises: |
|
FileNotFoundError: If the JSON file is not found. |
|
json.JSONDecodeError: If the JSON file is malformed. |
|
""" |
|
try: |
|
with open(self.json_file, 'r') as file: |
|
return json.load(file) |
|
except FileNotFoundError: |
|
raise FileNotFoundError(f"The file {self.json_file} was not found.") |
|
except json.JSONDecodeError: |
|
raise json.JSONDecodeError(f"The file {self.json_file} contains invalid JSON.") |
|
|
|
def _pick_random_question(self): |
|
""" |
|
Pick a random question from the loaded questions. |
|
|
|
Returns: |
|
dict: A randomly selected question. |
|
""" |
|
return random.choice(self.questions) |
|
|
|
def _check_answer(self, question: dict, user_answer: str) -> tuple[bool, str]: |
|
""" |
|
Check if the user's answer is correct and provide an explanation. |
|
|
|
Args: |
|
question (dict): The question dictionary containing the correct answer and explanation. |
|
user_answer (str): The user's selected answer. |
|
|
|
Returns: |
|
tuple: A tuple containing a boolean (True if correct, False otherwise) and the explanation. |
|
""" |
|
is_correct = user_answer == question["correct_answer"] |
|
return is_correct, question["explanation"] |
|
|
|
def forward(self, user_answer: str) -> str: |
|
""" |
|
The main entry point for the QCM tool. It picks a random question, checks the user's answer, |
|
and provides feedback. |
|
|
|
Args: |
|
user_answer: The user's selected answer (e.g., "A", "B", "C", "D"). |
|
|
|
Returns: |
|
str: A formatted string indicating whether the answer was correct and providing an explanation. |
|
|
|
Raises: |
|
ValueError: If the user's answer is invalid. |
|
""" |
|
|
|
question = self._pick_random_question() |
|
|
|
|
|
if user_answer.upper() not in ['A', 'B', 'C', 'D']: |
|
raise ValueError("Invalid input. Please enter a valid option letter (A, B, C, D).") |
|
|
|
|
|
option_index = ord(user_answer.upper()) - 65 |
|
selected_option = question["options"][option_index] |
|
|
|
|
|
is_correct, explanation = self._check_answer(question, selected_option) |
|
|
|
|
|
if is_correct: |
|
return f"Correct! 🎉\nExplanation: {explanation}" |
|
else: |
|
return f"Incorrect! 😞\nExplanation: {explanation}" |
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
qcm_tool = QCMTool(json_file="../info/questions.json") |
|
question = qcm_tool._pick_random_question() |
|
print(question) |
|
|
|
|
|
try: |
|
result = qcm_tool.forward(user_answer="A") |
|
print(result) |
|
except ValueError as e: |
|
print(f"Error: {e}") |
|
|