mah-diaa commited on
Commit
61303c0
Β·
1 Parent(s): 31e0198

Remove preloaded quiz data and test data for full application testing

Browse files

- Removed load_preloaded_concepts() function and preloaded concepts loading
- Removed create_test_quiz() function and all test quiz data
- Updated user_state initialization to start with empty data
- Removed all using_preloaded_data logic throughout the codebase
- Fixed quiz initialization to start with empty quiz instead of test data
- Now requires full document upload and processing workflow for testing

Files changed (1) hide show
  1. app.py +60 -216
app.py CHANGED
@@ -11,109 +11,20 @@ from utils.tutor import ask_guiding_question, analyze_student_approach
11
  from utils.exercise_generator import generate_exercise
12
  from utils.llm_client import call_llm
13
 
14
- # Load preloaded concepts data if available
15
- def load_preloaded_concepts():
16
- """Load preloaded concepts from JSON file to avoid LLM calls."""
17
- preloaded_file = Path(__file__).parent / "data" / "preloaded_concepts.json"
18
- if preloaded_file.exists():
19
- try:
20
- with open(preloaded_file, 'r', encoding='utf-8') as f:
21
- data = json.load(f)
22
- print(f"βœ… [PRELOAD] Loaded preloaded concepts: {len(data.get('concepts', []))} concepts, {len(data.get('topics', []))} topics, {len(data.get('key_points', []))} key points")
23
- return data
24
- except Exception as e:
25
- print(f"⚠️ [PRELOAD] Failed to load preloaded concepts: {str(e)}")
26
- return None
27
- else:
28
- print("ℹ️ [PRELOAD] No preloaded concepts file found, will use LLM extraction")
29
- return None
30
-
31
- # Load preloaded data on startup
32
- preloaded_data = load_preloaded_concepts()
33
-
34
- # Create test quiz data for testing (option 1 is always correct)
35
- def create_test_quiz():
36
- """Create test quiz data with option 1 as correct answer for each question."""
37
- return [
38
- {
39
- "question": "What is the time complexity of binary search?",
40
- "type": "mcq",
41
- "options": [
42
- "O(log n)", # Option 1 - correct
43
- "O(n)",
44
- "O(n log n)",
45
- "O(1)"
46
- ],
47
- "correct_answer": "O(log n)",
48
- "explanation": "Binary search has O(log n) time complexity because it eliminates half of the search space in each iteration."
49
- },
50
- {
51
- "question": "Which data structure follows LIFO (Last In First Out) principle?",
52
- "type": "mcq",
53
- "options": [
54
- "Stack", # Option 1 - correct
55
- "Queue",
56
- "Linked List",
57
- "Tree"
58
- ],
59
- "correct_answer": "Stack",
60
- "explanation": "A stack follows the LIFO principle where the last element added is the first one to be removed."
61
- },
62
- {
63
- "question": "What is the result of 2^3 in binary?",
64
- "type": "mcq",
65
- "options": [
66
- "1000", # Option 1 - correct (8 in binary)
67
- "1010",
68
- "1100",
69
- "1110"
70
- ],
71
- "correct_answer": "1000",
72
- "explanation": "2^3 = 8, which is 1000 in binary representation."
73
- },
74
- {
75
- "question": "Which sorting algorithm has the best average time complexity?",
76
- "type": "mcq",
77
- "options": [
78
- "Merge Sort", # Option 1 - correct
79
- "Bubble Sort",
80
- "Selection Sort",
81
- "Insertion Sort"
82
- ],
83
- "correct_answer": "Merge Sort",
84
- "explanation": "Merge Sort has O(n log n) average time complexity, which is optimal for comparison-based sorting algorithms."
85
- },
86
- {
87
- "question": "What does CPU stand for?",
88
- "type": "mcq",
89
- "options": [
90
- "Central Processing Unit", # Option 1 - correct
91
- "Computer Processing Unit",
92
- "Central Program Unit",
93
- "Computer Program Unit"
94
- ],
95
- "correct_answer": "Central Processing Unit",
96
- "explanation": "CPU stands for Central Processing Unit, which is the primary component that executes instructions in a computer."
97
- }
98
- ]
99
 
