UNPKG

supa-seed

Version:

A constraint-aware, framework-agnostic database seeding framework with deep PostgreSQL business logic discovery and MakerKit integration support

518 lines (493 loc) 19 kB
"use strict"; /** * Domain-Specific Prompt Engine * Phase 5, Checkpoint E1 - Intelligent prompts for seeding and template generation */ Object.defineProperty(exports, "__esModule", { value: true }); exports.promptEngine = exports.DomainPromptEngine = void 0; const logger_1 = require("../core/utils/logger"); class DomainPromptEngine { constructor() { this.promptTemplates = new Map(); this.domainKnowledge = new Map(); this.initializeBuiltInTemplates(); this.initializeDomainKnowledge(); } /** * Generate a contextual prompt for AI generation */ generatePrompt(request) { const template = this.getTemplateForRequest(request); if (!template) { throw new Error(`No prompt template found for type: ${request.type}`); } logger_1.Logger.debug(`🧠 Generating ${request.type} prompt for ${request.context.table || 'unknown'}`); // Build system prompt with domain context const systemPrompt = this.buildSystemPrompt(template, request.context); // Build user prompt with specific requirements const userPrompt = this.buildUserPrompt(template, request); // Generate JSON schema for structured output const schema = this.generateOutputSchema(template, request); return { system: systemPrompt, user: userPrompt, schema }; } /** * Generate seed data prompts */ generateSeedDataPrompt(context, count = 10) { const request = { type: 'seed_data', context, count, format: 'json' }; return this.generatePrompt(request); } /** * Generate template content prompts */ generateTemplatePrompt(context, templateType) { const request = { type: 'template_content', context: { ...context, domain: templateType }, format: 'typescript' }; const result = this.generatePrompt(request); return { system: result.system, user: result.user }; } /** * Generate schema improvement suggestions */ generateSchemaAnalysisPrompt(schema) { const request = { type: 'schema_suggestion', context: { schema }, format: 'json' }; const result = this.generatePrompt(request); return { system: result.system, user: result.user }; } /** * Add custom prompt template */ addPromptTemplate(template) { this.promptTemplates.set(template.id, template); logger_1.Logger.debug(`🧠 Added prompt template: ${template.name}`); } /** * Get available templates */ getAvailableTemplates() { return Array.from(this.promptTemplates.values()); } /** * Add domain knowledge */ addDomainKnowledge(domain, knowledge) { this.domainKnowledge.set(domain, knowledge); logger_1.Logger.debug(`🧠 Added domain knowledge: ${domain}`); } /** * Private: Initialize built-in prompt templates */ initializeBuiltInTemplates() { // Seed Data Generation Template this.addPromptTemplate({ id: 'seed_data_realistic', name: 'Realistic Seed Data Generator', description: 'Generates realistic seed data based on table schema', type: 'seed_data', systemPrompt: `You are an expert database seeding specialist. Your task is to generate realistic, diverse, and coherent seed data for database tables. Key principles: - Generate data that feels authentic and realistic - Ensure data relationships are logical and consistent - Follow common naming patterns and conventions - Consider cultural and regional variations when appropriate - Maintain data quality and avoid obvious patterns - Respect data types and constraints strictly Always respond with valid JSON only, no additional text or explanations.`, userPromptTemplate: `Generate {{count}} realistic records for the "{{table}}" table with the following schema: {{#if columns}} Columns: {{#each columns}} - {{name}} ({{type}}){{#if nullable}} - nullable{{/if}} {{/each}} {{/if}} {{#if relationships}} Relationships: {{#each relationships}} - {{from}} -> {{to}} ({{type}}) {{/each}} {{/if}} {{#if constraints}} Constraints: {{#each constraints}} - {{field}}: {{rule}}{{#if value}} = {{value}}{{/if}} {{/each}} {{/if}} {{#if domain}} Domain context: {{domain}} {{/if}} {{#if style}} Data style: {{style}} {{/if}} Generate data that is: 1. Realistic and diverse 2. Internally consistent 3. Appropriate for the domain 4. Following proper data types 5. Respectful of constraints Return as JSON array of objects.`, outputFormat: 'json', validation: { required_fields: ['records'], data_types: { records: 'array' } } }); // Template Content Generation this.addPromptTemplate({ id: 'template_content_generator', name: 'Template Content Generator', description: 'Generates template code for seeders and configurations', type: 'template_content', systemPrompt: `You are an expert TypeScript and database seeding developer. Generate clean, efficient, and well-documented seeding templates. Guidelines: - Use modern TypeScript patterns - Include proper type annotations - Add helpful comments - Follow naming conventions - Ensure code is reusable and maintainable - Handle edge cases appropriately`, userPromptTemplate: `Generate a {{domain}} template for table "{{table}}" with the following specifications: {{#if columns}} Table Columns: {{#each columns}} - {{name}}: {{type}} {{/each}} {{/if}} {{#if schema}} Schema Context: - Framework: {{schema.frameworkType}} - Version: {{schema.makerkitVersion}} - Has Users: {{schema.hasUsers}} - Has Profiles: {{schema.hasProfiles}} - Custom Tables: {{join schema.customTables ", "}} {{/if}} Requirements: 1. Generate TypeScript seeder class 2. Include realistic data generation 3. Handle relationships properly 4. Add proper error handling 5. Include configuration options Template should be production-ready and well-documented.`, outputFormat: 'text' }); // Schema Analysis Template this.addPromptTemplate({ id: 'schema_analysis', name: 'Schema Improvement Analyzer', description: 'Analyzes database schema and suggests improvements', type: 'schema_suggestion', systemPrompt: `You are a database architecture expert. Analyze the provided schema and suggest improvements for better seeding, performance, and maintainability. Focus on: - Relationship opportunities - Missing indexes for seeding - Data type optimizations - Constraint improvements - Seeding strategy recommendations`, userPromptTemplate: `Analyze this database schema and provide improvement suggestions: Schema Information: - Framework: {{schema.frameworkType}} - Version: {{schema.makerkitVersion}} - Has Users: {{schema.hasUsers}} - Has Profiles: {{schema.hasProfiles}} - Has Teams: {{schema.hasTeams}} - Primary User Table: {{schema.primaryUserTable}} Custom Tables: {{join schema.customTables ", "}} {{#if schema.detectedRelationships}} Detected Relationships: {{#each schema.detectedRelationships}} - {{from}} -> {{to}} ({{type}}) {{/each}} {{/if}} Provide suggestions for: 1. Missing relationships that could be detected 2. Seeding order recommendations 3. Performance improvements for seeding 4. Data consistency checks 5. Additional constraints that would help seeding Return as structured JSON with categories and specific recommendations.`, outputFormat: 'json', validation: { required_fields: ['suggestions'], data_types: { suggestions: 'array' } } }); // Field Name Generation this.addPromptTemplate({ id: 'field_names_generator', name: 'Smart Field Names Generator', description: 'Generates contextually appropriate field names', type: 'field_names', systemPrompt: `You are a database design expert specializing in naming conventions. Generate meaningful, consistent field names that follow best practices. Principles: - Use clear, descriptive names - Follow snake_case for database fields - Avoid abbreviations unless they're standard - Consider the domain context - Ensure names are searchable and understandable`, userPromptTemplate: `Generate appropriate field names for a {{table}} table in the {{domain}} domain. Context: {{#if columns}} Existing columns: {{join (map columns "name") ", "}} {{/each}} {{#if relationships}} Related to: {{#each relationships}} - {{to}} table {{/each}} {{/if}} Generate 10-15 additional field names that would be commonly needed for this table type. Consider both required and optional fields. Return as JSON array of objects with: name, type, description, required boolean.`, outputFormat: 'json' }); } /** * Private: Initialize domain knowledge */ initializeDomainKnowledge() { // E-commerce domain knowledge this.addDomainKnowledge('ecommerce', { common_tables: ['products', 'orders', 'customers', 'categories', 'inventory'], naming_patterns: { products: ['name', 'description', 'price', 'sku', 'category_id', 'stock_quantity'], orders: ['order_number', 'customer_id', 'total_amount', 'status', 'created_at'], customers: ['first_name', 'last_name', 'email', 'phone', 'address'] }, realistic_data: { products: { categories: ['Electronics', 'Clothing', 'Books', 'Home & Garden', 'Sports'], price_ranges: { min: 5, max: 500 }, status_options: ['active', 'inactive', 'discontinued'] } } }); // Blog/CMS domain knowledge this.addDomainKnowledge('blog', { common_tables: ['posts', 'authors', 'categories', 'comments', 'tags'], naming_patterns: { posts: ['title', 'content', 'excerpt', 'author_id', 'published_at', 'status'], authors: ['name', 'bio', 'avatar_url', 'social_links'], comments: ['post_id', 'author_name', 'content', 'approved', 'created_at'] }, realistic_data: { posts: { categories: ['Technology', 'Lifestyle', 'Business', 'Travel', 'Food'], status_options: ['draft', 'published', 'archived'], content_length: { min: 500, max: 2000 } } } }); // SaaS domain knowledge this.addDomainKnowledge('saas', { common_tables: ['users', 'subscriptions', 'plans', 'usage_metrics', 'billing'], naming_patterns: { users: ['email', 'first_name', 'last_name', 'role', 'subscription_id'], subscriptions: ['plan_id', 'user_id', 'status', 'started_at', 'expires_at'], plans: ['name', 'price', 'features', 'max_users', 'billing_interval'] }, realistic_data: { plans: { tiers: ['free', 'basic', 'pro', 'enterprise'], billing_intervals: ['monthly', 'yearly'], features: ['API Access', 'Advanced Analytics', 'Priority Support'] } } }); } /** * Private: Get template for request type */ getTemplateForRequest(request) { // Find most suitable template for request type const templates = Array.from(this.promptTemplates.values()) .filter(t => t.type === request.type); if (templates.length === 0) return undefined; // For now, return first matching template // Could implement more sophisticated selection logic return templates[0]; } /** * Private: Build system prompt with context */ buildSystemPrompt(template, context) { let systemPrompt = template.systemPrompt; // Add domain-specific context if (context.domain && this.domainKnowledge.has(context.domain)) { const domainInfo = this.domainKnowledge.get(context.domain); systemPrompt += `\n\nDomain Knowledge (${context.domain}):\n${JSON.stringify(domainInfo, null, 2)}`; } // Add style guidance if (context.style) { systemPrompt += `\n\nData Style: Generate data in "${context.style}" style.`; } // Add cultural context if (context.culture) { systemPrompt += `\n\nCultural Context: Consider ${context.culture} cultural patterns and naming conventions.`; } return systemPrompt; } /** * Private: Build user prompt from template */ buildUserPrompt(template, request) { let userPrompt = template.userPromptTemplate; // Simple template variable replacement const variables = { count: request.count || 10, table: request.context.table || 'unknown', columns: request.context.columns || [], relationships: request.context.relationships || [], constraints: request.context.constraints || [], domain: request.context.domain || 'general', style: request.context.style || 'realistic', schema: request.context.schema }; // Replace simple variables for (const [key, value] of Object.entries(variables)) { const regex = new RegExp(`{{${key}}}`, 'g'); userPrompt = userPrompt.replace(regex, String(value)); } // Handle more complex Handlebars-like syntax (simplified) userPrompt = this.processHandlebarsLike(userPrompt, variables); // Add additional requirements if (request.requirements && request.requirements.length > 0) { userPrompt += '\n\nAdditional Requirements:\n'; request.requirements.forEach((req, i) => { userPrompt += `${i + 1}. ${req}\n`; }); } return userPrompt; } /** * Private: Generate output schema for structured responses */ generateOutputSchema(template, request) { if (template.outputFormat !== 'json') return undefined; const baseSchema = { type: 'object', properties: {}, required: [] }; // Generate schema based on request type switch (request.type) { case 'seed_data': return { type: 'object', properties: { records: { type: 'array', items: { type: 'object', properties: this.generateColumnSchema(request.context.columns || []) } }, metadata: { type: 'object', properties: { count: { type: 'number' }, table: { type: 'string' }, generated_at: { type: 'string' } } } }, required: ['records'] }; case 'schema_suggestion': return { type: 'object', properties: { suggestions: { type: 'array', items: { type: 'object', properties: { category: { type: 'string' }, title: { type: 'string' }, description: { type: 'string' }, priority: { type: 'string', enum: ['low', 'medium', 'high'] }, implementation: { type: 'string' } }, required: ['category', 'title', 'description'] } } }, required: ['suggestions'] }; default: return baseSchema; } } /** * Private: Generate schema for table columns */ generateColumnSchema(columns) { const properties = {}; for (const column of columns) { let jsonType = 'string'; // Map SQL types to JSON types if (column.type.includes('int') || column.type.includes('numeric') || column.type.includes('decimal')) { jsonType = 'number'; } else if (column.type.includes('bool')) { jsonType = 'boolean'; } else if (column.type.includes('json') || column.type.includes('array')) { jsonType = 'object'; } properties[column.name] = { type: jsonType }; } return properties; } /** * Private: Process simple Handlebars-like template syntax */ processHandlebarsLike(template, variables) { let result = template; // Handle {{#if variable}} blocks result = result.replace(/{{#if\s+(\w+)}}([\s\S]*?){{\/if}}/g, (match, varName, content) => { return variables[varName] ? content : ''; }); // Handle {{#each array}} blocks result = result.replace(/{{#each\s+(\w+)}}([\s\S]*?){{\/each}}/g, (match, arrayName, content) => { const array = variables[arrayName]; if (!Array.isArray(array)) return ''; return array.map(item => { return content.replace(/{{(\w+)}}/g, (itemMatch, propName) => { return item[propName] || ''; }); }).join(''); }); // Handle {{join array "separator"}} helper result = result.replace(/{{join\s+(\w+)\s+"([^"]+)"}}/g, (match, arrayName, separator) => { const array = variables[arrayName]; return Array.isArray(array) ? array.join(separator) : ''; }); return result; } } exports.DomainPromptEngine = DomainPromptEngine; // Export singleton instance exports.promptEngine = new DomainPromptEngine(); //# sourceMappingURL=prompt-engine.js.map