Fix: Updated app.py with better error handling and compatibility
Browse files
app.py
CHANGED
@@ -6,50 +6,62 @@ from peft import PeftModel
|
|
6 |
import re
|
7 |
|
8 |
# Model configuration
|
9 |
-
BASE_MODEL = "deepseek-ai/deepseek-math-7b-instruct"
|
10 |
REPO_ID = "danxh/math-mcq-generator-v1"
|
11 |
|
12 |
-
#
|
13 |
-
|
|
|
|
|
14 |
def load_model():
|
15 |
-
"""Load the fine-tuned model"""
|
16 |
-
|
17 |
-
bnb_config = BitsAndBytesConfig(
|
18 |
-
load_in_4bit=True,
|
19 |
-
bnb_4bit_use_double_quant=True,
|
20 |
-
bnb_4bit_quant_type="nf4",
|
21 |
-
bnb_4bit_compute_dtype="bfloat16"
|
22 |
-
)
|
23 |
-
|
24 |
-
# Load base model
|
25 |
-
base_model = AutoModelForCausalLM.from_pretrained(
|
26 |
-
BASE_MODEL,
|
27 |
-
quantization_config=bnb_config,
|
28 |
-
device_map="auto",
|
29 |
-
torch_dtype=torch.bfloat16
|
30 |
-
)
|
31 |
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
|
47 |
def generate_mcq(chapter, topics, difficulty="medium", cognitive_skill="direct_application"):
|
48 |
"""Generate MCQ using the fine-tuned model"""
|
49 |
|
50 |
-
|
|
|
51 |
|
52 |
-
|
|
|
|
|
|
|
53 |
Generate a math MCQ similar in style to the provided examples.
|
54 |
|
55 |
### Input:
|
@@ -57,26 +69,29 @@ Generate a math MCQ similar in style to the provided examples.
|
|
57 |
|
58 |
### Response:
|
59 |
"""
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
|
|
|
|
|
|
80 |
|
81 |
def parse_mcq_response(response):
|
82 |
"""Parse the model response"""
|
@@ -112,13 +127,159 @@ def parse_mcq_response(response):
|
|
112 |
"error": str(e)
|
113 |
}
|
114 |
|
115 |
-
# [Include the web interface function here - copy from above]
|
116 |
def generate_mcq_web(chapter, topics_text, difficulty, cognitive_skill, num_questions=1):
|
117 |
-
|
118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
|
120 |
-
|
121 |
-
|
|
|
|
|
122 |
|
|
|
123 |
if __name__ == "__main__":
|
124 |
interface.launch()
|
|
|
6 |
import re
|
7 |
|
8 |
# Model configuration
|
9 |
+
BASE_MODEL = "deepseek-ai/deepseek-math-7b-instruct"
|
10 |
REPO_ID = "danxh/math-mcq-generator-v1"
|
11 |
|
12 |
+
# Global variables for model and tokenizer
|
13 |
+
model = None
|
14 |
+
tokenizer = None
|
15 |
+
|
16 |
def load_model():
|
17 |
+
"""Load the fine-tuned model with error handling"""
|
18 |
+
global model, tokenizer
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
|
20 |
+
try:
|
21 |
+
print("🔄 Loading model and tokenizer...")
|
22 |
+
|
23 |
+
# Simplified loading for Hugging Face Spaces
|
24 |
+
bnb_config = BitsAndBytesConfig(
|
25 |
+
load_in_4bit=True,
|
26 |
+
bnb_4bit_use_double_quant=True,
|
27 |
+
bnb_4bit_quant_type="nf4",
|
28 |
+
bnb_4bit_compute_dtype=torch.float16 # Changed to float16 for better compatibility
|
29 |
+
)
|
30 |
+
|
31 |
+
# Load base model
|
32 |
+
base_model = AutoModelForCausalLM.from_pretrained(
|
33 |
+
BASE_MODEL,
|
34 |
+
quantization_config=bnb_config,
|
35 |
+
device_map="auto",
|
36 |
+
torch_dtype=torch.float16,
|
37 |
+
trust_remote_code=True
|
38 |
+
)
|
39 |
+
|
40 |
+
# Load LoRA adapter
|
41 |
+
model = PeftModel.from_pretrained(base_model, REPO_ID)
|
42 |
+
|
43 |
+
# Load tokenizer
|
44 |
+
tokenizer = AutoTokenizer.from_pretrained(REPO_ID)
|
45 |
+
if tokenizer.pad_token is None:
|
46 |
+
tokenizer.pad_token = tokenizer.eos_token
|
47 |
+
|
48 |
+
print("✅ Model loaded successfully!")
|
49 |
+
return True
|
50 |
+
|
51 |
+
except Exception as e:
|
52 |
+
print(f"❌ Error loading model: {str(e)}")
|
53 |
+
return False
|
54 |
|
55 |
def generate_mcq(chapter, topics, difficulty="medium", cognitive_skill="direct_application"):
|
56 |
"""Generate MCQ using the fine-tuned model"""
|
57 |
|
58 |
+
if model is None or tokenizer is None:
|
59 |
+
return "❌ Model not loaded. Please wait for initialization."
|
60 |
|
61 |
+
try:
|
62 |
+
input_text = f"chapter: {chapter}\ntopics: {topics}\nDifficulty: {difficulty}\nCognitive Skill: {cognitive_skill}"
|
63 |
+
|
64 |
+
prompt = f"""### Instruction:
|
65 |
Generate a math MCQ similar in style to the provided examples.
|
66 |
|
67 |
### Input:
|
|
|
69 |
|
70 |
### Response:
|
71 |
"""
|
72 |
+
|
73 |
+
inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=512)
|
74 |
+
inputs = {k: v.to(model.device) for k, v in inputs.items()}
|
75 |
+
|
76 |
+
with torch.no_grad():
|
77 |
+
outputs = model.generate(
|
78 |
+
**inputs,
|
79 |
+
max_new_tokens=300,
|
80 |
+
temperature=0.7,
|
81 |
+
do_sample=True,
|
82 |
+
pad_token_id=tokenizer.eos_token_id,
|
83 |
+
eos_token_id=tokenizer.eos_token_id,
|
84 |
+
repetition_penalty=1.1
|
85 |
+
)
|
86 |
+
|
87 |
+
generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True)
|
88 |
+
response_start = generated_text.find("### Response:") + len("### Response:")
|
89 |
+
response = generated_text[response_start:].strip()
|
90 |
+
|
91 |
+
return response
|
92 |
+
|
93 |
+
except Exception as e:
|
94 |
+
return f"❌ Error generating MCQ: {str(e)}"
|
95 |
|
96 |
def parse_mcq_response(response):
|
97 |
"""Parse the model response"""
|
|
|
127 |
"error": str(e)
|
128 |
}
|
129 |
|
|
|
130 |
def generate_mcq_web(chapter, topics_text, difficulty, cognitive_skill, num_questions=1):
|
131 |
+
"""Web interface wrapper for MCQ generation"""
|
132 |
+
|
133 |
+
if model is None or tokenizer is None:
|
134 |
+
return """
|
135 |
+
<div style="border: 2px solid #ffc107; border-radius: 10px; padding: 20px; margin: 10px 0; background: #fff3cd;">
|
136 |
+
<h3 style="color: #856404;">⏳ Model Loading</h3>
|
137 |
+
<p>The model is still loading. Please wait a moment and try again.</p>
|
138 |
+
</div>
|
139 |
+
"""
|
140 |
+
|
141 |
+
try:
|
142 |
+
# Parse topics
|
143 |
+
topics_list = [t.strip() for t in topics_text.split(',') if t.strip()]
|
144 |
+
if not topics_list:
|
145 |
+
topics_list = ["General"]
|
146 |
+
|
147 |
+
results = []
|
148 |
+
|
149 |
+
for i in range(min(num_questions, 3)): # Limit to 3 questions max
|
150 |
+
# Generate MCQ
|
151 |
+
raw_response = generate_mcq(chapter, topics_list, difficulty, cognitive_skill)
|
152 |
+
parsed = parse_mcq_response(raw_response)
|
153 |
+
|
154 |
+
if "error" not in parsed:
|
155 |
+
# Format for web display
|
156 |
+
question_html = f"""
|
157 |
+
<div style="border: 2px solid #e1e5e9; border-radius: 10px; padding: 20px; margin: 10px 0; background: #f8f9fa;">
|
158 |
+
<h3 style="color: #2c3e50; margin-top: 0;">📚 Question {i+1}</h3>
|
159 |
+
<p style="font-size: 16px; line-height: 1.6; margin: 15px 0;"><strong>{parsed['question']}</strong></p>
|
160 |
+
|
161 |
+
<div style="margin: 15px 0;">
|
162 |
+
<h4 style="color: #34495e;">Options:</h4>
|
163 |
+
<ul style="list-style: none; padding: 0;">
|
164 |
+
<li style="margin: 8px 0; padding: 8px; background: #ecf0f1; border-radius: 5px;">
|
165 |
+
<strong>(A)</strong> {parsed['options'][0] if len(parsed['options']) > 0 else 'N/A'}
|
166 |
+
</li>
|
167 |
+
<li style="margin: 8px 0; padding: 8px; background: #ecf0f1; border-radius: 5px;">
|
168 |
+
<strong>(B)</strong> {parsed['options'][1] if len(parsed['options']) > 1 else 'N/A'}
|
169 |
+
</li>
|
170 |
+
<li style="margin: 8px 0; padding: 8px; background: #ecf0f1; border-radius: 5px;">
|
171 |
+
<strong>(C)</strong> {parsed['options'][2] if len(parsed['options']) > 2 else 'N/A'}
|
172 |
+
</li>
|
173 |
+
<li style="margin: 8px 0; padding: 8px; background: #ecf0f1; border-radius: 5px;">
|
174 |
+
<strong>(D)</strong> {parsed['options'][3] if len(parsed['options']) > 3 else 'N/A'}
|
175 |
+
</li>
|
176 |
+
</ul>
|
177 |
+
</div>
|
178 |
+
|
179 |
+
<div style="margin-top: 15px; padding: 10px; background: #d5edda; border-radius: 5px; border-left: 4px solid #28a745;">
|
180 |
+
<strong>✅ Correct Answer: {parsed['correct_answer']}</strong>
|
181 |
+
</div>
|
182 |
+
</div>
|
183 |
+
"""
|
184 |
+
results.append(question_html)
|
185 |
+
else:
|
186 |
+
error_html = f"""
|
187 |
+
<div style="border: 2px solid #dc3545; border-radius: 10px; padding: 20px; margin: 10px 0; background: #f8d7da;">
|
188 |
+
<h3 style="color: #721c24;">❌ Error generating question {i+1}</h3>
|
189 |
+
<p>{parsed.get('error', 'Unknown error occurred')}</p>
|
190 |
+
</div>
|
191 |
+
"""
|
192 |
+
results.append(error_html)
|
193 |
+
|
194 |
+
return "".join(results)
|
195 |
+
|
196 |
+
except Exception as e:
|
197 |
+
return f"""
|
198 |
+
<div style="border: 2px solid #dc3545; border-radius: 10px; padding: 20px; margin: 10px 0; background: #f8d7da;">
|
199 |
+
<h3 style="color: #721c24;">❌ System Error</h3>
|
200 |
+
<p>Error: {str(e)}</p>
|
201 |
+
</div>
|
202 |
+
"""
|
203 |
+
|
204 |
+
# Create the interface
|
205 |
+
interface = gr.Interface(
|
206 |
+
fn=generate_mcq_web,
|
207 |
+
inputs=[
|
208 |
+
gr.Textbox(
|
209 |
+
label="📚 Chapter",
|
210 |
+
placeholder="e.g., Applications of Trigonometry, Conic Sections",
|
211 |
+
value="Applications of Trigonometry",
|
212 |
+
info="Enter the mathematics chapter or topic area"
|
213 |
+
),
|
214 |
+
gr.Textbox(
|
215 |
+
label="📝 Topics (comma-separated)",
|
216 |
+
placeholder="e.g., Heights and Distances, Circle, Tangents",
|
217 |
+
value="Heights and Distances",
|
218 |
+
info="Enter specific topics within the chapter, separated by commas"
|
219 |
+
),
|
220 |
+
gr.Dropdown(
|
221 |
+
choices=["easy", "medium", "hard"],
|
222 |
+
label="⚡ Difficulty Level",
|
223 |
+
value="medium",
|
224 |
+
info="Select the difficulty level for the questions"
|
225 |
+
),
|
226 |
+
gr.Dropdown(
|
227 |
+
choices=["recall", "direct_application", "pattern_recognition", "strategic_reasoning", "trap_aware"],
|
228 |
+
label="🧠 Cognitive Skill",
|
229 |
+
value="direct_application",
|
230 |
+
info="Select the type of thinking skill required"
|
231 |
+
),
|
232 |
+
gr.Slider(
|
233 |
+
minimum=1,
|
234 |
+
maximum=3,
|
235 |
+
step=1,
|
236 |
+
label="🔢 Number of Questions",
|
237 |
+
value=1,
|
238 |
+
info="How many questions to generate (max 3)"
|
239 |
+
)
|
240 |
+
],
|
241 |
+
outputs=gr.HTML(label="Generated MCQ(s)"),
|
242 |
+
|
243 |
+
title="🧮 Mathematics MCQ Generator",
|
244 |
+
description="""
|
245 |
+
Generate high-quality mathematics multiple choice questions using AI.
|
246 |
+
This model has been fine-tuned specifically for educational content creation.
|
247 |
+
|
248 |
+
**Note**: Model loading may take a few minutes on first startup.
|
249 |
+
""",
|
250 |
+
|
251 |
+
article="""
|
252 |
+
### 🔬 About This Model
|
253 |
+
|
254 |
+
This MCQ generator is powered by a fine-tuned version of DeepSeek-Math-7B, specifically adapted for mathematics education.
|
255 |
+
|
256 |
+
### 💡 Tips for Best Results:
|
257 |
+
- Be specific with chapter and topic names
|
258 |
+
- Try different cognitive skill levels for variety
|
259 |
+
- Start with 1 question to test, then generate more
|
260 |
+
|
261 |
+
### 🤝 Collaboration
|
262 |
+
This is part of a collaborative project to create specialized educational AI tools.
|
263 |
+
""",
|
264 |
+
|
265 |
+
theme=gr.themes.Soft(primary_hue="blue", secondary_hue="purple"),
|
266 |
+
|
267 |
+
examples=[
|
268 |
+
["Applications of Trigonometry", "Heights and Distances", "easy", "recall", 1],
|
269 |
+
["Conic Sections", "Circle", "medium", "pattern_recognition", 1],
|
270 |
+
["Applications of Trigonometry", "Angle of Elevation and Depression", "hard", "strategic_reasoning", 1]
|
271 |
+
]
|
272 |
+
)
|
273 |
+
|
274 |
+
# Initialize model loading
|
275 |
+
print("🚀 Starting model loading...")
|
276 |
+
model_loaded = load_model()
|
277 |
|
278 |
+
if model_loaded:
|
279 |
+
print("✅ Ready to generate MCQs!")
|
280 |
+
else:
|
281 |
+
print("❌ Model loading failed. The interface may not work properly.")
|
282 |
|
283 |
+
# Launch the interface
|
284 |
if __name__ == "__main__":
|
285 |
interface.launch()
|