"
}}
]
Make questions test understanding, not just memorization. Vary the question types."""
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
payload = {
"model": "llama-3.3-70b-versatile",
"messages": [{"role": "user", "content": prompt}],
"max_tokens": 3000,
"temperature": 0.4,
}
r = requests.post("https://api.groq.com/openai/v1/chat/completions",
headers=headers, json=payload, timeout=45)
r.raise_for_status()
raw = r.json()["choices"][0]["message"]["content"]
raw = re.sub(r"```json|```", "", raw).strip()
return json.loads(raw)
# โโโ Sidebar โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
with st.sidebar:
st.markdown("## ๐ง Quiz Generator")
st.markdown("AI-powered assessment builder
", unsafe_allow_html=True)
st.markdown("---")
env_key = os.environ.get("GROQ_API_KEY", "")
api_key = env_key if env_key else st.text_input("๐ Groq API Key", type="password", placeholder="gsk_...")
if not env_key and not api_key:
st.caption("Free key โ [console.groq.com](https://console.groq.com)")
st.markdown("---")
st.markdown("Quiz Settings
", unsafe_allow_html=True)
num_questions = st.slider("Number of Questions", min_value=3, max_value=15, value=5)
difficulty = st.selectbox("Difficulty", ["Mixed", "Easy", "Medium", "Hard"])
topic_focus = st.text_input("Topic Focus (optional)", placeholder="e.g. neural networks, photosynthesis")
st.markdown("---")
if st.session_state.questions:
total = len(st.session_state.questions)
answered = len(st.session_state.answers)
st.markdown(f"""
Progress: {answered}/{total} answered
""", unsafe_allow_html=True)
if st.button("๐ Reset Quiz", use_container_width=True):
st.session_state.answers = {}
st.session_state.revealed = {}
st.session_state.quiz_submitted = False
st.rerun()
if st.button("๐๏ธ Clear & New Quiz", use_container_width=True):
st.session_state.questions = []
st.session_state.answers = {}
st.session_state.revealed = {}
st.session_state.quiz_submitted = False
st.session_state.doc_title = ""
st.rerun()
# โโโ Main UI โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
st.markdown("""
๐ง AI Knowledge Quiz Generator
Upload any PDF โ textbook, training doc, research paper โ and AI generates a complete multiple choice quiz with explanations
""", unsafe_allow_html=True)
if not api_key:
st.warning("๐ Add your Groq API key to get started.")
st.stop()
# Upload section (only if no quiz yet)
if not st.session_state.questions:
st.markdown("Upload a Document
", unsafe_allow_html=True)
uploaded_file = st.file_uploader("Upload PDF", type=["pdf"], label_visibility="collapsed")
if uploaded_file:
st.markdown(f"๐ {uploaded_file.name} ยท {round(uploaded_file.size/1024,1)}KB
", unsafe_allow_html=True)
if st.button(f"๐ง Generate {num_questions} Questions", type="primary", use_container_width=True):
with st.spinner("Reading document and generating quiz..."):
try:
pdf_text = extract_pdf_text(uploaded_file.read())
questions = generate_quiz(pdf_text, num_questions, difficulty, topic_focus, api_key)
st.session_state.questions = questions
st.session_state.doc_title = uploaded_file.name.replace(".pdf", "")
st.session_state.answers = {}
st.session_state.revealed = {}
st.session_state.quiz_submitted = False
st.rerun()
except json.JSONDecodeError:
st.error("โ AI returned unexpected format. Try again.")
except Exception as e:
st.error(f"โ Error: {str(e)}")
else:
st.markdown("""
๐
Upload a PDF to generate your quiz.
Works with textbooks, training docs, research papers, manuals.
""", unsafe_allow_html=True)
# โโโ Quiz Display โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
if st.session_state.questions:
questions = st.session_state.questions
total = len(questions)
# Stats bar
answered = len(st.session_state.answers)
correct_so_far = sum(
1 for i, q in enumerate(questions)
if st.session_state.answers.get(i) == q.get("correct")
)
easy_c = sum(1 for q in questions if q.get("difficulty") == "Easy")
med_c = sum(1 for q in questions if q.get("difficulty") == "Medium")
hard_c = sum(1 for q in questions if q.get("difficulty") == "Hard")
st.markdown(f"### ๐ Quiz: {st.session_state.doc_title}")
st.markdown(f"""
""", unsafe_allow_html=True)
# โโ Questions โโ
for i, q in enumerate(questions):
diff = q.get("difficulty", "Medium")
diff_class = diff.lower()
user_answer = st.session_state.answers.get(i)
is_revealed = st.session_state.revealed.get(i, False)
is_submitted = st.session_state.quiz_submitted
with st.container():
st.markdown(f"""
Question {i+1} of {total} ยท {q.get("topic","")}
{diff}
{q["question"]}
""", unsafe_allow_html=True)
options = q.get("options", {})
correct = q.get("correct", "A")
cols = st.columns(2)
for j, (key, val) in enumerate(options.items()):
col = cols[j % 2]
with col:
show_result = is_submitted or is_revealed
if show_result:
if key == correct:
btn_style = "option-correct"
elif key == user_answer and user_answer != correct:
btn_style = "option-wrong"
else:
btn_style = ""
st.markdown(f"{key}. {val}
", unsafe_allow_html=True)
else:
if st.button(f"{key}. {val}", key=f"q{i}_opt_{key}", use_container_width=True):
st.session_state.answers[i] = key
st.rerun()
# Show selected answer indicator
if user_answer and not is_submitted and not is_revealed:
st.caption(f"โ
Selected: **{user_answer}** โ {options.get(user_answer, '')}")
# Reveal/explanation
if is_submitted or is_revealed:
exp = q.get("explanation", "")
is_correct = user_answer == correct
result_text = "โ
Correct!" if is_correct else f"โ Incorrect. Correct answer: **{correct}. {options.get(correct,'')}**"
st.markdown(f"""
""", unsafe_allow_html=True)
elif user_answer:
if st.button(f"๐ก Reveal Answer", key=f"reveal_{i}", use_container_width=False):
st.session_state.revealed[i] = True
st.rerun()
st.markdown("", unsafe_allow_html=True)
st.markdown("---")
# Submit or Score
if not st.session_state.quiz_submitted:
col_sub, col_clear = st.columns([3, 1])
with col_sub:
if st.button("๐ Submit Quiz & See Results", type="primary", use_container_width=True,
disabled=answered < total):
st.session_state.quiz_submitted = True
st.rerun()
if answered < total:
st.caption(f"Answer all {total} questions to submit. ({total - answered} remaining)")
else:
score = sum(1 for i, q in enumerate(questions)
if st.session_state.answers.get(i) == q.get("correct"))
pct = round(score / total * 100)
grade = "๐ Excellent!" if pct >= 90 else "โ
Good Job!" if pct >= 70 else "๐ Keep Studying!" if pct >= 50 else "๐ช Needs Work"
color = "#22c55e" if pct >= 70 else "#f59e0b" if pct >= 50 else "#ef4444"
st.markdown(f"""
{pct}%
{score} out of {total} correct ยท {grade}
""", unsafe_allow_html=True)