Skip to main content
Glama

Prompt Auto-Optimizer MCP

by sloth-wq
prompt-evolution.ts38.2 kB
/** * Evolution Engine - Orchestrates the genetic evolutionary prompt adaptation process * * This class coordinates the entire evolution process including: * - Population management and candidate lifecycle tracking * - Generation cycles with mutation and selection * - Convergence detection and early stopping * - Integration with ParetoFrontier, ReflectionEngine, and PromptMutator * - Fault tolerance and recovery mechanisms * - Evolution history tracking and metrics collection */ import type { EvolutionConfig, EvolutionResult, PromptCandidate, ExecutionTrajectory, TaskContext, StartEvolutionParams } from '../types/gepa'; import type { ParetoFrontier, ConvergenceMetrics } from './pareto-frontier'; import type { ReflectionEngine } from './reflection-engine'; import type { PromptMutator } from '../services/prompt-mutator'; import type { LLMAdapter } from '../services/llm-adapter'; import type { TrajectoryStore } from './trajectory-store'; // Evolution Engine Dependencies Interface export interface EvolutionEngineDependencies { llmAdapter: LLMAdapter; paretoFrontier: ParetoFrontier; reflectionEngine: ReflectionEngine; promptMutator: PromptMutator; trajectoryStore: TrajectoryStore; config: Partial<EvolutionConfig>; } // Evolution Metrics Interface export interface EvolutionMetrics { totalGenerations: number; totalRollouts: number; bestScore: number; averageScore: number; convergenceMetrics: ConvergenceMetrics; diversityTrend: number[]; improvementRate: number; stagnationCount: number; } // Evaluation Cache Entry interface EvaluationCacheEntry { score: number; timestamp: number; trajectory?: ExecutionTrajectory; } // Generation Statistics interface GenerationStats { averageFitness: number; bestFitness: number; diversityScore: number; mutationSuccessRate: number; evaluationTime: number; } export class EvolutionEngine { public readonly config: Required<EvolutionConfig>; private readonly llmAdapter: LLMAdapter; private readonly paretoFrontier: ParetoFrontier; private readonly reflectionEngine: ReflectionEngine; private readonly promptMutator: PromptMutator; private readonly trajectoryStore: TrajectoryStore; // State management private evaluationCache = new Map<string, EvaluationCacheEntry>(); private currentEvolutionId?: string; private isShutdown = false; private activeConcurrentEvaluations = 0; private readonly maxConcurrentEvaluations = 5; // Metrics tracking private generationStats: GenerationStats[] = []; private stagnationCounter = 0; private lastBestScore = 0; private readonly stagnationThreshold = 3; private readonly targetScore = 0.95; private readonly improvementThreshold = 0.01; constructor(dependencies: EvolutionEngineDependencies) { this.validateDependencies(dependencies); this.llmAdapter = dependencies.llmAdapter; this.paretoFrontier = dependencies.paretoFrontier; this.reflectionEngine = dependencies.reflectionEngine; this.promptMutator = dependencies.promptMutator; this.trajectoryStore = dependencies.trajectoryStore; // Set default configuration with proper typing this.config = { taskDescription: dependencies.config.taskDescription || '', seedPrompt: dependencies.config.seedPrompt || '', targetModules: dependencies.config.targetModules || [], maxGenerations: dependencies.config.maxGenerations ?? 10, populationSize: dependencies.config.populationSize ?? 20, mutationRate: dependencies.config.mutationRate ?? 0.4 }; this.validateConfig(); } /** * Validate required dependencies */ private validateDependencies(dependencies: EvolutionEngineDependencies): void { if (!dependencies.llmAdapter) { throw new Error('LLM adapter is required'); } if (!dependencies.paretoFrontier) { throw new Error('Pareto frontier is required'); } if (!dependencies.reflectionEngine) { throw new Error('Reflection engine is required'); } if (!dependencies.promptMutator) { throw new Error('Prompt mutator is required'); } if (!dependencies.trajectoryStore) { throw new Error('Trajectory store is required'); } if (!dependencies.config) { throw new Error('Configuration is required'); } } /** * Validate evolution configuration */ private validateConfig(): void { if (!this.config.taskDescription || this.config.taskDescription.trim() === '') { throw new Error('Task description is required'); } if (this.config.maxGenerations <= 0) { throw new Error('Max generations must be positive'); } if (this.config.populationSize <= 0) { throw new Error('Population size must be positive'); } if (this.config.mutationRate < 0 || this.config.mutationRate > 1) { throw new Error('Mutation rate must be between 0 and 1'); } } /** * Start the complete evolution process */ async startEvolution(params: StartEvolutionParams): Promise<EvolutionResult> { // Validate parameters this.validateEvolutionParams(params); // Generate unique evolution ID this.currentEvolutionId = this.generateEvolutionId(); // Create task context const taskContext = this.createTaskContext(params); // Initialize population const initialPopulation = await this.initializePopulation(params); if (initialPopulation.length === 0) { throw new Error('Failed to initialize population'); } // Evolution history tracking const evolutionHistory: PromptCandidate[][] = []; let currentPopulation = initialPopulation; let generation = 0; let convergenceAchieved = false; let totalRollouts = 0; // Reset evolution state this.generationStats = []; this.stagnationCounter = 0; this.lastBestScore = 0; try { // Evolution loop while (generation < this.config.maxGenerations && !convergenceAchieved && !this.isShutdown) { generation++; // Run generation cycle const newGeneration = await this.runGeneration(currentPopulation, taskContext, generation); // Update evolution history this.updateEvolutionHistory(evolutionHistory, newGeneration); // Check for convergence const convergenceMetrics = this.paretoFrontier.getConvergenceMetrics(); convergenceAchieved = this.detectConvergence(convergenceMetrics, generation); // Check early stopping conditions const shouldStop = await this.checkEarlyStoppingConditions(newGeneration); if (shouldStop) { break; } // Update population for next generation currentPopulation = newGeneration; totalRollouts += newGeneration.reduce((sum, candidate) => sum + candidate.rolloutCount, 0); // Store generation metrics await this.storeGenerationMetrics(generation, newGeneration, convergenceMetrics); } // Get best candidate from final population const bestPrompt = this.getBestCandidate(currentPopulation); // Compile evolution result const result: EvolutionResult = { evolutionId: this.currentEvolutionId, taskDescription: params.taskDescription, generations: generation, bestPrompt, convergenceAchieved, totalRollouts, evolutionHistory }; // Store final evolution result await this.storeEvolutionResult(result); return result; } catch (error: any) { // Handle evolution failures gracefully const fallbackResult = await this.createFallbackResult(params, currentPopulation, generation, evolutionHistory); throw new Error(`Evolution failed: ${error.message}. Fallback result: ${fallbackResult.evolutionId}`); } } /** * Run a single generation cycle */ async runGeneration( population: PromptCandidate[], taskContext: TaskContext, generation: number ): Promise<PromptCandidate[]> { const startTime = Date.now(); try { // Generate new candidates through mutation const newCandidates = await this.generateCandidates(population, taskContext); // Combine with existing population const allCandidates = [...population, ...newCandidates]; // Evaluate all candidates await this.evaluatePopulation(allCandidates, taskContext); // Select survivors for next generation const survivors = await this.selectSurvivors(allCandidates, this.config.populationSize); // Update generation statistics const stats: GenerationStats = { averageFitness: survivors.reduce((sum, c) => sum + c.averageScore, 0) / survivors.length, bestFitness: Math.max(...survivors.map(c => c.averageScore)), diversityScore: this.calculateDiversityScore(survivors), mutationSuccessRate: newCandidates.length / (population.length * this.config.mutationRate), evaluationTime: Date.now() - startTime }; this.generationStats.push(stats); // Check for improvement and update stagnation counter this.updateStagnationCounter(stats.bestFitness); return survivors; } catch (error: any) { // Handle generation failures with fallback // eslint-disable-next-line no-console console.warn(`Generation ${generation} failed: ${error.message}. Using fallback selection.`); return this.createFallbackGeneration(population); } } /** * Generate new candidate mutations from current population (OPTIMIZED: Parallel processing) */ async generateCandidates(population: PromptCandidate[], taskContext: TaskContext): Promise<PromptCandidate[]> { if (population.length === 0) { return []; } const maxCandidates = Math.min(this.config.populationSize, population.length * 3); const candidatePool = new CandidatePool(maxCandidates); try { // OPTIMIZATION: Get trajectories once and reuse const trajectories = await this.getRecentTrajectories(population); const trajectoryMap = new Map<string, ExecutionTrajectory[]>(); trajectories.forEach(t => { if (!trajectoryMap.has(t.promptId)) { trajectoryMap.set(t.promptId, []); } const trajectories = trajectoryMap.get(t.promptId); if (trajectories) { trajectories.push(t); } }); // OPTIMIZATION: Parallel mutation generation with limits const mutationPromises: Promise<PromptCandidate[]>[] = []; // Reflective mutations (top 5 candidates) const topCandidates = population.slice(0, Math.min(5, population.length)); mutationPromises.push( this.generateReflectiveMutationsBatch(topCandidates, trajectoryMap, maxCandidates / 3) ); // Crossover mutations (if enough candidates) if (population.length >= 2) { mutationPromises.push( this.generateCrossoverMutationsBatch(population.slice(0, 10), maxCandidates / 3) ); } // Adaptive mutations (top 3 candidates) const adaptiveCandidates = population.slice(0, Math.min(3, population.length)); mutationPromises.push( this.generateAdaptiveMutationsBatch(adaptiveCandidates, taskContext, maxCandidates / 3) ); // OPTIMIZATION: Wait for all mutation types to complete const mutationResults = await Promise.allSettled(mutationPromises); // Collect successful results mutationResults.forEach(result => { if (result.status === 'fulfilled') { candidatePool.addCandidates(result.value); } else { // eslint-disable-next-line no-console console.warn(`Mutation batch failed: ${result.reason}`); } }); return candidatePool.getCandidates(); } catch (error: any) { // eslint-disable-next-line no-console console.warn(`Failed to generate candidates: ${error.message}`); return []; } } /** * Evaluate a single candidate's performance */ async evaluateCandidate(candidate: PromptCandidate, taskContext: TaskContext): Promise<number> { // Check cache first const cacheKey = this.createCacheKey(candidate, taskContext); const cached = this.evaluationCache.get(cacheKey); if (cached && Date.now() - cached.timestamp < 3600000) { // 1 hour cache return cached.score; } // Wait for available evaluation slot while (this.activeConcurrentEvaluations >= this.maxConcurrentEvaluations) { await this.sleep(100); } this.activeConcurrentEvaluations++; try { // Evaluate with retry logic const result = await this.evaluateWithRetry(candidate.content, taskContext, 3); // Update candidate metrics candidate.taskPerformance.set(taskContext.description, result.score); candidate.averageScore = this.calculateAverageScore(candidate); candidate.rolloutCount++; candidate.lastEvaluated = new Date(); // Cache result this.evaluationCache.set(cacheKey, { score: result.score, timestamp: Date.now(), ...(result.trajectory ? { trajectory: result.trajectory } : {}) }); // Store trajectory if available if (result.trajectory) { try { await this.trajectoryStore.save(result.trajectory); } catch (error) { // eslint-disable-next-line no-console console.warn(`Failed to store trajectory: ${error}`); } } return result.score; } catch (error: any) { // eslint-disable-next-line no-console console.warn(`Failed to evaluate candidate ${candidate.id}: ${error.message}`); return 0; // Return 0 on evaluation failure } finally { this.activeConcurrentEvaluations--; } } /** * Evaluate with exponential backoff retry */ private async evaluateWithRetry( prompt: string, taskContext: TaskContext, maxRetries: number ): Promise<{ score: number; trajectory?: ExecutionTrajectory; metrics?: any }> { let lastError: Error | null = null; for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await this.llmAdapter.evaluatePrompt(prompt, taskContext); } catch (error: any) { lastError = error; if (attempt < maxRetries - 1) { // Exponential backoff: 1s, 2s, 4s const delay = Math.pow(2, attempt) * 1000; await this.sleep(delay); } } } throw lastError || new Error('Evaluation failed after retries'); } /** * Evaluate entire population with concurrency control (OPTIMIZED: Adaptive batching) */ private async evaluatePopulation(candidates: PromptCandidate[], taskContext: TaskContext): Promise<void> { // OPTIMIZATION: Adaptive batch sizing based on system resources const optimalBatchSize = Math.min( this.maxConcurrentEvaluations, Math.max(2, Math.floor(candidates.length / 4)) // Dynamic sizing ); // OPTIMIZATION: Priority-based evaluation (evaluate best candidates first) const sortedCandidates = [...candidates].sort((a, b) => b.averageScore - a.averageScore); for (let i = 0; i < sortedCandidates.length; i += optimalBatchSize) { const batch = sortedCandidates.slice(i, i + optimalBatchSize); // OPTIMIZATION: Use Promise.allSettled to continue on partial failures const evaluationResults = await Promise.allSettled( batch.map(candidate => this.evaluateCandidate(candidate, taskContext)) ); // Log failed evaluations without stopping the process evaluationResults.forEach((result, index) => { if (result.status === 'rejected') { const candidate = batch[index]; const candidateId = candidate?.id || 'unknown'; // eslint-disable-next-line no-console console.warn(`Evaluation failed for candidate ${candidateId}: ${result.reason}`); } }); } } /** * Select survivors for next generation using Pareto frontier */ async selectSurvivors(candidates: PromptCandidate[], targetSize: number): Promise<PromptCandidate[]> { // Add all candidates to Pareto frontier for (const candidate of candidates) { try { await this.paretoFrontier.addCandidate(candidate); } catch (error) { // eslint-disable-next-line no-console console.warn(`Failed to add candidate to frontier: ${error}`); } } // Get current frontier const frontierPoints = this.paretoFrontier.getFrontier(); const frontierCandidates = frontierPoints.map(point => point.candidate); // If we have enough candidates from frontier, use them if (frontierCandidates.length >= targetSize) { return frontierCandidates.slice(0, targetSize); } // Otherwise, supplement with best performing candidates const remainingCandidates = candidates .filter(c => !frontierCandidates.some(fc => fc.id === c.id)) .sort((a, b) => b.averageScore - a.averageScore); const survivors = [ ...frontierCandidates, ...remainingCandidates.slice(0, targetSize - frontierCandidates.length) ]; return survivors.slice(0, targetSize); } /** * Detect convergence based on diversity and performance metrics */ detectConvergence(metrics: ConvergenceMetrics, generation: number): boolean { // Require minimum generations before allowing convergence if (generation < 3) { return false; } // Check multiple convergence criteria const diversityConverged = metrics.diversity < 0.05; const spacingConverged = metrics.spacing < 0.05; const spreadConverged = metrics.spread < 0.05; const hypervolumeStable = metrics.hypervolume > 0.9; // Consider converged if multiple criteria are met const convergenceCriteriaMet = [ diversityConverged, spacingConverged, spreadConverged, hypervolumeStable ].filter(Boolean).length >= 3; return convergenceCriteriaMet; } /** * Check early stopping conditions */ private async checkEarlyStoppingConditions( generation: PromptCandidate[] ): Promise<boolean> { const bestCandidate = this.getBestCandidate(generation); // Stop if target score reached if (bestCandidate.averageScore >= this.targetScore) { return true; } // Stop if stagnation detected if (this.stagnationCounter >= this.stagnationThreshold) { return true; } // Stop if shutdown requested if (this.isShutdown) { return true; } return false; } /** * Update evolution history with memory management */ updateEvolutionHistory( history: PromptCandidate[][], generation: PromptCandidate[] ): void { history.push([...generation]); // Trim history to prevent memory issues (keep last 50 generations) if (history.length > 50) { history.splice(0, history.length - 50); } } /** * Get best candidate from population */ getBestCandidate(candidates: PromptCandidate[]): PromptCandidate { if (candidates.length === 0) { throw new Error('No candidates available'); } return candidates.reduce((best, current) => current.averageScore > best.averageScore ? current : best ); } /** * Get comprehensive evolution metrics */ getEvolutionMetrics(history: PromptCandidate[][], totalRollouts: number): EvolutionMetrics { if (history.length === 0) { return { totalGenerations: 0, totalRollouts: 0, bestScore: 0, averageScore: 0, convergenceMetrics: this.paretoFrontier.getConvergenceMetrics(), diversityTrend: [], improvementRate: 0, stagnationCount: this.stagnationCounter }; } const allCandidates = history.flat(); const scores = allCandidates.map(c => c.averageScore); const bestScore = Math.max(...scores); const averageScore = scores.reduce((sum, score) => sum + score, 0) / scores.length; const diversityTrend = this.generationStats.map(stats => stats.diversityScore); const improvementRate = this.calculateImprovementRate(); return { totalGenerations: history.length, totalRollouts, bestScore, averageScore, convergenceMetrics: this.paretoFrontier.getConvergenceMetrics(), diversityTrend, improvementRate, stagnationCount: this.stagnationCounter }; } /** * Shutdown evolution engine and cleanup resources */ async shutdown(): Promise<void> { this.isShutdown = true; // Wait for ongoing evaluations to complete while (this.activeConcurrentEvaluations > 0) { await this.sleep(100); } // Shutdown dependencies await this.llmAdapter.shutdown(); this.promptMutator.shutdown(); // Clear caches this.evaluationCache.clear(); this.generationStats = []; } // Private helper methods /** * Validate evolution parameters */ private validateEvolutionParams(params: StartEvolutionParams): void { if (!params.taskDescription || params.taskDescription.trim() === '') { throw new Error('Task description is required'); } if (params.config) { if (params.config.maxGenerations !== undefined && params.config.maxGenerations <= 0) { throw new Error('Max generations must be positive'); } if (params.config.populationSize !== undefined && params.config.populationSize <= 0) { throw new Error('Population size must be positive'); } if (params.config.mutationRate !== undefined && (params.config.mutationRate < 0 || params.config.mutationRate > 1)) { throw new Error('Mutation rate must be between 0 and 1'); } } } /** * Generate unique evolution ID */ private generateEvolutionId(): string { return `evolution-${Date.now()}-${Math.random().toString(36).substring(2)}`; } /** * Create task context from parameters */ private createTaskContext(params: StartEvolutionParams): TaskContext { return { taskId: this.generateEvolutionId(), description: params.taskDescription, category: 'prompt-evolution', difficulty: 'medium', requiredCapabilities: params.targetModules || ['reasoning'], expectedDuration: 60 }; } /** * Initialize population with seed prompt */ private async initializePopulation(params: StartEvolutionParams): Promise<PromptCandidate[]> { const seedPrompt = params.seedPrompt || this.config.seedPrompt || 'You are a helpful assistant.'; const seedCandidate: PromptCandidate = { id: this.generateCandidateId(), content: seedPrompt, generation: 0, taskPerformance: new Map(), averageScore: 0, rolloutCount: 0, createdAt: new Date(), lastEvaluated: new Date(), mutationType: 'initial' }; return [seedCandidate]; } /** * Get recent trajectories for reflection analysis (OPTIMIZED: Batch loading) */ private async getRecentTrajectories(population: PromptCandidate[]): Promise<ExecutionTrajectory[]> { try { const promptIds = population.map(p => p.id).slice(0, 5); // Limit to prevent performance issues // OPTIMIZATION: Batch parallel loading instead of sequential const trajectoryPromises = promptIds.map(async promptId => { try { return await this.trajectoryStore.query({ promptId, limit: 10 }); } catch (error) { // eslint-disable-next-line no-console console.warn(`Failed to query trajectories for prompt ${promptId}: ${error}`); return []; } }); const trajectoryBatches = await Promise.all(trajectoryPromises); return trajectoryBatches.flat(); } catch (error) { // eslint-disable-next-line no-console console.warn(`Failed to get recent trajectories: ${error}`); return []; } } /** * Create cache key for evaluation results */ private createCacheKey(candidate: PromptCandidate, taskContext: TaskContext): string { const contentHash = this.hashString(candidate.content); const contextHash = this.hashString(JSON.stringify(taskContext)); return `${contentHash}-${contextHash}`; } /** * Calculate average score across all tasks */ private calculateAverageScore(candidate: PromptCandidate): number { if (candidate.taskPerformance.size === 0) { return 0; } const scores = Array.from(candidate.taskPerformance.values()); return scores.reduce((sum, score) => sum + score, 0) / scores.length; } /** * Calculate diversity score for population */ private calculateDiversityScore(population: PromptCandidate[]): number { if (population.length < 2) { return 0; } // Use genetic diversity calculation from PromptMutator return this.promptMutator.calculateGeneticDiversity(population); } /** * Update stagnation counter based on improvement */ private updateStagnationCounter(currentBestScore: number): void { const improvement = currentBestScore - this.lastBestScore; if (improvement < this.improvementThreshold) { this.stagnationCounter++; } else { this.stagnationCounter = 0; } this.lastBestScore = currentBestScore; } /** * Calculate improvement rate over recent generations */ private calculateImprovementRate(): number { if (this.generationStats.length < 2) { return 0; } const recentStats = this.generationStats.slice(-5); // Last 5 generations const firstStat = recentStats[0]; const lastStat = recentStats[recentStats.length - 1]; if (!firstStat || !lastStat) return 0; const firstScore = firstStat.bestFitness; const lastScore = lastStat.bestFitness; return (lastScore - firstScore) / recentStats.length; } /** * Create fallback generation in case of failures */ private createFallbackGeneration(population: PromptCandidate[]): PromptCandidate[] { // Return best performers from current population return population .sort((a, b) => b.averageScore - a.averageScore) .slice(0, Math.min(this.config.populationSize, population.length)); } /** * Create fallback result for failed evolution */ private async createFallbackResult( params: StartEvolutionParams, currentPopulation: PromptCandidate[], generation: number, history: PromptCandidate[][] ): Promise<EvolutionResult> { const bestPrompt = currentPopulation.length > 0 ? this.getBestCandidate(currentPopulation) : await this.createEmergencyCandidate(params); return { evolutionId: this.currentEvolutionId || this.generateEvolutionId(), taskDescription: params.taskDescription, generations: generation, bestPrompt, convergenceAchieved: false, totalRollouts: currentPopulation.reduce((sum, c) => sum + c.rolloutCount, 0), evolutionHistory: history }; } /** * Create emergency candidate when all else fails */ private async createEmergencyCandidate(params: StartEvolutionParams): Promise<PromptCandidate> { return { id: this.generateCandidateId(), content: params.seedPrompt || 'You are a helpful assistant.', generation: 0, taskPerformance: new Map(), averageScore: 0, rolloutCount: 0, createdAt: new Date(), lastEvaluated: new Date(), mutationType: 'initial' }; } /** * Store generation metrics for analysis */ private async storeGenerationMetrics( generation: number, population: PromptCandidate[], convergenceMetrics: ConvergenceMetrics ): Promise<void> { try { const metrics = { evolutionId: this.currentEvolutionId, generation, populationSize: population.length, averageScore: population.reduce((sum, c) => sum + c.averageScore, 0) / population.length, bestScore: Math.max(...population.map(c => c.averageScore)), convergenceMetrics, timestamp: new Date() }; // Store metrics (implementation would depend on storage system) // await this.metricsStore.store(metrics); console.log(`Generation ${generation} metrics:`, metrics); } catch (error) { // eslint-disable-next-line no-console console.warn(`Failed to store generation metrics: ${error}`); } } /** * Store final evolution result */ private async storeEvolutionResult(result: EvolutionResult): Promise<void> { try { // Store evolution result (implementation would depend on storage system) // await this.evolutionStore.store(result); console.log('Evolution result stored:', result.evolutionId); } catch (error) { // eslint-disable-next-line no-console console.warn(`Failed to store evolution result: ${error}`); } } /** * Generate unique candidate ID */ private generateCandidateId(): string { return `candidate-${Date.now()}-${Math.random().toString(36).substring(2)}`; } /** * Simple string hash function */ private hashString(str: string): string { let hash = 0; for (let i = 0; i < str.length; i++) { const char = str.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32bit integer } return hash.toString(); } /** * Sleep utility for concurrency control */ private sleep(ms: number): Promise<void> { return new Promise(resolve => setTimeout(resolve, ms)); } /** * Generate reflective mutations with batch processing */ private async generateReflectiveMutationsBatch( candidates: PromptCandidate[], trajectoryMap: Map<string, ExecutionTrajectory[]>, maxCandidates: number ): Promise<PromptCandidate[]> { const mutations: PromptCandidate[] = []; for (const candidate of candidates.slice(0, Math.min(5, candidates.length))) { try { const trajectories = trajectoryMap.get(candidate.id) || []; if (trajectories.length === 0) continue; // Analyze failed trajectories for this candidate const failedTrajectories = trajectories.filter(t => !t.finalResult.success); if (failedTrajectories.length === 0) continue; // Get reflection analysis const failedTrajectory = failedTrajectories[0]; if (!failedTrajectory) continue; const analysis = await this.reflectionEngine.analyzeTrajectory(failedTrajectory); // Generate mutations based on suggestions for (const suggestion of analysis.suggestions.slice(0, 2)) { const mutatedPrompt = await this.applyImprovement(candidate.content, suggestion); const mutatedCandidate = this.createMutatedCandidate( candidate, mutatedPrompt, 'reflective' ); mutations.push(mutatedCandidate); if (mutations.length >= maxCandidates) break; } } catch (error) { // eslint-disable-next-line no-console console.warn(`Failed to generate reflective mutation for candidate ${candidate.id}:`, error); } if (mutations.length >= maxCandidates) break; } return mutations; } /** * Generate crossover mutations with batch processing */ private async generateCrossoverMutationsBatch( candidates: PromptCandidate[], maxCandidates: number ): Promise<PromptCandidate[]> { const mutations: PromptCandidate[] = []; // Simple crossover: combine best parts of different candidates for (let i = 0; i < candidates.length && mutations.length < maxCandidates; i++) { for (let j = i + 1; j < candidates.length && mutations.length < maxCandidates; j++) { try { const parent1 = candidates[i]; const parent2 = candidates[j]; if (!parent1 || !parent2) { continue; } // Simple crossover by combining prompts const crossoverPrompt = this.performCrossover(parent1.content, parent2.content); const crossoverCandidate = this.createMutatedCandidate( parent1, crossoverPrompt, 'crossover' ); mutations.push(crossoverCandidate); } catch (error) { // eslint-disable-next-line no-console console.warn(`Failed to generate crossover mutation:`, error); } } } return mutations; } /** * Generate adaptive mutations with batch processing */ private async generateAdaptiveMutationsBatch( candidates: PromptCandidate[], taskContext: TaskContext, maxCandidates: number ): Promise<PromptCandidate[]> { const mutations: PromptCandidate[] = []; for (const candidate of candidates.slice(0, Math.min(3, candidates.length))) { try { // Generate adaptive variations const adaptivePrompt = await this.generateAdaptiveVariation(candidate.content, taskContext); const adaptiveCandidate = this.createMutatedCandidate( candidate, adaptivePrompt, 'adaptive' ); mutations.push(adaptiveCandidate); if (mutations.length >= maxCandidates) break; } catch (error) { // eslint-disable-next-line no-console console.warn(`Failed to generate adaptive mutation for candidate ${candidate.id}:`, error); } } return mutations; } /** * Apply improvement suggestion to prompt */ private async applyImprovement(prompt: string, improvement: any): Promise<string> { try { return await this.llmAdapter.generateMutation(prompt, improvement); } catch (error) { // eslint-disable-next-line no-console console.warn('Failed to apply improvement via LLM, using fallback:', error); return this.applyImprovementFallback(prompt, improvement); } } /** * Fallback improvement application */ private applyImprovementFallback(prompt: string, improvement: any): string { let modifiedPrompt = prompt; switch (improvement.type) { case 'add_instruction': modifiedPrompt += `\n\nAdditional instruction: ${improvement.proposedChange}`; break; case 'clarify_step': modifiedPrompt += `\n\nClarification: ${improvement.proposedChange}`; break; case 'add_example': modifiedPrompt += `\n\nExample: ${improvement.proposedChange}`; break; case 'add_constraint': modifiedPrompt += `\n\nConstraint: ${improvement.proposedChange}`; break; default: modifiedPrompt += `\n\nImprovement: ${improvement.proposedChange}`; } return modifiedPrompt; } /** * Perform crossover between two prompts */ private performCrossover(prompt1: string, prompt2: string): string { // Simple crossover: take first half of prompt1 and second half of prompt2 const lines1 = prompt1.split('\n'); const lines2 = prompt2.split('\n'); const midpoint1 = Math.floor(lines1.length / 2); const midpoint2 = Math.floor(lines2.length / 2); const firstHalf = lines1.slice(0, midpoint1); const secondHalf = lines2.slice(midpoint2); return [...firstHalf, ...secondHalf].join('\n'); } /** * Generate adaptive variation based on task context */ private async generateAdaptiveVariation(prompt: string, taskContext: TaskContext): Promise<string> { // Add task-specific adaptations let adaptedPrompt = prompt; if (taskContext.requiredCapabilities.includes('reasoning')) { adaptedPrompt += '\n\nUse step-by-step reasoning to solve this problem.'; } if (taskContext.difficulty === 'hard') { adaptedPrompt += '\n\nThis is a complex problem. Take your time and think carefully.'; } if (taskContext.expectedDuration > 120) { adaptedPrompt += '\n\nThis may require multiple steps. Plan your approach first.'; } return adaptedPrompt; } /** * Create mutated candidate from parent */ private createMutatedCandidate( parent: PromptCandidate, newContent: string, mutationType: string ): PromptCandidate { return { id: this.generateCandidateId(), content: newContent, generation: parent.generation + 1, taskPerformance: new Map(), averageScore: 0, rolloutCount: 0, createdAt: new Date(), lastEvaluated: new Date(), mutationType: mutationType as 'initial' | 'reflection' | 'crossover' | 'random' }; } } /** * Candidate Pool for managing generation candidates */ class CandidatePool { private candidates: PromptCandidate[] = []; private readonly maxSize: number; constructor(maxSize: number) { this.maxSize = maxSize; } addCandidates(newCandidates: PromptCandidate[]): void { this.candidates.push(...newCandidates); // Deduplicate by content const seen = new Set<string>(); this.candidates = this.candidates.filter(candidate => { const key = this.getContentHash(candidate.content); if (seen.has(key)) { return false; } seen.add(key); return true; }); // Limit size if (this.candidates.length > this.maxSize) { this.candidates = this.candidates.slice(0, this.maxSize); } } getCandidates(): PromptCandidate[] { return [...this.candidates]; } private getContentHash(content: string): string { let hash = 0; for (let i = 0; i < content.length; i++) { const char = content.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32bit integer } return hash.toString(36); } }

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/sloth-wq/prompt-auto-optimizer-mcp'

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