| "use strict"; |
|
|
| const DATASET_URL = "https://huggingface.co/datasets/Max00035/ml-systems-interview-bench/resolve/main/data/questions.jsonl"; |
| const ALL = "All"; |
| const COMPARISON_MARKERS = ["tradeoff", "trade off", "however", "whereas", "depends on", "compared with", "compared to", "on the other hand", "rather than", "versus"]; |
| const TRADEOFF_DOMAIN_SIGNALS = ["cost", "latency", "throughput", "risk", "complexity", "reliability", "accuracy", "memory"]; |
| const STRUCTURE_SIGNALS = ["first", "second", "finally", "because", "for example", "for instance", "therefore", "then"]; |
| const REQUIRED_FIELDS = ["question", "domain", "difficulty", "question_type", "expected_concepts", "reference_answer", "evaluation_rubric", "answer_dimensions", "max_score", "follow_up_questions"]; |
|
|
| const state = { questions: [], selectedQuestion: null }; |
| const elements = { |
| status: document.querySelector("#status"), domain: document.querySelector("#domain-filter"), |
| difficulty: document.querySelector("#difficulty-filter"), questionType: document.querySelector("#type-filter"), |
| randomButton: document.querySelector("#random-button"), questionCard: document.querySelector("#question-card"), |
| answer: document.querySelector("#answer-input"), evaluateButton: document.querySelector("#evaluate-button"), |
| clearButton: document.querySelector("#clear-button"), evaluationStatus: document.querySelector("#evaluation-status"), |
| scoreCard: document.querySelector("#score-card"), reviewCard: document.querySelector("#review-card"), |
| referenceAnswer: document.querySelector("#reference-answer"), rubricExcellent: document.querySelector("#rubric-excellent"), |
| rubricAcceptable: document.querySelector("#rubric-acceptable"), rubricWeak: document.querySelector("#rubric-weak"), |
| followUps: document.querySelector("#follow-ups"), |
| }; |
|
|
| function normalize(text) { |
| return String(text).toLowerCase().replace(/[-/]/g, " ").match(/[a-z0-9]+/g)?.join(" ") || ""; |
| } |
|
|
| function containsSignal(normalizedAnswer, signal) { |
| return ` ${normalizedAnswer} `.includes(` ${normalize(signal)} `); |
| } |
|
|
| function conceptIsDetected(concept, normalizedAnswer) { |
| const normalizedConcept = normalize(concept); |
| if (!normalizedConcept) return false; |
| if (containsSignal(normalizedAnswer, normalizedConcept)) return true; |
| const answerTokens = new Set(normalizedAnswer.split(" ")); |
| const meaningfulTokens = normalizedConcept.split(" ").filter((token) => token.length > 2); |
| return meaningfulTokens.length > 1 && meaningfulTokens.every((token) => answerTokens.has(token)); |
| } |
|
|
| function isValidQuestion(record) { |
| const rubric = record?.evaluation_rubric; |
| return record && REQUIRED_FIELDS.every((field) => Object.hasOwn(record, field)) |
| && Array.isArray(record.expected_concepts) && Array.isArray(record.answer_dimensions) |
| && Array.isArray(record.follow_up_questions) && record.follow_up_questions.length >= 2 |
| && rubric && typeof rubric === "object" |
| && ["excellent", "acceptable", "weak"].every((key) => Object.hasOwn(rubric, key)); |
| } |
|
|
| async function loadQuestions() { |
| try { |
| const response = await fetch(DATASET_URL, { headers: { Accept: "application/jsonl, application/json" } }); |
| if (!response.ok) throw new Error(`Dataset request returned HTTP ${response.status}.`); |
| const lines = (await response.text()).split(/\r?\n/).filter((line) => line.trim()); |
| const records = lines.map((line) => JSON.parse(line)); |
| if (!records.length || !records.every(isValidQuestion)) throw new Error("The dataset is empty or does not match the expected schema."); |
| state.questions = records; |
| populateSelect(elements.domain, uniqueValues(records, "domain"), ALL); |
| updateDependentFilters(); |
| [elements.domain, elements.difficulty, elements.questionType, elements.randomButton].forEach((element) => { element.disabled = false; }); |
| elements.status.className = "status"; |
| elements.status.textContent = `${records.length} questions ready. Choose filters and select a random question.`; |
| } catch (error) { |
| elements.status.className = "status error"; |
| elements.status.textContent = `Dataset unavailable. The public question bank could not be loaded. Please refresh in a moment. ${error.message}`; |
| } |
| } |
|
|
| function uniqueValues(records, field) { |
| return [...new Set(records.map((record) => String(record[field])))].sort(); |
| } |
|
|
| function populateSelect(select, values, preferredValue) { |
| const nextValue = values.includes(preferredValue) ? preferredValue : ALL; |
| select.replaceChildren(); |
| [ALL, ...values].forEach((value) => { |
| const option = document.createElement("option"); |
| option.value = value; |
| option.textContent = displayValue(value); |
| select.append(option); |
| }); |
| select.value = nextValue; |
| } |
|
|
| function displayValue(value) { |
| if (value === ALL) return value; |
| return value.replaceAll("_", " ").replaceAll("-", " ").replace(/\b\w/g, (letter) => letter.toUpperCase()); |
| } |
|
|
| function updateDependentFilters() { |
| const domainValue = elements.domain.value || ALL; |
| const domainMatches = state.questions.filter((question) => domainValue === ALL || question.domain === domainValue); |
| populateSelect(elements.difficulty, uniqueValues(domainMatches, "difficulty"), elements.difficulty.value); |
| updateQuestionTypes(); |
| } |
|
|
| function updateQuestionTypes() { |
| const matches = state.questions.filter((question) => |
| (elements.domain.value === ALL || question.domain === elements.domain.value) |
| && (elements.difficulty.value === ALL || question.difficulty === elements.difficulty.value)); |
| populateSelect(elements.questionType, uniqueValues(matches, "question_type"), elements.questionType.value); |
| } |
|
|
| function matchingQuestions() { |
| return state.questions.filter((question) => |
| (elements.domain.value === ALL || question.domain === elements.domain.value) |
| && (elements.difficulty.value === ALL || question.difficulty === elements.difficulty.value) |
| && (elements.questionType.value === ALL || question.question_type === elements.questionType.value)); |
| } |
|
|
| function resetEvaluation() { |
| elements.answer.value = ""; |
| elements.evaluationStatus.textContent = ""; |
| elements.evaluationStatus.className = "inline-status"; |
| elements.scoreCard.hidden = true; |
| elements.scoreCard.replaceChildren(); |
| elements.reviewCard.hidden = true; |
| elements.referenceAnswer.textContent = ""; |
| elements.rubricExcellent.textContent = ""; |
| elements.rubricAcceptable.textContent = ""; |
| elements.rubricWeak.textContent = ""; |
| elements.followUps.replaceChildren(); |
| } |
|
|
| function selectRandomQuestion() { |
| const matches = matchingQuestions(); |
| resetEvaluation(); |
| if (!matches.length) { |
| state.selectedQuestion = null; |
| elements.evaluateButton.disabled = true; |
| elements.questionCard.replaceChildren(createElement("p", "No questions match these filters. Try a broader selection.", "muted")); |
| elements.evaluationStatus.textContent = "No questions match these filters."; |
| return; |
| } |
| state.selectedQuestion = matches[Math.floor(Math.random() * matches.length)]; |
| elements.evaluateButton.disabled = false; |
| renderQuestion(state.selectedQuestion); |
| elements.answer.focus(); |
| } |
|
|
| function renderQuestion(question) { |
| const badges = createElement("div", "", "badges"); |
| [question.domain, question.difficulty, question.question_type].forEach((value) => badges.append(createElement("span", displayValue(String(value)), "badge"))); |
| elements.questionCard.replaceChildren(badges, createElement("h2", question.question)); |
| } |
|
|
| function evaluateAnswer() { |
| const question = state.selectedQuestion; |
| if (!question) return showEvaluationError("Select a question first."); |
| const normalizedAnswer = normalize(elements.answer.value); |
| const words = normalizedAnswer ? normalizedAnswer.split(" ") : []; |
| if (words.length < 12) return showEvaluationError("Please give a fuller interview answer (at least 12 words) before evaluating."); |
|
|
| const detected = question.expected_concepts.filter((concept) => conceptIsDetected(concept, normalizedAnswer)); |
| const missing = question.expected_concepts.filter((concept) => !detected.includes(concept)); |
| const coverage = question.expected_concepts.length ? detected.length / question.expected_concepts.length : 0; |
| const dimensions = new Set(question.answer_dimensions); |
| const conceptCoverage = dimensions.has("correctness") ? 4 * coverage : 0; |
| const lengthFactor = Math.min(1, words.length / 90); |
| const completeness = dimensions.has("completeness") ? 2.5 * ((0.8 * coverage) + (0.2 * lengthFactor)) : 0; |
| const hasComparison = COMPARISON_MARKERS.some((marker) => containsSignal(normalizedAnswer, marker)); |
| const domainSignalCount = TRADEOFF_DOMAIN_SIGNALS.filter((signal) => containsSignal(normalizedAnswer, signal)).length; |
| const tradeoffs = dimensions.has("tradeoffs") && hasComparison ? Math.min(2, 1 + (0.5 * Math.min(2, domainSignalCount))) : 0; |
| const sentenceCount = elements.answer.value.split(/[.!?]+/).filter((part) => part.trim()).length; |
| const structureHits = STRUCTURE_SIGNALS.filter((signal) => containsSignal(normalizedAnswer, signal)).length; |
| const communicationFactor = (0.45 * Math.min(1, sentenceCount / 3)) |
| + (0.35 * Math.min(1, structureHits / 2)) |
| + (0.20 * (words.length >= 35 && words.length <= 300 ? 1 : 0.5)); |
| const communication = dimensions.has("communication") ? 1.5 * communicationFactor : 0; |
| const rawTotal = conceptCoverage + completeness + tradeoffs + communication; |
| const parsedMaximum = Number(question.max_score); |
| const maximum = Number.isFinite(parsedMaximum) && parsedMaximum > 0 ? parsedMaximum : 10; |
| const total = Math.min(maximum, rawTotal * maximum / 10); |
|
|
| renderScore({ total, maximum, conceptCoverage, completeness, tradeoffs, communication, detected, missing }); |
| revealReview(question); |
| elements.evaluationStatus.textContent = "Evaluation complete. Reference material is now available below."; |
| elements.evaluationStatus.className = "inline-status"; |
| } |
|
|
| function showEvaluationError(message) { |
| elements.evaluationStatus.textContent = message; |
| elements.evaluationStatus.className = "inline-status error"; |
| elements.scoreCard.hidden = true; |
| elements.reviewCard.hidden = true; |
| } |
|
|
| function renderScore(scores) { |
| const { total, maximum, conceptCoverage, completeness, tradeoffs, communication, detected, missing } = scores; |
| const anchor = total >= 8 ? "excellent" : total >= 5 ? "acceptable" : "weak"; |
| const improvement = missing.length ? `Add explicit evidence for: ${missing.slice(0, 3).join(", ")}.` : "All listed concepts had literal evidence; sharpen examples and trade-off reasoning."; |
| const warning = createElement("p", "", "heuristic-warning"); |
| warning.append(createElement("strong", "This is a heuristic baseline, not an AI judge. "), document.createTextNode("It checks literal concept evidence and basic answer structure; it does not understand meaning.")); |
| const metricGrid = createElement("div", "", "score-grid"); |
| metricGrid.append(metric("Concept coverage", conceptCoverage, 4, "Heuristic proxy for correctness"), metric("Completeness", completeness, 2.5), metric("Trade-offs", tradeoffs, 2), metric("Communication", communication, 1.5)); |
| const concepts = createElement("div", "", "concept-grid"); |
| concepts.append(conceptList("Concepts detected", detected), conceptList("Concepts not detected", missing)); |
| const feedback = createElement("p", "", "feedback"); |
| feedback.append(createElement("strong", "Feedback: "), document.createTextNode(`The heuristic result is closest to the dataset's ${anchor} anchor. ${improvement}`)); |
| const title = createElement("h2", `${total.toFixed(1)} / ${formatMaximum(maximum)}`); |
| title.id = "score-title"; |
| elements.scoreCard.replaceChildren(createElement("p", "Heuristic result", "eyebrow"), title, warning, metricGrid, concepts, feedback); |
| elements.scoreCard.hidden = false; |
| } |
|
|
| function metric(label, value, maximum, note = "") { |
| const container = createElement("div", "", "metric"); |
| container.append(createElement("span", label), createElement("strong", `${value.toFixed(1)} / ${maximum}`)); |
| if (note) container.append(createElement("small", note)); |
| return container; |
| } |
|
|
| function conceptList(title, items) { |
| const container = document.createElement("div"); |
| const list = document.createElement("ul"); |
| (items.length ? items : ["None"]).forEach((item) => list.append(createElement("li", item))); |
| container.append(createElement("h3", title), list); |
| return container; |
| } |
|
|
| function revealReview(question) { |
| elements.referenceAnswer.textContent = question.reference_answer; |
| elements.rubricExcellent.textContent = question.evaluation_rubric.excellent; |
| elements.rubricAcceptable.textContent = question.evaluation_rubric.acceptable; |
| elements.rubricWeak.textContent = question.evaluation_rubric.weak; |
| elements.followUps.replaceChildren(); |
| question.follow_up_questions.slice(0, 2).forEach((followUp) => elements.followUps.append(createElement("li", followUp))); |
| elements.reviewCard.hidden = false; |
| } |
|
|
| function createElement(tag, text = "", className = "") { |
| const element = document.createElement(tag); |
| element.textContent = text; |
| if (className) element.className = className; |
| return element; |
| } |
|
|
| function formatMaximum(maximum) { |
| return Number.isInteger(maximum) ? String(maximum) : maximum.toFixed(1); |
| } |
|
|
| elements.domain.addEventListener("change", updateDependentFilters); |
| elements.difficulty.addEventListener("change", updateQuestionTypes); |
| elements.randomButton.addEventListener("click", selectRandomQuestion); |
| elements.evaluateButton.addEventListener("click", evaluateAnswer); |
| elements.clearButton.addEventListener("click", () => { elements.answer.value = ""; elements.answer.focus(); }); |
| loadQuestions(); |
|
|