Redfire-1234 commited on
Commit
bd22d5c
Β·
verified Β·
1 Parent(s): ed4c641

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +245 -331
app.py CHANGED
@@ -916,9 +916,9 @@ print("=" * 50)
916
  print("STARTING MCQ GENERATOR APP")
917
  print("=" * 50)
918
 
919
- # ------------------------------
920
  # Chapter Names (Actual Textbook Chapters)
921
- # ------------------------------
922
  CHAPTER_NAMES = {
923
  "biology": [
924
  "Reproduction in Lower and Higher Plants",
@@ -975,9 +975,9 @@ CHAPTER_NAMES = {
975
  ]
976
  }
977
 
978
- # ------------------------------
979
  # Initialize Groq API Client
980
- # ------------------------------
981
  print("\nStep 1: Checking Groq API Key...")
982
  print("-" * 50)
983
 
@@ -995,8 +995,10 @@ if not GROQ_API_KEY:
995
  else:
996
  print(f"βœ“ GROQ_API_KEY found ({len(GROQ_API_KEY)} chars)")
997
  print(f" First 20 chars: {GROQ_API_KEY[:20]}...")
 
998
  try:
999
  groq_client = Groq(api_key=GROQ_API_KEY)
 
1000
  # Test the API
1001
  print(" Testing API connection...")
1002
  test = groq_client.chat.completions.create(
@@ -1005,6 +1007,7 @@ else:
1005
  max_tokens=5
1006
  )
1007
  print("βœ“ Groq API working!")
 
1008
  except Exception as e:
1009
  print(f"❌ Groq API initialization failed:")
1010
  print(f" Error: {str(e)}")
@@ -1012,16 +1015,16 @@ else:
1012
 
1013
  print("-" * 50)
1014
 
1015
- # ------------------------------
1016
  # Load embedding model (CPU)
1017
- # ------------------------------
1018
  print("\nStep 2: Loading embedding model...")
1019
  embed_model = SentenceTransformer("all-MiniLM-L6-v2")
1020
  print("βœ“ Embedding model loaded")
1021
 
1022
- # ------------------------------
1023
  # Download files from Hugging Face
1024
- # ------------------------------
1025
  REPO_ID = "Redfire-1234/pcb_tutor"
1026
 
1027
  print("\nStep 3: Downloading subject files...")
@@ -1044,7 +1047,6 @@ except Exception as e:
1044
 
1045
  # Load all subjects into memory
1046
  print("\nStep 4: Loading subject data into memory...")
1047
-
1048
  SUBJECTS = {
1049
  "biology": {
1050
  "chunks": pickle.load(open(bio_chunks_path, "rb")),
@@ -1068,9 +1070,9 @@ print("\n" + "=" * 50)
1068
  print("βœ“ ALL SYSTEMS READY!")
1069
  print("=" * 50 + "\n")
1070
 
1071
- # ------------------------------
1072
  # Caching
1073
- # ------------------------------
1074
  MCQ_CACHE = {}
1075
  MAX_CACHE_SIZE = 100
1076
 
@@ -1082,9 +1084,9 @@ def cache_mcq(key, mcqs):
1082
  MCQ_CACHE.pop(next(iter(MCQ_CACHE)))
1083
  MCQ_CACHE[key] = mcqs
1084
 
1085
- # ------------------------------
1086
  # RAG Search
1087
- # ------------------------------
1088
  def rag_search(query, subject, k=5):
1089
  if subject not in SUBJECTS:
1090
  return None
@@ -1102,9 +1104,9 @@ def rag_search(query, subject, k=5):
1102
 
1103
  return "\n\n".join(results)
1104
 
1105
- # ------------------------------
1106
  # Topic Validation (Check if topic belongs to subject)
1107
- # ------------------------------
1108
  def validate_topic_subject(topic, subject):
1109
  """
1110
  Validate if the topic belongs to the selected subject using LLM
@@ -1113,9 +1115,7 @@ def validate_topic_subject(topic, subject):
1113
  if not groq_client:
1114
  return True # Skip validation if API not available
1115
 
1116
- validation_prompt = f"""You are a Class 12 PCB subject expert.
1117
-
1118
- Determine if the following topic belongs to {subject.title()}.
1119
 
1120
  Topic: "{topic}"
1121
  Subject: {subject.title()}
@@ -1159,9 +1159,9 @@ Answer:"""
1159
  print(f"⚠️ Validation failed: {e}")
1160
  return True # Allow on error to avoid blocking
1161
 
1162
- # ------------------------------
1163
  # Chapter Detection (Using Actual Chapter Names)
1164
- # ------------------------------
1165
  def detect_chapter_from_list(context, topic, subject):
1166
  """
1167
  Detect chapter using the actual chapter list by matching keywords
@@ -1269,19 +1269,17 @@ Response:"""
1269
  print(f"⚠️ Chapter detection failed: {e}")
1270
  return None
1271
 
1272
- # ------------------------------
1273
  # MCQ Generation
1274
- # ------------------------------
1275
  def generate_mcqs(context, topic, subject, num_questions=5):
1276
  # Check if Groq is available
1277
  if not groq_client:
1278
  error_msg = """ERROR: Groq API not initialized!
1279
-
1280
  Please check:
1281
  1. GROQ_API_KEY is set in Space Settings β†’ Repository secrets
1282
  2. API key is valid (get one from https://console.groq.com/keys)
1283
  3. Space has been restarted after adding the key
1284
-
1285
  Current status: API key not found or invalid."""
1286
  return error_msg, None
1287
 
@@ -1305,17 +1303,14 @@ Current status: API key not found or invalid."""
1305
  return error_msg, None
1306
 
1307
  prompt = f"""You are a Class-12 {subject.title()} teacher creating MCQs.
1308
-
1309
  Topic: "{topic}"
1310
  Chapter: "{chapter}"
1311
-
1312
  Reference material from textbook:
1313
  {context[:1500]}
1314
 
1315
  Generate exactly {num_questions} multiple-choice questions based on the reference material.
1316
 
1317
  FORMAT (follow EXACTLY):
1318
-
1319
  Q1. [Question based on material]
1320
  A) [Option 1]
1321
  B) [Option 2]
@@ -1372,12 +1367,10 @@ Generate {num_questions} MCQs now:"""
1372
 
1373
  except Exception as e:
1374
  error_msg = f"""Error calling Groq API: {str(e)}
1375
-
1376
  Possible causes:
1377
  1. Rate limit exceeded (wait a moment)
1378
  2. Invalid API key
1379
  3. Network issue
1380
-
1381
  Please try again in a few seconds."""
1382
  print(f"❌ Groq API Error: {e}")
1383
  return error_msg, chapter
@@ -1388,8 +1381,9 @@ def clean_mcq_output(text):
1388
 
1389
  for line in lines:
1390
  line = line.strip()
 
1391
  if (re.match(r'^Q\d+\.', line) or
1392
- line.startswith(('A)', 'B)', 'C)', 'D)', 'Answer:', 'Correct Answer:')) or
1393
  not line):
1394
 
1395
  if line.startswith('Correct Answer:'):
@@ -1399,30 +1393,23 @@ def clean_mcq_output(text):
1399
 
1400
  return '\n'.join(cleaned_lines)
1401
 
1402
- # ------------------------------
1403
  # HTML UI
1404
- # ------------------------------
 
1405
  HTML_TEMPLATE = """
1406
  <!DOCTYPE html>
1407
- <html lang="en">
1408
  <head>
1409
- <meta charset="UTF-8">
1410
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
1411
  <title>Class 12 PCB MCQ Generator</title>
1412
  <style>
1413
- * {
1414
- margin: 0;
1415
- padding: 0;
1416
- box-sizing: border-box;
1417
- }
1418
-
1419
  body {
1420
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
1421
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1422
  min-height: 100vh;
1423
  padding: 20px;
1424
  }
1425
-
1426
  .container {
1427
  max-width: 900px;
1428
  margin: 0 auto;
@@ -1431,146 +1418,120 @@ HTML_TEMPLATE = """
1431
  box-shadow: 0 20px 60px rgba(0,0,0,0.3);
1432
  overflow: hidden;
1433
  }
1434
-
1435
  .header {
1436
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1437
  color: white;
1438
  padding: 30px;
1439
  text-align: center;
1440
  }
1441
-
1442
- .header h1 {
1443
- font-size: 2.5em;
1444
- margin-bottom: 10px;
1445
- }
1446
-
1447
- .header p {
1448
- opacity: 0.9;
1449
- font-size: 1.1em;
1450
- }
1451
-
1452
- .subject-tabs {
1453
  display: flex;
1454
- background: #f8f9fa;
1455
- border-bottom: 2px solid #e9ecef;
1456
- }
1457
-
1458
- .subject-tab {
1459
- flex: 1;
1460
- padding: 20px;
1461
- text-align: center;
1462
- cursor: pointer;
1463
- background: white;
1464
- border: none;
1465
- font-size: 1.1em;
1466
- font-weight: 600;
1467
- color: #6c757d;
1468
- transition: all 0.3s;
1469
  }
1470
-
1471
- .subject-tab:hover {
1472
- background: #f8f9fa;
1473
  }
1474
-
1475
- .subject-tab.active {
1476
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1477
- color: white;
1478
- }
1479
-
1480
- .form-section {
1481
- padding: 40px;
1482
  }
1483
-
1484
- .form-group {
1485
- margin-bottom: 25px;
1486
  }
1487
-
1488
- .form-group label {
1489
  display: block;
1490
- margin-bottom: 10px;
1491
  font-weight: 600;
 
1492
  color: #333;
1493
- font-size: 1.1em;
1494
  }
1495
-
1496
- .form-group input {
1497
  width: 100%;
1498
  padding: 15px;
1499
- border: 2px solid #e9ecef;
1500
  border-radius: 10px;
1501
- font-size: 1em;
1502
- transition: border 0.3s;
 
1503
  }
1504
-
1505
- .form-group input:focus {
1506
  outline: none;
1507
  border-color: #667eea;
1508
  }
1509
-
1510
- /* Updated layout for topic and MCQ count */
1511
- .topic-mcq-row {
1512
- display: flex;
1513
- gap: 15px;
1514
- align-items: flex-end;
1515
- }
1516
-
1517
- .topic-input-wrapper {
1518
- flex: 1;
1519
- }
1520
-
1521
- .mcq-count-wrapper {
1522
- width: 120px;
1523
- }
1524
-
1525
- .form-group select {
1526
- width: 100%;
1527
- padding: 12px;
1528
- border: 2px solid #e9ecef;
1529
- border-radius: 10px;
1530
- font-size: 0.95em;
1531
- background: white;
1532
- cursor: pointer;
1533
- transition: border 0.3s;
1534
- }
1535
-
1536
- .form-group select:focus {
1537
- outline: none;
1538
- border-color: #667eea;
1539
- }
1540
-
1541
- .generate-btn {
1542
  width: 100%;
1543
  padding: 18px;
1544
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1545
  color: white;
1546
  border: none;
1547
  border-radius: 10px;
1548
- font-size: 1.2em;
1549
  font-weight: 600;
1550
  cursor: pointer;
1551
- transition: transform 0.2s, box-shadow 0.3s;
1552
  }
1553
-
1554
- .generate-btn:hover {
1555
  transform: translateY(-2px);
1556
- box-shadow: 0 10px 25px rgba(102, 126, 234, 0.4);
1557
  }
1558
-
1559
- .generate-btn:active {
1560
- transform: translateY(0);
 
1561
  }
1562
-
1563
- .loading {
 
 
 
 
1564
  display: none;
1565
- text-align: center;
1566
- padding: 30px;
 
1567
  color: #667eea;
 
 
1568
  }
1569
-
1570
- .loading.show {
1571
- display: block;
 
 
 
 
 
 
 
1572
  }
1573
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1574
  .spinner {
1575
  border: 4px solid #f3f3f3;
1576
  border-top: 4px solid #667eea;
@@ -1578,60 +1539,30 @@ HTML_TEMPLATE = """
1578
  width: 50px;
1579
  height: 50px;
1580
  animation: spin 1s linear infinite;
1581
- margin: 0 auto 20px;
1582
  }
1583
-
1584
  @keyframes spin {
1585
  0% { transform: rotate(0deg); }
1586
  100% { transform: rotate(360deg); }
1587
  }
1588
-
1589
- .result-section {
1590
- display: none;
1591
- padding: 40px;
1592
- background: #f8f9fa;
1593
- }
1594
-
1595
- .result-section.show {
1596
- display: block;
1597
- }
1598
-
1599
- .chapter-badge {
1600
  display: inline-block;
1601
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1602
- color: white;
1603
- padding: 10px 20px;
1604
- border-radius: 25px;
1605
- margin-bottom: 20px;
1606
  font-weight: 600;
 
1607
  }
1608
-
1609
- .mcq-output {
1610
- background: white;
1611
- padding: 30px;
 
 
 
1612
  border-radius: 15px;
1613
- box-shadow: 0 5px 15px rgba(0,0,0,0.1);
1614
- white-space: pre-wrap;
1615
- font-family: 'Courier New', monospace;
1616
- line-height: 1.8;
1617
- }
1618
-
1619
- .error-message {
1620
- background: #f8d7da;
1621
- color: #721c24;
1622
- padding: 20px;
1623
- border-radius: 10px;
1624
- border-left: 4px solid #f5c6cb;
1625
- margin: 20px 0;
1626
- }
1627
-
1628
- .info-badge {
1629
- background: #d1ecf1;
1630
- color: #0c5460;
1631
- padding: 15px;
1632
- border-radius: 10px;
1633
- margin-top: 20px;
1634
- text-align: center;
1635
  }
1636
  </style>
1637
  </head>
@@ -1639,169 +1570,151 @@ HTML_TEMPLATE = """
1639
  <div class="container">
1640
  <div class="header">
1641
  <h1>πŸŽ“ Class 12 PCB MCQ Generator</h1>
1642
- <p>Generate practice MCQs from your textbooks ⚑ Llama 3.3 70B</p>
1643
- </div>
1644
-
1645
- <div class="subject-tabs">
1646
- <button class="subject-tab active" data-subject="biology">Biology</button>
1647
- <button class="subject-tab" data-subject="chemistry">Chemistry</button>
1648
- <button class="subject-tab" data-subject="physics">Physics</button>
 
 
1649
  </div>
1650
-
1651
- <div class="form-section">
1652
- <form id="mcqForm">
 
 
 
 
 
 
 
 
 
1653
  <div class="form-group">
1654
- <label>πŸ“š Select Subject</label>
1655
- <select id="subject">
1656
- <option value="biology">Biology</option>
1657
- <option value="chemistry">Chemistry</option>
1658
- <option value="physics">Physics</option>
1659
- </select>
1660
  </div>
1661
-
1662
  <div class="form-group">
1663
- <label>✏️ Enter Topic</label>
1664
- <div class="topic-mcq-row">
1665
- <div class="topic-input-wrapper">
1666
- <input type="text" id="topic" placeholder="e.g., Photosynthesis, Electrolysis, Projectile Motion" required>
1667
- </div>
1668
- <div class="mcq-count-wrapper">
1669
- <label style="font-size: 0.9em; margin-bottom: 5px;">πŸ”’ MCQs</label>
1670
- <select id="numQuestions">
1671
- <option value="1">1 MCQ</option>
1672
- <option value="2">2 MCQs</option>
1673
- <option value="3">3 MCQs</option>
1674
- <option value="4">4 MCQs</option>
1675
- <option value="5" selected>5 MCQs</option>
1676
- <option value="6">6 MCQs</option>
1677
- <option value="7">7 MCQs</option>
1678
- <option value="8">8 MCQs</option>
1679
- <option value="9">9 MCQs</option>
1680
- <option value="10">10 MCQs</option>
1681
- <option value="11">11 MCQs</option>
1682
- <option value="12">12 MCQs</option>
1683
- <option value="13">13 MCQs</option>
1684
- <option value="14">14 MCQs</option>
1685
- <option value="15">15 MCQs</option>
1686
- <option value="16">16 MCQs</option>
1687
- <option value="17">17 MCQs</option>
1688
- <option value="18">18 MCQs</option>
1689
- <option value="19">19 MCQs</option>
1690
- <option value="20">20 MCQs</option>
1691
- </select>
1692
- </div>
1693
- </div>
1694
  </div>
1695
-
1696
- <button type="submit" class="generate-btn">πŸš€ Generate MCQs</button>
1697
- </form>
1698
-
1699
  <div class="loading" id="loading">
1700
  <div class="spinner"></div>
1701
- <p><strong>Generating MCQs with AI...</strong></p>
1702
- <p>⚑ Detecting chapter from textbook...</p>
1703
  </div>
1704
- </div>
1705
-
1706
- <div class="result-section" id="resultSection">
1707
- <h2>πŸ“ Generated MCQs:</h2>
1708
- <div id="chapterInfo"></div>
1709
- <div class="mcq-output" id="mcqOutput"></div>
1710
- <div class="info-badge">
1711
- βœ“ High Quality: Generated by Llama 3.3 70B via Groq API
 
 
 
 
 
 
 
 
1712
  </div>
1713
  </div>
1714
  </div>
1715
-
1716
  <script>
1717
- // Subject tab switching
1718
- const tabs = document.querySelectorAll('.subject-tab');
1719
- const subjectSelect = document.getElementById('subject');
1720
-
1721
- tabs.forEach(tab => {
1722
- tab.addEventListener('click', () => {
1723
- tabs.forEach(t => t.classList.remove('active'));
1724
- tab.classList.add('active');
1725
-
1726
- const subject = tab.dataset.subject;
1727
- subjectSelect.value = subject;
1728
- });
1729
- });
1730
-
1731
- // Form submission
1732
- document.getElementById('mcqForm').addEventListener('submit', async (e) => {
1733
- e.preventDefault();
1734
-
1735
  const subject = document.getElementById('subject').value;
1736
- const topic = document.getElementById('topic').value;
1737
- const numQuestions = document.getElementById('numQuestions').value;
1738
-
1739
- // Show loading, hide results
1740
- document.getElementById('loading').classList.add('show');
1741
- document.getElementById('resultSection').classList.remove('show');
1742
-
 
 
 
 
 
 
 
 
 
 
 
 
1743
  try {
1744
  const response = await fetch('/generate', {
1745
  method: 'POST',
1746
- headers: {
1747
- 'Content-Type': 'application/json'
1748
- },
1749
- body: JSON.stringify({
1750
- subject: subject,
1751
- topic: topic,
1752
- num_questions: parseInt(numQuestions)
1753
- })
1754
  });
1755
-
1756
  const data = await response.json();
1757
-
1758
- // Hide loading
1759
- document.getElementById('loading').classList.remove('show');
1760
-
1761
- if (response.ok) {
1762
- // Show results
1763
- const chapterInfo = document.getElementById('chapterInfo');
1764
- if (data.chapter) {
1765
- chapterInfo.innerHTML = `<span class="chapter-badge">πŸ“– Chapter: ${data.chapter}</span>`;
1766
- } else {
1767
- chapterInfo.innerHTML = '';
1768
- }
1769
-
1770
- document.getElementById('mcqOutput').textContent = data.mcqs;
1771
- document.getElementById('resultSection').classList.add('show');
1772
-
1773
- // Scroll to results
1774
- document.getElementById('resultSection').scrollIntoView({ behavior: 'smooth' });
1775
- } else {
1776
- // Show error
1777
- document.getElementById('resultSection').innerHTML = `
1778
- <div class="error-message">
1779
- <strong>❌ Error:</strong><br>
1780
- ${data.error}
1781
- </div>
1782
- `;
1783
- document.getElementById('resultSection').classList.add('show');
1784
  }
1785
-
 
 
 
 
 
 
 
 
1786
  } catch (error) {
1787
- document.getElementById('loading').classList.remove('show');
1788
- document.getElementById('resultSection').innerHTML = `
1789
- <div class="error-message">
1790
- <strong>❌ Network Error:</strong><br>
1791
- ${error.message}
1792
- </div>
1793
- `;
1794
- document.getElementById('resultSection').classList.add('show');
 
 
 
1795
  }
1796
  });
1797
  </script>
1798
  </body>
1799
  </html>
1800
  """
1801
-
1802
- # ------------------------------
1803
  # Routes
1804
- # ------------------------------
1805
  @app.route("/")
1806
  def home():
1807
  return render_template_string(HTML_TEMPLATE)
@@ -1834,7 +1747,7 @@ def generate():
1834
  if not validate_topic_subject(topic, subject):
1835
  subject_names = {
1836
  "biology": "Biology",
1837
- "chemistry": "Chemistry",
1838
  "physics": "Physics"
1839
  }
1840
  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"
@@ -1859,11 +1772,11 @@ def generate():
1859
  return jsonify({"error": mcqs}), 400
1860
 
1861
  return jsonify({
1862
- "mcqs": mcqs,
1863
  "subject": subject,
1864
  "chapter": chapter
1865
  })
1866
-
1867
  except Exception as e:
1868
  print(f"❌ Error: {e}")
1869
  import traceback
@@ -1878,10 +1791,11 @@ def health():
1878
  "cache_size": len(MCQ_CACHE)
1879
  })
1880
 
1881
- # ------------------------------
1882
  # Run
1883
- # ------------------------------
1884
  if __name__ == "__main__":
1885
  port = int(os.environ.get("PORT", 7860))
1886
  print(f"\nπŸš€ Starting server on port {port}...\n")
1887
- app.run(host="0.0.0.0", port=port, debug=False)
 
 
916
  print("STARTING MCQ GENERATOR APP")
917
  print("=" * 50)
918
 
919
+ # ------------------------------
920
  # Chapter Names (Actual Textbook Chapters)
921
+ # ------------------------------
922
  CHAPTER_NAMES = {
923
  "biology": [
924
  "Reproduction in Lower and Higher Plants",
 
975
  ]
976
  }
977
 
978
+ # ------------------------------
979
  # Initialize Groq API Client
980
+ # ------------------------------
981
  print("\nStep 1: Checking Groq API Key...")
982
  print("-" * 50)
983
 
 
995
  else:
996
  print(f"βœ“ GROQ_API_KEY found ({len(GROQ_API_KEY)} chars)")
997
  print(f" First 20 chars: {GROQ_API_KEY[:20]}...")
998
+
999
  try:
1000
  groq_client = Groq(api_key=GROQ_API_KEY)
1001
+
1002
  # Test the API
1003
  print(" Testing API connection...")
1004
  test = groq_client.chat.completions.create(
 
1007
  max_tokens=5
1008
  )
1009
  print("βœ“ Groq API working!")
1010
+
1011
  except Exception as e:
1012
  print(f"❌ Groq API initialization failed:")
1013
  print(f" Error: {str(e)}")
 
1015
 
1016
  print("-" * 50)
1017
 
1018
+ # ------------------------------
1019
  # Load embedding model (CPU)
1020
+ # ------------------------------
1021
  print("\nStep 2: Loading embedding model...")
1022
  embed_model = SentenceTransformer("all-MiniLM-L6-v2")
1023
  print("βœ“ Embedding model loaded")
1024
 
1025
+ # ------------------------------
1026
  # Download files from Hugging Face
1027
+ # ------------------------------
1028
  REPO_ID = "Redfire-1234/pcb_tutor"
1029
 
1030
  print("\nStep 3: Downloading subject files...")
 
1047
 
1048
  # Load all subjects into memory
1049
  print("\nStep 4: Loading subject data into memory...")
 
1050
  SUBJECTS = {
1051
  "biology": {
1052
  "chunks": pickle.load(open(bio_chunks_path, "rb")),
 
1070
  print("βœ“ ALL SYSTEMS READY!")
1071
  print("=" * 50 + "\n")
1072
 
1073
+ # ------------------------------
1074
  # Caching
1075
+ # ------------------------------
1076
  MCQ_CACHE = {}
1077
  MAX_CACHE_SIZE = 100
1078
 
 
1084
  MCQ_CACHE.pop(next(iter(MCQ_CACHE)))
1085
  MCQ_CACHE[key] = mcqs
1086
 
1087
+ # ------------------------------
1088
  # RAG Search
1089
+ # ------------------------------
1090
  def rag_search(query, subject, k=5):
1091
  if subject not in SUBJECTS:
1092
  return None
 
1104
 
1105
  return "\n\n".join(results)
1106
 
1107
+ # ------------------------------
1108
  # Topic Validation (Check if topic belongs to subject)
1109
+ # ------------------------------
1110
  def validate_topic_subject(topic, subject):
1111
  """
1112
  Validate if the topic belongs to the selected subject using LLM
 
1115
  if not groq_client:
1116
  return True # Skip validation if API not available
1117
 
1118
+ validation_prompt = f"""You are a Class 12 PCB subject expert. Determine if the following topic belongs to {subject.title()}.
 
 
1119
 
1120
  Topic: "{topic}"
1121
  Subject: {subject.title()}
 
1159
  print(f"⚠️ Validation failed: {e}")
1160
  return True # Allow on error to avoid blocking
1161
 
1162
+ # ------------------------------
1163
  # Chapter Detection (Using Actual Chapter Names)
1164
+ # ------------------------------
1165
  def detect_chapter_from_list(context, topic, subject):
1166
  """
1167
  Detect chapter using the actual chapter list by matching keywords
 
1269
  print(f"⚠️ Chapter detection failed: {e}")
1270
  return None
1271
 
1272
+ # ------------------------------
1273
  # MCQ Generation
1274
+ # ------------------------------
1275
  def generate_mcqs(context, topic, subject, num_questions=5):
1276
  # Check if Groq is available
1277
  if not groq_client:
1278
  error_msg = """ERROR: Groq API not initialized!
 
1279
  Please check:
1280
  1. GROQ_API_KEY is set in Space Settings β†’ Repository secrets
1281
  2. API key is valid (get one from https://console.groq.com/keys)
1282
  3. Space has been restarted after adding the key
 
1283
  Current status: API key not found or invalid."""
1284
  return error_msg, None
1285
 
 
1303
  return error_msg, None
1304
 
1305
  prompt = f"""You are a Class-12 {subject.title()} teacher creating MCQs.
 
1306
  Topic: "{topic}"
1307
  Chapter: "{chapter}"
 
1308
  Reference material from textbook:
1309
  {context[:1500]}
1310
 
1311
  Generate exactly {num_questions} multiple-choice questions based on the reference material.
1312
 
1313
  FORMAT (follow EXACTLY):
 
1314
  Q1. [Question based on material]
1315
  A) [Option 1]
1316
  B) [Option 2]
 
1367
 
1368
  except Exception as e:
1369
  error_msg = f"""Error calling Groq API: {str(e)}
 
1370
  Possible causes:
1371
  1. Rate limit exceeded (wait a moment)
1372
  2. Invalid API key
1373
  3. Network issue
 
1374
  Please try again in a few seconds."""
1375
  print(f"❌ Groq API Error: {e}")
1376
  return error_msg, chapter
 
1381
 
1382
  for line in lines:
1383
  line = line.strip()
1384
+
1385
  if (re.match(r'^Q\d+\.', line) or
1386
+ line.startswith(('A)', 'B)', 'C)', 'D)', 'Answer:', 'Correct Answer:')) or
1387
  not line):
1388
 
1389
  if line.startswith('Correct Answer:'):
 
1393
 
1394
  return '\n'.join(cleaned_lines)
1395
 
1396
+ # ------------------------------
1397
  # HTML UI
1398
+ # ------------------------------
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;
 
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
+ .form-row {
 
 
 
 
 
 
 
 
1431
  display: flex;
1432
+ gap: 15px;
1433
+ margin-bottom: 25px;
 
 
 
 
 
 
 
 
 
 
 
 
 
1434
  }
1435
+ .form-row .form-group {
1436
+ margin-bottom: 0;
 
1437
  }
1438
+ .form-row .form-group:first-child {
1439
+ flex: 2;
 
 
 
 
 
 
1440
  }
1441
+ .form-row .form-group:last-child {
1442
+ flex: 1;
1443
+ min-width: 140px;
1444
  }
1445
+ label {
 
1446
  display: block;
 
1447
  font-weight: 600;
1448
+ margin-bottom: 10px;
1449
  color: #333;
1450
+ font-size: 16px;
1451
  }
1452
+ select, input {
 
1453
  width: 100%;
1454
  padding: 15px;
1455
+ border: 2px solid #e0e0e0;
1456
  border-radius: 10px;
1457
+ font-size: 16px;
1458
+ font-family: inherit;
1459
+ transition: border-color 0.3s;
1460
  }
1461
+ select:focus, input:focus {
 
1462
  outline: none;
1463
  border-color: #667eea;
1464
  }
1465
+ button {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1466
  width: 100%;
1467
  padding: 18px;
1468
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1469
  color: white;
1470
  border: none;
1471
  border-radius: 10px;
1472
+ font-size: 18px;
1473
  font-weight: 600;
1474
  cursor: pointer;
1475
+ transition: all 0.3s;
1476
  }
1477
+ button:hover {
 
1478
  transform: translateY(-2px);
1479
+ box-shadow: 0 10px 20px rgba(102, 126, 234, 0.4);
1480
  }
1481
+ button:disabled {
1482
+ background: #ccc;
1483
+ cursor: not-allowed;
1484
+ transform: none;
1485
  }
1486
+ .result {
1487
+ margin-top: 30px;
1488
+ padding: 25px;
1489
+ background: #f8f9fa;
1490
+ border-radius: 10px;
1491
+ border-left: 4px solid #667eea;
1492
  display: none;
1493
+ }
1494
+ .result.show { display: block; }
1495
+ .result h3 {
1496
  color: #667eea;
1497
+ margin-bottom: 20px;
1498
+ font-size: 1.4em;
1499
  }
1500
+ .chapter-info {
1501
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1502
+ color: white;
1503
+ padding: 15px 20px;
1504
+ border-radius: 8px;
1505
+ margin-bottom: 20px;
1506
+ display: flex;
1507
+ align-items: center;
1508
+ gap: 10px;
1509
+ font-size: 16px;
1510
  }
1511
+ .chapter-icon {
1512
+ font-size: 24px;
1513
+ }
1514
+ .chapter-text {
1515
+ flex: 1;
1516
+ }
1517
+ .chapter-name {
1518
+ font-weight: 700;
1519
+ font-size: 18px;
1520
+ }
1521
+ .mcq-content {
1522
+ background: white;
1523
+ padding: 25px;
1524
+ border-radius: 8px;
1525
+ white-space: pre-wrap;
1526
+ line-height: 1.9;
1527
+ font-size: 15px;
1528
+ }
1529
+ .loading {
1530
+ text-align: center;
1531
+ padding: 30px;
1532
+ display: none;
1533
+ }
1534
+ .loading.show { display: block; }
1535
  .spinner {
1536
  border: 4px solid #f3f3f3;
1537
  border-top: 4px solid #667eea;
 
1539
  width: 50px;
1540
  height: 50px;
1541
  animation: spin 1s linear infinite;
1542
+ margin: 0 auto 15px;
1543
  }
 
1544
  @keyframes spin {
1545
  0% { transform: rotate(0deg); }
1546
  100% { transform: rotate(360deg); }
1547
  }
1548
+ .subject-tag {
 
 
 
 
 
 
 
 
 
 
 
1549
  display: inline-block;
1550
+ padding: 5px 15px;
1551
+ border-radius: 20px;
1552
+ font-size: 13px;
 
 
1553
  font-weight: 600;
1554
+ margin-right: 10px;
1555
  }
1556
+ .bio { background: #d4edda; color: #155724; }
1557
+ .chem { background: #d1ecf1; color: #0c5460; }
1558
+ .phy { background: #f8d7da; color: #721c24; }
1559
+ .api-badge {
1560
+ background: #17a2b8;
1561
+ color: white;
1562
+ padding: 5px 12px;
1563
  border-radius: 15px;
1564
+ font-size: 12px;
1565
+ margin-left: 10px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1566
  }
1567
  </style>
1568
  </head>
 
1570
  <div class="container">
1571
  <div class="header">
1572
  <h1>πŸŽ“ Class 12 PCB MCQ Generator</h1>
1573
+ <p style="font-size: 1.1em; margin-bottom: 15px;">
1574
+ Generate practice MCQs from your textbooks
1575
+ <span class="api-badge">⚑ Llama 3.3 70B</span>
1576
+ </p>
1577
+ <div>
1578
+ <span class="subject-tag bio">Biology</span>
1579
+ <span class="subject-tag chem">Chemistry</span>
1580
+ <span class="subject-tag phy">Physics</span>
1581
+ </div>
1582
  </div>
1583
+
1584
+ <div class="content">
1585
+ <div class="form-group">
1586
+ <label for="subject">πŸ“š Select Subject</label>
1587
+ <select id="subject">
1588
+ <option value="biology">Biology</option>
1589
+ <option value="chemistry">Chemistry</option>
1590
+ <option value="physics">Physics</option>
1591
+ </select>
1592
+ </div>
1593
+
1594
+ <div class="form-row">
1595
  <div class="form-group">
1596
+ <label for="topic">✏️ Enter Topic</label>
1597
+ <input type="text" id="topic" placeholder="e.g., Mitochondria, Chemical Bonding, Newton's Laws">
 
 
 
 
1598
  </div>
1599
+
1600
  <div class="form-group">
1601
+ <label for="numQuestions">πŸ”’ MCQs</label>
1602
+ <select id="numQuestions">
1603
+ <option value="1">1</option>
1604
+ <option value="2">2</option>
1605
+ <option value="3">3</option>
1606
+ <option value="4">4</option>
1607
+ <option value="5" selected>5</option>
1608
+ <option value="6">6</option>
1609
+ <option value="7">7</option>
1610
+ <option value="8">8</option>
1611
+ <option value="9">9</option>
1612
+ <option value="10">10</option>
1613
+ <option value="11">11</option>
1614
+ <option value="12">12</option>
1615
+ <option value="13">13</option>
1616
+ <option value="14">14</option>
1617
+ <option value="15">15</option>
1618
+ <option value="16">16</option>
1619
+ <option value="17">17</option>
1620
+ <option value="18">18</option>
1621
+ <option value="19">19</option>
1622
+ <option value="20">20</option>
1623
+ </select>
 
 
 
 
 
 
 
 
1624
  </div>
1625
+ </div>
1626
+
1627
+ <button onclick="generateMCQs()">πŸš€ Generate MCQs</button>
1628
+
1629
  <div class="loading" id="loading">
1630
  <div class="spinner"></div>
1631
+ <p style="color: #666; font-size: 16px;">Generating MCQs with AI...</p>
1632
+ <p style="color: #999; font-size: 13px; margin-top: 10px;">⚑ Detecting chapter from textbook...</p>
1633
  </div>
1634
+
1635
+ <div class="result" id="result">
1636
+ <h3>πŸ“ Generated MCQs:</h3>
1637
+
1638
+ <div class="chapter-info" id="chapterInfo" style="display: none;">
1639
+ <span class="chapter-icon">πŸ“–</span>
1640
+ <div class="chapter-text">
1641
+ <div style="font-size: 13px; opacity: 0.9;">Chapter:</div>
1642
+ <div class="chapter-name" id="chapterName"></div>
1643
+ </div>
1644
+ </div>
1645
+
1646
+ <div style="background: #d4edda; padding: 12px; border-radius: 6px; margin-bottom: 15px; color: #155724; font-size: 14px;">
1647
+ βœ“ <strong>High Quality:</strong> Generated by Llama 3.3 70B via Groq API
1648
+ </div>
1649
+ <div class="mcq-content" id="mcqContent"></div>
1650
  </div>
1651
  </div>
1652
  </div>
 
1653
  <script>
1654
+ async function generateMCQs() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1655
  const subject = document.getElementById('subject').value;
1656
+ const topic = document.getElementById('topic').value.trim();
1657
+ const numQuestions = parseInt(document.getElementById('numQuestions').value);
1658
+
1659
+ if (!topic) {
1660
+ alert('⚠️ Please enter a topic!');
1661
+ return;
1662
+ }
1663
+
1664
+ const loading = document.getElementById('loading');
1665
+ const result = document.getElementById('result');
1666
+ const btn = document.querySelector('button');
1667
+ const chapterInfo = document.getElementById('chapterInfo');
1668
+
1669
+ loading.classList.add('show');
1670
+ result.classList.remove('show');
1671
+ chapterInfo.style.display = 'none';
1672
+ btn.disabled = true;
1673
+ btn.textContent = '⏳ Generating...';
1674
+
1675
  try {
1676
  const response = await fetch('/generate', {
1677
  method: 'POST',
1678
+ headers: {'Content-Type': 'application/json'},
1679
+ body: JSON.stringify({subject, topic, num_questions: numQuestions})
 
 
 
 
 
 
1680
  });
1681
+
1682
  const data = await response.json();
1683
+
1684
+ if (data.error) {
1685
+ alert('❌ Error: ' + data.error);
1686
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1687
  }
1688
+
1689
+ // Display chapter info
1690
+ if (data.chapter && data.chapter !== 'Unknown Chapter') {
1691
+ document.getElementById('chapterName').textContent = data.chapter;
1692
+ chapterInfo.style.display = 'flex';
1693
+ }
1694
+
1695
+ document.getElementById('mcqContent').textContent = data.mcqs;
1696
+ result.classList.add('show');
1697
  } catch (error) {
1698
+ alert('❌ Error: ' + error.message);
1699
+ } finally {
1700
+ loading.classList.remove('show');
1701
+ btn.disabled = false;
1702
+ btn.textContent = 'πŸš€ Generate MCQs';
1703
+ }
1704
+ }
1705
+
1706
+ document.getElementById('topic').addEventListener('keypress', function(e) {
1707
+ if (e.key === 'Enter') {
1708
+ generateMCQs();
1709
  }
1710
  });
1711
  </script>
1712
  </body>
1713
  </html>
1714
  """
1715
+ # ------------------------------
 
1716
  # Routes
1717
+ # ------------------------------
1718
  @app.route("/")
1719
  def home():
1720
  return render_template_string(HTML_TEMPLATE)
 
1747
  if not validate_topic_subject(topic, subject):
1748
  subject_names = {
1749
  "biology": "Biology",
1750
+ "chemistry": "Chemistry",
1751
  "physics": "Physics"
1752
  }
1753
  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"
 
1772
  return jsonify({"error": mcqs}), 400
1773
 
1774
  return jsonify({
1775
+ "mcqs": mcqs,
1776
  "subject": subject,
1777
  "chapter": chapter
1778
  })
1779
+
1780
  except Exception as e:
1781
  print(f"❌ Error: {e}")
1782
  import traceback
 
1791
  "cache_size": len(MCQ_CACHE)
1792
  })
1793
 
1794
+ # ------------------------------
1795
  # Run
1796
+ # ------------------------------
1797
  if __name__ == "__main__":
1798
  port = int(os.environ.get("PORT", 7860))
1799
  print(f"\nπŸš€ Starting server on port {port}...\n")
1800
+ app.run(host="0.0.0.0", port=port, debug=False)
1801
+