retvq commited on
Commit
3be7efd
Β·
verified Β·
1 Parent(s): 9e84cc4

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +82 -191
app.py CHANGED
@@ -5,243 +5,134 @@ from langchain_text_splitters import RecursiveCharacterTextSplitter
5
  from langchain_community.embeddings.fastembed import FastEmbedEmbeddings
6
  from langchain_community.vectorstores import FAISS
7
  from huggingface_hub import InferenceClient
 
8
 
9
- # --- 1. Configuration & Setup ---
10
  HF_TOKEN = os.environ.get("HF_TOKEN", "")
11
 
12
- # Use InferenceClient
 
 
 
13
  client = InferenceClient(token=HF_TOKEN)
14
 
15
- # --- 2. Logic: Paper Generation ---
16
- def generate_question_paper(pdf_file, difficulty, num_questions, progress=gr.Progress()):
17
  if not pdf_file:
18
- raise gr.Error("Please upload a PDF file first.")
19
 
20
  if not HF_TOKEN:
21
- raise gr.Error("HF_TOKEN is missing. Please check your Space settings.")
22
-
23
  try:
24
- # Step 1: Loading
25
- progress(0.1, desc="Reading PDF...")
26
  loader = PyPDFLoader(pdf_file.name)
27
  pages = loader.load()
28
 
29
  if not pages:
30
- raise gr.Error("The PDF appears to be empty or unreadable.")
31
-
32
- # Step 2: Splitting
33
- progress(0.3, desc="Analyzing text structure...")
34
  text_splitter = RecursiveCharacterTextSplitter(
35
  chunk_size=1000,
36
  chunk_overlap=100
37
  )
38
  chunks = text_splitter.split_documents(pages)
39
-
40
- # Step 3: Embedding
41
- progress(0.5, desc="Building semantic index...")
42
  embeddings = FastEmbedEmbeddings()
43
  vector_store = FAISS.from_documents(chunks, embeddings)
44
-
45
- # Step 4: Retrieval
46
- progress(0.7, desc="Extracting key concepts...")
47
  retriever = vector_store.as_retriever(search_kwargs={"k": 7})
48
- # We broaden the search query to get a general overview
49
- context_docs = retriever.invoke("Chapter summary, definitions, and key concepts")
50
  context_text = "\n\n".join([doc.page_content for doc in context_docs])
51
-
52
- # Step 5: Generation
53
- progress(0.8, desc="Drafting questions...")
54
 
55
- prompt = f"""
56
- Role: Academic Examiner.
57
- Task: Create a structured examination paper.
58
- Source Material: Use the context below.
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
- CONTEXT:
61
- {context_text}
62
-
63
- REQUIREMENTS:
64
- - Difficulty: {difficulty}
65
- - Total Questions: {num_questions}
66
- - Structure:
67
- 1. HEADER (University/Course Name Placeholder, Duration: 1 Hour)
68
- 2. SECTION A: Multiple Choice ({int(num_questions * 0.4)} questions)
69
- 3. SECTION B: Short Answer ({int(num_questions * 0.4)} questions)
70
- 4. SECTION C: Essay/Long Answer ({int(num_questions * 0.2)} questions)
71
- 5. ANSWER KEY (at the very bottom)
72
-
73
- OUTPUT FORMAT:
74
- Return ONLY the exam paper in clean Markdown. Use bold headers.
75
- """
76
-
77
  messages = [{"role": "user", "content": prompt}]
78
 
79
- partial_response = ""
80
  for message in client.chat_completion(
81
  messages=messages,
82
  model="meta-llama/Llama-3.2-3B-Instruct",
83
- max_tokens=2500,
84
- temperature=0.6,
85
  stream=True,
86
  ):
87
- if hasattr(message.choices[0].delta, 'content'):
88
- content = message.choices[0].delta.content
89
- if content:
90
- partial_response += content
91
- yield partial_response
92
-
93
- progress(1.0, desc="Finalizing formatting...")
94
- return partial_response
95
 
96
  except Exception as e:
