Redfire-1234 commited on
Commit
3a9814a
Β·
verified Β·
1 Parent(s): 24d1a81

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +459 -12
app.py CHANGED
@@ -380,7 +380,7 @@
380
  # # ------------------------------
381
  # # MCQ Generation
382
  # # ------------------------------
383
- # def generate_mcqs(context, topic, subject):
384
  # # Check if Groq is available
385
  # if not groq_client:
386
  # error_msg = """ERROR: Groq API not initialized!
@@ -393,13 +393,13 @@
393
 
394
  # # Check cache
395
  # context_hash = hashlib.md5(context.encode()).hexdigest()[:8]
396
- # cache_key = get_cache_key(topic, subject, context_hash)
397
 
398
  # if cache_key in MCQ_CACHE:
399
  # print("βœ“ Using cached MCQs")
400
  # return MCQ_CACHE[cache_key]["mcqs"], MCQ_CACHE[cache_key]["chapter"]
401
 
402
- # print(f"πŸ€– Generating MCQs for {subject} - {topic}")
403
 
404
  # # Detect the chapter from our actual chapter list
405
  # chapter = detect_chapter_from_list(context, topic, subject)
@@ -416,7 +416,7 @@
416
  # Reference material from textbook:
417
  # {context[:1500]}
418
 
419
- # Generate exactly 5 multiple-choice questions based on the reference material.
420
 
421
  # FORMAT (follow EXACTLY):
422
  # Q1. [Question based on material]
@@ -433,7 +433,7 @@
433
  # D) [Option 4]
434
  # Answer: [A/B/C/D] - [Brief explanation]
435
 
436
- # Continue for Q3, Q4, Q5.
437
 
438
  # REQUIREMENTS:
439
  # - All questions must be answerable from the reference material
@@ -441,9 +441,12 @@
441
  # - Correct answer must be clearly supported by material
442
  # - Keep explanations brief (1-2 sentences)
443
 
444
- # Generate 5 MCQs now:"""
445
 
446
  # try:
 
 
 
447
  # chat_completion = groq_client.chat.completions.create(
448
  # messages=[
449
  # {
@@ -457,7 +460,7 @@
457
  # ],
458
  # model="llama-3.3-70b-versatile",
459
  # temperature=0.3,
460
- # max_tokens=1500,
461
  # top_p=0.9
462
  # )
463
 
@@ -685,7 +688,33 @@
685
  # <input type="text" id="topic" placeholder="e.g., Mitochondria, Chemical Bonding, Newton's Laws">
686
  # </div>
687
 
688
- # <button onclick="generateMCQs()">πŸš€ Generate 5 MCQs</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
689
 
690
  # <div class="loading" id="loading">
691
  # <div class="spinner"></div>
@@ -715,6 +744,7 @@
715
  # async function generateMCQs() {
716
  # const subject = document.getElementById('subject').value;
717
  # const topic = document.getElementById('topic').value.trim();
 
718
 
719
  # if (!topic) {
720
  # alert('⚠️ Please enter a topic!');
@@ -736,7 +766,7 @@
736
  # const response = await fetch('/generate', {
737
  # method: 'POST',
738
  # headers: {'Content-Type': 'application/json'},
739
- # body: JSON.stringify({subject, topic})
740
  # });
741
 
742
  # const data = await response.json();
@@ -759,7 +789,7 @@
759
  # } finally {
760
  # loading.classList.remove('show');
761
  # btn.disabled = false;
762
- # btn.textContent = 'πŸš€ Generate 5 MCQs';
763
  # }
764
  # }
765
 
@@ -786,6 +816,15 @@
786
  # data = request.json
787
  # subject = data.get("subject", "").lower()
788
  # topic = data.get("topic", "")
 
 
 
 
 
 
 
 
 
789
 
790
  # if not topic:
791
  # return jsonify({"error": "Topic is required"}), 400