100
  # Global state
101
  user_state = {
102
- "concepts": preloaded_data.get("concepts", []) if preloaded_data else [],
103
- "topics": preloaded_data.get("topics", []) if preloaded_data else [],
104
- "key_points": preloaded_data.get("key_points", []) if preloaded_data else [],
105
- "document_parsed": bool(preloaded_data), # Mark as parsed if preloaded data exists
106
  "learning_style": None,
107
- "parsed_data": {
108
- "concepts": preloaded_data.get("concepts", []) if preloaded_data else [],
109
- "topics": preloaded_data.get("topics", []) if preloaded_data else [],
110
- "key_points": preloaded_data.get("key_points", []) if preloaded_data else [],
111
- "text_preview": "Preloaded concepts data" if preloaded_data else ""
112
- } if preloaded_data else None,
113
- "current_quiz": create_test_quiz(), # Load test quiz on startup
114
- "show_quiz_modal": True, # Show quiz modal by default for testing
115
- "learning_context": "", # User-provided context for better concept extraction
116
- "using_preloaded_data": bool(preloaded_data) # Flag to indicate preloaded data is being used
117
  }
118
 
119
 
@@ -261,23 +172,18 @@ Just tell me what you'd like! 😊"""
261
 
262
  For example: "I want to learn about ARM assembly" or "Teach me recursion" """
263
  else:
264
- # Generate quiz - use preloaded test quiz if available
265
  try:
266
- # If we have preloaded data, use test quiz instead of calling LLM
267
- if user_state.get("using_preloaded_data", False):
268
- print("πŸ“ [QUIZ] Using preloaded test quiz data (no LLM call)")
269
- questions = create_test_quiz()
270
- else:
271
- learning_context = user_state.get("learning_context", "")
272
- questions = generate_quiz(
273
- concepts=user_state["concepts"],
274
- topics=user_state.get("topics", []),
275
- num_questions=10,
276
- question_types=["mcq"],
277
- document_context=user_state.get("parsed_data", {}).get("text_preview", ""),
278
- learning_context=learning_context
279
- )
280
-
281
  if questions:
282
  # Store quiz in state
283
  user_state["current_quiz"] = questions
@@ -286,7 +192,7 @@ For example: "I want to learn about ARM assembly" or "Teach me recursion" """
286
  user_state["quiz_trigger_value"] = user_state.get("quiz_trigger_value", 0) + 1
287
  # Update the quiz_trigger State component value (this will trigger the change event)
288
  # Note: We can't directly update State from here, so we'll use a workaround
289
-
290
  response = f"βœ… **Quiz Generated!** ({len(questions)} questions)\n\nI've created a quiz based on your concepts. Check the quiz panel on the right! πŸ“\n\nYou can:\n- Take the quiz interactively\n- Review all questions\n- Generate more questions"
291
  else:
292
  response = "⚠️ I had trouble generating questions. Could you try again or provide more specific concepts?"
@@ -337,39 +243,26 @@ Or tell me: "Generate exercise for {main_concept} in {detected_lang}" and I'll d
337
  user_state.get("topics", [])
338
  )
339
  try:
340
- # Skip LLM call if using preloaded data
341
- if user_state.get("using_preloaded_data", False):
342
- response = f"""πŸ“š **I can create a progressive learning curriculum for: {main_concept}**
 
 
 
 
343
 
344
- Since you're using preloaded data, I'll create a curriculum based on the concepts we have.
 
 
 
345
 
346
- **To generate the full curriculum:**
347
- 1. Go to the "πŸ“š Curriculum" tab in the sidebar
348
- 2. The concept field is already filled: **{main_concept}**
349
- 3. Click "πŸš€ Generate Curriculum"
 
350
 
351
- Or tell me more about what specific aspects you want to focus on! πŸŽ“"""
352
- else:
353
- curriculum = generate_curriculum(
354
- concept=main_concept,
355
- language=detected_lang if detected_lang != "general" else "c", # Default to C for assembly/low-level
356
- starting_level="beginner",
357
- num_exercises=5,
358
- generate_all=False
359
- )
360
-
361
- curriculum_text = f"# πŸ“š Learning Curriculum: {main_concept}\n\n"
362
- curriculum_text += f"**Overview:** {curriculum.get('overview', '')}\n\n"
363
- curriculum_text += f"**Estimated Time:** {curriculum.get('estimated_total_time_minutes', 0)} minutes\n\n"
364
- curriculum_text += "## Learning Path:\n\n"
365
-
366
- for ex in curriculum.get("learning_path", [])[:3]:
367
- curriculum_text += f"**Exercise {ex.get('exercise_number')}** ({ex.get('difficulty')}):\n"
368
- for obj in ex.get("learning_objectives", [])[:2]:
369
- curriculum_text += f"- {obj}\n"
370
- curriculum_text += "\n"
371
-
372
- response = curriculum_text + "\nWould you like to see the full curriculum or start with the first exercise?"
373
  except Exception as e:
