Skip to main content
Glama
complexity-adapter.js21.4 kB
/** * Complexity Adaptation System * @module content-generation/complexity-adapter * @description Adapts assessment complexity for different grade levels * @version 5.0.0-alpha */ export class ComplexityAdapter { constructor(config = {}) { this.config = { defaultGradeLevel: 'medio', preserveEducationalValue: true, adaptVocabulary: true, adaptQuestionLength: true, adaptConceptDepth: true, ...config }; // Grade level complexity frameworks this.complexityFrameworks = { 'fundamental-1': { name: 'Ensino Fundamental I', vocabularyLevel: 'simple', questionLength: 'short', conceptDepth: 'basic', questionTypes: ['knowledge', 'comprehension'], maxOptions: 3, distractorComplexity: 'obvious', sentenceStructure: 'simple', abstractionLevel: 'concrete', cognitiveLoad: 'low' }, 'fundamental-2': { name: 'Ensino Fundamental II', vocabularyLevel: 'intermediate', questionLength: 'medium', conceptDepth: 'moderate', questionTypes: ['knowledge', 'comprehension', 'application'], maxOptions: 4, distractorComplexity: 'plausible', sentenceStructure: 'compound', abstractionLevel: 'semi-concrete', cognitiveLoad: 'medium' }, 'medio': { name: 'Ensino Médio', vocabularyLevel: 'advanced', questionLength: 'detailed', conceptDepth: 'comprehensive', questionTypes: ['application', 'analysis', 'synthesis'], maxOptions: 5, distractorComplexity: 'challenging', sentenceStructure: 'complex', abstractionLevel: 'abstract', cognitiveLoad: 'high' }, 'superior': { name: 'Ensino Superior', vocabularyLevel: 'professional', questionLength: 'complex', conceptDepth: 'expert', questionTypes: ['analysis', 'synthesis', 'evaluation'], maxOptions: 5, distractorComplexity: 'expert', sentenceStructure: 'sophisticated', abstractionLevel: 'highly-abstract', cognitiveLoad: 'very-high' } }; // Vocabulary adaptation dictionaries this.vocabularyMappings = this.initializeVocabularyMappings(); } /** * Adapt entire assessment for grade level * @param {Object} assessment - Assessment package * @param {string} gradeLevel - Target grade level * @returns {Promise<Object>} Adapted assessment */ async adaptAssessment(assessment, gradeLevel) { console.log(`[COMPLEXITY-ADAPTER] Adapting assessment for grade level: ${gradeLevel}`); if (!gradeLevel || gradeLevel === 'auto') { gradeLevel = this.config.defaultGradeLevel; } const framework = this.complexityFrameworks[gradeLevel]; if (!framework) { console.warn(`[COMPLEXITY-ADAPTER] Unknown grade level: ${gradeLevel}, using default`); gradeLevel = this.config.defaultGradeLevel; } try { const adapted = { ...assessment }; // Adapt flashcards if (adapted.flashcards) { adapted.flashcards = await this.adaptFlashcards(adapted.flashcards, gradeLevel); } // Adapt quiz questions if (adapted.quiz && adapted.quiz.questions) { adapted.quiz.questions = await this.adaptQuestions(adapted.quiz.questions, gradeLevel); } // Update metadata if (adapted.metadata) { adapted.metadata.adaptedGradeLevel = gradeLevel; adapted.metadata.complexityFramework = framework.name; adapted.metadata.adaptationTimestamp = new Date().toISOString(); } console.log(`[COMPLEXITY-ADAPTER] Assessment adapted for ${framework.name}`); return adapted; } catch (error) { console.error('[COMPLEXITY-ADAPTER] Adaptation failed:', error); throw new Error(`Complexity adaptation failed: ${error.message}`); } } /** * Adapt flashcards for grade level * @param {Array} flashcards - Flashcards array * @param {string} gradeLevel - Target grade level * @returns {Promise<Array>} Adapted flashcards */ async adaptFlashcards(flashcards, gradeLevel) { const framework = this.complexityFrameworks[gradeLevel]; const adapted = []; for (const flashcard of flashcards) { const adaptedCard = await this.adaptFlashcard(flashcard, framework); adapted.push(adaptedCard); } return adapted; } /** * Adapt individual flashcard * @param {Object} flashcard - Flashcard object * @param {Object} framework - Complexity framework * @returns {Promise<Object>} Adapted flashcard */ async adaptFlashcard(flashcard, framework) { const adapted = { ...flashcard }; // Adapt vocabulary if (this.config.adaptVocabulary) { adapted.front = this.adaptVocabulary(adapted.front, framework.vocabularyLevel); adapted.back = this.adaptVocabulary(adapted.back, framework.vocabularyLevel); } // Adapt sentence structure adapted.front = this.adaptSentenceStructure(adapted.front, framework.sentenceStructure); adapted.back = this.adaptSentenceStructure(adapted.back, framework.sentenceStructure); // Adapt concept depth if (this.config.adaptConceptDepth) { adapted.back = this.adaptConceptDepth(adapted.back, framework.conceptDepth); } // Add adaptation metadata adapted.adaptation = { originalDifficulty: flashcard.difficulty, targetFramework: framework.name, adaptedAt: new Date().toISOString(), adaptationMethods: ['vocabulary', 'sentence_structure', 'concept_depth'] }; return adapted; } /** * Adapt quiz questions for grade level * @param {Array} questions - Questions array * @param {string} gradeLevel - Target grade level * @returns {Promise<Array>} Adapted questions */ async adaptQuestions(questions, gradeLevel) { const framework = this.complexityFrameworks[gradeLevel]; const adapted = []; for (const question of questions) { const adaptedQuestion = await this.adaptQuestion(question, framework); if (adaptedQuestion) { // Some questions may be filtered out adapted.push(adaptedQuestion); } } return adapted; } /** * Adapt individual question * @param {Object} question - Question object * @param {Object} framework - Complexity framework * @returns {Promise<Object|null>} Adapted question or null if filtered */ async adaptQuestion(question, framework) { // Filter out inappropriate question types for grade level if (question.category && !framework.questionTypes.includes(question.category)) { return null; // Filter out this question type } const adapted = { ...question }; // Adapt question text if (this.config.adaptVocabulary) { adapted.question = this.adaptVocabulary(adapted.question, framework.vocabularyLevel); } // Adapt question length if (this.config.adaptQuestionLength) { adapted.question = this.adaptQuestionLength(adapted.question, framework.questionLength); } // Adapt sentence structure adapted.question = this.adaptSentenceStructure(adapted.question, framework.sentenceStructure); // Adapt specific question types switch (question.type) { case 'multiple_choice': adapted = await this.adaptMultipleChoice(adapted, framework); break; case 'short_answer': adapted = await this.adaptShortAnswer(adapted, framework); break; case 'fill_blank': adapted = await this.adaptFillBlank(adapted, framework); break; case 'true_false': adapted = await this.adaptTrueFalse(adapted, framework); break; case 'matching': adapted = await this.adaptMatching(adapted, framework); break; } // Add adaptation metadata adapted.adaptation = { originalDifficulty: question.difficulty, targetFramework: framework.name, adaptedAt: new Date().toISOString(), cognitiveLoad: framework.cognitiveLoad, abstractionLevel: framework.abstractionLevel }; return adapted; } /** * Adapt multiple choice question * @param {Object} question - Multiple choice question * @param {Object} framework - Complexity framework * @returns {Promise<Object>} Adapted question */ async adaptMultipleChoice(question, framework) { const adapted = { ...question }; // Limit number of options based on grade level if (adapted.options && adapted.options.length > framework.maxOptions) { const correctAnswer = adapted.options[adapted.correct]; // Keep correct answer and random selection of distractors const otherOptions = adapted.options.filter((_, index) => index !== adapted.correct); const selectedDistractors = this.selectBestDistractors( otherOptions, framework.maxOptions - 1, framework.distractorComplexity ); adapted.options = [correctAnswer, ...selectedDistractors]; adapted.correct = 0; // Correct answer is now first, will be randomized later } // Adapt option vocabulary if (this.config.adaptVocabulary && adapted.options) { adapted.options = adapted.options.map(option => this.adaptVocabulary(option, framework.vocabularyLevel) ); } // Adapt explanation if (adapted.explanation) { adapted.explanation = this.adaptVocabulary(adapted.explanation, framework.vocabularyLevel); adapted.explanation = this.adaptConceptDepth(adapted.explanation, framework.conceptDepth); } return adapted; } /** * Adapt short answer question * @param {Object} question - Short answer question * @param {Object} framework - Complexity framework * @returns {Promise<Object>} Adapted question */ async adaptShortAnswer(question, framework) { const adapted = { ...question }; // Adapt expected elements if (adapted.expectedElements) { adapted.expectedElements = adapted.expectedElements.map(element => this.adaptVocabulary(element, framework.vocabularyLevel) ); // Adjust number of expected elements based on cognitive load const maxElements = this.getMaxElementsForCognitiveLoad(framework.cognitiveLoad); if (adapted.expectedElements.length > maxElements) { adapted.expectedElements = adapted.expectedElements.slice(0, maxElements); } } // Adapt rubric if (adapted.rubric) { adapted.rubric = this.adaptVocabulary(adapted.rubric, framework.vocabularyLevel); adapted.rubric = this.adaptConceptDepth(adapted.rubric, framework.conceptDepth); } return adapted; } /** * Adapt fill-in-the-blank question * @param {Object} question - Fill blank question * @param {Object} framework - Complexity framework * @returns {Promise<Object>} Adapted question */ async adaptFillBlank(question, framework) { const adapted = { ...question }; // Adapt hints if they exist if (adapted.hints) { adapted.hints = adapted.hints.map(hint => this.adaptVocabulary(hint, framework.vocabularyLevel) ); // Provide more hints for lower grade levels if (framework.cognitiveLoad === 'low' && adapted.hints.length < 2) { adapted.hints.push(this.generateAdditionalHint(adapted.answer, framework)); } } return adapted; } /** * Adapt true/false question * @param {Object} question - True/false question * @param {Object} framework - Complexity framework * @returns {Promise<Object>} Adapted question */ async adaptTrueFalse(question, framework) { const adapted = { ...question }; // Adapt explanation if (adapted.explanation) { adapted.explanation = this.adaptVocabulary(adapted.explanation, framework.vocabularyLevel); adapted.explanation = this.adaptSentenceStructure(adapted.explanation, framework.sentenceStructure); } return adapted; } /** * Adapt matching question * @param {Object} question - Matching question * @param {Object} framework - Complexity framework * @returns {Promise<Object>} Adapted question */ async adaptMatching(question, framework) { const adapted = { ...question }; // Limit number of pairs based on cognitive load const maxPairs = this.getMaxPairsForCognitiveLoad(framework.cognitiveLoad); if (adapted.leftColumn && adapted.leftColumn.length > maxPairs) { // Keep first maxPairs items adapted.leftColumn = adapted.leftColumn.slice(0, maxPairs); adapted.rightColumn = adapted.rightColumn.slice(0, maxPairs); adapted.correctPairs = adapted.correctPairs.filter(pair => pair.left < maxPairs && pair.right < maxPairs ); } // Adapt vocabulary in columns if (this.config.adaptVocabulary) { if (adapted.leftColumn) { adapted.leftColumn = adapted.leftColumn.map(item => this.adaptVocabulary(item, framework.vocabularyLevel) ); } if (adapted.rightColumn) { adapted.rightColumn = adapted.rightColumn.map(item => this.adaptVocabulary(item, framework.vocabularyLevel) ); } } return adapted; } /** * Adapt vocabulary for grade level * @param {string} text - Text to adapt * @param {string} vocabularyLevel - Target vocabulary level * @returns {string} Adapted text */ adaptVocabulary(text, vocabularyLevel) { if (!text || !this.config.adaptVocabulary) return text; let adapted = text; const mappings = this.vocabularyMappings[vocabularyLevel]; if (mappings) { Object.entries(mappings).forEach(([complex, simple]) => { const regex = new RegExp(`\\b${complex}\\b`, 'gi'); adapted = adapted.replace(regex, simple); }); } return adapted; } /** * Adapt question length * @param {string} question - Question text * @param {string} lengthLevel - Target length level * @returns {string} Adapted question */ adaptQuestionLength(question, lengthLevel) { if (!question) return question; switch (lengthLevel) { case 'short': return this.simplifyQuestion(question); case 'medium': return this.moderateQuestion(question); case 'detailed': return this.elaborateQuestion(question); case 'complex': return this.complexifyQuestion(question); default: return question; } } /** * Adapt sentence structure * @param {string} text - Text to adapt * @param {string} structureLevel - Target structure level * @returns {string} Adapted text */ adaptSentenceStructure(text, structureLevel) { if (!text) return text; switch (structureLevel) { case 'simple': return this.simplifyStructure(text); case 'compound': return this.createCompoundStructure(text); case 'complex': return this.createComplexStructure(text); case 'sophisticated': return this.createSophisticatedStructure(text); default: return text; } } /** * Adapt concept depth * @param {string} content - Content to adapt * @param {string} depthLevel - Target depth level * @returns {string} Adapted content */ adaptConceptDepth(content, depthLevel) { if (!content) return content; switch (depthLevel) { case 'basic': return this.simplifyConceptDepth(content); case 'moderate': return this.moderateConceptDepth(content); case 'comprehensive': return this.comprehensiveConceptDepth(content); case 'expert': return this.expertConceptDepth(content); default: return content; } } /** * Select best distractors based on complexity * @param {Array} distractors - Available distractors * @param {number} count - Number to select * @param {string} complexityLevel - Target complexity * @returns {Array} Selected distractors */ selectBestDistractors(distractors, count, complexityLevel) { if (distractors.length <= count) return distractors; // Score distractors by complexity appropriateness const scored = distractors.map(distractor => ({ text: distractor, score: this.scoreDistractorComplexity(distractor, complexityLevel) })); // Sort by score and take top count scored.sort((a, b) => b.score - a.score); return scored.slice(0, count).map(item => item.text); } /** * Score distractor complexity * @param {string} distractor - Distractor text * @param {string} complexityLevel - Target complexity * @returns {number} Complexity score */ scoreDistractorComplexity(distractor, complexityLevel) { let score = 0; // Basic scoring based on length and vocabulary const words = distractor.split(' '); const avgWordLength = words.reduce((sum, word) => sum + word.length, 0) / words.length; switch (complexityLevel) { case 'obvious': score = Math.max(0, 100 - avgWordLength * 10); // Prefer simpler break; case 'plausible': score = 100 - Math.abs(avgWordLength - 6) * 10; // Prefer medium complexity break; case 'challenging': score = avgWordLength * 10; // Prefer more complex break; case 'expert': score = avgWordLength * 15 + words.length * 5; // Prefer complex and long break; } return Math.max(0, Math.min(100, score)); } /** * Get maximum elements for cognitive load * @param {string} cognitiveLoad - Cognitive load level * @returns {number} Maximum elements */ getMaxElementsForCognitiveLoad(cognitiveLoad) { const limits = { 'low': 2, 'medium': 3, 'high': 4, 'very-high': 5 }; return limits[cognitiveLoad] || 3; } /** * Get maximum pairs for cognitive load * @param {string} cognitiveLoad - Cognitive load level * @returns {number} Maximum pairs */ getMaxPairsForCognitiveLoad(cognitiveLoad) { const limits = { 'low': 3, 'medium': 4, 'high': 5, 'very-high': 6 }; return limits[cognitiveLoad] || 4; } // Text adaptation helper methods simplifyQuestion(question) { return question.replace(/,.*?(?=\?|$)/, ''); // Remove complex clauses } moderateQuestion(question) { return question; // Keep as is for moderate complexity } elaborateQuestion(question) { return question + ' Justifique sua resposta.'; } complexifyQuestion(question) { return question + ' Analise criticamente e considere múltiplas perspectivas.'; } simplifyStructure(text) { return text.replace(/;/g, '.').replace(/,.*?,/g, ','); // Break complex sentences } createCompoundStructure(text) { return text; // Keep moderate complexity } createComplexStructure(text) { return text; // Maintain original complexity } createSophisticatedStructure(text) { return text; // Keep sophisticated structure } simplifyConceptDepth(content) { return content.replace(/\b(processo|metodologia|estratégia)\b/gi, 'forma'); } moderateConceptDepth(content) { return content; } comprehensiveConceptDepth(content) { return content; } expertConceptDepth(content) { return content; } generateAdditionalHint(answer, framework) { return `A resposta tem ${answer.length} letras.`; } /** * Initialize vocabulary mappings for different levels * @returns {Object} Vocabulary mappings */ initializeVocabularyMappings() { return { 'simple': { 'compreender': 'entender', 'analisar': 'estudar', 'metodologia': 'forma', 'estratégia': 'plano', 'processo': 'forma', 'conceito': 'ideia', 'fundamental': 'importante', 'significativo': 'importante', 'característica': 'qualidade', 'propriedade': 'qualidade' }, 'intermediate': { 'metodologia': 'método', 'estratégia': 'estratégia', 'fundamental': 'básico', 'significativo': 'importante' }, 'advanced': { // Keep most complex terms }, 'professional': { // Keep all technical terms } }; } /** * Calculate adaptation statistics * @param {Object} originalAssessment - Original assessment * @param {Object} adaptedAssessment - Adapted assessment * @returns {Object} Adaptation statistics */ calculateAdaptationStatistics(originalAssessment, adaptedAssessment) { const stats = { vocabularyChanges: 0, structureChanges: 0, questionsFiltered: 0, optionsReduced: 0, complexityReduction: 0 }; // Compare and calculate changes // This would be implemented based on specific tracking needs return stats; } } // Factory function for creating ComplexityAdapter instances export function createComplexityAdapter(config = {}) { return new ComplexityAdapter(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