LFM / app.py
ma4389's picture
Update app.py
e45239e verified
Raw
History Blame Contribute Delete
8.78 kB
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()