@@ -817,7 +856,7 @@
817
  # print(f"βœ“ Context found ({len(context)} chars)")
818
 
819
  # # STEP 3: Generate MCQs
820
- # mcqs, chapter = generate_mcqs(context, topic, subject)
821
 
822
  # # Check if there was a subject mismatch
823
  # if chapter is None:
@@ -850,6 +889,7 @@
850
  # port = int(os.environ.get("PORT", 7860))
851
  # print(f"\nπŸš€ Starting server on port {port}...\n")
852
  # app.run(host="0.0.0.0", port=port, debug=False)
 
853
  import pickle
854
  import faiss
855
  from flask import Flask, request, jsonify, render_template_string
@@ -1349,7 +1389,414 @@ def clean_mcq_output(text):
1349
  if line.startswith('Correct Answer:'):
1350
  line = line.replace('Correct Answer:', 'Answer:')
1351
 
1352
- cleaned_lines.append(line)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1353
 
1354
  return '\n'.join(cleaned_lines)
1355
 
 
380
  # # ------------------------------
381
  # # MCQ Generation
382
  # # ------------------------------
383
+ # def generate_mcqs(context, topic, subject, num_questions=5):
384
  # # Check if Groq is available
385
  # if not groq_client:
386
  # error_msg = """ERROR: Groq API not initialized!
 
393
 
394
  # # Check cache
395
  # context_hash = hashlib.md5(context.encode()).hexdigest()[:8]
396
+ # cache_key = get_cache_key(topic, subject, context_hash) + f":{num_questions}"
397
 
398
  # if cache_key in MCQ_CACHE:
399
  # print("βœ“ Using cached MCQs")
400
  # return MCQ_CACHE[cache_key]["mcqs"], MCQ_CACHE[cache_key]["chapter"]
401
 
402
+ # print(f"πŸ€– Generating {num_questions} MCQs for {subject} - {topic}")
403
 
404
  # # Detect the chapter from our actual chapter list
405
  # chapter = detect_chapter_from_list(context, topic, subject)
 
416
  # Reference material from textbook:
417
  # {context[:1500]}
418
 
419
+ # Generate exactly {num_questions} multiple-choice questions based on the reference material.
420
 
421
  # FORMAT (follow EXACTLY):
422
  # Q1. [Question based on material]
 
433
  # D) [Option 4]
434
  # Answer: [A/B/C/D] - [Brief explanation]
435
 
436
+ # Continue for Q3, Q4, Q5{"..." if num_questions > 5 else ""}.
437
 
438
  # REQUIREMENTS:
439
  # - All questions must be answerable from the reference material
 
441
  # - Correct answer must be clearly supported by material
442
  # - Keep explanations brief (1-2 sentences)
443
 
444
+ # Generate {num_questions} MCQs now:"""
445
 
446
  # try:
447
+ # # Adjust max_tokens based on number of questions
448
+ # max_tokens = min(3000, 300 * num_questions)
449
+
450
  # chat_completion = groq_client.chat.completions.create(
451
  # messages=[
452
  # {
 
460
  # ],
461
  # model="llama-3.3-70b-versatile",
462
  # temperature=0.3,
463
+ # max_tokens=max_tokens,
464
  # top_p=0.9
465
  # )
466
 
 
688
  # <input type="text" id="topic" placeholder="e.g., Mitochondria, Chemical Bonding, Newton's Laws">
689
  # </div>
690
 
