Skip to main content
Glama
adapter-factory.js16 kB
/** * Enhanced Adapter Factory for Intelligent Subject Adapter Selection * @module content-generation/adapter-factory * @description Factory system for creating appropriate enhanced subject adapters with fallback capability * @version 5.0.0-alpha */ import { BaseAdapter } from './base-adapter.js'; import { PhysicsAdapter } from './physics-adapter.js'; import { ChemistryAdapter } from './chemistry-adapter.js'; import { HistoryAdapter } from './history-adapter.js'; export class AdapterFactory { constructor() { // Subject mapping for enhanced adapters this.enhancedSubjects = { 'física': PhysicsAdapter, 'chemistry': PhysicsAdapter, // English fallback 'physics': PhysicsAdapter, // English fallback 'química': ChemistryAdapter, 'chemistry': ChemistryAdapter, // English fallback 'história': HistoryAdapter, 'history': HistoryAdapter // English fallback }; // Subject domain patterns for enhanced detection this.subjectPatterns = { physics: [ /\b(física|mecânica|termodinâmica|eletricidade|magnetismo|óptica|ondas|energia|força|movimento|velocidade|aceleração|balística|projétil|trajetória|atrito|gravidade|pressão|fluidos|calor|temperatura|eletromagnetismo|radioatividade|relatividade|quântica|partículas|átomos|moléculas)\b/i, /\b(physics|mechanics|thermodynamics|electricity|magnetism|optics|waves|energy|force|motion|velocity|acceleration|ballistics|projectile|trajectory|friction|gravity|pressure|fluids|heat|temperature|electromagnetic|radioactivity|relativity|quantum|particles|atoms|molecules)\b/i ], chemistry: [ /\b(química|elemento|composto|reação|molecular|átomo|íon|orgânica|inorgânica|analítica|físico-química|bioquímica|estequiometria|soluções|ácidos|bases|sais|oxidação|redução|catálise|cinética|equilíbrio|termodinâmica química|eletroquímica|polímeros|combustão|ligações|estrutura atômica|ligação química|tabela periódica)\b/i, /\b(chemistry|element|compound|reaction|molecular|atom|ion|organic|inorganic|analytical|physical-chemistry|biochemistry|stoichiometry|solutions|acids|bases|salts|oxidation|reduction|catalysis|kinetics|equilibrium|chemical thermodynamics|electrochemistry|polymers|combustion|bonds|atomic structure|chemical bond|periodic table)\b/i ], history: [ /\b(história|histórico|civilização|império|reino|guerra|revolução|independência|colonização|escravidão|abolição|república|democracia|ditadura|idade média|renascimento|iluminismo|primeira guerra|segunda guerra|revolução industrial|descobrimentos|conquista|período|época|século|dinastia|cultura|sociedade)\b/i, /\b(history|historical|civilization|empire|kingdom|war|revolution|independence|colonization|slavery|abolition|republic|democracy|dictatorship|middle ages|renaissance|enlightenment|first war|second war|industrial revolution|discoveries|conquest|period|era|century|dynasty|culture|society)\b/i ] }; // Performance metrics for adapter selection this.adapterPerformance = { BaseAdapter: { speed: 1, coverage: 100, specialization: 20 }, PhysicsAdapter: { speed: 0.9, coverage: 30, specialization: 95 }, ChemistryAdapter: { speed: 0.9, coverage: 25, specialization: 95 }, HistoryAdapter: { speed: 0.9, coverage: 35, specialization: 90 } }; } /** * Create appropriate adapter based on topic analysis * @param {string} topicDescription - Natural description of the topic * @param {Object} config - Adapter configuration * @returns {Promise<BaseAdapter>} Appropriate adapter instance */ async createAdapter(topicDescription, config = {}) { console.log('[ADAPTER-FACTORY] Analyzing topic for adapter selection:', topicDescription); try { // Analyze topic to determine best adapter const adapterAnalysis = await this.analyzeTopicForAdapter(topicDescription); // Select adapter based on analysis const selectedAdapter = this.selectOptimalAdapter(adapterAnalysis); // Create and configure adapter const adapter = this.instantiateAdapter(selectedAdapter, config); console.log(`[ADAPTER-FACTORY] Selected ${selectedAdapter} adapter`); return adapter; } catch (error) { console.error('[ADAPTER-FACTORY] Adapter creation failed, falling back to BaseAdapter:', error); return new BaseAdapter(config); } } /** * Analyze topic to determine best adapter * @param {string} topicDescription - Topic description * @returns {Promise<Object>} Adapter analysis result */ async analyzeTopicForAdapter(topicDescription) { const text = topicDescription.toLowerCase(); // Analyze subject patterns const subjectScores = {}; for (const [subject, patterns] of Object.entries(this.subjectPatterns)) { subjectScores[subject] = this.calculateSubjectScore(text, patterns); } // Determine confidence levels const maxScore = Math.max(...Object.values(subjectScores)); const primarySubject = Object.keys(subjectScores).find( subject => subjectScores[subject] === maxScore ); // Calculate specialization benefit const specializationBenefit = this.calculateSpecializationBenefit( primarySubject, maxScore ); return { subjectScores: subjectScores, primarySubject: primarySubject, confidence: maxScore, specializationBenefit: specializationBenefit, recommendedAdapter: this.getRecommendedAdapter(primarySubject, maxScore), fallbackRequired: maxScore < 0.6 }; } /** * Calculate subject score based on pattern matching * @param {string} text - Topic text * @param {Array} patterns - Subject patterns * @returns {number} Subject score (0-1) */ calculateSubjectScore(text, patterns) { let totalMatches = 0; let totalWeight = 0; for (const pattern of patterns) { const matches = (text.match(pattern) || []).length; const weight = 1; // Equal weight for all patterns totalMatches += matches * weight; totalWeight += weight; } // Normalize score const rawScore = totalWeight > 0 ? totalMatches / totalWeight : 0; return Math.min(rawScore, 1); // Cap at 1.0 } /** * Calculate specialization benefit * @param {string} subject - Primary subject * @param {number} confidence - Subject confidence * @returns {number} Specialization benefit score */ calculateSpecializationBenefit(subject, confidence) { if (!subject || confidence < 0.3) return 0; const adapterName = this.getAdapterName(subject); const performance = this.adapterPerformance[adapterName]; if (!performance) return 0; // Benefit = confidence * specialization_level * coverage_factor const specializationFactor = performance.specialization / 100; const coverageFactor = Math.min(performance.coverage / 50, 1); // Normalize coverage return confidence * specializationFactor * coverageFactor; } /** * Get recommended adapter for subject and confidence * @param {string} subject - Primary subject * @param {number} confidence - Subject confidence * @returns {string} Recommended adapter name */ getRecommendedAdapter(subject, confidence) { // High confidence threshold for specialized adapters if (confidence >= 0.5) { const adapterName = this.getAdapterName(subject); if (adapterName !== 'BaseAdapter') { return adapterName; } } // Medium confidence threshold with benefit analysis if (confidence >= 0.3) { const benefit = this.calculateSpecializationBenefit(subject, confidence); if (benefit >= 0.25) { return this.getAdapterName(subject); } } // Lower threshold for clear subject indicators if (confidence >= 0.2 && subject) { const adapterName = this.getAdapterName(subject); if (adapterName !== 'BaseAdapter') { return adapterName; } } return 'BaseAdapter'; } /** * Get adapter name for subject * @param {string} subject - Subject name * @returns {string} Adapter class name */ getAdapterName(subject) { const mapping = { physics: 'PhysicsAdapter', chemistry: 'ChemistryAdapter', history: 'HistoryAdapter' }; return mapping[subject] || 'BaseAdapter'; } /** * Select optimal adapter based on analysis * @param {Object} analysis - Adapter analysis result * @returns {string} Selected adapter name */ selectOptimalAdapter(analysis) { const { recommendedAdapter, fallbackRequired, confidence, primarySubject } = analysis; // Use recommendation if confidence is sufficient if (!fallbackRequired && confidence >= 0.3) { return recommendedAdapter; } // Use specialized adapter with lower threshold if benefit is high if (confidence >= 0.2) { const benefit = this.calculateSpecializationBenefit(primarySubject, confidence); if (benefit >= 0.2) { return this.getAdapterName(primarySubject); } } // Direct subject match with any confidence if (primarySubject && confidence > 0) { const adapterName = this.getAdapterName(primarySubject); if (adapterName !== 'BaseAdapter') { return adapterName; } } // Fallback to BaseAdapter return 'BaseAdapter'; } /** * Instantiate selected adapter * @param {string} adapterName - Name of adapter to instantiate * @param {Object} config - Adapter configuration * @returns {BaseAdapter} Adapter instance */ instantiateAdapter(adapterName, config) { const adapters = { BaseAdapter: BaseAdapter, PhysicsAdapter: PhysicsAdapter, ChemistryAdapter: ChemistryAdapter, HistoryAdapter: HistoryAdapter }; const AdapterClass = adapters[adapterName] || BaseAdapter; return new AdapterClass(config); } /** * Generate content with automatic adapter selection * @param {string} topicDescription - Topic description * @param {Object} requirements - Content requirements * @param {Object} config - Adapter configuration * @returns {Promise<Object>} Generated content with adapter metadata */ async generateContentWithOptimalAdapter(topicDescription, requirements = {}, config = {}) { console.log('[ADAPTER-FACTORY] Generating content with optimal adapter selection'); try { // Create optimal adapter const adapter = await this.createAdapter(topicDescription, config); // Analyze topic const topicAnalysis = await adapter.analyzeTopic(topicDescription); // Generate content const content = await adapter.generateContent(topicAnalysis, requirements); // Add adapter metadata content.metadata.adapterUsed = adapter.constructor.name; content.metadata.enhancedFeatures = adapter.config.enhancedFeatures || []; content.metadata.factoryVersion = '5.0.0-alpha'; console.log(`[ADAPTER-FACTORY] Content generated successfully with ${adapter.constructor.name}`); return content; } catch (error) { console.error('[ADAPTER-FACTORY] Content generation failed:', error); throw new Error(`Enhanced content generation failed: ${error.message}`); } } /** * Get available enhanced adapters * @returns {Array} Available adapter information */ getAvailableAdapters() { return [ { name: 'BaseAdapter', description: 'Universal content generation for any educational topic', subjects: ['All subjects'], features: ['universal_coverage', 'grade_adaptation', 'quality_validation'], coverage: 100, specialization: 20 }, { name: 'PhysicsAdapter', description: 'Enhanced physics content with equations and calculations', subjects: ['física', 'physics'], features: ['equations', 'calculations', 'diagrams', 'units'], coverage: 30, specialization: 95 }, { name: 'ChemistryAdapter', description: 'Enhanced chemistry content with molecular structures and reactions', subjects: ['química', 'chemistry'], features: ['molecular_structures', 'reactions', 'periodic_table', 'laboratory'], coverage: 25, specialization: 95 }, { name: 'HistoryAdapter', description: 'Enhanced history content with timelines and causality analysis', subjects: ['história', 'history'], features: ['timelines', 'causality', 'cultural_context', 'primary_sources'], coverage: 35, specialization: 90 } ]; } /** * Validate adapter selection for topic * @param {string} topicDescription - Topic description * @returns {Promise<Object>} Validation result */ async validateAdapterSelection(topicDescription) { const analysis = await this.analyzeTopicForAdapter(topicDescription); const selectedAdapter = this.selectOptimalAdapter(analysis); return { topicDescription: topicDescription, analysis: analysis, selectedAdapter: selectedAdapter, confidence: analysis.confidence, benefit: analysis.specializationBenefit, alternatives: this.getAlternativeAdapters(analysis), recommendation: this.getSelectionRecommendation(analysis, selectedAdapter) }; } /** * Get alternative adapters for analysis * @param {Object} analysis - Adapter analysis result * @returns {Array} Alternative adapters */ getAlternativeAdapters(analysis) { const { subjectScores } = analysis; return Object.entries(subjectScores) .filter(([subject, score]) => score >= 0.2) .map(([subject, score]) => ({ subject: subject, adapter: this.getAdapterName(subject), score: score, benefit: this.calculateSpecializationBenefit(subject, score) })) .sort((a, b) => b.score - a.score); } /** * Get selection recommendation * @param {Object} analysis - Adapter analysis result * @param {string} selectedAdapter - Selected adapter * @returns {Object} Selection recommendation */ getSelectionRecommendation(analysis, selectedAdapter) { const { confidence, specializationBenefit, fallbackRequired } = analysis; let recommendation = 'optimal'; let reasoning = 'Selected adapter provides best balance of coverage and specialization'; if (fallbackRequired) { recommendation = 'fallback'; reasoning = 'Low confidence in subject detection, using universal BaseAdapter'; } else if (selectedAdapter === 'BaseAdapter' && confidence >= 0.5) { recommendation = 'conservative'; reasoning = 'Specialized adapter available but BaseAdapter chosen for reliability'; } else if (specializationBenefit >= 0.6) { recommendation = 'specialized'; reasoning = 'High specialization benefit justifies enhanced adapter usage'; } return { level: recommendation, reasoning: reasoning, confidence: confidence, benefit: specializationBenefit }; } } // Factory instance for global use export const adapterFactory = new AdapterFactory(); // Convenience functions for easy usage export async function createOptimalAdapter(topicDescription, config = {}) { return adapterFactory.createAdapter(topicDescription, config); } export async function generateEnhancedContent(topicDescription, requirements = {}, config = {}) { return adapterFactory.generateContentWithOptimalAdapter(topicDescription, requirements, config); } export function getAvailableEnhancedAdapters() { return adapterFactory.getAvailableAdapters(); } export async function validateEnhancedAdapterSelection(topicDescription) { return adapterFactory.validateAdapterSelection(topicDescription); }

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