import os import torch import gradio as gr from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig from peft import PeftModel import re # Model configuration BASE_MODEL = "deepseek-ai/deepseek-math-7b-instruct" REPO_ID = "danxh/math-mcq-generator-v1" # Global variables for model and tokenizer model = None tokenizer = None def load_model(): """Load the fine-tuned model with error handling""" global model, tokenizer try: print("🔄 Loading model and tokenizer...") # Simplified loading for Hugging Face Spaces bnb_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_use_double_quant=True, bnb_4bit_quant_type="nf4", bnb_4bit_compute_dtype=torch.float16 # Changed to float16 for better compatibility ) # Load base model base_model = AutoModelForCausalLM.from_pretrained( BASE_MODEL, quantization_config=bnb_config, device_map="auto", torch_dtype=torch.float16, trust_remote_code=True ) # Load LoRA adapter model = PeftModel.from_pretrained(base_model, REPO_ID) # Load tokenizer tokenizer = AutoTokenizer.from_pretrained(REPO_ID) if tokenizer.pad_token is None: tokenizer.pad_token = tokenizer.eos_token print("✅ Model loaded successfully!") return True except Exception as e: print(f"❌ Error loading model: {str(e)}") return False def generate_mcq(chapter, topics, difficulty="medium", cognitive_skill="direct_application"): """Generate MCQ using the fine-tuned model""" if model is None or tokenizer is None: return "❌ Model not loaded. Please wait for initialization." try: input_text = f"chapter: {chapter}\ntopics: {topics}\nDifficulty: {difficulty}\nCognitive Skill: {cognitive_skill}" prompt = f"""### Instruction: Generate a math MCQ similar in style to the provided examples. ### Input: {input_text} ### Response: """ inputs = tokenizer(prompt, return_tensors="pt", truncation=True, max_length=512) inputs = {k: v.to(model.device) for k, v in inputs.items()} with torch.no_grad(): outputs = model.generate( **inputs, max_new_tokens=300, temperature=0.7, do_sample=True, pad_token_id=tokenizer.eos_token_id, eos_token_id=tokenizer.eos_token_id, repetition_penalty=1.1 ) generated_text = tokenizer.decode(outputs[0], skip_special_tokens=True) response_start = generated_text.find("### Response:") + len("### Response:") response = generated_text[response_start:].strip() return response except Exception as e: return f"❌ Error generating MCQ: {str(e)}" def parse_mcq_response(response): """Parse the model response""" try: question_match = re.search(r'Question:\s*(.*?)(?=\nOptions:|Options:)', response, re.DOTALL) question = question_match.group(1).strip() if question_match else "Question not found" options_match = re.search(r'Options:\s*(.*?)(?=\nAnswer:|Answer:)', response, re.DOTALL) if options_match: options_text = options_match.group(1).strip() option_pattern = r'\([A-D]\)\s*([^(]*?)(?=\s*\([A-D]\)|$)' options = [] for match in re.finditer(option_pattern, options_text): option_text = match.group(1).strip() if option_text: options.append(option_text) else: options = ["Options not found"] answer_match = re.search(r'Answer:\s*([A-D])', response) answer = answer_match.group(1) if answer_match else "Answer not found" return { "question": question, "options": options, "correct_answer": answer } except Exception as e: return { "question": "Parsing error", "options": ["Error parsing options"], "correct_answer": "N/A", "error": str(e) } def generate_mcq_web(chapter, topics_text, difficulty, cognitive_skill, num_questions=1): """Web interface wrapper for MCQ generation""" if model is None or tokenizer is None: return """
The model is still loading. Please wait a moment and try again.
{parsed['question']}
{parsed.get('error', 'Unknown error occurred')}
Error: {str(e)}