vibe-coder-mcp
Version:
Production-ready MCP server with complete agent integration, multi-transport support, and comprehensive development automation tools for AI-assisted workflows.
230 lines (229 loc) • 9.91 kB
JavaScript
import logger from '../logger.js';
export class PromptOptimizer {
static instance;
config;
errorPatterns = new Map();
promptSuccessRates = new Map();
constructor(config) {
this.config = config;
this.loadErrorPatterns();
}
static getInstance(config) {
if (!PromptOptimizer.instance) {
const defaultConfig = {
enableJsonOptimization: true,
includeSchemaHints: true,
useErrorPatternLearning: true,
maxPromptLength: 4000
};
PromptOptimizer.instance = new PromptOptimizer(config || defaultConfig);
}
return PromptOptimizer.instance;
}
static resetInstance() {
PromptOptimizer.instance = undefined;
}
optimizeForJsonGeneration(systemPrompt, userPrompt, taskName, expectedSchema) {
if (!this.config.enableJsonOptimization) {
return {
optimizedSystemPrompt: systemPrompt,
optimizedUserPrompt: userPrompt,
optimizationApplied: [],
confidenceScore: 1.0
};
}
const optimizations = [];
let optimizedSystem = systemPrompt;
let optimizedUser = userPrompt;
const jsonTemplate = this.getJsonPromptTemplate(taskName);
optimizedSystem = this.enhanceSystemPrompt(optimizedSystem, jsonTemplate);
optimizations.push('json-system-enhancement');
optimizedUser = this.enhanceUserPrompt(optimizedUser, jsonTemplate, expectedSchema);
optimizations.push('json-user-enhancement');
const errorPreventionRules = this.getErrorPreventionRules(taskName);
if (errorPreventionRules.length > 0) {
optimizedSystem += '\n\n' + errorPreventionRules.join('\n');
optimizations.push('error-prevention-rules');
}
if (this.config.includeSchemaHints && expectedSchema) {
const schemaHints = this.generateSchemaHints(expectedSchema);
optimizedUser += '\n\n' + schemaHints;
optimizations.push('schema-hints');
}
const confidenceScore = this.calculateConfidenceScore(taskName, optimizations);
logger.debug({
taskName,
optimizations,
confidenceScore,
originalSystemLength: systemPrompt.length,
optimizedSystemLength: optimizedSystem.length,
originalUserLength: userPrompt.length,
optimizedUserLength: optimizedUser.length
}, 'Prompt optimization completed');
return {
optimizedSystemPrompt: optimizedSystem,
optimizedUserPrompt: optimizedUser,
optimizationApplied: optimizations,
confidenceScore
};
}
getJsonPromptTemplate(taskName) {
const baseTemplate = {
systemPromptEnhancement: `
CRITICAL JSON OUTPUT REQUIREMENTS:
- You MUST respond with valid, parseable JSON only
- Do NOT include markdown code blocks, backticks, or any formatting
- Do NOT add explanatory text before or after the JSON
- Start your response with { and end with }
- Ensure all strings are properly quoted with double quotes
- Ensure all object keys are strings in double quotes
- Do NOT use trailing commas
- Do NOT use single quotes
- Escape special characters properly (\\n, \\t, \\", \\\\)`,
userPromptEnhancement: `
OUTPUT FORMAT: Respond with a single, valid JSON object. No additional text, formatting, or explanations.`,
outputFormatInstructions: `
Your response must be a single JSON object that can be parsed by JSON.parse() without any modifications.`,
errorPreventionRules: [
'Never use single quotes - always use double quotes for strings and keys',
'Never include trailing commas after the last property',
'Always escape special characters in strings (\\n, \\t, \\", \\\\)',
'Never include comments in JSON output',
'Ensure all brackets and braces are properly matched',
'Use null instead of undefined for missing values'
]
};
if (taskName.includes('module_selection') || taskName.includes('yaml')) {
baseTemplate.errorPreventionRules.push('For large numbers (>15 digits), use strings to prevent precision loss', 'Ensure nested objects have proper comma separation', 'Validate that all required schema fields are included');
}
return baseTemplate;
}
enhanceSystemPrompt(systemPrompt, template) {
return template.systemPromptEnhancement + '\n\n' + systemPrompt + '\n\n' + template.outputFormatInstructions;
}
enhanceUserPrompt(userPrompt, template, _schema) {
let enhanced = userPrompt;
enhanced += '\n\n' + template.userPromptEnhancement;
enhanced += '\n\nIMPORTANT: Your entire response must be a single, valid JSON object with no additional text.';
return enhanced;
}
generateSchemaHints(schema) {
try {
const schemaStr = JSON.stringify(schema, null, 2);
return `EXPECTED JSON STRUCTURE EXAMPLE:\n${schemaStr}\n\nEnsure your response matches this structure exactly.`;
}
catch (error) {
logger.warn({ error }, 'Failed to generate schema hints');
return '';
}
}
getErrorPreventionRules(taskName) {
const rules = [];
for (const [, errorData] of this.errorPatterns) {
if (errorData.frequency > 1) {
rules.push(`AVOID: ${errorData.preventionRule}`);
}
}
if (taskName.includes('module_selection') || taskName.includes('yaml')) {
rules.push('AVOID: Using large numbers without string conversion');
rules.push('AVOID: Missing commas between nested object properties');
rules.push('AVOID: Omitting required schema fields');
}
return rules;
}
calculateConfidenceScore(taskName, optimizations) {
const baseScore = 0.7;
const optimizationBonus = optimizations.length * 0.05;
const successData = this.promptSuccessRates.get(taskName);
const historicalBonus = successData
? (successData.successes / successData.total) * 0.2
: 0;
return Math.min(1.0, baseScore + optimizationBonus + historicalBonus);
}
recordParsingResult(taskName, success, error) {
if (!this.config.useErrorPatternLearning)
return;
const successData = this.promptSuccessRates.get(taskName) || { successes: 0, total: 0 };
successData.total++;
if (success) {
successData.successes++;
}
this.promptSuccessRates.set(taskName, successData);
if (!success && error) {
this.learnFromError(error);
}
logger.debug({
taskName,
success,
successRate: successData.successes / successData.total,
totalAttempts: successData.total
}, 'Recorded parsing result for prompt optimization learning');
}
learnFromError(error) {
const patterns = this.extractErrorPatterns(error);
for (const pattern of patterns) {
const existing = this.errorPatterns.get(pattern.pattern) || {
pattern: pattern.pattern,
frequency: 0,
lastSeen: new Date(),
preventionRule: pattern.preventionRule
};
existing.frequency++;
existing.lastSeen = new Date();
this.errorPatterns.set(pattern.pattern, existing);
}
}
extractErrorPatterns(error) {
const patterns = [];
if (error.includes('position 2572') || error.includes('missing comma')) {
patterns.push({
pattern: 'missing_comma',
preventionRule: 'Always include commas between object properties'
});
}
if (error.includes('control character') || error.includes('position 1210')) {
patterns.push({
pattern: 'control_character',
preventionRule: 'Escape control characters in strings (\\n, \\t, etc.)'
});
}
if (error.includes('trailing comma')) {
patterns.push({
pattern: 'trailing_comma',
preventionRule: 'Never include trailing commas after the last property'
});
}
if (error.includes('single quote') || error.includes("'")) {
patterns.push({
pattern: 'single_quotes',
preventionRule: 'Always use double quotes, never single quotes'
});
}
return patterns;
}
loadErrorPatterns() {
logger.debug('Error patterns loaded (currently empty - will be learned during runtime)');
}
getOptimizationStats() {
const totalTasks = this.promptSuccessRates.size;
const averageSuccessRate = Array.from(this.promptSuccessRates.values())
.reduce((sum, data) => sum + (data.successes / data.total), 0) / totalTasks || 0;
const topErrors = Array.from(this.errorPatterns.values())
.sort((a, b) => b.frequency - a.frequency)
.slice(0, 5)
.map(error => ({ pattern: error.pattern, frequency: error.frequency }));
return {
totalTasks,
averageSuccessRate,
errorPatterns: this.errorPatterns.size,
topErrors
};
}
}
export function getPromptOptimizer(config) {
return PromptOptimizer.getInstance(config);
}
export function optimizeJsonPrompts(systemPrompt, userPrompt, taskName, expectedSchema) {
const optimizer = getPromptOptimizer();
return optimizer.optimizeForJsonGeneration(systemPrompt, userPrompt, taskName, expectedSchema);
}