import streamlit as st
import PyPDF2
import json
from xai_sdk import Client
from xai_sdk.chat import user, system
from langchain_text_splitters import RecursiveCharacterTextSplitter
from dotenv import load_dotenv
import os
import random
load_dotenv() # .env file se API key load kar sakte ho (optional)
st.set_page_config(page_title="PDF เคธเฅ Pro Quiz Generator", page_icon="๐ง ", layout="wide")
st.title("๐ PDF เคธเฅ High-Quality Quiz Generator (50 เคคเค Questions)")
st.markdown("Upload PDF โ Grok se accurate MCQs banaye โ Quiz attempt karo aur score dekho!")
# Sidebar
with st.sidebar:
st.header("Settings")
api_key = st.text_input("xAI Grok API Key", type="password", value=os.getenv("XAI_API_KEY", ""))
model = st.selectbox("Grok Model", ["grok-beta", "grok-4"], index=0) # Latest models check kar lena docs se
num_questions = st.slider("Kitne questions chahiye?", 5, 50, 15, step=5)
difficulty = st.selectbox("Difficulty Level", ["Easy", "Medium", "Hard", "Mixed"], index=2)
if not api_key:
st.warning("API key daalo sidebar mein. https://console.x.ai se milega.")
st.stop()
# PDF Upload
uploaded_file = st.file_uploader("PDF file daalo (.pdf)", type="pdf")
if uploaded_file and st.button("๐ Quiz Generate Karo", type="primary"):
if not api_key:
st.error("API key missing hai!")
st.stop()
with st.spinner("PDF padh raha hoon + questions bana raha hoon... (1-2 min lagega)"):
# PDF โ Text
try:
reader = PyPDF2.PdfReader(uploaded_file)
full_text = ""
for page in reader.pages:
text = page.extract_text() or ""
full_text += text + "\n\n"
if len(full_text.strip()) < 200:
st.error("PDF mein readable text bahut kam hai (scanned image PDF ho sakta hai). OCR tool use karo pehle.")
st.stop()
except Exception as e:
st.error(f"PDF padhne mein problem: {e}")
st.stop()
# Text ko chunks mein baanto (context limit ke liye)
splitter = RecursiveCharacterTextSplitter(
chunk_size=12000, # ~3000-4000 tokens safe
chunk_overlap=1500,
length_function=len
)
chunks = splitter.split_text(full_text)
st.info(f"PDF ko {len(chunks)} chunks mein baanta gaya.")
# Grok client
client = Client(api_key=api_key)
all_mcqs = []
questions_per_chunk = max(3, num_questions // len(chunks) + 1)
progress_bar = st.progress(0)
status_text = st.empty()
for i, chunk in enumerate(chunks):
status_text.text(f"Processing chunk {i+1}/{len(chunks)}...")
prompt = f"""
You are an expert quiz creator and educator. From the given content, create exactly **{questions_per_chunk}** high-quality MCQs.
Rules (strictly follow):
- Questions factual, clear, and based ONLY on the provided text.
- Each has exactly 4 options (A, B, C, D).
- Only ONE correct answer.
- Options should be plausible but only one 100% correct.
- Vary difficulty: {difficulty} level (make some tricky if medium/hard).
- No duplicates with previous questions.
- Output **ONLY valid JSON array**, nothing else. No explanation, no markdown.
Format:
[
{{
"question": "Question text?",
"A": "Option A here",
"B": "Option B here",
"C": "Option C here",
"D": "Option D here",
"correct": "A", // only A/B/C/D
"explanation": "Short explanation why this is correct (1-2 sentences)"
}}
]
Content:
{chunk}
"""
try:
chat = client.chat.create(model=model)
chat.append(system("You are a precise quiz generator. Respond with ONLY the requested JSON array. No other text."))
chat.append(user(prompt))
response = chat.sample()
content = response.content.strip()
# JSON try parse
mcqs = json.loads(content)
if isinstance(mcqs, list):
all_mcqs.extend(mcqs)
except Exception as e:
st.warning(f"Chunk {i+1} mein error: {e}. Skipping...")
progress_bar.progress((i + 1) / len(chunks))
status_text.text("Done!")
# Limit to desired number
all_mcqs = all_mcqs[:num_questions]
if not all_mcqs:
st.error("Koi bhi question generate nahi hua. Prompt ya API check karo.")
st.stop()
# Shuffle questions
random.shuffle(all_mcqs)
st.session_state.mcqs = all_mcqs
st.session_state.user_answers = [None] * len(all_mcqs)
st.session_state.score = 0
st.success(f"**{len(all_mcqs)} high-quality questions ready!** Ab quiz attempt karo โ")
# ---------------- Quiz Mode ----------------
if 'mcqs' in st.session_state and st.session_state.mcqs:
st.subheader("๐ง Quiz Time โ Attempt Karo!")
score = 0
for idx, q in enumerate(st.session_state.mcqs):
st.markdown(f"**Q{idx+1}:** {q.get('question', 'N/A')}")
options = ["A", "B", "C", "D"]
choice = st.radio(
"Choose answer:",
options,
format_func=lambda x: f"{x}) {q.get(x, 'N/A')}",
key=f"q_{idx}",
index=None if st.session_state.user_answers[idx] is None else options.index(st.session_state.user_answers[idx])
)
if choice:
st.session_state.user_answers[idx] = choice
# Show explanation only after attempt (optional toggle)
if st.toggle("Explanation dikhao", key=f"exp_{idx}", value=False):
st.info(q.get("explanation", "No explanation available."))
if st.button("๐ Submit Quiz & See Score", type="primary"):
score = sum(1 for i, ans in enumerate(st.session_state.user_answers) if ans == st.session_state.mcqs[i].get("correct"))
st.balloons()
st.success(f"**Your Score: {score} / {len(st.session_state.mcqs)}** ({score/len(st.session_state.mcqs)*100:.1f}%)")
# Show correct answers
with st.expander("Detailed Answers & Explanations"):
for i, q in enumerate(st.session_state.mcqs):
user_ans = st.session_state.user_answers[i]
correct = q["correct"]
st.write(f"**Q{i+1}:** {q['question']}")
st.write(f"Your answer: **{user_ans or 'Not attempted'}**")
st.write(f"Correct: **{correct}** โ {q.get('A','')} / {q.get('B','')} / {q.get('C','')} / {q.get('D','')}")
st.info(f"Explanation: {q.get('explanation','')}")
st.markdown("---")
# Download JSON
st.download_button(
"Download Quiz (JSON)",
json.dumps(st.session_state.mcqs, ensure_ascii=False, indent=2),
file_name="my_generated_quiz.json",
mime="application/json"
)
st.markdown("---")
st.caption("Made with โค๏ธ using Grok API + Streamlit | Questions improve karne ke liye prompt tweak kar sakte ho")