374
  response = f"❌ Error generating curriculum: {str(e)}"
375
 
@@ -387,35 +280,23 @@ Tell me a concept or upload a document, and I'll find the best resources for you
387
  user_state.get("topics", [])
388
  )
389
  try:
390
- # Skip LLM call if using preloaded data
391
- if user_state.get("using_preloaded_data", False):
392
- print(f"πŸ“ [RESOURCES] Using preloaded data - skipping LLM call for: {main_concept}")
393
- # Return basic resources without LLM
394
- resources = [
395
- {
396
- "title": f"{main_concept} - Documentation",
397
- "url": f"https://www.google.com/search?q={main_concept}+documentation",
398
- "excerpt": f"Search for {main_concept} documentation and tutorials online."
399
- }
400
- ]
401
- else:
402
- print(f"πŸ”§ [MCP TOOL] search_learning_resources_tool called (chat) - concept: {main_concept}, language: {detected_lang}")
403
- print(f"πŸš€ [MCP TOOL] search_learning_resources_tool - Searching for: {main_concept}")
404
- resources = search_learning_resources(
405
- concept=main_concept,
406
- language=detected_lang if detected_lang != "general" else "c", # Default to C for assembly/low-level
407
- resource_type="all",
408
- max_results=5
409
- )
410
- print(f"βœ… [MCP TOOL] search_learning_resources_tool - Found {len(resources)} resources")
411
-
412
  resources_text = f"# πŸ” Learning Resources for: {main_concept}\n\n"
413
  for i, res in enumerate(resources, 1):
414
  resources_text += f"**{i}. {res.get('title', 'Resource')}**\n"
415
  if res.get('url'):
416
  resources_text += f"πŸ”— {res['url']}\n"
417
  resources_text += f"{res.get('excerpt', '')}\n\n"
418
-
419
  response = resources_text
420
  except Exception as e:
421
  response = f"❌ Error finding resources: {str(e)}"
@@ -516,26 +397,7 @@ Provide a helpful, intelligent response. If they're asking a question, answer it
516
  else:
517
  # Initial greeting or subject prompt
518
  if any(word in message_lower for word in ["learn", "teach", "help", "want to", "study"]):
519
- # Extract subject from message - SKIP LLM if using preloaded data
520
- if user_state.get("using_preloaded_data", False):
521
- # Use preloaded concepts instead of calling LLM
522
- concepts = user_state.get("concepts", [])
523
- subject = concepts[0] if concepts else message_text
524
- response = f"""πŸŽ“ **Great! You want to learn about: {subject}**
525
-
526
- I've identified these concepts: {', '.join(concepts[:5]) if concepts else subject}
527
-
528
- **What would you like to do?**
529
- 1. πŸ“ Generate a quiz to test your understanding
530
- 2. πŸ“š Create a progressive learning curriculum
531
- 3. πŸ’» Generate a programming exercise with starter files and tests
532
- 4. πŸ” Find learning resources and tutorials
533
- 5. πŸ’¬ Ask me questions (I'll guide you with questions, not answers!)
534
-
535
- **πŸ’‘ Pro Tip:** Check the "πŸ’» Programming Challenges" tab to generate complete exercise packages with starter files, tests, and hints!
536
-
537
- Just tell me what you'd like! 😊"""
538
- else:
539
  # Extract subject from message