691
+ # <div class="form-group">
692
+ # <label for="numQuestions">πŸ”’ Number of MCQs</label>
693
+ # <select id="numQuestions">
694
+ # <option value="1">1 MCQ</option>
695
+ # <option value="2">2 MCQs</option>
696
+ # <option value="3">3 MCQs</option>
697
+ # <option value="4">4 MCQs</option>
698
+ # <option value="5" selected>5 MCQs</option>
699
+ # <option value="6">6 MCQs</option>
700
+ # <option value="7">7 MCQs</option>
701
+ # <option value="8">8 MCQs</option>
702
+ # <option value="9">9 MCQs</option>
703
+ # <option value="10">10 MCQs</option>
704
+ # <option value="11">11 MCQs</option>
705
+ # <option value="12">12 MCQs</option>
706
+ # <option value="13">13 MCQs</option>
707
+ # <option value="14">14 MCQs</option>
708
+ # <option value="15">15 MCQs</option>
709
+ # <option value="16">16 MCQs</option>
710
+ # <option value="17">17 MCQs</option>
711
+ # <option value="18">18 MCQs</option>
712
+ # <option value="19">19 MCQs</option>
713
+ # <option value="20">20 MCQs</option>
714
+ # </select>
715
+ # </div>
716
+
717
+ # <button onclick="generateMCQs()">πŸš€ Generate MCQs</button>
718
 
719
  # <div class="loading" id="loading">
720
  # <div class="spinner"></div>
 
744
  # async function generateMCQs() {
745
  # const subject = document.getElementById('subject').value;
746
  # const topic = document.getElementById('topic').value.trim();
747
+ # const numQuestions = parseInt(document.getElementById('numQuestions').value);
748
 
749
  # if (!topic) {
750
  # alert('⚠️ Please enter a topic!');
 
766
  # const response = await fetch('/generate', {
767
  # method: 'POST',
768
  # headers: {'Content-Type': 'application/json'},
769
+ # body: JSON.stringify({subject, topic, num_questions: numQuestions})
770
  # });
771
 
772
  # const data = await response.json();
 
789
  # } finally {
790
  # loading.classList.remove('show');
791
  # btn.disabled = false;
792
+ # btn.textContent = 'πŸš€ Generate MCQs';
793
  # }
794
  # }
795
 
 
816
  # data = request.json
817
  # subject = data.get("subject", "").lower()
818
  # topic = data.get("topic", "")
819
+ # num_questions = data.get("num_questions", 5)
820
+
821
+ # # Validate num_questions
822
+ # try:
823
+ # num_questions = int(num_questions)
824
+ # if num_questions < 1 or num_questions > 20:
825
+ # num_questions = 5
826
+ # except:
827
+ # num_questions = 5
828
 
829
  # if not topic:
830
  # return jsonify({"error": "Topic is required"}), 400
 
856
  # print(f"βœ“ Context found ({len(context)} chars)")
857
 
858
  # # STEP 3: Generate MCQs
859
+ # mcqs, chapter = generate_mcqs(context, topic, subject, num_questions)
860
 
861
  # # Check if there was a subject mismatch
862
  # if chapter is None:
 
889
  # port = int(os.environ.get("PORT", 7860))
890
  # print(f"\nπŸš€ Starting server on port {port}...\n")
891
  # app.run(host="0.0.0.0", port=port, debug=False)
892
+
893
  import pickle
894
  import faiss
895
  from flask import Flask, request, jsonify, render_template_string
 
1389
  if line.startswith('Correct Answer:'):
1390
  line = line.replace('Correct Answer:', 'Answer:')
1391
 
