Skip to main content
Glama
quality-validator.js34.4 kB
/** * Assessment Quality Validation System * @module content-generation/quality-validator * @description Validates and improves assessment quality across all components * @version 5.0.0-alpha */ export class QualityValidator { constructor(config = {}) { this.config = { minQualityScore: 70, enableAutoCorrection: true, strictValidation: false, maxCorrectionAttempts: 3, qualityThreshold: 0.7, ...config }; // Quality validation rules this.validationRules = { flashcards: { minCount: 8, maxCount: 30, minContentLength: 10, maxContentLength: 500, balanceThreshold: 0.3, qualityChecks: ['content_relevance', 'clarity', 'difficulty_balance', 'type_variety'] }, quiz: { minQuestions: 5, maxQuestions: 20, minOptionCount: 2, maxOptionCount: 5, difficultyDistribution: { easy: [0.2, 0.5], medium: [0.3, 0.6], hard: [0.1, 0.4] }, qualityChecks: ['question_clarity', 'answer_distribution', 'distractor_quality', 'type_variety'] }, overall: { educationalAlignment: true, gradeAppropriate: true, contentAccuracy: true, assessmentValidity: true } }; // Error patterns and corrections this.correctionStrategies = { insufficient_content: this.correctInsufficientContent.bind(this), poor_balance: this.correctPoorBalance.bind(this), low_quality_questions: this.correctLowQualityQuestions.bind(this), inappropriate_difficulty: this.correctInappropriateDifficulty.bind(this), poor_distractors: this.correctPoorDistractors.bind(this), unclear_content: this.correctUnclearContent.bind(this) }; } /** * Validate complete assessment package * @param {Object} assessment - Assessment package * @returns {Promise<Object>} Validated and potentially corrected assessment */ async validateAssessment(assessment) { console.log('[QUALITY-VALIDATOR] Starting assessment validation'); try { // Run comprehensive validation const validationResult = await this.runValidation(assessment); // Apply corrections if needed and enabled let correctedAssessment = assessment; if (!validationResult.passed && this.config.enableAutoCorrection) { correctedAssessment = await this.applyCorrections(assessment, validationResult.issues); } // Final quality check const finalValidation = await this.runValidation(correctedAssessment); // Add validation metadata correctedAssessment.validation = { qualityScore: finalValidation.qualityScore, validationPassed: finalValidation.passed, issues: finalValidation.issues, corrections: validationResult.passed ? [] : this.getCorrectionsSummary(validationResult.issues), validatedAt: new Date().toISOString(), validatorVersion: '5.0.0-alpha' }; console.log(`[QUALITY-VALIDATOR] Validation complete. Quality score: ${finalValidation.qualityScore}`); return correctedAssessment; } catch (error) { console.error('[QUALITY-VALIDATOR] Validation failed:', error); throw new Error(`Assessment validation failed: ${error.message}`); } } /** * Run comprehensive validation on assessment * @param {Object} assessment - Assessment to validate * @returns {Promise<Object>} Validation result */ async runValidation(assessment) { const validationResult = { passed: false, qualityScore: 0, issues: [], componentScores: {} }; // Validate flashcards if (assessment.flashcards) { const flashcardValidation = await this.validateFlashcards(assessment.flashcards); validationResult.componentScores.flashcards = flashcardValidation.score; validationResult.issues.push(...flashcardValidation.issues); } // Validate quiz if (assessment.quiz) { const quizValidation = await this.validateQuiz(assessment.quiz); validationResult.componentScores.quiz = quizValidation.score; validationResult.issues.push(...quizValidation.issues); } // Validate overall assessment const overallValidation = await this.validateOverallAssessment(assessment); validationResult.componentScores.overall = overallValidation.score; validationResult.issues.push(...overallValidation.issues); // Calculate overall quality score validationResult.qualityScore = this.calculateOverallQualityScore(validationResult.componentScores); validationResult.passed = validationResult.qualityScore >= this.config.minQualityScore; return validationResult; } /** * Validate flashcards component * @param {Array} flashcards - Flashcards to validate * @returns {Promise<Object>} Flashcard validation result */ async validateFlashcards(flashcards) { const validation = { score: 100, issues: [] }; const rules = this.validationRules.flashcards; // Check minimum count if (flashcards.length < rules.minCount) { validation.issues.push({ type: 'insufficient_content', severity: 'high', component: 'flashcards', message: `Too few flashcards: ${flashcards.length} < ${rules.minCount}`, expectedValue: rules.minCount, actualValue: flashcards.length }); validation.score -= 30; } // Check maximum count if (flashcards.length > rules.maxCount) { validation.issues.push({ type: 'excessive_content', severity: 'medium', component: 'flashcards', message: `Too many flashcards: ${flashcards.length} > ${rules.maxCount}`, expectedValue: rules.maxCount, actualValue: flashcards.length }); validation.score -= 10; } // Check content quality for (const [index, flashcard] of flashcards.entries()) { const cardValidation = await this.validateFlashcard(flashcard, index); validation.issues.push(...cardValidation.issues); validation.score = Math.max(0, validation.score - cardValidation.deductions); } // Check type variety const typeVariety = this.checkFlashcardTypeVariety(flashcards); if (typeVariety.score < 70) { validation.issues.push({ type: 'poor_variety', severity: 'medium', component: 'flashcards', message: 'Insufficient flashcard type variety', details: typeVariety }); validation.score -= 15; } // Check difficulty balance const difficultyBalance = this.checkFlashcardDifficultyBalance(flashcards); if (difficultyBalance.imbalanceScore > rules.balanceThreshold) { validation.issues.push({ type: 'poor_balance', severity: 'medium', component: 'flashcards', message: 'Poor difficulty balance in flashcards', details: difficultyBalance }); validation.score -= 20; } return { score: Math.max(0, validation.score), issues: validation.issues }; } /** * Validate individual flashcard * @param {Object} flashcard - Flashcard to validate * @param {number} index - Flashcard index * @returns {Promise<Object>} Single flashcard validation */ async validateFlashcard(flashcard, index) { const validation = { issues: [], deductions: 0 }; const rules = this.validationRules.flashcards; // Check required fields if (!flashcard.front || !flashcard.back) { validation.issues.push({ type: 'missing_content', severity: 'high', component: 'flashcard', index: index, message: 'Flashcard missing front or back content' }); validation.deductions += 10; } // Check content length if (flashcard.front && flashcard.front.length < rules.minContentLength) { validation.issues.push({ type: 'insufficient_content', severity: 'medium', component: 'flashcard', index: index, field: 'front', message: 'Flashcard front content too short' }); validation.deductions += 2; } if (flashcard.back && flashcard.back.length < rules.minContentLength) { validation.issues.push({ type: 'insufficient_content', severity: 'medium', component: 'flashcard', index: index, field: 'back', message: 'Flashcard back content too short' }); validation.deductions += 2; } // Check content clarity if (flashcard.front && this.isContentUnclear(flashcard.front)) { validation.issues.push({ type: 'unclear_content', severity: 'medium', component: 'flashcard', index: index, field: 'front', message: 'Flashcard front content unclear' }); validation.deductions += 3; } if (flashcard.back && this.isContentUnclear(flashcard.back)) { validation.issues.push({ type: 'unclear_content', severity: 'medium', component: 'flashcard', index: index, field: 'back', message: 'Flashcard back content unclear' }); validation.deductions += 3; } // Check educational relevance if (!this.isEducationallyRelevant(flashcard)) { validation.issues.push({ type: 'poor_educational_value', severity: 'high', component: 'flashcard', index: index, message: 'Flashcard has poor educational value' }); validation.deductions += 5; } return validation; } /** * Validate quiz component * @param {Object} quiz - Quiz to validate * @returns {Promise<Object>} Quiz validation result */ async validateQuiz(quiz) { const validation = { score: 100, issues: [] }; if (!quiz.questions || !Array.isArray(quiz.questions)) { validation.issues.push({ type: 'missing_content', severity: 'critical', component: 'quiz', message: 'Quiz missing questions array' }); return { score: 0, issues: validation.issues }; } const rules = this.validationRules.quiz; // Check question count if (quiz.questions.length < rules.minQuestions) { validation.issues.push({ type: 'insufficient_content', severity: 'high', component: 'quiz', message: `Too few questions: ${quiz.questions.length} < ${rules.minQuestions}` }); validation.score -= 30; } if (quiz.questions.length > rules.maxQuestions) { validation.issues.push({ type: 'excessive_content', severity: 'medium', component: 'quiz', message: `Too many questions: ${quiz.questions.length} > ${rules.maxQuestions}` }); validation.score -= 10; } // Validate individual questions for (const [index, question] of quiz.questions.entries()) { const questionValidation = await this.validateQuestion(question, index); validation.issues.push(...questionValidation.issues); validation.score = Math.max(0, validation.score - questionValidation.deductions); } // Check question type variety const typeVariety = this.checkQuestionTypeVariety(quiz.questions); if (typeVariety.score < 70) { validation.issues.push({ type: 'poor_variety', severity: 'medium', component: 'quiz', message: 'Insufficient question type variety', details: typeVariety }); validation.score -= 15; } // Check difficulty distribution const difficultyDistribution = this.checkQuestionDifficultyDistribution(quiz.questions); if (!this.isDifficultyDistributionValid(difficultyDistribution)) { validation.issues.push({ type: 'poor_balance', severity: 'medium', component: 'quiz', message: 'Poor difficulty distribution', details: difficultyDistribution }); validation.score -= 20; } // Check answer position distribution (for multiple choice) const answerDistribution = this.checkAnswerPositionDistribution(quiz.questions); if (answerDistribution.imbalanceScore > 0.3) { validation.issues.push({ type: 'poor_answer_distribution', severity: 'low', component: 'quiz', message: 'Uneven answer position distribution', details: answerDistribution }); validation.score -= 10; } return { score: Math.max(0, validation.score), issues: validation.issues }; } /** * Validate individual question * @param {Object} question - Question to validate * @param {number} index - Question index * @returns {Promise<Object>} Question validation result */ async validateQuestion(question, index) { const validation = { issues: [], deductions: 0 }; // Check required fields if (!question.question) { validation.issues.push({ type: 'missing_content', severity: 'critical', component: 'question', index: index, message: 'Question missing question text' }); validation.deductions += 10; return validation; } // Validate by question type switch (question.type) { case 'multiple_choice': const mcValidation = await this.validateMultipleChoiceQuestion(question, index); validation.issues.push(...mcValidation.issues); validation.deductions += mcValidation.deductions; break; case 'true_false': const tfValidation = await this.validateTrueFalseQuestion(question, index); validation.issues.push(...tfValidation.issues); validation.deductions += tfValidation.deductions; break; case 'fill_blank': const fbValidation = await this.validateFillBlankQuestion(question, index); validation.issues.push(...fbValidation.issues); validation.deductions += fbValidation.deductions; break; case 'short_answer': const saValidation = await this.validateShortAnswerQuestion(question, index); validation.issues.push(...saValidation.issues); validation.deductions += saValidation.deductions; break; case 'matching': const matchValidation = await this.validateMatchingQuestion(question, index); validation.issues.push(...matchValidation.issues); validation.deductions += matchValidation.deductions; break; } // Check question clarity if (this.isContentUnclear(question.question)) { validation.issues.push({ type: 'unclear_content', severity: 'medium', component: 'question', index: index, message: 'Question text unclear' }); validation.deductions += 3; } // Check educational value if (!this.isEducationallyRelevant(question)) { validation.issues.push({ type: 'poor_educational_value', severity: 'high', component: 'question', index: index, message: 'Question has poor educational value' }); validation.deductions += 5; } return validation; } /** * Validate multiple choice question * @param {Object} question - Multiple choice question * @param {number} index - Question index * @returns {Promise<Object>} Validation result */ async validateMultipleChoiceQuestion(question, index) { const validation = { issues: [], deductions: 0 }; const rules = this.validationRules.quiz; // Check options if (!question.options || !Array.isArray(question.options)) { validation.issues.push({ type: 'missing_content', severity: 'critical', component: 'question', index: index, message: 'Multiple choice question missing options' }); validation.deductions += 10; return validation; } // Check option count if (question.options.length < rules.minOptionCount) { validation.issues.push({ type: 'insufficient_content', severity: 'high', component: 'question', index: index, message: `Too few options: ${question.options.length}` }); validation.deductions += 5; } if (question.options.length > rules.maxOptionCount) { validation.issues.push({ type: 'excessive_content', severity: 'medium', component: 'question', index: index, message: `Too many options: ${question.options.length}` }); validation.deductions += 2; } // Check correct answer index if (typeof question.correct !== 'number' || question.correct < 0 || question.correct >= question.options.length) { validation.issues.push({ type: 'invalid_answer', severity: 'critical', component: 'question', index: index, message: 'Invalid correct answer index' }); validation.deductions += 10; } // Check distractor quality const distractorQuality = this.assessDistractorQuality(question.options, question.correct); if (distractorQuality.score < 70) { validation.issues.push({ type: 'poor_distractors', severity: 'medium', component: 'question', index: index, message: 'Poor quality distractors', details: distractorQuality }); validation.deductions += 3; } return validation; } /** * Validate true/false question * @param {Object} question - True/false question * @param {number} index - Question index * @returns {Promise<Object>} Validation result */ async validateTrueFalseQuestion(question, index) { const validation = { issues: [], deductions: 0 }; // Check correct answer if (typeof question.correct !== 'boolean') { validation.issues.push({ type: 'invalid_answer', severity: 'critical', component: 'question', index: index, message: 'True/false question correct answer must be boolean' }); validation.deductions += 5; } return validation; } /** * Validate fill-in-the-blank question * @param {Object} question - Fill blank question * @param {number} index - Question index * @returns {Promise<Object>} Validation result */ async validateFillBlankQuestion(question, index) { const validation = { issues: [], deductions: 0 }; // Check answer if (!question.answer || typeof question.answer !== 'string') { validation.issues.push({ type: 'missing_content', severity: 'critical', component: 'question', index: index, message: 'Fill blank question missing answer' }); validation.deductions += 5; } // Check for blank in question if (!question.question.includes('_')) { validation.issues.push({ type: 'invalid_format', severity: 'high', component: 'question', index: index, message: 'Fill blank question missing blank marker' }); validation.deductions += 3; } return validation; } /** * Validate short answer question * @param {Object} question - Short answer question * @param {number} index - Question index * @returns {Promise<Object>} Validation result */ async validateShortAnswerQuestion(question, index) { const validation = { issues: [], deductions: 0 }; // Check expected elements or rubric if (!question.expectedElements && !question.rubric) { validation.issues.push({ type: 'missing_content', severity: 'medium', component: 'question', index: index, message: 'Short answer question missing expected elements or rubric' }); validation.deductions += 2; } return validation; } /** * Validate matching question * @param {Object} question - Matching question * @param {number} index - Question index * @returns {Promise<Object>} Validation result */ async validateMatchingQuestion(question, index) { const validation = { issues: [], deductions: 0 }; // Check columns if (!question.leftColumn || !question.rightColumn) { validation.issues.push({ type: 'missing_content', severity: 'critical', component: 'question', index: index, message: 'Matching question missing columns' }); validation.deductions += 10; return validation; } // Check correct pairs if (!question.correctPairs || !Array.isArray(question.correctPairs)) { validation.issues.push({ type: 'missing_content', severity: 'critical', component: 'question', index: index, message: 'Matching question missing correct pairs' }); validation.deductions += 10; } return validation; } /** * Validate overall assessment * @param {Object} assessment - Complete assessment * @returns {Promise<Object>} Overall validation result */ async validateOverallAssessment(assessment) { const validation = { score: 100, issues: [] }; // Check metadata if (!assessment.metadata) { validation.issues.push({ type: 'missing_metadata', severity: 'medium', component: 'assessment', message: 'Assessment missing metadata' }); validation.score -= 10; } // Check educational alignment if (!this.isEducationallyAligned(assessment)) { validation.issues.push({ type: 'poor_educational_alignment', severity: 'high', component: 'assessment', message: 'Poor educational alignment' }); validation.score -= 20; } // Check content coherence if (!this.isContentCoherent(assessment)) { validation.issues.push({ type: 'poor_coherence', severity: 'medium', component: 'assessment', message: 'Poor content coherence between components' }); validation.score -= 15; } return { score: Math.max(0, validation.score), issues: validation.issues }; } // Quality check helper methods isContentUnclear(content) { if (!content || typeof content !== 'string') return true; // Simple clarity checks const hasBasicStructure = content.length > 5; const hasProperCapitalization = /^[A-Z]/.test(content.trim()); const hasEndingPunctuation = /[.!?]$/.test(content.trim()); const hasReasonableWords = content.split(' ').length >= 2; return !(hasBasicStructure && hasProperCapitalization && hasEndingPunctuation && hasReasonableWords); } isEducationallyRelevant(item) { // Basic educational relevance check if (!item) return false; const content = item.question || item.front || item.back || ''; const hasEducationalKeywords = /\b(conceito|definição|exemplo|aplicação|princípio|teoria|método|processo|análise|estudo|compreender|explicar|demonstrar|identificar)\b/i.test(content); const hasSubstantiveContent = content.length > 20; return hasEducationalKeywords && hasSubstantiveContent; } isEducationallyAligned(assessment) { // Check if flashcards and quiz are aligned with the same subject/topic const flashcardSubjects = new Set(); const questionSubjects = new Set(); if (assessment.flashcards) { assessment.flashcards.forEach(card => { if (card.subject) flashcardSubjects.add(card.subject); }); } if (assessment.quiz && assessment.quiz.questions) { assessment.quiz.questions.forEach(question => { if (question.subject) questionSubjects.add(question.subject); }); } // Check for subject alignment const hasCommonSubjects = [...flashcardSubjects].some(subject => questionSubjects.has(subject)); return hasCommonSubjects || flashcardSubjects.size === 0 || questionSubjects.size === 0; } isContentCoherent(assessment) { // Basic coherence check - could be more sophisticated return true; // Placeholder - implement actual coherence checking } checkFlashcardTypeVariety(flashcards) { const types = {}; flashcards.forEach(card => { const type = card.type || 'unknown'; types[type] = (types[type] || 0) + 1; }); const uniqueTypes = Object.keys(types).length; const totalCards = flashcards.length; const varietyScore = totalCards > 0 ? (uniqueTypes / Math.min(4, totalCards)) * 100 : 0; return { score: Math.min(100, varietyScore), types: types, uniqueTypes: uniqueTypes }; } checkFlashcardDifficultyBalance(flashcards) { const difficulties = { easy: 0, medium: 0, hard: 0 }; flashcards.forEach(card => { const difficulty = card.difficulty || 'medium'; if (difficulties[difficulty] !== undefined) { difficulties[difficulty]++; } }); const total = flashcards.length; const distribution = { easy: total > 0 ? difficulties.easy / total : 0, medium: total > 0 ? difficulties.medium / total : 0, hard: total > 0 ? difficulties.hard / total : 0 }; // Calculate imbalance score const ideal = { easy: 0.4, medium: 0.4, hard: 0.2 }; const imbalanceScore = Object.keys(ideal).reduce((sum, key) => sum + Math.abs(distribution[key] - ideal[key]), 0) / 2; return { distribution: distribution, imbalanceScore: imbalanceScore, counts: difficulties }; } checkQuestionTypeVariety(questions) { const types = {}; questions.forEach(question => { const type = question.type || 'unknown'; types[type] = (types[type] || 0) + 1; }); const uniqueTypes = Object.keys(types).length; const totalQuestions = questions.length; const varietyScore = totalQuestions > 0 ? (uniqueTypes / Math.min(5, totalQuestions)) * 100 : 0; return { score: Math.min(100, varietyScore), types: types, uniqueTypes: uniqueTypes }; } checkQuestionDifficultyDistribution(questions) { const difficulties = { easy: 0, medium: 0, hard: 0 }; questions.forEach(question => { const difficulty = question.difficulty || 'medium'; if (difficulties[difficulty] !== undefined) { difficulties[difficulty]++; } }); const total = questions.length; return { easy: total > 0 ? difficulties.easy / total : 0, medium: total > 0 ? difficulties.medium / total : 0, hard: total > 0 ? difficulties.hard / total : 0, counts: difficulties }; } isDifficultyDistributionValid(distribution) { const rules = this.validationRules.quiz.difficultyDistribution; return Object.keys(rules).every(difficulty => { const [min, max] = rules[difficulty]; const actual = distribution[difficulty] || 0; return actual >= min && actual <= max; }); } checkAnswerPositionDistribution(questions) { const positions = {}; let totalMultipleChoice = 0; questions.forEach(question => { if (question.type === 'multiple_choice' && typeof question.correct === 'number') { totalMultipleChoice++; const position = question.correct; positions[position] = (positions[position] || 0) + 1; } }); if (totalMultipleChoice === 0) { return { imbalanceScore: 0, distribution: {} }; } // Calculate distribution const distribution = {}; Object.keys(positions).forEach(position => { distribution[position] = positions[position] / totalMultipleChoice; }); // Calculate imbalance (variance from uniform distribution) const expectedProbability = 1 / Object.keys(positions).length; const imbalanceScore = Object.values(distribution).reduce((sum, probability) => sum + Math.abs(probability - expectedProbability), 0) / 2; return { imbalanceScore: imbalanceScore, distribution: distribution, counts: positions }; } assessDistractorQuality(options, correctIndex) { if (!options || options.length < 2) { return { score: 0, issues: ['Insufficient options'] }; } const correctAnswer = options[correctIndex]; const distractors = options.filter((_, index) => index !== correctIndex); let score = 100; const issues = []; // Check for obviously wrong distractors distractors.forEach((distractor, index) => { if (this.isObviouslyWrong(distractor, correctAnswer)) { score -= 20; issues.push(`Distractor ${index + 1} is obviously wrong`); } }); // Check for duplicate or very similar distractors for (let i = 0; i < distractors.length; i++) { for (let j = i + 1; j < distractors.length; j++) { if (this.areTooSimilar(distractors[i], distractors[j])) { score -= 15; issues.push(`Distractors ${i + 1} and ${j + 1} are too similar`); } } } return { score: Math.max(0, score), issues: issues }; } isObviouslyWrong(distractor, correctAnswer) { // Simple checks for obviously wrong distractors const correctWords = correctAnswer.toLowerCase().split(' '); const distractorWords = distractor.toLowerCase().split(' '); // Check if distractor is completely unrelated (no common words) const commonWords = correctWords.filter(word => distractorWords.includes(word) && word.length > 3 ); return commonWords.length === 0 && Math.abs(correctAnswer.length - distractor.length) > correctAnswer.length * 0.5; } areTooSimilar(option1, option2) { // Simple similarity check const words1 = option1.toLowerCase().split(' '); const words2 = option2.toLowerCase().split(' '); const commonWords = words1.filter(word => words2.includes(word)); const totalWords = new Set([...words1, ...words2]).size; return commonWords.length / totalWords > 0.7; } calculateOverallQualityScore(componentScores) { const weights = { flashcards: 0.4, quiz: 0.4, overall: 0.2 }; let totalScore = 0; let totalWeight = 0; Object.entries(componentScores).forEach(([component, score]) => { const weight = weights[component] || 0; totalScore += score * weight; totalWeight += weight; }); return totalWeight > 0 ? Math.round(totalScore / totalWeight) : 0; } // Correction methods async applyCorrections(assessment, issues) { console.log('[QUALITY-VALIDATOR] Applying corrections to assessment'); let correctedAssessment = { ...assessment }; let attempts = 0; while (attempts < this.config.maxCorrectionAttempts) { const appliedCorrections = []; for (const issue of issues) { const correctionStrategy = this.correctionStrategies[issue.type]; if (correctionStrategy) { try { correctedAssessment = await correctionStrategy(correctedAssessment, issue); appliedCorrections.push(issue.type); } catch (error) { console.warn(`[QUALITY-VALIDATOR] Correction failed for ${issue.type}:`, error.message); } } } if (appliedCorrections.length === 0) break; // Re-validate after corrections const revalidation = await this.runValidation(correctedAssessment); if (revalidation.passed) break; issues = revalidation.issues; attempts++; } return correctedAssessment; } async correctInsufficientContent(assessment, issue) { // Add placeholder content based on component type if (issue.component === 'flashcards') { const needed = issue.expectedValue - issue.actualValue; for (let i = 0; i < needed; i++) { assessment.flashcards.push(this.generatePlaceholderFlashcard(i)); } } else if (issue.component === 'quiz') { const needed = issue.expectedValue - issue.actualValue; for (let i = 0; i < needed; i++) { assessment.quiz.questions.push(this.generatePlaceholderQuestion(i)); } } return assessment; } async correctPoorBalance(assessment, issue) { // Implement balance correction logic return assessment; } async correctLowQualityQuestions(assessment, issue) { // Implement question quality improvement return assessment; } async correctInappropriateDifficulty(assessment, issue) { // Implement difficulty adjustment return assessment; } async correctPoorDistractors(assessment, issue) { // Implement distractor improvement return assessment; } async correctUnclearContent(assessment, issue) { // Implement content clarity improvement return assessment; } generatePlaceholderFlashcard(index) { return { id: `placeholder-${index}-${Date.now()}`, type: 'concept', front: 'Conceito importante', back: 'Definição ou explicação relevante para o aprendizado', difficulty: 'easy', tags: ['placeholder'], source: 'quality_correction' }; } generatePlaceholderQuestion(index) { return { id: `placeholder-${index}-${Date.now()}`, type: 'multiple_choice', question: 'Qual é a importância do conceito estudado?', options: ['Muito importante', 'Pouco importante', 'Sem importância', 'Não sei'], correct: 0, difficulty: 'easy', explanation: 'O conceito estudado é fundamental para o aprendizado.', source: 'quality_correction' }; } getCorrectionsSummary(issues) { const corrections = {}; issues.forEach(issue => { if (!corrections[issue.type]) { corrections[issue.type] = 0; } corrections[issue.type]++; }); return corrections; } } // Factory function for creating QualityValidator instances export function createQualityValidator(config = {}) { return new QualityValidator(config); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/rkm097git/euconquisto-composer-mcp-poc'

If you have feedback or need assistance with the MCP directory API, please join our Discord server