| import spaces
|
| import torch
|
| import gradio as gr
|
| from transformers import AutoTokenizer, AutoModelForCausalLM
|
|
|
|
|
|
|
|
|
|
|
| MODEL_NAME = "ma4389/LFM2-DPO"
|
|
|
|
|
|
|
|
|
|
|
| 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 = {
|
|
|
| "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.
|
| """
|
| }
|
|
|
|
|
|
|
|
|
|
|
| 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()
|
|
|
|
|
|
|
|
|
|
|
|
|
| @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()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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)
|
|
|
|
|
|
|
|
|
| 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.
|
| """
|
| )
|
|
|
|
|
|
|
|
|
|
|
|
|
| if __name__ == "__main__":
|
| demo.queue(max_size=20)
|
| demo.launch() |