supa-seed
Version:
A constraint-aware, framework-agnostic database seeding framework with deep PostgreSQL business logic discovery and MakerKit integration support
578 lines โข 27.3 kB
JavaScript
;
/**
* Main Domain Detection Engine for Epic 2: Smart Platform Detection Engine
* Multi-domain detection with confidence scoring and evidence collection
* Part of Task 2.2.3: Implement main domain detector with multi-domain support
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DEFAULT_DOMAIN_DETECTION_CONFIG = exports.DomainDetectionEngine = void 0;
const logger_1 = require("../../core/utils/logger");
const domain_analyzers_1 = require("./domain-analyzers");
const domain_patterns_1 = require("./domain-patterns");
/**
* Main Domain Detection Engine
* Orchestrates comprehensive domain detection with multi-domain support
*/
class DomainDetectionEngine {
constructor() {
this.detectionCache = new Map();
this.patternOrchestrator = new domain_analyzers_1.DomainPatternAnalysisOrchestrator();
}
/**
* Detect content domain from database schema analysis
*/
async detectDomain(context, config = {}) {
const startTime = Date.now();
// Merge config with defaults
const detectionConfig = this.mergeWithDefaults(config);
logger_1.Logger.info(`๐ฏ Starting domain detection with strategy: ${detectionConfig.strategy}`);
try {
// Check cache if enabled
if (detectionConfig.useCaching) {
const cached = this.getCachedResult(context, detectionConfig);
if (cached) {
logger_1.Logger.info('๐ฆ Using cached domain detection result');
return cached;
}
}
// Handle manual override first
if (detectionConfig.manualOverride?.primaryDomain) {
const overrideResult = await this.handleManualOverride(context, detectionConfig, startTime);
if (overrideResult) {
logger_1.Logger.info(`๐ฏ Manual override applied: ${overrideResult.primaryDomain}`);
return overrideResult;
}
}
// Execute detection based on strategy
const result = await this.executeDetectionStrategy(context, detectionConfig, startTime);
// Cache result if enabled
if (detectionConfig.useCaching) {
this.cacheResult(context, detectionConfig, result);
}
logger_1.Logger.info(`๐ฏ Domain detection completed: ${result.primaryDomain} (confidence: ${result.confidence.toFixed(3)})`);
return result;
}
catch (error) {
logger_1.Logger.error('Domain detection failed:', error);
// Return fallback result
const fallbackResult = this.createFallbackResult(context, error, startTime);
return fallbackResult;
}
}
/**
* Execute detection based on configured strategy
*/
async executeDetectionStrategy(context, config, startTime) {
switch (config.strategy) {
case 'comprehensive':
return await this.comprehensiveDetection(context, config, startTime);
case 'fast':
return await this.fastDetection(context, config, startTime);
case 'conservative':
return await this.conservativeDetection(context, config, startTime);
case 'aggressive':
return await this.aggressiveDetection(context, config, startTime);
default:
return await this.comprehensiveDetection(context, config, startTime);
}
}
/**
* Comprehensive detection strategy - thorough analysis of all domains
*/
async comprehensiveDetection(context, config, startTime) {
logger_1.Logger.debug('๐ Executing comprehensive domain detection strategy');
// Analyze all domains
const domainAnalysisResults = await this.patternOrchestrator.analyzeAllDomains(context);
// Filter excluded domains
const filteredResults = domainAnalysisResults.filter(result => !config.excludeDomains.includes(result.domain));
// Determine primary domain
const primaryDomainResult = filteredResults[0]; // Already sorted by confidence
// Determine secondary domains
const secondaryDomains = this.determineSecondaryDomains(filteredResults.slice(1), config.detectSecondaryDomains);
// Collect all evidence
const allEvidence = this.collectAllEvidence(filteredResults);
// Check for hybrid capabilities
const hybridCapabilities = this.detectHybridCapabilities(filteredResults);
// Generate comprehensive reasoning
const reasoning = this.generateComprehensiveReasoning(filteredResults, hybridCapabilities);
// Calculate detection metrics
const detectionMetrics = {
executionTime: Date.now() - startTime,
patternsAnalyzed: filteredResults.reduce((sum, result) => sum + result.matchedPatterns.length, 0),
evidenceCount: allEvidence.length,
strategyUsed: 'comprehensive'
};
return {
primaryDomain: primaryDomainResult.domain,
confidence: primaryDomainResult.overallConfidence,
confidenceLevel: this.getConfidenceLevel(primaryDomainResult.overallConfidence),
secondaryDomains,
domainEvidence: allEvidence,
hybridCapabilities,
detectedFeatures: this.extractDomainFeatures(allEvidence),
reasoning,
recommendations: this.generateDomainRecommendations(primaryDomainResult.domain, primaryDomainResult.overallConfidence),
detectionMetrics,
warnings: this.generateWarnings(filteredResults),
errors: []
};
}
/**
* Fast detection strategy - quick analysis focusing on distinctive patterns
*/
async fastDetection(context, config, startTime) {
logger_1.Logger.debug('โก Executing fast domain detection strategy');
// Use quick analysis method
const domainAnalysisResults = await this.patternOrchestrator.getQuickDomainAnalysis(context);
// Filter excluded domains
const filteredResults = domainAnalysisResults.filter(result => !config.excludeDomains.includes(result.domain));
const primaryDomainResult = filteredResults[0];
// Limited secondary domain detection in fast mode
const secondaryDomains = config.detectSecondaryDomains
? this.determineSecondaryDomains(filteredResults.slice(1), true).slice(0, 2) // Max 2 secondary
: [];
const allEvidence = this.collectHighConfidenceEvidence(filteredResults);
const hybridCapabilities = false; // Skip hybrid detection in fast mode
const reasoning = [
`Fast analysis identified ${primaryDomainResult.domain} domain`,
`Based on ${primaryDomainResult.matchedPatterns.length} distinctive patterns`
];
const detectionMetrics = {
executionTime: Date.now() - startTime,
patternsAnalyzed: filteredResults.reduce((sum, result) => sum + result.matchedPatterns.length, 0),
evidenceCount: allEvidence.length,
strategyUsed: 'fast'
};
return {
primaryDomain: primaryDomainResult.domain,
confidence: primaryDomainResult.overallConfidence * 0.95, // Slightly reduce confidence for fast mode
confidenceLevel: this.getConfidenceLevel(primaryDomainResult.overallConfidence * 0.95),
secondaryDomains,
domainEvidence: allEvidence,
hybridCapabilities,
detectedFeatures: this.extractDomainFeatures(allEvidence),
reasoning,
recommendations: this.generateDomainRecommendations(primaryDomainResult.domain, primaryDomainResult.overallConfidence * 0.95),
detectionMetrics,
warnings: ['Fast detection mode - some patterns may not be fully analyzed'],
errors: []
};
}
/**
* Conservative detection strategy - high precision, may default to generic
*/
async conservativeDetection(context, config, startTime) {
logger_1.Logger.debug('๐ Executing conservative domain detection strategy');
const domainAnalysisResults = await this.patternOrchestrator.analyzeAllDomains(context);
const filteredResults = domainAnalysisResults.filter(result => !config.excludeDomains.includes(result.domain));
// Conservative logic: require high confidence or default to generic
let primaryDomainResult = filteredResults[0];
// If confidence is not high enough, check if we should default to generic
if (primaryDomainResult.overallConfidence < domain_patterns_1.DOMAIN_CONFIDENCE_THRESHOLDS.strong) {
const genericResult = filteredResults.find(result => result.domain === 'generic');
if (genericResult) {
primaryDomainResult = genericResult;
primaryDomainResult.overallConfidence = Math.max(primaryDomainResult.overallConfidence, 0.6);
}
}
// Reduce confidence for conservative approach
const adjustedConfidence = primaryDomainResult.overallConfidence * 0.9;
const secondaryDomains = config.detectSecondaryDomains
? this.determineSecondaryDomains(filteredResults.filter(r => r !== primaryDomainResult && r.overallConfidence > domain_patterns_1.DOMAIN_CONFIDENCE_THRESHOLDS.strong), true)
: [];
const allEvidence = this.collectHighConfidenceEvidence(filteredResults);
const hybridCapabilities = this.detectHybridCapabilities(filteredResults);
const reasoning = [
`Conservative analysis with high-confidence requirement`,
`Selected ${primaryDomainResult.domain} with confidence adjustments for reliability`,
`Required threshold: ${domain_patterns_1.DOMAIN_CONFIDENCE_THRESHOLDS.strong}, achieved: ${primaryDomainResult.overallConfidence.toFixed(2)}`
];
const detectionMetrics = {
executionTime: Date.now() - startTime,
patternsAnalyzed: filteredResults.reduce((sum, result) => sum + result.matchedPatterns.length, 0),
evidenceCount: allEvidence.length,
strategyUsed: 'conservative'
};
return {
primaryDomain: primaryDomainResult.domain,
confidence: adjustedConfidence,
confidenceLevel: this.getConfidenceLevel(adjustedConfidence),
secondaryDomains,
domainEvidence: allEvidence,
hybridCapabilities,
detectedFeatures: this.extractDomainFeatures(allEvidence),
reasoning,
recommendations: this.generateDomainRecommendations(primaryDomainResult.domain, adjustedConfidence),
detectionMetrics,
warnings: ['Conservative mode - may default to generic for ambiguous cases'],
errors: []
};
}
/**
* Aggressive detection strategy - favor definitive classifications with boosted confidence
*/
async aggressiveDetection(context, config, startTime) {
logger_1.Logger.debug('๐ Executing aggressive domain detection strategy');
const domainAnalysisResults = await this.patternOrchestrator.analyzeAllDomains(context);
const filteredResults = domainAnalysisResults.filter(result => !config.excludeDomains.includes(result.domain));
const primaryDomainResult = filteredResults[0];
// Boost confidence for clear winners in aggressive mode
const boostedConfidence = Math.min(primaryDomainResult.overallConfidence * 1.1, 0.99);
// More liberal secondary domain detection
const secondaryDomains = config.detectSecondaryDomains
? this.determineSecondaryDomains(filteredResults.slice(1).filter(r => r.overallConfidence > domain_patterns_1.DOMAIN_CONFIDENCE_THRESHOLDS.moderate), true)
: [];
const allEvidence = this.collectAllEvidence(filteredResults);
const hybridCapabilities = this.detectHybridCapabilities(filteredResults);
const reasoning = [
`Aggressive analysis boosted confidence for clear ${primaryDomainResult.domain} classification`,
`Based on ${allEvidence.length} evidence pieces across ${filteredResults.length} domains`,
`Confidence boost applied: ${primaryDomainResult.overallConfidence.toFixed(2)} โ ${boostedConfidence.toFixed(2)}`
];
const detectionMetrics = {
executionTime: Date.now() - startTime,
patternsAnalyzed: filteredResults.reduce((sum, result) => sum + result.matchedPatterns.length, 0),
evidenceCount: allEvidence.length,
strategyUsed: 'aggressive'
};
return {
primaryDomain: primaryDomainResult.domain,
confidence: boostedConfidence,
confidenceLevel: this.getConfidenceLevel(boostedConfidence),
secondaryDomains,
domainEvidence: allEvidence,
hybridCapabilities,
detectedFeatures: this.extractDomainFeatures(allEvidence),
reasoning,
recommendations: this.generateDomainRecommendations(primaryDomainResult.domain, boostedConfidence),
detectionMetrics,
warnings: ['Aggressive mode - confidence may be optimistically high'],
errors: []
};
}
/**
* Determine secondary domains with confidence thresholds
*/
determineSecondaryDomains(analysisResults, detectSecondary) {
if (!detectSecondary || analysisResults.length === 0) {
return [];
}
return analysisResults
.filter(result => result.overallConfidence > domain_patterns_1.DOMAIN_CONFIDENCE_THRESHOLDS.moderate)
.slice(0, 3) // Max 3 secondary domains
.map(result => ({
domain: result.domain,
confidence: result.overallConfidence,
reasoning: `Secondary domain based on ${result.matchedPatterns.length} pattern matches`
}));
}
/**
* Collect all evidence from analysis results
*/
collectAllEvidence(analysisResults) {
const allEvidence = [];
for (const result of analysisResults) {
allEvidence.push(...result.evidence);
}
// Sort by confidence and weight
return allEvidence.sort((a, b) => (b.confidence * b.weight) - (a.confidence * a.weight));
}
/**
* Collect only high-confidence evidence
*/
collectHighConfidenceEvidence(analysisResults) {
const allEvidence = this.collectAllEvidence(analysisResults);
return allEvidence.filter(evidence => evidence.confidence > domain_patterns_1.DOMAIN_CONFIDENCE_THRESHOLDS.moderate);
}
/**
* Detect hybrid domain capabilities
*/
detectHybridCapabilities(analysisResults) {
// Consider hybrid if multiple domains have reasonable confidence
const moderateConfidenceDomains = analysisResults.filter(result => result.overallConfidence > domain_patterns_1.DOMAIN_CONFIDENCE_THRESHOLDS.moderate);
return moderateConfidenceDomains.length >= 2;
}
/**
* Generate comprehensive reasoning
*/
generateComprehensiveReasoning(analysisResults, hybridCapabilities) {
const reasoning = [];
// Overall analysis summary
const nonZeroResults = analysisResults.filter(r => r.overallConfidence > 0);
reasoning.push(`Domain analysis across ${nonZeroResults.length} domains with pattern matches`);
// Top domain reasoning
if (nonZeroResults.length > 0) {
const topResult = nonZeroResults[0];
reasoning.push(`Primary domain ${topResult.domain}: ${topResult.overallConfidence.toFixed(2)} confidence (${topResult.matchedPatterns.length} patterns)`);
// Add specific pattern insights
const topPatterns = topResult.matchedPatterns
.sort((a, b) => b.confidence - a.confidence)
.slice(0, 3);
for (const pattern of topPatterns) {
reasoning.push(` โข ${pattern.pattern.name}: ${pattern.confidence.toFixed(2)} confidence`);
}
}
// Hybrid capabilities
if (hybridCapabilities) {
reasoning.push('Platform shows hybrid domain capabilities with multiple domain patterns');
}
// Secondary domains
const secondaryDomains = nonZeroResults.slice(1, 3);
if (secondaryDomains.length > 0) {
reasoning.push(`Secondary domains: ${secondaryDomains.map(r => `${r.domain}(${r.overallConfidence.toFixed(2)})`).join(', ')}`);
}
return reasoning;
}
/**
* Generate warnings based on analysis
*/
generateWarnings(analysisResults) {
const warnings = [];
// Check for low confidence
const topResult = analysisResults[0];
if (topResult && topResult.overallConfidence < domain_patterns_1.DOMAIN_CONFIDENCE_THRESHOLDS.moderate) {
warnings.push(`Low confidence in domain detection (${topResult.overallConfidence.toFixed(2)}) - consider manual verification`);
}
// Check for close competitions
if (analysisResults.length >= 2) {
const confidenceDiff = analysisResults[0].overallConfidence - analysisResults[1].overallConfidence;
if (confidenceDiff < 0.2) {
warnings.push(`Close competition between ${analysisResults[0].domain} and ${analysisResults[1].domain} domains`);
}
}
// Check for no clear winner
const highConfidenceResults = analysisResults.filter(r => r.overallConfidence > domain_patterns_1.DOMAIN_CONFIDENCE_THRESHOLDS.strong);
if (highConfidenceResults.length === 0) {
warnings.push('No domain reached high confidence threshold - platform may be generic or mixed-domain');
}
return warnings;
}
/**
* Handle manual domain override
*/
async handleManualOverride(context, config, startTime) {
const override = config.manualOverride;
if (!override.primaryDomain) {
return null;
}
// Create basic analysis for the overridden domain
const specificResults = await this.patternOrchestrator.analyzeSpecificDomains([override.primaryDomain], context);
const overriddenResult = specificResults[0];
// Create evidence from override
const overrideEvidence = override.customEvidence || [];
const analysisEvidence = overriddenResult ? overriddenResult.evidence : [];
return {
primaryDomain: override.primaryDomain,
confidence: 0.95, // High confidence for manual override
confidenceLevel: 'very_high',
secondaryDomains: [],
domainEvidence: [...overrideEvidence, ...analysisEvidence],
hybridCapabilities: false,
detectedFeatures: [],
reasoning: [
`Manual override applied: ${override.primaryDomain}`,
`Override confidence: 0.95`,
overriddenResult ? `Supporting analysis: ${overriddenResult.overallConfidence.toFixed(2)} confidence` : 'No supporting analysis available'
],
recommendations: [`Manual override for ${override.primaryDomain} domain applied`, 'Verify seeding configuration matches overridden domain'],
detectionMetrics: {
executionTime: Date.now() - startTime,
patternsAnalyzed: overriddenResult ? overriddenResult.matchedPatterns.length : 0,
evidenceCount: overrideEvidence.length + analysisEvidence.length,
strategyUsed: `${config.strategy}_with_override`
},
warnings: ['Manual domain override applied - automatic detection bypassed'],
errors: []
};
}
/**
* Create fallback result when detection fails
*/
createFallbackResult(context, error, startTime) {
return {
primaryDomain: 'generic',
confidence: 0.3,
confidenceLevel: 'low',
secondaryDomains: [],
domainEvidence: [],
hybridCapabilities: false,
detectedFeatures: [],
reasoning: ['Detection failed - defaulting to generic domain'],
recommendations: ['Manual domain verification recommended due to detection failure'],
detectionMetrics: {
executionTime: Date.now() - startTime,
patternsAnalyzed: 0,
evidenceCount: 0,
strategyUsed: 'fallback'
},
warnings: ['Domain detection failed - using fallback result'],
errors: [error.message]
};
}
/**
* Get confidence level from confidence score
*/
getConfidenceLevel(confidence) {
if (confidence >= domain_patterns_1.DOMAIN_CONFIDENCE_THRESHOLDS.definitive)
return 'very_high';
if (confidence >= domain_patterns_1.DOMAIN_CONFIDENCE_THRESHOLDS.strong)
return 'high';
if (confidence >= domain_patterns_1.DOMAIN_CONFIDENCE_THRESHOLDS.moderate)
return 'medium';
if (confidence >= domain_patterns_1.DOMAIN_CONFIDENCE_THRESHOLDS.weak)
return 'low';
return 'very_low';
}
/**
* Merge config with defaults
*/
mergeWithDefaults(config) {
return {
strategy: 'comprehensive',
confidenceThreshold: domain_patterns_1.DOMAIN_CONFIDENCE_THRESHOLDS.moderate,
detectSecondaryDomains: true,
analyzeBusinessLogic: false,
deepRelationshipAnalysis: true,
excludeDomains: [],
maxExecutionTime: 15000, // 15 seconds
useCaching: true,
...config
};
}
/**
* Cache management methods
*/
getCachedResult(context, config) {
const cacheKey = this.generateCacheKey(context, config);
const cached = this.detectionCache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < cached.ttl) {
return cached.result;
}
return null;
}
cacheResult(context, config, result) {
const cacheKey = this.generateCacheKey(context, config);
const schemaHash = this.generateSchemaHash(context);
this.detectionCache.set(cacheKey, {
schemaHash,
result,
timestamp: Date.now(),
config,
ttl: 300000 // 5 minutes
});
}
generateCacheKey(context, config) {
const schemaHash = this.generateSchemaHash(context);
const configHash = JSON.stringify({
strategy: config.strategy,
excludeDomains: config.excludeDomains,
detectSecondaryDomains: config.detectSecondaryDomains
});
return `domain_${schemaHash}_${btoa(configHash)}`;
}
generateSchemaHash(context) {
const schemaString = JSON.stringify({
tables: context.schema.tableNames.sort(),
relationships: context.schema.relationships.length,
constraints: context.schema.constraints.length
});
// Simple hash function
let hash = 0;
for (let i = 0; i < schemaString.length; i++) {
const char = schemaString.charCodeAt(i);
hash = ((hash << 5) - hash) + char;
hash = hash & hash; // Convert to 32-bit integer
}
return hash.toString(36);
}
/**
* Clear detection cache
*/
clearCache() {
this.detectionCache.clear();
logger_1.Logger.info('๐งน Domain detection cache cleared');
}
/**
* Get cache statistics
*/
getCacheStats() {
return {
size: this.detectionCache.size,
entries: Array.from(this.detectionCache.keys())
};
}
/**
* Extract detected features from domain evidence
*/
extractDomainFeatures(evidence) {
const features = new Set();
evidence.forEach(ev => {
// Extract feature names from evidence types and descriptions
if (ev.type === 'table_pattern') {
features.add(`table_pattern_${ev.description.toLowerCase().replace(/\s+/g, '_')}`);
}
else if (ev.type === 'column_analysis') {
features.add(`column_pattern_${ev.description.toLowerCase().replace(/\s+/g, '_')}`);
}
else if (ev.type === 'relationship_pattern') {
features.add(`relationship_${ev.description.toLowerCase().replace(/\s+/g, '_')}`);
}
else {
features.add(ev.type);
}
});
return Array.from(features);
}
/**
* Generate domain-specific recommendations
*/
generateDomainRecommendations(domain, confidence) {
const recommendations = [];
// Base recommendations based on confidence
if (confidence < 0.5) {
recommendations.push('Consider manual domain verification due to low confidence');
}
else if (confidence < 0.7) {
recommendations.push('Review domain detection results and verify against expected schema patterns');
}
// Domain-specific recommendations
switch (domain) {
case 'outdoor':
recommendations.push('Consider enabling outdoor-specific data generation patterns');
recommendations.push('Review gear and setup table relationships for outdoor domain');
break;
case 'saas':
recommendations.push('Enable team and workspace management features');
recommendations.push('Consider subscription and billing table requirements');
break;
case 'ecommerce':
recommendations.push('Verify product catalog and order management tables');
recommendations.push('Consider payment processing and inventory tracking features');
break;
case 'social':
recommendations.push('Enable user interaction and content sharing features');
recommendations.push('Consider privacy settings and content moderation requirements');
break;
case 'generic':
recommendations.push('Generic domain detected - consider more specific domain configuration');
recommendations.push('Review schema patterns to identify potential specialized domain features');
break;
}
return recommendations;
}
}
exports.DomainDetectionEngine = DomainDetectionEngine;
/**
* Default domain detection configuration
*/
exports.DEFAULT_DOMAIN_DETECTION_CONFIG = {
strategy: 'comprehensive',
confidenceThreshold: domain_patterns_1.DOMAIN_CONFIDENCE_THRESHOLDS.moderate,
detectSecondaryDomains: true,
analyzeBusinessLogic: false,
deepRelationshipAnalysis: true,
excludeDomains: [],
maxExecutionTime: 15000,
useCaching: true
};
//# sourceMappingURL=domain-detector.js.map