97
- raise gr.Error(f"Generation failed: {str(e)}")
98
-
99
- # --- 3. UI Layout (MVP Styling) ---
100
-
101
- # CSS Injection for SaaS/MVP Look
102
- css = """
103
- @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;800&display=swap');
104
-
105
- .gradio-container {
106
- font-family: 'Inter', sans-serif !important;
107
- background-color: #f3f4f6 !important;
108
- }
109
-
110
- .header-container {
111
- text-align: center;
112
- padding: 3rem 1rem;
113
- background: white;
114
- border-bottom: 1px solid #e5e7eb;
115
- margin-bottom: 2rem;
116
- }
117
-
118
- .logo-text {
119
- font-size: 2rem;
120
- font-weight: 800;
121
- background: linear-gradient(135deg, #4f46e5 0%, #ec4899 100%);
122
- -webkit-background-clip: text;
123
- -webkit-text-fill-color: transparent;
124
- }
125
-
126
- .subtitle {
127
- color: #6b7280;
128
- font-size: 1.1rem;
129
- margin-top: 0.5rem;
130
- }
131
-
132
- /* Custom Panel Styling */
133
- .control-panel {
134
- background: white !important;
135
- border: 1px solid #e5e7eb !important;
136
- border-radius: 12px !important;
137
- padding: 1.5rem !important;
138
- box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1) !important;
139
- }
140
-
141
- /* Paper Output Styling */
142
- .paper-preview {
143
- background: white !important;
144
- border: 1px solid #e5e7eb !important;
145
- min-height: 700px !important;
146
- padding: 3rem !important;
147
- border-radius: 2px !important;
148
- box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04) !important;
149
- font-family: 'Times New Roman', serif !important; /* Academic look */
150
- }
151
 
152
- /* Primary Button Styling */
153
- #gen-btn {
154
- background: linear-gradient(135deg, #4f46e5 0%, #4338ca 100%) !important;
155
- border: none !important;
156
- color: white !important;
157
- font-weight: 600 !important;
158
- padding: 12px !important;
159
- border-radius: 8px !important;
160
- transition: transform 0.2s, box-shadow 0.2s !important;
161
- }
162
-
163
- #gen-btn:hover {
164
- transform: translateY(-2px);
165
- box-shadow: 0 10px 15px -3px rgba(79, 70, 229, 0.4) !important;
166
- }
167
- """
168
-
169
- with gr.Blocks(title="ExamGen AI", css=css) as demo:
170
 
171
- # Header
172
- gr.HTML("""
173
- <div class="header-container">
174
- <div class="logo-text">ExamGen AI</div>
175
- <div class="subtitle">Enterprise-grade Question Paper Generator</div>
176
- </div>
177
- """)
178
-
179
  with gr.Row():
180
-
181
- # --- Left Sidebar: Controls ---
182
- with gr.Column(scale=1, elem_classes="control-panel"):
183
- gr.Markdown("### βš™οΈ Configuration")
184
-
185
- gr.Markdown("**1. Upload Material**")
186
  pdf_input = gr.File(
187
- label="",
188
- file_types=[".pdf"],
189
- file_count="single"
190
  )
191
 
192
- gr.HTML("<hr style='margin: 1.5rem 0; border-color: #f3f4f6;'>")
193
-
194
- gr.Markdown("**2. Exam Settings**")
195
- difficulty = gr.Radio(
196
- choices=["Easy", "Medium", "Hard"],
197
- value="Medium",
198
- label="Difficulty Level",
199
- info="Determines concept depth"
200
- )
201
-
202
- num_questions = gr.Slider(
203
- minimum=5,
204
- maximum=30,
205
- value=10,
206
- step=1,
207
- label="Total Questions",
208
- info="Mix of MCQ, Short & Long answers"
209
- )
210
 
211
- gr.HTML("<div style='margin-top: 1.5rem;'></div>")
212
 
213
- btn_generate = gr.Button(
214
- "✨ Generate Question Paper",
215
- elem_id="gen-btn",
216
- size="lg"
217
- )
218
-
219
- gr.Markdown(
220
- """
221
- <div style="margin-top: 2rem; padding: 1rem; background: #f9fafb; border-radius: 6px; font-size: 0.8rem; color: #6b7280;">
222
- <strong>System Status:</strong><br>
223
- 🟒 Model: Llama-3.2-3B<br>
224
- 🟒 RAG Engine: Active
225
- </div>
226
- """
227
- )
228
-
229
- # --- Right Content: Paper Preview ---
230
  with gr.Column(scale=2):
231
- with gr.Group():
232
- gr.Markdown("### πŸ“„ Examination Paper Preview")
233
- output = gr.Markdown(
234
- value="<div style='text-align: center; color: #9ca3af; margin-top: 5rem;'><i>Generated paper will appear here formatted as a document.</i></div>",
235
- elem_classes="paper-preview"
236
- )
237
 
238
- # --- Interaction Wiring ---
239
- btn_generate.click(
240
  fn=generate_question_paper,
241
  inputs=[pdf_input, difficulty, num_questions],
242
- outputs=[output],
243
- concurrency_limit=1
244
  )
 
 
 
 
 
 
245
 
246
  if __name__ == "__main__":
247
  demo.launch()
 
5
  from langchain_community.embeddings.fastembed import FastEmbedEmbeddings
6
  from langchain_community.vectorstores import FAISS
7
  from huggingface_hub import InferenceClient
8
+ from langchain_core.prompts import ChatPromptTemplate
9
 
10
+ # --- 1. Model Setup using HF Inference Client ---
11
  HF_TOKEN = os.environ.get("HF_TOKEN", "")
12
 
13
+ if not HF_TOKEN:
14
+ print("⚠️ Warning: HF_TOKEN not set. The app may not work properly.")
15
+
16
+ # Use InferenceClient directly instead of LangChain wrapper
17
  client = InferenceClient(token=HF_TOKEN)
18
 
19
+ # --- 2. The Core Logic ---
20
+ def generate_question_paper(pdf_file, difficulty, num_questions):
21
  if not pdf_file:
22
+ return "❌ Please upload a PDF file first."
23
 
24
  if not HF_TOKEN:
