UNPKG

adpa-enterprise-framework-automation

Version:

Modular, standards-compliant Node.js/TypeScript automation framework for enterprise requirements, project, and data management. Provides CLI and API for BABOK v3, PMBOK 7th Edition, and DMBOK 2.0 (in progress). Production-ready Express.js API with TypeSpe

483 lines (480 loc) 20.3 kB
/** * Advanced Template Engine for Requirements Gathering Agent * * Revolutionary context injection and AI instruction system with database backing. * Preserves the brilliant context building and injection architecture while adding * dynamic, database-driven template management. * * @version 3.0.0 * @author Requirements Gathering Agent Team * @created June 2025 * * Key Revolutionary Features: * - Dynamic context building with dependency resolution * - AI instruction templates with variable injection * - Large context management and intelligent chunking * - Database-backed template versioning and evolution * - Cross-document relationship analysis * - Context inheritance and composition * * @filepath c:\Users\menno\Source\Repos\requirements-gathering-agent\src\modules\templates\AdvancedTemplateEngine.ts */ import { AIProcessor } from '../ai/AIProcessor.js'; import { ContextManager } from '../contextManager.js'; export class AdvancedTemplateEngine { aiProcessor; contextManager; contextRepository; templateRepository; templates = new Map(); contextCache = new Map(); constructor(contextRepository, aiProcessor, contextManager, templateRepository) { this.contextRepository = contextRepository; this.templateRepository = templateRepository || null; // TODO: Fix database connection this.aiProcessor = aiProcessor || AIProcessor.getInstance(); this.contextManager = contextManager || new ContextManager(); } /** * Revolutionary Context Building with Smart Dependency Resolution * * This method implements the brilliant context injection architecture * but with database backing and intelligent dependency management. */ async buildEnhancedContext(templateId, baseContext, variables = {}) { const template = this.templates.get(templateId); if (!template) { throw new Error(`Template not found: ${templateId}`); } let enrichedContext = baseContext; // Process each context injection point for (const injectionPoint of template.contextInjectionPoints) { const contextSegment = await this.buildContextSegment(injectionPoint, variables); enrichedContext = enrichedContext.replace(injectionPoint.placeholder, contextSegment); } // Apply conditional logic enrichedContext = await this.applyConditionalLogic(enrichedContext, template.conditionalLogic, variables); // Perform final context optimization enrichedContext = await this.optimizeContext(enrichedContext, template.maxTokens); return enrichedContext; } /** * Build Context Segment with Intelligent Dependency Resolution * * This preserves your revolutionary dependency-based context building * while adding database-backed dynamic resolution. */ async buildContextSegment(injectionPoint, variables) { const contextSegments = []; // Resolve dependencies with intelligent caching for (const dependency of injectionPoint.dependencies) { const cacheKey = `${dependency.documentKey}_${JSON.stringify(variables)}`; let content = await this.getCachedContext(cacheKey); if (!content) { content = await this.resolveDependency(dependency, variables); if (content && dependency.maxAge) { await this.cacheContext(cacheKey, content, dependency.maxAge); } } if (content) { // Apply context transformation if specified if (dependency.contextTransform) { content = dependency.contextTransform(content); } contextSegments.push({ content, weight: dependency.weight }); } else if (dependency.required) { throw new Error(`Required dependency not found: ${dependency.documentKey}`); } } // Apply aggregation strategy return await this.aggregateContextSegments(contextSegments, injectionPoint.aggregationStrategy, injectionPoint.maxLength); } /** * Revolutionary Dependency Resolution with Cross-Document Intelligence */ async resolveDependency(dependency, variables) { // First, try direct document retrieval let content = await this.contextRepository.getDocument(dependency.documentKey); if (!content) { // Try semantic search if direct lookup fails const searchResults = await this.contextRepository.searchDocuments(dependency.documentKey); if (searchResults.length > 0) { content = await this.contextRepository.getDocument(searchResults[0].key); } } if (!content) { // Try to find related documents const relatedDocs = await this.contextRepository.getRelatedDocuments(dependency.documentKey); if (relatedDocs.length > 0) { const relatedContent = await Promise.all(relatedDocs.slice(0, 3).map(key => this.contextRepository.getDocument(key))); content = relatedContent.filter(Boolean).join('\n\n---\n\n'); } } return content; } /** * Intelligent Context Aggregation with Multiple Strategies */ async aggregateContextSegments(segments, strategy, maxLength) { switch (strategy) { case 'concatenate': return this.concatenateSegments(segments, maxLength); case 'summarize': return await this.summarizeSegments(segments, maxLength); case 'prioritize': return this.prioritizeSegments(segments, maxLength); case 'template': return this.templateAggregation(segments, maxLength); default: return this.concatenateSegments(segments, maxLength); } } /** * AI-Powered Context Summarization for Large Context Management */ async summarizeSegments(segments, maxLength) { const combinedContent = segments .sort((a, b) => b.weight - a.weight) .map(s => s.content) .join('\n\n---\n\n'); if (!maxLength || combinedContent.length <= maxLength) { return combinedContent; } // Use AI to intelligently summarize while preserving key information const messages = [ { role: 'system', content: 'You are an expert at summarizing project documentation while preserving all critical information and context.' }, { role: 'user', content: `Summarize the following project documentation, preserving all critical information, key requirements, and important details. Target length: approximately ${maxLength} characters.\n\n${combinedContent}` } ]; const response = await this.aiProcessor.makeAICall(messages, Math.min(maxLength * 2, 4000)); return this.aiProcessor.extractContent(response) || combinedContent.substring(0, maxLength); } /** * Priority-Based Context Assembly */ prioritizeSegments(segments, maxLength) { const sortedSegments = segments.sort((a, b) => b.weight - a.weight); let result = ''; let currentLength = 0; for (const segment of sortedSegments) { if (maxLength && currentLength + segment.content.length > maxLength) { // Try to fit a truncated version const remainingSpace = maxLength - currentLength; if (remainingSpace > 100) { // Only include if meaningful space remains result += segment.content.substring(0, remainingSpace - 3) + '...'; } break; } result += (result ? '\n\n---\n\n' : '') + segment.content; currentLength = result.length; } return Promise.resolve(result); } /** * Template-Based Context Assembly */ templateAggregation(segments, maxLength) { // Create a structured template based on segment weights and content const template = ` # Context Overview ## Primary Context (High Priority) ${segments.filter(s => s.weight >= 0.8).map(s => s.content).join('\n\n')} ## Supporting Context (Medium Priority) ${segments.filter(s => s.weight >= 0.5 && s.weight < 0.8).map(s => s.content).join('\n\n')} ## Additional Context (Low Priority) ${segments.filter(s => s.weight < 0.5).map(s => s.content).join('\n\n')} `.trim(); if (maxLength && template.length > maxLength) { return this.prioritizeSegments(segments, maxLength); } return Promise.resolve(template); } /** * Simple concatenation with length management */ concatenateSegments(segments, maxLength) { const result = segments .sort((a, b) => b.weight - a.weight) .map(s => s.content) .join('\n\n---\n\n'); if (maxLength && result.length > maxLength) { return this.prioritizeSegments(segments, maxLength); } return Promise.resolve(result); } /** * Apply Conditional Logic to Template Content */ async applyConditionalLogic(content, rules, variables) { let processedContent = content; for (const rule of rules) { try { // Create a safe evaluation context const evalContext = { ...variables, content: processedContent }; const conditionResult = this.evaluateCondition(rule.condition, evalContext); if (conditionResult) { switch (rule.action) { case 'include': // Content already included, no action needed break; case 'exclude': processedContent = this.removeSection(processedContent, rule.target); break; case 'modify': processedContent = this.modifySection(processedContent, rule.target, rule.value); break; } } } catch (error) { console.warn(`Error applying conditional rule: ${rule.condition}`, error); } } return processedContent; } /** * Safe condition evaluation (simplified for security) */ evaluateCondition(condition, context) { // This is a simplified implementation. In production, use a proper expression evaluator // that prevents code injection try { // Basic variable substitution for common patterns let evaluableCondition = condition; // Replace variable references for (const [key, value] of Object.entries(context)) { const regex = new RegExp(`\\b${key}\\b`, 'g'); evaluableCondition = evaluableCondition.replace(regex, JSON.stringify(value)); } // Only allow safe operations if (!/^[a-zA-Z0-9_.$"\s>=<!=&|()]+$/.test(evaluableCondition)) { console.warn('Unsafe condition detected:', condition); return false; } return Function(`"use strict"; return (${evaluableCondition})`)(); } catch { return false; } } /** * Context Optimization for Large Context Management */ async optimizeContext(content, maxTokens) { // Estimate tokens (rough approximation: 1 token ≈ 4 characters) const estimatedTokens = content.length / 4; if (estimatedTokens <= maxTokens) { return content; } // Intelligent truncation while preserving structure const targetLength = maxTokens * 4; // Try to preserve section headers and important content const sections = content.split(/\n#{1,6}\s/); let optimizedContent = ''; let currentLength = 0; for (const section of sections) { if (currentLength + section.length <= targetLength) { optimizedContent += (optimizedContent ? '\n# ' : '') + section; currentLength = optimizedContent.length; } else { // Add truncated section if space allows const remainingSpace = targetLength - currentLength; if (remainingSpace > 200) { optimizedContent += (optimizedContent ? '\n# ' : '') + section.substring(0, remainingSpace - 100) + '\n\n[Content truncated...]'; } break; } } return optimizedContent; } /** * Generate Document with Revolutionary Context Injection * * This is the main method that brings together all the revolutionary features */ async generateDocument(templateId, baseContext, variables = {}) { const template = this.templates.get(templateId); if (!template) { throw new Error(`Template not found: ${templateId}`); } // Build enhanced context with dependency resolution const enhancedContext = await this.buildEnhancedContext(templateId, baseContext, variables); // Process variables in user prompt template let userPrompt = template.userPromptTemplate; for (const [key, value] of Object.entries(variables)) { userPrompt = userPrompt.replace(new RegExp(`{{${key}}}`, 'g'), String(value)); } // Replace remaining context placeholders userPrompt = userPrompt.replace(/{{ENHANCED_CONTEXT}}/g, enhancedContext); // Create AI messages const messages = [ { role: 'system', content: template.systemPrompt }, { role: 'user', content: userPrompt } ]; // Generate content with template preferences const response = await this.aiProcessor.makeAICall(messages, template.maxTokens); const generatedContent = this.aiProcessor.extractContent(response); if (!generatedContent) { throw new Error('Failed to generate content from AI'); } // Apply quality checks await this.performQualityChecks(generatedContent, template.qualityChecks); return generatedContent; } /** * Template Management Methods */ async saveTemplate(template) { // Save to in-memory cache for quick access this.templates.set(template.id, template); // Convert AIInstructionTemplate to DatabaseTemplate format and save to database try { const databaseTemplate = { name: template.name, description: `AI Instruction Template: ${template.name}`, category: template.category || 'General', template_type: 'ai_instruction', ai_instructions: template.systemPrompt || '', prompt_template: template.userPromptTemplate || '', generation_function: 'getAiGenericDocument', metadata: { id: template.id, variables: template.variables || [], conditionalLogic: template.conditionalLogic || [], contextInjectionPoints: template.contextInjectionPoints || [], maxTokens: template.maxTokens || 4000, temperature: template.temperature || 0.7, modelPreferences: template.modelPreferences || [], qualityChecks: template.qualityChecks || [], emoji: '🤖', priority: 100 }, version: parseInt(template.version) || 1, is_active: template.isActive !== false, is_system: true, created_by: 'system' }; if (this.templateRepository) { await this.templateRepository.createTemplate(databaseTemplate); } console.log(`Template saved: ${template.id}`); } catch (error) { console.warn(`Failed to save template to database: ${template.id}`, error); // Continue with in-memory storage even if database fails } } async loadTemplate(templateId) { return this.templates.get(templateId) || null; } async loadAllTemplates() { return Array.from(this.templates.values()); } /** * Cache Management */ async getCachedContext(key) { const cached = this.contextCache.get(key); if (cached && Date.now() - cached.timestamp < cached.ttl) { return cached.content; } this.contextCache.delete(key); return null; } async cacheContext(key, content, ttl) { this.contextCache.set(key, { content, timestamp: Date.now(), ttl }); } /** * Utility Methods */ removeSection(content, target) { // Remove specified section from content const regex = new RegExp(`## ${target}[\\s\\S]*?(?=## |$)`, 'i'); return content.replace(regex, ''); } modifySection(content, target, value) { // Modify specified section in content const regex = new RegExp(`(## ${target}[\\s\\S]*?)(?=## |$)`, 'i'); return content.replace(regex, `## ${target}\n${value}\n\n`); } async performQualityChecks(content, checks) { for (const check of checks) { // Implement quality checks based on type switch (check.type) { case 'length': if (content.length < check.criteria.min || content.length > check.criteria.max) { console.warn(`Quality check failed: ${check.name} - Length: ${content.length}`); } break; case 'keywords': const hasRequiredKeywords = check.criteria.required.every((keyword) => content.toLowerCase().includes(keyword.toLowerCase())); if (!hasRequiredKeywords) { console.warn(`Quality check failed: ${check.name} - Missing required keywords`); } break; // Add more quality check types as needed } } } } /** * Factory for creating template engine with database repository */ export class TemplateEngineFactory { static async createWithDatabase(databaseConfig, aiProcessor, contextManager) { // In a real implementation, create database-backed context repository const contextRepository = new MockContextRepository(); return new AdvancedTemplateEngine(contextRepository, aiProcessor, contextManager); } } /** * Mock implementation for development */ class MockContextRepository { documents = new Map(); metadata = new Map(); async getDocument(key) { return this.documents.get(key) || null; } async getRelatedDocuments(key) { // Mock implementation - in real version, use semantic similarity return Array.from(this.documents.keys()).filter(k => k !== key).slice(0, 3); } async getDocumentMetadata(key) { return this.metadata.get(key) || null; } async searchDocuments(query) { // Mock implementation - in real version, use vector search const results = []; for (const [key, content] of this.documents) { if (content.toLowerCase().includes(query.toLowerCase())) { results.push({ key, title: key, relevance: 0.8, excerpt: content.substring(0, 200) }); } } return results; } async cacheContext(key, context, ttl) { // Mock implementation } async getCachedContext(key) { // Mock implementation return null; } } //# sourceMappingURL=AdvancedTemplateEngine.js.map