gladguy commited on
Commit
6be7aaf
Β·
1 Parent(s): 766eaee

Add interactive VIVA training mode with AI-powered question generation and evaluation

Browse files
Files changed (1) hide show
  1. app.py +309 -41
app.py CHANGED
@@ -158,6 +158,134 @@ Keep it concise and educational."""
158
  return f"Error generating information: {str(e)}"
159
 
160
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
  def process_anatomy_query(query: str) -> tuple:
162
  """
163
  Main function to process anatomy queries.
@@ -202,66 +330,206 @@ def process_anatomy_query(query: str) -> tuple:
202
  return image, info, error_message
203
 
204
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  # Create Gradio interface
206
  with gr.Blocks(title="AnatomyBot - MBBS Anatomy Tutor") as demo:
207
  gr.Markdown(
208
  """
209
  # 🩺 AnatomyBot - Your MBBS Anatomy Tutor
210
 
211
- Ask questions about human anatomy and receive visual diagrams with key learning points!
212
  """
213
  )
214
 
215
- with gr.Row():
216
- with gr.Column():
217
- query_input = gr.Textbox(
218
- label="Ask an Anatomy Question",
219
- placeholder="e.g., Show me the Circle of Willis",
220
- lines=2
221
- )
222
-
223
- # Examples moved here - right below the text box
224
- gr.Examples(
225
- examples=[
226
- ["Show me the Circle of Willis"],
227
- ["Brachial plexus anatomy"],
228
- ["Carpal bones arrangement"],
229
- ["Layers of the scalp"],
230
- ["Anatomy of the heart chambers"],
231
- ["Cranial nerves and their functions"],
232
- ["Structure of the kidney nephron"],
233
- ["Branches of the abdominal aorta"],
234
- ["Rotator cuff muscles"],
235
- ["Spinal cord cross section"],
236
- ["Femoral triangle anatomy"],
237
- ["Larynx cartilages and membranes"],
238
- ["Portal venous system"],
239
- ["Anatomy of the eyeball"],
240
- ["Bronchopulmonary segments"]
241
- ],
242
- inputs=query_input
243
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
 
245
- submit_btn = gr.Button("πŸ” Search & Learn", variant="primary", size="lg")
246
- error_output = gr.Markdown(label="Status")
247
 
248
- with gr.Column():
249
- image_output = gr.Image(label="Anatomy Diagram", type="pil")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
 
251
- with gr.Row():
252
- info_output = gr.Markdown(label="Key Points to Know")
 
 
 
253
 
254
- # Event handler
255
  submit_btn.click(
256
- fn=process_anatomy_query,
257
  inputs=[query_input],
258
- outputs=[image_output, info_output, error_output]
259
  )
260
 
261
  query_input.submit(
262
- fn=process_anatomy_query,
263
  inputs=[query_input],
264
- outputs=[image_output, info_output, error_output]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
  )
266
 
267
  if __name__ == "__main__":
 
158
  return f"Error generating information: {str(e)}"
159
 
160
 
161
+ def generate_viva_questions(topic: str) -> list:
162
+ """
163
+ Generate 5 viva questions for the anatomy topic.
164
+ Returns list of question dictionaries with question, hint, and expected answer.
165
+ """
166
+ try:
167
+ headers = {
168
+ "Content-Type": "application/json",
169
+ "Authorization": f"Bearer {HYPERBOLIC_API_KEY}"
170
+ }
171
+
172
+ prompt = f"""You are an anatomy professor conducting a VIVA exam on: {topic}
173
+
174
+ Generate exactly 5 viva questions that would be asked during an MBBS anatomy practical exam. For each question, provide:
175
+ 1. The question
176
+ 2. A helpful hint (not the answer, but guides student thinking)
177
+ 3. The expected key points in the answer
178
+
179
+ Format your response EXACTLY as follows:
180
+ Q1: [question]
181
+ HINT: [hint]
182
+ ANSWER: [expected answer key points]
183
+
184
+ Q2: [question]
185
+ HINT: [hint]
186
+ ANSWER: [expected answer key points]
187
+
188
+ ... and so on for all 5 questions.
189
+
190
+ Make questions progressively harder. Start with identification, then structure, then clinical relevance."""
191
+
192
+ payload = {
193
+ "model": HYPERBOLIC_MODEL,
194
+ "messages": [{"role": "user", "content": prompt}],
195
+ "max_tokens": 800,
196
+ "temperature": 0.7
197
+ }
198
+
199
+ response = requests.post(HYPERBOLIC_API_URL, headers=headers, json=payload, timeout=25)
200
+ response.raise_for_status()
201
+
202
+ result = response.json()
203
+ content = result["choices"][0]["message"]["content"]
204
+
205
+ # Parse the questions
206
+ questions = []
207
+ lines = content.split('\n')
208
+ current_q = {}
209
+
210
+ for line in lines:
211
+ line = line.strip()
212
+ if line.startswith('Q') and ':' in line:
213
+ if current_q:
214
+ questions.append(current_q)
215
+ current_q = {'question': line.split(':', 1)[1].strip()}
216
+ elif line.startswith('HINT:'):
217
+ current_q['hint'] = line.split(':', 1)[1].strip()
218
+ elif line.startswith('ANSWER:'):
219
+ current_q['answer'] = line.split(':', 1)[1].strip()
220
+
221
+ if current_q:
222
+ questions.append(current_q)
223
+
224
+ return questions[:5] # Ensure exactly 5 questions
225
+
226
+ except Exception as e:
227
+ print(f"Error generating viva questions: {e}")
228
+ return []
229
+
230
+
231
+ def evaluate_viva_answer(question: str, student_answer: str, expected_answer: str) -> tuple[str, str]:
232
+ """
233
+ Evaluate student's answer and provide feedback.
234
+ Returns (feedback, score_emoji)
235
+ """
236
+ if not student_answer.strip():
237
+ return "Please provide an answer to continue.", "⏸️"
238
+
239
+ try:
240
+ headers = {
241
+ "Content-Type": "application/json",
242
+ "Authorization": f"Bearer {HYPERBOLIC_API_KEY}"
243
+ }
244
+
245
+ prompt = f"""You are an anatomy professor evaluating a student's VIVA answer.
246
+
247
+ Question: {question}
248
+ Expected key points: {expected_answer}
249
+ Student's answer: {student_answer}
250
+
251
+ Evaluate the answer and provide:
252
+ 1. Brief feedback (2-3 sentences)
253
+ 2. What was correct
254
+ 3. What was missing (if anything)
255
+ 4. Score as: EXCELLENT, GOOD, FAIR, or NEEDS_IMPROVEMENT
256
+
257
+ Be encouraging but honest. Format your response as friendly feedback."""
258
+
259
+ payload = {
260
+ "model": HYPERBOLIC_MODEL,
261
+ "messages": [{"role": "user", "content": prompt}],
262
+ "max_tokens": 300,
263
+ "temperature": 0.6
264
+ }
265
+
266
+ response = requests.post(HYPERBOLIC_API_URL, headers=headers, json=payload, timeout=15)
267
+ response.raise_for_status()
268
+
269
+ result = response.json()
270
+ feedback = result["choices"][0]["message"]["content"]
271
+
272
+ # Determine emoji based on feedback content
273
+ feedback_upper = feedback.upper()
274
+ if "EXCELLENT" in feedback_upper:
275
+ emoji = "🌟"
276
+ elif "GOOD" in feedback_upper:
277
+ emoji = "βœ…"
278
+ elif "FAIR" in feedback_upper:
279
+ emoji = "πŸ‘"
280
+ else:
281
+ emoji = "πŸ“š"
282
+
283
+ return feedback, emoji
284
+
285
+ except Exception as e:
286
+ return f"Could not evaluate answer: {str(e)}", "⚠️"
287
+
288
+
289
  def process_anatomy_query(query: str) -> tuple:
290
  """
291
  Main function to process anatomy queries.
 
330
  return image, info, error_message
331
 
332
 
333
+ # VIVA Mode Handler Functions
334
+ def start_viva_mode(topic, image):
335
+ """Initialize VIVA mode with questions."""
336
+ if not topic or not image:
337
+ return (
338
+ gr.update(visible=False), # viva_container
339
+ "Please learn about a topic first before starting VIVA mode!", # viva_status
340
+ None, None, None, None, None, None, [] # other outputs
341
+ )
342
+
343
+ questions = generate_viva_questions(topic)
344
+
345
+ if not questions or len(questions) == 0:
346
+ return (
347
+ gr.update(visible=False),
348
+ "Error generating VIVA questions. Please try again.",
349
+ None, None, None, None, None, None, []
350
+ )
351
+
352
+ # Start with question 1
353
+ q1 = questions[0]
354
+ return (
355
+ gr.update(visible=True), # Show VIVA container
356
+ f"**VIVA MODE ACTIVE** πŸ“\nTopic: {topic}", # viva_status
357
+ image, # viva_image
358
+ f"### Question 1 of 5\n\n**{q1['question']}**", # current_question
359
+ f"πŸ’‘ **Hint:** {q1.get('hint', 'Think about the key anatomical features.')}", # hint_display
360
+ "", # Clear answer input
361
+ "", # Clear feedback
362
+ gr.update(interactive=True, value="Submit Answer"), # Enable submit button
363
+ questions # Store questions in state
364
+ )
365
+
366
+
367
+ def submit_viva_answer(answer, questions, current_q_idx):
368
+ """Process student's answer and move to next question."""
369
+ if not questions or current_q_idx >= len(questions):
370
+ return ("VIVA Complete!", "", "", gr.update(interactive=False), current_q_idx)
371
+
372
+ q = questions[current_q_idx]
373
+ feedback, emoji = evaluate_viva_answer(q['question'], answer, q.get('answer', ''))
374
+
375
+ feedback_text = f"{emoji} **Feedback:**\n\n{feedback}\n\n**Expected key points:** {q.get('answer', 'N/A')}"
376
+
377
+ # Move to next question
378
+ next_idx = current_q_idx + 1
379
+
380
+ if next_idx < len(questions):
381
+ next_q = questions[next_idx]
382
+ next_question = f"### Question {next_idx + 1} of 5\n\n**{next_q['question']}**"
383
+ next_hint = f"πŸ’‘ **Hint:** {next_q.get('hint', 'Think carefully about the anatomical relationships.')}"
384
+ return (
385
+ next_question, # Show next question
386
+ next_hint, # Show next hint
387
+ "", # Clear answer box
388
+ feedback_text, # Show feedback for current answer
389
+ gr.update(interactive=True, value="Submit Answer"), # Keep button enabled
390
+ next_idx # Update question index
391
+ )
392
+ else:
393
+ # VIVA complete
394
+ completion_msg = f"### πŸŽ‰ VIVA Complete!\n\nYou've answered all 5 questions. Great job on completing your anatomy VIVA training!"
395
+ return (
396
+ completion_msg,
397
+ "", # Clear hint
398
+ "", # Clear answer
399
+ feedback_text, # Final feedback
400
+ gr.update(interactive=False, value="VIVA Complete"),
401
+ next_idx
402
+ )
403
+
404
+
405
  # Create Gradio interface
406
  with gr.Blocks(title="AnatomyBot - MBBS Anatomy Tutor") as demo:
407
  gr.Markdown(
408
  """
409
  # 🩺 AnatomyBot - Your MBBS Anatomy Tutor
410
 
411
+ Master anatomy through AI-powered learning and interactive VIVA practice!
412
  """
413
  )
414
 
415
+ # State variables for VIVA mode
416
+ viva_questions_state = gr.State([])
417
+ current_question_idx = gr.State(0)
418
+ current_topic = gr.State("")
419
+ current_image_state = gr.State(None)
420
+
421
+ with gr.Tabs() as tabs:
422
+ # LEARNING MODE TAB
423
+ with gr.Tab("πŸ“š Learning Mode"):
424
+ with gr.Row():
425
+ with gr.Column():
426
+ query_input = gr.Textbox(
427
+ label="Ask an Anatomy Question",
428
+ placeholder="e.g., Show me the Circle of Willis",
429
+ lines=2
430
+ )
431
+
432
+ # Examples moved here - right below the text box
433
+ gr.Examples(
434
+ examples=[
435
+ ["Show me the Circle of Willis"],
436
+ ["Brachial plexus anatomy"],
437
+ ["Carpal bones arrangement"],
438
+ ["Layers of the scalp"],
439
+ ["Anatomy of the heart chambers"],
440
+ ["Cranial nerves and their functions"],
441
+ ["Structure of the kidney nephron"],
442
+ ["Branches of the abdominal aorta"],
443
+ ["Rotator cuff muscles"],
444
+ ["Spinal cord cross section"],
445
+ ["Femoral triangle anatomy"],
446
+ ["Larynx cartilages and membranes"],
447
+ ["Portal venous system"],
448
+ ["Anatomy of the eyeball"],
449
+ ["Bronchopulmonary segments"]
450
+ ],
451
+ inputs=query_input
452
+ )
453
+
454
+ submit_btn = gr.Button("πŸ” Search & Learn", variant="primary", size="lg")
455
+ error_output = gr.Markdown(label="Status")
456
+
457
+ # VIVA Mode Button
458
+ start_viva_btn = gr.Button("🎯 Start VIVA Training", variant="secondary", size="lg")
459
+
460
+ with gr.Column():
461
+ image_output = gr.Image(label="Anatomy Diagram", type="pil")
462
 
463
+ with gr.Row():
464
+ info_output = gr.Markdown(label="Key Points to Know")
465
 
466
+ # VIVA MODE TAB
467
+ with gr.Tab("🎯 VIVA Training Mode") as viva_tab:
468
+ viva_status = gr.Markdown("Click 'Start VIVA Training' from Learning Mode after studying a topic!")
469
+
470
+ with gr.Column(visible=False) as viva_container:
471
+ with gr.Row():
472
+ with gr.Column(scale=1):
473
+ viva_image = gr.Image(label="Reference Image", type="pil", interactive=False)
474
+
475
+ with gr.Column(scale=2):
476
+ current_question_display = gr.Markdown("### Question will appear here")
477
+ hint_display = gr.Markdown("πŸ’‘ Hint will appear here")
478
+
479
+ student_answer = gr.Textbox(
480
+ label="Your Answer",
481
+ placeholder="Type your answer here...",
482
+ lines=4
483
+ )
484
+
485
+ submit_answer_btn = gr.Button("Submit Answer", variant="primary")
486
+
487
+ feedback_display = gr.Markdown("Feedback will appear here after you submit your answer")
488
 
489
+ # Event handlers for Learning Mode
490
+ def handle_query(query):
491
+ """Handle learning mode query and store topic/image."""
492
+ img, info, error = process_anatomy_query(query)
493
+ return img, info, error, query, img # Return topic and image for VIVA mode
494
 
 
495
  submit_btn.click(
496
+ fn=handle_query,
497
  inputs=[query_input],
498
+ outputs=[image_output, info_output, error_output, current_topic, current_image_state]
499
  )
500
 
501
  query_input.submit(
502
+ fn=handle_query,
503
  inputs=[query_input],
504
+ outputs=[image_output, info_output, error_output, current_topic, current_image_state]
505
+ )
506
+
507
+ # Start VIVA Mode
508
+ start_viva_btn.click(
509
+ fn=start_viva_mode,
510
+ inputs=[current_topic, current_image_state],
511
+ outputs=[
512
+ viva_container, viva_status, viva_image,
513
+ current_question_display, hint_display,
514
+ student_answer, feedback_display, submit_answer_btn,
515
+ viva_questions_state
516
+ ]
517
+ ).then(
518
+ fn=lambda: gr.update(selected=1), # Switch to VIVA tab
519
+ outputs=[tabs]
520
+ ).then(
521
+ fn=lambda: 0, # Reset question index
522
+ outputs=[current_question_idx]
523
+ )
524
+
525
+ # Submit VIVA Answer
526
+ submit_answer_btn.click(
527
+ fn=submit_viva_answer,
528
+ inputs=[student_answer, viva_questions_state, current_question_idx],
529
+ outputs=[
530
+ current_question_display, hint_display, student_answer,
531
+ feedback_display, submit_answer_btn, current_question_idx
532
+ ]
533
  )
534
 
535
  if __name__ == "__main__":