540
  try:
541
  prompt = f"""Extract the main learning subject/concept from this message: "{message_text}"
@@ -606,9 +468,8 @@ I can help you learn programming and CS concepts in several ways:
606
 
607
  **What would you like to do?** 😊"""
608
  else:
609
- # Only use LLM for substantial questions, and only if not using preloaded data
610
- if not user_state.get("using_preloaded_data", False):
611
- try:
612
  system_prompt = """You are Gnosis, a friendly and helpful AI learning tutor. You help students learn programming and computer science.
613
 
614
  Be conversational, welcoming, and helpful. Explain what you can do in a friendly way."""
@@ -633,11 +494,6 @@ I can help you learn programming and CS concepts. Upload a PDF or tell me what y
633
  except Exception as e:
634
  response = """πŸ‘‹ **Hi! I'm Gnosis, your AI learning tutor!** πŸŽ“
635
 
636
- I can help you learn programming and CS concepts. Upload a PDF or tell me what you want to learn! 😊"""
637
- else:
638
- # Using preloaded data - skip LLM
639
- response = """πŸ‘‹ **Hi! I'm Gnosis, your AI learning tutor!** πŸŽ“
640
-
641
  I can help you learn programming and CS concepts. Upload a PDF or tell me what you want to learn! 😊"""
642
 
643
  # ChatInterface handles appending to history automatically
@@ -828,9 +684,8 @@ with gr.Blocks(title="Gnosis Tutor") as app:
828
 
829
 
830
  # Quiz Popup Modal (shown when quiz is generated) - positioned as floating panel
831
- # Initialize quiz_data_state with test quiz to ensure it's always available
832
- initial_quiz = create_test_quiz()
833
- quiz_data_state = gr.State(value=initial_quiz)
834
  current_question_idx = gr.State(value=0)
835
  quiz_score_state = gr.State(value={"correct": 0, "total": 0})
836
  quiz_trigger = gr.State(value=0) # Trigger to update quiz modal when quiz is generated
@@ -925,7 +780,6 @@ with gr.Blocks(title="Gnosis Tutor") as app:
925
  user_state["key_points"] = parsed_data.get("key_points", [])
926
  user_state["document_parsed"] = True
927
  user_state["parsed_data"] = parsed_data
928
- user_state["using_preloaded_data"] = False # Override preloaded data with new extraction
929
 
930
  print(f"πŸ“Š [UPLOAD] Extracted: {len(user_state['concepts'])} concepts, {len(user_state['topics'])} topics, {len(user_state['key_points'])} key points")
931
 
@@ -961,15 +815,7 @@ with gr.Blocks(title="Gnosis Tutor") as app:
961
  return {}, {}, {}, gr.update(), gr.update(), gr.update(), gr.update()
962
 
963
  def quick_generate_quiz():
964
- """Generate quiz - use preloaded test quiz if available, otherwise use LLM."""
965
- # If we have preloaded data, use test quiz instead of calling LLM
966
- if user_state.get("using_preloaded_data", False):
967
- print("πŸ“ [QUIZ] Using preloaded test quiz data (no LLM call)")
968
- questions = create_test_quiz()
969
- user_state["current_quiz"] = questions
970
- user_state["show_quiz_modal"] = True
971
- user_state["quiz_trigger_value"] = user_state.get("quiz_trigger_value", 0) + 1
972
- return f"βœ… Generated {len(questions)} quiz questions from preloaded data! Check the quiz panel on the right!"
973
 
974
  concepts = user_state.get("concepts", [])
975
  if not concepts:
@@ -1329,16 +1175,14 @@ with gr.Blocks(title="Gnosis Tutor") as app:
1329
  def load_quiz_to_modal():
1330
  """Load quiz from state into modal."""
1331
  quiz = user_state.get("current_quiz")
1332
- # Always ensure we have test quiz data for testing
1333
  if not quiz or len(quiz) == 0:
