Upload QCMTool.py
Browse files- tools/QCMTool.py +118 -0
tools/QCMTool.py
ADDED
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from typing import Any, Optional
|
2 |
+
from smolagents.tools import Tool
|
3 |
+
import json
|
4 |
+
import random
|
5 |
+
|
6 |
+
class QCMTool(Tool):
|
7 |
+
"""
|
8 |
+
A tool for running multiple-choice question (QCM) quizzes.
|
9 |
+
It picks a random question, checks the user's answer, and provides feedback.
|
10 |
+
"""
|
11 |
+
name = "qcm_tool"
|
12 |
+
description = "A tool for running multiple-choice question (QCM) quizzes. It picks a random question, checks the user's answer, and provides feedback."
|
13 |
+
inputs = {
|
14 |
+
'user_answer': {
|
15 |
+
'type': 'string',
|
16 |
+
'description': 'The user\'s selected answer (e.g., "A", "B", "C", "D").'
|
17 |
+
}
|
18 |
+
}
|
19 |
+
output_type = "string"
|
20 |
+
|
21 |
+
def __init__(self, json_file: str, *args, **kwargs):
|
22 |
+
"""
|
23 |
+
Initialize the QCM tool with a JSON file containing questions.
|
24 |
+
|
25 |
+
Args:
|
26 |
+
json_file (str): Path to the JSON file containing the questions.
|
27 |
+
"""
|
28 |
+
super().__init__(*args, **kwargs)
|
29 |
+
self.json_file = json_file
|
30 |
+
self.questions = self._load_questions()
|
31 |
+
self.is_initialized = True
|
32 |
+
|
33 |
+
def _load_questions(self):
|
34 |
+
"""
|
35 |
+
Load questions from the JSON file.
|
36 |
+
|
37 |
+
Returns:
|
38 |
+
list: A list of questions loaded from the JSON file.
|
39 |
+
|
40 |
+
Raises:
|
41 |
+
FileNotFoundError: If the JSON file is not found.
|
42 |
+
json.JSONDecodeError: If the JSON file is malformed.
|
43 |
+
"""
|
44 |
+
try:
|
45 |
+
with open(self.json_file, 'r') as file:
|
46 |
+
return json.load(file)
|
47 |
+
except FileNotFoundError:
|
48 |
+
raise FileNotFoundError(f"The file {self.json_file} was not found.")
|
49 |
+
except json.JSONDecodeError:
|
50 |
+
raise json.JSONDecodeError(f"The file {self.json_file} contains invalid JSON.")
|
51 |
+
|
52 |
+
def _pick_random_question(self):
|
53 |
+
"""
|
54 |
+
Pick a random question from the loaded questions.
|
55 |
+
|
56 |
+
Returns:
|
57 |
+
dict: A randomly selected question.
|
58 |
+
"""
|
59 |
+
return random.choice(self.questions)
|
60 |
+
|
61 |
+
def _check_answer(self, question: dict, user_answer: str) -> tuple[bool, str]:
|
62 |
+
"""
|
63 |
+
Check if the user's answer is correct and provide an explanation.
|
64 |
+
|
65 |
+
Args:
|
66 |
+
question (dict): The question dictionary containing the correct answer and explanation.
|
67 |
+
user_answer (str): The user's selected answer.
|
68 |
+
|
69 |
+
Returns:
|
70 |
+
tuple: A tuple containing a boolean (True if correct, False otherwise) and the explanation.
|
71 |
+
"""
|
72 |
+
is_correct = user_answer == question["correct_answer"]
|
73 |
+
return is_correct, question["explanation"]
|
74 |
+
|
75 |
+
def forward(self, user_answer: str) -> str:
|
76 |
+
"""
|
77 |
+
The main entry point for the QCM tool. It picks a random question, checks the user's answer,
|
78 |
+
and provides feedback.
|
79 |
+
|
80 |
+
Args:
|
81 |
+
user_answer (str): The user's selected answer (e.g., "A", "B", "C", "D").
|
82 |
+
|
83 |
+
Returns:
|
84 |
+
str: A formatted string indicating whether the answer was correct and providing an explanation.
|
85 |
+
|
86 |
+
Raises:
|
87 |
+
ValueError: If the user's answer is invalid.
|
88 |
+
"""
|
89 |
+
# Pick a random question
|
90 |
+
question = self._pick_random_question()
|
91 |
+
|
92 |
+
# Validate the user's answer
|
93 |
+
if user_answer.upper() not in ['A', 'B', 'C', 'D']:
|
94 |
+
raise ValueError("Invalid input. Please enter a valid option letter (A, B, C, D).")
|
95 |
+
|
96 |
+
# Map the user's answer to the corresponding option
|
97 |
+
option_index = ord(user_answer.upper()) - 65 # Convert 'A' to 0, 'B' to 1, etc.
|
98 |
+
selected_option = question["options"][option_index]
|
99 |
+
|
100 |
+
# Check the answer
|
101 |
+
is_correct, explanation = self._check_answer(question, selected_option)
|
102 |
+
|
103 |
+
# Return feedback
|
104 |
+
if is_correct:
|
105 |
+
return f"Correct! 🎉\nExplanation: {explanation}"
|
106 |
+
else:
|
107 |
+
return f"Incorrect! 😞\nExplanation: {explanation}"
|
108 |
+
|
109 |
+
if __name__ == "__main__":
|
110 |
+
# Initialize the QCM tool
|
111 |
+
qcm_tool = QCMTool(json_file="../info/questions.json")
|
112 |
+
|
113 |
+
# Simulate a user answering 'A'
|
114 |
+
try:
|
115 |
+
result = qcm_tool.forward(user_answer="A")
|
116 |
+
print(result)
|
117 |
+
except ValueError as e:
|
118 |
+
print(f"Error: {e}")
|