adapter-factory.js•16 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);
}