25
+ return "❌ Error: HF_TOKEN not configured. Please add your Hugging Face token in Space Settings > Repository secrets."
26
+
27
  try:
28
+ # A. Load PDF
 
29
  loader = PyPDFLoader(pdf_file.name)
30
  pages = loader.load()
31
 
32
  if not pages:
33
+ return "❌ Error: Could not extract text from PDF. Please ensure it's a valid PDF with text content."
34
+
35
+ # B. Split Text
 
36
  text_splitter = RecursiveCharacterTextSplitter(
37
  chunk_size=1000,
38
  chunk_overlap=100
39
  )
40
  chunks = text_splitter.split_documents(pages)
41
+
42
+ # C. Vector Store (FAISS)
 
43
  embeddings = FastEmbedEmbeddings()
44
  vector_store = FAISS.from_documents(chunks, embeddings)
45
+
46
+ # D. Retrieve Context
 
47
  retriever = vector_store.as_retriever(search_kwargs={"k": 7})
48
+ context_docs = retriever.invoke("Key concepts and definitions")
 
49
  context_text = "\n\n".join([doc.page_content for doc in context_docs])
 
 
 
50
 
51
+ # E. Create Prompt
52
+ prompt = f"""You are an expert academic examiner. Create a formal Question Paper based ONLY on the context provided below.
53
+
54
+ CONTEXT:
55
+ {context_text}
56
+
57
+ INSTRUCTIONS:
58
+ - Difficulty: {difficulty}
59
+ - Total Questions: {num_questions}
60
+ - Format:
61
+ Section A: Multiple Choice Questions (MCQs)
62
+ Section B: Short Answer Questions
63
+ Section C: Long Answer/Essay Questions
64
+ - Provide the Answer Key for MCQs at the very end.
65
+
66
+ Do not output conversational text. Output ONLY the exam paper in a well-formatted structure."""
67
 
68
+ # F. Generate using chat completion with a supported model
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  messages = [{"role": "user", "content": prompt}]
70
 
71
+ response = ""
72
  for message in client.chat_completion(
73
  messages=messages,
74
  model="meta-llama/Llama-3.2-3B-Instruct",
75
+ max_tokens=2000,
76
+ temperature=0.7,
77
  stream=True,
78
  ):
79
+ if hasattr(message, 'choices') and len(message.choices) > 0:
80
+ if hasattr(message.choices[0], 'delta') and hasattr(message.choices[0].delta, 'content'):
81
+ response += message.choices[0].delta.content or ""
82
+
83
+ return response
 
 
 
84
 
85
  except Exception as e:
86
+ return f"❌ Error: {str(e)}\n\nPlease check:\n1. PDF is valid and contains text\n2. HF_TOKEN is correctly set in Space secrets\n3. Try again or contact support"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
+ # --- 3. The UI ---
89
+ with gr.Blocks(title="AI Question Paper Generator", theme=gr.themes.Soft(primary_hue="blue")) as demo:
90
+ gr.Markdown("# πŸ“„ AI Question Paper Generator")
91
+ gr.Markdown("Powered by **Llama 3.2 3B** via Hugging Face Inference API")
92
+ gr.Markdown("⚑ Fast β€’ 🎯 Accurate β€’ πŸ“š Context-Aware")
 
 
 
 
 
 
 
 
 
 
 
 
 
93
 
 
 
 
 
 
 
 
 
94
  with gr.Row():
95
+ with gr.Column(scale=1):
 
 
 
 
 
96
  pdf_input = gr.File(
97
+ label="πŸ“„ Upload Study Material (PDF)",
98
+ file_types=[".pdf"]
 
99
  )
100
 
101
+ with gr.Group():
102
+ difficulty = gr.Radio(
103
+ ["Easy", "Medium", "Hard"],
104
+ label="🎚️ Difficulty Level",
105
+ value="Medium"
106
+ )
107
+ num_questions = gr.Slider(
108
+ 5, 20, value=10, step=1,
109
+ label="πŸ“Š Total Questions"
110
+ )
 
 
 
 
 
 
 
 
111
 
112
+ btn = gr.Button("✨ Generate Question Paper", variant="primary", size="lg")
113
 
114
+ gr.Markdown("""
115
+ ### πŸ“ Instructions:
116
+ 1. Upload a PDF containing study material
117
+ 2. Select difficulty level
118
+ 3. Choose number of questions
119
+ 4. Click Generate!
120
+ """)
121
+
 
 
 
 
 
 
 
 
 
122
  with gr.Column(scale=2):
123
+ output = gr.Markdown(label="Generated Question Paper")
 
 
 
 
 
124
 
125
+ btn.click(
 
126
  fn=generate_question_paper,
127
  inputs=[pdf_input, difficulty, num_questions],
128
+ outputs=output
 
129
  )
130
+
131
+ gr.Markdown("""
132
+ ---
133
+ **Note:** Set `HF_TOKEN` in your Space's Settings β†’ Repository secrets.
134
+ Get your token from https://huggingface.co/settings/tokens
135
+ """)
136
 
137
  if __name__ == "__main__":
138
  demo.launch()