| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0" /> |
|
|
| <title>Smart MCQ Solver</title> |
|
|
| <style> |
| * { |
| box-sizing: border-box; |
| } |
| |
| body { |
| margin: 0; |
| padding: 30px 15px; |
| font-family: Arial, sans-serif; |
| background: linear-gradient(135deg, #eef2ff, #f8fafc); |
| color: #172033; |
| } |
| |
| .container { |
| width: 100%; |
| max-width: 850px; |
| margin: auto; |
| padding: 30px; |
| background: white; |
| border-radius: 18px; |
| box-shadow: 0 12px 35px rgba(0, 0, 0, 0.1); |
| } |
| |
| h1 { |
| margin-top: 0; |
| text-align: center; |
| color: #4338ca; |
| } |
| |
| .description { |
| text-align: center; |
| color: #64748b; |
| margin-bottom: 28px; |
| } |
| |
| label { |
| display: block; |
| margin-top: 15px; |
| margin-bottom: 6px; |
| font-weight: bold; |
| } |
| |
| textarea, |
| input { |
| width: 100%; |
| padding: 12px; |
| border: 1px solid #cbd5e1; |
| border-radius: 9px; |
| font-size: 15px; |
| } |
| |
| textarea { |
| min-height: 100px; |
| resize: vertical; |
| } |
| |
| .options { |
| display: grid; |
| grid-template-columns: 1fr 1fr; |
| gap: 12px; |
| } |
| |
| button { |
| width: 100%; |
| margin-top: 24px; |
| padding: 14px; |
| border: none; |
| border-radius: 9px; |
| background: #4f46e5; |
| color: white; |
| font-size: 17px; |
| font-weight: bold; |
| cursor: pointer; |
| } |
| |
| button:hover { |
| background: #3730a3; |
| } |
| |
| button:disabled { |
| background: #94a3b8; |
| cursor: wait; |
| } |
| |
| #status { |
| margin-top: 18px; |
| padding: 12px; |
| text-align: center; |
| background: #f1f5f9; |
| border-radius: 9px; |
| color: #475569; |
| } |
| |
| #result { |
| display: none; |
| margin-top: 20px; |
| padding: 20px; |
| border: 2px solid #c7d2fe; |
| border-radius: 12px; |
| background: #eef2ff; |
| } |
| |
| .prediction { |
| text-align: center; |
| font-size: 25px; |
| font-weight: bold; |
| color: #4338ca; |
| margin-bottom: 18px; |
| } |
| |
| .rank { |
| margin-top: 9px; |
| padding: 11px; |
| background: white; |
| border-radius: 8px; |
| } |
| |
| .note { |
| margin-top: 25px; |
| font-size: 13px; |
| color: #64748b; |
| text-align: center; |
| } |
| |
| @media (max-width: 600px) { |
| .options { |
| grid-template-columns: 1fr; |
| } |
| |
| .container { |
| padding: 20px; |
| } |
| } |
| </style> |
| </head> |
|
|
| <body> |
| <main class="container"> |
| <h1>🧠 Smart MCQ Solver</h1> |
|
|
| <p class="description"> |
| Rank five answer options using MiniLM sentence embeddings |
| and semantic similarity. |
| </p> |
|
|
| <label for="question">Question</label> |
| <textarea |
| id="question" |
| placeholder="Enter your multiple-choice question" |
| ></textarea> |
|
|
| <div class="options"> |
| <div> |
| <label for="optionA">Option A</label> |
| <input id="optionA" placeholder="Enter option A" /> |
| </div> |
|
|
| <div> |
| <label for="optionB">Option B</label> |
| <input id="optionB" placeholder="Enter option B" /> |
| </div> |
|
|
| <div> |
| <label for="optionC">Option C</label> |
| <input id="optionC" placeholder="Enter option C" /> |
| </div> |
|
|
| <div> |
| <label for="optionD">Option D</label> |
| <input id="optionD" placeholder="Enter option D" /> |
| </div> |
|
|
| <div> |
| <label for="optionE">Option E</label> |
| <input id="optionE" placeholder="Enter option E" /> |
| </div> |
| </div> |
|
|
| <button id="solveButton">Rank Answers</button> |
|
|
| <div id="status"> |
| The AI model will download when you make the first prediction. |
| </div> |
|
|
| <section id="result"> |
| <div id="prediction" class="prediction"></div> |
| <div id="rankings"></div> |
| </section> |
|
|
| <p class="note"> |
| This demonstration uses the MiniLM component from the Smart MCQ |
| Solver Kaggle project. |
| </p> |
| </main> |
|
|
| <script type="module"> |
| import { pipeline } from |
| "https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.8.1"; |
| |
| const MODEL_ID = |
| "onnx-community/all-MiniLM-L6-v2-ONNX"; |
| |
| const labels = ["A", "B", "C", "D", "E"]; |
| |
| const button = document.getElementById("solveButton"); |
| const status = document.getElementById("status"); |
| const result = document.getElementById("result"); |
| const prediction = document.getElementById("prediction"); |
| const rankings = document.getElementById("rankings"); |
| |
| let extractor = null; |
| |
| function getValue(id) { |
| return document.getElementById(id).value.trim(); |
| } |
| |
| function escapeHTML(value) { |
| const element = document.createElement("div"); |
| element.textContent = value; |
| return element.innerHTML; |
| } |
| |
| function dotProduct(first, second) { |
| return first.reduce( |
| (total, value, index) => |
| total + value * second[index], |
| 0 |
| ); |
| } |
| |
| button.addEventListener("click", async () => { |
| const question = getValue("question"); |
| |
| const options = [ |
| getValue("optionA"), |
| getValue("optionB"), |
| getValue("optionC"), |
| getValue("optionD"), |
| getValue("optionE") |
| ]; |
| |
| if (!question) { |
| status.textContent = "Please enter a question."; |
| return; |
| } |
| |
| if (options.some(option => !option)) { |
| status.textContent = "Please enter all five options."; |
| return; |
| } |
| |
| button.disabled = true; |
| result.style.display = "none"; |
| |
| try { |
| if (!extractor) { |
| status.textContent = |
| "Downloading the MiniLM model. Please wait..."; |
| |
| extractor = await pipeline( |
| "feature-extraction", |
| MODEL_ID, |
| { |
| dtype: "q4", |
| progress_callback: event => { |
| if ( |
| event.status === "progress" && |
| event.progress !== undefined |
| ) { |
| status.textContent = |
| `Downloading model: ${Math.round( |
| event.progress |
| )}%`; |
| } |
| } |
| } |
| ); |
| } |
| |
| status.textContent = "Analysing the answer options..."; |
| |
| const output = await extractor( |
| [question, ...options], |
| { |
| pooling: "mean", |
| normalize: true |
| } |
| ); |
| |
| const vectors = output.tolist(); |
| const questionVector = vectors[0]; |
| |
| const scores = options.map( |
| (_, index) => |
| dotProduct( |
| questionVector, |
| vectors[index + 1] |
| ) |
| ); |
| |
| const rankedIndices = [0, 1, 2, 3, 4].sort( |
| (first, second) => |
| scores[second] - scores[first] |
| ); |
| |
| const topThree = rankedIndices |
| .slice(0, 3) |
| .map(index => labels[index]) |
| .join(" "); |
| |
| prediction.textContent = |
| `Top-3 Prediction: ${topThree}`; |
| |
| rankings.innerHTML = rankedIndices |
| .map( |
| (index, position) => ` |
| <div class="rank"> |
| <strong> |
| ${position + 1}. Option ${labels[index]} |
| </strong> |
| — ${escapeHTML(options[index])} |
| <br> |
| Similarity score: ${scores[index].toFixed(4)} |
| </div> |
| ` |
| ) |
| .join(""); |
| |
| result.style.display = "block"; |
| status.textContent = |
| "Prediction completed successfully."; |
| } catch (error) { |
| console.error(error); |
| |
| status.textContent = |
| "The model could not load. Refresh the page and try again."; |
| } finally { |
| button.disabled = false; |
| } |
| }); |
| </script> |
| </body> |
| </html> |