ppalit's picture
Update app.py
e52177c verified
import gradio as gr
from transformers import pipeline, AutoTokenizer, AutoModelForSeq2SeqLM
TITLE = "Bloom’s Taxonomy Helper (Classify + Generate + Rewrite)"
# ------------------ Bloom labels ------------------
LABELS = ["Remember", "Understand", "Apply", "Analyze", "Evaluate", "Create"]
# ------------------ Generation templates by Bloom ------------------
PROMPT_TEMPLATES = {
"Remember": (
"Write {n} distinct factual recall questions for college students.\n"
"Topic: {topic}\n"
"Cognitive focus: Remember (recognize/recall facts and terms).\n"
"Style: Use 'What', 'When', 'Define', or 'List'.\n"
"Do not include answers. Number each on its own line."
),
"Understand": (
"Write {n} distinct comprehension questions for college students.\n"
"Topic: {topic}\n"
"Cognitive focus: Understand (explain, summarize, interpret).\n"
"Style: Use 'Explain', 'Summarize', 'Describe', or 'Give an example of'.\n"
"Do not include answers. Number each on its own line."
),
"Apply": (
"Write {n} distinct application questions for college students.\n"
"Topic: {topic}\n"
"Cognitive focus: Apply (use procedures, compute, demonstrate use).\n"
"Style: Ask students to calculate, implement, or solve with concrete data.\n"
"Avoid 'define' or 'explain' prompts. Do not include answers. Number each on its own line."
),
"Analyze": (
"Write {n} distinct analysis questions for college students.\n"
"Topic: {topic}\n"
"Cognitive focus: Analyze (compare/contrast, break down relationships, cause–effect).\n"
"Style: Use 'Compare', 'Differentiate', 'Explain why', 'Break down', or 'Trace'.\n"
"Do not include answers. Number each on its own line."
),
"Evaluate": (
"Write {n} distinct evaluation questions for college students.\n"
"Topic: {topic}\n"
"Cognitive focus: Evaluate (judge, critique, justify with criteria and evidence).\n"
"Style: Use 'Argue', 'Defend', 'Critique', 'Which is better and why', or 'Assess'.\n"
"Do not include answers. Number each on its own line."
),
"Create": (
"Write {n} distinct creation/synthesis tasks for college students.\n"
"Topic: {topic}\n"
"Cognitive focus: Create (design, plan, invent, propose, produce a novel artifact).\n"
"Style: Use 'Design', 'Propose', 'Develop', 'Compose', or 'Build'. Include realistic constraints.\n"
"Do not include answers. Number each on its own line."
),
}
# ------------------ Pipelines ------------------
# Zero-shot classifier (no training)
clf = pipeline("zero-shot-classification", model="typeform/distilbert-base-uncased-mnli")
# Higher-quality generator (CPU-friendly but better than *small*)
GEN_MODEL = "google/flan-t5-base"
gen_tok = AutoTokenizer.from_pretrained(GEN_MODEL)
gen_mdl = AutoModelForSeq2SeqLM.from_pretrained(GEN_MODEL)
gen = pipeline("text2text-generation", model=gen_mdl, tokenizer=gen_tok)
# ------------------ Helpers ------------------
def clean_numbering(text: str, n: int) -> str:
"""Force clean 1..n numbering, each on its own line."""
lines = [ln.strip(" -*\t") for ln in text.splitlines() if ln.strip()]
# If model returned a paragraph, split by sentence end
if len(lines) == 1 and "1." not in lines[0]:
import re
parts = [p.strip() for p in re.split(r"(?<=[.?!])\s+", lines[0]) if p.strip()]
lines = parts
lines = [ln for ln in lines if any(c.isalpha() for c in ln)]
lines = lines[:max(1, n)]
return "\n".join(f"{i+1}. {ln.lstrip('0123456789. ').strip()}" for i, ln in enumerate(lines))
# ------------------ Functions ------------------
def classify_bloom(question: str):
question = (question or "").strip()
if not question:
return "", ""
res = clf(question, LABELS)
labels = res["labels"]
scores = [round(float(s), 3) for s in res["scores"]]
top = labels[0] if labels else ""
table = "\n".join(f"{l}: {s}" for l, s in zip(labels, scores))
return top, table
def generate_questions(topic: str, level: str, n: int, creativity: float):
topic = (topic or "").strip()
if not topic:
return "Please enter a topic."
template = PROMPT_TEMPLATES.get(level, PROMPT_TEMPLATES["Understand"])
prompt = template.format(n=int(n), topic=topic)
out = gen(
prompt,
max_new_tokens=180,
do_sample=(creativity > 0.01),
temperature=max(0.01, min(1.2, creativity)),
top_p=0.9,
num_beams=1,
)[0]["generated_text"]
return clean_numbering(out, int(n))
def rewrite_level(question: str, target_level: str):
question = (question or "").strip()
if not question:
return "Paste a question to rewrite."
# Leverage the template for target level to steer rewriting
template = PROMPT_TEMPLATES.get(target_level, PROMPT_TEMPLATES["Understand"])
prompt = (
f"{template}\n\n"
f"Transform the following single question to match the level above. Keep it concise and do not include the answer.\n"
f"Original: {question}\n"
f"Return exactly 1 numbered question."
)
out = gen(prompt, max_new_tokens=100, do_sample=False)[0]["generated_text"]
return clean_numbering(out, 1)
# ------------------ UI ------------------
with gr.Blocks(title=TITLE) as demo:
gr.Markdown(f"# {TITLE}")
gr.Markdown(
"Classify questions by Bloom level, generate new questions aligned to a level, "
"and rewrite a question to a different level. Runs fully on open models."
)
with gr.Tab("Classify"):
q = gr.Textbox(
label="Enter a question",
lines=4,
placeholder="e.g., Explain why randomized controlled trials reduce bias."
)
top = gr.Textbox(label="Predicted Bloom level", interactive=False)
scores = gr.Textbox(label="All scores", interactive=False)
gr.Button("Classify").click(classify_bloom, [q], [top, scores])
with gr.Tab("Generate"):
with gr.Row():
topic = gr.Textbox(label="Topic", value="binary numbers in computer science")
level = gr.Dropdown(LABELS, value="Apply", label="Bloom level")
with gr.Row():
n = gr.Slider(1, 10, value=5, step=1, label="How many questions")
creativity = gr.Slider(0.0, 1.2, value=0.6, step=0.1, label="Creativity (temperature)")
out = gr.Textbox(label="Generated questions", lines=12)
gr.Button("Generate").click(generate_questions, [topic, level, n, creativity], out)
with gr.Tab("Rewrite"):
q2 = gr.Textbox(label="Original question", lines=4, value="Define binary number.")
target = gr.Dropdown(LABELS, value="Analyze", label="Target Bloom level")
out2 = gr.Textbox(label="Rewritten question", lines=4)
gr.Button("Rewrite").click(rewrite_level, [q2, target], out2)
demo.launch()