1392
+ # Split Answer and explanation onto separate lines
1393
+ if line.startswith('Answer:'):
1394
+ # Match pattern: Answer: X) text - explanation
1395
+ match = re.match(r'(Answer:\s*[A-D]\))\s*(.+?)(\s*-\s*(.+))?
1396
+
1397
+ # ------------------------------
1398
+ # HTML UI
1399
+ # ------------------------------
1400
+ HTML_TEMPLATE = """
1401
+ <!DOCTYPE html>
1402
+ <html>
1403
+ <head>
1404
+ <title>Class 12 PCB MCQ Generator</title>
1405
+ <style>
1406
+ * { margin: 0; padding: 0; box-sizing: border-box; }
1407
+ body {
1408
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
1409
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1410
+ min-height: 100vh;
1411
+ padding: 20px;
1412
+ }
1413
+ .container {
1414
+ max-width: 900px;
1415
+ margin: 0 auto;
1416
+ background: white;
1417
+ border-radius: 20px;
1418
+ box-shadow: 0 20px 60px rgba(0,0,0,0.3);
1419
+ overflow: hidden;
1420
+ }
1421
+ .header {
1422
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1423
+ color: white;
1424
+ padding: 30px;
1425
+ text-align: center;
1426
+ }
1427
+ .header h1 { font-size: 2.5em; margin-bottom: 10px; }
1428
+ .content { padding: 40px; }
1429
+ .form-group { margin-bottom: 25px; }
1430
+ label {
1431
+ display: block;
1432
+ font-weight: 600;
1433
+ margin-bottom: 10px;
1434
+ color: #333;
1435
+ font-size: 16px;
1436
+ }
1437
+ select, input {
1438
+ width: 100%;
1439
+ padding: 15px;
1440
+ border: 2px solid #e0e0e0;
1441
+ border-radius: 10px;
1442
+ font-size: 16px;
1443
+ font-family: inherit;
1444
+ transition: border-color 0.3s;
1445
+ }
1446
+ select:focus, input:focus {
1447
+ outline: none;
1448
+ border-color: #667eea;
1449
+ }
1450
+ button {
1451
+ width: 100%;
1452
+ padding: 18px;
1453
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1454
+ color: white;
1455
+ border: none;
1456
+ border-radius: 10px;
1457
+ font-size: 18px;
1458
+ font-weight: 600;
1459
+ cursor: pointer;
1460
+ transition: all 0.3s;
1461
+ }
1462
+ button:hover {
1463
+ transform: translateY(-2px);
1464
+ box-shadow: 0 10px 20px rgba(102, 126, 234, 0.4);
1465
+ }
1466
+ button:disabled {
1467
+ background: #ccc;
1468
+ cursor: not-allowed;
1469
+ transform: none;
1470
+ }
1471
+ .result {
1472
+ margin-top: 30px;
1473
+ padding: 25px;
1474
+ background: #f8f9fa;
1475
+ border-radius: 10px;
1476
+ border-left: 4px solid #667eea;
1477
+ display: none;
1478
+ }
1479
+ .result.show { display: block; }
1480
+ .result h3 {
1481
+ color: #667eea;
1482
+ margin-bottom: 20px;
1483
+ font-size: 1.4em;
1484
+ }
1485
+ .chapter-info {
1486
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1487
+ color: white;
1488
+ padding: 15px 20px;
1489
+ border-radius: 8px;
1490
+ margin-bottom: 20px;
1491
+ display: flex;
1492
+ align-items: center;
1493
+ gap: 10px;
1494
+ font-size: 16px;
1495
+ }
1496
+ .chapter-icon {
1497
+ font-size: 24px;
1498
+ }
1499
+ .chapter-text {
1500
+ flex: 1;
1501
+ }
1502
+ .chapter-name {
1503
+ font-weight: 700;
1504
+ font-size: 18px;
1505
+ }
1506
+ .mcq-content {
1507
+ background: white;
1508
+ padding: 25px;
1509
+ border-radius: 8px;
1510
+ white-space: pre-wrap;
1511
+ line-height: 1.9;
1512
+ font-size: 15px;
1513
+ }
1514
+ .loading {
1515
+ text-align: center;
1516
+ padding: 30px;
1517
+ display: none;
1518
+ }
1519
+ .loading.show { display: block; }
1520
+ .spinner {
1521
+ border: 4px solid #f3f3f3;
1522
+ border-top: 4px solid #667eea;
1523
+ border-radius: 50%;
1524
+ width: 50px;
1525
+ height: 50px;
1526
+ animation: spin 1s linear infinite;
1527
+ margin: 0 auto 15px;
1528
+ }
1529
+ @keyframes spin {
1530
+ 0% { transform: rotate(0deg); }
1531
+ 100% { transform: rotate(360deg); }
1532
+ }
1533
+ .subject-tag {
1534
+ display: inline-block;
1535
+ padding: 5px 15px;
1536
+ border-radius: 20px;
1537
+ font-size: 13px;
1538
+ font-weight: 600;
1539
+ margin-right: 10px;
1540
+ }
1541
+ .bio { background: #d4edda; color: #155724; }
1542
+ .chem { background: #d1ecf1; color: #0c5460; }
1543
+ .phy { background: #f8d7da; color: #721c24; }
1544
+ .api-badge {
1545
+ background: #17a2b8;
1546
+ color: white;
1547
+ padding: 5px 12px;
1548
+ border-radius: 15px;
1549
+ font-size: 12px;
1550
+ margin-left: 10px;
1551
+ }
1552
+ </style>
1553
+ </head>
1554
+ <body>
1555
+ <div class="container">
1556
+ <div class="header">
1557
+ <h1>πŸŽ“ Class 12 PCB MCQ Generator</h1>
1558
+ <p style="font-size: 1.1em; margin-bottom: 15px;">
1559
+ Generate practice MCQs from your textbooks
1560
+ <span class="api-badge">⚑ Llama 3.3 70B</span>
1561
+ </p>
1562
+ <div>
1563
+ <span class="subject-tag bio">Biology</span>
1564
+ <span class="subject-tag chem">Chemistry</span>
1565
+ <span class="subject-tag phy">Physics</span>
1566
+ </div>
1567
+ </div>
1568
+
1569
+ <div class="content">
1570
+ <div class="form-group">
1571
+ <label for="subject">πŸ“š Select Subject</label>
1572
+ <select id="subject">
1573
+ <option value="biology">Biology</option>
1574
+ <option value="chemistry">Chemistry</option>
1575
+ <option value="physics">Physics</option>
1576
+ </select>
1577
+ </div>
1578
+
1579
+ <div class="form-group">
1580
+ <label for="topic">✏️ Enter Topic</label>
1581
+ <input type="text" id="topic" placeholder="e.g., Mitochondria, Chemical Bonding, Newton's Laws">
1582
+ </div>
1583
+
1584
+ <div class="form-group">
1585
+ <label for="numQuestions">πŸ”’ Number of MCQs</label>
1586
+ <select id="numQuestions">
1587
+ <option value="1">1 MCQ</option>
1588
+ <option value="2">2 MCQs</option>
1589
+ <option value="3">3 MCQs</option>
1590
+ <option value="4">4 MCQs</option>
1591
+ <option value="5" selected>5 MCQs</option>
1592
+ <option value="6">6 MCQs</option>
1593
+ <option value="7">7 MCQs</option>
1594
+ <option value="8">8 MCQs</option>
1595
+ <option value="9">9 MCQs</option>
1596
+ <option value="10">10 MCQs</option>
1597
+ <option value="11">11 MCQs</option>
1598
+ <option value="12">12 MCQs</option>
1599
+ <option value="13">13 MCQs</option>
1600
+ <option value="14">14 MCQs</option>
1601
+ <option value="15">15 MCQs</option>
1602
+ <option value="16">16 MCQs</option>
1603
+ <option value="17">17 MCQs</option>
1604
+ <option value="18">18 MCQs</option>
1605
+ <option value="19">19 MCQs</option>
1606
+ <option value="20">20 MCQs</option>
1607
+ </select>
1608
+ </div>
1609
+
1610
+ <button onclick="generateMCQs()">πŸš€ Generate MCQs</button>
1611
+
1612
+ <div class="loading" id="loading">
1613
+ <div class="spinner"></div>
1614
+ <p style="color: #666; font-size: 16px;">Generating MCQs with AI...</p>
1615
+ <p style="color: #999; font-size: 13px; margin-top: 10px;">⚑ Detecting chapter from textbook...</p>
1616
+ </div>
1617
+
1618
+ <div class="result" id="result">
1619
+ <h3>πŸ“ Generated MCQs:</h3>
1620
+
1621
+ <div class="chapter-info" id="chapterInfo" style="display: none;">
1622
+ <span class="chapter-icon">πŸ“–</span>
1623
+ <div class="chapter-text">
1624
+ <div style="font-size: 13px; opacity: 0.9;">Chapter:</div>
1625
+ <div class="chapter-name" id="chapterName"></div>
1626
+ </div>
1627
+ </div>
1628
+
1629
+ <div style="background: #d4edda; padding: 12px; border-radius: 6px; margin-bottom: 15px; color: #155724; font-size: 14px;">
1630
+ βœ“ <strong>High Quality:</strong> Generated by Llama 3.3 70B via Groq API
1631
+ </div>
1632
+ <div class="mcq-content" id="mcqContent"></div>
1633
+ </div>
1634
+ </div>
1635
+ </div>
1636
+ <script>
1637
+ async function generateMCQs() {
1638
+ const subject = document.getElementById('subject').value;
1639
+ const topic = document.getElementById('topic').value.trim();
1640
+ const numQuestions = parseInt(document.getElementById('numQuestions').value);
1641
+
1642
+ if (!topic) {
1643
+ alert('⚠️ Please enter a topic!');
1644
+ return;
1645
+ }
1646
+
1647
+ const loading = document.getElementById('loading');
1648
+ const result = document.getElementById('result');
1649
+ const btn = document.querySelector('button');
1650
+ const chapterInfo = document.getElementById('chapterInfo');
1651
+
1652
+ loading.classList.add('show');
1653
+ result.classList.remove('show');
1654
+ chapterInfo.style.display = 'none';
1655
+ btn.disabled = true;
1656
+ btn.textContent = '⏳ Generating...';
1657
+
1658
+ try {
1659
+ const response = await fetch('/generate', {
1660
+ method: 'POST',
1661
+ headers: {'Content-Type': 'application/json'},
1662
+ body: JSON.stringify({subject, topic, num_questions: numQuestions})
1663
+ });
1664
+
1665
+ const data = await response.json();
1666
+
1667
+ if (data.error) {
1668
+ alert('❌ Error: ' + data.error);
1669
+ return;
1670
+ }
1671
+
1672
+ // Display chapter info
1673
+ if (data.chapter && data.chapter !== 'Unknown Chapter') {
1674
+ document.getElementById('chapterName').textContent = data.chapter;
1675
+ chapterInfo.style.display = 'flex';
1676
+ }
1677
+
1678
+ document.getElementById('mcqContent').textContent = data.mcqs;
1679
+ result.classList.add('show');
1680
+ } catch (error) {
1681
+ alert('❌ Error: ' + error.message);
1682
+ } finally {
1683
+ loading.classList.remove('show');
1684
+ btn.disabled = false;
1685
+ btn.textContent = 'πŸš€ Generate MCQs';
1686
+ }
1687
+ }
1688
+
1689
+ document.getElementById('topic').addEventListener('keypress', function(e) {
1690
+ if (e.key === 'Enter') {
1691
+ generateMCQs();
1692
+ }
1693
+ });
1694
+ </script>
1695
+ </body>
1696
+ </html>
1697
+ """
1698
+
1699
+ # ------------------------------
1700
+ # Routes
1701
+ # ------------------------------
1702
+ @app.route("/")
1703
+ def home():
1704
+ return render_template_string(HTML_TEMPLATE)
1705
+
1706
+ @app.route("/generate", methods=["POST"])
1707
+ def generate():
1708
+ try:
1709
+ data = request.json
1710
+ subject = data.get("subject", "").lower()
1711
+ topic = data.get("topic", "")
1712
+ num_questions = data.get("num_questions", 5)
1713
+
1714
+ # Validate num_questions
1715
+ try:
1716
+ num_questions = int(num_questions)
1717
+ if num_questions < 1 or num_questions > 20:
1718
+ num_questions = 5
1719
+ except:
1720
+ num_questions = 5
1721
+
1722
+ if not topic:
1723
+ return jsonify({"error": "Topic is required"}), 400
1724
+
1725
+ if subject not in SUBJECTS:
1726
+ return jsonify({"error": "Invalid subject"}), 400
1727
+
1728
+ print(f"\nπŸ” Validating topic for {subject}...")
1729
+
1730
+ # STEP 1: Validate if topic belongs to subject (BEFORE RAG search)
1731
+ if not validate_topic_subject(topic, subject):
1732
+ subject_names = {
1733
+ "biology": "Biology",
1734
+ "chemistry": "Chemistry",
1735
+ "physics": "Physics"
1736
+ }
1737
+ error_msg = f"The topic '{topic}' does not appear to be related to {subject_names[subject]}.\n\nPlease either:\nβ€’ Enter a {subject_names[subject]}-related topic, or\nβ€’ Select the correct subject for this topic"
1738
+ return jsonify({"error": error_msg}), 400
1739
+
1740
+ print(f"βœ“ Topic validated for {subject}")
1741
+ print(f"πŸ” Searching {subject} for: {topic}")
1742
+
1743
+ # STEP 2: RAG search
1744
+ context = rag_search(topic, subject, k=5)
1745
+
1746
+ if not context or len(context.strip()) < 50:
1747
+ return jsonify({"error": f"No content found for: {topic}"}), 404
1748
+
1749
+ print(f"βœ“ Context found ({len(context)} chars)")
1750
+
1751
+ # STEP 3: Generate MCQs
1752
+ mcqs, chapter = generate_mcqs(context, topic, subject, num_questions)
1753
+
1754
+ # Check if there was a subject mismatch
1755
+ if chapter is None:
1756
+ return jsonify({"error": mcqs}), 400
1757
+
1758
+ return jsonify({
1759
+ "mcqs": mcqs,
1760
+ "subject": subject,
1761
+ "chapter": chapter
1762
+ })
1763
+
1764
+ except Exception as e:
1765
+ print(f"❌ Error: {e}")
1766
+ import traceback
1767
+ traceback.print_exc()
1768
+ return jsonify({"error": str(e)}), 500
1769
+
1770
+ @app.route("/health")
1771
+ def health():
1772
+ return jsonify({
1773
+ "status": "healthy",
1774
+ "groq_available": groq_client is not None,
1775
+ "cache_size": len(MCQ_CACHE)
1776
+ })
1777
+
1778
+ # ------------------------------
1779
+ # Run
1780
+ # ------------------------------
1781
+ if __name__ == "__main__":
1782
+ port = int(os.environ.get("PORT", 7860))
1783
+ print(f"\nπŸš€ Starting server on port {port}...\n")
1784
+ app.run(host="0.0.0.0", port=port, debug=False), line)
1785
+ if match:
1786
+ answer_part = match.group(1) # "Answer: A)"
1787
+ option_text = match.group(2) if match.group(2) else "" # The option text
1788
+ explanation = match.group(4) if match.group(4) else "" # The explanation after -
1789
+
1790
+ if explanation:
1791
+ # Put answer with option text on one line, explanation on next
1792
+ cleaned_lines.append(f"{answer_part} {option_text}")
1793
+ cleaned_lines.append(f"Explanation: {explanation}")
1794
+ else:
1795
+ cleaned_lines.append(line)
1796
+ else:
1797
+ cleaned_lines.append(line)
1798
+ else:
1799
+ cleaned_lines.append(line)
1800
 
1801
  return '\n'.join(cleaned_lines)
1802