1334
- quiz = create_test_quiz()
1335
- user_state["current_quiz"] = quiz
1336
- print(f"πŸ“ [QUIZ] Created and loaded test quiz with {len(quiz)} questions")
1337
  else:
1338
  print(f"πŸ“ [QUIZ] Loading existing quiz with {len(quiz)} questions")
1339
 
1340
- # Always show quiz (remove the empty check)
1341
- if False: # Changed to False to always show quiz
1342
  return (
1343
  gr.update(visible=True, value="**No quiz available yet. Ask me to generate a quiz!**"),
1344
  gr.update(visible=False),
 
11
  from utils.exercise_generator import generate_exercise
12
  from utils.llm_client import call_llm
13
 
14
+ # Removed preloaded concepts and test quiz data for full testing
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  # Global state
17
  user_state = {
18
+ "concepts": [],
19
+ "topics": [],
20
+ "key_points": [],
21
+ "document_parsed": False,
22
  "learning_style": None,
23
+ "parsed_data": None,
24
+ "current_quiz": [], # Start with empty quiz
25
+ "show_quiz_modal": False, # Don't show quiz modal by default
26
+ "learning_context": "",
27
+ "using_preloaded_data": False
 
 
 
 
 
28
  }
29
 
30
 
 
172
 
173
  For example: "I want to learn about ARM assembly" or "Teach me recursion" """
174
  else:
175
+ # Generate quiz using LLM
176
  try:
177
+ learning_context = user_state.get("learning_context", "")
178
+ questions = generate_quiz(
179
+ concepts=user_state["concepts"],
180
+ topics=user_state.get("topics", []),
181
+ num_questions=10,
182
+ question_types=["mcq"],
183
+ document_context=user_state.get("parsed_data", {}).get("text_preview", ""),
184
+ learning_context=learning_context
185
+ )
186
+
 
 
 
 
 
187
  if questions:
188
  # Store quiz in state
189
  user_state["current_quiz"] = questions
 
192
  user_state["quiz_trigger_value"] = user_state.get("quiz_trigger_value", 0) + 1
193
  # Update the quiz_trigger State component value (this will trigger the change event)
194
  # Note: We can't directly update State from here, so we'll use a workaround
195
+
196
  response = f"βœ… **Quiz Generated!** ({len(questions)} questions)\n\nI've created a quiz based on your concepts. Check the quiz panel on the right! πŸ“\n\nYou can:\n- Take the quiz interactively\n- Review all questions\n- Generate more questions"
197
  else:
198
  response = "⚠️ I had trouble generating questions. Could you try again or provide more specific concepts?"
 
243
  user_state.get("topics", [])
244
  )
245
  try:
246
+ curriculum = generate_curriculum(
247
+ concept=main_concept,
248
+ language=detected_lang if detected_lang != "general" else "c", # Default to C for assembly/low-level
249
+ starting_level="beginner",
250
+ num_exercises=5,
251
+ generate_all=False
252
+ )
253
 
254
+ curriculum_text = f"# πŸ“š Learning Curriculum: {main_concept}\n\n"
255
+ curriculum_text += f"**Overview:** {curriculum.get('overview', '')}\n\n"
256
+ curriculum_text += f"**Estimated Time:** {curriculum.get('estimated_total_time_minutes', 0)} minutes\n\n"
257
+ curriculum_text += "## Learning Path:\n\n"
258
 
259
+ for ex in curriculum.get("learning_path", [])[:3]:
260
+ curriculum_text += f"**Exercise {ex.get('exercise_number')}** ({ex.get('difficulty')}):\n"
261
+ for obj in ex.get("learning_objectives", [])[:2]:
262
+ curriculum_text += f"- {obj}\n"
263
+ curriculum_text += "\n"
264
 
265
+ response = curriculum_text + "\nWould you like to see the full curriculum or start with the first exercise?"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  except Exception as e:
267
  response = f"❌ Error generating curriculum: {str(e)}"
268
 
 
280
  user_state.get("topics", [])
281
  )
282
  try:
283
+ print(f"πŸ”§ [MCP TOOL] search_learning_resources_tool called (chat) - concept: {main_concept}, language: {detected_lang}")
284
+ print(f"πŸš€ [MCP TOOL] search_learning_resources_tool - Searching for: {main_concept}")
285
+ resources = search_learning_resources(
286
+ concept=main_concept,
287
+ language=detected_lang if detected_lang != "general" else "c", # Default to C for assembly/low-level
288
+ resource_type="all",
289
+ max_results=5
290
+ )
291
+ print(f"βœ… [MCP TOOL] search_learning_resources_tool - Found {len(resources)} resources")
292
+
 
 
 
 
 
 
 
 
 
 
 
 
293
  resources_text = f"# πŸ” Learning Resources for: {main_concept}\n\n"
294
  for i, res in enumerate(resources, 1):
295
  resources_text += f"**{i}. {res.get('title', 'Resource')}**\n"
296
  if res.get('url'):
297
  resources_text += f"πŸ”— {res['url']}\n"
298
  resources_text += f"{res.get('excerpt', '')}\n\n"
299
+
300
  response = resources_text
301
  except Exception as e:
302
  response = f"❌ Error finding resources: {str(e)}"
 
397
  else:
398
  # Initial greeting or subject prompt
399
  if any(word in message_lower for word in ["learn", "teach", "help", "want to", "study"]):
400
+ # Extract subject from message using LLM
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
  # Extract subject from message
402
  try:
403
  prompt = f"""Extract the main learning subject/concept from this message: "{message_text}"
 
468
 
469
  **What would you like to do?** 😊"""
470
  else:
471
+ # Use LLM for substantial questions
472
+ try:
 
473
  system_prompt = """You are Gnosis, a friendly and helpful AI learning tutor. You help students learn programming and computer science.
474
 
475
  Be conversational, welcoming, and helpful. Explain what you can do in a friendly way."""
 
494
  except Exception as e:
495
  response = """πŸ‘‹ **Hi! I'm Gnosis, your AI learning tutor!** πŸŽ“
496
 
 
 
 
 
 
497
  I can help you learn programming and CS concepts. Upload a PDF or tell me what you want to learn! 😊"""
498
 
499
  # ChatInterface handles appending to history automatically
 
684
 
685
 
686
  # Quiz Popup Modal (shown when quiz is generated) - positioned as floating panel
687
+ # Initialize quiz_data_state with empty quiz
688
+ quiz_data_state = gr.State(value=[])
 
689
  current_question_idx = gr.State(value=0)
690
  quiz_score_state = gr.State(value={"correct": 0, "total": 0})
691
  quiz_trigger = gr.State(value=0) # Trigger to update quiz modal when quiz is generated
 
780
  user_state["key_points"] = parsed_data.get("key_points", [])
781
  user_state["document_parsed"] = True
782
  user_state["parsed_data"] = parsed_data
 
783
 
784
  print(f"πŸ“Š [UPLOAD] Extracted: {len(user_state['concepts'])} concepts, {len(user_state['topics'])} topics, {len(user_state['key_points'])} key points")
785
 
 
815
  return {}, {}, {}, gr.update(), gr.update(), gr.update(), gr.update()
816
 
817
  def quick_generate_quiz():
818
+ """Generate quiz using LLM."""
 
 
 
 
 
 
 
 
819
 
820
  concepts = user_state.get("concepts", [])
821
  if not concepts:
 
1175
  def load_quiz_to_modal():
1176
  """Load quiz from state into modal."""
1177
  quiz = user_state.get("current_quiz")
1178
+ # Check if we have a quiz to load
1179
  if not quiz or len(quiz) == 0:
1180
+ print("πŸ“ [QUIZ] No quiz available to load")
1181
+ return [], 0, {"correct": 0, "total": 0}, "No quiz available. Generate a quiz first!", [], gr.update(visible=False), gr.update(visible=False), gr.update(visible=False), gr.update(visible=False)
 
1182
  else:
1183
  print(f"πŸ“ [QUIZ] Loading existing quiz with {len(quiz)} questions")
1184
 
1185
+ # Show quiz if available
 
1186
  return (
1187
  gr.update(visible=True, value="**No quiz available yet. Ask me to generate a quiz!**"),
1188
  gr.update(visible=False),