import spaces import torch import gradio as gr from transformers import AutoTokenizer, AutoModelForCausalLM # ============================================================ # Model # ============================================================ MODEL_NAME = "ma4389/LFM2-DPO" # ============================================================ # System Prompt # ============================================================ SYSTEM_PROMPT = """ You are an expert educational AI assistant. Your task is to generate high-quality educational questions ONLY from the paragraph provided by the user. Rules: - Use ONLY the provided paragraph. - Never use outside knowledge. - Never hallucinate. - Never invent facts. - If the paragraph does not contain enough information, generate only the questions that are supported. - Follow the user's requested format exactly. - Do not include explanations unless explicitly requested. - Return only the generated questions. """ # ============================================================ # Prompts # ============================================================ PROMPTS = { "MCQ": """ Paragraph: {context} Generate EXACTLY {num} multiple-choice questions if the paragraph contains enough information. Strict Rules: - Use ONLY the provided paragraph. - Never use outside knowledge. - Never hallucinate. - Never invent facts. - Never invent examples. - Every question must assess a DIFFERENT concept. - Never repeat questions. - Do not copy entire sentences from the paragraph. - Questions should test understanding. - Generate EXACTLY four options. - There must be EXACTLY one correct answer. - Distractors must be realistic. - Vary the correct answer naturally between A, B, C and D. - Do NOT explain the answers. - Do NOT stop after generating one question. Output Format: 1. Question? A) ... B) ... C) ... D) ... Answer: B 2. Question? A) ... B) ... C) ... D) ... Answer: D 3. Question? A) ... B) ... C) ... D) ... Answer: A ... Continue until EXACTLY {num} questions have been generated. You have NOT finished until EXACTLY {num} questions are written. Return ONLY the questions. """, "True / False": """ Paragraph: {context} Generate EXACTLY {num} True/False questions if the paragraph contains enough information. Strict Rules: - Use ONLY the provided paragraph. - Never use outside knowledge. - Never hallucinate. - Never invent facts. - Every statement must assess a DIFFERENT concept. - Never repeat ideas. - Mix True and False naturally. - False statements should modify ONLY one important fact. - Avoid obviously false statements. - End every statement with (T/F). - Do NOT explain the answers. - Do NOT stop after generating one question. Output Format: 1. Statement. (T/F) Answer: True 2. Statement. (T/F) Answer: False 3. Statement. (T/F) Answer: True ... Continue until EXACTLY {num} questions have been generated. You have NOT finished until EXACTLY {num} questions are written. Return ONLY the questions. """, "Essay": """ Paragraph: {context} Generate EXACTLY {num} essay questions if the paragraph contains enough information. Strict Rules: - Use ONLY the provided paragraph. - Never use outside knowledge. - Never hallucinate. - Never invent facts. - Every question must assess a DIFFERENT concept. - Never repeat questions. - Answers must contain ONLY information from the paragraph. - Never invent information. - Each answer should contain 3–6 complete sentences. - Keep answers concise and educational. - Do NOT stop after generating one question. Output Format: 1. Question? Answer: ... 2. Question? Answer: ... 3. Question? Answer: ... Continue until EXACTLY {num} questions have been generated. You have NOT finished until EXACTLY {num} questions are written. Return ONLY the questions. """ } # ============================================================ # Lazy Loading # ============================================================ model = None tokenizer = None def load_model(): global model, tokenizer if model is None: print("Loading model...") tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME) model = AutoModelForCausalLM.from_pretrained( MODEL_NAME, torch_dtype=torch.float16, device_map="auto", ) model.eval() # ============================================================ # Generation # ============================================================ @spaces.GPU def ask(prompt): load_model() messages = [ { "role": "system", "content": SYSTEM_PROMPT, }, { "role": "user", "content": prompt, }, ] inputs = tokenizer.apply_chat_template( messages, tokenize=True, add_generation_prompt=True, return_dict=True, return_tensors="pt", ) inputs = { k: v.to(model.device) for k, v in inputs.items() } with torch.no_grad(): outputs = model.generate( **inputs, max_new_tokens=1200, temperature=0.6, top_p=0.95, top_k=50, do_sample=True, repetition_penalty=1.15, no_repeat_ngram_size=4, pad_token_id=tokenizer.eos_token_id, eos_token_id=tokenizer.eos_token_id, ) response = tokenizer.decode( outputs[0][inputs["input_ids"].shape[-1]:], skip_special_tokens=True, ) return response.strip() # ============================================================ # Main Function # ============================================================ def generate(paragraph, qtype, num): paragraph = paragraph.strip() if not paragraph: return "Please enter a paragraph." prompt = PROMPTS[qtype].format( context=paragraph, num=num, ) return ask(prompt) # ============================================================ # Gradio Interface # ============================================================ with gr.Blocks( title="📚 AI Question Generator", theme=gr.themes.Soft(), ) as demo: gr.Markdown( """ # 📚 AI Question Generator Generate **Multiple Choice**, **True/False**, and **Essay** questions from any paragraph using a fine-tuned **LFM2-DPO** language model. ### Features - ✅ Multiple Choice Questions - ✅ True / False Questions - ✅ Essay Questions - ✅ Grounded only in the provided paragraph - ✅ Covers different concepts with minimal repetition """ ) with gr.Row(): with gr.Column(scale=1): paragraph = gr.Textbox( label="Paragraph", lines=16, placeholder="Paste your paragraph here...", ) question_type = gr.Radio( choices=[ "MCQ", "True / False", "Essay", ], value="MCQ", label="Question Type", ) number = gr.Slider( minimum=1, maximum=10, value=5, step=1, label="Number of Questions", ) generate_btn = gr.Button( "Generate Questions", variant="primary", ) clear_btn = gr.Button("Clear") with gr.Column(scale=1): output = gr.Textbox( label="Generated Questions", lines=28 ) generate_btn.click( fn=generate, inputs=[ paragraph, question_type, number, ], outputs=output, ) clear_btn.click( lambda: ("", "MCQ", 5, ""), outputs=[ paragraph, question_type, number, output, ], ) gr.Markdown( """ --- ### Notes - The model uses **only the supplied paragraph**. - It does **not** use external knowledge. - Each generated question is designed to assess a different concept whenever possible. """ ) # ============================================================ # Launch # ============================================================ if __name__ == "__main__": demo.queue(max_size=20) demo.launch()