supa-seed
Version:
A constraint-aware, framework-agnostic database seeding framework with deep PostgreSQL business logic discovery and MakerKit integration support
382 lines • 15.7 kB
JavaScript
;
/**
* AI-Powered Asset Generation System
* Phase 5, Checkpoint E1 - Complete AI integration for intelligent asset and template generation
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.aiAssetGenerator = exports.AIAssetGenerator = void 0;
const ollama_client_1 = require("./ollama-client");
const prompt_engine_1 = require("./prompt-engine");
const response_cache_1 = require("./response-cache");
const logger_1 = require("../core/utils/logger");
class AIAssetGenerator {
constructor(ollama, promptEngine, cache) {
this.ollama = ollama || ollama_client_1.ollamaClient;
this.promptEngine = promptEngine || prompt_engine_1.promptEngine;
this.cache = cache || response_cache_1.aiCache;
logger_1.Logger.info('🚀 AI Asset Generator initialized');
}
/**
* Generate realistic seed data using AI
*/
async generateSeedData(table, count, context, options) {
const startTime = Date.now();
try {
logger_1.Logger.info(`🎯 Generating ${count} records for ${table} using AI`);
// Check if Ollama is available
const health = await this.ollama.checkHealth();
if (!health.connected && !options?.fallbackToFaker) {
return {
success: false,
metadata: {
source: 'ai',
responseTime: Date.now() - startTime,
cacheHit: false
},
errors: [`AI service unavailable: ${health.error}`],
warnings: []
};
}
// Generate prompt
const { system, user, schema } = this.promptEngine.generateSeedDataPrompt({
...context,
table
}, count);
// Check cache first
if (options?.useCache !== false) {
const cacheKey = `seed_data:${table}:${count}`;
const cached = await this.cache.get(user, options?.model, [cacheKey, ...(options?.tags || [])]);
if (cached) {
logger_1.Logger.info(`💰 Using cached seed data for ${table}`);
return {
success: true,
data: cached,
metadata: {
source: 'cache',
responseTime: Date.now() - startTime,
cacheHit: true
},
errors: [],
warnings: []
};
}
}
// Generate with AI if available
if (health.connected) {
try {
const response = await this.ollama.generateJSON(user, system, schema);
const responseTime = Date.now() - startTime;
const quality = this.assessDataQuality(response, context);
// Cache the response
if (options?.useCache !== false && quality >= (options?.qualityThreshold || 70)) {
await this.cache.set(user, response, health.recommendedModel || 'unknown', responseTime, {
tags: [`seed_data`, table, ...(options?.tags || [])],
quality,
ttl: options?.cacheTimeout
});
}
logger_1.Logger.info(`✨ Generated ${count} records for ${table} (quality: ${quality}%)`);
return {
success: true,
data: response,
metadata: {
source: 'ai',
model: health.recommendedModel,
responseTime,
quality,
cacheHit: false
},
errors: [],
warnings: quality < 80 ? [`Generated data quality is ${quality}%, consider manual review`] : []
};
}
catch (error) {
logger_1.Logger.warn(`🤖 AI generation failed: ${error.message}`);
if (options?.fallbackToFaker) {
return await this.fallbackToFaker(table, count, context, startTime);
}
return {
success: false,
metadata: {
source: 'ai',
responseTime: Date.now() - startTime,
cacheHit: false
},
errors: [`AI generation failed: ${error.message}`],
warnings: []
};
}
}
// Fallback if AI not available
if (options?.fallbackToFaker) {
return await this.fallbackToFaker(table, count, context, startTime);
}
return {
success: false,
metadata: {
source: 'ai',
responseTime: Date.now() - startTime,
cacheHit: false
},
errors: ['AI service unavailable and fallback disabled'],
warnings: []
};
}
catch (error) {
logger_1.Logger.error(`🚨 Asset generation failed: ${error.message}`);
return {
success: false,
metadata: {
source: 'ai',
responseTime: Date.now() - startTime,
cacheHit: false
},
errors: [`Generation failed: ${error.message}`],
warnings: []
};
}
}
/**
* Generate intelligent template recommendations
*/
async recommendTemplate(context, templateType, options) {
try {
logger_1.Logger.info(`🎨 Generating template recommendation for ${templateType}`);
const health = await this.ollama.checkHealth();
if (!health.connected) {
logger_1.Logger.warn('🤖 AI service unavailable for template recommendation');
return null;
}
const { system, user } = this.promptEngine.generateTemplatePrompt(context, templateType);
const response = await this.ollama.generateJSON(user, system);
// Parse and validate template recommendation
const recommendation = this.parseTemplateRecommendation(response, context, templateType);
logger_1.Logger.info(`🎨 Generated template recommendation (confidence: ${recommendation.confidence}%)`);
return recommendation;
}
catch (error) {
logger_1.Logger.error(`🚨 Template recommendation failed: ${error.message}`);
return null;
}
}
/**
* Analyze schema and suggest improvements
*/
async analyzeSchema(schema, options) {
try {
logger_1.Logger.info('🔍 Analyzing schema with AI for improvements');
const health = await this.ollama.checkHealth();
if (!health.connected) {
logger_1.Logger.warn('🤖 AI service unavailable for schema analysis');
return null;
}
// Check cache
const cacheKey = `schema_analysis:${schema.makerkitVersion}:${schema.customTables.length}`;
if (options?.useCache !== false) {
const cached = await this.cache.get(JSON.stringify(schema), options?.model, [cacheKey]);
if (cached) {
logger_1.Logger.info('💰 Using cached schema analysis');
return cached;
}
}
const { system, user } = this.promptEngine.generateSchemaAnalysisPrompt(schema);
const response = await this.ollama.generateJSON(user, system);
const analysis = this.parseSchemaAnalysis(response, schema);
// Cache the analysis
if (options?.useCache !== false) {
await this.cache.set(JSON.stringify(schema), analysis, health.recommendedModel || 'unknown', 0, {
tags: ['schema_analysis', schema.frameworkType, cacheKey],
quality: 90 // Schema analysis is generally high quality
});
}
logger_1.Logger.info(`🔍 Schema analysis complete (score: ${analysis.qualityScore}%)`);
return analysis;
}
catch (error) {
logger_1.Logger.error(`🚨 Schema analysis failed: ${error.message}`);
return null;
}
}
/**
* Generate contextual field suggestions
*/
async suggestFields(table, existingColumns, domain, options) {
try {
logger_1.Logger.info(`💡 Generating field suggestions for ${table} in ${domain} domain`);
const health = await this.ollama.checkHealth();
if (!health.connected) {
logger_1.Logger.warn('🤖 AI service unavailable for field suggestions');
return [];
}
const request = {
type: 'field_names',
context: {
table,
columns: existingColumns,
domain
}
};
const { system, user } = this.promptEngine.generatePrompt(request);
const response = await this.ollama.generateJSON(user, system);
return response.fields || [];
}
catch (error) {
logger_1.Logger.error(`🚨 Field suggestion failed: ${error.message}`);
return [];
}
}
/**
* Get AI service status
*/
async getStatus() {
const health = await this.ollama.checkHealth();
const cacheStats = this.cache.getStats();
const recommendations = [];
if (!health.connected) {
recommendations.push('Install and start Ollama for AI-powered generation');
}
if (cacheStats.hitRate < 0.3) {
recommendations.push('Consider enabling caching to improve performance');
}
if (health.availableModels.length === 0) {
recommendations.push('Pull AI models using: ollama pull llama3.1:latest');
}
return {
ai_available: health.connected,
model: health.recommendedModel || 'none',
cache_stats: cacheStats,
recommendations
};
}
/**
* Clear AI cache
*/
clearCache(criteria) {
return this.cache.clear(criteria);
}
/**
* Private: Fallback to Faker.js generation
*/
async fallbackToFaker(table, count, context, startTime) {
logger_1.Logger.info(`🎲 Falling back to Faker.js for ${table}`);
// This would integrate with existing faker-based generation
// For now, return a placeholder structure
const fakerData = {
records: Array.from({ length: count }, (_, i) => ({
id: i + 1,
// Would generate actual faker data based on context
generated_by: 'faker_fallback'
})),
metadata: {
count,
table,
generated_at: new Date().toISOString()
}
};
return {
success: true,
data: fakerData,
metadata: {
source: 'fallback',
responseTime: Date.now() - startTime,
cacheHit: false
},
errors: [],
warnings: ['Using Faker.js fallback - data may be less contextual']
};
}
/**
* Private: Assess quality of generated data
*/
assessDataQuality(data, context) {
let score = 100;
// Check if data structure is valid
if (!data || !data.records) {
return 0;
}
const records = data.records;
if (!Array.isArray(records) || records.length === 0) {
return 0;
}
// Check for required fields
if (context.columns) {
const requiredFields = context.columns.filter(c => !c.nullable).map(c => c.name);
const firstRecord = records[0];
for (const field of requiredFields) {
if (!(field in firstRecord)) {
score -= 20;
}
}
}
// Check for data diversity (simple check)
if (records.length > 1) {
const firstRecord = JSON.stringify(records[0]);
const duplicateCount = records.filter(r => JSON.stringify(r) === firstRecord).length;
if (duplicateCount > records.length * 0.5) {
score -= 30; // Too many duplicates
}
}
// Check for reasonable data types
for (const record of records.slice(0, 3)) { // Check first 3 records
for (const [key, value] of Object.entries(record)) {
if (value === null || value === undefined)
continue;
// Basic type checking
if (key.includes('email') && typeof value === 'string' && !value.includes('@')) {
score -= 10;
}
if (key.includes('id') && typeof value !== 'number' && typeof value !== 'string') {
score -= 10;
}
}
}
return Math.max(0, Math.min(100, score));
}
/**
* Private: Parse template recommendation from AI response
*/
parseTemplateRecommendation(response, context, templateType) {
// This would parse and validate the AI response
// For now, return a basic structure
return {
template: {
id: `ai-generated-${templateType}-${Date.now()}`,
name: `AI Generated ${templateType} Template`,
description: response.description || `Generated template for ${templateType}`,
category: 'seeder',
variables: response.variables || {},
metadata: {
created: new Date(),
updated: new Date(),
tags: ['ai-generated', templateType],
version: '1.0.0',
compatibility: {
supaSeedVersion: '1.0.0'
},
files: response.files || []
}
},
confidence: response.confidence || 75,
reasoning: response.reasoning || 'AI generated based on schema analysis',
suggestedVariables: response.suggestedVariables || []
};
}
/**
* Private: Parse schema analysis from AI response
*/
parseSchemaAnalysis(response, schema) {
return {
improvements: response.suggestions || [],
seedingStrategy: {
order: response.seedingOrder || schema.customTables,
relationships: response.relationships || [],
estimatedTime: response.estimatedTime || schema.customTables.length * 30 // 30s per table estimate
},
qualityScore: response.qualityScore || 85
};
}
}
exports.AIAssetGenerator = AIAssetGenerator;
// Export singleton instance
exports.aiAssetGenerator = new AIAssetGenerator();
//# sourceMappingURL=asset-generator.js.map